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