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