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