Line data Source code
1 : #include "fd_loader_v4_program.h"
2 :
3 : /* Helper functions that would normally be provided by fd_types. */
4 : FD_FN_PURE uchar
5 0 : fd_loader_v4_status_is_deployed( fd_loader_v4_state_t const * state ) {
6 0 : return state->status==FD_LOADER_V4_STATUS_ENUM_DELOYED;
7 0 : }
8 :
9 : FD_FN_PURE uchar
10 864 : fd_loader_v4_status_is_retracted( fd_loader_v4_state_t const * state ) {
11 864 : return state->status==FD_LOADER_V4_STATUS_ENUM_RETRACTED;
12 864 : }
13 :
14 : FD_FN_PURE uchar
15 0 : fd_loader_v4_status_is_finalized( fd_loader_v4_state_t const * state ) {
16 0 : return state->status==FD_LOADER_V4_STATUS_ENUM_FINALIZED;
17 0 : }
18 :
19 : /* Convenience method to set the state of a writable program account. Similar to `get_state()`,
20 : `set_state()` will do a simple transmute operation, which is quicker. */
21 : static int
22 : fd_loader_v4_set_state( fd_exec_instr_ctx_t * instr_ctx,
23 : ulong instr_acc_idx,
24 0 : fd_loader_v4_state_t * state ) {
25 0 : int err;
26 :
27 0 : uchar * data = NULL;
28 0 : ulong dlen = 0UL;
29 :
30 0 : err = fd_account_get_data_mut( instr_ctx, instr_acc_idx, &data, &dlen );
31 0 : if( FD_UNLIKELY( err ) ) {
32 0 : return err;
33 0 : }
34 :
35 0 : if( FD_UNLIKELY( dlen<LOADER_V4_PROGRAM_DATA_OFFSET ) ) {
36 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
37 0 : }
38 :
39 0 : fd_loader_v4_state_t * out_state = fd_type_pun( data );
40 0 : *out_state = *state;
41 0 : return FD_EXECUTOR_INSTR_SUCCESS;
42 0 : }
43 :
44 : /* `loader_v4::get_state()` performs a `transmute` operation which bypasses any decoding and directly
45 : reinterprets the data as a loader v4 state. The key difference between the transmute and standard
46 : decoding logic is that `get_state()` won't fail if `state.status` is not a valid discriminant, i.e.
47 : `state.status` can be any value within a ulong range that's not {Deployed, Retracted, Finalized}.
48 :
49 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L28-L40 */
50 : int
51 : fd_loader_v4_get_state( fd_borrowed_account_t const * program,
52 2943 : fd_loader_v4_state_t * state ) {
53 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L32 */
54 2943 : if( FD_UNLIKELY( program->const_meta->dlen<LOADER_V4_PROGRAM_DATA_OFFSET ) ) {
55 2079 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
56 2079 : }
57 :
58 864 : fd_loader_v4_state_t const * in_state = fd_type_pun_const( program->const_data );
59 864 : *state = *in_state;
60 864 : return FD_EXECUTOR_INSTR_SUCCESS;
61 2943 : }
62 :
63 : /* `check_program_account()` validates the program account's state from its data.
64 : Returns an instruction error if any of the checks fail. Writes the decoded
65 : state into `state`. */
66 : static int
67 : check_program_account( fd_exec_instr_ctx_t * instr_ctx,
68 : fd_borrowed_account_t const * program,
69 : fd_pubkey_t const * authority_address,
70 0 : fd_loader_v4_state_t * state ) {
71 0 : int err;
72 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L62-L65 */
73 0 : if( FD_UNLIKELY( memcmp( program->const_meta->info.owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
74 0 : fd_log_collector_msg_literal( instr_ctx, "Program not owned by loader" );
75 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
76 0 : }
77 :
78 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L66 */
79 0 : err = fd_loader_v4_get_state( program, state );
80 0 : if( FD_UNLIKELY( err ) ) {
81 0 : return err;
82 0 : }
83 :
84 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L67-L70 */
85 0 : if( FD_UNLIKELY( !fd_instr_acc_is_writable( instr_ctx->instr, program->pubkey ) ) ) {
86 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not writeable" );
87 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
88 0 : }
89 :
90 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L71-L74 */
91 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL ) ) ) {
92 0 : fd_log_collector_msg_literal( instr_ctx, "Authority did not sign" );
93 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
94 0 : }
95 :
96 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L75-L78 */
97 0 : if( FD_UNLIKELY( memcmp( &state->authority_address_or_next_version, authority_address, sizeof(fd_pubkey_t) ) ) ) {
98 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect authority provided" );
99 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
100 0 : }
101 :
102 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L79-L82 */
103 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_finalized( state ) ) ) {
104 0 : fd_log_collector_msg_literal( instr_ctx, "Program is finalized" );
105 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
106 0 : }
107 :
108 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L83 */
109 0 : return FD_EXECUTOR_INSTR_SUCCESS;
110 0 : }
111 :
112 : /* `process_instruction_write()` writes ELF data into an undeployed program account.
113 : This could either be a program which was already deployed and then retracted, or
114 : a new program which is uninitialized with enough space allocated from a call to `truncate`.
115 :
116 : Accounts:
117 : 0. Program account (writable)
118 : 1. Authority account (signer)
119 :
120 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L86-L121 */
121 : static int
122 : fd_loader_v4_program_instruction_write( fd_exec_instr_ctx_t * instr_ctx,
123 0 : fd_loader_v4_program_instruction_write_t const * write ) {
124 0 : int err;
125 0 : uint offset = write->offset;
126 0 : uchar const * bytes = write->bytes;
127 0 : ulong bytes_len = write->bytes_len;
128 :
129 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L94 */
130 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
131 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L95-L97 */
132 0 : fd_pubkey_t const * authority_address = &instr_ctx->instr->acct_pubkeys[ 1UL ];
133 :
134 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L98-L103 */
135 0 : fd_loader_v4_state_t state = {0};
136 0 : err = check_program_account( instr_ctx, program, authority_address, &state );
137 0 : if( FD_UNLIKELY( err ) ) {
138 0 : return err;
139 0 : }
140 :
141 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L104-L107 */
142 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( &state ) ) ) {
143 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not retracted" );
144 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
145 0 : }
146 :
147 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L108 */
148 0 : ulong end_offset = fd_ulong_sat_add( offset, bytes_len );
149 :
150 : /* Break up the chained operations into separate lines...
151 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L109-L110 */
152 0 : uchar * data = NULL;
153 0 : ulong dlen = 0UL;
154 0 : err = fd_account_get_data_mut( instr_ctx, 0UL, &data, &dlen );
155 0 : if( FD_UNLIKELY( err ) ) {
156 0 : return err;
157 0 : }
158 :
159 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L111-L114 */
160 0 : if( FD_UNLIKELY( fd_ulong_sat_add( LOADER_V4_PROGRAM_DATA_OFFSET, end_offset )>dlen ) ) {
161 0 : fd_log_collector_msg_literal( instr_ctx, "Write out of bounds" );
162 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
163 0 : }
164 :
165 0 : if( FD_LIKELY( bytes_len>0 ) ) {
166 0 : fd_memcpy( data+LOADER_V4_PROGRAM_DATA_OFFSET+offset, bytes, bytes_len );
167 0 : }
168 :
169 0 : return FD_EXECUTOR_INSTR_SUCCESS;
170 0 : } FD_BORROWED_ACCOUNT_DROP( program );
171 0 : }
172 :
173 : /* `process_instruction_truncate()` resizes an undeployed program account to the specified size.
174 : Initialization is taken care of when the program account size is first increased. Decreasing the size
175 : to 0 will close the program account.
176 :
177 : Other notes:
178 : - The executable status is set to true here on initialization.
179 :
180 : Accounts:
181 : 0. Program account (writable + signer)
182 : 1. Authority account (signer)
183 : 2. Recipient account to receive excess lamports from rent when decreasing account size (writable)
184 : - This account is only required when the program account size is being decreased (new_size < program dlen).
185 :
186 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L123-L208 */
187 : static int
188 : fd_loader_v4_program_instruction_truncate( fd_exec_instr_ctx_t * instr_ctx,
189 0 : fd_loader_v4_program_instruction_truncate_t const * truncate ) {
190 0 : int err;
191 0 : uint new_size = truncate->new_size;
192 :
193 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L130 */
194 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
195 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L131-L133 */
196 0 : fd_pubkey_t const * authority_address = &instr_ctx->instr->acct_pubkeys[ 1UL ];
197 :
198 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L134C9-L135 */
199 0 : uchar is_initialization = !!( new_size>0UL && program->const_meta->dlen<LOADER_V4_PROGRAM_DATA_OFFSET );
200 :
201 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L136-L164 */
202 0 : if( is_initialization ) {
203 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L137-L140 */
204 0 : if( FD_UNLIKELY( memcmp( program->const_meta->info.owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
205 0 : fd_log_collector_msg_literal( instr_ctx, "Program not owned by loader" );
206 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
207 0 : }
208 :
209 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L141-L144 */
210 0 : if( FD_UNLIKELY( !fd_instr_acc_is_writable( instr_ctx->instr, program->pubkey ) ) ) {
211 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not writeable" );
212 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
213 0 : }
214 :
215 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L145-L148 */
216 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 0UL ) ) ) {
217 0 : fd_log_collector_msg_literal( instr_ctx, "Program did not sign" );
218 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
219 0 : }
220 :
221 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L149-L152 */
222 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL ) ) ) {
223 0 : fd_log_collector_msg_literal( instr_ctx, "Authority did not sign" );
224 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
225 0 : }
226 0 : } else {
227 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L154-L159 */
228 0 : fd_loader_v4_state_t state = {0};
229 0 : err = check_program_account( instr_ctx, program, authority_address, &state );
230 0 : if( FD_UNLIKELY( err ) ) {
231 0 : return err;
232 0 : }
233 :
234 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L160-L163 */
235 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( &state ) ) ) {
236 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not retracted" );
237 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
238 0 : }
239 0 : }
240 :
241 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L165-L171 */
242 0 : fd_rent_t const * rent = fd_sysvar_cache_rent( instr_ctx->slot_ctx->sysvar_cache );
243 0 : if( FD_UNLIKELY( rent==NULL ) ) {
244 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
245 0 : }
246 :
247 : /* Note that this new_program_dlen is only relevant when new_size > 0 */
248 0 : ulong new_program_dlen = fd_ulong_sat_add( LOADER_V4_PROGRAM_DATA_OFFSET, new_size );
249 0 : ulong required_lamports = ( new_size==0UL ) ?
250 0 : 0UL :
251 0 : fd_ulong_max( fd_rent_exempt_minimum_balance( rent, new_program_dlen ),
252 0 : 1UL );
253 :
254 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L172-L193 */
255 0 : if( FD_UNLIKELY( program->const_meta->info.lamports<required_lamports ) ) {
256 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L174-L179 */
257 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
258 0 : "Insufficient lamports, %lu are required", required_lamports );
259 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
260 0 : } else if( program->const_meta->info.lamports>required_lamports ) {
261 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L182-L183 */
262 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 2UL, recipient ) {
263 :
264 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L184-L187 */
265 0 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( instr_ctx->instr, 2UL ) ) ) {
266 0 : fd_log_collector_msg_literal( instr_ctx, "Recipient is not writeable" );
267 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
268 0 : }
269 :
270 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L190 */
271 0 : ulong lamports_to_receive = fd_ulong_sat_sub( program->const_meta->info.lamports,
272 0 : required_lamports );
273 0 : err = fd_account_checked_sub_lamports( instr_ctx, 0UL, lamports_to_receive );
274 0 : if( FD_UNLIKELY( err ) ) {
275 0 : return err;
276 0 : }
277 0 : err = fd_account_checked_add_lamports( instr_ctx, 2UL, lamports_to_receive );
278 0 : if( FD_UNLIKELY( err ) ) {
279 0 : return err;
280 0 : }
281 0 : } FD_BORROWED_ACCOUNT_DROP( recipient );
282 0 : } /* no-op for program lamports == required lamports */
283 :
284 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L194-L206 */
285 0 : if( new_size==0UL ) {
286 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L195 */
287 0 : err = fd_account_set_data_length( instr_ctx, 0UL, 0UL );
288 0 : if( FD_UNLIKELY( err ) ) {
289 0 : return err;
290 0 : }
291 0 : } else {
292 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L197-L199 */
293 0 : err = fd_account_set_data_length( instr_ctx, 0UL, new_program_dlen );
294 0 : if( FD_UNLIKELY( err ) ) {
295 0 : return err;
296 0 : }
297 :
298 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L200-L205 */
299 0 : if( is_initialization ) {
300 : /* https://github.com/anza-xyz/agave/blob/09ef71223b24e30e59eaeaf5eb95e85f222c7de1/programs/loader-v4/src/lib.rs#L197 */
301 0 : program->meta->info.executable = true;
302 :
303 : /* Serialize into the program account directly. Note that an error is impossible
304 : because `new_program_dlen` > `LOADER_V4_PROGRAM_DATA_OFFSET`.
305 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L201-L204 */
306 0 : fd_loader_v4_state_t state = {
307 0 : .slot = 0UL,
308 0 : .status = FD_LOADER_V4_STATUS_ENUM_RETRACTED,
309 0 : .authority_address_or_next_version = *authority_address,
310 0 : };
311 0 : err = fd_loader_v4_set_state( instr_ctx, 0UL, &state );
312 0 : if( FD_UNLIKELY( err ) ) {
313 0 : return err;
314 0 : }
315 0 : }
316 0 : }
317 :
318 0 : return FD_EXECUTOR_INSTR_SUCCESS;
319 0 : } FD_BORROWED_ACCOUNT_DROP( program );
320 0 : }
321 :
322 : /* `process_instruction_deploy()` will verify the ELF bytes of a program account in a `Retracted`
323 : state and deploy it if successful, making it ready for use. Optionally, a source buffer account
324 : may be provided to copy the ELF bytes from. In this case, the program data is overwritten by the
325 : data in the buffer account, and the buffer account is closed with some of its lamports transferred
326 : to the program account if needed to meet the minimum rent exemption balance.
327 :
328 : Other notes:
329 : - Newly deployed programs may not be retracted/redeployed within `LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS` (750) slots.
330 :
331 : Accounts:
332 : 0. Program account (writable)
333 : 1. Authority account (signer)
334 : 2. (OPTIONAL) Source buffer account (writable)
335 :
336 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L210-L325 */
337 : static int
338 0 : fd_loader_v4_program_instruction_deploy( fd_exec_instr_ctx_t * instr_ctx ) {
339 0 : int err;
340 :
341 : /* These variables should exist outside of borrowed account scopes. */
342 0 : uchar source_program_present = !!( instr_ctx->instr->acct_cnt>2 );
343 0 : fd_loader_v4_state_t program_state = {0};
344 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock( instr_ctx->slot_ctx->sysvar_cache );
345 :
346 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L217-L219 */
347 0 : fd_pubkey_t const * authority_address = &instr_ctx->instr->acct_pubkeys[ 1UL ];
348 :
349 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L216 */
350 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
351 : /* Here Agave tries to acquire the source buffer account but does not fail if it is not present.
352 : We will only try to borrow the account when we need it.
353 :
354 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L220-L222
355 :
356 : No-op for us... */
357 :
358 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L223-L228 */
359 0 : err = check_program_account( instr_ctx, program, authority_address, &program_state );
360 0 : if( FD_UNLIKELY( err ) ) {
361 0 : return err;
362 0 : }
363 :
364 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L229 */
365 0 : if( FD_UNLIKELY( clock==NULL ) ) {
366 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
367 0 : }
368 0 : ulong current_slot = clock->slot;
369 :
370 : /* `current_slot == 0` indicates that the program hasn't been deployed yet, so a cooldown check
371 : is not needed. Otherwise, a cooldown of 750 slots is applied before being able to
372 : redeploy or retract the program.
373 :
374 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L231-L240 */
375 0 : if( FD_UNLIKELY( current_slot>0UL && fd_ulong_sat_add( program_state.slot, LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS )>current_slot ) ) {
376 0 : fd_log_collector_msg_literal( instr_ctx, "Program was deployed recently, cooldown still in effect" );
377 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
378 0 : }
379 :
380 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L241-L244 */
381 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( &program_state ) ) ) {
382 0 : fd_log_collector_msg_literal( instr_ctx, "Destination program is not retracted" );
383 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
384 0 : }
385 0 : } FD_BORROWED_ACCOUNT_DROP( program ); // We no longer need the program account
386 :
387 : /* Since our borrowed account semantics are slightly different than Agave's, we drop the program account above
388 : (since the variable is no longer needed) and acquire a new borrowed account that may point to the program account
389 : OR a new buffer account, if it's present. This way, the account borrowing semantics stay conformant with Agave.
390 :
391 : If the source program (account key at idx 2) is present, we acquire that as the buffer and perform checks on its
392 : state to make sure it's valid. Otherwise, we reacquire the same program account from above. */
393 0 : ulong buffer_idx = ( source_program_present ) ? 2UL : 0UL;
394 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, buffer_idx, buffer ) {
395 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L245-L259 */
396 0 : if( source_program_present ) {
397 : /* buffer == source_program here
398 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L246-L251 */
399 0 : fd_loader_v4_state_t source_state = {0};
400 0 : err = check_program_account( instr_ctx, buffer, authority_address, &source_state );
401 0 : if( FD_UNLIKELY( err ) ) {
402 0 : return err;
403 0 : }
404 :
405 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L252-L255 */
406 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( &source_state ) ) ) {
407 0 : fd_log_collector_msg_literal( instr_ctx, "Source program is not retracted" );
408 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
409 0 : }
410 0 : }
411 :
412 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L261-L264 */
413 0 : if( FD_UNLIKELY( buffer->const_meta->dlen<LOADER_V4_PROGRAM_DATA_OFFSET ) ) {
414 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
415 0 : }
416 0 : uchar const * programdata = buffer->const_data + LOADER_V4_PROGRAM_DATA_OFFSET;
417 :
418 : /* Our program cache is fundamentally different from Agave's. Here, they would perform verifications and
419 : add the program to their cache, but we only perform verifications now and defer cache population to the
420 : end of the slot. Since programs cannot be invoked until the next slot anyways, doing this is okay.
421 :
422 : https://github.com/anza-xyz/agave/blob/09ef71223b24e30e59eaeaf5eb95e85f222c7de1/programs/loader-v4/src/lib.rs#L262-L269 */
423 0 : err = fd_deploy_program( instr_ctx, programdata, buffer->const_meta->dlen - LOADER_V4_PROGRAM_DATA_OFFSET, fd_spad_virtual( instr_ctx->txn_ctx->spad ) );
424 0 : if( FD_UNLIKELY( err ) ) {
425 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
426 0 : }
427 :
428 : /* Transfer enough lamports from the source program, if exists, to the program account to cover
429 : the rent exempt minimum balance amount. Then, close the source program account.
430 :
431 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L295-L303 */
432 0 : if( source_program_present ) {
433 : /* We can safely reacquire the program account here since the buffer account corresponds to the source
434 : program here. */
435 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
436 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L296 */
437 0 : fd_rent_t const * rent = fd_sysvar_cache_rent( instr_ctx->slot_ctx->sysvar_cache );
438 0 : if( FD_UNLIKELY( rent==NULL ) ) {
439 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
440 0 : }
441 :
442 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L297 */
443 0 : ulong required_lamports = fd_rent_exempt_minimum_balance( rent, buffer->const_meta->dlen );
444 :
445 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L298 */
446 0 : ulong transfer_lamports = fd_ulong_sat_sub( required_lamports, program->const_meta->info.lamports );
447 :
448 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L299 */
449 0 : err = fd_account_set_data_from_slice( instr_ctx, 0UL, buffer->const_data, buffer->const_meta->dlen );
450 0 : if( FD_UNLIKELY( err ) ) {
451 0 : return err;
452 0 : }
453 :
454 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L300 */
455 0 : err = fd_account_set_data_length( instr_ctx, 2UL, 0UL );
456 0 : if( FD_UNLIKELY( err ) ) {
457 0 : return err;
458 0 : }
459 :
460 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L301 */
461 0 : err = fd_account_checked_sub_lamports( instr_ctx, 2UL, transfer_lamports );
462 0 : if( FD_UNLIKELY( err ) ) {
463 0 : return err;
464 0 : }
465 :
466 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L302 */
467 0 : err = fd_account_checked_add_lamports( instr_ctx, 0UL, transfer_lamports );
468 0 : if( FD_UNLIKELY( err ) ) {
469 0 : return err;
470 0 : }
471 0 : } FD_BORROWED_ACCOUNT_DROP( program );
472 0 : }
473 0 : } FD_BORROWED_ACCOUNT_DROP( buffer ); // Drop the buffer account. At this point, there are no outstanding account borrows.
474 :
475 : /* Reacquire the program account to persist state changes to it. */
476 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
477 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L304-L306 */
478 0 : program_state.slot = clock->slot;
479 0 : program_state.status = FD_LOADER_V4_STATUS_ENUM_DELOYED;
480 0 : err = fd_loader_v4_set_state( instr_ctx, 0UL, &program_state );
481 0 : if( FD_UNLIKELY( err ) ) {
482 0 : return err;
483 0 : }
484 :
485 0 : return FD_EXECUTOR_INSTR_SUCCESS;
486 0 : } FD_BORROWED_ACCOUNT_DROP( program );
487 0 : }
488 :
489 : /* `process_instruction_retract()` retracts a currently deployed program, making it writable
490 : and uninvokable. After a program is retracted, users can write and truncate data freely,
491 : allowing them to upgrade or close the program account.
492 :
493 : Other notes:
494 : - Newly deployed programs may not be retracted/redeployed within `LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS` (750) slots.
495 : - When a program is retracted, the executable flag is NOT changed.
496 :
497 : Accounts:
498 : 0. Program account (writable)
499 : 1. Authority account (signer)
500 :
501 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L327-L369 */
502 : static int
503 0 : fd_loader_v4_program_instruction_retract( fd_exec_instr_ctx_t * instr_ctx ) {
504 0 : int err;
505 :
506 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
507 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L335-L337 */
508 0 : fd_pubkey_t const * authority_address = &instr_ctx->instr->acct_pubkeys[ 1UL ];
509 :
510 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L338-L343 */
511 0 : fd_loader_v4_state_t state = {0};
512 0 : err = check_program_account( instr_ctx, program, authority_address, &state );
513 0 : if( FD_UNLIKELY( err ) ) {
514 0 : return err;
515 0 : }
516 :
517 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L344 */
518 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock( instr_ctx->slot_ctx->sysvar_cache );
519 0 : if( FD_UNLIKELY( clock==NULL ) ) {
520 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
521 0 : }
522 0 : ulong current_slot = clock->slot;
523 :
524 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L345-L351 */
525 0 : if( FD_UNLIKELY( fd_ulong_sat_add( state.slot, LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS )>current_slot ) ) {
526 0 : fd_log_collector_msg_literal( instr_ctx, "Program was deployed recently, cooldown still in effect" );
527 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
528 0 : }
529 :
530 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L352-L355 */
531 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_deployed( &state ) ) ) {
532 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
533 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
534 0 : }
535 :
536 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L356 */
537 0 : state.status = FD_LOADER_V4_STATUS_ENUM_RETRACTED;
538 0 : err = fd_loader_v4_set_state( instr_ctx, 0UL, &state );
539 0 : if( FD_UNLIKELY( err ) ) {
540 0 : return err;
541 0 : }
542 :
543 : /* No need to update program cache - see note in `deploy` processor. */
544 :
545 0 : return FD_EXECUTOR_INSTR_SUCCESS;
546 0 : } FD_BORROWED_ACCOUNT_DROP( program );
547 0 : }
548 :
549 : /* `process_instruction_transfer_authority()` transfers the authority of a program account.
550 :
551 : Accounts:
552 : 0. Program account (writable)
553 : 1. Current authority (signer)
554 : 2. New authority (signer
555 :
556 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L371-L401 */
557 : static int
558 0 : fd_loader_v4_program_instruction_transfer_authority( fd_exec_instr_ctx_t * instr_ctx ) {
559 0 : int err;
560 :
561 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L377 */
562 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
563 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L378-L380 */
564 0 : fd_pubkey_t const * authority_address = &instr_ctx->instr->acct_pubkeys[ 1UL ];
565 :
566 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L381-L383 */
567 0 : fd_pubkey_t const * new_authority_address = &instr_ctx->instr->acct_pubkeys[ 2UL ];
568 :
569 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L384-L389 */
570 0 : fd_loader_v4_state_t state = {0};
571 0 : err = check_program_account( instr_ctx, program, authority_address, &state );
572 0 : if( FD_UNLIKELY( err ) ) {
573 0 : return err;
574 0 : }
575 :
576 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L390-L393 */
577 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL ) ) ) {
578 0 : fd_log_collector_msg_literal( instr_ctx, "New authority did not sign" );
579 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
580 0 : }
581 :
582 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L394-L397 */
583 0 : if( FD_UNLIKELY( !memcmp( state.authority_address_or_next_version.key, new_authority_address, sizeof(fd_pubkey_t) ) ) ) {
584 0 : fd_log_collector_msg_literal( instr_ctx, "No change" );
585 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
586 0 : }
587 :
588 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L398-L399 */
589 0 : state.authority_address_or_next_version = *new_authority_address;
590 0 : err = fd_loader_v4_set_state( instr_ctx, 0UL, &state );
591 0 : if( FD_UNLIKELY( err ) ) {
592 0 : return err;
593 0 : }
594 :
595 0 : return FD_EXECUTOR_INSTR_SUCCESS;
596 0 : } FD_BORROWED_ACCOUNT_DROP( program );
597 0 : }
598 :
599 : /* `process_instruction_finalize()` finalizes the program account, rendering it immutable.
600 :
601 : Other notes:
602 : - Users must specify a "next version" which, from my inspection, serves no functional purpose besides showing up
603 : as extra information on a block explorer. The next version can be itself.
604 : - The next version must be a program that...
605 : - Is not finalized
606 : - Has the same authority as the current program
607 :
608 : Accounts:
609 : 0. Program account (writable)
610 : 1. Authority account (signer)
611 : 2. Next version of the program
612 :
613 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L403-L446 */
614 : static int
615 0 : fd_loader_v4_program_instruction_finalize( fd_exec_instr_ctx_t * instr_ctx ) {
616 0 : int err;
617 :
618 : /* Contains variables that need to be accessed in multiple borrowed account scopes.
619 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L410-L412 */
620 0 : fd_pubkey_t const * authority_address = &instr_ctx->instr->acct_pubkeys[ 1UL ];
621 0 : fd_pubkey_t const * address_of_next_version = &instr_ctx->instr->acct_pubkeys[ 2UL ];
622 0 : fd_loader_v4_state_t state = {0};
623 :
624 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
625 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L413-L418 */
626 :
627 0 : err = check_program_account( instr_ctx, program, authority_address, &state );
628 0 : if( FD_UNLIKELY( err ) ) {
629 0 : return err;
630 0 : }
631 :
632 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L419-L422 */
633 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_deployed( &state ) ) ) {
634 0 : fd_log_collector_msg_literal( instr_ctx, "Program must be deployed to be finalized" );
635 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
636 0 : }
637 0 : } FD_BORROWED_ACCOUNT_DROP( program );
638 :
639 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L424-L425 */
640 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 2UL, next_version ) {
641 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L426-L429 */
642 0 : if( FD_UNLIKELY( memcmp( next_version->const_meta->info.owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
643 0 : fd_log_collector_msg_literal( instr_ctx, "Next version is not owned by loader" );
644 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
645 0 : }
646 :
647 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L430 */
648 0 : fd_loader_v4_state_t state_of_next_version = {0};
649 0 : err = fd_loader_v4_get_state( next_version, &state_of_next_version );
650 0 : if( FD_UNLIKELY( err ) ) {
651 0 : return err;
652 0 : }
653 :
654 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L431-L434 */
655 0 : if( FD_UNLIKELY( memcmp( state_of_next_version.authority_address_or_next_version.key, authority_address, sizeof(fd_pubkey_t) ) ) ) {
656 0 : fd_log_collector_msg_literal( instr_ctx, "Next version has different authority" );
657 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
658 0 : }
659 :
660 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L435-L438 */
661 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_finalized( &state_of_next_version ) ) ) {
662 0 : fd_log_collector_msg_literal( instr_ctx, "Next version is finalized" );
663 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
664 0 : }
665 0 : } FD_BORROWED_ACCOUNT_DROP( next_version );
666 :
667 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L441 */
668 0 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( instr_ctx, 0UL, program ) {
669 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L442-L444 */
670 0 : state.authority_address_or_next_version = *address_of_next_version;
671 0 : state.status = FD_LOADER_V4_STATUS_ENUM_FINALIZED;
672 0 : err = fd_loader_v4_set_state( instr_ctx, 0UL, &state );
673 0 : if( FD_UNLIKELY( err ) ) {
674 0 : return err;
675 0 : }
676 :
677 0 : return FD_EXECUTOR_INSTR_SUCCESS;
678 0 : } FD_BORROWED_ACCOUNT_DROP( program );
679 0 : }
680 :
681 : /* `process_instruction_inner()`, the entrypoint for all loader v4 instruction invocations +
682 : any loader v4-owned programs.
683 :
684 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L463-L526 */
685 : int
686 0 : fd_loader_v4_program_execute( fd_exec_instr_ctx_t * instr_ctx ) {
687 0 : if( FD_UNLIKELY( !FD_FEATURE_ACTIVE( instr_ctx->slot_ctx, enable_program_runtime_v2_and_loader_v4 ) ) ) {
688 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
689 0 : }
690 :
691 0 : FD_SPAD_FRAME_BEGIN( instr_ctx->txn_ctx->spad ) {
692 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L470 */
693 0 : fd_pubkey_t const * program_id = &instr_ctx->instr->program_id_pubkey;
694 :
695 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L471-L488 */
696 0 : int rc = FD_EXECUTOR_INSTR_SUCCESS;
697 0 : if( !memcmp( program_id, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
698 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L472 */
699 0 : FD_EXEC_CU_UPDATE( instr_ctx, LOADER_V4_DEFAULT_COMPUTE_UNITS );
700 :
701 : /* Note the dataend is capped at a 1232 bytes offset to mirror the semantics of `limited_deserialize`.
702 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L473 */
703 0 : uchar const * data = instr_ctx->instr->data;
704 :
705 0 : fd_loader_v4_program_instruction_t instruction = {0};
706 0 : fd_bincode_decode_ctx_t decode_ctx = {
707 0 : .data = data,
708 0 : .dataend = &data[ instr_ctx->instr->data_sz > 1232UL ? 1232UL : instr_ctx->instr->data_sz ],
709 0 : .valloc = fd_spad_virtual( instr_ctx->txn_ctx->spad ),
710 0 : };
711 :
712 0 : if( FD_UNLIKELY( fd_loader_v4_program_instruction_decode( &instruction, &decode_ctx ) ) ) {
713 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
714 0 : }
715 :
716 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L473-L486 */
717 0 : switch( instruction.discriminant ) {
718 0 : case fd_loader_v4_program_instruction_enum_write: {
719 0 : CHECK_NUM_INSN_ACCS( instr_ctx, 2U );
720 :
721 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L474-L476 */
722 0 : rc = fd_loader_v4_program_instruction_write( instr_ctx, &instruction.inner.write );
723 0 : break;
724 0 : }
725 0 : case fd_loader_v4_program_instruction_enum_truncate: {
726 0 : CHECK_NUM_INSN_ACCS( instr_ctx, 2U );
727 :
728 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L477-L479 */
729 0 : rc = fd_loader_v4_program_instruction_truncate( instr_ctx, &instruction.inner.truncate );
730 0 : break;
731 0 : }
732 0 : case fd_loader_v4_program_instruction_enum_deploy: {
733 0 : CHECK_NUM_INSN_ACCS( instr_ctx, 2U );
734 :
735 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L480 */
736 0 : rc = fd_loader_v4_program_instruction_deploy( instr_ctx );
737 0 : break;
738 0 : }
739 0 : case fd_loader_v4_program_instruction_enum_retract: {
740 0 : CHECK_NUM_INSN_ACCS( instr_ctx, 2U );
741 :
742 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L481 */
743 0 : rc = fd_loader_v4_program_instruction_retract( instr_ctx );
744 0 : break;
745 0 : }
746 0 : case fd_loader_v4_program_instruction_enum_transfer_authority: {
747 0 : CHECK_NUM_INSN_ACCS( instr_ctx, 3U );
748 :
749 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L482-L484 */
750 0 : rc = fd_loader_v4_program_instruction_transfer_authority( instr_ctx );
751 0 : break;
752 0 : }
753 0 : case fd_loader_v4_program_instruction_enum_finalize: {
754 0 : CHECK_NUM_INSN_ACCS( instr_ctx, 3U );
755 :
756 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L485 */
757 0 : rc = fd_loader_v4_program_instruction_finalize( instr_ctx );
758 0 : break;
759 0 : }
760 0 : }
761 0 : } else {
762 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L489 */
763 0 : fd_borrowed_account_t * program = NULL;
764 0 : rc = fd_txn_borrowed_account_view_idx( instr_ctx->txn_ctx, instr_ctx->instr->program_id, &program );
765 0 : if( FD_UNLIKELY( rc ) ) {
766 0 : return rc;
767 0 : }
768 :
769 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L490 */
770 0 : fd_loader_v4_state_t state = {0};
771 0 : rc = fd_loader_v4_get_state( program, &state );
772 0 : if( FD_UNLIKELY( rc ) ) {
773 0 : return rc;
774 0 : }
775 :
776 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L491-L494 */
777 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_retracted( &state ) ) ) {
778 0 : fd_log_collector_msg_literal( instr_ctx, "Program is retracted" );
779 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
780 0 : }
781 :
782 : /* Handle `DelayedVisibility` case */
783 0 : if( FD_UNLIKELY( state.slot>=instr_ctx->slot_ctx->slot_bank.slot ) ) {
784 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
785 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
786 0 : }
787 :
788 : /* See note in `fd_bpf_loader_program_execute()` as to why we must tie the cache into consensus :(
789 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L496-L502 */
790 0 : fd_sbpf_validated_program_t * prog = NULL;
791 0 : if( FD_UNLIKELY( fd_bpf_load_cache_entry( instr_ctx->slot_ctx, &instr_ctx->instr->program_id_pubkey, &prog )!=0 ) ) {
792 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not cached" );
793 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
794 0 : }
795 :
796 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/loader-v4/src/lib.rs#L519 */
797 0 : rc = fd_bpf_execute( instr_ctx, prog, 0 );
798 0 : }
799 :
800 0 : return rc;
801 0 : } FD_SPAD_FRAME_END;
802 0 : }
|