Line data Source code
1 : #include "fd_bpf_loader_program.h"
2 : #include "fd_system_program.h"
3 :
4 : /* For additional context see https://solana.com/docs/programs/deploying#state-accounts */
5 :
6 : #include "../../progcache/fd_progcache_user.h"
7 : #include "../fd_runtime_helpers.h"
8 : #include "../fd_executor.h"
9 : #include "../tests/fd_dump_pb.h"
10 : #include "../sysvar/fd_sysvar.h"
11 : #include "../fd_pubkey_utils.h"
12 : #include "../fd_borrowed_account.h"
13 : #include "../fd_system_ids.h"
14 : #include "fd_bpf_loader_serialization.h"
15 : #include "fd_builtin_programs.h"
16 : #include "fd_native_cpi.h"
17 :
18 : /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/sdk/program/src/program_error.rs#L290-L335 */
19 : static inline int
20 : program_error_to_instr_error( ulong err,
21 51 : uint * custom_err ) {
22 51 : switch( err ) {
23 0 : case CUSTOM_ZERO:
24 0 : *custom_err = 0;
25 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
26 0 : case INVALID_ARGUMENT:
27 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
28 0 : case INVALID_INSTRUCTION_DATA:
29 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
30 0 : case INVALID_ACCOUNT_DATA:
31 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
32 0 : case ACCOUNT_DATA_TOO_SMALL:
33 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
34 0 : case INSUFFICIENT_FUNDS:
35 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
36 0 : case INCORRECT_PROGRAM_ID:
37 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
38 0 : case MISSING_REQUIRED_SIGNATURES:
39 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
40 0 : case ACCOUNT_ALREADY_INITIALIZED:
41 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
42 0 : case UNINITIALIZED_ACCOUNT:
43 0 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
44 0 : case NOT_ENOUGH_ACCOUNT_KEYS:
45 0 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
46 0 : case ACCOUNT_BORROW_FAILED:
47 0 : return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED;
48 0 : case MAX_SEED_LENGTH_EXCEEDED:
49 0 : return FD_EXECUTOR_INSTR_ERR_MAX_SEED_LENGTH_EXCEEDED;
50 0 : case INVALID_SEEDS:
51 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_SEEDS;
52 0 : case BORSH_IO_ERROR:
53 0 : return FD_EXECUTOR_INSTR_ERR_BORSH_IO_ERROR;
54 0 : case ACCOUNT_NOT_RENT_EXEMPT:
55 0 : return FD_EXECUTOR_INSTR_ERR_ACC_NOT_RENT_EXEMPT;
56 0 : case UNSUPPORTED_SYSVAR:
57 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
58 0 : case ILLEGAL_OWNER:
59 0 : return FD_EXECUTOR_INSTR_ERR_ILLEGAL_OWNER;
60 0 : case MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED:
61 0 : return FD_EXECUTOR_INSTR_ERR_MAX_ACCS_DATA_ALLOCS_EXCEEDED;
62 0 : case INVALID_ACCOUNT_DATA_REALLOC:
63 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
64 0 : case MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED:
65 0 : return FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED;
66 0 : case BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS:
67 0 : return FD_EXECUTOR_INSTR_ERR_BUILTINS_MUST_CONSUME_CUS;
68 0 : case INVALID_ACCOUNT_OWNER:
69 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
70 0 : case ARITHMETIC_OVERFLOW:
71 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
72 0 : case IMMUTABLE:
73 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
74 0 : case INCORRECT_AUTHORITY:
75 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
76 51 : default:
77 51 : if( err>>BUILTIN_BIT_SHIFT == 0 ) {
78 51 : *custom_err = (uint)err;
79 51 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
80 51 : }
81 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ERR;
82 51 : }
83 51 : }
84 :
85 : /* https://github.com/anza-xyz/agave/blob/9b22f28104ec5fd606e4bb39442a7600b38bb671/programs/bpf_loader/src/lib.rs#L216-L229 */
86 : static ulong
87 2469 : calculate_heap_cost( ulong heap_size, ulong heap_cost ) {
88 4938 : #define KIBIBYTE_MUL_PAGES (1024UL * 32UL)
89 2469 : #define KIBIBYTE_MUL_PAGES_SUB_1 (KIBIBYTE_MUL_PAGES - 1UL)
90 :
91 2469 : heap_size = fd_ulong_sat_add( heap_size, KIBIBYTE_MUL_PAGES_SUB_1 );
92 :
93 2469 : heap_size = fd_ulong_sat_mul( fd_ulong_sat_sub( heap_size / KIBIBYTE_MUL_PAGES, 1UL ), heap_cost );
94 2469 : return heap_size;
95 :
96 2469 : #undef KIBIBYTE_MUL_PAGES
97 2469 : #undef KIBIBYTE_MUL_PAGES_SUB_1
98 2469 : }
99 :
100 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L105-L171
101 :
102 : Our arguments to deploy_program are different from the Agave version because
103 : we handle the caching of deployed programs differently. In Firedancer we
104 : lack the concept of ProgramCacheEntryType entirely.
105 : https://github.com/anza-xyz/agave/blob/114d94a25e9631f9bf6349c4b833d7900ef1fb1c/program-runtime/src/loaded_programs.rs#L158
106 :
107 : In Agave there is a separate caching structure that is used to store the
108 : deployed programs. In Firedancer the deployed, validated program is stored as
109 : metadata for the account.
110 :
111 : See https://github.com/firedancer-io/firedancer/blob/9c1df680b3f38bebb0597e089766ec58f3b41e85/src/flamenco/runtime/program/fd_bpf_loader_v3_program.c#L1640
112 : for how we handle the concept of 'LoadedProgramType::DelayVisibility' in Firedancer.
113 :
114 : As a concrete example, our version of deploy_program does not have the
115 : 'account_size' argument because we do not update the record here. */
116 : int
117 : fd_deploy_program( fd_exec_instr_ctx_t * instr_ctx,
118 : uchar const * programdata,
119 : ulong programdata_size,
120 21 : int disable_sbpf_v0_v1_v2_deployment ) {
121 21 : int deploy_mode = 1;
122 21 : int direct_mapping = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, account_data_direct_mapping );
123 21 : int syscall_parameter_address_restrictions = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, syscall_parameter_address_restrictions );
124 21 : int virtual_address_space_adjustments = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, virtual_address_space_adjustments );
125 :
126 21 : uchar syscalls_mem[ FD_SBPF_SYSCALLS_FOOTPRINT ] __attribute__((aligned(FD_SBPF_SYSCALLS_ALIGN)));
127 21 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_join( fd_sbpf_syscalls_new( syscalls_mem ) );
128 21 : if( FD_UNLIKELY( !syscalls ) ) {
129 : //TODO: full log including err
130 0 : fd_log_collector_msg_literal( instr_ctx, "Failed to register syscalls" );
131 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
132 0 : }
133 :
134 : /* Agave uses the feature set from the next slot for deployment
135 : verification (DELAY_VISIBILITY_SLOT_OFFSET = 1). This matters at
136 : epoch boundaries where features activate: a deployment at the last
137 : slot of an epoch should see features that activate at the boundary.
138 : https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L3280-L3295 */
139 21 : ulong deploy_slot = instr_ctx->bank->f.slot+1UL;
140 :
141 21 : fd_vm_syscall_register_slot( syscalls,
142 21 : deploy_slot,
143 21 : &instr_ctx->bank->f.features,
144 21 : 1 );
145 :
146 : /* Load executable */
147 21 : fd_sbpf_elf_info_t elf_info[ 1UL ];
148 21 : fd_prog_versions_t versions = fd_prog_versions( &instr_ctx->bank->f.features, deploy_slot );
149 :
150 : /* SIMD-0500: when active, restrict program deployment to SBPF v3+.
151 : Older SBPF versions remain executable.
152 : TODO: fix link when 4.1 is released
153 : https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/program-runtime/src/deploy.rs#L30-L32 */
154 21 : if( disable_sbpf_v0_v1_v2_deployment ) {
155 6 : versions.min_sbpf_version = FD_SBPF_V3;
156 6 : }
157 :
158 21 : fd_sbpf_loader_config_t config = { 0 };
159 21 : config.elf_deploy_checks = deploy_mode;
160 21 : config.sbpf_min_version = versions.min_sbpf_version;
161 21 : config.sbpf_max_version = versions.max_sbpf_version;
162 :
163 21 : if( FD_UNLIKELY( fd_sbpf_elf_peek( elf_info, programdata, programdata_size, &config )<0 ) ) {
164 3 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
165 3 : }
166 :
167 : /* Allocate rodata segment */
168 18 : void * rodata = instr_ctx->runtime->bpf_loader_program.rodata;
169 18 : if( FD_UNLIKELY( !rodata ) ) {
170 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
171 0 : }
172 :
173 : /* Allocate program buffer */
174 18 : fd_sbpf_program_t * prog = fd_sbpf_program_new( instr_ctx->runtime->bpf_loader_program.sbpf_footprint, elf_info, rodata );
175 18 : if( FD_UNLIKELY( !prog ) ) {
176 0 : FD_LOG_ERR(( "fd_sbpf_program_new() failed" ));
177 0 : }
178 :
179 : /* Load program */
180 18 : void * scratch = instr_ctx->runtime->bpf_loader_program.programdata;
181 18 : int err = fd_sbpf_program_load( prog, programdata, programdata_size, syscalls, &config, scratch, programdata_size );
182 18 : if( FD_UNLIKELY( err ) ) {
183 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
184 0 : }
185 :
186 : /* Validate the program */
187 18 : fd_vm_t _vm[ 1UL ];
188 18 : fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
189 :
190 18 : vm = fd_vm_init(
191 18 : /* vm */ vm,
192 18 : /* instr_ctx */ instr_ctx,
193 18 : /* heap_max */ instr_ctx->txn_out->details.compute_budget.heap_size,
194 18 : /* entry_cu */ instr_ctx->txn_out->details.compute_budget.compute_meter,
195 18 : /* rodata */ prog->rodata,
196 18 : /* rodata_sz */ prog->rodata_sz,
197 18 : /* text */ prog->text,
198 18 : /* text_cnt */ prog->info.text_cnt,
199 18 : /* text_off */ prog->info.text_off, /* FIXME: What if text_off is not multiple of 8 */
200 18 : /* text_sz */ prog->info.text_sz,
201 18 : /* entry_pc */ prog->entry_pc,
202 18 : /* calldests */ prog->calldests,
203 18 : /* sbpf_version */ elf_info->sbpf_version,
204 18 : /* syscalls */ syscalls,
205 18 : /* trace */ NULL,
206 18 : /* sha */ NULL,
207 18 : /* mem_regions */ NULL,
208 18 : /* mem_regions_cnt */ 0,
209 18 : /* mem_region_accs */ NULL,
210 18 : /* is_deprecated */ 0,
211 18 : /* direct mapping */ direct_mapping,
212 18 : /* syscall_parameter_address_restrictions */ syscall_parameter_address_restrictions,
213 18 : /* virtual_address_space_adjustments */ virtual_address_space_adjustments,
214 18 : /* dump_syscall_to_pb */ 0,
215 18 : /* r2_initial_value */ 0UL );
216 18 : if ( FD_UNLIKELY( vm == NULL ) ) {
217 0 : FD_LOG_WARNING(( "NULL vm" ));
218 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
219 0 : }
220 :
221 18 : int validate_result = fd_vm_validate( vm );
222 18 : if( FD_UNLIKELY( validate_result!=FD_VM_SUCCESS ) ) {
223 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
224 0 : }
225 :
226 18 : return FD_EXECUTOR_INSTR_SUCCESS;
227 18 : }
228 :
229 : /* SIMD-0500 finalize gate: when the feature is active and the ELF
230 : embedded in `programdata` parses as SBPFv0/v1/v2, reject;
231 : otherwise accept (including the short-programdata case, which
232 : mirrors the let-chain short-circuit in agave). See the call site
233 : in `process_loader_upgradeable_instruction` for the SetAuthority
234 : handler.
235 : https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/bpf_loader/src/lib.rs#L580-L591 */
236 : int
237 : fd_bpf_loader_finalize_v3_check( int feature_active,
238 : uchar const * programdata,
239 12 : ulong programdata_len ) {
240 12 : ulong const off = PROGRAMDATA_METADATA_SIZE + 48UL; /* ELF64 e_flags */
241 12 : if( !feature_active ) return FD_EXECUTOR_INSTR_SUCCESS;
242 6 : if( programdata_len < off + sizeof(uint) ) return FD_EXECUTOR_INSTR_SUCCESS;
243 6 : if( FD_LOAD( uint, programdata+off )<FD_SBPF_V3 ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
244 3 : return FD_EXECUTOR_INSTR_SUCCESS;
245 6 : }
246 :
247 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L195-L218 */
248 : static int
249 : write_program_data( fd_exec_instr_ctx_t * instr_ctx,
250 : ushort instr_acc_idx,
251 : ulong program_data_offset,
252 : uchar const * bytes,
253 0 : ulong bytes_len ) {
254 0 : int err;
255 :
256 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L202 */
257 0 : fd_guarded_borrowed_account_t program = {0};
258 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, instr_acc_idx, &program );
259 :
260 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L203 */
261 0 : uchar * data = NULL;
262 0 : ulong dlen = 0UL;
263 0 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
264 0 : if( FD_UNLIKELY( err ) ) {
265 0 : return err;
266 0 : }
267 :
268 0 : ulong write_offset = fd_ulong_sat_add( program_data_offset, bytes_len );
269 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &program )<write_offset ) ) {
270 : /* Max msg_sz: 24 - 6 + 2*20 = 58 < 127 => we can use printf */
271 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
272 0 : "Write overflow: %lu < %lu", fd_borrowed_account_get_data_len( &program ), write_offset );
273 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
274 0 : }
275 :
276 0 : if( FD_UNLIKELY( program_data_offset>dlen ) ) {
277 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
278 0 : }
279 :
280 0 : if( FD_LIKELY( bytes_len ) ) {
281 0 : fd_memcpy( data+program_data_offset, bytes, bytes_len );
282 0 : }
283 :
284 0 : return FD_EXECUTOR_INSTR_SUCCESS;
285 0 : }
286 :
287 : int
288 : fd_bpf_loader_program_get_state( fd_acc_t const * acc,
289 444 : fd_bpf_state_t * state ) {
290 444 : if( FD_UNLIKELY( fd_bpf_state_decode( state, acc->data, acc->data_len ) ) ) {
291 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
292 0 : }
293 444 : return FD_EXECUTOR_INSTR_SUCCESS;
294 444 : }
295 :
296 : int
297 : fd_bpf_loader_program_get_state2( uchar const * data,
298 : ulong data_len,
299 6 : fd_bpf_state_t * state ) {
300 6 : if( FD_UNLIKELY( fd_bpf_state_decode( state, data, data_len ) ) ) {
301 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
302 0 : }
303 6 : return FD_EXECUTOR_INSTR_SUCCESS;
304 6 : }
305 :
306 : /* Mirrors solana_sdk::transaction_context::BorrowedAccount::set_state()
307 : https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L973 */
308 : int
309 : fd_bpf_loader_v3_program_set_state( fd_borrowed_account_t * borrowed_acct,
310 27 : fd_bpf_state_t * state ) {
311 27 : ulong state_size = fd_bpf_state_size( state );
312 :
313 27 : uchar * data = NULL;
314 27 : ulong dlen = 0UL;
315 :
316 27 : int err = fd_borrowed_account_get_data_mut( borrowed_acct, &data, &dlen );
317 27 : if( FD_UNLIKELY( err ) ) {
318 0 : return err;
319 0 : }
320 :
321 27 : if( FD_UNLIKELY( state_size>dlen ) ) {
322 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
323 0 : }
324 :
325 27 : ulong out_sz = 0UL;
326 27 : if( FD_UNLIKELY( fd_bpf_state_encode( state, data, state_size, &out_sz ) ) ) {
327 0 : return FD_EXECUTOR_INSTR_ERR_GENERIC_ERR;
328 0 : }
329 :
330 27 : return FD_EXECUTOR_INSTR_SUCCESS;
331 27 : }
332 :
333 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1299-L1331 */
334 : static int
335 : common_close_account( fd_pubkey_t * authority_address,
336 : fd_exec_instr_ctx_t * instr_ctx,
337 9 : fd_bpf_state_t * state ) {
338 9 : int err;
339 :
340 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1307 */
341 9 : if( FD_UNLIKELY( !authority_address ) ) {
342 0 : fd_log_collector_msg_literal( instr_ctx, "Account is immutable" );
343 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
344 0 : }
345 :
346 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1312-L1313 */
347 9 : fd_pubkey_t const * acc_key = NULL;
348 9 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &acc_key );
349 9 : if( FD_UNLIKELY( err ) ) {
350 0 : return err;
351 0 : }
352 :
353 9 : if( FD_UNLIKELY( memcmp( authority_address, acc_key, sizeof(fd_pubkey_t) ) ) ) {
354 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect authority provided" );
355 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
356 0 : }
357 :
358 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1319-L1322 */
359 9 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL, &err ) ) ) {
360 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
361 0 : if( FD_UNLIKELY( !!err ) ) return err;
362 0 : fd_log_collector_msg_literal( instr_ctx, "Authority did not sign" );
363 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
364 0 : }
365 :
366 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1324 */
367 9 : fd_guarded_borrowed_account_t close_account = {0};
368 9 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &close_account );
369 :
370 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1326 */
371 9 : fd_guarded_borrowed_account_t recipient_account = {0};
372 9 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &recipient_account );
373 :
374 9 : err = fd_borrowed_account_checked_add_lamports( &recipient_account,
375 9 : fd_borrowed_account_get_lamports( &close_account ) );
376 9 : if( FD_UNLIKELY( err ) ) {
377 0 : return err;
378 0 : }
379 :
380 9 : err = fd_borrowed_account_set_lamports( &close_account, 0UL );
381 9 : if( FD_UNLIKELY( err ) ) {
382 0 : return err;
383 0 : }
384 :
385 9 : state->discriminant = FD_BPF_STATE_UNINITIALIZED;
386 9 : err = fd_bpf_loader_v3_program_set_state( &close_account, state );
387 9 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
388 0 : return err;
389 0 : }
390 :
391 9 : return FD_EXECUTOR_INSTR_SUCCESS;
392 9 : }
393 :
394 :
395 : /* Every loader-owned BPF program goes through this function, which goes into the VM.
396 :
397 : https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1332-L1501 */
398 : int
399 : fd_bpf_execute( fd_exec_instr_ctx_t * instr_ctx,
400 : fd_progcache_rec_t const * cache_entry,
401 2469 : uchar is_deprecated ) {
402 2469 : long const regime0 = fd_tickcount();
403 :
404 2469 : int err = FD_EXECUTOR_INSTR_SUCCESS;
405 :
406 2469 : uchar syscalls_mem[ FD_SBPF_SYSCALLS_FOOTPRINT ] __attribute__((aligned(FD_SBPF_SYSCALLS_ALIGN)));
407 2469 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_join( fd_sbpf_syscalls_new( syscalls_mem ) );
408 2469 : if( FD_UNLIKELY( !syscalls ) ) {
409 0 : FD_LOG_CRIT(( "Unable to allocate syscalls" ));
410 0 : }
411 :
412 : /* TODO do we really need to re-do this on every instruction? */
413 2469 : fd_vm_syscall_register_slot( syscalls,
414 2469 : instr_ctx->bank->f.slot,
415 2469 : &instr_ctx->bank->f.features,
416 2469 : 0 );
417 :
418 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1362-L1368 */
419 2469 : ulong input_sz = 0UL;
420 2469 : ulong pre_lens[256] = {0};
421 2469 : fd_vm_input_region_t input_mem_regions[1000] = {0}; /* We can have a max of (3 * num accounts + 1) regions */
422 2469 : fd_vm_acc_region_meta_t acc_region_metas[256] = {0}; /* instr acc idx to idx */
423 2469 : uint input_mem_regions_cnt = 0U;
424 2469 : int direct_mapping = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, account_data_direct_mapping );
425 2469 : int syscall_parameter_address_restrictions = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, syscall_parameter_address_restrictions );
426 2469 : int virtual_address_space_adjustments = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, virtual_address_space_adjustments );
427 2469 : int provide_instruction_data_offset_in_vm_r2 = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, provide_instruction_data_offset_in_vm_r2 );
428 2469 : int direct_account_pointers_in_program_input = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, direct_account_pointers_in_program_input );
429 :
430 2469 : ulong instruction_data_offset = 0UL;
431 : /* 16-byte aligned buffer:
432 : https://github.com/anza-xyz/agave/blob/v3.0.0/program-runtime/src/serialization.rs#L60 */
433 2469 : uchar * input = instr_ctx->runtime->bpf_loader_serialization.serialization_mem[ instr_ctx->runtime->instr.stack_sz-1UL ];
434 2469 : err = fd_bpf_loader_input_serialize_parameters( instr_ctx, pre_lens,
435 2469 : input_mem_regions, &input_mem_regions_cnt,
436 2469 : acc_region_metas, virtual_address_space_adjustments, direct_mapping,
437 2469 : direct_account_pointers_in_program_input, is_deprecated,
438 2469 : &instruction_data_offset, &input_sz );
439 2469 : if( FD_UNLIKELY( err ) ) {
440 0 : return err;
441 0 : }
442 :
443 2469 : fd_sha256_t _sha[1];
444 2469 : fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
445 :
446 2469 : fd_vm_t _vm[1];
447 2469 : fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
448 :
449 2469 : ulong pre_insn_cus = instr_ctx->txn_out->details.compute_budget.compute_meter;
450 2469 : ulong heap_size = instr_ctx->txn_out->details.compute_budget.heap_size;
451 :
452 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L275-L278 */
453 2469 : ulong heap_cost = calculate_heap_cost( heap_size, FD_VM_HEAP_COST );
454 2469 : int heap_cost_result = fd_executor_consume_cus( instr_ctx->txn_out, heap_cost );
455 2469 : if( FD_UNLIKELY( heap_cost_result ) ) {
456 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
457 0 : }
458 :
459 : /* For dumping syscalls for seed corpora */
460 2469 : int dump_syscall_to_pb = instr_ctx->runtime->log.dump_proto_ctx &&
461 2469 : instr_ctx->bank->f.slot>=instr_ctx->runtime->log.dump_proto_ctx->dump_proto_start_slot &&
462 2469 : instr_ctx->runtime->log.dump_proto_ctx->dump_syscall_to_pb;
463 :
464 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/bpf_loader/src/lib.rs#L1525-L1528 */
465 2469 : ulong r2_initial_value = provide_instruction_data_offset_in_vm_r2 ? instruction_data_offset : 0UL;
466 :
467 : /* TODO: (topointon): correctly set check_size in vm setup */
468 2469 : fd_wksp_t * progcache_wksp = instr_ctx->runtime->progcache->join->data_base;
469 2469 : void const * rodata = fd_progcache_rec_rodata( cache_entry, progcache_wksp );
470 2469 : vm = fd_vm_init(
471 2469 : /* vm */ vm,
472 2469 : /* instr_ctx */ instr_ctx,
473 2469 : /* heap_max */ heap_size,
474 2469 : /* entry_cu */ instr_ctx->txn_out->details.compute_budget.compute_meter,
475 2469 : /* rodata */ rodata,
476 2469 : /* rodata_sz */ cache_entry->rodata_sz,
477 2469 : /* text (note: text_off is byte offset) */ (ulong *)( (ulong)rodata + cache_entry->text_off ),
478 2469 : /* text_cnt */ cache_entry->text_cnt,
479 2469 : /* text_off */ cache_entry->text_off,
480 2469 : /* text_sz */ cache_entry->text_sz,
481 2469 : /* entry_pc */ cache_entry->entry_pc,
482 2469 : /* calldests */ fd_progcache_rec_calldests( cache_entry, progcache_wksp ),
483 2469 : /* sbpf_version */ cache_entry->sbpf_version,
484 2469 : /* syscalls */ syscalls,
485 2469 : /* trace */ NULL,
486 2469 : /* sha */ sha,
487 2469 : /* input_mem_regions */ input_mem_regions,
488 2469 : /* input_mem_regions_cnt */ input_mem_regions_cnt,
489 2469 : /* acc_region_metas */ acc_region_metas,
490 2469 : /* is_deprecated */ is_deprecated,
491 2469 : /* direct_mapping */ direct_mapping,
492 2469 : /* syscall_parameter_address_restrictions */ syscall_parameter_address_restrictions,
493 2469 : /* virtual_address_space_adjustments */ virtual_address_space_adjustments,
494 2469 : /* dump_syscall_to_pb */ dump_syscall_to_pb,
495 2469 : /* r2_initial_value */ r2_initial_value );
496 2469 : if( FD_UNLIKELY( !vm ) ) {
497 : /* We throw an error here because it could be the case that the given heap_size > HEAP_MAX.
498 : In this case, Agave fails the transaction but does not error out.
499 :
500 : https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1396 */
501 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
502 0 : }
503 :
504 2469 : if( FD_UNLIKELY( instr_ctx->runtime->log.enable_vm_tracing && instr_ctx->runtime->log.tracing_mem ) ) {
505 0 : vm->trace = fd_vm_trace_join( fd_vm_trace_new( instr_ctx->runtime->log.tracing_mem + ((instr_ctx->runtime->instr.stack_sz-1UL) * FD_RUNTIME_VM_TRACE_STATIC_FOOTPRINT), FD_RUNTIME_VM_TRACE_EVENT_MAX, FD_RUNTIME_VM_TRACE_EVENT_DATA_MAX ));
506 0 : if( FD_UNLIKELY( !vm->trace ) ) FD_LOG_ERR(( "unable to create trace; make sure you've compiled with sufficient spad size " ));
507 0 : }
508 :
509 2469 : long const regime1 = fd_tickcount();
510 :
511 2469 : int exec_err = fd_vm_exec( vm );
512 2469 : instr_ctx->txn_out->details.compute_budget.compute_meter = vm->cu;
513 :
514 2469 : long const regime2 = fd_tickcount();
515 :
516 2469 : if( instr_ctx->instr->stack_height==1 ) {
517 15 : instr_ctx->runtime->metrics.vm_setup_cum_ticks += (ulong)( regime1-regime0 );
518 15 : instr_ctx->runtime->metrics.vm_exec_cum_ticks += (ulong)( regime2-regime1 );
519 2454 : } else {
520 2454 : instr_ctx->runtime->metrics.cpi_setup_cum_ticks += (ulong)( regime1-regime0 );
521 2454 : }
522 :
523 2469 : if( FD_UNLIKELY( vm->trace ) ) {
524 0 : err = fd_vm_trace_printf( vm->trace, vm->syscalls );
525 0 : if( FD_UNLIKELY( err ) ) {
526 0 : FD_LOG_WARNING(( "fd_vm_trace_printf failed (%i-%s)", err, fd_vm_strerror( err ) ));
527 0 : }
528 0 : }
529 :
530 : /* Log consumed compute units and return data.
531 : https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/lib.rs#L1418-L1429 */
532 2469 : fd_log_collector_program_consumed( instr_ctx, pre_insn_cus-vm->cu, pre_insn_cus );
533 2469 : if( FD_UNLIKELY( instr_ctx->txn_out->details.return_data.len ) ) {
534 0 : fd_log_collector_program_return( instr_ctx );
535 0 : }
536 :
537 : /* We have a big error-matching arm here
538 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1674-L1744 */
539 :
540 : /* Handle non-zero return status with successful VM execution. This is
541 : the Ok(status) case, hence exec_err must be 0 for this case to be hit.
542 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1675-L1678 */
543 2469 : if( FD_LIKELY( !exec_err ) ) {
544 2439 : ulong status = vm->reg[0];
545 2439 : if( FD_UNLIKELY( status ) ) {
546 51 : err = program_error_to_instr_error( status, &instr_ctx->txn_out->err.custom_err );
547 51 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
548 51 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
549 51 : return err;
550 51 : }
551 2439 : } else {
552 : /* https://github.com/anza-xyz/agave/blob/v2.1.13/programs/bpf_loader/src/lib.rs#L1434-L1439 */
553 : /* (SIMD-182) Consume ALL requested CUs on non-Syscall errors */
554 30 : if( FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, deplete_cu_meter_on_vm_failure ) &&
555 30 : exec_err!=FD_VM_ERR_EBPF_SYSCALL_ERROR ) {
556 0 : instr_ctx->txn_out->details.compute_budget.compute_meter = 0UL;
557 0 : }
558 :
559 : /* Direct mapping access violation case
560 : Edge case with error codes: if direct mapping is enabled, the EBPF error is an access violation,
561 : and the access type was a store, a different error code is returned to give developers more insight
562 : as to what caused the error.
563 : https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1556-L1618 */
564 30 : if( FD_UNLIKELY( virtual_address_space_adjustments &&
565 30 : ( exec_err==FD_VM_ERR_EBPF_ACCESS_VIOLATION || instr_ctx->txn_out->err.exec_err==FD_VM_ERR_EBPF_ACCESS_VIOLATION ) &&
566 30 : vm->segv_vaddr!=ULONG_MAX ) ) {
567 :
568 : /* If the vaddr of the access violation falls within the bounds of a
569 : serialized account vaddr range, then try to retrieve a more specific
570 : vm error based on the account's access permissions. */
571 24 : for( ushort i=0UL; i<instr_ctx->instr->acct_cnt; i++ ) {
572 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1455 */
573 :
574 : /* Find the input memory region that corresponds to the access
575 : https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1566-L1617 */
576 24 : ulong idx = acc_region_metas[i].region_idx;
577 24 : fd_vm_input_region_t const * input_mem_region = &input_mem_regions[idx];
578 24 : fd_vm_acc_region_meta_t const * acc_region_meta = &acc_region_metas[i];
579 :
580 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1484-L1492 */
581 24 : ulong region_data_vaddr_start = FD_VM_MEM_MAP_INPUT_REGION_START + input_mem_region->vaddr_offset + input_mem_region->region_sz;
582 24 : ulong region_data_vaddr_end = fd_ulong_sat_add( region_data_vaddr_start, acc_region_meta->original_data_len );
583 24 : if( FD_LIKELY( !is_deprecated ) ) {
584 24 : region_data_vaddr_end = fd_ulong_sat_add( region_data_vaddr_end, MAX_PERMITTED_DATA_INCREASE );
585 24 : }
586 :
587 24 : if( vm->segv_vaddr >= region_data_vaddr_start && vm->segv_vaddr <= region_data_vaddr_end ) {
588 :
589 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1575-L1616 */
590 24 : fd_guarded_borrowed_account_t instr_acc = {0};
591 24 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, i, &instr_acc );
592 :
593 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1581-L1616 */
594 24 : if( fd_ulong_sat_add( vm->segv_vaddr, vm->segv_access_len ) <= region_data_vaddr_end ) {
595 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1592-L1601 */
596 24 : if( vm->segv_access_type==FD_VM_ACCESS_TYPE_ST ) {
597 24 : int borrow_err = FD_EXECUTOR_INSTR_SUCCESS;
598 24 : if( !fd_borrowed_account_can_data_be_changed( &instr_acc, &borrow_err ) || borrow_err != FD_EXECUTOR_INSTR_SUCCESS ) {
599 24 : err = borrow_err;
600 24 : } else {
601 0 : err = FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
602 0 : }
603 24 : } else if( vm->segv_access_type==FD_VM_ACCESS_TYPE_LD ) {
604 0 : int borrow_err = FD_EXECUTOR_INSTR_SUCCESS;
605 0 : if( !fd_borrowed_account_can_data_be_changed( &instr_acc, &borrow_err ) || borrow_err != FD_EXECUTOR_INSTR_SUCCESS ) {
606 0 : err = FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
607 0 : } else {
608 0 : err = FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
609 0 : }
610 0 : } else {
611 0 : FD_LOG_CRIT(( "invariant violation: unsupported access type: %i", vm->segv_access_type ));
612 0 : }
613 :
614 24 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
615 24 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
616 24 : return err;
617 24 : }
618 24 : }
619 24 : }
620 24 : }
621 :
622 : /* The error kind should have been set in the VM. Match it and set
623 : the error code accordingly. There are no direct permalinks here -
624 : this is all a result of Agave's complex nested error-code handling
625 : and our design decisions for making our error codes match. */
626 :
627 : /* Instr error case. Set the error kind and return the instruction error */
628 6 : if( instr_ctx->txn_out->err.exec_err_kind==FD_EXECUTOR_ERR_KIND_INSTR ) {
629 0 : err = instr_ctx->txn_out->err.exec_err;
630 0 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
631 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
632 0 : return err;
633 0 : }
634 :
635 : /* Syscall error case. The VM would have also set the syscall error
636 : code in the txn_ctx exec_err. */
637 6 : if( instr_ctx->txn_out->err.exec_err_kind==FD_EXECUTOR_ERR_KIND_SYSCALL ) {
638 0 : err = instr_ctx->txn_out->err.exec_err;
639 0 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
640 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, err );
641 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_FAILED_TO_COMPLETE;
642 0 : }
643 :
644 : /* An access violation that takes place inside a syscall will
645 : cause `exec_res` to be set to EbpfError::SyscallError,
646 : 'but the `txn_ctx->err.exec_err_kind` will be set to EBPF and
647 : `txn_ctx->err.exec_err` will be set to the EBPF error. In this
648 : specific case, there is nothing to do since the error and error
649 : kind area already set correctly. Otherwise, we need to log the
650 : EBPF error. */
651 6 : if( exec_err!=FD_VM_ERR_EBPF_SYSCALL_ERROR ) {
652 6 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
653 6 : FD_VM_ERR_FOR_LOG_EBPF( vm, exec_err );
654 6 : }
655 :
656 6 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_FAILED_TO_COMPLETE;
657 6 : }
658 :
659 2388 : err = fd_bpf_loader_input_deserialize_parameters(
660 2388 : instr_ctx, pre_lens, input, input_sz, virtual_address_space_adjustments, direct_mapping, is_deprecated );
661 :
662 2388 : long const regime3 = fd_tickcount();
663 2388 : if( instr_ctx->instr->stack_height==1 ) {
664 15 : instr_ctx->runtime->metrics.vm_commit_cum_ticks += (ulong)( regime3-regime2 );
665 2373 : } else {
666 2373 : instr_ctx->runtime->metrics.cpi_commit_cum_ticks += (ulong)( regime3-regime2 );
667 2373 : }
668 :
669 2388 : return err;
670 2469 : }
671 :
672 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1358-L1539 */
673 : static int
674 : common_extend_program( fd_exec_instr_ctx_t * instr_ctx,
675 : uint additional_bytes,
676 3 : uchar check_authority ) {
677 3 : int err;
678 :
679 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1366 */
680 3 : fd_pubkey_t const * program_id = NULL;
681 3 : err = fd_exec_instr_ctx_get_last_program_key( instr_ctx, &program_id );
682 3 : if( FD_UNLIKELY( err ) ) {
683 0 : return err;
684 0 : }
685 :
686 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1368-L1370 */
687 3 : #define PROGRAM_DATA_ACCOUNT_INDEX (0)
688 3 : #define PROGRAM_ACCOUNT_INDEX (1)
689 3 : #define AUTHORITY_ACCOUNT_INDEX (2)
690 :
691 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1371-L1372 */
692 3 : uchar optional_payer_account_index = check_authority ? 4 : 3;
693 :
694 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1374-L1377 */
695 3 : if( FD_UNLIKELY( additional_bytes==0U ) ) {
696 0 : fd_log_collector_msg_literal( instr_ctx, "Additional bytes must be greater than 0" );
697 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
698 0 : }
699 :
700 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1379-L1381 */
701 3 : fd_guarded_borrowed_account_t programdata_account = {0};
702 3 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, PROGRAM_DATA_ACCOUNT_INDEX, &programdata_account );
703 3 : fd_pubkey_t const * programdata_key = (fd_pubkey_t*)programdata_account.acc->pubkey;
704 :
705 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1383-L1386 */
706 3 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &programdata_account ), sizeof(fd_pubkey_t) ) ) ) {
707 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData owner is invalid" );
708 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
709 0 : }
710 :
711 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1387-L1390 */
712 3 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &programdata_account ) ) ) {
713 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData is not writable" );
714 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
715 0 : }
716 :
717 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1392-L1393 */
718 3 : fd_guarded_borrowed_account_t program_account = {0};
719 3 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, PROGRAM_ACCOUNT_INDEX, &program_account );
720 :
721 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1394-L1397 */
722 3 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &program_account ) ) ) {
723 0 : fd_log_collector_msg_literal( instr_ctx, "Program account is not writable" );
724 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
725 0 : }
726 :
727 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1398-L1401 */
728 3 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &program_account ), sizeof(fd_pubkey_t) ) ) ) {
729 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not owned by loader" );
730 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
731 0 : }
732 :
733 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1403-L1419 */
734 3 : fd_bpf_state_t program_state[1];
735 3 : err = fd_bpf_loader_program_get_state( program_account.acc, program_state );
736 3 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
737 0 : return err;
738 0 : }
739 :
740 3 : if( program_state->discriminant==FD_BPF_STATE_PROGRAM ) {
741 3 : if( FD_UNLIKELY( memcmp( &program_state->inner.program.programdata_address, programdata_key, sizeof(fd_pubkey_t) ) ) ) {
742 0 : fd_log_collector_msg_literal( instr_ctx, "Program account does not match ProgramData account" );
743 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
744 0 : }
745 3 : } else {
746 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Program account" );
747 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
748 0 : }
749 :
750 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1420 */
751 3 : fd_borrowed_account_drop( &program_account );
752 :
753 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1422-L1432 */
754 3 : ulong old_len = fd_borrowed_account_get_data_len( &programdata_account );
755 3 : ulong new_len = fd_ulong_sat_add( old_len, additional_bytes );
756 3 : if( FD_UNLIKELY( new_len>MAX_PERMITTED_DATA_LENGTH ) ) {
757 : /* Max msg_sz: 85 - 6 + 2*20 = 119 < 127 => we can use printf */
758 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
759 0 : "Extended ProgramData length of %lu bytes exceeds max account data length of %lu bytes", new_len, MAX_PERMITTED_DATA_LENGTH );
760 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
761 0 : }
762 :
763 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-beta.1/programs/bpf_loader/src/lib.rs#L861-L883 */
764 3 : if( FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, loader_v3_minimum_extend_program_size ) ) {
765 0 : ulong headroom = fd_ulong_sat_sub( MAX_PERMITTED_DATA_LENGTH, old_len );
766 0 : if( FD_UNLIKELY( additional_bytes<MINIMUM_EXTEND_PROGRAM_BYTES && additional_bytes!=headroom ) ) {
767 : /* Max msg_sz: 113 - (3+2) + (5+10) = 123 < 127 => we can use printf */
768 0 : fd_log_collector_printf_dangerous_max_127(
769 0 : instr_ctx,
770 0 : "ExtendProgram requires a minimum of %lu additional bytes or to extend to maximum size, but only %u were requested",
771 0 : MINIMUM_EXTEND_PROGRAM_BYTES,
772 0 : additional_bytes
773 0 : );
774 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
775 0 : }
776 0 : }
777 :
778 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1434-L1437 */
779 3 : fd_sol_sysvar_clock_t clock[1];
780 3 : if( FD_UNLIKELY( !fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, clock ) ) ) {
781 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
782 0 : }
783 3 : ulong clock_slot = clock->slot;
784 :
785 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1439-L1478 */
786 3 : fd_pubkey_t * upgrade_authority_address = NULL;
787 3 : fd_bpf_state_t programdata_state[1];
788 3 : err = fd_bpf_loader_program_get_state( programdata_account.acc, programdata_state );
789 3 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
790 0 : return err;
791 0 : }
792 3 : if( programdata_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
793 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1444-L1447 */
794 3 : if( FD_UNLIKELY( clock_slot==programdata_state->inner.program_data.slot ) ) {
795 0 : fd_log_collector_msg_literal( instr_ctx, "Program was extended in this block already" );
796 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
797 0 : }
798 :
799 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1449-L1455 */
800 3 : if( FD_UNLIKELY( !programdata_state->inner.program_data.has_upgrade_authority_address ) ) {
801 0 : fd_log_collector_msg_literal( instr_ctx, "Cannot extend ProgramData accounts that are not upgradeable" );
802 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
803 0 : }
804 :
805 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1457-L1472 */
806 3 : if( check_authority ) {
807 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1458-L1463 */
808 0 : fd_pubkey_t const * authority_key = NULL;
809 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, AUTHORITY_ACCOUNT_INDEX, &authority_key );
810 0 : if( FD_UNLIKELY( err ) ) {
811 0 : return err;
812 0 : }
813 :
814 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1464-L1467 */
815 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &programdata_state->inner.program_data.upgrade_authority_address, authority_key ) ) ) {
816 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
817 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
818 0 : }
819 :
820 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1468-L1471 */
821 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, AUTHORITY_ACCOUNT_INDEX, &err ) ) ) {
822 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
823 0 : if( FD_UNLIKELY( !!err ) ) return err;
824 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
825 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
826 0 : }
827 0 : }
828 :
829 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1474 */
830 3 : fd_bpf_state_program_data_t * pd = &programdata_state->inner.program_data;
831 3 : upgrade_authority_address = pd->has_upgrade_authority_address ? &pd->upgrade_authority_address : NULL;
832 3 : } else {
833 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1476-L1477 */
834 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData state is invalid" );
835 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
836 0 : }
837 :
838 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1480-L1485 */
839 3 : fd_rent_t rent_;
840 3 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
841 3 : if( FD_UNLIKELY( !rent ) ) {
842 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
843 0 : }
844 :
845 3 : ulong balance = fd_borrowed_account_get_lamports( &programdata_account );
846 3 : ulong min_balance = fd_ulong_max( fd_rent_exempt_minimum_balance( rent, new_len ), 1UL );
847 3 : ulong required_payment = fd_ulong_sat_sub( min_balance, balance );
848 :
849 : /* Borrowed accounts need to be dropped before native invocations. Note:
850 : the programdata account is manually released and acquired within the
851 : extend instruction to preserve the local variable scoping to maintain
852 : readability. The scoped macro still successfully handles the case of
853 : freeing a write lock in case of an early termination. */
854 :
855 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1488 */
856 3 : fd_borrowed_account_drop( &programdata_account );
857 :
858 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1492-L1502 */
859 3 : if( FD_UNLIKELY( required_payment>0UL ) ) {
860 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1493-L1496 */
861 3 : fd_pubkey_t const * payer_key = NULL;
862 3 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, optional_payer_account_index, &payer_key );
863 3 : if( FD_UNLIKELY( err ) ) {
864 0 : return err;
865 0 : }
866 :
867 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1498-L1501 */
868 3 : uchar instr_data[FD_TXN_MTU];
869 3 : fd_system_program_instruction_t instr = {
870 3 : .discriminant = FD_SYSTEM_PROGRAM_INSTR_TRANSFER,
871 3 : .inner = {
872 3 : .transfer = required_payment
873 3 : }
874 3 : };
875 :
876 3 : ulong instr_data_sz;
877 3 : int err = fd_system_program_instruction_encode( &instr, instr_data, FD_TXN_MTU, &instr_data_sz );
878 3 : if( FD_UNLIKELY( err ) ) {
879 0 : FD_LOG_CRIT(( "Failed to encode SystemInstruction with error %d", err ));
880 0 : }
881 :
882 3 : fd_vm_rust_account_meta_t acct_metas[ 2UL ];
883 3 : fd_native_cpi_create_account_meta( payer_key, 1UL, 1UL, &acct_metas[ 0UL ] );
884 3 : fd_native_cpi_create_account_meta( programdata_key, 0UL, 1UL, &acct_metas[ 1UL ] );
885 :
886 3 : err = fd_native_cpi_native_invoke( instr_ctx,
887 3 : &fd_solana_system_program_id,
888 3 : instr_data,
889 3 : instr_data_sz,
890 3 : acct_metas,
891 3 : 2UL,
892 3 : NULL,
893 3 : 0UL );
894 3 : if( FD_UNLIKELY( err ) ) {
895 0 : return err;
896 0 : }
897 3 : }
898 :
899 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1506-L1507 */
900 3 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, PROGRAM_DATA_ACCOUNT_INDEX, &programdata_account );
901 :
902 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1508 */
903 3 : err = fd_borrowed_account_set_data_length( &programdata_account, new_len );
904 3 : if( FD_UNLIKELY( err ) ) {
905 0 : return err;
906 0 : }
907 :
908 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1510 */
909 3 : ulong programdata_data_offset = PROGRAMDATA_METADATA_SIZE;
910 :
911 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1517-L1520 */
912 3 : if( FD_UNLIKELY( programdata_data_offset>fd_borrowed_account_get_data_len( &programdata_account ) ) ) {
913 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
914 0 : }
915 3 : uchar const * programdata_data = fd_borrowed_account_get_data( &programdata_account ) + programdata_data_offset;
916 3 : ulong programdata_size = new_len - PROGRAMDATA_METADATA_SIZE;
917 :
918 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1512-L1522 */
919 : /* SIMD-0500: extend_program is exempt from the deployment-version gate. */
920 3 : err = fd_deploy_program( instr_ctx, programdata_data, programdata_size, /* disable_sbpf_v0_v1_v2_deployment */ 0 );
921 3 : if( FD_UNLIKELY( err ) ) {
922 0 : return err;
923 0 : }
924 :
925 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1523 */
926 3 : fd_borrowed_account_drop( &programdata_account );
927 :
928 : /* Setting the discriminant and upgrade authority address here can likely
929 : be a no-op because these values shouldn't change. These can probably be
930 : removed, but can help to mirror against Agave client's implementation.
931 : The set_state function also contains an ownership check. */
932 :
933 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1525-L1526 */
934 3 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &programdata_account );
935 :
936 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1527-L1530 */
937 3 : programdata_state->discriminant = FD_BPF_STATE_PROGRAM_DATA;
938 3 : programdata_state->inner.program_data.slot = clock_slot;
939 3 : programdata_state->inner.program_data.has_upgrade_authority_address = !!upgrade_authority_address;
940 3 : if( upgrade_authority_address ) programdata_state->inner.program_data.upgrade_authority_address = *upgrade_authority_address;
941 :
942 3 : err = fd_bpf_loader_v3_program_set_state( &programdata_account, programdata_state );
943 3 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
944 0 : return err;
945 0 : }
946 3 : programdata_account.acc->pd_write = 1;
947 :
948 : /* Max msg_sz: 41 - 2 + 20 = 57 < 127 => we can use printf
949 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1532-L1536 */
950 3 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
951 3 : "Extended ProgramData account by %u bytes", additional_bytes );
952 :
953 : /* programdata account is dropped when it goes out of scope */
954 :
955 3 : return FD_EXECUTOR_INSTR_SUCCESS;
956 :
957 3 : #undef PROGRAM_DATA_ACCOUNT_INDEX
958 3 : #undef PROGRAM_ACCOUNT_INDEX
959 3 : #undef AUTHORITY_ACCOUNT_INDEX
960 3 : }
961 :
962 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L566-L1444 */
963 : static int
964 27 : process_loader_upgradeable_instruction( fd_exec_instr_ctx_t * instr_ctx ) {
965 27 : fd_bpf_instruction_t instruction[1] = {0};
966 27 : if( FD_UNLIKELY( fd_bpf_instruction_decode(
967 27 : instruction,
968 27 : instr_ctx->instr->data,
969 27 : instr_ctx->instr->data_sz ) ) ) {
970 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
971 0 : }
972 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/bpf_loader/src/lib.rs#L510 */
973 27 : fd_pubkey_t const * program_id = NULL;
974 27 : int err = fd_exec_instr_ctx_get_last_program_key( instr_ctx, &program_id );
975 27 : if( FD_UNLIKELY( err ) ) {
976 0 : return err;
977 0 : }
978 :
979 27 : switch( instruction->discriminant ) {
980 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L476-L493 */
981 0 : case FD_BPF_INSTR_INITIALIZE_BUFFER: {
982 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
983 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
984 0 : }
985 :
986 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L479 */
987 0 : fd_guarded_borrowed_account_t buffer = {0};
988 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &buffer );
989 :
990 0 : fd_bpf_state_t buffer_state[1];
991 0 : err = fd_bpf_loader_program_get_state( buffer.acc, buffer_state );
992 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
993 0 : return err;
994 0 : }
995 :
996 0 : if( FD_UNLIKELY( buffer_state->discriminant!=FD_BPF_STATE_UNINITIALIZED ) ) {
997 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account already initialized" );
998 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
999 0 : }
1000 :
1001 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L487-L489 */
1002 0 : fd_pubkey_t const * authority_key = NULL;
1003 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_key );
1004 0 : if( FD_UNLIKELY( err ) ) {
1005 0 : return err;
1006 0 : }
1007 :
1008 0 : buffer_state->discriminant = FD_BPF_STATE_BUFFER;
1009 0 : buffer_state->inner.buffer.has_authority_address = 1;
1010 0 : buffer_state->inner.buffer.authority_address = *authority_key;
1011 :
1012 0 : err = fd_bpf_loader_v3_program_set_state( &buffer, buffer_state );
1013 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1014 0 : return err;
1015 0 : }
1016 :
1017 : /* implicit drop of buffer account */
1018 :
1019 0 : break;
1020 0 : }
1021 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L494-L525 */
1022 0 : case FD_BPF_INSTR_WRITE: {
1023 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
1024 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1025 0 : }
1026 :
1027 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L497 */
1028 0 : fd_guarded_borrowed_account_t buffer = {0};
1029 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &buffer );
1030 :
1031 0 : fd_bpf_state_t loader_state[1];
1032 0 : err = fd_bpf_loader_program_get_state( buffer.acc, loader_state );
1033 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1034 0 : return err;
1035 0 : }
1036 :
1037 0 : if( loader_state->discriminant==FD_BPF_STATE_BUFFER ) {
1038 0 : if( FD_UNLIKELY( !loader_state->inner.buffer.has_authority_address ) ) {
1039 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer is immutable" );
1040 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1041 0 : }
1042 :
1043 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L505-L507 */
1044 0 : fd_pubkey_t const * authority_key = NULL;
1045 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_key );
1046 0 : if( FD_UNLIKELY( err ) ) {
1047 0 : return err;
1048 0 : }
1049 :
1050 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &loader_state->inner.buffer.authority_address, authority_key ) ) ) {
1051 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect buffer authority provided" );
1052 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1053 0 : }
1054 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &err ) ) ) {
1055 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1056 0 : if( FD_UNLIKELY( !!err ) ) return err;
1057 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority did not sign" );
1058 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1059 0 : }
1060 0 : } else {
1061 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Buffer account" );
1062 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1063 0 : }
1064 :
1065 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L520 */
1066 0 : fd_borrowed_account_drop( &buffer );
1067 :
1068 0 : ulong program_data_offset = fd_ulong_sat_add( BUFFER_METADATA_SIZE, instruction->inner.write.offset );
1069 0 : err = write_program_data( instr_ctx,
1070 0 : 0UL,
1071 0 : program_data_offset,
1072 0 : instruction->inner.write.bytes,
1073 0 : instruction->inner.write.bytes_len );
1074 0 : if( FD_UNLIKELY( err ) ) {
1075 0 : return err;
1076 0 : }
1077 :
1078 0 : break;
1079 0 : }
1080 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L526-L702 */
1081 6 : case FD_BPF_INSTR_DEPLOY_WITH_MAX_DATA_LEN: {
1082 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L527-L541 */
1083 6 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 4U ) ) ) {
1084 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1085 0 : }
1086 :
1087 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L529-L534 */
1088 6 : fd_pubkey_t const * payer_key = NULL;
1089 6 : fd_pubkey_t const * programdata_key = NULL;
1090 :
1091 6 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 0UL, &payer_key );
1092 6 : if( FD_UNLIKELY( err ) ) {
1093 0 : return err;
1094 0 : }
1095 :
1096 6 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &programdata_key );
1097 6 : if( FD_UNLIKELY( err ) ) {
1098 0 : return err;
1099 0 : }
1100 :
1101 6 : err = fd_sysvar_instr_acct_check( instr_ctx, 4UL, &fd_sysvar_rent_id );
1102 6 : if( FD_UNLIKELY( err ) ) {
1103 0 : return err;
1104 0 : }
1105 :
1106 6 : fd_rent_t rent_;
1107 6 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
1108 6 : if( FD_UNLIKELY( !rent ) ) {
1109 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1110 0 : }
1111 :
1112 6 : err = fd_sysvar_instr_acct_check( instr_ctx, 5UL, &fd_sysvar_clock_id );
1113 6 : if( FD_UNLIKELY( err ) ) {
1114 0 : return err;
1115 0 : }
1116 :
1117 6 : fd_sol_sysvar_clock_t clock_;
1118 6 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
1119 6 : if( FD_UNLIKELY( !clock ) ) {
1120 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1121 0 : }
1122 :
1123 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L538 */
1124 6 : if( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 8U ) ) {
1125 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1126 0 : }
1127 :
1128 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L539-L541 */
1129 6 : fd_pubkey_t const * authority_key = NULL;
1130 6 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 7UL, &authority_key );
1131 6 : if( FD_UNLIKELY( err ) ) return err;
1132 :
1133 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L542-L560 */
1134 : /* Verify Program account */
1135 6 : fd_pubkey_t const * new_program_id = NULL;
1136 :
1137 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L545 */
1138 6 : fd_guarded_borrowed_account_t program = {0};
1139 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &program );
1140 :
1141 6 : fd_bpf_state_t loader_state[1];
1142 6 : int err = fd_bpf_loader_program_get_state( program.acc, loader_state );
1143 6 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1144 0 : return err;
1145 0 : }
1146 6 : if( FD_UNLIKELY( loader_state->discriminant!=FD_BPF_STATE_UNINITIALIZED ) ) {
1147 0 : fd_log_collector_msg_literal( instr_ctx, "Program account already initialized" );
1148 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
1149 0 : }
1150 6 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &program )<SIZE_OF_PROGRAM ) ) {
1151 0 : fd_log_collector_msg_literal( instr_ctx, "Program account too small" );
1152 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1153 0 : }
1154 6 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &program )<
1155 6 : fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &program ) ) ) ) {
1156 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not rent-exempt" );
1157 0 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT;
1158 0 : }
1159 6 : new_program_id = (fd_pubkey_t*)program.acc->pubkey;
1160 :
1161 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L560 */
1162 6 : fd_borrowed_account_drop( &program );
1163 :
1164 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L561-L600 */
1165 : /* Verify Buffer account */
1166 :
1167 6 : fd_pubkey_t const* buffer_key = NULL;
1168 6 : ulong buffer_data_offset = 0UL;
1169 6 : ulong buffer_data_len = 0UL;
1170 6 : ulong programdata_len = 0UL;
1171 :
1172 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L564-L565 */
1173 6 : fd_guarded_borrowed_account_t buffer = {0};
1174 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1175 :
1176 : /* https://github.com/anza-xyz/agave/blob/645f638832e19269d7d1c8614bed3b3b9badb158/programs/bpf_loader/src/lib.rs#L234-L241 */
1177 6 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &buffer ) ) ) {
1178 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not writeable" );
1179 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1180 0 : }
1181 :
1182 6 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &buffer ), sizeof(fd_pubkey_t) ) ) ) {
1183 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not owned by loader" );
1184 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1185 0 : }
1186 :
1187 6 : fd_bpf_state_t buffer_state[1];
1188 6 : err = fd_bpf_loader_program_get_state( buffer.acc, buffer_state );
1189 6 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1190 0 : return err;
1191 0 : }
1192 :
1193 6 : if( buffer_state->discriminant==FD_BPF_STATE_BUFFER ) {
1194 6 : if( FD_UNLIKELY( (authority_key==NULL) != (!buffer_state->inner.buffer.has_authority_address) ||
1195 6 : (authority_key!=NULL && !fd_pubkey_eq( &buffer_state->inner.buffer.authority_address, authority_key ) ) ) ) {
1196 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer and upgrade authority don't match" );
1197 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1198 0 : }
1199 6 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 7UL, &err ) ) ) {
1200 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1201 0 : if( FD_UNLIKELY( !!err ) ) return err;
1202 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1203 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1204 0 : }
1205 6 : } else {
1206 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Buffer account" );
1207 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1208 0 : }
1209 6 : buffer_key = (fd_pubkey_t*)buffer.acc->pubkey;
1210 6 : buffer_data_offset = BUFFER_METADATA_SIZE;
1211 6 : buffer_data_len = fd_ulong_sat_sub( fd_borrowed_account_get_data_len( &buffer ), buffer_data_offset );
1212 : /* UpgradeableLoaderState::size_of_program_data( max_data_len ) */
1213 6 : programdata_len = fd_ulong_sat_add( PROGRAMDATA_METADATA_SIZE,
1214 6 : instruction->inner.deploy_with_max_data_len.max_data_len );
1215 :
1216 6 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &buffer )<BUFFER_METADATA_SIZE || buffer_data_len==0UL ) ) {
1217 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account too small" );
1218 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1219 0 : }
1220 :
1221 6 : if( FD_UNLIKELY( instruction->inner.deploy_with_max_data_len.max_data_len<buffer_data_len ) ) {
1222 0 : fd_log_collector_msg_literal( instr_ctx, "Max data length is too small to hold Buffer data" );
1223 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1224 0 : }
1225 :
1226 6 : if( FD_UNLIKELY( programdata_len>MAX_PERMITTED_DATA_LENGTH ) ) {
1227 0 : fd_log_collector_msg_literal( instr_ctx, "Max data length is too large" );
1228 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1229 0 : }
1230 :
1231 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L590 */
1232 6 : fd_borrowed_account_drop( &buffer );
1233 :
1234 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L602-L608 */
1235 : /* Create ProgramData account */
1236 :
1237 6 : fd_pubkey_t derived_address[ 1UL ];
1238 6 : uchar const * seeds[ 1UL ];
1239 6 : seeds[ 0UL ] = (uchar const *)new_program_id;
1240 6 : ulong seed_sz = sizeof(fd_pubkey_t);
1241 6 : uchar bump_seed = 0;
1242 6 : err = fd_pubkey_find_program_address( program_id, 1UL, seeds, &seed_sz, derived_address,
1243 6 : &bump_seed, &instr_ctx->txn_out->err.custom_err );
1244 6 : if( FD_UNLIKELY( err ) ) {
1245 : /* TODO: We should handle these errors more gracefully instead of just killing the client (e.g. excluding the transaction
1246 : from the block). */
1247 0 : FD_LOG_ERR(( "Unable to find a viable program address bump seed" )); // Solana panics, error code is undefined
1248 0 : return err;
1249 0 : }
1250 6 : if( FD_UNLIKELY( memcmp( derived_address, programdata_key, sizeof(fd_pubkey_t) ) ) ) {
1251 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData address is not derived" );
1252 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1253 0 : }
1254 :
1255 : /* Drain the Buffer account to payer before paying for programdata account in a local scope
1256 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L612-L628 */
1257 :
1258 6 : do {
1259 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L615 */
1260 6 : fd_guarded_borrowed_account_t payer = {0};
1261 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &payer );
1262 :
1263 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L613 */
1264 6 : fd_guarded_borrowed_account_t buffer = {0};
1265 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1266 :
1267 6 : err = fd_borrowed_account_checked_add_lamports( &payer, fd_borrowed_account_get_lamports( &buffer ) );
1268 6 : if( FD_UNLIKELY( err ) ) {
1269 0 : return err;
1270 0 : }
1271 6 : err = fd_borrowed_account_set_lamports( &buffer, 0UL );
1272 6 : if( FD_UNLIKELY( err ) ) {
1273 0 : return err;
1274 0 : }
1275 6 : } while (0);
1276 :
1277 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L628-L642 */
1278 : /* Pass an extra account to avoid the overly strict unbalanced instruction error */
1279 : /* Invoke the system program to create the new account */
1280 6 : uchar instr_data[FD_TXN_MTU];
1281 6 : create_account_t create_acct = {
1282 6 : .lamports = fd_rent_exempt_minimum_balance( rent, programdata_len ),
1283 6 : .space = programdata_len,
1284 6 : .owner = *program_id,
1285 6 : };
1286 6 : if( !create_acct.lamports ) {
1287 0 : create_acct.lamports = 1UL;
1288 0 : }
1289 :
1290 6 : fd_system_program_instruction_t instr = {
1291 6 : .discriminant = FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT,
1292 6 : .inner = {
1293 6 : .create_account = create_acct,
1294 6 : }
1295 6 : };
1296 :
1297 6 : ulong instr_data_sz;
1298 6 : err = fd_system_program_instruction_encode( &instr, instr_data, FD_TXN_MTU, &instr_data_sz );
1299 6 : if( FD_UNLIKELY( err ) ) {
1300 0 : FD_LOG_CRIT(( "Failed to encode SystemInstruction with error %d", err ));
1301 0 : }
1302 :
1303 6 : fd_vm_rust_account_meta_t acct_metas[ 3UL ];
1304 6 : fd_native_cpi_create_account_meta( payer_key, 1U, 1U, &acct_metas[ 0UL ] );
1305 6 : fd_native_cpi_create_account_meta( programdata_key, 1U, 1U, &acct_metas[ 1UL ] );
1306 6 : fd_native_cpi_create_account_meta( buffer_key, 0U, 1U, &acct_metas[ 2UL ] );
1307 :
1308 : /* caller_program_id == program_id */
1309 6 : fd_pubkey_t signers[ 1UL ];
1310 6 : err = fd_pubkey_derive_pda( program_id, 1UL, seeds, &seed_sz, &bump_seed, signers, &instr_ctx->txn_out->err.custom_err );
1311 6 : if( FD_UNLIKELY( err ) ) {
1312 0 : return err;
1313 0 : }
1314 6 : err = fd_native_cpi_native_invoke( instr_ctx,
1315 6 : &fd_solana_system_program_id,
1316 6 : instr_data,
1317 6 : instr_data_sz,
1318 6 : acct_metas,
1319 6 : 3UL,
1320 6 : signers,
1321 6 : 1UL );
1322 6 : if( FD_UNLIKELY( err ) ) {
1323 0 : return err;
1324 0 : }
1325 :
1326 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L644-L665 */
1327 : /* Load and verify the program bits */
1328 :
1329 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L648-L649 */
1330 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1331 :
1332 6 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ) {
1333 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1334 0 : }
1335 :
1336 6 : const uchar * buffer_data = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1337 :
1338 6 : err = fd_deploy_program( instr_ctx, buffer_data, buffer_data_len,
1339 6 : FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, disable_sbpf_v0_v1_v2_deployment ) );
1340 6 : if( FD_UNLIKELY( err ) ) {
1341 0 : return err;
1342 0 : }
1343 :
1344 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L657 */
1345 6 : fd_borrowed_account_drop( &buffer );
1346 :
1347 : /* Update the ProgramData account and record the program bits in a local scope
1348 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L669-L691 */
1349 6 : do {
1350 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L670-L671 */
1351 6 : fd_guarded_borrowed_account_t programdata = {0};
1352 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &programdata );
1353 :
1354 6 : fd_bpf_state_t programdata_loader_state = {
1355 6 : .discriminant = FD_BPF_STATE_PROGRAM_DATA,
1356 6 : .inner.program_data = {
1357 6 : .slot = clock->slot,
1358 6 : .has_upgrade_authority_address = !!authority_key,
1359 6 : .upgrade_authority_address = authority_key ? *authority_key : (fd_pubkey_t){{0}},
1360 6 : },
1361 6 : };
1362 6 : err = fd_bpf_loader_v3_program_set_state( &programdata, &programdata_loader_state );
1363 6 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1364 0 : return err;
1365 0 : }
1366 6 : programdata.acc->pd_write = 1;
1367 :
1368 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L675-L689 */
1369 6 : if( FD_UNLIKELY( PROGRAMDATA_METADATA_SIZE+buffer_data_len>fd_borrowed_account_get_data_len( &programdata ) ) ) {
1370 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1371 0 : }
1372 :
1373 6 : uchar * programdata_data = NULL;
1374 6 : ulong programdata_dlen = 0UL;
1375 6 : err = fd_borrowed_account_get_data_mut( &programdata, &programdata_data, &programdata_dlen );
1376 6 : if( FD_UNLIKELY( err ) ) {
1377 0 : return err;
1378 0 : }
1379 :
1380 6 : uchar * dst_slice = programdata_data + PROGRAMDATA_METADATA_SIZE;
1381 6 : ulong dst_slice_len = buffer_data_len;
1382 :
1383 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L683-L684 */
1384 6 : fd_guarded_borrowed_account_t buffer = {0};
1385 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1386 :
1387 6 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ) {
1388 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1389 0 : }
1390 6 : const uchar * src_slice = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1391 6 : fd_memcpy( dst_slice, src_slice, dst_slice_len );
1392 : /* Update buffer data length.
1393 : BUFFER_METADATA_SIZE == UpgradeableLoaderState::size_of_buffer(0) */
1394 6 : err = fd_borrowed_account_set_data_length( &buffer, BUFFER_METADATA_SIZE );
1395 6 : if( FD_UNLIKELY( err ) ) {
1396 0 : return err;
1397 0 : }
1398 6 : } while(0);
1399 :
1400 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L692-L699 */
1401 :
1402 : /* Update the Program account
1403 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L694-L695 */
1404 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &program );
1405 :
1406 6 : loader_state->discriminant = FD_BPF_STATE_PROGRAM;
1407 6 : loader_state->inner.program.programdata_address = *programdata_key;
1408 6 : err = fd_bpf_loader_v3_program_set_state( &program, loader_state );
1409 6 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1410 0 : return err;
1411 0 : }
1412 6 : err = fd_borrowed_account_set_executable( &program, 1 );
1413 6 : if( FD_UNLIKELY( err ) ) {
1414 0 : return err;
1415 0 : }
1416 :
1417 6 : FD_BASE58_ENCODE_32_BYTES( program.acc->pubkey, program_b58 );
1418 6 : FD_LOG_INFO(( "Program deployed %s", program_b58 ));
1419 :
1420 : /* Max msg_sz: 19 - 2 + 45 = 62 < 127 => we can use printf */
1421 6 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Deployed program %s", program_b58 );
1422 :
1423 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L700 */
1424 6 : fd_borrowed_account_drop( &program );
1425 :
1426 6 : break;
1427 6 : }
1428 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L703-L891 */
1429 0 : case FD_BPF_INSTR_UPGRADE: {
1430 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L704-L714 */
1431 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 3U ) ) ) {
1432 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1433 0 : }
1434 :
1435 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L706-L708 */
1436 0 : fd_pubkey_t const * programdata_key = NULL;
1437 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 0UL, &programdata_key );
1438 0 : if( FD_UNLIKELY( err ) ) {
1439 0 : return err;
1440 0 : }
1441 :
1442 : /* rent is accessed directly from the epoch bank and the clock from the
1443 : slot context. However, a check must be done to make sure that the
1444 : sysvars are correctly included in the set of transaction accounts. */
1445 0 : err = fd_sysvar_instr_acct_check( instr_ctx, 4UL, &fd_sysvar_rent_id );
1446 0 : if( FD_UNLIKELY( err ) ) {
1447 0 : return err;
1448 0 : }
1449 :
1450 0 : fd_rent_t rent_;
1451 0 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
1452 0 : if( FD_UNLIKELY( !rent ) ) {
1453 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1454 0 : }
1455 :
1456 0 : err = fd_sysvar_instr_acct_check( instr_ctx, 5UL, &fd_sysvar_clock_id );
1457 0 : if( FD_UNLIKELY( err ) ) {
1458 0 : return err;
1459 0 : }
1460 :
1461 0 : fd_sol_sysvar_clock_t clock_;
1462 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
1463 0 : if( FD_UNLIKELY( !clock ) ) {
1464 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1465 0 : }
1466 :
1467 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 7U ) ) ) {
1468 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1469 0 : }
1470 :
1471 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L713-L715 */
1472 0 : fd_pubkey_t const * authority_key = NULL;
1473 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 6UL, &authority_key );
1474 0 : if( FD_UNLIKELY( err ) ) return err;
1475 :
1476 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L716-L745 */
1477 : /* Verify Program account */
1478 :
1479 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L719-L720 */
1480 0 : fd_guarded_borrowed_account_t program = {0};
1481 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &program );
1482 :
1483 0 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &program ) ) ) {
1484 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not writeable" );
1485 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1486 0 : }
1487 0 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &program ), program_id, sizeof(fd_pubkey_t) ) ) ) {
1488 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not owned by loader" );
1489 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1490 0 : }
1491 0 : fd_bpf_state_t program_state[1];
1492 0 : err = fd_bpf_loader_program_get_state( program.acc, program_state );
1493 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1494 0 : return err;
1495 0 : }
1496 0 : if( FD_UNLIKELY( program_state->discriminant==FD_BPF_STATE_PROGRAM ) ) {
1497 0 : if( FD_UNLIKELY( memcmp( &program_state->inner.program.programdata_address, programdata_key, sizeof(fd_pubkey_t) ) ) ) {
1498 0 : fd_log_collector_msg_literal( instr_ctx, "Program and ProgramData account mismatch" );
1499 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1500 0 : }
1501 0 : } else {
1502 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Program account" );
1503 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1504 0 : }
1505 :
1506 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L746 */
1507 0 : fd_pubkey_t new_program_id = *(fd_pubkey_t*)program.acc->pubkey;
1508 0 : fd_borrowed_account_drop( &program );
1509 :
1510 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L747-L773 */
1511 : /* Verify Buffer account */
1512 :
1513 0 : ulong buffer_lamports = 0UL;
1514 0 : ulong buffer_data_offset = 0UL;
1515 0 : ulong buffer_data_len = 0UL;
1516 :
1517 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L750-L751 */
1518 0 : fd_guarded_borrowed_account_t buffer = {0};
1519 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1520 :
1521 : /* https://github.com/anza-xyz/agave/blob/645f638832e19269d7d1c8614bed3b3b9badb158/programs/bpf_loader/src/lib.rs#L407-L414 */
1522 0 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &buffer ) ) ) {
1523 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not writeable" );
1524 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1525 0 : }
1526 :
1527 0 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &buffer ), sizeof(fd_pubkey_t) ) ) ) {
1528 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not owned by loader" );
1529 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1530 0 : }
1531 :
1532 0 : fd_bpf_state_t buffer_state[1];
1533 0 : err = fd_bpf_loader_program_get_state( buffer.acc, buffer_state );
1534 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1535 0 : return err;
1536 0 : }
1537 0 : if( buffer_state->discriminant==FD_BPF_STATE_BUFFER ) {
1538 0 : if( FD_UNLIKELY( (authority_key==NULL) != (!buffer_state->inner.buffer.has_authority_address) ||
1539 0 : (authority_key!=NULL && !fd_pubkey_eq( &buffer_state->inner.buffer.authority_address, authority_key ) ) ) ) {
1540 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer and upgrade authority don't match" );
1541 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1542 0 : }
1543 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 6UL, &err ) ) ) {
1544 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1545 0 : if( FD_UNLIKELY( !!err ) ) return err;
1546 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1547 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1548 0 : }
1549 0 : } else {
1550 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Buffer account" );
1551 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1552 0 : }
1553 0 : buffer_lamports = fd_borrowed_account_get_lamports( &buffer );
1554 0 : buffer_data_offset = BUFFER_METADATA_SIZE;
1555 0 : buffer_data_len = fd_ulong_sat_sub( fd_borrowed_account_get_data_len( &buffer ), buffer_data_offset );
1556 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &buffer )<BUFFER_METADATA_SIZE || buffer_data_len==0UL ) ) {
1557 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account too small" );
1558 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1559 0 : }
1560 :
1561 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L774 */
1562 0 : fd_borrowed_account_drop( &buffer );
1563 :
1564 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L775-L823 */
1565 : /* Verify ProgramData account */
1566 :
1567 0 : ulong programdata_data_offset = PROGRAMDATA_METADATA_SIZE;
1568 0 : ulong programdata_balance_required = 0UL;
1569 :
1570 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L778-L779 */
1571 0 : fd_guarded_borrowed_account_t programdata = {0};
1572 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &programdata );
1573 :
1574 0 : programdata_balance_required = fd_ulong_max( 1UL, fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &programdata ) ) );
1575 :
1576 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &programdata )<fd_ulong_sat_add( PROGRAMDATA_METADATA_SIZE, buffer_data_len ) ) ) {
1577 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData account not large enough" );
1578 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1579 0 : }
1580 0 : if( FD_UNLIKELY( fd_ulong_sat_add( fd_borrowed_account_get_lamports( &programdata ), buffer_lamports )<programdata_balance_required ) ) {
1581 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account balance too low to fund upgrade" );
1582 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1583 0 : }
1584 :
1585 0 : fd_bpf_state_t programdata_state[1];
1586 0 : err = fd_bpf_loader_program_get_state( programdata.acc, programdata_state );
1587 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1588 0 : return err;
1589 0 : }
1590 :
1591 0 : if( programdata_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
1592 0 : if( FD_UNLIKELY( clock->slot==programdata_state->inner.program_data.slot ) ) {
1593 0 : fd_log_collector_msg_literal( instr_ctx, "Program was deployed in this block already" );
1594 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1595 0 : }
1596 0 : if( FD_UNLIKELY( !programdata_state->inner.program_data.has_upgrade_authority_address ) ) {
1597 0 : fd_log_collector_msg_literal( instr_ctx, "Program not upgradeable" );
1598 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1599 0 : }
1600 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &programdata_state->inner.program_data.upgrade_authority_address, authority_key ) ) ) {
1601 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
1602 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1603 0 : }
1604 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 6UL, &err ) ) ) {
1605 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1606 0 : if( FD_UNLIKELY( !!err ) ) return err;
1607 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1608 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1609 0 : }
1610 0 : } else {
1611 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid ProgramData account" );
1612 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1613 0 : }
1614 :
1615 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L824 */
1616 0 : fd_borrowed_account_drop( &programdata );
1617 :
1618 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L825-L845 */
1619 : /* Load and verify the program bits */
1620 :
1621 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L827-L828 */
1622 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1623 :
1624 0 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ) {
1625 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1626 0 : }
1627 :
1628 0 : const uchar * buffer_data = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1629 0 : err = fd_deploy_program( instr_ctx, buffer_data, buffer_data_len,
1630 0 : FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, disable_sbpf_v0_v1_v2_deployment ) );
1631 0 : if( FD_UNLIKELY( err ) ) {
1632 0 : return err;
1633 0 : }
1634 :
1635 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L836 */
1636 0 : fd_borrowed_account_drop( &buffer );
1637 :
1638 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L849-L850 */
1639 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &programdata );
1640 :
1641 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L846-L874 */
1642 : /* Update the ProgramData account, record the upgraded data, and zero the rest in a local scope */
1643 0 : do {
1644 0 : programdata_state->discriminant = FD_BPF_STATE_PROGRAM_DATA;
1645 0 : programdata_state->inner.program_data.slot = clock->slot;
1646 0 : programdata_state->inner.program_data.has_upgrade_authority_address = 1;
1647 0 : programdata_state->inner.program_data.upgrade_authority_address = *authority_key;
1648 0 : err = fd_bpf_loader_v3_program_set_state( &programdata, programdata_state );
1649 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1650 0 : return err;
1651 0 : }
1652 0 : programdata.acc->pd_write = 1;
1653 :
1654 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L846-L875 */
1655 : /* We want to copy over the data and zero out the rest */
1656 0 : if( FD_UNLIKELY( programdata_data_offset+buffer_data_len>fd_borrowed_account_get_data_len( &programdata ) ) ) {
1657 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1658 0 : }
1659 :
1660 0 : uchar * programdata_data = NULL;
1661 0 : ulong programdata_dlen = 0UL;
1662 0 : err = fd_borrowed_account_get_data_mut( &programdata, &programdata_data, &programdata_dlen );
1663 0 : if( FD_UNLIKELY( err ) ) {
1664 0 : return err;
1665 0 : }
1666 0 : uchar * dst_slice = programdata_data + programdata_data_offset;
1667 0 : ulong dst_slice_len = buffer_data_len;
1668 :
1669 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L863-L864 */
1670 0 : fd_guarded_borrowed_account_t buffer = {0};
1671 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1672 :
1673 0 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ){
1674 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1675 0 : }
1676 :
1677 0 : const uchar * src_slice = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1678 0 : fd_memcpy( dst_slice, src_slice, dst_slice_len );
1679 0 : fd_memset( dst_slice + dst_slice_len, 0, fd_borrowed_account_get_data_len( &programdata ) - programdata_data_offset - dst_slice_len );
1680 :
1681 : /* implicit drop of buffer */
1682 0 : } while (0);
1683 :
1684 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L876-L891 */
1685 : /* Fund ProgramData to rent-exemption, spill the rest */
1686 :
1687 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L878-L879 */
1688 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1689 :
1690 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L880-L881 */
1691 0 : fd_guarded_borrowed_account_t spill = {0};
1692 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &spill );
1693 :
1694 0 : ulong spill_addend = fd_ulong_sat_sub( fd_ulong_sat_add( fd_borrowed_account_get_lamports( &programdata ), buffer_lamports ),
1695 0 : programdata_balance_required );
1696 0 : err = fd_borrowed_account_checked_add_lamports( &spill, spill_addend );
1697 0 : if( FD_UNLIKELY( err ) ) {
1698 0 : return err;
1699 0 : }
1700 0 : err = fd_borrowed_account_set_lamports( &buffer, 0UL );
1701 0 : if( FD_UNLIKELY( err ) ) {
1702 0 : return err;
1703 0 : }
1704 0 : err = fd_borrowed_account_set_lamports( &programdata, programdata_balance_required );
1705 0 : if( FD_UNLIKELY( err ) ) {
1706 0 : return err;
1707 0 : }
1708 :
1709 : /* Buffer account set_data_length */
1710 0 : err = fd_borrowed_account_set_data_length( &buffer, BUFFER_METADATA_SIZE );
1711 0 : if( FD_UNLIKELY( err ) ) {
1712 0 : return err;
1713 0 : }
1714 :
1715 : /* buffer is dropped when it goes out of scope */
1716 : /* spill is dropped when it goes out of scope */
1717 : /* programdata is dropped when it goes out of scope */
1718 :
1719 : /* Max msg_sz: 19 - 2 + 45 = 62 < 127 => we can use printf */
1720 0 : FD_BASE58_ENCODE_32_BYTES( new_program_id.uc, program_id_b58 );
1721 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Upgraded program %s", program_id_b58 );
1722 :
1723 0 : break;
1724 0 : }
1725 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L893-L957 */
1726 3 : case FD_BPF_INSTR_SET_AUTHORITY: {
1727 3 : int err;
1728 3 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
1729 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1730 0 : }
1731 :
1732 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L896-L897 */
1733 3 : fd_guarded_borrowed_account_t account = {0};
1734 3 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &account );
1735 :
1736 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L898-L900 */
1737 3 : fd_pubkey_t const * present_authority_key = NULL;
1738 3 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &present_authority_key );
1739 3 : if( FD_UNLIKELY( err ) ) {
1740 0 : return err;
1741 0 : }
1742 :
1743 : /* Don't check the error here because the new_authority key is allowed to be NULL until further checks.
1744 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L901-L906 */
1745 3 : fd_pubkey_t const * new_authority = NULL;
1746 3 : fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &new_authority );
1747 :
1748 3 : fd_bpf_state_t account_state[1];
1749 3 : err = fd_bpf_loader_program_get_state( account.acc, account_state );
1750 3 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1751 0 : return err;
1752 0 : }
1753 :
1754 3 : if( account_state->discriminant==FD_BPF_STATE_BUFFER ) {
1755 0 : if( FD_UNLIKELY( !new_authority ) ) {
1756 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority is not optional" );
1757 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1758 0 : }
1759 0 : if( FD_UNLIKELY( !account_state->inner.buffer.has_authority_address ) ) {
1760 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer is immutable" );
1761 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1762 0 : }
1763 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.buffer.authority_address, present_authority_key ) ) ) {
1764 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect buffer authority provided" );
1765 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1766 0 : }
1767 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &err ) ) ) {
1768 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1769 0 : if( FD_UNLIKELY( !!err ) ) return err;
1770 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority did not sign" );
1771 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1772 0 : }
1773 :
1774 : /* copy in the authority public key into the authority address.
1775 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L926-L928 */
1776 0 : account_state->inner.buffer.has_authority_address = !!new_authority;
1777 0 : if( new_authority ) {
1778 0 : account_state->inner.buffer.authority_address = *new_authority;
1779 0 : }
1780 :
1781 0 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
1782 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1783 0 : return err;
1784 0 : }
1785 3 : } else if( account_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
1786 3 : if( FD_UNLIKELY( !account_state->inner.program_data.has_upgrade_authority_address ) ) {
1787 0 : fd_log_collector_msg_literal( instr_ctx, "Program not upgradeable" );
1788 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1789 0 : }
1790 3 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.program_data.upgrade_authority_address, present_authority_key ) ) ) {
1791 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
1792 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1793 0 : }
1794 3 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &err ) ) ) {
1795 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1796 0 : if( FD_UNLIKELY( !!err ) ) return err;
1797 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1798 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1799 0 : }
1800 :
1801 : /* SIMD-0500: forbid finalize (SetAuthority None) on programs
1802 : that are not at least SBPF v3. */
1803 3 : if( !new_authority ) {
1804 0 : int rc = fd_bpf_loader_finalize_v3_check(
1805 0 : FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, disable_sbpf_v0_v1_v2_deployment ),
1806 0 : fd_borrowed_account_get_data ( &account ),
1807 0 : fd_borrowed_account_get_data_len( &account ) );
1808 0 : if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) return rc;
1809 0 : }
1810 :
1811 : /* copy in the authority public key into the upgrade authority address.
1812 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L946-L949 */
1813 3 : account_state->inner.program_data.has_upgrade_authority_address = !!new_authority;
1814 3 : if( new_authority ) {
1815 3 : account_state->inner.program_data.upgrade_authority_address = *new_authority;
1816 3 : }
1817 :
1818 3 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
1819 3 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1820 0 : return err;
1821 0 : }
1822 3 : } else {
1823 0 : fd_log_collector_msg_literal( instr_ctx, "Account does not support authorities" );
1824 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1825 0 : }
1826 :
1827 : /* Max msg_sz: 16 - 2 + 45 = 59 < 127 => we can use printf */
1828 3 : if( new_authority ) {
1829 3 : FD_BASE58_ENCODE_32_BYTES( new_authority->uc, new_authority_b58 );
1830 3 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "New authority Some(%s)", new_authority_b58 );
1831 3 : } else {
1832 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "New authority None" );
1833 0 : }
1834 :
1835 : /* implicit drop of account */
1836 :
1837 3 : break;
1838 3 : }
1839 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L958-L1030 */
1840 0 : case FD_BPF_INSTR_SET_AUTHORITY_CHECKED: {
1841 0 : int err;
1842 0 : if( !FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, enable_bpf_loader_set_authority_checked_ix ) ) {
1843 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1844 0 : }
1845 :
1846 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 3U ) ) ) {
1847 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1848 0 : }
1849 :
1850 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L968-L969 */
1851 0 : fd_guarded_borrowed_account_t account = {0};
1852 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &account );
1853 :
1854 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L970-L975 */
1855 0 : fd_pubkey_t const * present_authority_key = NULL;
1856 0 : fd_pubkey_t const * new_authority_key = NULL;
1857 :
1858 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &present_authority_key );
1859 0 : if( FD_UNLIKELY( err ) ) return err;
1860 :
1861 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &new_authority_key );
1862 0 : if( FD_UNLIKELY( err ) ) return err;
1863 :
1864 0 : fd_bpf_state_t account_state[1];
1865 0 : err = fd_bpf_loader_program_get_state( account.acc, account_state );
1866 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1867 0 : return err;
1868 0 : }
1869 :
1870 0 : if( account_state->discriminant==FD_BPF_STATE_BUFFER ) {
1871 0 : if( FD_UNLIKELY( !account_state->inner.buffer.has_authority_address ) ) {
1872 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer is immutable" );
1873 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1874 0 : }
1875 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.buffer.authority_address, present_authority_key ) ) ) {
1876 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect buffer authority provided" );
1877 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1878 0 : }
1879 0 : int instr_err_code = 0;
1880 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &instr_err_code ) ) ) {
1881 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1882 0 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1883 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority did not sign" );
1884 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1885 0 : }
1886 0 : instr_err_code = 0;
1887 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL, &instr_err_code ) ) ) {
1888 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1889 0 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1890 0 : fd_log_collector_msg_literal( instr_ctx, "New authority did not sign" );
1891 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1892 0 : }
1893 0 : account_state->inner.buffer.has_authority_address = 1;
1894 0 : account_state->inner.buffer.authority_address = *new_authority_key;
1895 0 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
1896 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1897 0 : return err;
1898 0 : }
1899 0 : } else if( account_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
1900 0 : if( FD_UNLIKELY( !account_state->inner.program_data.has_upgrade_authority_address ) ) {
1901 0 : fd_log_collector_msg_literal( instr_ctx, "Program not upgradeable" );
1902 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1903 0 : }
1904 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.program_data.upgrade_authority_address, present_authority_key ) ) ) {
1905 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
1906 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1907 0 : }
1908 0 : int instr_err_code = 0;
1909 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &instr_err_code ) ) ) {
1910 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1911 0 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1912 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1913 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1914 0 : }
1915 0 : instr_err_code = 0;
1916 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL, &instr_err_code ) ) ) {
1917 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1918 0 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1919 0 : fd_log_collector_msg_literal( instr_ctx, "New authority did not sign" );
1920 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1921 0 : }
1922 0 : account_state->inner.program_data.has_upgrade_authority_address = 1;
1923 0 : account_state->inner.program_data.upgrade_authority_address = *new_authority_key;
1924 0 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
1925 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1926 0 : return err;
1927 0 : }
1928 0 : } else {
1929 0 : fd_log_collector_msg_literal( instr_ctx, "Account does not support authorities" );
1930 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1931 0 : }
1932 :
1933 : /* Max msg_sz: 16 - 2 + 45 = 59 < 127 => we can use printf */
1934 0 : FD_BASE58_ENCODE_32_BYTES( new_authority_key->uc, new_authority_b58 );
1935 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "New authority %s", new_authority_b58 );
1936 :
1937 : /* implicit drop of account */
1938 :
1939 0 : break;
1940 0 : }
1941 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1031-L1134 */
1942 15 : case FD_BPF_INSTR_CLOSE: {
1943 15 : int err;
1944 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1032-L1046 */
1945 15 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
1946 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1947 0 : }
1948 :
1949 : /* It's safe to directly access the instruction accounts because we already checked for two
1950 : instruction accounts previously.
1951 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1034-L1035 */
1952 15 : if( FD_UNLIKELY( instr_ctx->instr->accounts[ 0UL ].index_in_transaction ==
1953 15 : instr_ctx->instr->accounts[ 1UL ].index_in_transaction ) ) {
1954 0 : fd_log_collector_msg_literal( instr_ctx, "Recipient is the same as the account being closed" );
1955 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1956 0 : }
1957 :
1958 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1043-L1044 */
1959 15 : fd_guarded_borrowed_account_t close_account = {0};
1960 15 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &close_account );
1961 :
1962 15 : fd_pubkey_t const * close_key = (fd_pubkey_t*)close_account.acc->pubkey;
1963 15 : fd_bpf_state_t close_account_state[1];
1964 15 : err = fd_bpf_loader_program_get_state( close_account.acc, close_account_state );
1965 15 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1966 0 : return err;
1967 0 : }
1968 : /* Close account set data length */
1969 15 : err = fd_borrowed_account_set_data_length( &close_account, SIZE_OF_UNINITIALIZED );
1970 15 : if( FD_UNLIKELY( err ) ) {
1971 0 : return err;
1972 0 : }
1973 :
1974 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1049-L1056 */
1975 15 : if( close_account_state->discriminant==FD_BPF_STATE_UNINITIALIZED ) {
1976 :
1977 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1050-L1051 */
1978 6 : fd_guarded_borrowed_account_t recipient_account = {0};
1979 6 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &recipient_account );
1980 :
1981 6 : err = fd_borrowed_account_checked_add_lamports( &recipient_account, fd_borrowed_account_get_lamports( &close_account ) );
1982 6 : if( FD_UNLIKELY( err ) ) {
1983 0 : return err;
1984 0 : }
1985 6 : err = fd_borrowed_account_set_lamports( &close_account, 0UL );
1986 6 : if( FD_UNLIKELY( err ) ) {
1987 0 : return err;
1988 0 : }
1989 : /* Max msg_sz: 23 - 2 + 45 = 66 < 127 => we can use printf */
1990 6 : FD_BASE58_ENCODE_32_BYTES( close_key->uc, close_key_b58 );
1991 6 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Closed Uninitialized %s", close_key_b58 );
1992 :
1993 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1057-L1068 */
1994 9 : } else if( close_account_state->discriminant==FD_BPF_STATE_BUFFER ) {
1995 :
1996 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1059 */
1997 0 : fd_borrowed_account_drop( &close_account );
1998 :
1999 0 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 3U ) ) ) {
2000 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2001 0 : }
2002 :
2003 0 : fd_bpf_state_buffer_t * state_buf = &close_account_state->inner.buffer;
2004 0 : err = common_close_account(
2005 0 : state_buf->has_authority_address ? &state_buf->authority_address : NULL,
2006 0 : instr_ctx,
2007 0 : close_account_state );
2008 0 : if( FD_UNLIKELY( err ) ) return err;
2009 :
2010 : /* Max msg_sz: 16 - 2 + 45 = 63 < 127 => we can use printf */
2011 0 : FD_BASE58_ENCODE_32_BYTES( close_key->uc, close_key_b58 );
2012 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Closed Buffer %s", close_key_b58 );
2013 :
2014 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1069-L1129 */
2015 9 : } else if( close_account_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
2016 9 : int err;
2017 9 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 4U ) ) ) {
2018 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2019 0 : }
2020 :
2021 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1074 */
2022 9 : fd_borrowed_account_drop( &close_account );
2023 :
2024 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1075-L1076 */
2025 9 : fd_guarded_borrowed_account_t program_account = {0};
2026 9 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK(instr_ctx, 3UL, &program_account );
2027 :
2028 9 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &program_account ) ) ) {
2029 0 : fd_log_collector_msg_literal( instr_ctx, "Program account is not writable" );
2030 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2031 0 : }
2032 9 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &program_account ), program_id, sizeof(fd_pubkey_t) ) ) ) {
2033 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not owned by loader" );
2034 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
2035 0 : }
2036 9 : fd_sol_sysvar_clock_t clock_;
2037 9 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
2038 9 : if( FD_UNLIKELY( !clock ) ) {
2039 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2040 0 : }
2041 9 : if( FD_UNLIKELY( clock->slot==close_account_state->inner.program_data.slot ) ) {
2042 0 : fd_log_collector_msg_literal( instr_ctx,"Program was deployed in this block already" );
2043 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2044 0 : }
2045 :
2046 9 : fd_bpf_state_t program_state[1];
2047 9 : err = fd_bpf_loader_program_get_state( program_account.acc, program_state );
2048 9 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2049 0 : return err;
2050 0 : }
2051 :
2052 9 : if( program_state->discriminant==FD_BPF_STATE_PROGRAM ) {
2053 9 : if( FD_UNLIKELY( memcmp( &program_state->inner.program.programdata_address, close_key, sizeof(fd_pubkey_t) ) ) ) {
2054 0 : fd_log_collector_msg_literal( instr_ctx,"ProgramData account does not match ProgramData account" );
2055 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2056 0 : }
2057 :
2058 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1105 */
2059 9 : fd_borrowed_account_drop( &program_account );
2060 :
2061 9 : fd_bpf_state_program_data_t * pd = &close_account_state->inner.program_data;
2062 9 : err = common_close_account(
2063 9 : pd->has_upgrade_authority_address ? &pd->upgrade_authority_address : NULL,
2064 9 : instr_ctx,
2065 9 : close_account_state );
2066 9 : if( FD_UNLIKELY( err ) ) return err;
2067 :
2068 : /* Mark the closed programdata pd_write via the txn account (no
2069 : borrow is live here, and buffer/uninitialized closes going
2070 : through common_close_account must not set the bit). */
2071 9 : ushort pd_idx_in_txn;
2072 9 : err = fd_exec_instr_ctx_get_index_of_instr_account_in_transaction( instr_ctx, 0U, &pd_idx_in_txn );
2073 9 : if( FD_UNLIKELY( err ) ) return err; /* unreachable */
2074 9 : instr_ctx->txn_out->accounts.account[ pd_idx_in_txn ]->pd_write = 1;
2075 :
2076 : /* The Agave client updates the account state upon closing an account
2077 : in their loaded program cache. Checking for a program can be
2078 : checked by checking to see if the programdata account's loader state
2079 : is uninitialized. The firedancer implementation also removes closed
2080 : accounts from the loaded program cache at the end of a slot. Closed
2081 : accounts are not checked from the cache, instead the account state
2082 : is looked up. */
2083 :
2084 9 : } else {
2085 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Program account" );
2086 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2087 0 : }
2088 :
2089 : /* Max msg_sz: 17 - 2 + 45 = 60 < 127 => we can use printf */
2090 9 : FD_BASE58_ENCODE_32_BYTES( program_account.acc->pubkey, program_account_b58 );
2091 9 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Closed Program %s", program_account_b58 );
2092 :
2093 : /* program account is dropped when it goes out of scope */
2094 9 : } else {
2095 0 : fd_log_collector_msg_literal( instr_ctx, "Account does not support closing" );
2096 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2097 0 : }
2098 :
2099 : /* implicit drop of close account */
2100 15 : break;
2101 15 : }
2102 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1158-L1170 */
2103 15 : case FD_BPF_INSTR_EXTEND_PROGRAM: {
2104 3 : err = common_extend_program( instr_ctx, instruction->inner.extend_program.additional_bytes, 0 );
2105 3 : if( FD_UNLIKELY( err ) ) {
2106 0 : return err;
2107 0 : }
2108 :
2109 3 : break;
2110 3 : }
2111 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1171-L1179 */
2112 3 : case FD_BPF_INSTR_EXTEND_PROGRAM_CHECKED: {
2113 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2114 3 : }
2115 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/bpf_loader/src/lib.rs#L1338-L1508 */
2116 0 : case FD_BPF_INSTR_MIGRATE: {
2117 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/bpf_loader/src/lib.rs#L1339-L1344 */
2118 0 : if( FD_UNLIKELY( !FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, enable_loader_v4 ) ) ) {
2119 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2120 0 : }
2121 :
2122 : /* TODO: Implement me */
2123 0 : FD_LOG_WARNING(( "migrate instruction is not implemented" ));
2124 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2125 0 : }
2126 0 : default: {
2127 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2128 0 : }
2129 27 : }
2130 27 : return FD_EXECUTOR_INSTR_SUCCESS;
2131 27 : }
2132 :
2133 : /* process_instruction_inner() */
2134 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L394-L564 */
2135 : int
2136 2550 : fd_bpf_loader_program_execute( fd_exec_instr_ctx_t * ctx ) {
2137 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L491-L529 */
2138 :
2139 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L403-L404 */
2140 2550 : fd_guarded_borrowed_account_t program_account = {0};
2141 2550 : int err = fd_exec_instr_ctx_try_borrow_last_program_account( ctx, &program_account );
2142 2550 : if( FD_UNLIKELY( err ) ) {
2143 0 : return err;
2144 0 : }
2145 :
2146 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/bpf_loader/src/lib.rs#L409 */
2147 2550 : fd_pubkey_t const * program_id = NULL;
2148 2550 : err = fd_exec_instr_ctx_get_last_program_key( ctx, &program_id );
2149 2550 : if( FD_UNLIKELY( err ) ) {
2150 0 : return err;
2151 0 : }
2152 :
2153 : /* Program management instruction */
2154 2550 : if( FD_UNLIKELY( !memcmp( &fd_solana_native_loader_id, fd_borrowed_account_get_owner( &program_account ), sizeof(fd_pubkey_t) ) ) ) {
2155 : /* https://github.com/anza-xyz/agave/blob/v2.2.3/programs/bpf_loader/src/lib.rs#L416 */
2156 27 : fd_borrowed_account_drop( &program_account );
2157 :
2158 27 : if( FD_UNLIKELY( !memcmp( &fd_solana_bpf_loader_upgradeable_program_id, program_id, sizeof(fd_pubkey_t) ) ) ) {
2159 27 : FD_EXEC_CU_UPDATE( ctx, UPGRADEABLE_LOADER_COMPUTE_UNITS );
2160 27 : return process_loader_upgradeable_instruction( ctx );
2161 27 : } else if( FD_UNLIKELY( !memcmp( &fd_solana_bpf_loader_program_id, program_id, sizeof(fd_pubkey_t) ) ) ) {
2162 0 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_LOADER_COMPUTE_UNITS );
2163 0 : fd_log_collector_msg_literal( ctx, "BPF loader management instructions are no longer supported" );
2164 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2165 0 : } else if( FD_UNLIKELY( !memcmp( &fd_solana_bpf_loader_deprecated_program_id, program_id, sizeof(fd_pubkey_t) ) ) ) {
2166 0 : FD_EXEC_CU_UPDATE( ctx, DEPRECATED_LOADER_COMPUTE_UNITS );
2167 0 : fd_log_collector_msg_literal( ctx, "Deprecated loader is no longer supported" );
2168 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2169 0 : } else {
2170 0 : fd_log_collector_msg_literal( ctx, "Invalid BPF loader id" );
2171 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2172 0 : }
2173 27 : }
2174 :
2175 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L551-L563 */
2176 : /* The Agave client stores a loaded program type state in its implementation
2177 : of the loaded program cache. It checks to see if an account is able to be
2178 : executed. It is possible for a program to be in the DelayVisibility state or
2179 : Closed state but it won't be reflected in the Firedancer cache. Program
2180 : accounts that are in this state should exit with an invalid account data
2181 : error. For programs that are recently deployed or upgraded, they should not
2182 : be allowed to be executed for the remainder of the slot. For closed
2183 : accounts, they're uninitialized and shouldn't be executed as well.
2184 :
2185 : For the former case the slot that the
2186 : program was last updated in is in the program data account.
2187 : This means that if the slot in the program data account is greater than or
2188 : equal to the current execution slot, then the account is in a
2189 : 'LoadedProgramType::DelayVisiblity' state.
2190 :
2191 : The latter case as described above is a tombstone account which is in a Closed
2192 : state. This occurs when a program data account is closed. However, our cache
2193 : does not track this. Instead, this can be checked for by seeing if the program
2194 : account's respective program data account is uninitialized. This should only
2195 : happen when the account is closed.
2196 :
2197 : The programdata account here is fetched via fd_runtime_get_executable_account,
2198 : which may return a copy read from the parent fork. A parent copy's
2199 : program_data.slot cannot reflect a current-slot deploy/upgrade/extend/close,
2200 : so the DelayVisibility gate below is provenance-aware: current-fork copies keep
2201 : the slot comparison, parent-fork copies gate on the accdb pd_write probe
2202 : result instead.
2203 :
2204 : Every error that comes out of this block is mapped to an InvalidAccountData instruction error in Agave. */
2205 :
2206 2523 : uchar is_deprecated = !memcmp( program_account.acc->owner, &fd_solana_bpf_loader_deprecated_program_id, sizeof(fd_pubkey_t) );
2207 :
2208 2523 : fd_acc_t const * progdata_ro = program_account.acc;
2209 2523 : if( !memcmp( program_account.acc->owner, &fd_solana_bpf_loader_upgradeable_program_id, sizeof(fd_pubkey_t) ) ) {
2210 69 : fd_bpf_state_t program_account_state[1];
2211 69 : err = fd_bpf_loader_program_get_state( program_account.acc, program_account_state );
2212 69 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2213 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2214 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2215 0 : }
2216 :
2217 : /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/program_loader.rs#L96-L98
2218 : Program account and program data account discriminants get checked when loading in program accounts
2219 : into the program cache. If the discriminants are incorrect, the program is marked as closed. */
2220 69 : if( FD_UNLIKELY( program_account_state->discriminant!=FD_BPF_STATE_PROGRAM ) ) {
2221 : /* https://github.com/anza-xyz/agave/tree/v3.0.5/programs/bpf_loader/src/lib.rs#L424-L433
2222 : Agave's program cache will add any non-migrating built-ins as built-in
2223 : accounts, even though they might be owned by the BPF loader. In these
2224 : cases, Agave does not log this message. Meanwhile, non-migrating
2225 : built-in programs do not use the BPF loader, by definition. */
2226 0 : if( !fd_is_non_migrating_builtin_program( program_id ) ) {
2227 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2228 0 : }
2229 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2230 0 : }
2231 :
2232 69 : fd_pubkey_t * programdata_pubkey = &program_account_state->inner.program.programdata_address;
2233 69 : int from_parent_copy = 0;
2234 69 : int pd_write_this_slot = 0;
2235 69 : progdata_ro = fd_runtime_get_executable_account( ctx->txn_out, programdata_pubkey, &from_parent_copy, &pd_write_this_slot );
2236 69 : if( FD_UNLIKELY( !progdata_ro ) ) {
2237 33 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2238 33 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2239 33 : }
2240 :
2241 36 : if( FD_UNLIKELY( progdata_ro->data_len<PROGRAMDATA_METADATA_SIZE ) ) {
2242 3 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2243 3 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2244 3 : }
2245 :
2246 33 : fd_bpf_state_t program_data_account_state[1];
2247 33 : err = fd_bpf_loader_program_get_state( progdata_ro, program_data_account_state );
2248 33 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2249 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2250 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2251 0 : }
2252 :
2253 : /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/program_loader.rs#L100-L104
2254 : Same as above comment. Program data discriminant must be set correctly. */
2255 33 : if( FD_UNLIKELY( program_data_account_state->discriminant!=FD_BPF_STATE_PROGRAM_DATA ) ) {
2256 : /* The account is closed. */
2257 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2258 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2259 0 : }
2260 :
2261 33 : ulong program_data_slot = program_data_account_state->inner.program_data.slot;
2262 33 : if( FD_LIKELY( !from_parent_copy ) ) {
2263 3 : if( FD_UNLIKELY( program_data_slot>=ctx->bank->f.slot ) ) {
2264 : /* The account was likely just deployed or upgraded. Corresponds to
2265 : 'LoadedProgramType::DelayVisibility' */
2266 3 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2267 3 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2268 3 : }
2269 30 : } else {
2270 30 : if( FD_UNLIKELY( pd_write_this_slot ) ) {
2271 15 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2272 15 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2273 15 : }
2274 30 : }
2275 33 : }
2276 :
2277 2469 : fd_prog_load_env_t load_env[1]; fd_prog_load_env_from_bank( load_env, ctx->bank );
2278 2469 : fd_progcache_t * progcache = ctx->runtime->progcache;
2279 2469 : fd_progcache_rec_t * cache_entry =
2280 2469 : fd_progcache_pull( progcache, ctx->bank->progcache_fork_id, program_id, load_env, progdata_ro );
2281 2469 : if( FD_UNLIKELY( !cache_entry ) ) {
2282 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2283 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2284 0 : }
2285 :
2286 : /* The program may be in the cache but could have failed verification in the current epoch. */
2287 2469 : if( FD_UNLIKELY( !cache_entry->data_gaddr ) ) {
2288 0 : fd_progcache_rec_close( progcache, cache_entry );
2289 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2290 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2291 0 : }
2292 :
2293 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L446 */
2294 2469 : fd_borrowed_account_drop( &program_account );
2295 :
2296 2469 : int exec_err = fd_bpf_execute( ctx, cache_entry, is_deprecated );
2297 2469 : fd_progcache_rec_close( progcache, cache_entry );
2298 2469 : return exec_err;
2299 2469 : }
|