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