LCOV - code coverage report
Current view: top level - flamenco/runtime/program - fd_bpf_loader_program.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 3 1407 0.2 %
Date: 2025-03-20 12:08:36 Functions: 1 15 6.7 %

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

Generated by: LCOV version 1.14