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