LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_executor.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 968 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 34 0.0 %

          Line data    Source code
       1             : #include "fd_executor.h"
       2             : #include "context/fd_exec_epoch_ctx.h"
       3             : #include "fd_acc_mgr.h"
       4             : #include "fd_hashes.h"
       5             : #include "fd_runtime_err.h"
       6             : #include "context/fd_exec_slot_ctx.h"
       7             : #include "context/fd_exec_txn_ctx.h"
       8             : #include "context/fd_exec_instr_ctx.h"
       9             : 
      10             : #include "../../util/rng/fd_rng.h"
      11             : #include "fd_system_ids.h"
      12             : #include "program/fd_address_lookup_table_program.h"
      13             : #include "program/fd_bpf_loader_program.h"
      14             : #include "program/fd_loader_v4_program.h"
      15             : #include "program/fd_compute_budget_program.h"
      16             : #include "program/fd_config_program.h"
      17             : #include "program/fd_precompiles.h"
      18             : #include "program/fd_stake_program.h"
      19             : #include "program/fd_system_program.h"
      20             : #include "program/fd_vote_program.h"
      21             : #include "program/fd_zk_elgamal_proof_program.h"
      22             : #include "program/fd_bpf_program_util.h"
      23             : #include "sysvar/fd_sysvar_cache.h"
      24             : #include "sysvar/fd_sysvar_slot_history.h"
      25             : #include "sysvar/fd_sysvar_epoch_schedule.h"
      26             : #include "sysvar/fd_sysvar_instructions.h"
      27             : 
      28             : #include "tests/fd_dump_pb.h"
      29             : 
      30             : #include "../../ballet/base58/fd_base58.h"
      31             : #include "../../disco/pack/fd_pack.h"
      32             : #include "../../disco/pack/fd_pack_cost.h"
      33             : #include "../../ballet/sbpf/fd_sbpf_loader.h"
      34             : 
      35             : #include "../../util/bits/fd_uwide.h"
      36             : 
      37             : #include <assert.h>
      38             : #include <errno.h>
      39             : #include <stdio.h>   /* snprintf(3) */
      40             : #include <fcntl.h>   /* openat(2) */
      41             : #include <unistd.h>  /* write(3) */
      42             : #include <time.h>
      43             : 
      44             : #define MAX_COMPUTE_UNITS_PER_BLOCK                (48000000UL)
      45             : #define MAX_COMPUTE_UNITS_PER_WRITE_LOCKED_ACCOUNT (12000000UL)
      46             : /* We should strive for these max limits matching between pack and the
      47             :    runtime. If there's a reason for these limits to mismatch, and there
      48             :    could be, then someone should explicitly document that when relaxing
      49             :    these assertions. */
      50             : FD_STATIC_ASSERT( FD_PACK_MAX_COST_PER_BLOCK==MAX_COMPUTE_UNITS_PER_BLOCK,                     executor_pack_max_mismatch );
      51             : FD_STATIC_ASSERT( FD_PACK_MAX_WRITE_COST_PER_ACCT==MAX_COMPUTE_UNITS_PER_WRITE_LOCKED_ACCOUNT, executor_pack_max_mismatch );
      52             : 
      53             : struct fd_native_prog_info {
      54             :   fd_pubkey_t key;
      55             :   fd_exec_instr_fn_t fn;
      56             : };
      57             : typedef struct fd_native_prog_info fd_native_prog_info_t;
      58             : 
      59             : #define MAP_PERFECT_NAME fd_native_program_fn_lookup_tbl
      60             : #define MAP_PERFECT_LG_TBL_SZ 4
      61             : #define MAP_PERFECT_T fd_native_prog_info_t
      62           0 : #define MAP_PERFECT_HASH_C 478U
      63             : #define MAP_PERFECT_KEY key.uc
      64             : #define MAP_PERFECT_KEY_T fd_pubkey_t const *
      65             : #define MAP_PERFECT_ZERO_KEY  (0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0)
      66             : #define MAP_PERFECT_COMPLEX_KEY 1
      67           0 : #define MAP_PERFECT_KEYS_EQUAL(k1,k2) (!memcmp( (k1), (k2), 32UL ))
      68             : 
      69           0 : #define PERFECT_HASH( u ) (((MAP_PERFECT_HASH_C*(u))>>28)&0xFU)
      70             : 
      71             : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
      72             :                              a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
      73             :                                           PERFECT_HASH( (a08 | (a09<<8) | (a10<<16) | (a11<<24)) )
      74           0 : #define MAP_PERFECT_HASH_R( ptr ) PERFECT_HASH( fd_uint_load_4( (uchar const *)ptr + 8UL ) )
      75             : 
      76             : #define MAP_PERFECT_0       ( VOTE_PROG_ID            ), .fn = fd_vote_program_execute
      77             : #define MAP_PERFECT_1       ( SYS_PROG_ID             ), .fn = fd_system_program_execute
      78             : #define MAP_PERFECT_2       ( CONFIG_PROG_ID          ), .fn = fd_config_program_execute
      79             : #define MAP_PERFECT_3       ( STAKE_PROG_ID           ), .fn = fd_stake_program_execute
      80             : #define MAP_PERFECT_4       ( COMPUTE_BUDGET_PROG_ID  ), .fn = fd_compute_budget_program_execute
      81             : #define MAP_PERFECT_5       ( ADDR_LUT_PROG_ID        ), .fn = fd_address_lookup_table_program_execute
      82             : #define MAP_PERFECT_6       ( ZK_EL_GAMAL_PROG_ID     ), .fn = fd_executor_zk_elgamal_proof_program_execute
      83             : #define MAP_PERFECT_7       ( BPF_LOADER_1_PROG_ID    ), .fn = fd_bpf_loader_program_execute
      84             : #define MAP_PERFECT_8       ( BPF_LOADER_2_PROG_ID    ), .fn = fd_bpf_loader_program_execute
      85             : #define MAP_PERFECT_9       ( BPF_UPGRADEABLE_PROG_ID ), .fn = fd_bpf_loader_program_execute
      86             : #define MAP_PERFECT_10      ( LOADER_V4_PROG_ID       ), .fn = fd_loader_v4_program_execute
      87             : 
      88             : #include "../../util/tmpl/fd_map_perfect.c"
      89             : #undef PERFECT_HASH
      90             : 
      91             : #define MAP_PERFECT_NAME fd_native_precompile_program_fn_lookup_tbl
      92             : #define MAP_PERFECT_LG_TBL_SZ 4
      93             : #define MAP_PERFECT_T fd_native_prog_info_t
      94           0 : #define MAP_PERFECT_HASH_C 478U
      95             : #define MAP_PERFECT_KEY key.uc
      96             : #define MAP_PERFECT_KEY_T fd_pubkey_t const *
      97             : #define MAP_PERFECT_ZERO_KEY  (0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0)
      98             : #define MAP_PERFECT_COMPLEX_KEY 1
      99           0 : #define MAP_PERFECT_KEYS_EQUAL(k1,k2) (!memcmp( (k1), (k2), 32UL ))
     100             : 
     101           0 : #define PERFECT_HASH( u ) (((MAP_PERFECT_HASH_C*(u))>>28)&0xFU)
     102             : 
     103             : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
     104             :                              a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
     105             :                                           PERFECT_HASH( (a08 | (a09<<8) | (a10<<16) | (a11<<24)) )
     106           0 : #define MAP_PERFECT_HASH_R( ptr ) PERFECT_HASH( fd_uint_load_4( (uchar const *)ptr + 8UL ) )
     107             : 
     108             : #define MAP_PERFECT_0      ( ED25519_SV_PROG_ID      ), .fn = fd_precompile_ed25519_execute
     109             : #define MAP_PERFECT_1      ( KECCAK_SECP_PROG_ID     ), .fn = fd_precompile_secp256k1_execute
     110             : #define MAP_PERFECT_2      ( SECP256R1_PROG_ID       ), .fn = fd_precompile_secp256r1_execute
     111             : 
     112             : #include "../../util/tmpl/fd_map_perfect.c"
     113             : #undef PERFECT_HASH
     114             : 
     115             : /* https://github.com/anza-xyz/agave/blob/9efdd74b1b65ecfd85b0db8ad341c6bd4faddfef/program-runtime/src/invoke_context.rs#L461-L488 */
     116             : fd_exec_instr_fn_t
     117           0 : fd_executor_lookup_native_program( fd_txn_account_t const * prog_acc ) {
     118           0 :   fd_pubkey_t const * pubkey        = prog_acc->pubkey;
     119           0 :   fd_pubkey_t const * owner         = (fd_pubkey_t const *)prog_acc->const_meta->info.owner;
     120             : 
     121             :   /* Native programs should be owned by the native loader...
     122             :      This will not be the case though once core programs are migrated to BPF. */
     123           0 :   int is_native_program = !memcmp( owner, fd_solana_native_loader_id.key, sizeof(fd_pubkey_t) );
     124           0 :   if( FD_UNLIKELY( !memcmp( pubkey, fd_solana_ed25519_sig_verify_program_id.key, sizeof(fd_pubkey_t) ) &&
     125           0 :                    !memcmp( owner,  fd_solana_system_program_id.key,             sizeof(fd_pubkey_t) ) ) ) {
     126             :     /* ... except for the special case for testnet ed25519, which is
     127             :        bizarrely owned by the system program. */
     128           0 :     is_native_program = 1;
     129           0 :   }
     130           0 :   fd_pubkey_t const * lookup_pubkey         = is_native_program ? pubkey : owner;
     131           0 :   const fd_native_prog_info_t null_function = {0};
     132           0 :   return fd_native_program_fn_lookup_tbl_query( lookup_pubkey, &null_function )->fn;
     133           0 : }
     134             : 
     135             : fd_exec_instr_fn_t
     136           0 : fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc ) {
     137           0 :   fd_pubkey_t const * pubkey                = prog_acc->pubkey;
     138           0 :   const fd_native_prog_info_t null_function = {0};
     139           0 :   return fd_native_precompile_program_fn_lookup_tbl_query( pubkey, &null_function )->fn;
     140           0 : }
     141             : 
     142             : /* Returns 1 if the sysvar instruction is used, 0 otherwise */
     143             : uint
     144           0 : fd_executor_txn_uses_sysvar_instructions( fd_exec_txn_ctx_t const * txn_ctx ) {
     145           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
     146           0 :     if( FD_UNLIKELY( memcmp( txn_ctx->account_keys[i].key, fd_sysvar_instructions_id.key, sizeof(fd_pubkey_t) ) == 0 ) ) {
     147           0 :       return 1;
     148           0 :     }
     149           0 :   }
     150             : 
     151           0 :   return 0;
     152           0 : }
     153             : 
     154             : static int
     155           0 : fd_executor_is_system_nonce_account( fd_txn_account_t * account, fd_spad_t * exec_spad ) {
     156           0 :   if( memcmp( account->const_meta->info.owner, fd_solana_system_program_id.uc, sizeof(fd_pubkey_t) ) == 0 ) {
     157           0 :     if( !account->const_meta->dlen ) {
     158           0 :       return 0;
     159           0 :     } else {
     160           0 :       fd_bincode_decode_ctx_t decode = {
     161           0 :         .data    = account->const_data,
     162           0 :         .dataend = account->const_data + account->const_meta->dlen
     163           0 :       };
     164             : 
     165           0 :       if( account->const_meta->dlen!=FD_SYSTEM_PROGRAM_NONCE_DLEN ) {
     166           0 :         return -1;
     167           0 :       }
     168             : 
     169           0 :       ulong total_sz = 0UL;
     170           0 :       int   err      = fd_nonce_state_versions_decode_footprint( &decode, &total_sz );
     171           0 :       if( FD_UNLIKELY( err ) ) {
     172           0 :         return -1;
     173           0 :       }
     174             : 
     175           0 :       uchar * mem = fd_spad_alloc( exec_spad, fd_nonce_state_versions_align(), total_sz );
     176           0 :       if( FD_UNLIKELY( !mem ) ) {
     177           0 :         FD_LOG_ERR(( "Unable to allocate memory" ));
     178           0 :       }
     179             : 
     180           0 :       fd_nonce_state_versions_t * versions = fd_nonce_state_versions_decode( mem, &decode );
     181           0 :       fd_nonce_state_t *          state    = NULL;
     182           0 :       if( fd_nonce_state_versions_is_current( versions ) ) {
     183           0 :         state = &versions->inner.current;
     184           0 :       } else {
     185           0 :         state = &versions->inner.legacy;
     186           0 :       }
     187             : 
     188           0 :       if( fd_nonce_state_is_initialized( state ) ) {
     189           0 :         return 1;
     190           0 :       }
     191             : 
     192           0 :     }
     193           0 :   }
     194             : 
     195           0 :   return -1;
     196           0 : }
     197             : 
     198             : static int
     199           0 : check_rent_transition( fd_txn_account_t * account, fd_rent_t const * rent, ulong fee ) {
     200           0 :   ulong min_balance   = fd_rent_exempt_minimum_balance( rent, account->const_meta->dlen );
     201           0 :   ulong pre_lamports  = account->const_meta->info.lamports;
     202           0 :   uchar pre_is_exempt = pre_lamports >= min_balance;
     203             : 
     204           0 :   ulong post_lamports  = pre_lamports - fee;
     205           0 :   uchar post_is_exempt = post_lamports >= min_balance;
     206             : 
     207           0 :   if ( post_lamports == 0 || post_is_exempt ) {
     208           0 :     return 1;
     209           0 :   }
     210             : 
     211           0 :   if ( pre_lamports == 0 || pre_is_exempt ) {
     212           0 :     return 0;
     213           0 :   }
     214             : 
     215           0 :   return post_lamports <= pre_lamports;
     216           0 : }
     217             : 
     218             : /* https://github.com/anza-xyz/agave/blob/v2.0.2/svm/src/account_loader.rs#L103 */
     219             : static int
     220             : fd_validate_fee_payer( fd_txn_account_t * account,
     221             :                        fd_rent_t const *       rent,
     222             :                        ulong                   fee,
     223           0 :                        fd_spad_t *             exec_spad ) {
     224           0 :   if( FD_UNLIKELY( account->const_meta->info.lamports==0UL ) ) {
     225           0 :     return FD_RUNTIME_TXN_ERR_ACCOUNT_NOT_FOUND;
     226           0 :   }
     227             : 
     228           0 :   ulong min_balance = 0UL;
     229             : 
     230           0 :   int is_nonce = fd_executor_is_system_nonce_account( account, exec_spad );
     231           0 :   if ( FD_UNLIKELY( is_nonce<0 ) ) {
     232           0 :     return FD_RUNTIME_TXN_ERR_INVALID_ACCOUNT_FOR_FEE;
     233           0 :   }
     234             : 
     235           0 :   if( is_nonce ) {
     236           0 :     min_balance = fd_rent_exempt_minimum_balance( rent, 80 );
     237           0 :   }
     238             : 
     239           0 :   ulong out = ULONG_MAX;
     240           0 :   int cf = fd_ulong_checked_sub( account->const_meta->info.lamports, min_balance, &out);
     241           0 :   if( FD_UNLIKELY( cf!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
     242           0 :     return FD_RUNTIME_TXN_ERR_INSUFFICIENT_FUNDS_FOR_FEE;
     243           0 :   }
     244             : 
     245           0 :   cf = fd_ulong_checked_sub( out, fee, &out );
     246           0 :   if( FD_UNLIKELY( cf!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
     247           0 :     return FD_RUNTIME_TXN_ERR_INSUFFICIENT_FUNDS_FOR_FEE;
     248           0 :   }
     249             : 
     250           0 :   if( FD_UNLIKELY( account->const_meta->info.lamports<fee ) ) {
     251           0 :     return FD_RUNTIME_TXN_ERR_INSUFFICIENT_FUNDS_FOR_FEE;
     252           0 :   } else if( FD_UNLIKELY( memcmp( account->pubkey->key, fd_sysvar_incinerator_id.key, sizeof(fd_pubkey_t) ) != 0 &&
     253           0 :                           !check_rent_transition( account, rent, fee ) ) ) {
     254           0 :     return FD_RUNTIME_TXN_ERR_INSUFFICIENT_FUNDS_FOR_RENT;
     255           0 :   }
     256             : 
     257           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     258           0 : }
     259             : 
     260             : static int
     261           0 : status_check_tower( ulong slot, void * _ctx ) {
     262           0 :   fd_exec_txn_ctx_t * ctx = (fd_exec_txn_ctx_t *)_ctx;
     263           0 :   if( slot==ctx->slot ) {
     264           0 :     return 1;
     265           0 :   }
     266             : 
     267           0 :   if( fd_txncache_is_rooted_slot( ctx->status_cache, slot ) ) {
     268           0 :     return 1;
     269           0 :   }
     270             : 
     271           0 :   fd_slot_history_t * slot_history = fd_sysvar_slot_history_read( ctx->acc_mgr,
     272           0 :                                                                   ctx->funk_txn,
     273           0 :                                                                   ctx->spad );
     274             : 
     275           0 :   if( fd_sysvar_slot_history_find_slot( slot_history, slot ) == FD_SLOT_HISTORY_SLOT_FOUND ) {
     276           0 :     return 1;
     277           0 :   }
     278             : 
     279           0 :   return 0;
     280           0 : }
     281             : 
     282             : static int
     283           0 : fd_executor_check_status_cache( fd_exec_txn_ctx_t * txn_ctx ) {
     284             : 
     285           0 :   if( FD_UNLIKELY( !txn_ctx->status_cache ) ) {
     286           0 :     return FD_RUNTIME_EXECUTE_SUCCESS;
     287           0 :   }
     288             : 
     289           0 :   fd_hash_t * blockhash = (fd_hash_t *)((uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->recent_blockhash_off);
     290             : 
     291           0 :   fd_txncache_query_t curr_query;
     292           0 :   curr_query.blockhash = blockhash->uc;
     293           0 :   fd_blake3_t b3[1];
     294             : 
     295             :   /* Compute the blake3 hash of the transaction message
     296             :      https://github.com/anza-xyz/agave/blob/v2.1.7/sdk/program/src/message/versions/mod.rs#L159-L167 */
     297           0 :   fd_blake3_init( b3 );
     298           0 :   fd_blake3_append( b3, "solana-tx-message-v1", 20UL );
     299           0 :   fd_blake3_append( b3, ((uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->message_off),(ulong)( txn_ctx->_txn_raw->txn_sz - txn_ctx->txn_descriptor->message_off ) );
     300           0 :   fd_blake3_fini( b3, &txn_ctx->blake_txn_msg_hash );
     301           0 :   curr_query.txnhash = txn_ctx->blake_txn_msg_hash.uc;
     302             : 
     303             :   // TODO: figure out if it is faster to batch query properly and loop all txns again
     304           0 :   int err;
     305           0 :   fd_txncache_query_batch( txn_ctx->status_cache,
     306           0 :                            &curr_query,
     307           0 :                            1UL,
     308           0 :                            (void *)txn_ctx,
     309           0 :                            status_check_tower, &err );
     310           0 :   return err;
     311           0 : }
     312             : 
     313             : /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L3596-L3605 */
     314             : int
     315           0 : fd_executor_check_transactions( fd_exec_txn_ctx_t * txn_ctx ) {
     316             :   /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L3603 */
     317           0 :   int err = fd_check_transaction_age( txn_ctx );
     318           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     319           0 :     return err;
     320           0 :   }
     321             : 
     322             :   /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L3604 */
     323           0 :   err = fd_executor_check_status_cache( txn_ctx );
     324           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     325           0 :     return err;
     326           0 :   }
     327             : 
     328           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     329           0 : }
     330             : 
     331             : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/verify_precompiles.rs#L11-L34 */
     332             : int
     333           0 : fd_executor_verify_precompiles( fd_exec_txn_ctx_t * txn_ctx ) {
     334           0 :   ushort                 instr_cnt = txn_ctx->txn_descriptor->instr_cnt;
     335           0 :   fd_acct_addr_t const * tx_accs   = fd_txn_get_acct_addrs( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
     336           0 :   int                    err       = 0;
     337           0 :   for( ushort i=0; i<instr_cnt; i++ ) {
     338           0 :     fd_txn_instr_t  const * instr      = &txn_ctx->txn_descriptor->instr[i];
     339           0 :     fd_acct_addr_t  const * program_id = tx_accs + instr->program_id;
     340           0 :     if( !memcmp( program_id, &fd_solana_ed25519_sig_verify_program_id, sizeof(fd_pubkey_t) ) ) {
     341           0 :       err = fd_precompile_ed25519_verify( txn_ctx, instr );
     342           0 :       if( FD_UNLIKELY( err ) ) {
     343           0 :         FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, i );
     344           0 :         return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     345           0 :       }
     346           0 :     } else if( !memcmp( program_id, &fd_solana_keccak_secp_256k_program_id, sizeof(fd_pubkey_t) )) {
     347           0 :       err = fd_precompile_secp256k1_verify( txn_ctx, instr );
     348           0 :       if( FD_UNLIKELY( err ) ) {
     349           0 :         FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, i );
     350           0 :         return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     351           0 :       }
     352           0 :     } else if( !memcmp( program_id, &fd_solana_secp256r1_program_id, sizeof(fd_pubkey_t)) && FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, enable_secp256r1_precompile ) ) {
     353           0 :       err = fd_precompile_secp256r1_verify( txn_ctx, instr );
     354           0 :       if( FD_UNLIKELY( err ) ) {
     355           0 :         FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, i );
     356           0 :         return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     357           0 :       }
     358           0 :     }
     359           0 :   }
     360           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     361           0 : }
     362             : 
     363             : 
     364             : /* Only accounts in the transaction account keys that are owned by one of the four
     365             :    loaders (bpf v1, v2, v3, v4) are iterated over in Agave's replenish_program_cache()
     366             :    function to be loaded into the program cache. An account may be in the program cache
     367             :    iff the owners match one of the four loaders since `filter_executable_program_accounts()`
     368             :    filters out all other accounts here:
     369             :    https://github.com/anza-xyz/agave/blob/v2.1/svm/src/transaction_processor.rs#L530-L560
     370             :    If this check holds true, the account is promoted to an executable account within
     371             :    `fd_execute_load_transaction_accounts()`, which sadly involves modifying its read-only metadata
     372             :    to set the `executable` flag to true.
     373             : 
     374             :    Note that although the v4 loader is not yet activated, Agave still checks that the
     375             :    owner matches one of the four bpf loaders provided in the hyperlink below
     376             :    within `filter_executable_program_accounts()`:
     377             :    https://github.com/anza-xyz/agave/blob/v2.1/sdk/account/src/lib.rs#L800-L806 */
     378             : FD_FN_PURE static inline int
     379           0 : is_maybe_in_loaded_program_cache( fd_txn_account_t const * acct ) {
     380           0 :   return !memcmp( acct->const_meta->info.owner, fd_solana_bpf_loader_deprecated_program_id.key,  sizeof(fd_pubkey_t) ) ||
     381           0 :          !memcmp( acct->const_meta->info.owner, fd_solana_bpf_loader_program_id.key,             sizeof(fd_pubkey_t) ) ||
     382           0 :          !memcmp( acct->const_meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ||
     383           0 :          !memcmp( acct->const_meta->info.owner, fd_solana_bpf_loader_v4_program_id.key,          sizeof(fd_pubkey_t) );
     384           0 : }
     385             : 
     386             : /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L410-427 */
     387             : static int
     388             : accumulate_and_check_loaded_account_data_size( ulong   acc_size,
     389             :                                                ulong   requested_loaded_accounts_data_size,
     390           0 :                                                ulong * accumulated_account_size ) {
     391           0 :   *accumulated_account_size = fd_ulong_sat_add( *accumulated_account_size, acc_size );
     392           0 :   if( FD_UNLIKELY( *accumulated_account_size>requested_loaded_accounts_data_size ) ) {
     393           0 :     return FD_RUNTIME_TXN_ERR_MAX_LOADED_ACCOUNTS_DATA_SIZE_EXCEEDED;
     394           0 :   }
     395           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     396           0 : }
     397             : 
     398             : /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L191-L372 */
     399             : int
     400           0 : fd_executor_load_transaction_accounts( fd_exec_txn_ctx_t * txn_ctx ) {
     401           0 :   ulong                 requested_loaded_accounts_data_size = txn_ctx->loaded_accounts_data_size_limit;
     402           0 :   fd_rawtxn_b_t const * txn_raw                             = txn_ctx->_txn_raw;
     403           0 :   ushort                instr_cnt                           = txn_ctx->txn_descriptor->instr_cnt;
     404             : 
     405             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L323-L337
     406             :      In the agave client, this big chunk of code is responsible for loading in all of the
     407             :      accounts in the transaction, mimicking each call to `load_transaction_account()`
     408             :      (https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L406-L497)
     409             : 
     410             :      This contains a LOT of special casing as their accounts database and program cache
     411             :      is handled very differently than the FD client.
     412             : 
     413             :      The logic is as follows:
     414             :      1. If the account is the instructions sysvar, then load in the compiled
     415             :         instructions from the transactions into the sysvar's data.
     416             :      2. If the account is a fee payer, then it is already loaded.
     417             :      3. If the account is an account override, then handle seperately. Account
     418             :         overrides are used for simulating transactions.
     419             :         - This is only used for testing.
     420             :      4. If the account is not writable and not an instruction account and it is
     421             :         in the loaded program cache, then load in a dummy account with the
     422             :         correct owner and the executable flag set to true.
     423             :         - See comments below
     424             :      5. Otherwise load in the account from the accounts DB. If the account is
     425             :         writable try to collect rent from the account.
     426             : 
     427             :      After the account is loaded, accumulate the data size to make sure the
     428             :      transaction doesn't violate the transaction loading limit.
     429             : 
     430             :      In the firedancer client only some of these steps are necessary because
     431             :      all of the accounts are loaded in from the accounts db into borrowed
     432             :      accounts already.
     433             :      1. If the account is writable, try to collect fees on the account. Unlike
     434             :         the agave client, this is also done on the fee payer account. The agave
     435             :         client tries to collect rent on the fee payer while the fee is being
     436             :         collected in validate_fees().
     437             :      2. If the account is not writable and it is not an instruction account
     438             :         and would be in the loaded program cache, then it should be replaced
     439             :         with a dummy value.
     440             :      */
     441             : 
     442           0 :   fd_epoch_schedule_t const * schedule = (fd_epoch_schedule_t const *)fd_sysvar_cache_epoch_schedule( txn_ctx->sysvar_cache );
     443           0 :   ulong                       epoch    = fd_slot_to_epoch( schedule, txn_ctx->slot, NULL );
     444             : 
     445             :   /* In `load_transaction_account()`, there are special checks based on the priviledges of each account key.
     446             :      We precompute the results here before proceeding with the special casing. Note the start range of 1 since
     447             :      `load_transaction_account()` doesn't handle the fee payer. */
     448           0 :   uchar txn_account_is_instruction_account[MAX_TX_ACCOUNT_LOCKS] = {0};
     449           0 :   for( ushort i=0UL; i<instr_cnt; i++ ) {
     450             :     /* Set up the instr infos for the transaction */
     451           0 :     fd_txn_instr_t const * instr = &txn_ctx->txn_descriptor->instr[i];
     452           0 :     fd_convert_txn_instr_to_instr( txn_ctx, instr, txn_ctx->accounts, &txn_ctx->instr_infos[i] );
     453             : 
     454           0 :     uchar const * instr_acc_idxs = fd_txn_get_instr_accts( instr, txn_raw->raw );
     455           0 :     for( ushort j=0; j<instr->acct_cnt; j++ ) {
     456           0 :       uchar txn_acc_idx                               = instr_acc_idxs[j];
     457           0 :       txn_account_is_instruction_account[txn_acc_idx] = 1;
     458           0 :     }
     459           0 :   }
     460           0 :   txn_ctx->instr_info_cnt = txn_ctx->txn_descriptor->instr_cnt;
     461             : 
     462           0 :   for( ulong i=1UL; i<txn_ctx->accounts_cnt; i++ ) {
     463           0 :     fd_txn_account_t * acct = &txn_ctx->accounts[i];
     464             : 
     465             :     /* https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L418-L421 */
     466           0 :     uchar is_instruction_account = txn_account_is_instruction_account[i];
     467           0 :     uchar is_writable            = !!( fd_txn_account_is_writable_idx( txn_ctx, (int)i ) );
     468             : 
     469             :     /* https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L417 */
     470             : 
     471           0 :     int   err      = fd_exec_txn_ctx_get_account_view_idx( txn_ctx, (uchar)i, &acct );
     472           0 :     ulong acc_size = err==FD_ACC_MGR_SUCCESS ? acct->const_meta->dlen : 0UL;
     473             : 
     474             :     /* `load_transaction_account()`
     475             :         https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L406-L497 */
     476             : 
     477             :     /* First case: checking if the account is the instructions sysvar
     478             :        https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L422-L429 */
     479           0 :     if( FD_UNLIKELY( !memcmp( acct->pubkey->key, fd_sysvar_instructions_id.key, sizeof(fd_pubkey_t) ) ) ) {
     480           0 :       acct->account_found = 1;
     481           0 :       fd_sysvar_instructions_serialize_account( txn_ctx, (fd_instr_info_t const *)txn_ctx->instr_infos, txn_ctx->txn_descriptor->instr_cnt );
     482             :       /* Continue because this should not be counted towards the total loaded account size.
     483             :          https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L426 */
     484           0 :       continue;
     485           0 :     }
     486             : 
     487             :     /* Second case: loading a program account that is not writable, not an instruction account,
     488             :        and may be in the loaded program cache. We bypass this special casing if
     489             :        `disable_account_loader_special_case` is active.
     490             :        https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L438-L451 */
     491           0 :     if( FD_UNLIKELY( !is_instruction_account && !is_writable &&
     492           0 :                      !FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, disable_account_loader_special_case ) &&
     493           0 :                      is_maybe_in_loaded_program_cache( acct ) ) ) {
     494             : 
     495             :       /* In the corresponding branch in the agave client, if the account is not executable,
     496             :          a dummy account is loaded in that has the executable flag set to true. This is a hack
     497             :          to mirror those semantics.
     498             :          https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L499-L507 */
     499           0 :       acct->account_found = 1;
     500           0 :       if( FD_UNLIKELY( !fd_txn_account_is_executable( acct ) ) ) {
     501           0 :         void * borrowed_account_data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
     502           0 :         fd_txn_account_make_readonly( acct, borrowed_account_data );
     503           0 :         fd_account_meta_t * meta = (fd_account_meta_t *)acct->const_meta;
     504           0 :         meta->info.executable = 1;
     505           0 :       }
     506             : 
     507             :       /* For upgradeable programs, we also have to add the size of the programdata account.
     508             :          https://github.com/anza-xyz/agave/blob/v2.1.11/svm/src/program_loader.rs#L172-L175 */
     509           0 :       FD_SPAD_FRAME_BEGIN( txn_ctx->spad ) {
     510           0 :         if( FD_LIKELY( !memcmp( acct->const_meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     511           0 :           fd_bpf_upgradeable_loader_state_t * state = fd_bpf_loader_program_get_state( acct, txn_ctx->spad, &err );
     512           0 :           if( FD_LIKELY( !err && fd_bpf_upgradeable_loader_state_is_program( state ) ) ) {
     513           0 :             FD_TXN_ACCOUNT_DECL( programdata_account );
     514           0 :             err = fd_acc_mgr_view( txn_ctx->acc_mgr,
     515           0 :                                    txn_ctx->funk_txn,
     516           0 :                                    &state->inner.program.programdata_address,
     517           0 :                                    programdata_account );
     518           0 :             if( FD_LIKELY( err==FD_ACC_MGR_SUCCESS ) ) {
     519           0 :               acc_size += programdata_account->const_meta->dlen;
     520           0 :             }
     521           0 :           }
     522           0 :         }
     523           0 :       } FD_SPAD_FRAME_END;
     524           0 :     }
     525             :     /* Third case: Default case
     526             :        https://github.com/anza-xyz/agave/blob/v2.1.0/svm/src/account_loader.rs#L452-L494 */
     527           0 :     else {
     528             :       /* If the account exists and is writable, collect rent from it. */
     529           0 :       if( FD_LIKELY( acct->account_found ) ) {
     530           0 :         if( is_writable ) {
     531           0 :           txn_ctx->collected_rent += fd_runtime_collect_rent_from_account( txn_ctx->slot,
     532           0 :                                                                            &txn_ctx->schedule,
     533           0 :                                                                            &txn_ctx->rent,
     534           0 :                                                                            txn_ctx->slots_per_year,
     535           0 :                                                                            &txn_ctx->features,
     536           0 :                                                                            acct->meta,
     537           0 :                                                                            acct->pubkey,
     538           0 :                                                                            epoch );
     539           0 :           acct->starting_lamports = acct->meta->info.lamports;
     540           0 :         }
     541           0 :       }
     542           0 :     }
     543             : 
     544           0 :     err = accumulate_and_check_loaded_account_data_size( acc_size,
     545           0 :                                                          requested_loaded_accounts_data_size,
     546           0 :                                                          &txn_ctx->loaded_accounts_data_size );
     547             : 
     548           0 :     if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     549           0 :       return err;
     550           0 :     }
     551           0 :   }
     552             : 
     553           0 :   fd_pubkey_t program_owners[instr_cnt];
     554           0 :   ushort      program_owners_cnt = 0;
     555             : 
     556             :   /* The logic below handles special casing with loading instruction accounts.
     557             :      https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L297-L358 */
     558           0 :   for( ushort i=0; i<instr_cnt; i++ ) {
     559           0 :     fd_txn_instr_t const * instr = &txn_ctx->txn_descriptor->instr[i];
     560             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L304-306 */
     561           0 :     fd_txn_account_t * program_account = NULL;
     562           0 :     int err = fd_exec_txn_ctx_get_account_view_idx_allow_dead( txn_ctx, instr->program_id, &program_account );
     563           0 :     if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     564           0 :       return FD_RUNTIME_TXN_ERR_PROGRAM_ACCOUNT_NOT_FOUND;
     565           0 :     }
     566             : 
     567             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L307-309 */
     568           0 :     if( FD_UNLIKELY( !memcmp( program_account->pubkey->key, fd_solana_native_loader_id.key, sizeof(fd_pubkey_t) ) ) ) {
     569           0 :       continue;
     570           0 :     }
     571             : 
     572             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L311-L314 */
     573           0 :     if( FD_UNLIKELY( !program_account->account_found ) ) {
     574           0 :       return FD_RUNTIME_TXN_ERR_PROGRAM_ACCOUNT_NOT_FOUND;
     575           0 :     }
     576             : 
     577             :     /* The above checks from the mirrored `load_transaction_account()` function would promote
     578             :        this account to executable if necessary, so this check is sufficient.
     579             :        https://github.com/anza-xyz/agave/blob/89872fdb074e6658646b2b57a299984f0059cc84/svm/src/account_loader.rs#L493-L500 */
     580           0 :     if( FD_UNLIKELY( !FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, remove_accounts_executable_flag_checks ) &&
     581           0 :                      !fd_txn_account_is_executable( program_account ) ) ) {
     582           0 :       return FD_RUNTIME_TXN_ERR_INVALID_PROGRAM_FOR_EXECUTION;
     583           0 :     }
     584             : 
     585             :     /* At this point, program_indices will no longer have 0 length, so we are set this flag to 1 */
     586             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L321 */
     587             : 
     588             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L322-325 */
     589           0 :     if( !memcmp( program_account->const_meta->info.owner, fd_solana_native_loader_id.key, sizeof(fd_pubkey_t) ) ) {
     590           0 :       continue;
     591           0 :     }
     592             : 
     593             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L326-330
     594             :        The agave client does checks on the program account's owners as well.
     595             :        However, it is important to not do these checks multiple times as the
     596             :        total size of accounts and their owners are accumulated: duplicate owners
     597             :        should be avoided. */
     598             : 
     599             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L334-353 */
     600           0 :     FD_TXN_ACCOUNT_DECL( owner_account );
     601           0 :     err = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, (fd_pubkey_t *) program_account->const_meta->info.owner, owner_account );
     602           0 :     if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     603           0 :       return FD_RUNTIME_TXN_ERR_PROGRAM_ACCOUNT_NOT_FOUND;
     604           0 :     }
     605             : 
     606           0 :     for( ushort i=0; i<program_owners_cnt; i++ ) {
     607           0 :       if( !memcmp( program_owners[i].key, owner_account->pubkey, sizeof(fd_pubkey_t) ) ) {
     608             :         /* If the owner account has already been seen, skip the owner checks
     609             :            and do not acccumulate the account size. */
     610           0 :         continue;
     611           0 :       }
     612           0 :     }
     613             : 
     614             :     /* https://github.com/anza-xyz/agave/blob/89872fdb074e6658646b2b57a299984f0059cc84/svm/src/account_loader.rs#L537-L545 */
     615           0 :     if( FD_UNLIKELY( memcmp( owner_account->const_meta->info.owner, fd_solana_native_loader_id.key, sizeof(fd_pubkey_t) ) ||
     616           0 :                      ( !FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, remove_accounts_executable_flag_checks ) &&
     617           0 :                        !fd_txn_account_is_executable( owner_account ) ) ) ) {
     618           0 :       return FD_RUNTIME_TXN_ERR_INVALID_PROGRAM_FOR_EXECUTION;
     619           0 :     }
     620             : 
     621             :     /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L342-347 */
     622             :     /* Count the owner's data in the loaded account size for program accounts.
     623             :        However, it is important to not double count repeated owners. */
     624           0 :     err = accumulate_and_check_loaded_account_data_size( owner_account->const_meta->dlen,
     625           0 :                                                          requested_loaded_accounts_data_size,
     626           0 :                                                          &txn_ctx->loaded_accounts_data_size );
     627           0 :     if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     628           0 :       return err;
     629           0 :     }
     630             : 
     631           0 :     fd_memcpy( &program_owners[ program_owners_cnt++ ], owner_account->pubkey, sizeof(fd_pubkey_t) );
     632           0 :   }
     633             : 
     634           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     635           0 : }
     636             : 
     637             : /* https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118 */
     638             : int
     639           0 : fd_executor_validate_account_locks( fd_exec_txn_ctx_t const * txn_ctx ) {
     640             :   /* Ensure the number of account keys does not exceed the transaction lock limit
     641             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L123 */
     642           0 :   ulong tx_account_lock_limit = get_transaction_account_lock_limit( txn_ctx );
     643           0 :   if( FD_UNLIKELY( txn_ctx->accounts_cnt>tx_account_lock_limit ) ) {
     644           0 :     return FD_RUNTIME_TXN_ERR_TOO_MANY_ACCOUNT_LOCKS;
     645           0 :   }
     646             : 
     647             :   /* Duplicate account check
     648             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L125 */
     649           0 :   for( ushort i=0; i<txn_ctx->accounts_cnt; i++ ) {
     650           0 :     for( ushort j=(ushort)(i+1U); j<txn_ctx->accounts_cnt; j++ ) {
     651           0 :       if( FD_UNLIKELY( !memcmp( &txn_ctx->account_keys[i], &txn_ctx->account_keys[j], sizeof(fd_pubkey_t) ) ) ) {
     652           0 :         return FD_RUNTIME_TXN_ERR_ACCOUNT_LOADED_TWICE;
     653           0 :       }
     654           0 :     }
     655           0 :   }
     656             : 
     657             :   /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/sdk/src/transaction/sanitized.rs#L286-L288 */
     658           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     659           0 : }
     660             : 
     661             : /* https://github.com/anza-xyz/agave/blob/89050f3cb7e76d9e273f10bea5e8207f2452f79f/svm/src/account_loader.rs#L101-L126 */
     662             : static int
     663             : fd_should_set_exempt_rent_epoch_max( fd_exec_txn_ctx_t * txn_ctx,
     664           0 :                                      fd_txn_account_t *  rec ) {
     665             :   /* https://github.com/anza-xyz/agave/blob/89050f3cb7e76d9e273f10bea5e8207f2452f79f/svm/src/account_loader.rs#L109-L125 */
     666           0 :   if( FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, disable_rent_fees_collection ) ) {
     667           0 :     if( FD_LIKELY( rec->const_meta->info.rent_epoch!=ULONG_MAX
     668           0 :                 && rec->const_meta->info.lamports>=fd_rent_exempt_minimum_balance( &txn_ctx->rent, rec->const_meta->dlen ) ) ) {
     669           0 :       return 1;
     670           0 :     }
     671           0 :     return 0;
     672           0 :   }
     673             : 
     674           0 :   ulong epoch = fd_slot_to_epoch( &txn_ctx->schedule, txn_ctx->slot, NULL );
     675             : 
     676             :   /* https://github.com/anza-xyz/agave/blob/89050f3cb7e76d9e273f10bea5e8207f2452f79f/sdk/src/rent_collector.rs#L158-L162 */
     677           0 :   if( rec->const_meta->info.rent_epoch==ULONG_MAX || rec->const_meta->info.rent_epoch>epoch ) {
     678           0 :     return 0;
     679           0 :   }
     680             : 
     681             :   /* https://github.com/anza-xyz/agave/blob/89050f3cb7e76d9e273f10bea5e8207f2452f79f/sdk/src/rent_collector.rs#L163-L166 */
     682           0 :   if( rec->const_meta->info.executable || !memcmp( rec->pubkey->key, fd_sysvar_incinerator_id.key, sizeof(fd_pubkey_t) ) ) {
     683           0 :     return 1;
     684           0 :   }
     685             : 
     686             :   /* https://github.com/anza-xyz/agave/blob/89050f3cb7e76d9e273f10bea5e8207f2452f79f/sdk/src/rent_collector.rs#L167-L183 */
     687           0 :   if( rec->const_meta->info.lamports && rec->const_meta->info.lamports<fd_rent_exempt_minimum_balance( &txn_ctx->rent, rec->const_meta->dlen ) ) {
     688           0 :     return 0;
     689           0 :   }
     690             : 
     691           0 :   return 1;
     692           0 : }
     693             : 
     694             : static void
     695             : compute_priority_fee( fd_exec_txn_ctx_t const * txn_ctx,
     696             :                       ulong *                   fee,
     697           0 :                       ulong *                   priority ) {
     698           0 :   switch( txn_ctx->prioritization_fee_type ) {
     699           0 :   case FD_COMPUTE_BUDGET_PRIORITIZATION_FEE_TYPE_DEPRECATED: {
     700           0 :     if( !txn_ctx->compute_unit_limit ) {
     701           0 :       *priority = 0UL;
     702           0 :     }
     703           0 :     else {
     704           0 :       uint128 micro_lamport_fee = (uint128)txn_ctx->compute_unit_price * (uint128)MICRO_LAMPORTS_PER_LAMPORT;
     705           0 :       uint128 _priority         = micro_lamport_fee / (uint128)txn_ctx->compute_unit_limit;
     706           0 :       *priority                 = _priority > (uint128)ULONG_MAX ? ULONG_MAX : (ulong)_priority;
     707           0 :     }
     708             : 
     709           0 :     *fee = txn_ctx->compute_unit_price;
     710           0 :     return;
     711             : 
     712           0 :   } case FD_COMPUTE_BUDGET_PRIORITIZATION_FEE_TYPE_COMPUTE_UNIT_PRICE: {
     713           0 :     uint128 micro_lamport_fee = (uint128)txn_ctx->compute_unit_price * (uint128)txn_ctx->compute_unit_limit;
     714           0 :     *priority                 = txn_ctx->compute_unit_price;
     715           0 :     uint128 _fee              = (micro_lamport_fee + (uint128)(MICRO_LAMPORTS_PER_LAMPORT - 1)) / (uint128)(MICRO_LAMPORTS_PER_LAMPORT);
     716           0 :     *fee                      = _fee > (uint128)ULONG_MAX ? ULONG_MAX : (ulong)_fee;
     717           0 :     return;
     718             : 
     719           0 :   }
     720           0 :   default:
     721           0 :     __builtin_unreachable();
     722           0 :   }
     723           0 : }
     724             : 
     725             : static ulong
     726           0 : fd_executor_lamports_per_signature( fd_fee_rate_governor_t const * fee_rate_governor ) {
     727             :   // https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/fee_calculator.rs#L110
     728           0 :   return fee_rate_governor->target_lamports_per_signature / 2;
     729           0 : }
     730             : 
     731             : static void
     732             : fd_executor_calculate_fee( fd_exec_txn_ctx_t *  txn_ctx,
     733             :                           fd_txn_t const *      txn_descriptor,
     734             :                           fd_rawtxn_b_t const * txn_raw,
     735             :                           ulong *               ret_execution_fee,
     736           0 :                           ulong *               ret_priority_fee) {
     737             : 
     738             :   // https://github.com/firedancer-io/solana/blob/08a1ef5d785fe58af442b791df6c4e83fe2e7c74/runtime/src/bank.rs#L4443
     739           0 :   ulong priority     = 0UL;
     740           0 :   ulong priority_fee = 0UL;
     741           0 :   compute_priority_fee( txn_ctx, &priority_fee, &priority );
     742             : 
     743             :   // let signature_fee = Self::get_num_signatures_in_message(message) .saturating_mul(fee_structure.lamports_per_signature);
     744           0 :   ulong num_signatures = txn_descriptor->signature_cnt;
     745           0 :   for (ushort i=0; i<txn_descriptor->instr_cnt; ++i ) {
     746           0 :     fd_txn_instr_t const * txn_instr  = &txn_descriptor->instr[i];
     747           0 :     fd_pubkey_t *          program_id = &txn_ctx->account_keys[txn_instr->program_id];
     748           0 :     if( !memcmp(program_id->uc, fd_solana_keccak_secp_256k_program_id.key, sizeof(fd_pubkey_t)) ||
     749           0 :         !memcmp(program_id->uc, fd_solana_ed25519_sig_verify_program_id.key, sizeof(fd_pubkey_t)) ||
     750           0 :         (!memcmp(program_id->uc, fd_solana_secp256r1_program_id.key, sizeof(fd_pubkey_t)) && FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, enable_secp256r1_precompile )) ) {
     751           0 :       if( !txn_instr->data_sz ) {
     752           0 :         continue;
     753           0 :       }
     754           0 :       uchar * data   = (uchar *)txn_raw->raw + txn_instr->data_off;
     755           0 :       num_signatures = fd_ulong_sat_add(num_signatures, (ulong)(data[0]));
     756           0 :     }
     757           0 :   }
     758             : 
     759           0 :   ulong signature_fee = fd_executor_lamports_per_signature( &txn_ctx->fee_rate_governor ) * num_signatures;
     760             : 
     761             :   // TODO: as far as I can tell, this is always 0
     762             :   //
     763             :   //            let write_lock_fee = Self::get_num_write_locks_in_message(message)
     764             :   //                .saturating_mul(fee_structure.lamports_per_write_lock);
     765           0 :   ulong lamports_per_write_lock = 0UL;
     766           0 :   ulong write_lock_fee          = fd_ulong_sat_mul(fd_txn_account_cnt(txn_descriptor, FD_TXN_ACCT_CAT_WRITABLE), lamports_per_write_lock);
     767             : 
     768             :   // TODO: the fee_structure bin is static and default..
     769             :   //        let loaded_accounts_data_size_cost = if include_loaded_account_data_size_in_fee {
     770             :   //            FeeStructure::calculate_memory_usage_cost(
     771             :   //                budget_limits.loaded_accounts_data_size_limit,
     772             :   //                budget_limits.heap_cost,
     773             :   //            )
     774             :   //        } else {
     775             :   //            0_u64
     776             :   //        };
     777             :   //        let total_compute_units =
     778             :   //            loaded_accounts_data_size_cost.saturating_add(budget_limits.compute_unit_limit);
     779             :   //        let compute_fee = self
     780             :   //            .compute_fee_bins
     781             :   //            .iter()
     782             :   //            .find(|bin| total_compute_units <= bin.limit)
     783             :   //            .map(|bin| bin.fee)
     784             :   //            .unwrap_or_else(|| {
     785             :   //                self.compute_fee_bins
     786             :   //                    .last()
     787             :   //                    .map(|bin| bin.fee)
     788             :   //                    .unwrap_or_default()
     789             :   //            });
     790             : 
     791             :   // https://github.com/anza-xyz/agave/blob/2e6ca8c1f62db62c1db7f19c9962d4db43d0d550/sdk/src/fee.rs#L203-L206
     792           0 :   ulong execution_fee = fd_ulong_sat_add( signature_fee, write_lock_fee );
     793             : 
     794           0 :   if( execution_fee >= ULONG_MAX ) {
     795           0 :     *ret_execution_fee = ULONG_MAX;
     796           0 :   } else {
     797           0 :     *ret_execution_fee = execution_fee;
     798           0 :   }
     799             : 
     800           0 :   if( priority_fee >= ULONG_MAX ) {
     801           0 :     *ret_priority_fee = ULONG_MAX;
     802           0 :   } else {
     803           0 :     *ret_priority_fee = priority_fee;
     804           0 :   }
     805           0 : }
     806             : 
     807             : static int
     808           0 : fd_executor_collect_fees( fd_exec_txn_ctx_t * txn_ctx, fd_txn_account_t * fee_payer_rec ) {
     809             : 
     810           0 :   ulong execution_fee = 0UL;
     811           0 :   ulong priority_fee  = 0UL;
     812             : 
     813           0 :   fd_executor_calculate_fee( txn_ctx, txn_ctx->txn_descriptor, txn_ctx->_txn_raw, &execution_fee, &priority_fee );
     814             : 
     815           0 :   ulong                   total_fee  = fd_ulong_sat_add( execution_fee, priority_fee );
     816             : 
     817             :   // https://github.com/anza-xyz/agave/blob/2e6ca8c1f62db62c1db7f19c9962d4db43d0d550/sdk/src/fee.rs#L54
     818           0 :   if( !FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, remove_rounding_in_fee_calculation ) ) {
     819           0 :     total_fee = fd_rust_cast_double_to_ulong( round( (double)total_fee ) );
     820           0 :   }
     821             : 
     822           0 :   int err = fd_validate_fee_payer( fee_payer_rec, &txn_ctx->rent, total_fee, txn_ctx->spad );
     823           0 :   if( FD_UNLIKELY( err ) ) {
     824           0 :     return err;
     825           0 :   }
     826             : 
     827             :   /* At this point, the fee payer has been validated and the fee has been
     828             :      calculated. This means that the fee can be safely subtracted from the
     829             :      fee payer's borrowed account. However, the starting lamports of the
     830             :      account must be updated as well. Each instruction must have the net
     831             :      same (balanced) amount of lamports. This is done by comparing the
     832             :      borrowed accounts starting lamports and comparing it to the sum of
     833             :      the ending lamports. Therefore, we need to update the starting lamports
     834             :      specifically for the fee payer.
     835             : 
     836             :      This is especially important in the case where the transaction fails. This
     837             :      is because we need to roll back the account to the balance AFTER the fee
     838             :      is paid. It is also possible for the accounts data and owner to change.
     839             :      This means that the entire state of the borrowed account must be rolled
     840             :      back to this point. */
     841             : 
     842           0 :   fee_payer_rec->meta->info.lamports -= total_fee;
     843           0 :   fee_payer_rec->starting_lamports    = fee_payer_rec->meta->info.lamports;
     844             : 
     845             :   /* Update the fee payer's rent epoch to ULONG_MAX if it is rent exempt. */
     846           0 :   if( fd_should_set_exempt_rent_epoch_max( txn_ctx, fee_payer_rec ) ) {
     847           0 :     fee_payer_rec->meta->info.rent_epoch = ULONG_MAX;
     848           0 :   }
     849             : 
     850           0 :   txn_ctx->execution_fee = execution_fee;
     851           0 :   txn_ctx->priority_fee  = priority_fee;
     852             : 
     853           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     854           0 : }
     855             : 
     856             : /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L413-L497 */
     857             : int
     858           0 : fd_executor_validate_transaction_fee_payer( fd_exec_txn_ctx_t * txn_ctx ) {
     859             :   /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/svm/src/transaction_processor.rs#L423-L430 */
     860           0 :   int err = fd_executor_compute_budget_program_execute_instructions( txn_ctx, txn_ctx->_txn_raw );
     861           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     862           0 :     return err;
     863           0 :   }
     864             : 
     865             :   /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/svm/src/transaction_processor.rs#L431-L436 */
     866           0 :   fd_txn_account_t * fee_payer_rec = NULL;
     867           0 :   err = fd_exec_txn_ctx_get_account_modify_fee_payer( txn_ctx, &fee_payer_rec );
     868           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     869           0 :     return FD_RUNTIME_TXN_ERR_ACCOUNT_NOT_FOUND;
     870           0 :   }
     871             : 
     872             :   /* Collect rent from the fee payer and set the starting lamports (to avoid unbalanced lamports issues in instruction execution)
     873             :      https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/svm/src/transaction_processor.rs#L438-L445 */
     874           0 :   fd_epoch_schedule_t const * schedule = (fd_epoch_schedule_t const *)fd_sysvar_cache_epoch_schedule( txn_ctx->sysvar_cache );
     875           0 :   ulong                       epoch    = fd_slot_to_epoch( schedule, txn_ctx->slot, NULL );
     876           0 :   txn_ctx->collected_rent += fd_runtime_collect_rent_from_account( txn_ctx->slot,
     877           0 :                                                                    &txn_ctx->schedule,
     878           0 :                                                                    &txn_ctx->rent,
     879           0 :                                                                    txn_ctx->slots_per_year,
     880           0 :                                                                    &txn_ctx->features,
     881           0 :                                                                    fee_payer_rec->meta,
     882           0 :                                                                    fee_payer_rec->pubkey,
     883           0 :                                                                    epoch );
     884           0 :   fee_payer_rec->starting_lamports = fee_payer_rec->meta->info.lamports;
     885             : 
     886             :   /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/svm/src/transaction_processor.rs#L431-L488 */
     887           0 :   err = fd_executor_collect_fees( txn_ctx, fee_payer_rec );
     888           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     889           0 :     return err;
     890           0 :   }
     891             : 
     892           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     893           0 : }
     894             : 
     895             : static int
     896             : fd_executor_setup_accessed_accounts_for_txn( fd_exec_txn_ctx_t * txn_ctx,
     897           0 :                                              fd_spad_t *         spad ) {
     898             : 
     899           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
     900             : 
     901           0 :   fd_pubkey_t * tx_accs = (fd_pubkey_t *)((uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->acct_addr_off);
     902             : 
     903             :   // Set up accounts in the transaction body and perform checks
     904           0 :   for( ulong i = 0; i < txn_ctx->txn_descriptor->acct_addr_cnt; i++ ) {
     905           0 :     txn_ctx->account_keys[i] = tx_accs[i];
     906           0 :   }
     907             : 
     908           0 :   txn_ctx->accounts_cnt += (uchar)txn_ctx->txn_descriptor->acct_addr_cnt;
     909             : 
     910           0 :   if( txn_ctx->txn_descriptor->transaction_version == FD_TXN_V0 ) {
     911             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/runtime/src/bank/address_lookup_table.rs#L44-L48 */
     912           0 :     fd_slot_hashes_global_t const * slot_hashes_global = fd_sysvar_cache_slot_hashes( txn_ctx->sysvar_cache );
     913           0 :     fd_slot_hashes_t slot_hashes[1];
     914           0 :     fd_bincode_decode_ctx_t decode = { .wksp = txn_ctx->runtime_pub_wksp };
     915           0 :     fd_slot_hashes_convert_global_to_local( slot_hashes_global, slot_hashes, &decode );
     916             : 
     917           0 :     if( FD_UNLIKELY( !&slot_hashes[0] ) ) {
     918           0 :       return FD_RUNTIME_TXN_ERR_ACCOUNT_NOT_FOUND;
     919           0 :     }
     920             : 
     921           0 :     fd_pubkey_t readonly_lut_accs[128];
     922           0 :     ulong       readonly_lut_accs_cnt = 0UL;
     923             :     // Set up accounts in the account look up tables.
     924           0 :     fd_txn_acct_addr_lut_t const * addr_luts = fd_txn_get_address_tables_const( txn_ctx->txn_descriptor );
     925           0 :     for( ulong i = 0UL; i < txn_ctx->txn_descriptor->addr_table_lookup_cnt; i++ ) {
     926           0 :       fd_txn_acct_addr_lut_t const * addr_lut = &addr_luts[i];
     927           0 :       fd_pubkey_t const * addr_lut_acc = (fd_pubkey_t *)((uchar *)txn_ctx->_txn_raw->raw + addr_lut->addr_off);
     928             : 
     929             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L90-L94 */
     930           0 :       FD_TXN_ACCOUNT_DECL( addr_lut_rec );
     931           0 :       int err = fd_acc_mgr_view( txn_ctx->acc_mgr,
     932           0 :                                  txn_ctx->funk_txn,
     933           0 :                                  addr_lut_acc,
     934           0 :                                  addr_lut_rec );
     935           0 :       if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
     936           0 :         return FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND;
     937           0 :       }
     938             : 
     939             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L96-L114 */
     940           0 :       if( FD_UNLIKELY( memcmp( addr_lut_rec->const_meta->info.owner, fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     941           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_OWNER;
     942           0 :       }
     943             : 
     944             :       /* Realistically impossible case, but need to make sure we don't cause an OOB data access
     945             :           https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L205-L209 */
     946           0 :       if( FD_UNLIKELY( addr_lut_rec->const_meta->dlen < FD_LOOKUP_TABLE_META_SIZE ) ) {
     947           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     948           0 :       }
     949             : 
     950             :       /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/accounts-db/src/accounts.rs#L141-L142 */
     951           0 :       fd_bincode_decode_ctx_t decode_ctx = {
     952           0 :         .data    = addr_lut_rec->const_data,
     953           0 :         .dataend = &addr_lut_rec->const_data[FD_LOOKUP_TABLE_META_SIZE]
     954           0 :       };
     955             : 
     956           0 :       ulong total_sz = 0UL;
     957           0 :       err = fd_address_lookup_table_state_decode_footprint( &decode_ctx, &total_sz );
     958           0 :       if( FD_UNLIKELY( err ) ) {
     959           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     960           0 :       }
     961             : 
     962           0 :       uchar * mem = fd_spad_alloc( spad, fd_address_lookup_table_state_align(), total_sz );
     963           0 :       if( FD_UNLIKELY( !mem ) ) {
     964           0 :         FD_LOG_ERR(( "Unable to allocate memory for address lookup table state" ));
     965           0 :       }
     966             : 
     967             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L197-L214 */
     968           0 :       fd_address_lookup_table_state_t * addr_lookup_table_state = fd_address_lookup_table_state_decode( mem, &decode_ctx );
     969             : 
     970             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L200-L203 */
     971           0 :       if( FD_UNLIKELY( addr_lookup_table_state->discriminant != fd_address_lookup_table_state_enum_lookup_table ) ) {
     972           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     973           0 :       }
     974             : 
     975             :       /* Again probably an impossible case, but the ALUT data needs to be 32-byte aligned
     976             :           https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L210-L214 */
     977           0 :       if( FD_UNLIKELY( (addr_lut_rec->const_meta->dlen - FD_LOOKUP_TABLE_META_SIZE) & 0x1fUL ) ) {
     978           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     979           0 :       }
     980             : 
     981             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L101-L112 */
     982           0 :       fd_pubkey_t * lookup_addrs     = (fd_pubkey_t *)&addr_lut_rec->const_data[FD_LOOKUP_TABLE_META_SIZE];
     983           0 :       ulong         lookup_addrs_cnt = (addr_lut_rec->const_meta->dlen - FD_LOOKUP_TABLE_META_SIZE) >> 5UL; // = (dlen - 56) / 32
     984             : 
     985             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L175-L176 */
     986           0 :       ulong active_addresses_len;
     987           0 :       err = fd_get_active_addresses_len( &addr_lookup_table_state->inner.lookup_table,
     988           0 :                                           txn_ctx->slot,
     989           0 :                                           slot_hashes->hashes,
     990           0 :                                           lookup_addrs_cnt,
     991           0 :                                           &active_addresses_len );
     992           0 :       if( FD_UNLIKELY( err ) ) {
     993           0 :         return err;
     994           0 :       }
     995             : 
     996             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L169-L182 */
     997           0 :       uchar * writable_lut_idxs = (uchar *)txn_ctx->_txn_raw->raw + addr_lut->writable_off;
     998           0 :       for( ulong j = 0; j < addr_lut->writable_cnt; j++ ) {
     999             :         /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */
    1000           0 :         if( writable_lut_idxs[j] >= active_addresses_len ) {
    1001           0 :           return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX;
    1002           0 :         }
    1003           0 :         txn_ctx->account_keys[txn_ctx->accounts_cnt++] = lookup_addrs[writable_lut_idxs[j]];
    1004           0 :       }
    1005             : 
    1006           0 :       uchar * readonly_lut_idxs = (uchar *)txn_ctx->_txn_raw->raw + addr_lut->readonly_off;
    1007           0 :       for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
    1008             :         /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */
    1009           0 :         if( readonly_lut_idxs[j] >= active_addresses_len ) {
    1010           0 :           return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX;
    1011           0 :         }
    1012           0 :         readonly_lut_accs[readonly_lut_accs_cnt++] = lookup_addrs[readonly_lut_idxs[j]];
    1013           0 :       }
    1014           0 :     }
    1015             : 
    1016           0 :     fd_memcpy( &txn_ctx->account_keys[txn_ctx->accounts_cnt], readonly_lut_accs, readonly_lut_accs_cnt * sizeof(fd_pubkey_t) );
    1017           0 :     txn_ctx->accounts_cnt += readonly_lut_accs_cnt;
    1018           0 :   }
    1019           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1020             : 
    1021           0 :   } FD_SPAD_FRAME_END;
    1022           0 : }
    1023             : 
    1024             : /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L319-L357 */
    1025             : static inline int
    1026             : fd_txn_ctx_push( fd_exec_txn_ctx_t * txn_ctx,
    1027           0 :                  fd_instr_info_t *   instr ) {
    1028             :   /* Earlier checks in the permalink are redundant since Agave maintains instr stack and trace accounts separately
    1029             :      https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L327-L328 */
    1030           0 :   ulong starting_lamports_h = 0UL;
    1031           0 :   ulong starting_lamports_l = 0UL;
    1032           0 :   int err = fd_instr_info_sum_account_lamports( instr, &starting_lamports_h, &starting_lamports_l );
    1033           0 :   if( FD_UNLIKELY( err ) ) {
    1034           0 :     return err;
    1035           0 :   }
    1036           0 :   instr->starting_lamports_h = starting_lamports_h;
    1037           0 :   instr->starting_lamports_l = starting_lamports_l;
    1038             : 
    1039             :   /* Check that the caller's lamport sum has not changed.
    1040             :      https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L329-L340 */
    1041           0 :   if( txn_ctx->instr_stack_sz>0 ) {
    1042             :     /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L330 */
    1043           0 :     fd_exec_instr_ctx_t const * caller_instruction_context = &txn_ctx->instr_stack[ txn_ctx->instr_stack_sz-1 ];
    1044             : 
    1045             :     /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L331-L332 */
    1046           0 :     ulong original_caller_lamport_sum_h = caller_instruction_context->instr->starting_lamports_h;
    1047           0 :     ulong original_caller_lamport_sum_l = caller_instruction_context->instr->starting_lamports_l;
    1048             : 
    1049             :     /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L333-L334 */
    1050           0 :     ulong current_caller_lamport_sum_h = 0UL;
    1051           0 :     ulong current_caller_lamport_sum_l = 0UL;
    1052           0 :     int err = fd_instr_info_sum_account_lamports( caller_instruction_context->instr, &current_caller_lamport_sum_h, &current_caller_lamport_sum_l );
    1053           0 :     if( FD_UNLIKELY( err ) ) {
    1054           0 :       return err;
    1055           0 :     }
    1056             : 
    1057             :     /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L335-L339 */
    1058           0 :     if( FD_UNLIKELY( current_caller_lamport_sum_h!=original_caller_lamport_sum_h ||
    1059           0 :                      current_caller_lamport_sum_l!=original_caller_lamport_sum_l ) ) {
    1060           0 :       return FD_EXECUTOR_INSTR_ERR_UNBALANCED_INSTR;
    1061           0 :     }
    1062           0 :   }
    1063             : 
    1064             :   /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L347-L351 */
    1065           0 :   if( FD_UNLIKELY( txn_ctx->instr_trace_length>=FD_MAX_INSTRUCTION_TRACE_LENGTH ) ) {
    1066           0 :     return FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED;
    1067           0 :   }
    1068           0 :   txn_ctx->instr_trace_length++;
    1069             : 
    1070             :   /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L352-L356 */
    1071           0 :   if( FD_UNLIKELY( txn_ctx->instr_stack_sz>=FD_MAX_INSTRUCTION_STACK_DEPTH ) ) {
    1072           0 :     return FD_EXECUTOR_INSTR_ERR_CALL_DEPTH;
    1073           0 :   }
    1074           0 :   txn_ctx->instr_stack_sz++;
    1075             : 
    1076           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
    1077           0 : }
    1078             : 
    1079             : /* Pushes a new instruction onto the instruction stack and trace. This check loops through all instructions in the current call stack
    1080             :    and checks for reentrancy violations. If successful, simply increments the instruction stack and trace size and returns. It is
    1081             :    the responsibility of the caller to populate the newly pushed instruction fields, which are undefined otherwise.
    1082             : 
    1083             :    https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L246-L290 */
    1084             : int
    1085             : fd_instr_stack_push( fd_exec_txn_ctx_t *     txn_ctx,
    1086           0 :                      fd_instr_info_t *       instr ) {
    1087             :   /* Agave keeps a vector of vectors called program_indices that stores the program_id index for each instruction within the transaction.
    1088             :      https://github.com/anza-xyz/agave/blob/v2.1.7/svm/src/account_loader.rs#L347-L402
    1089             :      If and only if the program_id is the native loader, then the vector for respective specific instruction (account_indices) is empty.
    1090             :      https://github.com/anza-xyz/agave/blob/v2.1.7/svm/src/account_loader.rs#L350-L358
    1091             :      While trying to push a new instruction onto the instruction stack, if the vector for the respective instruction is empty, Agave throws UnsupportedProgramId
    1092             :      https://github.com/anza-xyz/agave/blob/v2.1.7/program-runtime/src/invoke_context.rs#L253-L255
    1093             :      The only way for the vector to be empty is if the program_id is the native loader, so we can a program_id check here
    1094             :      */
    1095           0 :   if( FD_UNLIKELY( !memcmp(instr->program_id_pubkey.key, fd_solana_native_loader_id.key, sizeof(fd_pubkey_t)) ) ) {
    1096           0 :     return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
    1097           0 :   }
    1098             :   /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L256-L286 */
    1099           0 :   if( txn_ctx->instr_stack_sz ) {
    1100             :     /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L261-L285 */
    1101           0 :     uchar contains = 0;
    1102           0 :     uchar is_last  = 0;
    1103             : 
    1104             :     // Checks all previous instructions in the stack for reentrancy
    1105           0 :     for( uchar level=0; level<txn_ctx->instr_stack_sz; level++ ) {
    1106           0 :       fd_exec_instr_ctx_t * instr_ctx = &txn_ctx->instr_stack[level];
    1107             :       // Optimization: compare program id index instead of pubkey since account keys are unique
    1108           0 :       if( instr->program_id == instr_ctx->instr->program_id ) {
    1109             :         // Reentrancy not allowed unless caller is calling itself
    1110           0 :         if( level == txn_ctx->instr_stack_sz-1 ) {
    1111           0 :           is_last = 1;
    1112           0 :         }
    1113           0 :         contains = 1;
    1114           0 :       }
    1115           0 :     }
    1116             :     /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L282-L285 */
    1117           0 :     if( FD_UNLIKELY( contains && !is_last ) ) {
    1118           0 :       return FD_EXECUTOR_INSTR_ERR_REENTRANCY_NOT_ALLOWED;
    1119           0 :     }
    1120           0 :   }
    1121             :   /* "Push" a new instruction onto the stack by simply incrementing the stack and trace size counters
    1122             :      https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L289 */
    1123           0 :   return fd_txn_ctx_push( txn_ctx, instr );
    1124           0 : }
    1125             : 
    1126             : /* Pops an instruction from the instruction stack. Agave's implementation performs instruction balancing checks every time pop is called,
    1127             :    but error codes returned from `pop` are only used if the program's execution was successful. Therefore, we can optimize our code by only
    1128             :    checking for unbalanced instructions if the program execution was successful within fd_execute_instr.
    1129             : 
    1130             :    https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L293-L298 */
    1131             : int
    1132             : fd_instr_stack_pop( fd_exec_txn_ctx_t *       txn_ctx,
    1133           0 :                     fd_instr_info_t const *   instr ) {
    1134             :   /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L362-L364 */
    1135           0 :   if( FD_UNLIKELY( txn_ctx->instr_stack_sz==0 ) ) {
    1136           0 :     return FD_EXECUTOR_INSTR_ERR_CALL_DEPTH;
    1137           0 :   }
    1138           0 :   txn_ctx->instr_stack_sz--;
    1139             : 
    1140             :   /* Verify all executable accounts have no outstanding refs
    1141             :      https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L369-L374 */
    1142           0 :   for( ushort i=0; i<instr->acct_cnt; i++ ) {
    1143           0 :     if( FD_UNLIKELY( instr->accounts[i]->const_meta->info.executable &&
    1144           0 :                      instr->accounts[i]->refcnt_excl ) ) {
    1145           0 :       return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_OUTSTANDING;
    1146           0 :     }
    1147           0 :   }
    1148             : 
    1149             :   /* Verify lamports are balanced before and after instruction
    1150             :      https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L366-L380 */
    1151           0 :   ulong ending_lamports_h = 0UL;
    1152           0 :   ulong ending_lamports_l = 0UL;
    1153           0 :   int err = fd_instr_info_sum_account_lamports( instr, &ending_lamports_h, &ending_lamports_l );
    1154           0 :   if( FD_UNLIKELY( err ) ) {
    1155           0 :     return err;
    1156           0 :   }
    1157           0 :   if( FD_UNLIKELY( ending_lamports_l != instr->starting_lamports_l || ending_lamports_h != instr->starting_lamports_h ) ) {
    1158           0 :    return FD_EXECUTOR_INSTR_ERR_UNBALANCED_INSTR;
    1159           0 :   }
    1160             : 
    1161           0 :   return FD_EXECUTOR_INSTR_SUCCESS;;
    1162           0 : }
    1163             : 
    1164             : int
    1165             : fd_execute_instr( fd_exec_txn_ctx_t * txn_ctx,
    1166           0 :                   fd_instr_info_t *   instr ) {
    1167           0 :   FD_RUNTIME_TXN_SPAD_FRAME_BEGIN( txn_ctx->spad, txn_ctx ) {
    1168           0 :     fd_exec_instr_ctx_t * parent = NULL;
    1169           0 :     if( txn_ctx->instr_stack_sz ) {
    1170           0 :       parent = &txn_ctx->instr_stack[ txn_ctx->instr_stack_sz - 1 ];
    1171           0 :     }
    1172             : 
    1173           0 :     int instr_exec_result = fd_instr_stack_push( txn_ctx, instr );
    1174           0 :     if( FD_UNLIKELY( instr_exec_result ) ) {
    1175           0 :       FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx );
    1176           0 :       FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, instr_exec_result, txn_ctx->instr_err_idx );
    1177           0 :       return instr_exec_result;
    1178           0 :     }
    1179             : 
    1180           0 :     fd_exec_instr_ctx_t * ctx = &txn_ctx->instr_stack[ txn_ctx->instr_stack_sz - 1 ];
    1181           0 :     *ctx = (fd_exec_instr_ctx_t) {
    1182           0 :       .instr     = instr,
    1183           0 :       .txn_ctx   = txn_ctx,
    1184           0 :       .acc_mgr   = txn_ctx->acc_mgr,
    1185           0 :       .funk_txn  = txn_ctx->funk_txn,
    1186           0 :       .parent    = parent,
    1187           0 :       .index     = parent ? (parent->child_cnt++) : 0,
    1188           0 :       .depth     = parent ? (parent->depth+1    ) : 0,
    1189           0 :       .child_cnt = 0U,
    1190           0 :     };
    1191             : 
    1192           0 :     txn_ctx->instr_trace[ txn_ctx->instr_trace_length - 1 ] = (fd_exec_instr_trace_entry_t) {
    1193           0 :       .instr_info = instr,
    1194           0 :       .stack_height = txn_ctx->instr_stack_sz,
    1195           0 :     };
    1196             : 
    1197             :     /* Lookup whether the program is a native precompiled program first
    1198             :        https://github.com/anza-xyz/agave/blob/v2.1.6/svm/src/message_processor.rs#L88 */
    1199           0 :     fd_exec_instr_fn_t native_prog_fn = fd_executor_lookup_native_precompile_program( &txn_ctx->accounts[ instr->program_id ] );
    1200             : 
    1201           0 :     if( FD_LIKELY( native_prog_fn == NULL ) ) {
    1202             :       /* Lookup a native builtin program if the program is not a precompiled program */
    1203           0 :       native_prog_fn = fd_executor_lookup_native_program( &txn_ctx->accounts[ instr->program_id ] );
    1204             :       /* Only reset the return data when executing a native builtin program (not a precompile)
    1205             :          https://github.com/anza-xyz/agave/blob/v2.1.6/program-runtime/src/invoke_context.rs#L536-L537 */
    1206           0 :       fd_exec_txn_ctx_reset_return_data( txn_ctx );
    1207           0 :     }
    1208             : 
    1209           0 :     if( FD_LIKELY( native_prog_fn != NULL ) ) {
    1210             :       /* Log program invokation (internally caches program_id base58) */
    1211           0 :       fd_log_collector_program_invoke( ctx );
    1212           0 :       instr_exec_result = native_prog_fn( ctx );
    1213           0 :     } else {
    1214           0 :       instr_exec_result = FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
    1215           0 :     }
    1216             : 
    1217           0 :     int stack_pop_err = fd_instr_stack_pop( txn_ctx, instr );
    1218           0 :     if( FD_LIKELY( instr_exec_result==FD_EXECUTOR_INSTR_SUCCESS ) ) {
    1219             :       /* Log success */
    1220           0 :       fd_log_collector_program_success( ctx );
    1221             : 
    1222             :       /* Only report the stack pop error on success */
    1223           0 :       if( FD_UNLIKELY( stack_pop_err ) ) {
    1224           0 :         FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx );
    1225           0 :         FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, stack_pop_err, txn_ctx->instr_err_idx );
    1226           0 :         instr_exec_result = stack_pop_err;
    1227           0 :       }
    1228           0 :     } else {
    1229           0 :       FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx );
    1230           0 :       FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, instr_exec_result, txn_ctx->instr_err_idx );
    1231             : 
    1232             :       /* Log failure cases.
    1233             :          We assume that the correct type of error is stored in ctx.
    1234             :          Syscalls are expected to log when the error is generated, while
    1235             :          native programs will be logged here.
    1236             :          (This is because syscall errors often carry data with them.) */
    1237           0 :       fd_log_collector_program_failure( ctx );
    1238           0 :     }
    1239             : 
    1240           0 :     if( FD_UNLIKELY( instr_exec_result && !txn_ctx->failed_instr ) ) {
    1241           0 :       txn_ctx->failed_instr = ctx;
    1242           0 :       ctx->instr_err        = (uint)( -instr_exec_result - 1 );
    1243           0 :     }
    1244             : 
    1245             : #ifdef VLOG
    1246             :   if ( FD_UNLIKELY( exec_result != FD_EXECUTOR_INSTR_SUCCESS ) ) {
    1247             :     FD_LOG_WARNING(( "instruction executed unsuccessfully: error code %d, custom err: %d, program id: %s", exec_result, txn_ctx->custom_err, FD_BASE58_ENC_32_ALLOCA( instr->program_id_pubkey.uc ));
    1248             :   } else {
    1249             :     FD_LOG_WARNING(( "instruction executed successfully: error code %d, custom err: %d, program id: %s", exec_result, txn_ctx->custom_err, FD_BASE58_ENC_32_ALLOCA( instr->program_id_pubkey.uc ));
    1250             :   }
    1251             : #endif
    1252           0 :     return instr_exec_result;
    1253           0 :   } FD_RUNTIME_TXN_SPAD_FRAME_END;
    1254           0 : }
    1255             : 
    1256             : void
    1257           0 : fd_txn_reclaim_accounts( fd_exec_txn_ctx_t * txn_ctx ) {
    1258           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
    1259           0 :     fd_txn_account_t * acc_rec = &txn_ctx->accounts[i];
    1260             : 
    1261             :     /* An account writable iff it is writable AND it is not being demoted.
    1262             :         If this criteria is not met, the account should not be marked as touched
    1263             :         via updating its most recent slot. */
    1264           0 :     if( !fd_txn_account_is_writable_idx( txn_ctx, (int)i ) ) {
    1265           0 :       continue;
    1266           0 :     }
    1267             : 
    1268           0 :     acc_rec->meta->slot = txn_ctx->slot;
    1269             : 
    1270           0 :     if( acc_rec->meta->info.lamports == 0 ) {
    1271           0 :       acc_rec->meta->dlen = 0;
    1272           0 :       memset( acc_rec->meta->info.owner, 0, sizeof(fd_pubkey_t) );
    1273           0 :     }
    1274           0 :   }
    1275           0 : }
    1276             : 
    1277             : int
    1278             : fd_executor_is_blockhash_valid_for_age( fd_block_hash_queue_t const * block_hash_queue,
    1279             :                                         fd_hash_t const *             blockhash,
    1280           0 :                                         ulong                         max_age ) {
    1281           0 :   fd_hash_hash_age_pair_t_mapnode_t key;
    1282           0 :   fd_memcpy( key.elem.key.uc, blockhash, sizeof(fd_hash_t) );
    1283             : 
    1284           0 :   fd_hash_hash_age_pair_t_mapnode_t * hash_age = fd_hash_hash_age_pair_t_map_find( block_hash_queue->ages_pool, block_hash_queue->ages_root, &key );
    1285           0 :   if( hash_age==NULL ) {
    1286             :     #ifdef VLOG
    1287             :     FD_LOG_WARNING(( "txn with missing recent blockhash - blockhash: %s", FD_BASE58_ENC_32_ALLOCA( blockhash->uc ) ));
    1288             :     #endif
    1289           0 :     return 0;
    1290           0 :   }
    1291           0 :   ulong age = block_hash_queue->last_hash_index-hash_age->elem.val.hash_index;
    1292             : #ifdef VLOG
    1293             :   if( age>max_age ) {
    1294             :     FD_LOG_WARNING(( "txn with old blockhash - age: %lu, blockhash: %s", age, FD_BASE58_ENC_32_ALLOCA( hash_age->elem.key.uc ) ));
    1295             :   }
    1296             : #endif
    1297           0 :   return ( age<=max_age );
    1298           0 : }
    1299             : 
    1300             : void
    1301           0 : fd_executor_setup_borrowed_accounts_for_txn( fd_exec_txn_ctx_t * txn_ctx ) {
    1302           0 :   ulong j = 0UL;
    1303           0 :   for( ulong i = 0UL; i < txn_ctx->accounts_cnt; i++ ) {
    1304             : 
    1305           0 :     fd_pubkey_t * acc = &txn_ctx->account_keys[i];
    1306             : 
    1307           0 :     txn_ctx->nonce_account_idx_in_txn = ULONG_MAX;
    1308           0 :     txn_ctx->nonce_account_advanced   = 0U;
    1309             : 
    1310           0 :     int                err         = fd_txn_account_create_from_funk( &txn_ctx->accounts[i], acc, txn_ctx->acc_mgr, txn_ctx->funk_txn );
    1311           0 :     fd_txn_account_t * txn_account = &txn_ctx->accounts[i];
    1312           0 :     if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS && err!=FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) {
    1313           0 :       FD_LOG_ERR(( "fd_acc_mgr_view err=%d", err ));
    1314           0 :     }
    1315           0 :     uchar is_unknown_account = err==FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
    1316           0 :     memcpy( txn_account->pubkey->key, acc, sizeof(fd_pubkey_t) );
    1317           0 :     if ( FD_UNLIKELY( is_unknown_account ) ) {
    1318           0 :       txn_account->account_found = 0;
    1319           0 :     }
    1320             : 
    1321             :     /* Create a txn account for all writable accounts and the fee payer
    1322             :        account which is almost always writable, but doesn't have to be.
    1323             : 
    1324             :        TODO: The txn account semantics should better match Agave's. */
    1325           0 :     if( fd_txn_account_is_writable_idx( txn_ctx, (int)i ) || i==FD_FEE_PAYER_TXN_IDX ) {
    1326           0 :       void * txn_account_data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
    1327           0 :       fd_txn_account_make_mutable( txn_account, txn_account_data );
    1328             : 
    1329             :       /* All new accounts should have their rent epoch set to ULONG_MAX.
    1330             :          https://github.com/anza-xyz/agave/blob/89050f3cb7e76d9e273f10bea5e8207f2452f79f/svm/src/account_loader.rs#L485-L497 */
    1331           0 :       if( !txn_account->account_found ||
    1332           0 :           (i>0UL && fd_should_set_exempt_rent_epoch_max( txn_ctx, txn_account )) ) {
    1333           0 :         txn_account->meta->info.rent_epoch = ULONG_MAX;
    1334           0 :       }
    1335           0 :     }
    1336             : 
    1337           0 :     fd_account_meta_t const * meta = txn_account->const_meta ? txn_account->const_meta : txn_account->meta;
    1338           0 :     if( meta==NULL ) {
    1339           0 :       static const fd_account_meta_t sentinel = { .magic = FD_ACCOUNT_META_MAGIC, .info = { .rent_epoch = ULONG_MAX } };
    1340           0 :       txn_account->const_meta        = &sentinel;
    1341           0 :       txn_account->starting_lamports = 0UL;
    1342           0 :       txn_account->starting_dlen     = 0UL;
    1343           0 :       continue;
    1344           0 :     }
    1345             : 
    1346           0 :     if( meta->info.executable ) {
    1347           0 :       FD_TXN_ACCOUNT_DECL( owner_txn_account );
    1348           0 :       int err = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, (fd_pubkey_t *)meta->info.owner, owner_txn_account );
    1349           0 :       if( FD_UNLIKELY( err ) ) {
    1350           0 :         txn_account->starting_owner_dlen = 0UL;
    1351           0 :       } else {
    1352           0 :         txn_account->starting_owner_dlen = owner_txn_account->const_meta->dlen;
    1353           0 :       }
    1354           0 :     }
    1355             : 
    1356           0 :     if( FD_UNLIKELY( memcmp( meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) == 0 ) ) {
    1357           0 :       int err = 0;
    1358           0 :       fd_bpf_upgradeable_loader_state_t * program_loader_state = read_bpf_upgradeable_loader_state_for_program( txn_ctx, (uchar)i, &err );
    1359           0 :       if( FD_UNLIKELY( !program_loader_state ) ) {
    1360           0 :         continue;
    1361           0 :       }
    1362             : 
    1363           0 :       if( !fd_bpf_upgradeable_loader_state_is_program( program_loader_state ) ) {
    1364           0 :         continue;
    1365           0 :       }
    1366             : 
    1367           0 :       fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
    1368           0 :       fd_txn_account_create_from_funk( &txn_ctx->executable_accounts[j], programdata_acc, txn_ctx->acc_mgr, txn_ctx->funk_txn );
    1369           0 :       j++;
    1370           0 :     }
    1371           0 :   }
    1372           0 :   txn_ctx->executable_cnt = j;
    1373           0 : }
    1374             : 
    1375             : /* Stuff to be done before multithreading can begin */
    1376             : int
    1377             : fd_execute_txn_prepare_start( fd_exec_slot_ctx_t const * slot_ctx,
    1378             :                               fd_exec_txn_ctx_t *        txn_ctx,
    1379             :                               fd_txn_t const *           txn_descriptor,
    1380             :                               fd_rawtxn_b_t const *      txn_raw,
    1381           0 :                               fd_spad_t *                spad ) {
    1382             : 
    1383           0 :   fd_funk_t * funk               = slot_ctx->acc_mgr->funk;
    1384           0 :   fd_wksp_t * funk_wksp          = fd_funk_wksp( funk );
    1385           0 :   fd_wksp_t * runtime_pub_wksp   = fd_wksp_containing( slot_ctx );
    1386           0 :   ulong       funk_txn_gaddr     = fd_wksp_gaddr( funk_wksp, slot_ctx->funk_txn );
    1387           0 :   ulong       acc_mgr_gaddr      = fd_wksp_gaddr( runtime_pub_wksp, slot_ctx->acc_mgr );
    1388           0 :   ulong       funk_gaddr         = fd_wksp_gaddr( funk_wksp, slot_ctx->acc_mgr->funk );
    1389           0 :   ulong       sysvar_cache_gaddr = fd_wksp_gaddr( runtime_pub_wksp, slot_ctx->sysvar_cache );
    1390             : 
    1391             :   /* Init txn ctx */
    1392           0 :   fd_exec_txn_ctx_new( txn_ctx );
    1393           0 :   fd_exec_txn_ctx_from_exec_slot_ctx( slot_ctx,
    1394           0 :                                       txn_ctx,
    1395           0 :                                       funk_wksp,
    1396           0 :                                       runtime_pub_wksp,
    1397           0 :                                       funk_txn_gaddr,
    1398           0 :                                       acc_mgr_gaddr,
    1399           0 :                                       sysvar_cache_gaddr,
    1400           0 :                                       funk_gaddr );
    1401           0 :   fd_exec_txn_ctx_setup( txn_ctx, txn_descriptor, txn_raw );
    1402             : 
    1403             :   /* Unroll accounts from aluts and place into correct spots */
    1404           0 :   int res = fd_executor_setup_accessed_accounts_for_txn( txn_ctx, spad );
    1405             : 
    1406           0 :   return res;
    1407           0 : }
    1408             : 
    1409             : int
    1410           0 : fd_executor_txn_verify( fd_exec_txn_ctx_t * txn_ctx ) {
    1411           0 :   fd_sha512_t * shas[ FD_TXN_ACTUAL_SIG_MAX ];
    1412           0 :   for ( ulong i=0UL; i<FD_TXN_ACTUAL_SIG_MAX; i++ ) {
    1413           0 :     fd_sha512_t * sha = fd_sha512_join( fd_sha512_new( fd_spad_alloc( txn_ctx->spad, alignof(fd_sha512_t), sizeof(fd_sha512_t) ) ) );
    1414           0 :     if( FD_UNLIKELY( !sha ) ) FD_LOG_ERR(( "fd_sha512_join failed" ));
    1415           0 :     shas[i] = sha;
    1416           0 :   }
    1417             : 
    1418           0 :   uchar  signature_cnt = txn_ctx->txn_descriptor->signature_cnt;
    1419           0 :   ushort signature_off = txn_ctx->txn_descriptor->signature_off;
    1420           0 :   ushort acct_addr_off = txn_ctx->txn_descriptor->acct_addr_off;
    1421           0 :   ushort message_off   = txn_ctx->txn_descriptor->message_off;
    1422             : 
    1423           0 :   uchar const * signatures = (uchar *)txn_ctx->_txn_raw->raw + signature_off;
    1424           0 :   uchar const * pubkeys = (uchar *)txn_ctx->_txn_raw->raw + acct_addr_off;
    1425           0 :   uchar const * msg = (uchar *)txn_ctx->_txn_raw->raw + message_off;
    1426           0 :   ulong msg_sz = (ulong)txn_ctx->_txn_raw->txn_sz - message_off;
    1427             : 
    1428             :   /* Verify signatures */
    1429           0 :   int res = fd_ed25519_verify_batch_single_msg( msg, msg_sz, signatures, pubkeys, shas, signature_cnt );
    1430           0 :   if( FD_UNLIKELY( res != FD_ED25519_SUCCESS ) ) {
    1431           0 :     return -1;
    1432           0 :   }
    1433             : 
    1434           0 :   return 0;
    1435           0 : }
    1436             : 
    1437             : int
    1438           0 : fd_execute_txn( fd_execute_txn_task_info_t * task_info ) {
    1439             :   /* Don't execute transactions that are fee only.
    1440             :      https://github.com/anza-xyz/agave/blob/v2.1.6/svm/src/transaction_processor.rs#L341-L357 */
    1441           0 :   if( FD_UNLIKELY( task_info->txn->flags & FD_TXN_P_FLAGS_FEES_ONLY ) ) {
    1442             :     /* return the existing error */
    1443           0 :     return task_info->exec_res;
    1444           0 :   }
    1445             : 
    1446           0 :   fd_exec_txn_ctx_t * txn_ctx  = task_info->txn_ctx;
    1447           0 :   uint use_sysvar_instructions = fd_executor_txn_uses_sysvar_instructions( txn_ctx );
    1448           0 :   int  ret                     = 0;
    1449             : 
    1450             : #ifdef VLOG
    1451             :   fd_txn_t const *txn = txn_ctx->txn_descriptor;
    1452             :   fd_rawtxn_b_t const *raw_txn = txn_ctx->_txn_raw;
    1453             :   uchar * sig = (uchar *)raw_txn->raw + txn->signature_off;
    1454             : #endif
    1455             : 
    1456           0 :   bool dump_insn = txn_ctx->capture_ctx && txn_ctx->slot >= txn_ctx->capture_ctx->dump_proto_start_slot && txn_ctx->capture_ctx->dump_insn_to_pb;
    1457             : 
    1458             :   /* Initialize log collection */
    1459           0 :   fd_log_collector_init( &txn_ctx->log_collector, txn_ctx->enable_exec_recording );
    1460             : 
    1461           0 :   for ( ushort i = 0; i < txn_ctx->txn_descriptor->instr_cnt; i++ ) {
    1462             : #ifdef VLOG
    1463             :     FD_LOG_WARNING(( "Start of transaction for %d for %s", i, FD_BASE58_ENC_64_ALLOCA( sig ) ));
    1464             : #endif
    1465             : 
    1466           0 :     if ( FD_UNLIKELY( use_sysvar_instructions ) ) {
    1467           0 :       ret = fd_sysvar_instructions_update_current_instr_idx( txn_ctx, i );
    1468           0 :       if( ret != FD_ACC_MGR_SUCCESS ) {
    1469           0 :         FD_LOG_WARNING(( "sysvar instructions failed to update instruction index" ));
    1470           0 :         return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
    1471           0 :       }
    1472           0 :     }
    1473             : 
    1474           0 :     if( dump_insn ) {
    1475             :       // Capture the input and convert it into a Protobuf message
    1476           0 :       fd_dump_instr_to_protobuf(txn_ctx, &txn_ctx->instr_infos[i], i);
    1477           0 :     }
    1478             : 
    1479           0 :     txn_ctx->current_instr_idx = i;
    1480           0 :     int instr_exec_result = fd_execute_instr( txn_ctx, &txn_ctx->instr_infos[i] );
    1481             : #ifdef VLOG
    1482             :     FD_LOG_WARNING(( "fd_execute_instr result (%d) for %s", exec_result, FD_BASE58_ENC_64_ALLOCA( sig ) ));
    1483             : #endif
    1484           0 :     if( instr_exec_result != FD_EXECUTOR_INSTR_SUCCESS ) {
    1485           0 :       if ( txn_ctx->instr_err_idx == INT_MAX )
    1486           0 :       {
    1487           0 :         txn_ctx->instr_err_idx = i;
    1488           0 :       }
    1489             : #ifdef VLOG
    1490             :       if ( 257037453 == txn_ctx->slot ) {
    1491             : #endif
    1492           0 :         if (instr_exec_result == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
    1493             : #ifdef VLOG
    1494             :           FD_LOG_WARNING(( "fd_execute_instr failed (%d:%d) for %s",
    1495             :                             exec_result,
    1496             :                             txn_ctx->custom_err,
    1497             :                             FD_BASE58_ENC_64_ALLOCA( sig ) ));
    1498             : #endif
    1499           0 :         } else {
    1500             : #ifdef VLOG
    1501             :           FD_LOG_WARNING(( "fd_execute_instr failed (%d) index %u for %s",
    1502             :             exec_result,
    1503             :             i,
    1504             :             FD_BASE58_ENC_64_ALLOCA( sig ) ));
    1505             : #endif
    1506           0 :         }
    1507             : #ifdef VLOG
    1508             :       }
    1509             : #endif
    1510           0 :       return instr_exec_result ? FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR : FD_RUNTIME_EXECUTE_SUCCESS;
    1511           0 :     }
    1512           0 :   }
    1513             : 
    1514             :   /* TODO: This function needs to be split out of fd_execute_txn and be placed
    1515             :       into the replay tile once it is implemented. */
    1516           0 :   int err = fd_executor_txn_check( txn_ctx );
    1517           0 :   if( err != FD_EXECUTOR_INSTR_SUCCESS ) {
    1518           0 :     FD_LOG_DEBUG(( "fd_executor_txn_check failed (%d)", err ));
    1519           0 :     return err;
    1520           0 :   }
    1521             : 
    1522           0 :   return 0;
    1523           0 : }
    1524             : 
    1525             : int
    1526           0 : fd_executor_txn_check( fd_exec_txn_ctx_t * txn_ctx ) {
    1527           0 :   fd_rent_t const * rent = (fd_rent_t const *)fd_sysvar_cache_rent( txn_ctx->sysvar_cache );
    1528             : 
    1529           0 :   ulong starting_lamports_l = 0;
    1530           0 :   ulong starting_lamports_h = 0;
    1531             : 
    1532           0 :   ulong ending_lamports_l = 0;
    1533           0 :   ulong ending_lamports_h = 0;
    1534             : 
    1535             :   /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L63 */
    1536           0 :   for( ulong idx = 0; idx < txn_ctx->accounts_cnt; idx++ ) {
    1537           0 :     fd_txn_account_t * b = &txn_ctx->accounts[idx];
    1538             : 
    1539             :     // Was this account written to?
    1540           0 :     if( NULL != b->meta ) {
    1541           0 :       fd_uwide_inc( &ending_lamports_h, &ending_lamports_l, ending_lamports_h, ending_lamports_l, b->meta->info.lamports );
    1542             : 
    1543             :       /* Rent states are defined as followed:
    1544             :          - lamports == 0                      -> Uninitialized
    1545             :          - 0 < lamports < rent_exempt_minimum -> RentPaying
    1546             :          - lamports >= rent_exempt_minimum    -> RentExempt
    1547             :          In Agave, 'self' refers to our 'after' state. */
    1548           0 :       uchar after_uninitialized  = b->meta->info.lamports == 0;
    1549           0 :       uchar after_rent_exempt    = b->meta->info.lamports >= fd_rent_exempt_minimum_balance( rent, b->meta->dlen );
    1550             : 
    1551             :       /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L96 */
    1552           0 :       if( FD_LIKELY( memcmp( b->pubkey->key, fd_sysvar_incinerator_id.key, sizeof(fd_pubkey_t) ) != 0 ) ) {
    1553             :         /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L44 */
    1554           0 :         if( after_uninitialized || after_rent_exempt ) {
    1555             :           // no-op
    1556           0 :         } else {
    1557             :           /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L45-L59 */
    1558           0 :           uchar before_uninitialized = b->starting_dlen == ULONG_MAX || b->starting_lamports == 0;
    1559           0 :           uchar before_rent_exempt   = b->starting_dlen != ULONG_MAX && b->starting_lamports >= fd_rent_exempt_minimum_balance( rent, b->starting_dlen );
    1560             : 
    1561             :           /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L50 */
    1562           0 :           if( before_uninitialized || before_rent_exempt ) {
    1563           0 :             FD_LOG_DEBUG(( "Rent exempt error for %s Curr len %lu Starting len %lu Curr lamports %lu Starting lamports %lu Curr exempt %lu Starting exempt %lu",
    1564           0 :                            FD_BASE58_ENC_32_ALLOCA( b->pubkey->uc ),
    1565           0 :                            b->meta->dlen,
    1566           0 :                            b->starting_dlen,
    1567           0 :                            b->meta->info.lamports,
    1568           0 :                            b->starting_lamports,
    1569           0 :                            fd_rent_exempt_minimum_balance( rent, b->meta->dlen ),
    1570           0 :                            fd_rent_exempt_minimum_balance( rent, b->starting_dlen ) ));
    1571             :             /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L104 */
    1572           0 :             return FD_RUNTIME_TXN_ERR_INSUFFICIENT_FUNDS_FOR_RENT;
    1573             :           /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L56 */
    1574           0 :           } else if( (b->meta->dlen == b->starting_dlen) && b->meta->info.lamports <= b->starting_lamports ) {
    1575             :             // no-op
    1576           0 :           } else {
    1577           0 :             FD_LOG_DEBUG(( "Rent exempt error for %s Curr len %lu Starting len %lu Curr lamports %lu Starting lamports %lu Curr exempt %lu Starting exempt %lu",
    1578           0 :                            FD_BASE58_ENC_32_ALLOCA( b->pubkey->uc ),
    1579           0 :                            b->meta->dlen,
    1580           0 :                            b->starting_dlen,
    1581           0 :                            b->meta->info.lamports,
    1582           0 :                            b->starting_lamports,
    1583           0 :                            fd_rent_exempt_minimum_balance( rent, b->meta->dlen ),
    1584           0 :                            fd_rent_exempt_minimum_balance( rent, b->starting_dlen ) ));
    1585             :             /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/account_rent_state.rs#L104 */
    1586           0 :             return FD_RUNTIME_TXN_ERR_INSUFFICIENT_FUNDS_FOR_RENT;
    1587           0 :           }
    1588           0 :         }
    1589           0 :       }
    1590             : 
    1591           0 :       if( b->starting_lamports != ULONG_MAX ) {
    1592           0 :         fd_uwide_inc( &starting_lamports_h, &starting_lamports_l, starting_lamports_h, starting_lamports_l, b->starting_lamports );
    1593           0 :       }
    1594           0 :     }
    1595           0 :   }
    1596             : 
    1597             :   /* https://github.com/anza-xyz/agave/blob/b2c388d6cbff9b765d574bbb83a4378a1fc8af32/svm/src/transaction_processor.rs#L839-L845 */
    1598           0 :   if( FD_UNLIKELY( ending_lamports_l!=starting_lamports_l || ending_lamports_h!=starting_lamports_h ) ) {
    1599           0 :     FD_LOG_DEBUG(( "Lamport sum mismatch: starting %lx%lx ending %lx%lx", starting_lamports_h, starting_lamports_l, ending_lamports_h, ending_lamports_l ));
    1600           0 :     return FD_RUNTIME_TXN_ERR_UNBALANCED_TRANSACTION;
    1601           0 :   }
    1602             : 
    1603           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1604           0 : }
    1605             : #undef VLOG
    1606             : 
    1607             : /* fd_executor_instr_strerror() returns the error message corresponding to err,
    1608             :    intended to be logged by log_collector, or an empty string if the error code
    1609             :    should be omitted in logs for whatever reason.  Omitted examples are success,
    1610             :    fatal (placeholder just in firedancer), custom error.
    1611             :    See also fd_log_collector_program_failure(). */
    1612             : FD_FN_CONST char const *
    1613           0 : fd_executor_instr_strerror( int err ) {
    1614             : 
    1615           0 :   switch( err ) {
    1616           0 :   case FD_EXECUTOR_INSTR_SUCCESS                                : return ""; // not used
    1617           0 :   case FD_EXECUTOR_INSTR_ERR_FATAL                              : return ""; // not used
    1618           0 :   case FD_EXECUTOR_INSTR_ERR_GENERIC_ERR                        : return "generic instruction error";
    1619           0 :   case FD_EXECUTOR_INSTR_ERR_INVALID_ARG                        : return "invalid program argument";
    1620           0 :   case FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA                 : return "invalid instruction data";
    1621           0 :   case FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA                   : return "invalid account data for instruction";
    1622           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL                 : return "account data too small for instruction";
    1623           0 :   case FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS                 : return "insufficient funds for instruction";
    1624           0 :   case FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID               : return "incorrect program id for instruction";
    1625           0 :   case FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE         : return "missing required signature for instruction";
    1626           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED            : return "instruction requires an uninitialized account";
    1627           0 :   case FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT              : return "instruction requires an initialized account";
    1628           0 :   case FD_EXECUTOR_INSTR_ERR_UNBALANCED_INSTR                   : return "sum of account balances before and after instruction do not match";
    1629           0 :   case FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID                : return "instruction illegally modified the program id of an account";
    1630           0 :   case FD_EXECUTOR_INSTR_ERR_EXTERNAL_ACCOUNT_LAMPORT_SPEND     : return "instruction spent from the balance of an account it does not own";
    1631           0 :   case FD_EXECUTOR_INSTR_ERR_EXTERNAL_DATA_MODIFIED             : return "instruction modified data of an account it does not own";
    1632           0 :   case FD_EXECUTOR_INSTR_ERR_READONLY_LAMPORT_CHANGE            : return "instruction changed the balance of a read-only account";
    1633           0 :   case FD_EXECUTOR_INSTR_ERR_READONLY_DATA_MODIFIED             : return "instruction modified data of a read-only account";
    1634           0 :   case FD_EXECUTOR_INSTR_ERR_DUPLICATE_ACCOUNT_IDX              : return "instruction contains duplicate accounts";
    1635           0 :   case FD_EXECUTOR_INSTR_ERR_EXECUTABLE_MODIFIED                : return "instruction changed executable bit of an account";
    1636           0 :   case FD_EXECUTOR_INSTR_ERR_RENT_EPOCH_MODIFIED                : return "instruction modified rent epoch of an account";
    1637           0 :   case FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS                : return "insufficient account keys for instruction";
    1638           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_DATA_SIZE_CHANGED              : return "program other than the account's owner changed the size of the account data";
    1639           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_NOT_EXECUTABLE                 : return "instruction expected an executable account";
    1640           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED                  : return "instruction tries to borrow reference for an account which is already borrowed";
    1641           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_BORROW_OUTSTANDING             : return "instruction left account with an outstanding borrowed reference";
    1642           0 :   case FD_EXECUTOR_INSTR_ERR_DUPLICATE_ACCOUNT_OUT_OF_SYNC      : return "instruction modifications of multiply-passed account differ";
    1643           0 :   case FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR                         : return ""; // custom handling via txn_ctx->custom_err
    1644           0 :   case FD_EXECUTOR_INSTR_ERR_INVALID_ERR                        : return "program returned invalid error code";
    1645           0 :   case FD_EXECUTOR_INSTR_ERR_EXECUTABLE_DATA_MODIFIED           : return "instruction changed executable accounts data";
    1646           0 :   case FD_EXECUTOR_INSTR_ERR_EXECUTABLE_LAMPORT_CHANGE          : return "instruction changed the balance of an executable account";
    1647           0 :   case FD_EXECUTOR_INSTR_ERR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT : return "executable accounts must be rent exempt";
    1648           0 :   case FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID             : return "Unsupported program id";
    1649           0 :   case FD_EXECUTOR_INSTR_ERR_CALL_DEPTH                         : return "Cross-program invocation call depth too deep";
    1650           0 :   case FD_EXECUTOR_INSTR_ERR_MISSING_ACC                        : return "An account required by the instruction is missing";
    1651           0 :   case FD_EXECUTOR_INSTR_ERR_REENTRANCY_NOT_ALLOWED             : return "Cross-program invocation reentrancy not allowed for this instruction";
    1652           0 :   case FD_EXECUTOR_INSTR_ERR_MAX_SEED_LENGTH_EXCEEDED           : return "Length of the seed is too long for address generation";
    1653           0 :   case FD_EXECUTOR_INSTR_ERR_INVALID_SEEDS                      : return "Provided seeds do not result in a valid address";
    1654           0 :   case FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC                    : return "Failed to reallocate account data";
    1655           0 :   case FD_EXECUTOR_INSTR_ERR_COMPUTE_BUDGET_EXCEEDED            : return "Computational budget exceeded";
    1656           0 :   case FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION               : return "Cross-program invocation with unauthorized signer or writable account";
    1657           0 :   case FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE  : return "Failed to create program execution environment";
    1658           0 :   case FD_EXECUTOR_INSTR_ERR_PROGRAM_FAILED_TO_COMPLETE         : return "Program failed to complete";
    1659           0 :   case FD_EXECUTOR_INSTR_ERR_PROGRAM_FAILED_TO_COMPILE          : return "Program failed to compile";
    1660           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE                      : return "Account is immutable";
    1661           0 :   case FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY                : return "Incorrect authority provided";
    1662           0 :   case FD_EXECUTOR_INSTR_ERR_BORSH_IO_ERROR                     : return "Failed to serialize or deserialize account data"; // truncated
    1663           0 :   case FD_EXECUTOR_INSTR_ERR_ACC_NOT_RENT_EXEMPT                : return "An account does not have enough lamports to be rent-exempt";
    1664           0 :   case FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER                  : return "Invalid account owner";
    1665           0 :   case FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW                : return "Program arithmetic overflowed";
    1666           0 :   case FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR                 : return "Unsupported sysvar";
    1667           0 :   case FD_EXECUTOR_INSTR_ERR_ILLEGAL_OWNER                      : return "Provided owner is not allowed";
    1668           0 :   case FD_EXECUTOR_INSTR_ERR_MAX_ACCS_DATA_ALLOCS_EXCEEDED      : return "Accounts data allocations exceeded the maximum allowed per transaction";
    1669           0 :   case FD_EXECUTOR_INSTR_ERR_MAX_ACCS_EXCEEDED                  : return "Max accounts exceeded";
    1670           0 :   case FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED       : return "Max instruction trace length exceeded";
    1671           0 :   case FD_EXECUTOR_INSTR_ERR_BUILTINS_MUST_CONSUME_CUS          : return "Builtin programs must consume compute units";
    1672           0 :   default: break;
    1673           0 :   }
    1674             : 
    1675           0 :   return "";
    1676           0 : }
    1677             : 
    1678             : // This is purely linker magic to force the inclusion of the yaml type walker so that it is
    1679             : // available for debuggers
    1680             : void
    1681           0 : fd_debug_symbology(void) {
    1682           0 :   (void)fd_get_types_yaml();
    1683           0 : }

Generated by: LCOV version 1.14