LCOV - code coverage report
Current view: top level - flamenco/runtime/tests - fd_dump_pb.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 804 0.0 %
Date: 2025-11-11 04:33:07 Functions: 0 23 0.0 %

          Line data    Source code
       1             : #include "fd_dump_pb.h"
       2             : #include "generated/block.pb.h"
       3             : #include "generated/invoke.pb.h"
       4             : #include "generated/txn.pb.h"
       5             : #include "generated/vm.pb.h"
       6             : #include "../fd_system_ids.h"
       7             : #include "../fd_bank.h"
       8             : #include "../program/fd_address_lookup_table_program.h"
       9             : #include "../../../ballet/nanopb/pb_encode.h"
      10             : #include "../../progcache/fd_prog_load.h"
      11             : 
      12             : 
      13             : #include <stdio.h> /* fopen */
      14             : #include <sys/mman.h> /* mmap */
      15             : #include <unistd.h> /* ftruncate */
      16             : 
      17             : #define SORT_NAME        sort_uint64_t
      18           0 : #define SORT_KEY_T       uint64_t
      19           0 : #define SORT_BEFORE(a,b) (a)<(b)
      20             : #include "../../../util/tmpl/fd_sort.c"
      21             : 
      22             : struct fd_dump_account_key_node {
      23             :   fd_pubkey_t key;
      24             :   ulong       redblack_parent;
      25             :   ulong       redblack_left;
      26             :   ulong       redblack_right;
      27             :   int         redblack_color;
      28             : };
      29             : typedef struct fd_dump_account_key_node fd_dump_account_key_node_t;
      30           0 : #define REDBLK_T fd_dump_account_key_node_t
      31             : #define REDBLK_NAME fd_dump_account_key_map
      32           0 : long fd_dump_account_key_map_compare( fd_dump_account_key_node_t * left, fd_dump_account_key_node_t * right ) {
      33           0 :   return memcmp( left->key.uc, right->key.uc, sizeof(fd_pubkey_t) );
      34           0 : }
      35             : #include "../../../util/tmpl/fd_redblack.c"
      36             : 
      37             : /***** CONSTANTS *****/
      38             : static fd_pubkey_t const * fd_dump_sysvar_ids[] = {
      39             :   &fd_sysvar_recent_block_hashes_id,
      40             :   &fd_sysvar_clock_id,
      41             :   &fd_sysvar_slot_history_id,
      42             :   &fd_sysvar_slot_hashes_id,
      43             :   &fd_sysvar_epoch_schedule_id,
      44             :   &fd_sysvar_epoch_rewards_id,
      45             :   &fd_sysvar_fees_id,
      46             :   &fd_sysvar_rent_id,
      47             :   &fd_sysvar_stake_history_id,
      48             :   &fd_sysvar_last_restart_slot_id,
      49             :   &fd_sysvar_instructions_id,
      50             : };
      51             : static ulong const num_sysvar_entries = (sizeof(fd_dump_sysvar_ids) / sizeof(fd_pubkey_t *));
      52             : 
      53             : static fd_pubkey_t const * fd_dump_builtin_ids[] = {
      54             :   &fd_solana_system_program_id,
      55             :   &fd_solana_vote_program_id,
      56             :   &fd_solana_stake_program_id,
      57             :   &fd_solana_bpf_loader_v4_program_id,
      58             :   &fd_solana_bpf_loader_deprecated_program_id,
      59             :   &fd_solana_bpf_loader_program_id,
      60             :   &fd_solana_bpf_loader_upgradeable_program_id,
      61             :   &fd_solana_compute_budget_program_id,
      62             :   &fd_solana_keccak_secp_256k_program_id,
      63             :   &fd_solana_secp256r1_program_id,
      64             :   &fd_solana_zk_elgamal_proof_program_id,
      65             :   &fd_solana_ed25519_sig_verify_program_id,
      66             : };
      67             : static ulong const num_loaded_builtins = (sizeof(fd_dump_builtin_ids) / sizeof(fd_pubkey_t *));
      68             : 
      69             : /***** UTILITY FUNCTIONS *****/
      70             : 
      71             : /** GENERAL UTILITY FUNCTIONS AND MACROS **/
      72             : 
      73             : static inline int
      74           0 : is_builtin_account( fd_pubkey_t const * account_key ) {
      75           0 :   for( ulong j=0UL; j<num_loaded_builtins; j++ ) {
      76           0 :     if( !memcmp( account_key, fd_dump_builtin_ids[j], sizeof(fd_pubkey_t) ) ) {
      77           0 :       return 1;
      78           0 :     }
      79           0 :   }
      80           0 :   return 0;
      81           0 : }
      82             : 
      83             : /** FEATURE DUMPING **/
      84             : static void
      85             : dump_sorted_features( fd_features_t const *        features,
      86             :                       fd_exec_test_feature_set_t * output_feature_set,
      87           0 :                       fd_spad_t *                  spad ) {
      88             :   /* NOTE: Caller must have a spad frame prepared */
      89           0 :   uint64_t * unsorted_features = fd_spad_alloc( spad, alignof(uint64_t), FD_FEATURE_ID_CNT * sizeof(uint64_t) );
      90           0 :   ulong num_features = 0;
      91           0 :   for( const fd_feature_id_t * current_feature = fd_feature_iter_init(); !fd_feature_iter_done( current_feature ); current_feature = fd_feature_iter_next( current_feature ) ) {
      92           0 :     if (features->f[current_feature->index] != FD_FEATURE_DISABLED) {
      93           0 :       unsorted_features[num_features++] = (uint64_t) current_feature->id.ul[0];
      94           0 :     }
      95           0 :   }
      96             :   // Sort the features
      97           0 :   void * scratch = fd_spad_alloc( spad, sort_uint64_t_stable_scratch_align(), sort_uint64_t_stable_scratch_footprint(num_features) );
      98           0 :   uint64_t * sorted_features = sort_uint64_t_stable_fast( unsorted_features, num_features, scratch );
      99             : 
     100             :   // Set feature set in message
     101           0 :   output_feature_set->features_count = (pb_size_t) num_features;
     102           0 :   output_feature_set->features       = sorted_features;
     103           0 : }
     104             : 
     105             : /** ACCOUNT DUMPING **/
     106             : static void
     107             : dump_account_state( fd_txn_account_t const *    txn_account,
     108             :                     fd_exec_test_acct_state_t * output_account,
     109           0 :                     fd_spad_t *                 spad ) {
     110             :     // Address
     111           0 :     fd_memcpy(output_account->address, txn_account->pubkey, sizeof(fd_pubkey_t));
     112             : 
     113             :     // Lamports
     114           0 :     output_account->lamports = (uint64_t)fd_txn_account_get_lamports( txn_account );
     115             : 
     116             :     // Data
     117           0 :     output_account->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( fd_txn_account_get_data_len( txn_account ) ) );
     118           0 :     output_account->data->size = (pb_size_t) fd_txn_account_get_data_len( txn_account );
     119           0 :     fd_memcpy(output_account->data->bytes, fd_txn_account_get_data( txn_account ), fd_txn_account_get_data_len( txn_account ) );
     120             : 
     121             :     // Executable
     122           0 :     output_account->executable = (bool)fd_txn_account_is_executable( txn_account );
     123             : 
     124             :     // Owner
     125           0 :     fd_memcpy(output_account->owner, fd_txn_account_get_owner( txn_account ), sizeof(fd_pubkey_t));
     126           0 : }
     127             : 
     128             : static uchar
     129             : account_already_dumped( fd_exec_test_acct_state_t const * dumped_accounts,
     130             :                         ulong                             dumped_cnt,
     131           0 :                         fd_pubkey_t const *               account_key ) {
     132           0 :   for( ulong i=0UL; i<dumped_cnt; i++ ) {
     133           0 :     if( !memcmp( account_key, dumped_accounts[i].address, sizeof(fd_pubkey_t) ) ) {
     134           0 :       return 1;
     135           0 :     }
     136           0 :   }
     137           0 :   return 0;
     138           0 : }
     139             : 
     140             : /* Dumps a borrowed account if it exists and has not been dumped yet.
     141             :    Sets up the output borrowed account if it exists. Returns 0 if the
     142             :    account exists, 1 otherwise.
     143             :    TODO: This can be optimized by using a set. */
     144             : static uchar
     145             : dump_account_if_not_already_dumped( fd_funk_t const *           funk,
     146             :                                     fd_funk_txn_xid_t const *   xid,
     147             :                                     fd_pubkey_t const *         account_key,
     148             :                                     fd_spad_t *                 spad,
     149             :                                     fd_exec_test_acct_state_t * out_acct_states,
     150             :                                     pb_size_t *                 out_acct_states_cnt,
     151           0 :                                     fd_txn_account_t *          opt_out_borrowed_account ) {
     152           0 :   fd_txn_account_t account[1];
     153           0 :   if( fd_txn_account_init_from_funk_readonly( account, account_key, funk, xid ) ) {
     154           0 :     return 1;
     155           0 :   }
     156             : 
     157           0 :   if( !account_already_dumped( out_acct_states, *out_acct_states_cnt, account_key ) ) {
     158           0 :     dump_account_state( account, &out_acct_states[*out_acct_states_cnt], spad );
     159           0 :     (*out_acct_states_cnt)++;
     160           0 :   }
     161             : 
     162           0 :   if( opt_out_borrowed_account ) {
     163           0 :     *opt_out_borrowed_account = *account;
     164           0 :   }
     165           0 :   return 0;
     166           0 : }
     167             : 
     168             : static void
     169             : dump_executable_account_if_exists( fd_funk_t const *                 funk,
     170             :                                    fd_funk_txn_xid_t const *         xid,
     171             :                                    fd_exec_test_acct_state_t const * program_account,
     172             :                                    fd_spad_t *                       spad,
     173             :                                    fd_exec_test_acct_state_t *       out_account_states,
     174           0 :                                    pb_size_t *                       out_account_states_count ) {
     175           0 :   if( FD_LIKELY( memcmp( program_account->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     176           0 :     return;
     177           0 :   }
     178             : 
     179           0 :   int err;
     180           0 :   fd_bpf_upgradeable_loader_state_t * program_loader_state = fd_bincode_decode_spad(
     181           0 :       bpf_upgradeable_loader_state,
     182           0 :       spad,
     183           0 :       program_account->data->bytes,
     184           0 :       program_account->data->size,
     185           0 :       &err );
     186           0 :   if( FD_UNLIKELY( err ) ) return;
     187             : 
     188           0 :   if( !fd_bpf_upgradeable_loader_state_is_program( program_loader_state ) ) {
     189           0 :     return;
     190           0 :   }
     191             : 
     192           0 :   fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
     193           0 :   dump_account_if_not_already_dumped( funk, xid, programdata_acc, spad, out_account_states, out_account_states_count, NULL );
     194           0 : }
     195             : 
     196             : static void
     197             : dump_sanitized_transaction( fd_funk_t *                            funk,
     198             :                             fd_funk_txn_xid_t const *              xid,
     199             :                             fd_txn_t const *                       txn_descriptor,
     200             :                             uchar const *                          txn_payload,
     201             :                             fd_spad_t *                            spad,
     202           0 :                             fd_exec_test_sanitized_transaction_t * sanitized_transaction ) {
     203           0 :   fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     204             : 
     205             :   /* Transaction Context -> tx -> message */
     206           0 :   sanitized_transaction->has_message = true;
     207           0 :   fd_exec_test_transaction_message_t * message = &sanitized_transaction->message;
     208             : 
     209             :   /* Transaction Context -> tx -> message -> is_legacy */
     210           0 :   message->is_legacy = txn_descriptor->transaction_version == FD_TXN_VLEGACY;
     211             : 
     212             :   /* Transaction Context -> tx -> message -> header */
     213           0 :   message->has_header = true;
     214           0 :   fd_exec_test_message_header_t * header = &message->header;
     215             : 
     216             :   /* Transaction Context -> tx -> message -> header -> num_required_signatures */
     217           0 :   header->num_required_signatures = txn_descriptor->signature_cnt;
     218             : 
     219             :   /* Transaction Context -> tx -> message -> header -> num_readonly_signed_accounts */
     220           0 :   header->num_readonly_signed_accounts = txn_descriptor->readonly_signed_cnt;
     221             : 
     222             :   /* Transaction Context -> tx -> message -> header -> num_readonly_unsigned_accounts */
     223           0 :   header->num_readonly_unsigned_accounts = txn_descriptor->readonly_unsigned_cnt;
     224             : 
     225             :   /* Transaction Context -> tx -> message -> account_keys */
     226           0 :   message->account_keys_count = txn_descriptor->acct_addr_cnt;
     227           0 :   message->account_keys = fd_spad_alloc( spad, alignof(pb_bytes_array_t *), PB_BYTES_ARRAY_T_ALLOCSIZE(txn_descriptor->acct_addr_cnt * sizeof(pb_bytes_array_t *)) );
     228           0 :   fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_payload );
     229           0 :   for( ulong i = 0; i < txn_descriptor->acct_addr_cnt; i++ ) {
     230           0 :     pb_bytes_array_t * account_key = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_pubkey_t)) );
     231           0 :     account_key->size = sizeof(fd_pubkey_t);
     232           0 :     memcpy( account_key->bytes, &account_keys[i], sizeof(fd_pubkey_t) );
     233           0 :     message->account_keys[i] = account_key;
     234           0 :   }
     235             : 
     236             :   /* Transaction Context -> tx -> message -> recent_blockhash */
     237           0 :   uchar const * recent_blockhash = fd_txn_get_recent_blockhash( txn_descriptor, txn_payload );
     238           0 :   message->recent_blockhash = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_hash_t)) );
     239           0 :   message->recent_blockhash->size = sizeof(fd_hash_t);
     240           0 :   memcpy( message->recent_blockhash->bytes, recent_blockhash, sizeof(fd_hash_t) );
     241             : 
     242             :   /* Transaction Context -> tx -> message -> instructions */
     243           0 :   message->instructions_count = txn_descriptor->instr_cnt;
     244           0 :   message->instructions = fd_spad_alloc( spad, alignof(fd_exec_test_compiled_instruction_t), txn_descriptor->instr_cnt * sizeof(fd_exec_test_compiled_instruction_t) );
     245           0 :   for( ulong i = 0; i < txn_descriptor->instr_cnt; ++i ) {
     246           0 :     fd_txn_instr_t instr = txn_descriptor->instr[i];
     247           0 :     fd_exec_test_compiled_instruction_t * compiled_instruction = &message->instructions[i];
     248             : 
     249             :     // compiled instruction -> program_id_index
     250           0 :     compiled_instruction->program_id_index = instr.program_id;
     251             : 
     252             :     // compiled instruction -> accounts
     253           0 :     compiled_instruction->accounts_count = instr.acct_cnt;
     254           0 :     compiled_instruction->accounts = fd_spad_alloc( spad, alignof(uint32_t), instr.acct_cnt * sizeof(uint32_t) );
     255           0 :     uchar const * instr_accounts = fd_txn_get_instr_accts( &instr, txn_payload );
     256           0 :     for( ulong j = 0; j < instr.acct_cnt; ++j ) {
     257           0 :       uchar instr_acct_index = instr_accounts[j];
     258           0 :       compiled_instruction->accounts[j] = instr_acct_index;
     259           0 :     }
     260             : 
     261             :     // compiled instruction -> data
     262           0 :     uchar const * instr_data = fd_txn_get_instr_data( &instr, txn_payload );
     263           0 :     compiled_instruction->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(instr.data_sz) );
     264           0 :     compiled_instruction->data->size = instr.data_sz;
     265           0 :     memcpy( compiled_instruction->data->bytes, instr_data, instr.data_sz );
     266           0 :   }
     267             : 
     268             :   /* ALUT stuff (non-legacy) */
     269           0 :   message->address_table_lookups_count = 0;
     270           0 :   if( !message->is_legacy ) {
     271             :     /* Transaction Context -> tx -> message -> address_table_lookups */
     272           0 :     message->address_table_lookups_count = txn_descriptor->addr_table_lookup_cnt;
     273           0 :     message->address_table_lookups = fd_spad_alloc( spad,
     274           0 :                                                     alignof(fd_exec_test_message_address_table_lookup_t),
     275           0 :                                                     txn_descriptor->addr_table_lookup_cnt * sizeof(fd_exec_test_message_address_table_lookup_t) );
     276           0 :     for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
     277             :       // alut -> account_key
     278           0 :       fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + address_lookup_tables[i].addr_off);
     279           0 :       memcpy( message->address_table_lookups[i].account_key, alut_key, sizeof(fd_pubkey_t) );
     280             : 
     281             :       // Access ALUT account data to access its keys
     282           0 :       fd_txn_account_t addr_lut_rec[1];
     283           0 :       int err = fd_txn_account_init_from_funk_readonly( addr_lut_rec, alut_key, funk, xid );
     284           0 :       if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
     285           0 :         FD_LOG_ERR(( "addr lut not found" ));
     286           0 :       }
     287             : 
     288             :       // alut -> writable_indexes
     289           0 :       message->address_table_lookups[i].writable_indexes_count = address_lookup_tables[i].writable_cnt;
     290           0 :       message->address_table_lookups[i].writable_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].writable_cnt * sizeof(uint32_t) );
     291           0 :       uchar * writable_indexes = (uchar *) (txn_payload + address_lookup_tables[i].writable_off);
     292           0 :       for( ulong j = 0; j < address_lookup_tables[i].writable_cnt; ++j ) {
     293           0 :         message->address_table_lookups[i].writable_indexes[j] = writable_indexes[j];
     294           0 :       }
     295             : 
     296             :       // alut -> readonly_indexes
     297           0 :       message->address_table_lookups[i].readonly_indexes_count = address_lookup_tables[i].readonly_cnt;
     298           0 :       message->address_table_lookups[i].readonly_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].readonly_cnt * sizeof(uint32_t) );
     299           0 :       uchar * readonly_indexes = (uchar *) (txn_payload + address_lookup_tables[i].readonly_off);
     300           0 :       for( ulong j = 0; j < address_lookup_tables[i].readonly_cnt; ++j ) {
     301           0 :         message->address_table_lookups[i].readonly_indexes[j] = readonly_indexes[j];
     302           0 :       }
     303           0 :     }
     304           0 :   }
     305             : 
     306             :   /* Transaction Context -> tx -> message_hash */
     307             :   // Skip because it does not matter what's in here
     308             : 
     309             :   /* Transaction Context -> tx -> signatures */
     310           0 :   sanitized_transaction->signatures_count = txn_descriptor->signature_cnt;
     311           0 :   sanitized_transaction->signatures = fd_spad_alloc( spad, alignof(pb_bytes_array_t *), PB_BYTES_ARRAY_T_ALLOCSIZE(txn_descriptor->signature_cnt * sizeof(pb_bytes_array_t *)) );
     312           0 :   fd_ed25519_sig_t const * signatures = fd_txn_get_signatures( txn_descriptor, txn_payload );
     313           0 :   for( uchar i = 0; i < txn_descriptor->signature_cnt; ++i ) {
     314           0 :     pb_bytes_array_t * signature = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_ed25519_sig_t)) );
     315           0 :     signature->size = sizeof(fd_ed25519_sig_t);
     316           0 :     memcpy( signature->bytes, &signatures[i], sizeof(fd_ed25519_sig_t) );
     317           0 :     sanitized_transaction->signatures[i] = signature;
     318           0 :   }
     319           0 : }
     320             : 
     321             : /** BLOCKHASH QUEUE DUMPING **/
     322             : 
     323             : static void
     324             : dump_blockhash_queue( fd_blockhashes_t const * queue,
     325             :                       fd_spad_t *              spad,
     326             :                       pb_bytes_array_t **      output_blockhash_queue,
     327           0 :                       pb_size_t *              output_blockhash_queue_count ) {
     328           0 :   ulong bhq_size = fd_ulong_min( FD_BLOCKHASHES_MAX, fd_blockhash_deq_cnt( queue->d.deque ) );
     329             : 
     330             :   // Iterate over all block hashes in the queue and save them in the output
     331           0 :   pb_size_t cnt = 0U;
     332           0 :   for( fd_blockhash_deq_iter_t iter=fd_blockhash_deq_iter_init_rev( queue->d.deque );
     333           0 :        !fd_blockhash_deq_iter_done_rev( queue->d.deque, iter ) && cnt<FD_BLOCKHASHES_MAX;
     334           0 :        iter=fd_blockhash_deq_iter_prev( queue->d.deque, iter ), cnt++ ) {
     335           0 :     fd_blockhash_info_t const * ele              = fd_blockhash_deq_iter_ele_const( queue->d.deque, iter );
     336           0 :     pb_bytes_array_t *          output_blockhash = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_hash_t)) );
     337           0 :     output_blockhash->size = sizeof(fd_hash_t);
     338           0 :     fd_memcpy( output_blockhash->bytes, &ele->hash, sizeof(fd_hash_t) );
     339           0 :     output_blockhash_queue[ bhq_size-cnt-1UL ] = output_blockhash;
     340           0 :   }
     341             : 
     342           0 :   *output_blockhash_queue_count = cnt;
     343           0 : }
     344             : 
     345             : /** SECONDARY FUNCTIONS **/
     346             : 
     347             : /* add_account_to_dumped_accounts adds an account to the dumped accounts
     348             :    set if it does not exist already. Returns 0 if the account already
     349             :    exists, and 1 if the account was added successfully.
     350             : 
     351             :    TODO: Txn dumping should be optimized to use these functions. */
     352             : static uchar
     353             : add_account_to_dumped_accounts( fd_dump_account_key_node_t *  pool,
     354             :                                 fd_dump_account_key_node_t ** root,
     355           0 :                                 fd_pubkey_t const *           pubkey ) {
     356             :   /* If the key already exists, return early. */
     357           0 :   fd_dump_account_key_node_t node = {
     358           0 :     .key = *pubkey,
     359           0 :   };
     360           0 :   if( fd_dump_account_key_map_find( pool, *root, &node ) ) {
     361           0 :     return 0;
     362           0 :   }
     363             : 
     364           0 :   fd_dump_account_key_node_t * new_node = fd_dump_account_key_map_acquire( pool );
     365           0 :   new_node->key = *pubkey;
     366           0 :   fd_dump_account_key_map_insert( pool, root, new_node );
     367           0 :   return 1;
     368           0 : }
     369             : 
     370             : /* add_account_and_programdata_to_dumped_accounts adds an account and
     371             :    its programdata account (if the account is a v3 program) to the
     372             :    dumped accounts set if they do not exist already. */
     373             : static void
     374             : add_account_and_programdata_to_dumped_accounts( fd_funk_t *                   funk,
     375             :                                                 fd_funk_txn_xid_t const *     xid,
     376             :                                                 fd_dump_account_key_node_t *  pool,
     377             :                                                 fd_dump_account_key_node_t ** root,
     378           0 :                                                 fd_pubkey_t const *           pubkey ) {
     379             :   /* Add the current account to the dumped accounts set. We can save
     380             :      some time by enforcing an invariant that "if current account was
     381             :      dumped, then programdata account was also dumped," so we save
     382             :      ourselves a call to Funk. */
     383           0 :   uchar ret = add_account_to_dumped_accounts( pool, root, pubkey );
     384           0 :   if( ret==0 ) return;
     385             : 
     386             :   /* Read the account from Funk to see if its a program account and if
     387             :      it needs to be dumped. */
     388           0 :   fd_txn_account_t program_account[1];
     389           0 :   int err = fd_txn_account_init_from_funk_readonly( program_account, pubkey, funk, xid );
     390           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     391           0 :     return;
     392           0 :   }
     393             : 
     394             :   /* Return if its not owned by the v3 loader */
     395           0 :   if( FD_LIKELY( memcmp( fd_txn_account_get_owner( program_account ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     396           0 :     return;
     397           0 :   }
     398             : 
     399             :   /* Get the program account state */
     400           0 :   fd_bpf_upgradeable_loader_state_t program_account_state[1];
     401           0 :   if( FD_UNLIKELY( !fd_bincode_decode_static(
     402           0 :       bpf_upgradeable_loader_state,
     403           0 :       program_account_state,
     404           0 :       fd_txn_account_get_data( program_account ),
     405           0 :       fd_txn_account_get_data_len( program_account ),
     406           0 :       NULL ) ) ) {
     407           0 :     return;
     408           0 :   }
     409           0 :   if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) {
     410           0 :     return;
     411           0 :   }
     412             : 
     413             :   /* Dump the programdata address */
     414           0 :   add_account_to_dumped_accounts( pool, root, &program_account_state->inner.program.programdata_address );
     415           0 : }
     416             : 
     417             : /* add_lut_account_to_dumped_accounts adds an address lookup table
     418             :    account AND all pubkeys in the lookup table to the dumped accounts
     419             :    set if they do not exist already. */
     420             : static void
     421             : add_lut_accounts_to_dumped_accounts( fd_funk_t *                   funk,
     422             :                                      fd_funk_txn_xid_t const *     xid,
     423             :                                      fd_dump_account_key_node_t *  pool,
     424             :                                      fd_dump_account_key_node_t ** root,
     425           0 :                                      fd_pubkey_t const *           pubkey ) {
     426             :   /* Add the current account to the dumped accounts set. */
     427           0 :   add_account_to_dumped_accounts( pool, root, pubkey );
     428             : 
     429             :   /* Read the account and dump all pubkeys within the lookup table. */
     430           0 :   fd_txn_account_t lut_account[1];
     431           0 :   int err = fd_txn_account_init_from_funk_readonly( lut_account, pubkey, funk, xid );
     432           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     433           0 :     return;
     434           0 :   }
     435             : 
     436           0 :   uchar const  * data     = fd_txn_account_get_data( lut_account );
     437           0 :   ulong          data_len = fd_txn_account_get_data_len( lut_account );
     438             : 
     439             :   /* Decode the ALUT account and dump all pubkeys within the lookup
     440             :      table. */
     441           0 :   if( data_len<FD_LOOKUP_TABLE_META_SIZE || (data_len&0x1fUL) ) {
     442           0 :     return;
     443           0 :   }
     444           0 :   fd_pubkey_t const * lookup_addrs     = fd_type_pun_const( data+FD_LOOKUP_TABLE_META_SIZE );
     445           0 :   ulong               lookup_addrs_cnt = ( data_len-FD_LOOKUP_TABLE_META_SIZE)>>5UL; // = (dlen - 56) / 32
     446           0 :   for( ulong i=0UL; i<lookup_addrs_cnt; i++ ) {
     447           0 :     fd_pubkey_t const * referenced_pubkey = &lookup_addrs[i];
     448           0 :     add_account_and_programdata_to_dumped_accounts( funk, xid, pool, root, referenced_pubkey );
     449           0 :   }
     450           0 : }
     451             : 
     452             : /* create_synthetic_vote_account_from_vote_state creates a synthetic
     453             :    vote account from a vote state cache element. It fills in default
     454             :    values for unspecified fields and encodes the vote state into
     455             :    out_vote_account's data field. */
     456             : static void
     457             : create_synthetic_vote_account_from_vote_state( fd_vote_state_ele_t const *   vote_state,
     458             :                                                fd_spad_t *                   spad,
     459           0 :                                                fd_exec_test_vote_account_t * out_vote_account ) {
     460           0 :   out_vote_account->has_vote_account = true;
     461           0 :   fd_memcpy( out_vote_account->vote_account.address, &vote_state->vote_account, sizeof(fd_pubkey_t) );
     462           0 :   out_vote_account->vote_account.executable = false;
     463           0 :   out_vote_account->vote_account.lamports = 100000UL;
     464           0 :   fd_memcpy( out_vote_account->vote_account.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) );
     465           0 :   out_vote_account->stake = vote_state->stake;
     466             : 
     467             :   /* Construct the vote account data. Fill in missing fields with
     468             :      arbitrary defaults (since they're not used anyways) */
     469           0 :   fd_vote_state_versioned_t vsv = {
     470           0 :     .discriminant = fd_vote_state_versioned_enum_current,
     471           0 :     .inner = {
     472           0 :       .current = {
     473           0 :         .node_pubkey           = vote_state->node_account,
     474           0 :         .authorized_withdrawer = vote_state->node_account,
     475           0 :         .commission            = vote_state->commission,
     476           0 :         .root_slot             = 0UL,
     477           0 :         .has_root_slot         = 0,
     478           0 :         .last_timestamp        = {
     479           0 :           .timestamp           = vote_state->last_vote_timestamp,
     480           0 :           .slot                = vote_state->last_vote_slot,
     481           0 :         },
     482           0 :       }
     483           0 :     }
     484           0 :   };
     485           0 :   fd_vote_state_t * synthetic_vote_state = &vsv.inner.current;
     486             : 
     487             :   /* Create synthetic landed votes */
     488           0 :   synthetic_vote_state->votes = deq_fd_landed_vote_t_join(
     489           0 :       deq_fd_landed_vote_t_new(
     490           0 :           fd_spad_alloc(
     491           0 :               spad,
     492           0 :               deq_fd_landed_vote_t_align(),
     493           0 :               deq_fd_landed_vote_t_footprint( 32UL ) ),
     494           0 :           32UL ) );
     495           0 :   for( ulong i=0UL; i<32UL; i++ ) {
     496           0 :     fd_landed_vote_t elem = {0};
     497           0 :     deq_fd_landed_vote_t_push_tail( synthetic_vote_state->votes, elem );
     498           0 :   }
     499             : 
     500             :   /* Populate authoritzed voters */
     501           0 :   void * authorized_voters_pool_mem  = fd_spad_alloc(
     502           0 :       spad,
     503           0 :       fd_vote_authorized_voters_pool_align(),
     504           0 :       fd_vote_authorized_voters_pool_footprint( 5UL ) );
     505           0 :   void * authorized_voters_treap_mem = fd_spad_alloc(
     506           0 :       spad,
     507           0 :       fd_vote_authorized_voters_treap_align(),
     508           0 :       fd_vote_authorized_voters_treap_footprint( 5UL ) );
     509           0 :   synthetic_vote_state->authorized_voters.pool  = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( authorized_voters_pool_mem, 5UL ) );
     510           0 :   synthetic_vote_state->authorized_voters.treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( authorized_voters_treap_mem, 5UL ) );
     511             : 
     512             :   /* Encode the synthetic vote state */
     513           0 :   ulong encoded_sz                          = fd_vote_state_versioned_size( &vsv );
     514           0 :   out_vote_account->vote_account.data       = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( encoded_sz ) );
     515           0 :   out_vote_account->vote_account.data->size = (pb_size_t)encoded_sz;
     516             : 
     517           0 :   fd_bincode_encode_ctx_t encode_ctx = {
     518           0 :     .data    = out_vote_account->vote_account.data->bytes,
     519           0 :     .dataend = out_vote_account->vote_account.data->bytes+encoded_sz,
     520           0 :   };
     521           0 :   fd_vote_state_versioned_encode( &vsv, &encode_ctx );
     522           0 : }
     523             : 
     524             : static void
     525             : dump_prior_vote_accounts( fd_vote_states_t const *      vote_states,
     526             :                           fd_dump_account_key_node_t *  dumped_accounts_pool,
     527             :                           fd_dump_account_key_node_t ** dumped_accounts_root,
     528             :                           fd_exec_test_vote_account_t * out_vote_accounts,
     529             :                           pb_size_t *                   out_vote_accounts_count,
     530           0 :                           fd_spad_t *                   spad ) {
     531             : 
     532           0 :   fd_vote_states_iter_t iter_[1];
     533           0 :   for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( iter_, vote_states );
     534           0 :                                      !fd_vote_states_iter_done( iter );
     535           0 :                                       fd_vote_states_iter_next( iter ) ) {
     536           0 :     fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter );
     537           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, dumped_accounts_root, &vote_state->vote_account );
     538             : 
     539           0 :     create_synthetic_vote_account_from_vote_state(
     540           0 :         vote_state,
     541           0 :         spad,
     542           0 :         &out_vote_accounts[(*out_vote_accounts_count)++] );
     543           0 :   }
     544           0 : }
     545             : 
     546             : static void
     547             : create_block_context_protobuf_from_block( fd_block_dump_ctx_t * dump_ctx,
     548             :                                           fd_banks_t *          banks,
     549             :                                           fd_bank_t *           bank,
     550           0 :                                           fd_funk_t *           funk ) {
     551             :   /* We should use the bank fields and funk txn from the parent slot in
     552             :      order to capture the block context from before the current block
     553             :      was executed, since dumping is happening in the block finalize
     554             :      step. */
     555           0 :   fd_bank_t *                    parent_bank    = fd_banks_get_parent( banks, bank );
     556           0 :   ulong                          current_slot   = fd_bank_slot_get( bank );
     557           0 :   ulong                          parent_slot    = fd_bank_slot_get( parent_bank );
     558           0 :   fd_funk_txn_xid_t              parent_xid     = { .ul = { parent_slot, parent_bank->idx } };
     559           0 :   fd_exec_test_block_context_t * block_context  = &dump_ctx->block_context;
     560           0 :   ulong                          dump_txn_count = dump_ctx->txns_to_dump_cnt;
     561           0 :   fd_spad_t *                    spad           = dump_ctx->spad;
     562             : 
     563             :   /* Get vote and stake delegation infos */
     564           0 :   fd_vote_states_t const * vote_states        = fd_bank_vote_states_locking_query( parent_bank );
     565           0 :   ulong                    vote_account_t_cnt = fd_vote_states_cnt( vote_states );
     566           0 :   fd_bank_vote_states_end_locking_query( parent_bank );
     567             : 
     568           0 :   fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_frontier_query( banks, parent_bank );
     569           0 :   ulong                          stake_account_cnt = fd_stake_delegations_cnt( stake_delegations );
     570             : 
     571             :   /* Collect account states in a temporary set before iterating over
     572             :      them and dumping them out. */
     573           0 :   ulong                        total_num_accounts   = num_sysvar_entries +  /* Sysvars */
     574           0 :                                                       num_loaded_builtins + /* Builtins */
     575           0 :                                                       stake_account_cnt +   /* Stake accounts */
     576           0 :                                                       vote_account_t_cnt +  /* Current vote accounts */
     577           0 :                                                       dump_txn_count*128UL; /* Txn accounts upper bound */
     578           0 :   void *                       dumped_accounts_mem  = fd_spad_alloc( spad, fd_dump_account_key_map_align(), fd_dump_account_key_map_footprint( total_num_accounts ) );
     579           0 :   fd_dump_account_key_node_t * dumped_accounts_pool = fd_dump_account_key_map_join( fd_dump_account_key_map_new( dumped_accounts_mem, total_num_accounts ) );
     580           0 :   fd_dump_account_key_node_t * dumped_accounts_root = NULL;
     581             : 
     582             :   /* BlockContext -> txns */
     583           0 :   block_context->txns_count = (pb_size_t)dump_txn_count;
     584           0 :   block_context->txns       = fd_spad_alloc( spad, alignof(fd_exec_test_sanitized_transaction_t), dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
     585           0 :   fd_memset( block_context->txns, 0, dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
     586             : 
     587             :   /* Dump sanitized transactions from the transaction descriptors */
     588           0 :   for( ulong i=0UL; i<dump_ctx->txns_to_dump_cnt; i++ ) {
     589           0 :     fd_txn_p_t const * txn_ptr        = &dump_ctx->txns_to_dump[i];
     590           0 :     fd_txn_t const *   txn_descriptor = TXN( txn_ptr );
     591           0 :     dump_sanitized_transaction( funk, &parent_xid, txn_descriptor, txn_ptr->payload, spad, &block_context->txns[i] );
     592             : 
     593             :     /* Dump account + alut + programdata accounts (if applicable).
     594             :        1. Dump the raw txn account keys
     595             :        2. Dump the ALUT accounts
     596             :        3. Dump all referenced accounts in the ALUTs
     597             :        4. Dump any executable accounts */
     598             : 
     599             :     // 1 + 4. Dump any account keys that are referenced by transactions
     600             :     // + any programdata accounts (if applicable).
     601           0 :     fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload );
     602           0 :     for( ushort l=0; l<txn_descriptor->acct_addr_cnt; l++ ) {
     603           0 :       fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] );
     604           0 :       add_account_and_programdata_to_dumped_accounts( funk, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, account_key );
     605           0 :     }
     606             : 
     607             :     // 2 + 3 + 4. Dump any ALUT accounts + any accounts referenced in
     608             :     // the ALUTs + any programdata accounts (if applicable).
     609           0 :     fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     610           0 :     for( ushort l=0; l<txn_descriptor->addr_table_lookup_cnt; l++ ) {
     611           0 :       fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l];
     612           0 :       fd_pubkey_t const *            lut_key      = fd_type_pun_const( txn_ptr->payload+lookup_table->addr_off );
     613           0 :       add_lut_accounts_to_dumped_accounts( funk, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, lut_key );
     614           0 :     }
     615           0 :   }
     616             : 
     617             :   /* Dump sysvars */
     618           0 :   for( ulong i=0UL; i<num_sysvar_entries; i++ ) {
     619           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, fd_dump_sysvar_ids[i] );
     620           0 :   }
     621             : 
     622             :   /* Dump builtins */
     623           0 :   for( ulong i=0UL; i<num_loaded_builtins; i++ ) {
     624           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, fd_dump_builtin_ids[i] );
     625           0 :   }
     626             : 
     627             :   /* Dump stake accounts for this epoch */
     628           0 :   fd_stake_delegations_iter_t iter_[1];
     629           0 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
     630           0 :        !fd_stake_delegations_iter_done( iter );
     631           0 :        fd_stake_delegations_iter_next( iter ) ) {
     632           0 :     fd_stake_delegation_t * stake_delegation = fd_stake_delegations_iter_ele( iter );
     633           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &stake_delegation->stake_account );
     634           0 :   }
     635             : 
     636             :   /* Dump vote accounts for this epoch */
     637           0 :   vote_states = fd_bank_vote_states_locking_query( parent_bank );
     638           0 :   fd_vote_states_iter_t vote_iter_[1];
     639           0 :   for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( vote_iter_, vote_states ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) {
     640           0 :     fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter );
     641           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &vote_state->vote_account );
     642           0 :   }
     643           0 :   fd_bank_vote_states_end_locking_query( parent_bank );
     644             : 
     645             :   // BlockContext -> EpochContext -> vote_accounts_t_1 (vote accounts at epoch T-1)
     646           0 :   fd_vote_states_t const * vote_states_prev        = fd_bank_vote_states_prev_locking_query( parent_bank );
     647           0 :   block_context->epoch_ctx.vote_accounts_t_1       = fd_spad_alloc(
     648           0 :       spad,
     649           0 :       alignof(fd_exec_test_vote_account_t),
     650           0 :       sizeof(fd_exec_test_vote_account_t)*fd_vote_states_cnt( vote_states_prev ) );
     651           0 :   block_context->epoch_ctx.vote_accounts_t_1_count = 0U;
     652           0 :   dump_prior_vote_accounts(
     653           0 :       vote_states_prev,
     654           0 :       dumped_accounts_pool,
     655           0 :       &dumped_accounts_root,
     656           0 :       block_context->epoch_ctx.vote_accounts_t_1,
     657           0 :       &block_context->epoch_ctx.vote_accounts_t_1_count,
     658           0 :       spad );
     659           0 :   fd_bank_vote_states_prev_end_locking_query( parent_bank );
     660             : 
     661             :   // BlockContext -> EpochContext -> vote_accounts_t_2 (vote accounts at epoch T-2)
     662           0 :   fd_vote_states_t const * vote_states_prev_prev   = fd_bank_vote_states_prev_prev_locking_query( parent_bank );
     663           0 :   block_context->epoch_ctx.vote_accounts_t_2       = fd_spad_alloc(
     664           0 :       spad,
     665           0 :       alignof(fd_exec_test_vote_account_t),
     666           0 :       sizeof(fd_exec_test_vote_account_t)*fd_vote_states_cnt( vote_states_prev_prev ) );
     667           0 :   block_context->epoch_ctx.vote_accounts_t_2_count = 0U;
     668           0 :   dump_prior_vote_accounts(
     669           0 :       vote_states_prev_prev,
     670           0 :       dumped_accounts_pool,
     671           0 :       &dumped_accounts_root,
     672           0 :       block_context->epoch_ctx.vote_accounts_t_2,
     673           0 :       &block_context->epoch_ctx.vote_accounts_t_2_count,
     674           0 :       spad );
     675           0 :   fd_bank_vote_states_prev_prev_end_locking_query( parent_bank );
     676             : 
     677             :   /* BlockContext -> acct_states
     678             :      Iterate over the set and dump all the account keys in one pass. */
     679           0 :   block_context->acct_states_count = 0U;
     680           0 :   block_context->acct_states       = fd_spad_alloc(
     681           0 :       spad,
     682           0 :       alignof(fd_exec_test_acct_state_t),
     683           0 :       fd_dump_account_key_map_size( dumped_accounts_pool, dumped_accounts_root )*sizeof(fd_exec_test_acct_state_t) );
     684           0 :   for( fd_dump_account_key_node_t * node = fd_dump_account_key_map_minimum( dumped_accounts_pool, dumped_accounts_root );
     685           0 :                                     node;
     686           0 :                                     node = fd_dump_account_key_map_successor( dumped_accounts_pool, node ) ) {
     687           0 :     fd_txn_account_t txn_account[1];
     688           0 :     int ret = fd_txn_account_init_from_funk_readonly( txn_account, &node->key, funk, &parent_xid );
     689           0 :     if( FD_UNLIKELY( ret ) ) {
     690           0 :       continue;
     691           0 :     }
     692           0 :     dump_account_state( txn_account, &block_context->acct_states[block_context->acct_states_count++], spad );
     693           0 :   }
     694             : 
     695             :   /* BlockContext -> blockhash_queue */
     696           0 :   fd_blockhashes_t const * bhq   = fd_bank_block_hash_queue_query( parent_bank );
     697           0 :   block_context->blockhash_queue = fd_spad_alloc(
     698           0 :       spad,
     699           0 :       alignof(pb_bytes_array_t *),
     700           0 :       PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) );
     701           0 :   block_context->blockhash_queue_count = 0U;
     702           0 :   dump_blockhash_queue( bhq, spad, block_context->blockhash_queue, &block_context->blockhash_queue_count );
     703             : 
     704             :   /* BlockContext -> SlotContext */
     705           0 :   block_context->has_slot_ctx                       = true;
     706           0 :   block_context->slot_ctx.slot                      = current_slot;
     707           0 :   block_context->slot_ctx.block_height              = fd_bank_block_height_get( bank );
     708           0 :   block_context->slot_ctx.prev_slot                 = fd_bank_parent_slot_get( bank );
     709             : 
     710             :   // We need to store the POH hash for the current block since we don't
     711             :   // recalculate it in the harnesses.
     712           0 :   fd_memcpy( block_context->slot_ctx.poh, fd_bank_poh_query( bank ), sizeof(fd_pubkey_t) );
     713           0 :   fd_memcpy( block_context->slot_ctx.parent_bank_hash, fd_bank_bank_hash_query( parent_bank ), sizeof(fd_pubkey_t) );
     714             : 
     715           0 :   fd_lthash_value_t const * parent_lthash = fd_bank_lthash_locking_query( parent_bank );
     716           0 :   fd_memcpy( block_context->slot_ctx.parent_lthash, parent_lthash, sizeof(fd_lthash_value_t) );
     717           0 :   fd_bank_lthash_end_locking_query( parent_bank );
     718             : 
     719           0 :   block_context->slot_ctx.prev_lps                  = fd_bank_prev_lamports_per_signature_get( parent_bank );
     720           0 :   block_context->slot_ctx.prev_epoch_capitalization = fd_bank_capitalization_get( parent_bank );
     721             : 
     722             :   /* BlockContext -> SlotContext -> fee_rate_governor */
     723           0 :   fd_fee_rate_governor_t const * fee_rate_governor = fd_bank_fee_rate_governor_query( parent_bank );
     724           0 :   block_context->slot_ctx.has_fee_rate_governor     = true;
     725           0 :   block_context->slot_ctx.fee_rate_governor         = (fd_exec_test_fee_rate_governor_t){
     726           0 :       .target_lamports_per_signature = fee_rate_governor->target_lamports_per_signature,
     727           0 :       .target_signatures_per_slot    = fee_rate_governor->target_signatures_per_slot,
     728           0 :       .min_lamports_per_signature    = fee_rate_governor->min_lamports_per_signature,
     729           0 :       .max_lamports_per_signature    = fee_rate_governor->max_lamports_per_signature,
     730           0 :       .burn_percent                  = fee_rate_governor->burn_percent,
     731           0 :   };
     732             : 
     733             :   /* BlockContext -> EpochContext */
     734           0 :   block_context->has_epoch_ctx                        = true;
     735           0 :   block_context->epoch_ctx.has_features               = true;
     736           0 :   dump_sorted_features( fd_bank_features_query( parent_bank ), &block_context->epoch_ctx.features, spad );
     737           0 :   block_context->epoch_ctx.hashes_per_tick            = fd_bank_hashes_per_tick_get( parent_bank );
     738           0 :   block_context->epoch_ctx.ticks_per_slot             = fd_bank_ticks_per_slot_get( parent_bank );
     739           0 :   block_context->epoch_ctx.slots_per_year             = fd_bank_slots_per_year_get( parent_bank );
     740           0 :   block_context->epoch_ctx.has_inflation              = true;
     741             : 
     742           0 :   fd_inflation_t const * inflation = fd_bank_inflation_query( parent_bank );
     743           0 :   block_context->epoch_ctx.inflation                  = (fd_exec_test_inflation_t) {
     744           0 :       .initial         = inflation->initial,
     745           0 :       .terminal        = inflation->terminal,
     746           0 :       .taper           = inflation->taper,
     747           0 :       .foundation      = inflation->foundation,
     748           0 :       .foundation_term = inflation->foundation_term,
     749           0 :   };
     750           0 :   block_context->epoch_ctx.genesis_creation_time      = fd_bank_genesis_creation_time_get( parent_bank );
     751           0 : }
     752             : 
     753             : static void
     754             : create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_msg,
     755             :                                       fd_exec_txn_ctx_t *          txn_ctx,
     756           0 :                                       fd_spad_t *                  spad ) {
     757           0 :   fd_txn_t const * txn_descriptor = TXN( &txn_ctx->txn );
     758           0 :   uchar const *    txn_payload    = (uchar const *) txn_ctx->txn.payload;
     759             : 
     760             :   /* Transaction Context -> account_shared_data
     761             :      Contains:
     762             :       - Account data for regular accounts
     763             :       - Account data for LUT accounts
     764             :       - Account data for executable accounts
     765             :       - Account data for (almost) all sysvars
     766             : 
     767             :     We also don't want to store builtins in account shared data due to
     768             :     how Agave's bank handles them in the init phase. */
     769             :   // Dump regular accounts first
     770           0 :   txn_context_msg->account_shared_data_count = 0;
     771           0 :   txn_context_msg->account_shared_data = fd_spad_alloc( spad,
     772           0 :                                                         alignof(fd_exec_test_acct_state_t),
     773           0 :                                                         (256UL*2UL + txn_descriptor->addr_table_lookup_cnt + num_sysvar_entries) * sizeof(fd_exec_test_acct_state_t) );
     774           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; ++i ) {
     775           0 :     fd_txn_account_t txn_account[1];
     776           0 :     int ret = fd_txn_account_init_from_funk_readonly( txn_account, &txn_ctx->account_keys[i], txn_ctx->funk, txn_ctx->xid );
     777           0 :     if( FD_UNLIKELY( ret ) ) {
     778           0 :       continue;
     779           0 :     }
     780             : 
     781             :     // Make sure account is not a non-migrating builtin
     782           0 :     if( !is_builtin_account( &txn_ctx->account_keys[i] ) ) {
     783           0 :       dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     784           0 :     }
     785           0 :   }
     786             : 
     787             :   // Dump LUT accounts
     788           0 :   fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     789           0 :   for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
     790           0 :     fd_txn_account_t txn_account[1];
     791           0 :     fd_txn_acct_addr_lut_t const * addr_lut  = &address_lookup_tables[i];
     792           0 :     fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + addr_lut->addr_off);
     793           0 :     int ret = fd_txn_account_init_from_funk_readonly( txn_account, alut_key, txn_ctx->funk, txn_ctx->xid );
     794           0 :     if( FD_UNLIKELY( ret ) ) continue;
     795             : 
     796           0 :     dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     797             : 
     798           0 :     fd_acct_addr_t * lookup_addrs  = (fd_acct_addr_t *)&fd_txn_account_get_data( txn_account )[FD_LOOKUP_TABLE_META_SIZE];
     799           0 :     ulong lookup_addrs_cnt         = (fd_txn_account_get_data_len( txn_account ) - FD_LOOKUP_TABLE_META_SIZE) >> 5UL; // = (dlen - 56) / 32
     800             : 
     801             :     /* Dump any account state refererenced in ALUTs */
     802           0 :     uchar const * writable_lut_idxs = txn_payload + addr_lut->writable_off;
     803           0 :     for( ulong j=0; j<addr_lut->writable_cnt; j++ ) {
     804           0 :       if( writable_lut_idxs[j] >= lookup_addrs_cnt ) {
     805           0 :         continue;
     806           0 :       }
     807           0 :       fd_pubkey_t const * referenced_addr = fd_type_pun( &lookup_addrs[writable_lut_idxs[j]] );
     808           0 :       if( is_builtin_account( referenced_addr ) ) continue;
     809             : 
     810           0 :       fd_txn_account_t referenced_account[1];
     811           0 :       ret = fd_txn_account_init_from_funk_readonly( referenced_account, referenced_addr, txn_ctx->funk, txn_ctx->xid );
     812           0 :       if( FD_UNLIKELY( ret ) ) continue;
     813           0 :       dump_account_state( referenced_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     814           0 :     }
     815             : 
     816           0 :     uchar const * readonly_lut_idxs = txn_payload + addr_lut->readonly_off;
     817           0 :     for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
     818           0 :       if( readonly_lut_idxs[j] >= lookup_addrs_cnt ) {
     819           0 :         continue;
     820           0 :       }
     821           0 :       fd_pubkey_t const * referenced_addr = fd_type_pun( &lookup_addrs[readonly_lut_idxs[j]] );
     822           0 :       if( is_builtin_account( referenced_addr ) ) continue;
     823             : 
     824           0 :       fd_txn_account_t referenced_account[1];
     825           0 :       ret = fd_txn_account_init_from_funk_readonly( referenced_account, referenced_addr, txn_ctx->funk, txn_ctx->xid );
     826           0 :       if( FD_UNLIKELY( ret ) ) continue;
     827           0 :       dump_account_state( referenced_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     828           0 :     }
     829           0 :   }
     830             : 
     831             :   /* Dump the programdata accounts for any potential v3-owned program accounts */
     832           0 :   uint accounts_dumped_so_far = txn_context_msg->account_shared_data_count;
     833           0 :   for( uint i=0U; i<accounts_dumped_so_far; i++ ) {
     834           0 :     fd_exec_test_acct_state_t const * maybe_program_account = &txn_context_msg->account_shared_data[i];
     835           0 :     dump_executable_account_if_exists( txn_ctx->funk, txn_ctx->xid, maybe_program_account, spad, txn_context_msg->account_shared_data, &txn_context_msg->account_shared_data_count );
     836           0 :   }
     837             : 
     838             :   /* Dump sysvars */
     839           0 :   for( ulong i = 0; i < num_sysvar_entries; i++ ) {
     840           0 :     fd_txn_account_t txn_account[1];
     841           0 :     int ret = fd_txn_account_init_from_funk_readonly( txn_account, fd_dump_sysvar_ids[i], txn_ctx->funk, txn_ctx->xid );
     842           0 :     if( ret != FD_ACC_MGR_SUCCESS ) {
     843           0 :       continue;
     844           0 :     }
     845             : 
     846             :     // Make sure the account doesn't exist in the output accounts yet
     847           0 :     int account_exists = 0;
     848           0 :     for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) {
     849           0 :       if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_dump_sysvar_ids[i], sizeof(fd_pubkey_t) ) ) {
     850           0 :         account_exists = true;
     851           0 :         break;
     852           0 :       }
     853           0 :     }
     854             :     // Copy it into output
     855           0 :     if (!account_exists) {
     856           0 :       dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     857           0 :     }
     858           0 :   }
     859             : 
     860             :   /* Transaction Context -> tx */
     861           0 :   txn_context_msg->has_tx = true;
     862           0 :   fd_exec_test_sanitized_transaction_t * sanitized_transaction = &txn_context_msg->tx;
     863           0 :   dump_sanitized_transaction( txn_ctx->funk, txn_ctx->xid, txn_descriptor, txn_payload, spad, sanitized_transaction );
     864             : 
     865             :   /* Transaction Context -> blockhash_queue
     866             :      NOTE: Agave's implementation of register_hash incorrectly allows the blockhash queue to hold max_age + 1 (max 301)
     867             :      entries. We have this incorrect logic implemented in fd_sysvar_recent_hashes:register_blockhash and it's not a
     868             :      huge issue, but something to keep in mind. */
     869           0 :   pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc(
     870           0 :                                                       spad,
     871           0 :                                                       alignof(pb_bytes_array_t *),
     872           0 :                                                       PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) );
     873           0 :   txn_context_msg->blockhash_queue = output_blockhash_queue;
     874           0 :   fd_blockhashes_t const * block_hash_queue = fd_bank_block_hash_queue_query( txn_ctx->bank );
     875           0 :   dump_blockhash_queue( block_hash_queue, spad, output_blockhash_queue, &txn_context_msg->blockhash_queue_count );
     876             : 
     877             :   /* Transaction Context -> epoch_ctx */
     878           0 :   txn_context_msg->has_epoch_ctx = true;
     879           0 :   txn_context_msg->epoch_ctx.has_features = true;
     880           0 :   dump_sorted_features( &txn_ctx->features, &txn_context_msg->epoch_ctx.features, spad );
     881             : 
     882             :   /* Transaction Context -> slot_ctx */
     883           0 :   txn_context_msg->has_slot_ctx  = true;
     884           0 :   txn_context_msg->slot_ctx.slot = txn_ctx->slot;
     885           0 : }
     886             : 
     887             : static void
     888             : create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * instr_context,
     889             :                                                  fd_exec_txn_ctx_t const *      txn_ctx,
     890             :                                                  fd_instr_info_t const *        instr,
     891           0 :                                                  fd_spad_t *                    spad ) {
     892             :   /* Program ID */
     893           0 :   fd_memcpy( instr_context->program_id, txn_ctx->account_keys[ instr->program_id ].uc, sizeof(fd_pubkey_t) );
     894             : 
     895             :   /* Accounts */
     896           0 :   instr_context->accounts_count = (pb_size_t) txn_ctx->accounts_cnt;
     897           0 :   instr_context->accounts = fd_spad_alloc( spad, alignof(fd_exec_test_acct_state_t), (instr_context->accounts_count + num_sysvar_entries + txn_ctx->executable_cnt) * sizeof(fd_exec_test_acct_state_t));
     898           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
     899             :     // Copy account information over
     900           0 :     fd_txn_account_t const *    txn_account    = &txn_ctx->accounts[i];
     901           0 :     fd_exec_test_acct_state_t * output_account = &instr_context->accounts[i];
     902           0 :     dump_account_state( txn_account, output_account, spad );
     903           0 :   }
     904             : 
     905             :   /* Add sysvar cache variables */
     906           0 :   for( ulong i = 0; i < num_sysvar_entries; i++ ) {
     907           0 :     fd_txn_account_t txn_account[1];
     908           0 :     int ret = fd_txn_account_init_from_funk_readonly( txn_account, fd_dump_sysvar_ids[i], txn_ctx->funk, txn_ctx->xid );
     909           0 :     if( ret != FD_ACC_MGR_SUCCESS ) {
     910           0 :       continue;
     911           0 :     }
     912             :     // Make sure the account doesn't exist in the output accounts yet
     913           0 :     int account_exists = 0;
     914           0 :     for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) {
     915           0 :       if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_dump_sysvar_ids[i], sizeof(fd_pubkey_t) ) ) {
     916           0 :         account_exists = true;
     917           0 :         break;
     918           0 :       }
     919           0 :     }
     920             : 
     921             :     // Copy it into output
     922           0 :     if (!account_exists) {
     923           0 :       fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
     924           0 :       dump_account_state( txn_account, output_account, spad );
     925           0 :     }
     926           0 :   }
     927             : 
     928             :   /* Add executable accounts */
     929           0 :   for( ulong i = 0; i < txn_ctx->executable_cnt; i++ ) {
     930           0 :     fd_txn_account_t txn_account[1];
     931           0 :     int ret = fd_txn_account_init_from_funk_readonly( txn_account, txn_ctx->executable_accounts[i].pubkey, txn_ctx->funk, txn_ctx->xid );
     932           0 :     if( ret != FD_ACC_MGR_SUCCESS ) {
     933           0 :       continue;
     934           0 :     }
     935             :     // Make sure the account doesn't exist in the output accounts yet
     936           0 :     bool account_exists = false;
     937           0 :     for( ulong j = 0; j < instr_context->accounts_count; j++ ) {
     938           0 :       if( 0 == memcmp( instr_context->accounts[j].address, txn_ctx->executable_accounts[i].pubkey->uc, sizeof(fd_pubkey_t) ) ) {
     939           0 :         account_exists = true;
     940           0 :         break;
     941           0 :       }
     942           0 :     }
     943             :     // Copy it into output
     944           0 :     if( !account_exists ) {
     945           0 :       fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
     946           0 :       dump_account_state( txn_account, output_account, spad );
     947           0 :     }
     948           0 :   }
     949             : 
     950             :   /* Instruction Accounts */
     951           0 :   instr_context->instr_accounts_count = (pb_size_t) instr->acct_cnt;
     952           0 :   instr_context->instr_accounts = fd_spad_alloc( spad, alignof(fd_exec_test_instr_acct_t), instr_context->instr_accounts_count * sizeof(fd_exec_test_instr_acct_t) );
     953           0 :   for( ushort i = 0; i < instr->acct_cnt; i++ ) {
     954           0 :     fd_exec_test_instr_acct_t * output_instr_account = &instr_context->instr_accounts[i];
     955             : 
     956           0 :     output_instr_account->index       = instr->accounts[i].index_in_transaction;
     957           0 :     output_instr_account->is_writable = instr->accounts[i].is_writable;
     958           0 :     output_instr_account->is_signer   = instr->accounts[i].is_signer;
     959           0 :   }
     960             : 
     961             :   /* Data */
     962           0 :   instr_context->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( instr->data_sz ) );
     963           0 :   instr_context->data->size = (pb_size_t) instr->data_sz;
     964           0 :   fd_memcpy( instr_context->data->bytes, instr->data, instr->data_sz );
     965             : 
     966             :   /* Compute Units */
     967           0 :   instr_context->cu_avail = txn_ctx->compute_budget_details.compute_meter;
     968             : 
     969             :   /* Slot Context */
     970           0 :   instr_context->has_slot_context = true;
     971             : 
     972             :   /* Epoch Context */
     973           0 :   instr_context->has_epoch_context = true;
     974           0 :   instr_context->epoch_context.has_features = true;
     975           0 :   dump_sorted_features( &txn_ctx->features, &instr_context->epoch_context.features, spad );
     976           0 : }
     977             : 
     978             : /***** PUBLIC APIs *****/
     979             : 
     980             : void
     981             : fd_dump_instr_to_protobuf( fd_exec_txn_ctx_t * txn_ctx,
     982             :                            fd_instr_info_t *   instr,
     983           0 :                            ushort              instruction_idx ) {
     984           0 :   fd_spad_t * spad = fd_spad_join( fd_spad_new( txn_ctx->dumping_mem, 1UL<<28UL ) );
     985             : 
     986           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
     987             :     // Get base58-encoded tx signature
     988           0 :     const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( &txn_ctx->txn ), txn_ctx->txn.payload );
     989           0 :     fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
     990           0 :     char encoded_signature[FD_BASE58_ENCODED_64_SZ];
     991           0 :     ulong out_size;
     992           0 :     fd_base58_encode_64( signature, &out_size, encoded_signature );
     993             : 
     994           0 :     if (txn_ctx->capture_ctx->dump_proto_sig_filter) {
     995           0 :       ulong filter_strlen = (ulong) strlen(txn_ctx->capture_ctx->dump_proto_sig_filter);
     996             : 
     997             :       // Terminate early if the signature does not match
     998           0 :       if( memcmp( txn_ctx->capture_ctx->dump_proto_sig_filter, encoded_signature, filter_strlen < out_size ? filter_strlen : out_size ) ) {
     999           0 :         return;
    1000           0 :       }
    1001           0 :     }
    1002             : 
    1003           0 :     fd_exec_test_instr_context_t instr_context = FD_EXEC_TEST_INSTR_CONTEXT_INIT_DEFAULT;
    1004           0 :     create_instr_context_protobuf_from_instructions( &instr_context, txn_ctx, instr, spad );
    1005             : 
    1006             :     /* Output to file */
    1007           0 :     ulong        out_buf_size = 100 * 1024 * 1024;
    1008           0 :     uint8_t *    out          = fd_spad_alloc( spad, alignof(uchar) , out_buf_size );
    1009           0 :     pb_ostream_t stream       = pb_ostream_from_buffer( out, out_buf_size );
    1010           0 :     if (pb_encode(&stream, FD_EXEC_TEST_INSTR_CONTEXT_FIELDS, &instr_context)) {
    1011           0 :       char output_filepath[ PATH_MAX ];
    1012           0 :       snprintf( output_filepath, PATH_MAX, "%s/instr-%s-%hu.instrctx", txn_ctx->capture_ctx->dump_proto_output_dir, encoded_signature, instruction_idx );
    1013           0 :       FILE * file = fopen(output_filepath, "wb");
    1014           0 :       if( file ) {
    1015           0 :         fwrite( out, 1, stream.bytes_written, file );
    1016           0 :         fclose( file );
    1017           0 :       }
    1018           0 :     }
    1019           0 :   } FD_SPAD_FRAME_END;
    1020           0 : }
    1021             : 
    1022             : void
    1023           0 : fd_dump_txn_to_protobuf( fd_exec_txn_ctx_t * txn_ctx ) {
    1024           0 :   fd_spad_t * spad = fd_spad_join( fd_spad_new( txn_ctx->dumping_mem, 1UL<<28UL ) );
    1025             : 
    1026           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1027             :     // Get base58-encoded tx signature
    1028           0 :     const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( &txn_ctx->txn ), txn_ctx->txn.payload );
    1029           0 :     fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
    1030           0 :     char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1031           0 :     ulong out_size;
    1032           0 :     fd_base58_encode_64( signature, &out_size, encoded_signature );
    1033             : 
    1034           0 :     if( txn_ctx->capture_ctx->dump_proto_sig_filter ) {
    1035             :       // Terminate early if the signature does not match
    1036           0 :       if( strcmp( txn_ctx->capture_ctx->dump_proto_sig_filter, encoded_signature ) ) {
    1037           0 :         return;
    1038           0 :       }
    1039           0 :     }
    1040             : 
    1041           0 :     fd_exec_test_txn_context_t txn_context_msg = FD_EXEC_TEST_TXN_CONTEXT_INIT_DEFAULT;
    1042           0 :     create_txn_context_protobuf_from_txn( &txn_context_msg, txn_ctx, spad );
    1043             : 
    1044             :     /* Output to file */
    1045           0 :     ulong        out_buf_size = 100UL<<20UL; // 100 MB
    1046           0 :     uchar *      out          = fd_spad_alloc( spad, alignof(uchar), out_buf_size );
    1047           0 :     pb_ostream_t stream       = pb_ostream_from_buffer( out, out_buf_size );
    1048           0 :     if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_context_msg ) ) {
    1049           0 :       char output_filepath[ PATH_MAX ];
    1050           0 :       snprintf( output_filepath, PATH_MAX, "%s/txn-%s.txnctx", txn_ctx->capture_ctx->dump_proto_output_dir, encoded_signature );
    1051           0 :       FILE * file = fopen(output_filepath, "wb");
    1052           0 :       if( file ) {
    1053           0 :         fwrite( out, 1, stream.bytes_written, file );
    1054           0 :         fclose( file );
    1055           0 :       }
    1056           0 :     }
    1057           0 :   } FD_SPAD_FRAME_END;
    1058           0 : }
    1059             : 
    1060             : void
    1061             : fd_dump_block_to_protobuf_collect_tx( fd_block_dump_ctx_t * dump_ctx,
    1062           0 :                                       fd_txn_p_t const *    txn ) {
    1063           0 :   if( FD_UNLIKELY( dump_ctx->txns_to_dump_cnt>=FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ) ) {
    1064           0 :     FD_LOG_ERR(( "Please increase FD_BLOCK_DUMP_CTX_MAX_TXN_CNT to dump more than %lu transactions.", FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ));
    1065           0 :     return;
    1066           0 :   }
    1067           0 :   fd_memcpy( &dump_ctx->txns_to_dump[dump_ctx->txns_to_dump_cnt++], txn, sizeof(fd_txn_p_t) );
    1068           0 : }
    1069             : 
    1070             : void
    1071             : fd_dump_block_to_protobuf( fd_block_dump_ctx_t *     dump_ctx,
    1072             :                            fd_banks_t *              banks,
    1073             :                            fd_bank_t *               bank,
    1074             :                            fd_funk_t *               funk,
    1075           0 :                            fd_capture_ctx_t const *  capture_ctx ) {
    1076           0 : FD_SPAD_FRAME_BEGIN( dump_ctx->spad ) {
    1077           0 :   if( FD_UNLIKELY( capture_ctx==NULL ) ) {
    1078           0 :     FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." ));
    1079           0 :     return;
    1080           0 :   }
    1081             : 
    1082           0 :   if( FD_UNLIKELY( dump_ctx==NULL ) ) {
    1083           0 :     FD_LOG_WARNING(( "Block dumping context may not be NULL when dumping blocks." ));
    1084           0 :     return;
    1085           0 :   }
    1086             : 
    1087             :   /* Dump the block context */
    1088           0 :   create_block_context_protobuf_from_block( dump_ctx, banks, bank, funk );
    1089             : 
    1090             :   /* Output to file */
    1091           0 :   ulong        out_buf_size = 1UL<<30UL; /* 1 GB */
    1092           0 :   uint8_t *    out          = fd_spad_alloc( dump_ctx->spad, alignof(uint8_t), out_buf_size );
    1093           0 :   pb_ostream_t stream       = pb_ostream_from_buffer( out, out_buf_size );
    1094           0 :   if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, &dump_ctx->block_context ) ) {
    1095           0 :     char output_filepath[ PATH_MAX ];
    1096           0 :     snprintf( output_filepath, PATH_MAX, "%s/block-%lu.blockctx", capture_ctx->dump_proto_output_dir, fd_bank_slot_get( bank ) );
    1097           0 :     FILE * file = fopen(output_filepath, "wb");
    1098           0 :     if( file ) {
    1099           0 :       fwrite( out, 1, stream.bytes_written, file );
    1100           0 :       fclose( file );
    1101           0 :     }
    1102           0 :   }
    1103           0 : } FD_SPAD_FRAME_END;
    1104           0 : }
    1105             : 
    1106             : void
    1107             : fd_dump_vm_syscall_to_protobuf( fd_vm_t const * vm,
    1108           0 :                                 char const *    fn_name ) {
    1109             : 
    1110           0 :   fd_spad_t * spad = fd_spad_join( fd_spad_new( vm->instr_ctx->txn_ctx->dumping_mem, 1UL<<28UL ) );
    1111             : 
    1112           0 : FD_SPAD_FRAME_BEGIN( spad ) {
    1113             : 
    1114           0 :   fd_ed25519_sig_t signature;
    1115           0 :   memcpy( signature, (uchar const *)vm->instr_ctx->txn_ctx->txn.payload + TXN( &vm->instr_ctx->txn_ctx->txn )->signature_off, sizeof(fd_ed25519_sig_t) );
    1116           0 :   char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1117           0 :   fd_base58_encode_64( signature, NULL, encoded_signature );
    1118             : 
    1119           0 :   char filename[ PATH_MAX ];
    1120           0 :   snprintf( filename,
    1121           0 :           PATH_MAX,
    1122           0 :           "%s/syscall-%s-%s-%d-%hhu-%lu.sysctx",
    1123           0 :           vm->instr_ctx->txn_ctx->capture_ctx->dump_proto_output_dir,
    1124           0 :           fn_name,
    1125           0 :           encoded_signature,
    1126           0 :           vm->instr_ctx->txn_ctx->current_instr_idx,
    1127           0 :           vm->instr_ctx->txn_ctx->instr_stack_sz,
    1128           0 :           vm->cu );
    1129             : 
    1130             :   /* The generated filename should be unique for every call. Silently return otherwise. */
    1131           0 :   if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
    1132           0 :     return;
    1133           0 :   }
    1134             : 
    1135           0 :   fd_exec_test_syscall_context_t sys_ctx = FD_EXEC_TEST_SYSCALL_CONTEXT_INIT_ZERO;
    1136             : 
    1137             :   /* SyscallContext -> vm_ctx */
    1138           0 :   sys_ctx.has_vm_ctx = 1;
    1139             : 
    1140             :   /* SyscallContext -> vm_ctx -> heap_max */
    1141           0 :   sys_ctx.vm_ctx.heap_max = vm->heap_max; /* should be equiv. to txn_ctx->heap_sz */
    1142             : 
    1143             :   /* SyscallContext -> vm_ctx -> rodata */
    1144           0 :   sys_ctx.vm_ctx.rodata = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->rodata_sz ) );
    1145           0 :   sys_ctx.vm_ctx.rodata->size = (pb_size_t) vm->rodata_sz;
    1146           0 :   fd_memcpy( sys_ctx.vm_ctx.rodata->bytes, vm->rodata, vm->rodata_sz );
    1147             : 
    1148             :   /* SyscallContext -> vm_ctx -> rodata_text_section_offset */
    1149           0 :   sys_ctx.vm_ctx.rodata_text_section_offset = vm->text_off;
    1150             : 
    1151             :   /* SyscallContext -> vm_ctx -> rodata_text_section_length */
    1152           0 :   sys_ctx.vm_ctx.rodata_text_section_length = vm->text_sz;
    1153             : 
    1154             :   /* SyscallContext -> vm_ctx -> r0-11 */
    1155           0 :   sys_ctx.vm_ctx.r0  = vm->reg[0];
    1156           0 :   sys_ctx.vm_ctx.r1  = vm->reg[1];
    1157           0 :   sys_ctx.vm_ctx.r2  = vm->reg[2];
    1158           0 :   sys_ctx.vm_ctx.r3  = vm->reg[3];
    1159           0 :   sys_ctx.vm_ctx.r4  = vm->reg[4];
    1160           0 :   sys_ctx.vm_ctx.r5  = vm->reg[5];
    1161           0 :   sys_ctx.vm_ctx.r6  = vm->reg[6];
    1162           0 :   sys_ctx.vm_ctx.r7  = vm->reg[7];
    1163           0 :   sys_ctx.vm_ctx.r8  = vm->reg[8];
    1164           0 :   sys_ctx.vm_ctx.r9  = vm->reg[9];
    1165           0 :   sys_ctx.vm_ctx.r10 = vm->reg[10];
    1166           0 :   sys_ctx.vm_ctx.r11 = vm->reg[11];
    1167             : 
    1168             :   /* SyscallContext -> vm_ctx -> entry_pc */
    1169           0 :   sys_ctx.vm_ctx.entry_pc = vm->entry_pc;
    1170             : 
    1171             :   /* SyscallContext -> vm_ctx -> return_data */
    1172           0 :   sys_ctx.vm_ctx.has_return_data = 1;
    1173             : 
    1174             :   /* SyscallContext -> vm_ctx -> return_data -> data */
    1175           0 :   sys_ctx.vm_ctx.return_data.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->instr_ctx->txn_ctx->return_data.len ) );
    1176           0 :   sys_ctx.vm_ctx.return_data.data->size = (pb_size_t)vm->instr_ctx->txn_ctx->return_data.len;
    1177           0 :   fd_memcpy( sys_ctx.vm_ctx.return_data.data->bytes, vm->instr_ctx->txn_ctx->return_data.data, vm->instr_ctx->txn_ctx->return_data.len );
    1178             : 
    1179             :   /* SyscallContext -> vm_ctx -> return_data -> program_id */
    1180           0 :   sys_ctx.vm_ctx.return_data.program_id = fd_spad_alloc( spad, alignof(pb_bytes_array_t), sizeof(fd_pubkey_t) );
    1181           0 :   sys_ctx.vm_ctx.return_data.program_id->size = sizeof(fd_pubkey_t);
    1182           0 :   fd_memcpy( sys_ctx.vm_ctx.return_data.program_id->bytes, vm->instr_ctx->txn_ctx->return_data.program_id.key, sizeof(fd_pubkey_t) );
    1183             : 
    1184             :   /* SyscallContext -> vm_ctx -> sbpf_version */
    1185           0 :   sys_ctx.vm_ctx.sbpf_version = (uint)vm->sbpf_version;
    1186             : 
    1187             :   /* SyscallContext -> instr_ctx */
    1188           0 :   sys_ctx.has_instr_ctx = 1;
    1189           0 :   create_instr_context_protobuf_from_instructions( &sys_ctx.instr_ctx,
    1190           0 :                                                    vm->instr_ctx->txn_ctx,
    1191           0 :                                                    vm->instr_ctx->instr,
    1192           0 :                                                    spad );
    1193             : 
    1194             :   /* SyscallContext -> syscall_invocation */
    1195           0 :   sys_ctx.has_syscall_invocation = 1;
    1196             : 
    1197             :   /* SyscallContext -> syscall_invocation -> function_name */
    1198           0 :   sys_ctx.syscall_invocation.function_name.size = fd_uint_min( (uint) strlen(fn_name), sizeof(sys_ctx.syscall_invocation.function_name.bytes) );
    1199           0 :   fd_memcpy( sys_ctx.syscall_invocation.function_name.bytes,
    1200           0 :              fn_name,
    1201           0 :              sys_ctx.syscall_invocation.function_name.size );
    1202             : 
    1203             :   /* SyscallContext -> syscall_invocation -> heap_prefix */
    1204           0 :   sys_ctx.syscall_invocation.heap_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
    1205           0 :   sys_ctx.syscall_invocation.heap_prefix->size = (pb_size_t) vm->instr_ctx->txn_ctx->compute_budget_details.heap_size;
    1206           0 :   fd_memcpy( sys_ctx.syscall_invocation.heap_prefix->bytes, vm->heap, vm->instr_ctx->txn_ctx->compute_budget_details.heap_size );
    1207             : 
    1208             :   /* SyscallContext -> syscall_invocation -> stack_prefix */
    1209           0 :   pb_size_t stack_sz = (pb_size_t)FD_VM_STACK_MAX;
    1210           0 :   sys_ctx.syscall_invocation.stack_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( stack_sz ) );
    1211           0 :   sys_ctx.syscall_invocation.stack_prefix->size = stack_sz;
    1212           0 :   fd_memcpy( sys_ctx.syscall_invocation.stack_prefix->bytes, vm->stack, stack_sz );
    1213             : 
    1214             :   /* Output to file */
    1215           0 :   ulong out_buf_size = 1UL<<29UL; /* 128 MB */
    1216           0 :   uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
    1217           0 :   pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
    1218           0 :   if( pb_encode( &stream, FD_EXEC_TEST_SYSCALL_CONTEXT_FIELDS, &sys_ctx ) ) {
    1219           0 :     FILE * file = fopen(filename, "wb");
    1220           0 :     if( file ) {
    1221           0 :       fwrite( out, 1, stream.bytes_written, file );
    1222           0 :       fclose( file );
    1223           0 :     }
    1224           0 :   }
    1225           0 : } FD_SPAD_FRAME_END;
    1226           0 : }
    1227             : 
    1228             : void
    1229             : fd_dump_elf_to_protobuf( fd_exec_txn_ctx_t * txn_ctx,
    1230           0 :                          fd_txn_account_t *  program_acc ) {
    1231           0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( txn_ctx->dumping_mem, 1UL<<28UL ) );
    1232             : 
    1233           0 : FD_SPAD_FRAME_BEGIN( spad ) {
    1234             : 
    1235             :   /* Get the programdata for the account */
    1236           0 :   ulong         program_data_len = 0UL;
    1237           0 :   uchar const * program_data     =
    1238           0 :       fd_prog_load_elf( txn_ctx->funk, txn_ctx->xid, program_acc, &program_data_len, NULL );
    1239           0 :   if( program_data==NULL ) {
    1240           0 :     return;
    1241           0 :   }
    1242             : 
    1243             :   /* Serialize the ELF to protobuf */
    1244           0 :   fd_ed25519_sig_t signature;
    1245           0 :   memcpy( signature, (uchar const *)txn_ctx->txn.payload + TXN( &txn_ctx->txn )->signature_off, sizeof(fd_ed25519_sig_t) );
    1246           0 :   char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1247           0 :   fd_base58_encode_64( signature, NULL, encoded_signature );
    1248             : 
    1249           0 :   char filename[ PATH_MAX ];
    1250           0 :   snprintf( filename,
    1251           0 :           PATH_MAX,
    1252           0 :           "%s/elf-%s-%s-%lu.elfctx",
    1253           0 :           txn_ctx->capture_ctx->dump_proto_output_dir,
    1254           0 :           encoded_signature,
    1255           0 :           FD_BASE58_ENC_32_ALLOCA( program_acc->pubkey ),
    1256           0 :           txn_ctx->slot );
    1257             : 
    1258             :   /* The generated filename should be unique for every call. Silently return otherwise. */
    1259           0 :   if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
    1260           0 :     return;
    1261           0 :   }
    1262             : 
    1263           0 :   fd_exec_test_elf_loader_ctx_t elf_ctx = FD_EXEC_TEST_ELF_LOADER_CTX_INIT_ZERO;
    1264             : 
    1265             :   /* ElfLoaderCtx -> elf */
    1266           0 :   elf_ctx.has_elf = true;
    1267           0 :   elf_ctx.elf.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( program_data_len ) );
    1268           0 :   elf_ctx.elf.data->size = (pb_size_t)program_data_len;
    1269           0 :   fd_memcpy( elf_ctx.elf.data->bytes, program_data, program_data_len );
    1270             : 
    1271             :   /* ElfLoaderCtx -> features */
    1272           0 :   elf_ctx.has_features = true;
    1273           0 :   dump_sorted_features( &txn_ctx->features, &elf_ctx.features, spad );
    1274             : 
    1275             :   /* ElfLoaderCtx -> deploy_checks
    1276             :      We hardcode this to true and rely the fuzzer to toggle this as it pleases */
    1277           0 :   elf_ctx.deploy_checks = true;
    1278             : 
    1279             :   /* Output to file */
    1280           0 :   ulong out_buf_size = 1UL<<29UL; /* 128 MB */
    1281           0 :   uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
    1282           0 :   pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
    1283           0 :   if( pb_encode( &stream, FD_EXEC_TEST_ELF_LOADER_CTX_FIELDS, &elf_ctx ) ) {
    1284           0 :     FILE * file = fopen(filename, "wb");
    1285           0 :     if( file ) {
    1286           0 :       fwrite( out, 1, stream.bytes_written, file );
    1287           0 :       fclose( file );
    1288           0 :     }
    1289           0 :   }
    1290           0 : } FD_SPAD_FRAME_END;
    1291           0 : }

Generated by: LCOV version 1.14