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