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 0 : fd_loader_v4_status_is_retracted( fd_loader_v4_state_t const * state ) {
28 0 : return state->status==FD_LOADER_V4_STATUS_ENUM_RETRACTED;
29 0 : }
30 :
31 : FD_FN_PURE uchar
32 0 : fd_loader_v4_status_is_finalized( fd_loader_v4_state_t const * state ) {
33 0 : return state->status==FD_LOADER_V4_STATUS_ENUM_FINALIZED;
34 0 : }
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 0 : int * err ) {
70 0 : *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 0 : 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 0 : return fd_type_pun_const( data );
79 0 : }
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 0 : int * err ) {
90 0 : *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 0 : 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 0 : void const * data = fd_borrowed_account_get_data ( program );
101 0 : ulong data_sz = fd_borrowed_account_get_data_len( program );
102 0 : fd_loader_v4_state_t const * state = fd_loader_v4_get_state( data, data_sz, err );
103 0 : 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 0 : 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 0 : 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 0 : 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 0 : 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 0 : return state;
141 0 : }
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 0 : fd_loader_v4_program_instruction_set_program_length_t const * set_program_length ) {
309 0 : int err;
310 0 : 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 0 : fd_guarded_borrowed_account_t program = {0};
314 0 : 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 0 : fd_pubkey_t const * authority_address = NULL;
318 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
319 0 : 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 0 : 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 0 : 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 0 : } else {
348 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L210-L215 */
349 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
350 0 : 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 0 : 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 0 : }
360 :
361 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L221-L227 */
362 0 : fd_rent_t rent_;
363 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
364 0 : if( FD_UNLIKELY( !rent ) ) {
365 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
366 0 : }
367 :
368 0 : ulong new_program_dlen = fd_ulong_sat_add( LOADER_V4_PROGRAM_DATA_OFFSET, new_size );
369 0 : ulong required_lamports = ( new_size==0UL ) ?
370 0 : 0UL :
371 0 : 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 0 : ulong program_lamports = fd_borrowed_account_get_lamports( &program );
376 0 : 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 0 : } 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 0 : fd_guarded_borrowed_account_t recipient = {0};
387 0 : 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 0 : if( FD_LIKELY( !err ) ) {
391 0 : 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 0 : ulong lamports_to_receive = fd_ulong_sat_sub( program_lamports, required_lamports );
398 0 : err = fd_borrowed_account_checked_sub_lamports( &program, lamports_to_receive );
399 0 : if( FD_UNLIKELY( err ) ) {
400 0 : return err;
401 0 : }
402 0 : err = fd_borrowed_account_checked_add_lamports( &recipient, lamports_to_receive );
403 0 : if( FD_UNLIKELY( err ) ) {
404 0 : return err;
405 0 : }
406 0 : } 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 0 : } /* 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 0 : if( new_size==0UL ) {
416 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L260 */
417 0 : err = fd_borrowed_account_set_data_length( &program, 0UL );
418 0 : if( FD_UNLIKELY( err ) ) {
419 0 : return err;
420 0 : }
421 0 : } 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 0 : return FD_EXECUTOR_INSTR_SUCCESS;
458 0 : }
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 : /* Our program cache is fundamentally different from Agave's. Here,
530 : they would perform verifications and add the program to their
531 : cache, but we only perform verifications now and defer cache
532 : population to the end of the slot. Since programs cannot be
533 : invoked until the next slot anyways, doing this is okay.
534 :
535 : https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L309-L316 */
536 0 : err = fd_deploy_program( instr_ctx, program.pubkey, programdata, buffer_dlen - LOADER_V4_PROGRAM_DATA_OFFSET );
537 0 : if( FD_UNLIKELY( err ) ) {
538 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
539 0 : }
540 :
541 : /* https://github.com/anza-xyz/agave/blob/v2.2.13/programs/loader-v4/src/lib.rs#L318-L321 */
542 0 : uchar * data = NULL;
543 0 : ulong dlen = 0UL;
544 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
545 0 : if( FD_UNLIKELY( err ) ) {
546 0 : return err;
547 0 : }
548 :
549 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
550 0 : if( FD_UNLIKELY( err ) ) {
551 0 : return err;
552 0 : }
553 :
554 0 : mut_state->slot = current_slot;
555 0 : mut_state->status = FD_LOADER_V4_STATUS_ENUM_DELOYED;
556 :
557 0 : return FD_EXECUTOR_INSTR_SUCCESS;
558 0 : }
559 :
560 : /* `process_instruction_retract()` retracts a currently deployed program, making it writable
561 : and uninvokable. After a program is retracted, users can write and truncate data freely,
562 : allowing them to upgrade or close the program account.
563 :
564 : Other notes:
565 : - Newly deployed programs may not be retracted/redeployed within 1 slot.
566 : - When a program is retracted, the executable flag is NOT changed.
567 :
568 : Accounts:
569 : 0. Program account (writable)
570 : 1. Authority account (signer)
571 :
572 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L353-L393 */
573 : static int
574 0 : fd_loader_v4_program_instruction_retract( fd_exec_instr_ctx_t * instr_ctx ) {
575 0 : int err;
576 :
577 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L357 */
578 0 : fd_guarded_borrowed_account_t program = {0};
579 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
580 :
581 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L359-L361 */
582 0 : fd_pubkey_t const * authority_address = NULL;
583 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
584 0 : if( FD_UNLIKELY( err ) ) {
585 0 : return err;
586 0 : }
587 :
588 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L362-L367 */
589 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
590 0 : if( FD_UNLIKELY( err ) ) {
591 0 : return err;
592 0 : }
593 :
594 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L368 */
595 0 : fd_sol_sysvar_clock_t clock_;
596 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
597 0 : if( FD_UNLIKELY( !clock ) ) {
598 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
599 0 : }
600 0 : ulong current_slot = clock->slot;
601 :
602 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L369-L375 */
603 0 : if( FD_UNLIKELY( fd_ulong_sat_add( state->slot, LOADER_V4_DEPLOYMENT_COOLDOWN_IN_SLOTS )>current_slot ) ) {
604 0 : fd_log_collector_msg_literal( instr_ctx, "Program was deployed recently, cooldown still in effect" );
605 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
606 0 : }
607 :
608 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L376-L379 */
609 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_deployed( state ) ) ) {
610 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
611 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
612 0 : }
613 :
614 : /* No need to update program cache - see note in `deploy` processor.
615 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L380-L391 */
616 0 : uchar * data = NULL;
617 0 : ulong dlen = 0UL;
618 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
619 0 : if( FD_UNLIKELY( err ) ) {
620 0 : return err;
621 0 : }
622 :
623 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
624 0 : if( FD_UNLIKELY( err ) ) {
625 0 : return err;
626 0 : }
627 :
628 0 : mut_state->status = FD_LOADER_V4_STATUS_ENUM_RETRACTED;
629 :
630 0 : return FD_EXECUTOR_INSTR_SUCCESS;
631 0 : }
632 :
633 : /* `process_instruction_transfer_authority()` transfers the authority of a program account.
634 :
635 : Accounts:
636 : 0. Program account (writable)
637 : 1. Current authority (signer)
638 : 2. New authority (signer)
639 :
640 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L395-L425 */
641 : static int
642 0 : fd_loader_v4_program_instruction_transfer_authority( fd_exec_instr_ctx_t * instr_ctx ) {
643 0 : int err;
644 :
645 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L401 */
646 0 : fd_guarded_borrowed_account_t program = {0};
647 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
648 :
649 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L402-L404 */
650 0 : fd_pubkey_t const * authority_address = NULL;
651 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
652 0 : if( FD_UNLIKELY( err ) ) {
653 0 : return err;
654 0 : }
655 :
656 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L405-L407 */
657 0 : fd_pubkey_t const * new_authority_address = NULL;
658 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &new_authority_address );
659 0 : if( FD_UNLIKELY( err ) ) {
660 0 : return err;
661 0 : }
662 :
663 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L408-L413 */
664 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &err );
665 0 : if( FD_UNLIKELY( err ) ) {
666 0 : return err;
667 0 : }
668 :
669 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L414-L417 */
670 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL, &err ) ) ) {
671 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
672 0 : if( FD_UNLIKELY( !!err ) ) return err;
673 0 : fd_log_collector_msg_literal( instr_ctx, "New authority did not sign" );
674 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
675 0 : }
676 :
677 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L418-L421 */
678 0 : if( FD_UNLIKELY( !memcmp( state->authority_address_or_next_version.key, new_authority_address, sizeof(fd_pubkey_t) ) ) ) {
679 0 : fd_log_collector_msg_literal( instr_ctx, "No change" );
680 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
681 0 : }
682 :
683 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L422-L424 */
684 0 : uchar * data = NULL;
685 0 : ulong dlen = 0UL;
686 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
687 0 : if( FD_UNLIKELY( err ) ) {
688 0 : return err;
689 0 : }
690 :
691 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
692 0 : if( FD_UNLIKELY( err ) ) {
693 0 : return err;
694 0 : }
695 :
696 0 : mut_state->authority_address_or_next_version = *new_authority_address;
697 :
698 0 : return FD_EXECUTOR_INSTR_SUCCESS;
699 0 : }
700 :
701 : /* `process_instruction_finalize()` finalizes the program account,
702 : rendering it immutable.
703 :
704 : Other notes:
705 : - Users must specify a "next version" which, from my inspection,
706 : serves no functional purpose besides showing up
707 : as extra information on a block explorer. The next version can be
708 : itself.
709 : - The next version must be a program that...
710 : - Is not finalized
711 : - Has the same authority as the current program
712 :
713 : Accounts:
714 : 0. Program account (writable)
715 : 1. Authority account (signer)
716 : 2. Next version of the program
717 :
718 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L427-L470 */
719 : static int
720 0 : fd_loader_v4_program_instruction_finalize( fd_exec_instr_ctx_t * instr_ctx ) {
721 0 : int err;
722 :
723 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L433 */
724 0 : fd_guarded_borrowed_account_t program = {0};
725 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
726 :
727 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L434-L436 */
728 0 : fd_pubkey_t const * authority_address = NULL;
729 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_address );
730 0 : if( FD_UNLIKELY( err ) ) {
731 0 : return err;
732 0 : }
733 :
734 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L437-L442 */
735 0 : fd_loader_v4_state_t const * state = check_program_account( instr_ctx, &program, authority_address, &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#L443-L446 */
741 0 : if( FD_UNLIKELY( !fd_loader_v4_status_is_deployed( state ) ) ) {
742 0 : fd_log_collector_msg_literal( instr_ctx, "Program must be deployed to be finalized" );
743 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
744 0 : }
745 :
746 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L447 */
747 0 : fd_borrowed_account_drop( &program );
748 :
749 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L448-L449 */
750 0 : fd_guarded_borrowed_account_t next_version = {0};
751 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK(instr_ctx, 2UL, &next_version );
752 :
753 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L450-L453 */
754 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &next_version ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
755 0 : fd_log_collector_msg_literal( instr_ctx, "Next version is not owned by loader" );
756 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
757 0 : }
758 :
759 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L454 */
760 0 : void const * next_data = fd_borrowed_account_get_data ( &next_version );
761 0 : ulong next_data_sz = fd_borrowed_account_get_data_len( &next_version );
762 0 : fd_loader_v4_state_t const * state_of_next_version = fd_loader_v4_get_state( next_data, next_data_sz, &err );
763 0 : if( FD_UNLIKELY( err ) ) {
764 0 : return err;
765 0 : }
766 :
767 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L455-L458 */
768 0 : if( FD_UNLIKELY( memcmp( state_of_next_version->authority_address_or_next_version.key, authority_address, sizeof(fd_pubkey_t) ) ) ) {
769 0 : fd_log_collector_msg_literal( instr_ctx, "Next version has a different authority" );
770 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
771 0 : }
772 :
773 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L459-L462 */
774 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_finalized( state_of_next_version ) ) ) {
775 0 : fd_log_collector_msg_literal( instr_ctx, "Next version is finalized" );
776 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
777 0 : }
778 :
779 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L463 */
780 0 : fd_pubkey_t const * address_of_next_version = next_version.pubkey;
781 :
782 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L464 */
783 0 : fd_borrowed_account_drop( &next_version );
784 :
785 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L465 */
786 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &program );
787 :
788 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L466-L469 */
789 0 : uchar * data = NULL;
790 0 : ulong dlen = 0UL;
791 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
792 0 : if( FD_UNLIKELY( err ) ) {
793 0 : return err;
794 0 : }
795 :
796 0 : fd_loader_v4_state_t * mut_state = fd_loader_v4_get_state_mut( data, dlen, &err );
797 0 : if( FD_UNLIKELY( err ) ) {
798 0 : return err;
799 0 : }
800 :
801 0 : mut_state->authority_address_or_next_version = *address_of_next_version;
802 0 : mut_state->status = FD_LOADER_V4_STATUS_ENUM_FINALIZED;
803 :
804 0 : return FD_EXECUTOR_INSTR_SUCCESS;
805 0 : }
806 :
807 : /* `process_instruction_inner()`, the entrypoint for all loader v4 instruction invocations +
808 : any loader v4-owned programs.
809 :
810 : https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L487-L549 */
811 : int
812 0 : fd_loader_v4_program_execute( fd_exec_instr_ctx_t * instr_ctx ) {
813 0 : FD_TEST( FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, enable_loader_v4 ) );
814 :
815 0 : int rc = FD_EXECUTOR_INSTR_SUCCESS;
816 :
817 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L494 */
818 0 : fd_pubkey_t const * program_id = NULL;
819 0 : rc = fd_exec_instr_ctx_get_last_program_key( instr_ctx, &program_id );
820 0 : if( FD_UNLIKELY( rc ) ) {
821 0 : return rc;
822 0 : }
823 :
824 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L495-L519 */
825 0 : if( !memcmp( program_id, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
826 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L496 */
827 0 : FD_EXEC_CU_UPDATE( instr_ctx, LOADER_V4_DEFAULT_COMPUTE_UNITS );
828 :
829 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L497 */
830 0 : uchar __attribute__((aligned(FD_BPF_UPGRADEABLE_LOADER_PROGRAM_INSTRUCTION_ALIGN))) instruction_mem[ FD_LOADER_V4_PROGRAM_INSTRUCTION_FOOTPRINT ] = {0};
831 0 : rc = 0;
832 0 : fd_loader_v4_program_instruction_t * instruction = fd_bincode_decode_static_limited_deserialize(
833 0 : loader_v4_program_instruction,
834 0 : instruction_mem,
835 0 : instr_ctx->instr->data,
836 0 : instr_ctx->instr->data_sz,
837 0 : FD_TXN_MTU,
838 0 : &rc );
839 0 : if( FD_UNLIKELY( rc ) ) {
840 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
841 0 : }
842 :
843 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L497-L518 */
844 0 : switch( instruction->discriminant ) {
845 0 : case fd_loader_v4_program_instruction_enum_write: {
846 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L498-L500 */
847 0 : rc = fd_loader_v4_program_instruction_write( instr_ctx, &instruction->inner.write );
848 0 : break;
849 0 : }
850 0 : case fd_loader_v4_program_instruction_enum_copy: {
851 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L501-L507 */
852 0 : rc = fd_loader_v4_program_instruction_copy( instr_ctx, &instruction->inner.copy );
853 0 : break;
854 0 : }
855 0 : case fd_loader_v4_program_instruction_enum_set_program_length: {
856 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L508-L510 */
857 0 : rc = fd_loader_v4_program_instruction_set_program_length( instr_ctx, &instruction->inner.set_program_length );
858 0 : break;
859 0 : }
860 0 : case fd_loader_v4_program_instruction_enum_deploy: {
861 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L511 */
862 0 : rc = fd_loader_v4_program_instruction_deploy( instr_ctx );
863 0 : break;
864 0 : }
865 0 : case fd_loader_v4_program_instruction_enum_retract: {
866 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L512 */
867 0 : rc = fd_loader_v4_program_instruction_retract( instr_ctx );
868 0 : break;
869 0 : }
870 0 : case fd_loader_v4_program_instruction_enum_transfer_authority: {
871 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L513-L515 */
872 0 : rc = fd_loader_v4_program_instruction_transfer_authority( instr_ctx );
873 0 : break;
874 0 : }
875 0 : case fd_loader_v4_program_instruction_enum_finalize: {
876 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L516 */
877 0 : rc = fd_loader_v4_program_instruction_finalize( instr_ctx );
878 0 : break;
879 0 : }
880 0 : }
881 0 : } else {
882 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L520 */
883 0 : fd_guarded_borrowed_account_t program = {0};
884 0 : rc = fd_exec_instr_ctx_try_borrow_last_program_account( instr_ctx, &program );
885 0 : if( FD_UNLIKELY( rc ) ) {
886 0 : return rc;
887 0 : }
888 :
889 : /* Work around differences in program caching behavior between
890 : Fireadncer and Agave here.
891 :
892 : Agave includes load failures due to program metadata (e.g.
893 : "DelayVisibility", "Closed") in their cache. Firedancer
894 : does not create cache entries for these states, instead only
895 : including load results ("FailedVerification", "Loaded").
896 :
897 : Therefore, Firedancer recovers the DelayVisibility and Closed
898 : states on-the-fly before querying cahce. */
899 :
900 0 : void const * prog_data = fd_borrowed_account_get_data ( &program );
901 0 : ulong prog_data_sz = fd_borrowed_account_get_data_len( &program );
902 0 : fd_loader_v4_state_t const * state = fd_loader_v4_get_state( prog_data, prog_data_sz, &rc );
903 0 : if( FD_UNLIKELY( rc ) ) {
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 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_retracted( state ) ) ) {
909 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
910 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
911 0 : }
912 :
913 : /* Handle `DelayedVisibility` case */
914 0 : if( FD_UNLIKELY( state->slot>=fd_bank_slot_get( instr_ctx->bank ) ) ) {
915 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
916 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
917 0 : }
918 :
919 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L522-L528 */
920 0 : fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( instr_ctx->bank ), instr_ctx->bank->data->idx } };
921 0 : fd_prog_load_env_t load_env[1]; fd_prog_load_env_from_bank( load_env, instr_ctx->bank );
922 0 : fd_progcache_rec_t const * cache_entry = fd_progcache_pull( instr_ctx->runtime->progcache,
923 0 : instr_ctx->runtime->accdb,
924 0 : &xid,
925 0 : program_id,
926 0 : load_env );
927 0 : if( FD_UNLIKELY( !cache_entry ) ) {
928 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not cached" );
929 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
930 0 : }
931 :
932 : /* The program may be in the cache but could have failed
933 : verification in the current epoch. */
934 0 : if( FD_UNLIKELY( cache_entry->executable==0 ) ) {
935 0 : fd_log_collector_msg_literal( instr_ctx, "Program is not deployed" );
936 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
937 0 : }
938 :
939 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L531 */
940 0 : fd_borrowed_account_drop( &program );
941 :
942 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/loader-v4/src/lib.rs#L542 */
943 0 : rc = fd_bpf_execute( instr_ctx, cache_entry, 0 );
944 0 : }
945 :
946 0 : return rc;
947 0 : }
|