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