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