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