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