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