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