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