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 get the state of a writable program account as a mutable reference.
20 : `get_state_mut()` takes a mutable pointer to a borrowed account's writable data and transmutes
21 : it into a loader v4 state. It is safe to write to the returned loader v4 state. The function
22 : returns 0 on success or an error code if the account data is too small. Assumes `data` is a
23 : non-null, writable pointer into an account's data.
24 :
25 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L46-L58 */
26 : static fd_loader_v4_state_t *
27 : fd_loader_v4_get_state_mut( uchar * data,
28 : ulong dlen,
29 0 : int * err ) {
30 0 : *err = FD_EXECUTOR_INSTR_SUCCESS;
31 :
32 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L48-L52 */
33 0 : if( FD_UNLIKELY( dlen<LOADER_V4_PROGRAM_DATA_OFFSET ) ) {
34 0 : *err = FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
35 0 : return NULL;
36 0 : }
37 :
38 0 : return fd_type_pun( data );
39 0 : }
40 :
41 : /* `loader_v4::get_state()` performs a `transmute` operation which bypasses any decoding and directly
42 : reinterprets the data as a loader v4 state. The key difference between the transmute and standard
43 : decoding logic is that `get_state()` won't fail if `state.status` is not a valid discriminant, i.e.
44 : `state.status` can be any value within a ulong range that's not {Deployed, Retracted, Finalized}.
45 : Returns a casted pointer to the account data, interpreted as a loader v4 state type, or NULL if the
46 : account data is too small. The returned state is const and thus is NOT safe to modify.
47 :
48 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L32-L44 */
49 : fd_loader_v4_state_t const *
50 : fd_loader_v4_get_state( fd_txn_account_t const * program,
51 0 : int * err ) {
52 0 : *err = FD_EXECUTOR_INSTR_SUCCESS;
53 :
54 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L35-L36 */
55 0 : if( FD_UNLIKELY( program->vt->get_data_len( program )<LOADER_V4_PROGRAM_DATA_OFFSET ) ) {
56 0 : *err = FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
57 0 : return NULL;
58 0 : }
59 :
60 0 : return fd_type_pun_const( program->vt->get_data( program ) );
61 0 : }
62 :
63 : /* `check_program_account()` validates the program account's state from its data.
64 : Sets `err` to an instruction error if any of the checks fail. Otherwise, returns a
65 : const pointer to the program account data, transmuted as a loader v4 state.
66 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L60-L88 */
67 : static fd_loader_v4_state_t const *
68 : check_program_account( fd_exec_instr_ctx_t * instr_ctx,
69 : fd_borrowed_account_t const * program,
70 : fd_pubkey_t const * authority_address,
71 0 : int * err ) {
72 0 : *err = FD_EXECUTOR_INSTR_SUCCESS;
73 :
74 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L66-L69 */
75 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( program ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
76 0 : fd_log_collector_msg_literal( instr_ctx, "Program not owned by loader" );
77 0 : *err = FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
78 0 : return NULL;
79 0 : }
80 :
81 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L70 */
82 0 : fd_loader_v4_state_t const * state = fd_loader_v4_get_state( program->acct, err );
83 0 : if( FD_UNLIKELY( *err ) ) {
84 0 : return NULL;
85 0 : }
86 :
87 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L71-L73 */
88 0 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( program ) ) ) {
89 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not writeable" );
90 0 : *err = FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
91 0 : return NULL;
92 0 : }
93 :
94 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L75-L78 */
95 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL ) ) ) {
96 0 : fd_log_collector_msg_literal( instr_ctx, "Authority did not sign" );
97 0 : *err = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
98 0 : return NULL;
99 0 : }
100 :
101 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L79-L82 */
102 0 : if( FD_UNLIKELY( memcmp( &state->authority_address_or_next_version, authority_address, sizeof(fd_pubkey_t) ) ) ) {
103 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect authority provided" );
104 0 : *err = FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
105 0 : return NULL;
106 0 : }
107 :
108 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L83-L86 */
109 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_finalized( state ) ) ) {
110 0 : fd_log_collector_msg_literal( instr_ctx, "Program is finalized" );
111 0 : *err = FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
112 0 : return NULL;
113 0 : }
114 :
115 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L87 */
116 0 : return state;
117 0 : }
118 :
119 : /* `process_instruction_write()` writes ELF data into an undeployed program account.
120 : This could either be a program which was already deployed and then retracted, or
121 : a new program which is uninitialized with enough space allocated from a call to `set_program_length`.
122 :
123 : Accounts:
124 : 0. Program account (writable)
125 : 1. Authority account (signer)
126 :
127 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L90-L122 */
128 : static int
129 : fd_loader_v4_program_instruction_write( fd_exec_instr_ctx_t * instr_ctx,
130 0 : fd_loader_v4_program_instruction_write_t const * write ) {
131 0 : int err;
132 0 : uint offset = write->offset;
133 0 : uchar const * bytes = write->bytes;
134 0 : ulong bytes_len = write->bytes_len;
135 :
136 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L98 */
137 0 : fd_guarded_borrowed_account_t program;
138 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
139 :
140 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L99-L101 */
141 0 : fd_pubkey_t const * authority_address = NULL;
142 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
143 0 : if( FD_UNLIKELY( err ) ) {
144 0 : return err;
145 0 : }
146 :
147 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L102-L107 */
148 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
149 0 : if( FD_UNLIKELY( err ) ) {
150 0 : return err;
151 0 : }
152 :
153 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L108-L111 */
154 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( state ) ) ) {
155 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not retracted" );
156 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
157 0 : }
158 :
159 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L112 */
160 0 : ulong destination_offset = fd_ulong_sat_add( offset, LOADER_V4_PROGRAM_DATA_OFFSET );
161 :
162 : /* Break up the chained operations into separate lines...
163 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L113-L114 */
164 0 : uchar * data = NULL;
165 0 : ulong dlen = 0UL;
166 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
167 0 : if( FD_UNLIKELY( err ) ) {
168 0 : return err;
169 0 : }
170 :
171 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L115-L118 */
172 0 : if( FD_UNLIKELY( fd_ulong_sat_add( destination_offset, bytes_len )>dlen ) ) {
173 0 : fd_log_collector_msg_literal( instr_ctx, "Write out of bounds" );
174 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
175 0 : }
176 :
177 0 : if( FD_LIKELY( bytes_len>0 ) ) {
178 0 : fd_memcpy( data+destination_offset, bytes, bytes_len );
179 0 : }
180 :
181 0 : return FD_EXECUTOR_INSTR_SUCCESS;
182 0 : }
183 :
184 : /* `fd_loader_v4_program_instruction_copy()` is similar to `fd_loader_v4_program_instruction_write()`, except
185 : it copies ELF data from a source program account instead of from instruction data. This is useful
186 : for migrating existing v1/v2/v3 programs to v4.
187 :
188 : Accounts:
189 : 0. Program account (writable)
190 : 1. Authority account (signer)
191 : 2. Source program account
192 :
193 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L124-L181
194 : */
195 : static int
196 : fd_loader_v4_program_instruction_copy( fd_exec_instr_ctx_t * instr_ctx,
197 0 : fd_loader_v4_program_instruction_copy_t const * copy ) {
198 0 : int err;
199 0 : uint destination_offset = copy->destination_offset;
200 0 : uint source_offset = copy->source_offset;
201 0 : uint length = copy->length;
202 :
203 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L133 */
204 0 : fd_guarded_borrowed_account_t program;
205 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
206 :
207 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L134-L136 */
208 0 : fd_pubkey_t const * authority_address = NULL;
209 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
210 0 : if( FD_UNLIKELY( err ) ) {
211 0 : return err;
212 0 : }
213 :
214 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L137-L138 */
215 0 : fd_guarded_borrowed_account_t source_program;
216 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &source_program );
217 :
218 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L139-L144 */
219 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
220 0 : if( FD_UNLIKELY( err ) ) {
221 0 : return err;
222 0 : }
223 :
224 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L145-L148 */
225 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( state ) ) ) {
226 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not retracted" );
227 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
228 0 : }
229 :
230 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L149-L162 */
231 0 : fd_pubkey_t const * source_owner = fd_borrowed_account_get_owner( &source_program );
232 0 : if( !memcmp( source_owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
233 0 : source_offset = fd_uint_sat_add( source_offset, (uint)LOADER_V4_PROGRAM_DATA_OFFSET );
234 0 : } else if( !memcmp( source_owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
235 0 : source_offset = fd_uint_sat_add( source_offset, (uint)PROGRAMDATA_METADATA_SIZE );
236 0 : } else if( FD_UNLIKELY( memcmp( source_owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) &&
237 0 : memcmp( source_owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
238 0 : fd_log_collector_msg_literal( instr_ctx, "Source is not a program" );
239 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
240 0 : }
241 :
242 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L163-L169 */
243 0 : uchar const * data = fd_borrowed_account_get_data( &source_program );
244 0 : if( FD_UNLIKELY( fd_uint_sat_add( source_offset, length )>(uint)fd_borrowed_account_get_data_len( &source_program ) ) ) {
245 0 : fd_log_collector_msg_literal( instr_ctx, "Read out of bounds" );
246 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
247 0 : }
248 :
249 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L170-L179 */
250 0 : uchar * data_mut = NULL;
251 0 : ulong dlen = 0UL;
252 0 : err = fd_borrowed_account_get_data_mut( &program, &data_mut, &dlen );
253 0 : if( FD_UNLIKELY( err ) ) {
254 0 : return err;
255 0 : }
256 :
257 0 : destination_offset = fd_uint_sat_add( destination_offset, LOADER_V4_PROGRAM_DATA_OFFSET );
258 0 : if( FD_UNLIKELY( fd_uint_sat_add( destination_offset, length )>(uint)dlen ) ) {
259 0 : fd_log_collector_msg_literal( instr_ctx, "Write out of bounds" );
260 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
261 0 : }
262 :
263 0 : fd_memcpy( data_mut+destination_offset, data+source_offset, length );
264 0 : return FD_EXECUTOR_INSTR_SUCCESS;
265 0 : }
266 :
267 : /* `fd_loader_v4_program_instruction_set_program_length()` resizes an undeployed program account to the specified size.
268 : Initialization is taken care of when the program account size is first increased. Decreasing the size
269 : to 0 will close the program account. This instruction does NOT require a native CPI into the system program
270 : to resize the account.
271 :
272 : Other notes:
273 : - The executable status is set to true here on initialization.
274 :
275 : Accounts:
276 : 0. Program account (writable)
277 : 1. Authority account (signer)
278 : 2. Recipient account to receive excess lamports from rent when decreasing account size (writable)
279 : - This account is only required when the program account size is being decreased (new_size < program dlen).
280 :
281 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L183-L274 */
282 : static int
283 : fd_loader_v4_program_instruction_set_program_length( fd_exec_instr_ctx_t * instr_ctx,
284 0 : fd_loader_v4_program_instruction_set_program_length_t const * set_program_length ) {
285 0 : int err;
286 0 : uint new_size = set_program_length->new_size;
287 :
288 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L190 */
289 0 : fd_guarded_borrowed_account_t program;
290 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
291 :
292 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L191-L193 */
293 0 : fd_pubkey_t const * authority_address = NULL;
294 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
295 0 : if( FD_UNLIKELY( err ) ) {
296 0 : return err;
297 0 : }
298 :
299 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L194-L195 */
300 0 : uchar is_initialization = !!( fd_borrowed_account_get_data_len( &program )<LOADER_V4_PROGRAM_DATA_OFFSET );
301 :
302 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L196-L220 */
303 0 : if( is_initialization ) {
304 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L197-L200 */
305 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &program ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
306 0 : fd_log_collector_msg_literal( instr_ctx, "Program not owned by loader" );
307 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
308 0 : }
309 :
310 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L201-L204 */
311 0 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &program ) ) ) {
312 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not writeable" );
313 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
314 0 : }
315 :
316 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L205-L208 */
317 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL ) ) ) {
318 0 : fd_log_collector_msg_literal( instr_ctx, "Authority did not sign" );
319 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
320 0 : }
321 0 : } else {
322 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L210-L215 */
323 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
324 0 : if( FD_UNLIKELY( err ) ) {
325 0 : return err;
326 0 : }
327 :
328 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L216-L219 */
329 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( state ) ) ) {
330 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not retracted" );
331 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
332 0 : }
333 0 : }
334 :
335 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L221-L227 */
336 0 : fd_rent_t rent_;
337 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
338 0 : if( FD_UNLIKELY( !rent ) ) {
339 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
340 0 : }
341 :
342 0 : ulong new_program_dlen = fd_ulong_sat_add( LOADER_V4_PROGRAM_DATA_OFFSET, new_size );
343 0 : ulong required_lamports = ( new_size==0UL ) ?
344 0 : 0UL :
345 0 : fd_ulong_max( fd_rent_exempt_minimum_balance( rent, new_program_dlen ),
346 0 : 1UL );
347 :
348 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L228-L258 */
349 0 : ulong program_lamports = fd_borrowed_account_get_lamports( &program );
350 0 : if( FD_UNLIKELY( program_lamports<required_lamports ) ) {
351 :
352 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L229-L236 */
353 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
354 0 : "Insufficient lamports, %lu are required", required_lamports );
355 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
356 :
357 0 : } else if( FD_LIKELY( program_lamports>required_lamports ) ) {
358 :
359 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L238-L240 */
360 0 : fd_guarded_borrowed_account_t recipient;
361 0 : int err = fd_exec_instr_ctx_try_borrow_instr_account( instr_ctx, 2UL, &recipient );
362 :
363 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L241-L245 */
364 0 : if( FD_LIKELY( !err ) ) {
365 0 : if( FD_UNLIKELY( !instr_ctx->instr->accounts[ 2UL ].is_writable ) ) {
366 0 : fd_log_collector_msg_literal( instr_ctx, "Recipient is not writeable" );
367 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
368 0 : }
369 :
370 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L246-L248 */
371 0 : ulong lamports_to_receive = fd_ulong_sat_sub( program_lamports, required_lamports );
372 0 : err = fd_borrowed_account_checked_sub_lamports( &program, lamports_to_receive );
373 0 : if( FD_UNLIKELY( err ) ) {
374 0 : return err;
375 0 : }
376 0 : err = fd_borrowed_account_checked_add_lamports( &recipient, lamports_to_receive );
377 0 : if( FD_UNLIKELY( err ) ) {
378 0 : return err;
379 0 : }
380 0 : } else if( FD_UNLIKELY( new_size==0U ) ) {
381 0 : fd_log_collector_msg_literal( instr_ctx, "Closing a program requires a recipient account" );
382 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
383 0 : }
384 :
385 : /* recipient is dropped when it goes out of scope */
386 0 : } /* no-op for program lamports == required lamports */
387 :
388 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L259-L272 */
389 0 : if( new_size==0UL ) {
390 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L260 */
391 0 : err = fd_borrowed_account_set_data_length( &program, 0UL );
392 0 : if( FD_UNLIKELY( err ) ) {
393 0 : return err;
394 0 : }
395 0 : } else {
396 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L262-L264 */
397 0 : err = fd_borrowed_account_set_data_length( &program, new_program_dlen );
398 0 : if( FD_UNLIKELY( err ) ) {
399 0 : return err;
400 0 : }
401 :
402 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L265-L271 */
403 0 : if( is_initialization ) {
404 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L266 */
405 0 : err = fd_borrowed_account_set_executable( &program, 1 );
406 0 : if( FD_UNLIKELY( err ) ) {
407 0 : return err;
408 0 : }
409 :
410 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L267 */
411 0 : uchar * data = NULL;
412 0 : ulong dlen = 0UL;
413 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
414 0 : if( FD_UNLIKELY( err ) ) {
415 0 : return err;
416 0 : }
417 :
418 0 : fd_loader_v4_state_t * state = fd_loader_v4_get_state_mut( data, dlen, &err );
419 0 : if( FD_UNLIKELY( err ) ) {
420 0 : return err;
421 0 : }
422 :
423 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L268-L270 */
424 0 : state->slot = 0UL;
425 0 : state->status = FD_LOADER_V4_STATUS_ENUM_RETRACTED;
426 0 : state->authority_address_or_next_version = *authority_address;
427 0 : }
428 0 : }
429 :
430 : /* program is dropped when it goes out of scope */
431 0 : return FD_EXECUTOR_INSTR_SUCCESS;
432 0 : }
433 :
434 : /* `process_instruction_deploy()` will verify the ELF bytes of a program account in a `Retracted`
435 : state and deploy it if successful, making it ready for use. Optionally, a source buffer account
436 : may be provided to copy the ELF bytes from. In this case, the program data is overwritten by the
437 : data in the buffer account, and the buffer account is closed with some of its lamports transferred
438 : to the program account if needed to meet the minimum rent exemption balance. If a source program
439 : is provided, the source program's programdata is copied into the target program, and the source
440 : program is closed afterwards.
441 :
442 : Other notes:
443 : - Newly deployed programs may not be retracted/redeployed within `LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS` (750) slots.
444 :
445 : Accounts:
446 : 0. Program account (writable)
447 : 1. Authority account (signer)
448 :
449 : https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L274-L322 */
450 : static int
451 0 : fd_loader_v4_program_instruction_deploy( fd_exec_instr_ctx_t * instr_ctx ) {
452 0 : int err;
453 :
454 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L278 */
455 0 : fd_guarded_borrowed_account_t program;
456 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
457 :
458 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L279-L281 */
459 0 : fd_pubkey_t const * authority_address = NULL;
460 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
461 0 : if( FD_UNLIKELY( err ) ) {
462 0 : return err;
463 0 : }
464 :
465 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L282-L287 */
466 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
467 0 : if( FD_UNLIKELY( err ) ) {
468 0 : return err;
469 0 : }
470 :
471 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L288 */
472 0 : fd_sol_sysvar_clock_t clock_;
473 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
474 0 : if( FD_UNLIKELY( !clock ) ) {
475 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
476 0 : }
477 0 : ulong current_slot = clock->slot;
478 :
479 : /* `state->slot == 0` indicates that the program hasn't been deployed
480 : yet, so a cooldown check is not needed. Otherwise, a cooldown of 1
481 : slot is applied before being able to redeploy or retract the
482 : program.
483 :
484 : https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L293-L299 */
485 0 : if( FD_UNLIKELY( state->slot!=0UL && fd_ulong_sat_add( state->slot, LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS )>current_slot ) ) {
486 0 : fd_log_collector_msg_literal( instr_ctx, "Program was deployed recently, cooldown still in effect" );
487 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
488 0 : }
489 :
490 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L300-L303 */
491 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_retracted( state ) ) ) {
492 0 : fd_log_collector_msg_literal( instr_ctx, "Destination program is not retracted" );
493 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
494 0 : }
495 :
496 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L305-L308 */
497 0 : ulong buffer_dlen = fd_borrowed_account_get_data_len( &program );
498 0 : if( FD_UNLIKELY( buffer_dlen<LOADER_V4_PROGRAM_DATA_OFFSET ) ) {
499 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
500 0 : }
501 0 : uchar const * programdata = fd_borrowed_account_get_data( &program ) + LOADER_V4_PROGRAM_DATA_OFFSET;
502 :
503 : /* Our program cache is fundamentally different from Agave's. Here, they would perform verifications and
504 : add the program to their cache, but we only perform verifications now and defer cache population to the
505 : end of the slot. Since programs cannot be invoked until the next slot anyways, doing this is okay.
506 :
507 : https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L309-L316 */
508 0 : err = fd_deploy_program( instr_ctx, program.acct->pubkey, programdata, buffer_dlen - LOADER_V4_PROGRAM_DATA_OFFSET, instr_ctx->txn_ctx->spad );
509 0 : if( FD_UNLIKELY( err ) ) {
510 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
511 0 : }
512 :
513 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L318-L321 */
514 0 : uchar * data = NULL;
515 0 : ulong dlen = 0UL;
516 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
517 0 : if( FD_UNLIKELY( err ) ) {
518 0 : return err;
519 0 : }
520 :
521 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
522 0 : if( FD_UNLIKELY( err ) ) {
523 0 : return err;
524 0 : }
525 :
526 0 : mut_state->slot = current_slot;
527 0 : mut_state->status = FD_LOADER_V4_STATUS_ENUM_DELOYED;
528 :
529 0 : return FD_EXECUTOR_INSTR_SUCCESS;
530 0 : }
531 :
532 : /* `process_instruction_retract()` retracts a currently deployed program, making it writable
533 : and uninvokable. After a program is retracted, users can write and truncate data freely,
534 : allowing them to upgrade or close the program account.
535 :
536 : Other notes:
537 : - Newly deployed programs may not be retracted/redeployed within 1 slot.
538 : - When a program is retracted, the executable flag is NOT changed.
539 :
540 : Accounts:
541 : 0. Program account (writable)
542 : 1. Authority account (signer)
543 :
544 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L353-L393 */
545 : static int
546 0 : fd_loader_v4_program_instruction_retract( fd_exec_instr_ctx_t * instr_ctx ) {
547 0 : int err;
548 :
549 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L357 */
550 0 : fd_guarded_borrowed_account_t program;
551 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
552 :
553 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L359-L361 */
554 0 : fd_pubkey_t const * authority_address = NULL;
555 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
556 0 : if( FD_UNLIKELY( err ) ) {
557 0 : return err;
558 0 : }
559 :
560 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L362-L367 */
561 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
562 0 : if( FD_UNLIKELY( err ) ) {
563 0 : return err;
564 0 : }
565 :
566 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L368 */
567 0 : fd_sol_sysvar_clock_t clock_;
568 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
569 0 : if( FD_UNLIKELY( !clock ) ) {
570 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
571 0 : }
572 0 : ulong current_slot = clock->slot;
573 :
574 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L369-L375 */
575 0 : if( FD_UNLIKELY( fd_ulong_sat_add( state->slot, LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS )>current_slot ) ) {
576 0 : fd_log_collector_msg_literal( instr_ctx, "Program was deployed recently, cooldown still in effect" );
577 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
578 0 : }
579 :
580 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L376-L379 */
581 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_deployed( state ) ) ) {
582 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
583 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
584 0 : }
585 :
586 : /* No need to update program cache - see note in `deploy` processor.
587 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L380-L391 */
588 0 : uchar * data = NULL;
589 0 : ulong dlen = 0UL;
590 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
591 0 : if( FD_UNLIKELY( err ) ) {
592 0 : return err;
593 0 : }
594 :
595 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
596 0 : if( FD_UNLIKELY( err ) ) {
597 0 : return err;
598 0 : }
599 :
600 0 : mut_state->status = FD_LOADER_V4_STATUS_ENUM_RETRACTED;
601 :
602 0 : return FD_EXECUTOR_INSTR_SUCCESS;
603 0 : }
604 :
605 : /* `process_instruction_transfer_authority()` transfers the authority of a program account.
606 :
607 : Accounts:
608 : 0. Program account (writable)
609 : 1. Current authority (signer)
610 : 2. New authority (signer)
611 :
612 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L395-L425 */
613 : static int
614 0 : fd_loader_v4_program_instruction_transfer_authority( fd_exec_instr_ctx_t * instr_ctx ) {
615 0 : int err;
616 :
617 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L401 */
618 0 : fd_guarded_borrowed_account_t program;
619 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
620 :
621 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L402-L404 */
622 0 : fd_pubkey_t const * authority_address = NULL;
623 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
624 0 : if( FD_UNLIKELY( err ) ) {
625 0 : return err;
626 0 : }
627 :
628 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L405-L407 */
629 0 : fd_pubkey_t const * new_authority_address = NULL;
630 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &new_authority_address );
631 0 : if( FD_UNLIKELY( err ) ) {
632 0 : return err;
633 0 : }
634 :
635 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L408-L413 */
636 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
637 0 : if( FD_UNLIKELY( err ) ) {
638 0 : return err;
639 0 : }
640 :
641 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L414-L417 */
642 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL ) ) ) {
643 0 : fd_log_collector_msg_literal( instr_ctx, "New authority did not sign" );
644 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
645 0 : }
646 :
647 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L418-L421 */
648 0 : if( FD_UNLIKELY( !memcmp( state->authority_address_or_next_version.key, new_authority_address, sizeof(fd_pubkey_t) ) ) ) {
649 0 : fd_log_collector_msg_literal( instr_ctx, "No change" );
650 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
651 0 : }
652 :
653 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L422-L424 */
654 0 : uchar * data = NULL;
655 0 : ulong dlen = 0UL;
656 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
657 0 : if( FD_UNLIKELY( err ) ) {
658 0 : return err;
659 0 : }
660 :
661 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
662 0 : if( FD_UNLIKELY( err ) ) {
663 0 : return err;
664 0 : }
665 :
666 0 : mut_state->authority_address_or_next_version = *new_authority_address;
667 :
668 0 : return FD_EXECUTOR_INSTR_SUCCESS;
669 0 : }
670 :
671 : /* `process_instruction_finalize()` finalizes the program account, rendering it immutable.
672 :
673 : Other notes:
674 : - Users must specify a "next version" which, from my inspection, serves no functional purpose besides showing up
675 : as extra information on a block explorer. The next version can be itself.
676 : - The next version must be a program that...
677 : - Is not finalized
678 : - Has the same authority as the current program
679 :
680 : Accounts:
681 : 0. Program account (writable)
682 : 1. Authority account (signer)
683 : 2. Next version of the program
684 :
685 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L427-L470 */
686 : static int
687 0 : fd_loader_v4_program_instruction_finalize( fd_exec_instr_ctx_t * instr_ctx ) {
688 0 : int err;
689 :
690 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L433 */
691 0 : fd_guarded_borrowed_account_t program;
692 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
693 :
694 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L434-L436 */
695 0 : fd_pubkey_t const * authority_address = NULL;
696 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
697 0 : if( FD_UNLIKELY( err ) ) {
698 0 : return err;
699 0 : }
700 :
701 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L437-L442 */
702 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
703 0 : if( FD_UNLIKELY( err ) ) {
704 0 : return err;
705 0 : }
706 :
707 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L443-L446 */
708 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_deployed( state ) ) ) {
709 0 : fd_log_collector_msg_literal( instr_ctx, "Program must be deployed to be finalized" );
710 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
711 0 : }
712 :
713 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L447 */
714 0 : fd_borrowed_account_drop( &program );
715 :
716 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L448-L449 */
717 0 : fd_guarded_borrowed_account_t next_version;
718 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK(instr_ctx, 2UL, &next_version );
719 :
720 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L450-L453 */
721 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &next_version ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
722 0 : fd_log_collector_msg_literal( instr_ctx, "Next version is not owned by loader" );
723 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
724 0 : }
725 :
726 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L454 */
727 0 : fd_loader_v4_state_t const * state_of_next_version = fd_loader_v4_get_state( next_version.acct, &err );
728 0 : if( FD_UNLIKELY( err ) ) {
729 0 : return err;
730 0 : }
731 :
732 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L455-L458 */
733 0 : if( FD_UNLIKELY( memcmp( state_of_next_version->authority_address_or_next_version.key, authority_address, sizeof(fd_pubkey_t) ) ) ) {
734 0 : fd_log_collector_msg_literal( instr_ctx, "Next version has different authority" );
735 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
736 0 : }
737 :
738 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L459-L462 */
739 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_finalized( state_of_next_version ) ) ) {
740 0 : fd_log_collector_msg_literal( instr_ctx, "Next version is finalized" );
741 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
742 0 : }
743 :
744 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L463 */
745 0 : fd_pubkey_t * address_of_next_version = next_version.acct->pubkey;
746 :
747 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L464 */
748 0 : fd_borrowed_account_drop( &next_version );
749 :
750 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L465 */
751 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
752 :
753 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L466-L469 */
754 0 : uchar * data = NULL;
755 0 : ulong dlen = 0UL;
756 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
757 0 : if( FD_UNLIKELY( err ) ) {
758 0 : return err;
759 0 : }
760 :
761 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
762 0 : if( FD_UNLIKELY( err ) ) {
763 0 : return err;
764 0 : }
765 :
766 0 : mut_state->authority_address_or_next_version = *address_of_next_version;
767 0 : mut_state->status = FD_LOADER_V4_STATUS_ENUM_FINALIZED;
768 :
769 0 : return FD_EXECUTOR_INSTR_SUCCESS;
770 0 : }
771 :
772 : /* `process_instruction_inner()`, the entrypoint for all loader v4 instruction invocations +
773 : any loader v4-owned programs.
774 :
775 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L487-L549 */
776 : int
777 0 : fd_loader_v4_program_execute( fd_exec_instr_ctx_t * instr_ctx ) {
778 0 : if( FD_UNLIKELY( !FD_FEATURE_ACTIVE_BANK( instr_ctx->txn_ctx->bank, enable_loader_v4 ) ) ) {
779 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
780 0 : }
781 :
782 0 : FD_SPAD_FRAME_BEGIN( instr_ctx->txn_ctx->spad ) {
783 0 : int rc = FD_EXECUTOR_INSTR_SUCCESS;
784 :
785 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L494 */
786 0 : fd_pubkey_t const * program_id = NULL;
787 0 : rc = fd_exec_instr_ctx_get_last_program_key( instr_ctx, &program_id );
788 0 : if( FD_UNLIKELY( rc ) ) {
789 0 : return rc;
790 0 : }
791 :
792 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L495-L519 */
793 0 : if( !memcmp( program_id, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
794 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L496 */
795 0 : FD_EXEC_CU_UPDATE( instr_ctx, LOADER_V4_DEFAULT_COMPUTE_UNITS );
796 :
797 : /* Note the dataend is capped at a 1232 bytes offset to mirror the semantics of `limited_deserialize`.
798 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L497 */
799 :
800 0 : fd_loader_v4_program_instruction_t * instruction = fd_bincode_decode_spad(
801 0 : loader_v4_program_instruction,
802 0 : instr_ctx->txn_ctx->spad,
803 0 : instr_ctx->instr->data,
804 0 : instr_ctx->instr->data_sz > FD_TXN_MTU ? FD_TXN_MTU : instr_ctx->instr->data_sz,
805 0 : NULL );
806 0 : if( FD_UNLIKELY( !instruction ) ) {
807 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
808 0 : }
809 :
810 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L497-L518 */
811 0 : switch( instruction->discriminant ) {
812 0 : case fd_loader_v4_program_instruction_enum_write: {
813 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L498-L500 */
814 0 : rc = fd_loader_v4_program_instruction_write( instr_ctx, &instruction->inner.write );
815 0 : break;
816 0 : }
817 0 : case fd_loader_v4_program_instruction_enum_copy: {
818 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L501-L507 */
819 0 : rc = fd_loader_v4_program_instruction_copy( instr_ctx, &instruction->inner.copy );
820 0 : break;
821 0 : }
822 0 : case fd_loader_v4_program_instruction_enum_set_program_length: {
823 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L508-L510 */
824 0 : rc = fd_loader_v4_program_instruction_set_program_length( instr_ctx, &instruction->inner.set_program_length );
825 0 : break;
826 0 : }
827 0 : case fd_loader_v4_program_instruction_enum_deploy: {
828 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L511 */
829 0 : rc = fd_loader_v4_program_instruction_deploy( instr_ctx );
830 0 : break;
831 0 : }
832 0 : case fd_loader_v4_program_instruction_enum_retract: {
833 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L512 */
834 0 : rc = fd_loader_v4_program_instruction_retract( instr_ctx );
835 0 : break;
836 0 : }
837 0 : case fd_loader_v4_program_instruction_enum_transfer_authority: {
838 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L513-L515 */
839 0 : rc = fd_loader_v4_program_instruction_transfer_authority( instr_ctx );
840 0 : break;
841 0 : }
842 0 : case fd_loader_v4_program_instruction_enum_finalize: {
843 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L516 */
844 0 : rc = fd_loader_v4_program_instruction_finalize( instr_ctx );
845 0 : break;
846 0 : }
847 0 : }
848 0 : } else {
849 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L520 */
850 0 : fd_guarded_borrowed_account_t program;
851 0 : rc = fd_exec_instr_ctx_try_borrow_last_program_account( instr_ctx, &program );
852 0 : if( FD_UNLIKELY( rc ) ) {
853 0 : return rc;
854 0 : }
855 :
856 : /* See note in `fd_bpf_loader_program_execute()` as to why we must tie the cache into consensus :(
857 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L522-L528 */
858 0 : fd_program_cache_entry_t const * cache_entry = NULL;
859 0 : if( FD_UNLIKELY( fd_program_cache_load_entry( instr_ctx->txn_ctx->funk,
860 0 : instr_ctx->txn_ctx->funk_txn,
861 0 : program_id,
862 0 : &cache_entry )!=0 ) ) {
863 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not cached" );
864 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
865 0 : }
866 :
867 : /* The program may be in the cache but could have failed verification in the current epoch. */
868 0 : if( FD_UNLIKELY( cache_entry->failed_verification ) ) {
869 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
870 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
871 0 : }
872 :
873 : /* After the program is deployed, we wait a slot before adding it to our program cache. Agave, on the other hand,
874 : updates their program cache after every transaction. Because of this, for a program that was deployed in the
875 : current slot, Agave would log "Program is not deployed", while we would log "Program is not cached" since
876 : the program is not in the cache yet. The same thing holds for very old programs that fail ELF / VM validation
877 : checks and are thus non-invokable - if this program was invoked, Agave would keep it in their program cache and label
878 : it as "FailedVerification", while we would not include it at all.
879 :
880 : Because of the difference in our caching behavior, we need to perform checks that will filter out every single program
881 : from execution that Agave would. In Agave's `load_program_accounts()` function, they filter any retracted programs ("Closed")
882 : and mark any programs deployed in the current slot as "DelayedVisibility". Any programs that fail verification will also
883 : not be in the cache anyways. */
884 :
885 0 : fd_loader_v4_state_t const * state = fd_loader_v4_get_state( program.acct, &rc );
886 0 : if( FD_UNLIKELY( rc ) ) {
887 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
888 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
889 0 : }
890 :
891 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_retracted( state ) ) ) {
892 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
893 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
894 0 : }
895 :
896 : /* Handle `DelayedVisibility` case */
897 0 : if( FD_UNLIKELY( state->slot>=instr_ctx->txn_ctx->slot ) ) {
898 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
899 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
900 0 : }
901 :
902 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L531 */
903 0 : fd_borrowed_account_drop( &program );
904 :
905 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L542 */
906 0 : rc = fd_bpf_execute( instr_ctx, cache_entry, 0 );
907 0 : }
908 :
909 0 : return rc;
910 0 : } FD_SPAD_FRAME_END;
911 0 : }
|