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 787 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 18 0.0 %

          Line data    Source code
       1             : #include "fd_dump_pb.h"
       2             : 
       3             : #define SORT_NAME        sort_uint64_t
       4           0 : #define SORT_KEY_T       uint64_t
       5           0 : #define SORT_BEFORE(a,b) (a)<(b)
       6             : #include "../../../util/tmpl/fd_sort.c"
       7             : 
       8             : /***** UTILITY FUNCTIONS *****/
       9             : 
      10             : /** GENERAL UTILITY FUNCTIONS AND MACROS **/
      11             : 
      12             : /** FEATURE DUMPING **/
      13             : static void
      14             : dump_sorted_features( fd_features_t const * features,
      15             :                       fd_exec_test_feature_set_t * output_feature_set,
      16           0 :                       fd_spad_t * spad ) {
      17             :   /* NOTE: Caller must have a spad frame prepared */
      18           0 :   uint64_t * unsorted_features = fd_spad_alloc( spad, alignof(uint64_t), FD_FEATURE_ID_CNT * sizeof(uint64_t) );
      19           0 :   ulong num_features = 0;
      20           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 ) ) {
      21           0 :     if (features->f[current_feature->index] != FD_FEATURE_DISABLED) {
      22           0 :       unsorted_features[num_features++] = (uint64_t) current_feature->id.ul[0];
      23           0 :     }
      24           0 :   }
      25             :   // Sort the features
      26           0 :   void * scratch = fd_spad_alloc( spad, sort_uint64_t_stable_scratch_align(), sort_uint64_t_stable_scratch_footprint(num_features) );
      27           0 :   uint64_t * sorted_features = sort_uint64_t_stable_fast( unsorted_features, num_features, scratch );
      28             : 
      29             :   // Set feature set in message
      30           0 :   output_feature_set->features_count = (pb_size_t) num_features;
      31           0 :   output_feature_set->features       = sorted_features;
      32           0 : }
      33             : 
      34             : /** ACCOUNT DUMPING **/
      35             : static void
      36             : dump_account_state( fd_txn_account_t const *    txn_account,
      37             :                     fd_exec_test_acct_state_t * output_account,
      38           0 :                     fd_spad_t *                 spad ) {
      39             :     // Address
      40           0 :     fd_memcpy(output_account->address, txn_account->pubkey, sizeof(fd_pubkey_t));
      41             : 
      42             :     // Lamports
      43           0 :     output_account->lamports = (uint64_t) txn_account->const_meta->info.lamports;
      44             : 
      45             :     // Data
      46           0 :     output_account->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( txn_account->const_meta->dlen ) );
      47           0 :     output_account->data->size = (pb_size_t) txn_account->const_meta->dlen;
      48           0 :     fd_memcpy(output_account->data->bytes, txn_account->const_data, txn_account->const_meta->dlen);
      49             : 
      50             :     // Executable
      51           0 :     output_account->executable = (bool) txn_account->const_meta->info.executable;
      52             : 
      53             :     // Rent epoch
      54           0 :     output_account->rent_epoch = (uint64_t) txn_account->const_meta->info.rent_epoch;
      55             : 
      56             :     // Owner
      57           0 :     fd_memcpy(output_account->owner, txn_account->const_meta->info.owner, sizeof(fd_pubkey_t));
      58             : 
      59             :     // Seed address (not present)
      60           0 :     output_account->has_seed_addr = false;
      61           0 : }
      62             : 
      63             : static uchar
      64             : account_already_dumped( fd_exec_test_acct_state_t const * dumped_accounts,
      65             :                         ulong                             dumped_cnt,
      66           0 :                         fd_pubkey_t const *               account_key ) {
      67           0 :   for( ulong i=0UL; i<dumped_cnt; i++ ) {
      68           0 :     if( !memcmp( account_key, dumped_accounts[i].address, sizeof(fd_pubkey_t) ) ) {
      69           0 :       return 1;
      70           0 :     }
      71           0 :   }
      72           0 :   return 0;
      73           0 : }
      74             : 
      75             : /* Dumps a borrowed account if it exists and has not been dumped yet. Sets up the output borrowed
      76             :    account if it exists. Returns 0 if the account exists, 1 otherwise. */
      77             : static uchar
      78             : dump_account_if_not_already_dumped( fd_exec_slot_ctx_t const *  slot_ctx,
      79             :                                     fd_pubkey_t const *         account_key,
      80             :                                     fd_spad_t *                 spad,
      81             :                                     fd_exec_test_acct_state_t * out_acct_states,
      82             :                                     pb_size_t *                 out_acct_states_cnt,
      83           0 :                                     fd_txn_account_t *          opt_out_borrowed_account ) {
      84           0 :   FD_TXN_ACCOUNT_DECL( account );
      85           0 :   if( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, account_key, account ) ) {
      86           0 :     return 1;
      87           0 :   }
      88             : 
      89           0 :   if( !account_already_dumped( out_acct_states, *out_acct_states_cnt, account_key ) ) {
      90           0 :     dump_account_state( account, &out_acct_states[*out_acct_states_cnt], spad );
      91           0 :     (*out_acct_states_cnt)++;
      92           0 :   }
      93             : 
      94           0 :   if( opt_out_borrowed_account ) {
      95           0 :     *opt_out_borrowed_account = *account;
      96           0 :   }
      97           0 :   return 0;
      98           0 : }
      99             : 
     100             : /* TODO: This can be made slightly more efficient by dumping only the referenced ALUT accounts instead of all accounts */
     101             : static void
     102             : dump_lut_account_and_contained_accounts(  fd_exec_slot_ctx_t const *     slot_ctx,
     103             :                                           uchar const *                  txn_payload,
     104             :                                           fd_txn_acct_addr_lut_t const * lookup_table,
     105             :                                           fd_spad_t *                    spad,
     106             :                                           fd_exec_test_acct_state_t *    out_account_states,
     107           0 :                                           pb_size_t *                    out_account_states_count ) {
     108           0 :   FD_TXN_ACCOUNT_DECL( alut_account );
     109           0 :   fd_pubkey_t const * alut_pubkey = (fd_pubkey_t const *)((uchar *)txn_payload + lookup_table->addr_off);
     110           0 :   uchar account_exists = dump_account_if_not_already_dumped( slot_ctx, alut_pubkey, spad, out_account_states, out_account_states_count, alut_account );
     111           0 :   if( !account_exists || alut_account->const_meta->dlen<FD_LOOKUP_TABLE_META_SIZE ) {
     112           0 :     return;
     113           0 :   }
     114             : 
     115             :   /* Decode the ALUT account and find its referenced writable and readonly indices */
     116           0 :   if( alut_account->const_meta->dlen & 0x1fUL ) {
     117           0 :     return;
     118           0 :   }
     119             : 
     120           0 :   fd_pubkey_t * lookup_addrs = (fd_pubkey_t *)&alut_account->const_data[FD_LOOKUP_TABLE_META_SIZE];
     121           0 :   ulong lookup_addrs_cnt     = ( alut_account->const_meta->dlen - FD_LOOKUP_TABLE_META_SIZE ) >> 5UL; // = (dlen - 56) / 32
     122           0 :   for( ulong i=0UL; i<lookup_addrs_cnt; i++ ) {
     123           0 :     fd_pubkey_t const * referenced_pubkey = &lookup_addrs[i];
     124           0 :     dump_account_if_not_already_dumped( slot_ctx, referenced_pubkey, spad, out_account_states, out_account_states_count, NULL );
     125           0 :   }
     126           0 : }
     127             : 
     128             : static void
     129             : dump_executable_account_if_exists( fd_exec_slot_ctx_t const *        slot_ctx,
     130             :                                    fd_exec_test_acct_state_t const * program_account,
     131             :                                    fd_spad_t *                       spad,
     132             :                                    fd_exec_test_acct_state_t *       out_account_states,
     133           0 :                                    pb_size_t *                       out_account_states_count ) {
     134           0 :   if( FD_LIKELY( memcmp( program_account->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     135           0 :     return;
     136           0 :   }
     137             : 
     138           0 :   fd_bincode_decode_ctx_t ctx = {
     139           0 :     .data    = program_account->data->bytes,
     140           0 :     .dataend = program_account->data->bytes + program_account->data->size,
     141           0 :   };
     142             : 
     143           0 :   ulong total_sz = 0UL;
     144           0 :   int err = fd_bpf_upgradeable_loader_state_decode_footprint( &ctx, &total_sz );
     145           0 :   if( FD_UNLIKELY( err ) ) {
     146           0 :     return;
     147           0 :   }
     148             : 
     149           0 :   uchar * mem = fd_spad_alloc( spad, FD_BPF_UPGRADEABLE_LOADER_STATE_ALIGN, total_sz );
     150           0 :   fd_bpf_upgradeable_loader_state_t * program_loader_state = fd_bpf_upgradeable_loader_state_decode( mem, &ctx );
     151             : 
     152           0 :   if( !fd_bpf_upgradeable_loader_state_is_program( program_loader_state ) ) {
     153           0 :     return;
     154           0 :   }
     155             : 
     156           0 :   fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
     157           0 :   dump_account_if_not_already_dumped( slot_ctx, programdata_acc, spad, out_account_states, out_account_states_count, NULL );
     158           0 : }
     159             : 
     160             : /** VOTE ACCOUNTS DUMPING **/
     161             : static void
     162             : dump_vote_accounts( fd_exec_slot_ctx_t const *     slot_ctx,
     163             :                     fd_vote_accounts_t const *     vote_accounts,
     164             :                     fd_spad_t *                    spad,
     165             :                     fd_exec_test_vote_account_t ** out_vote_accounts,
     166             :                     pb_size_t *                    out_vote_accounts_count,
     167             :                     fd_exec_test_acct_state_t *    out_acct_states,
     168           0 :                     pb_size_t *                    out_acct_states_cnt ) {
     169           0 :   pb_size_t idx            = 0UL;
     170           0 :   ulong vote_account_t_cnt = fd_vote_accounts_pair_t_map_size( vote_accounts->vote_accounts_pool,
     171           0 :                                                                vote_accounts->vote_accounts_root );
     172           0 :   fd_exec_test_vote_account_t * vote_account_out = fd_spad_alloc( spad,
     173           0 :                                                                   alignof(fd_exec_test_vote_account_t),
     174           0 :                                                                   vote_account_t_cnt * sizeof(fd_exec_test_vote_account_t) );
     175             : 
     176           0 :   for( fd_vote_accounts_pair_t_mapnode_t const * curr = fd_vote_accounts_pair_t_map_minimum_const(
     177           0 :           vote_accounts->vote_accounts_pool,
     178           0 :           vote_accounts->vote_accounts_root );
     179           0 :        curr;
     180           0 :        curr = fd_vote_accounts_pair_t_map_successor_const( vote_accounts->vote_accounts_pool, curr ) ) {
     181           0 :     fd_exec_test_vote_account_t * vote_out = &vote_account_out[idx++];
     182             : 
     183           0 :     vote_out->has_vote_account           = true;
     184           0 :     vote_out->stake                      = curr->elem.stake;
     185           0 :     vote_out->vote_account.lamports      = curr->elem.value.lamports;
     186           0 :     vote_out->vote_account.rent_epoch    = curr->elem.value.rent_epoch;
     187           0 :     vote_out->vote_account.executable    = curr->elem.value.executable;
     188           0 :     vote_out->vote_account.has_seed_addr = false;
     189             : 
     190           0 :     fd_memcpy( &vote_out->vote_account.address, &curr->elem.key, sizeof(fd_pubkey_t) );
     191           0 :     fd_memcpy( &vote_out->vote_account.owner, &curr->elem.value.owner, sizeof(fd_pubkey_t) );
     192             : 
     193           0 :     vote_out->vote_account.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( curr->elem.value.data_len ) );
     194           0 :     vote_out->vote_account.data->size = (pb_size_t) curr->elem.value.data_len;
     195           0 :     fd_memcpy( &vote_out->vote_account.data->bytes, curr->elem.value.data, curr->elem.value.data_len );
     196             : 
     197             :     // Dump the vote account
     198           0 :     dump_account_if_not_already_dumped( slot_ctx, &curr->elem.key, spad, out_acct_states, out_acct_states_cnt, NULL );
     199           0 :   }
     200             : 
     201           0 :   *out_vote_accounts       = vote_account_out;
     202           0 :   *out_vote_accounts_count = idx;
     203           0 : }
     204             : 
     205             : /** TRANSACTION DUMPING **/
     206             : 
     207             : static void
     208             : dump_sanitized_transaction( fd_acc_mgr_t *                         acc_mgr,
     209             :                             fd_funk_txn_t const *                  funk_txn,
     210             :                             fd_txn_t const *                       txn_descriptor,
     211             :                             uchar const *                          txn_payload,
     212             :                             fd_spad_t *                            spad,
     213           0 :                             fd_exec_test_sanitized_transaction_t * sanitized_transaction ) {
     214           0 :   fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     215             : 
     216             :   /* Transaction Context -> tx -> message */
     217           0 :   sanitized_transaction->has_message = true;
     218           0 :   fd_exec_test_transaction_message_t * message = &sanitized_transaction->message;
     219             : 
     220             :   /* Transaction Context -> tx -> message -> is_legacy */
     221           0 :   message->is_legacy = txn_descriptor->transaction_version == FD_TXN_VLEGACY;
     222             : 
     223             :   /* Transaction Context -> tx -> message -> header */
     224           0 :   message->has_header = true;
     225           0 :   fd_exec_test_message_header_t * header = &message->header;
     226             : 
     227             :   /* Transaction Context -> tx -> message -> header -> num_required_signatures */
     228           0 :   header->num_required_signatures = txn_descriptor->signature_cnt;
     229             : 
     230             :   /* Transaction Context -> tx -> message -> header -> num_readonly_signed_accounts */
     231           0 :   header->num_readonly_signed_accounts = txn_descriptor->readonly_signed_cnt;
     232             : 
     233             :   /* Transaction Context -> tx -> message -> header -> num_readonly_unsigned_accounts */
     234           0 :   header->num_readonly_unsigned_accounts = txn_descriptor->readonly_unsigned_cnt;
     235             : 
     236             :   /* Transaction Context -> tx -> message -> account_keys */
     237           0 :   message->account_keys_count = txn_descriptor->acct_addr_cnt;
     238           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 *)) );
     239           0 :   fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_payload );
     240           0 :   for( ulong i = 0; i < txn_descriptor->acct_addr_cnt; i++ ) {
     241           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)) );
     242           0 :     account_key->size = sizeof(fd_pubkey_t);
     243           0 :     memcpy( account_key->bytes, &account_keys[i], sizeof(fd_pubkey_t) );
     244           0 :     message->account_keys[i] = account_key;
     245           0 :   }
     246             : 
     247             :   /* Transaction Context -> tx -> message -> recent_blockhash */
     248           0 :   uchar const * recent_blockhash = fd_txn_get_recent_blockhash( txn_descriptor, txn_payload );
     249           0 :   message->recent_blockhash = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_hash_t)) );
     250           0 :   message->recent_blockhash->size = sizeof(fd_hash_t);
     251           0 :   memcpy( message->recent_blockhash->bytes, recent_blockhash, sizeof(fd_hash_t) );
     252             : 
     253             :   /* Transaction Context -> tx -> message -> instructions */
     254           0 :   message->instructions_count = txn_descriptor->instr_cnt;
     255           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) );
     256           0 :   for( ulong i = 0; i < txn_descriptor->instr_cnt; ++i ) {
     257           0 :     fd_txn_instr_t instr = txn_descriptor->instr[i];
     258           0 :     fd_exec_test_compiled_instruction_t * compiled_instruction = &message->instructions[i];
     259             : 
     260             :     // compiled instruction -> program_id_index
     261           0 :     compiled_instruction->program_id_index = instr.program_id;
     262             : 
     263             :     // compiled instruction -> accounts
     264           0 :     compiled_instruction->accounts_count = instr.acct_cnt;
     265           0 :     compiled_instruction->accounts = fd_spad_alloc( spad, alignof(uint32_t), instr.acct_cnt * sizeof(uint32_t) );
     266           0 :     uchar const * instr_accounts = fd_txn_get_instr_accts( &instr, txn_payload );
     267           0 :     for( ulong j = 0; j < instr.acct_cnt; ++j ) {
     268           0 :       uchar instr_acct_index = instr_accounts[j];
     269           0 :       compiled_instruction->accounts[j] = instr_acct_index;
     270           0 :     }
     271             : 
     272             :     // compiled instruction -> data
     273           0 :     uchar const * instr_data = fd_txn_get_instr_data( &instr, txn_payload );
     274           0 :     compiled_instruction->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(instr.data_sz) );
     275           0 :     compiled_instruction->data->size = instr.data_sz;
     276           0 :     memcpy( compiled_instruction->data->bytes, instr_data, instr.data_sz );
     277           0 :   }
     278             : 
     279             :   /* ALUT stuff (non-legacy) */
     280           0 :   message->address_table_lookups_count = 0;
     281           0 :   if( !message->is_legacy ) {
     282             :     /* Transaction Context -> tx -> message -> address_table_lookups */
     283           0 :     message->address_table_lookups_count = txn_descriptor->addr_table_lookup_cnt;
     284           0 :     message->address_table_lookups = fd_spad_alloc( spad,
     285           0 :                                                     alignof(fd_exec_test_message_address_table_lookup_t),
     286           0 :                                                     txn_descriptor->addr_table_lookup_cnt * sizeof(fd_exec_test_message_address_table_lookup_t) );
     287           0 :     for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
     288             :       // alut -> account_key
     289           0 :       fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + address_lookup_tables[i].addr_off);
     290           0 :       memcpy( message->address_table_lookups[i].account_key, alut_key, sizeof(fd_pubkey_t) );
     291             : 
     292             :       // Access ALUT account data to access its keys
     293           0 :       FD_TXN_ACCOUNT_DECL(addr_lut_rec);
     294           0 :       int err = fd_acc_mgr_view( acc_mgr, funk_txn, alut_key, addr_lut_rec);
     295           0 :       if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
     296           0 :         FD_LOG_ERR(( "addr lut not found" ));
     297           0 :       }
     298             : 
     299             :       // alut -> writable_indexes
     300           0 :       message->address_table_lookups[i].writable_indexes_count = address_lookup_tables[i].writable_cnt;
     301           0 :       message->address_table_lookups[i].writable_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].writable_cnt * sizeof(uint32_t) );
     302           0 :       uchar * writable_indexes = (uchar *) (txn_payload + address_lookup_tables[i].writable_off);
     303           0 :       for( ulong j = 0; j < address_lookup_tables[i].writable_cnt; ++j ) {
     304           0 :         message->address_table_lookups[i].writable_indexes[j] = writable_indexes[j];
     305           0 :       }
     306             : 
     307             :       // alut -> readonly_indexes
     308           0 :       message->address_table_lookups[i].readonly_indexes_count = address_lookup_tables[i].readonly_cnt;
     309           0 :       message->address_table_lookups[i].readonly_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].readonly_cnt * sizeof(uint32_t) );
     310           0 :       uchar * readonly_indexes = (uchar *) (txn_payload + address_lookup_tables[i].readonly_off);
     311           0 :       for( ulong j = 0; j < address_lookup_tables[i].readonly_cnt; ++j ) {
     312           0 :         message->address_table_lookups[i].readonly_indexes[j] = readonly_indexes[j];
     313           0 :       }
     314           0 :     }
     315           0 :   }
     316             : 
     317             :   /* Transaction Context -> tx -> message_hash */
     318             :   // Skip because it does not matter what's in here
     319             : 
     320             :   /* Transaction Context -> tx -> signatures */
     321           0 :   sanitized_transaction->signatures_count = txn_descriptor->signature_cnt;
     322           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 *)) );
     323           0 :   fd_ed25519_sig_t const * signatures = fd_txn_get_signatures( txn_descriptor, txn_payload );
     324           0 :   for( uchar i = 0; i < txn_descriptor->signature_cnt; ++i ) {
     325           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)) );
     326           0 :     signature->size = sizeof(fd_ed25519_sig_t);
     327           0 :     memcpy( signature->bytes, &signatures[i], sizeof(fd_ed25519_sig_t) );
     328           0 :     sanitized_transaction->signatures[i] = signature;
     329           0 :   }
     330           0 : }
     331             : 
     332             : /** BLOCKHASH QUEUE DUMPING **/
     333             : 
     334             : static void
     335             : dump_blockhash_queue( fd_block_hash_queue_t const * queue,
     336             :                       fd_spad_t *                   spad,
     337             :                       pb_bytes_array_t **           output_blockhash_queue,
     338           0 :                       pb_size_t *                   output_blockhash_queue_count ) {
     339           0 :   pb_size_t cnt = 0;
     340           0 :   fd_hash_hash_age_pair_t_mapnode_t * nn;
     341             : 
     342             :   // Iterate over all block hashes in the queue and save them in the output
     343           0 :   for ( fd_hash_hash_age_pair_t_mapnode_t * n = fd_hash_hash_age_pair_t_map_minimum( queue->ages_pool, queue->ages_root ); n; n = nn ) {
     344           0 :     nn = fd_hash_hash_age_pair_t_map_successor( queue->ages_pool, n );
     345             : 
     346             :     /* Get the index in the blockhash queue
     347             :        - Lower index = newer
     348             :        - 0 will be the most recent blockhash
     349             :        - Index range is [0, max_age] (not a typo) */
     350           0 :     ulong queue_index = queue->last_hash_index - n->elem.val.hash_index;
     351           0 :     fd_hash_t blockhash = n->elem.key;
     352             : 
     353             :     // Write the blockhash to the correct index (note we write in reverse order since in the Protobuf message, the oldest blockhash goes first)
     354           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)) );
     355           0 :     output_blockhash->size = sizeof(fd_hash_t);
     356           0 :     fd_memcpy( output_blockhash->bytes, &blockhash, sizeof(fd_hash_t) );
     357           0 :     output_blockhash_queue[FD_BLOCKHASH_QUEUE_MAX_ENTRIES - queue_index] = output_blockhash;
     358           0 :     cnt++;
     359           0 :   }
     360             : 
     361             :   // Shift blockhash queue elements if num elements < 301
     362           0 :   if( cnt<FD_BLOCKHASH_QUEUE_MAX_ENTRIES + 1UL ) {
     363           0 :     ulong index_offset = FD_BLOCKHASH_QUEUE_MAX_ENTRIES + 1UL - cnt;
     364           0 :     for( pb_size_t i=0; i<cnt; i++ ) {
     365           0 :       output_blockhash_queue[i] = output_blockhash_queue[i + index_offset];
     366           0 :     }
     367           0 :   }
     368             : 
     369           0 :   *output_blockhash_queue_count = cnt;
     370           0 : }
     371             : 
     372             : /** SECONDARY FUNCTIONS **/
     373             : 
     374             : static void
     375             : create_block_context_protobuf_from_block( fd_exec_test_block_context_t * block_context,
     376             :                                           fd_exec_slot_ctx_t const *     slot_ctx,
     377           0 :                                           fd_spad_t *                    spad ) {
     378           0 :   fd_exec_epoch_ctx_t const * epoch_ctx = slot_ctx->epoch_ctx;
     379             : 
     380             :   /* BlockContext -> acct_states */
     381             :   // Dump sysvars + builtins
     382           0 :   fd_pubkey_t const fd_relevant_sysvar_ids[] = {
     383           0 :     fd_sysvar_recent_block_hashes_id,
     384           0 :     fd_sysvar_clock_id,
     385           0 :     fd_sysvar_slot_history_id,
     386           0 :     fd_sysvar_slot_hashes_id,
     387           0 :     fd_sysvar_epoch_schedule_id,
     388           0 :     fd_sysvar_epoch_rewards_id,
     389           0 :     fd_sysvar_fees_id,
     390           0 :     fd_sysvar_rent_id,
     391           0 :     fd_sysvar_stake_history_id,
     392           0 :     fd_sysvar_last_restart_slot_id,
     393           0 :   };
     394             : 
     395           0 :   fd_pubkey_t const loaded_builtins[] = {
     396           0 :     fd_solana_system_program_id,
     397           0 :     fd_solana_vote_program_id,
     398           0 :     fd_solana_stake_program_id,
     399           0 :     fd_solana_config_program_id,
     400             :     // fd_solana_zk_token_proof_program_id,
     401           0 :     fd_solana_bpf_loader_v4_program_id,
     402           0 :     fd_solana_address_lookup_table_program_id,
     403           0 :     fd_solana_bpf_loader_deprecated_program_id,
     404           0 :     fd_solana_bpf_loader_program_id,
     405           0 :     fd_solana_bpf_loader_upgradeable_program_id,
     406           0 :     fd_solana_compute_budget_program_id,
     407           0 :     fd_solana_keccak_secp_256k_program_id,
     408           0 :     fd_solana_secp256r1_program_id,
     409           0 :     fd_solana_zk_elgamal_proof_program_id,
     410           0 :     fd_solana_ed25519_sig_verify_program_id,
     411           0 :     fd_solana_spl_native_mint_id,
     412           0 :   };
     413           0 :   ulong num_sysvar_entries    = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t));
     414           0 :   ulong num_loaded_builtins   = (sizeof(loaded_builtins) / sizeof(fd_pubkey_t));
     415             : 
     416           0 :   ulong new_stake_account_cnt = fd_account_keys_pair_t_map_size( slot_ctx->slot_bank.stake_account_keys.account_keys_pool,
     417           0 :                                                                  slot_ctx->slot_bank.stake_account_keys.account_keys_root );
     418           0 :   ulong stake_account_cnt     = fd_delegation_pair_t_map_size( epoch_ctx->epoch_bank.stakes.stake_delegations_pool,
     419           0 :                                                                epoch_ctx->epoch_bank.stakes.stake_delegations_root );
     420             : 
     421           0 :   ulong vote_account_t_cnt    = fd_vote_accounts_pair_t_map_size( epoch_ctx->epoch_bank.stakes.vote_accounts.vote_accounts_pool,
     422           0 :                                                                   epoch_ctx->epoch_bank.stakes.vote_accounts.vote_accounts_root );
     423           0 :   ulong vote_account_t_1_cnt  = fd_vote_accounts_pair_t_map_size( epoch_ctx->epoch_bank.next_epoch_stakes.vote_accounts_pool,
     424           0 :                                                                   epoch_ctx->epoch_bank.next_epoch_stakes.vote_accounts_root );
     425           0 :   ulong vote_account_t_2_cnt  = fd_vote_accounts_pair_t_map_size( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool,
     426           0 :                                                                   slot_ctx->slot_bank.epoch_stakes.vote_accounts_root );
     427             : 
     428           0 :   ulong total_num_accounts    = num_sysvar_entries +
     429           0 :                                 num_loaded_builtins +
     430           0 :                                 new_stake_account_cnt +
     431           0 :                                 stake_account_cnt +
     432           0 :                                 stake_account_cnt +
     433           0 :                                 vote_account_t_cnt +
     434           0 :                                 vote_account_t_1_cnt +
     435           0 :                                 vote_account_t_2_cnt;
     436             : 
     437           0 :   block_context->acct_states_count = 0;
     438           0 :   block_context->acct_states       = fd_spad_alloc( spad,
     439           0 :                                                     alignof(fd_exec_test_acct_state_t),
     440           0 :                                                     total_num_accounts * sizeof(fd_exec_test_acct_state_t) );
     441             : 
     442             : 
     443           0 :   for( ulong i=0UL; i<num_sysvar_entries; i++ ) {
     444           0 :     dump_account_if_not_already_dumped( slot_ctx, &fd_relevant_sysvar_ids[i], spad, block_context->acct_states, &block_context->acct_states_count, NULL );
     445           0 :   }
     446             : 
     447           0 :   for( ulong i=0UL; i<num_loaded_builtins; i++ ) {
     448           0 :     dump_account_if_not_already_dumped( slot_ctx, &loaded_builtins[i], spad, block_context->acct_states, &block_context->acct_states_count, NULL );
     449           0 :   }
     450             : 
     451             :   /* BlockContext -> blockhash_queue */
     452           0 :   pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc( spad,
     453           0 :                                                               alignof(pb_bytes_array_t *),
     454           0 :                                                               PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASH_QUEUE_MAX_ENTRIES + 1) * sizeof(pb_bytes_array_t *)) );
     455           0 :   block_context->blockhash_queue = output_blockhash_queue;
     456           0 :   dump_blockhash_queue( &slot_ctx->slot_bank.block_hash_queue, spad, block_context->blockhash_queue, &block_context->blockhash_queue_count );
     457             : 
     458             :   /* BlockContext -> SlotContext */
     459           0 :   block_context->has_slot_ctx                       = true;
     460           0 :   block_context->slot_ctx.slot                      = slot_ctx->slot_bank.slot;
     461             :   // HACK FOR NOW: block height gets incremented in process_new_epoch, so we should dump block height + 1
     462           0 :   block_context->slot_ctx.block_height              = slot_ctx->slot_bank.block_height + 1UL;
     463             :   // fd_memcpy( block_context->slot_ctx.poh, &slot_ctx->slot_bank.poh, sizeof(fd_pubkey_t) ); // TODO: dump here when process epoch happens after poh verification
     464           0 :   fd_memcpy( block_context->slot_ctx.parent_bank_hash, &slot_ctx->slot_bank.banks_hash, sizeof(fd_pubkey_t) );
     465           0 :   fd_memcpy( block_context->slot_ctx.parent_lt_hash, &slot_ctx->slot_bank.lthash.lthash, FD_LTHASH_LEN_BYTES );
     466           0 :   block_context->slot_ctx.prev_slot                 = slot_ctx->slot_bank.prev_slot;
     467           0 :   block_context->slot_ctx.prev_lps                  = slot_ctx->prev_lamports_per_signature;
     468           0 :   block_context->slot_ctx.prev_epoch_capitalization = slot_ctx->slot_bank.capitalization;
     469             : 
     470             :   /* BlockContext -> EpochContext */
     471           0 :   block_context->has_epoch_ctx                        = true;
     472           0 :   block_context->epoch_ctx.has_features               = true;
     473           0 :   dump_sorted_features( &epoch_ctx->features, &block_context->epoch_ctx.features, spad );
     474           0 :   block_context->epoch_ctx.hashes_per_tick            = epoch_ctx->epoch_bank.hashes_per_tick;
     475           0 :   block_context->epoch_ctx.ticks_per_slot             = epoch_ctx->epoch_bank.ticks_per_slot;
     476           0 :   block_context->epoch_ctx.slots_per_year             = epoch_ctx->epoch_bank.slots_per_year;
     477           0 :   block_context->epoch_ctx.has_inflation              = true;
     478           0 :   block_context->epoch_ctx.inflation                  = (fd_exec_test_inflation_t) {
     479           0 :     .initial         = epoch_ctx->epoch_bank.inflation.initial,
     480           0 :     .terminal        = epoch_ctx->epoch_bank.inflation.terminal,
     481           0 :     .taper           = epoch_ctx->epoch_bank.inflation.taper,
     482           0 :     .foundation      = epoch_ctx->epoch_bank.inflation.foundation,
     483           0 :     .foundation_term = epoch_ctx->epoch_bank.inflation.foundation_term,
     484           0 :   };
     485           0 :   block_context->epoch_ctx.genesis_creation_time      = epoch_ctx->epoch_bank.genesis_creation_time;
     486             : 
     487             :   /* Dumping stake accounts */
     488             : 
     489             :   // BlockContext -> EpochContext -> new_stake_accounts (stake accounts for the current running epoch)
     490           0 :   block_context->epoch_ctx.new_stake_accounts_count = 0UL;
     491           0 :   block_context->epoch_ctx.new_stake_accounts       = fd_spad_alloc( spad,
     492           0 :                                                                      alignof(pb_bytes_array_t *),
     493           0 :                                                                      new_stake_account_cnt * sizeof(pb_bytes_array_t *) );
     494             : 
     495           0 :   for( fd_account_keys_pair_t_mapnode_t const * curr = fd_account_keys_pair_t_map_minimum_const(
     496           0 :           slot_ctx->slot_bank.stake_account_keys.account_keys_pool,
     497           0 :           slot_ctx->slot_bank.stake_account_keys.account_keys_root );
     498           0 :        curr;
     499           0 :        curr = fd_account_keys_pair_t_map_successor_const( slot_ctx->slot_bank.stake_account_keys.account_keys_pool, curr ) ) {
     500             : 
     501             :     // Verify the stake state before dumping
     502           0 :     FD_TXN_ACCOUNT_DECL( account );
     503           0 :     int rc = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &curr->elem.key, account );
     504           0 :     if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS ) ) {
     505           0 :       continue;
     506           0 :     }
     507             : 
     508             :     // Dump the new stake account pubkey
     509           0 :     pb_bytes_array_t * stake_out = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( sizeof(fd_pubkey_t) ) );
     510           0 :     stake_out->size = sizeof(fd_pubkey_t);
     511           0 :     fd_memcpy( stake_out->bytes, &curr->elem.key, sizeof(fd_pubkey_t) );
     512           0 :     block_context->epoch_ctx.new_stake_accounts[block_context->epoch_ctx.new_stake_accounts_count++] = stake_out;
     513             : 
     514             :     // Dump the account state as well
     515           0 :     dump_account_if_not_already_dumped( slot_ctx, &curr->elem.key, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
     516           0 :   }
     517             : 
     518             :   // BlockContext -> EpochContext -> stake_accounts (stake accounts at epoch T)
     519           0 :   block_context->epoch_ctx.stake_accounts_count = 0UL;
     520           0 :   block_context->epoch_ctx.stake_accounts       = fd_spad_alloc( spad,
     521           0 :                                                                  alignof(fd_exec_test_stake_account_t),
     522           0 :                                                                  stake_account_cnt * sizeof(fd_exec_test_stake_account_t) );
     523             : 
     524           0 :   for( fd_delegation_pair_t_mapnode_t const * curr = fd_delegation_pair_t_map_minimum_const(
     525           0 :           epoch_ctx->epoch_bank.stakes.stake_delegations_pool,
     526           0 :           epoch_ctx->epoch_bank.stakes.stake_delegations_root );
     527           0 :        curr;
     528           0 :        curr = fd_delegation_pair_t_map_successor_const( epoch_ctx->epoch_bank.stakes.stake_delegations_pool, curr ) ) {
     529           0 :     FD_TXN_ACCOUNT_DECL( account );
     530           0 :     if( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &curr->elem.account, account ) ) {
     531           0 :       continue;
     532           0 :     }
     533             : 
     534           0 :     fd_exec_test_stake_account_t * stake_out = &block_context->epoch_ctx.stake_accounts[block_context->epoch_ctx.stake_accounts_count++];
     535             : 
     536           0 :     fd_memcpy( stake_out->stake_account_pubkey, &curr->elem.account, sizeof(fd_pubkey_t) );
     537           0 :     fd_memcpy( stake_out->voter_pubkey, &curr->elem.delegation.voter_pubkey, sizeof(fd_pubkey_t) );
     538           0 :     stake_out->stake                = curr->elem.delegation.stake;
     539           0 :     stake_out->activation_epoch     = curr->elem.delegation.activation_epoch;
     540           0 :     stake_out->deactivation_epoch   = curr->elem.delegation.deactivation_epoch;
     541           0 :     stake_out->warmup_cooldown_rate = curr->elem.delegation.warmup_cooldown_rate;
     542             : 
     543             :     // Dump the account state
     544           0 :     dump_account_if_not_already_dumped( slot_ctx, &curr->elem.account, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
     545           0 :   }
     546             : 
     547             :   /* Dumping vote accounts */
     548             : 
     549             :   // BlockContext -> EpochContext -> new_vote_accounts (vote accounts for the current running epoch)
     550           0 :   ulong new_vote_account_cnt = fd_account_keys_pair_t_map_size( slot_ctx->slot_bank.vote_account_keys.account_keys_pool,
     551           0 :                                                                 slot_ctx->slot_bank.vote_account_keys.account_keys_root );
     552           0 :   block_context->epoch_ctx.new_vote_accounts_count = 0UL;
     553           0 :   block_context->epoch_ctx.new_vote_accounts       = fd_spad_alloc( spad,
     554           0 :                                                                    alignof(fd_exec_test_vote_account_t),
     555           0 :                                                                    new_vote_account_cnt * sizeof(fd_exec_test_vote_account_t) );
     556             : 
     557           0 :   for( fd_account_keys_pair_t_mapnode_t const * curr = fd_account_keys_pair_t_map_minimum_const(
     558           0 :           slot_ctx->slot_bank.vote_account_keys.account_keys_pool,
     559           0 :           slot_ctx->slot_bank.vote_account_keys.account_keys_root );
     560           0 :        curr;
     561           0 :        curr = fd_account_keys_pair_t_map_successor_const( slot_ctx->slot_bank.vote_account_keys.account_keys_pool, curr ) ) {
     562             : 
     563             :     // Verify the vote account before dumping
     564           0 :     FD_TXN_ACCOUNT_DECL( account );
     565           0 :     int rc = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &curr->elem.key, account );
     566           0 :     if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS ) ) {
     567           0 :       continue;
     568           0 :     }
     569             : 
     570             :     // Dump the new vote account pubkey
     571           0 :     pb_bytes_array_t * vote_out = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( sizeof(fd_pubkey_t) ) );
     572           0 :     vote_out->size = sizeof(fd_pubkey_t);
     573           0 :     fd_memcpy( vote_out->bytes, &curr->elem.key, sizeof(fd_pubkey_t) );
     574           0 :     block_context->epoch_ctx.new_vote_accounts[block_context->epoch_ctx.new_vote_accounts_count++] = vote_out;
     575             : 
     576             :     // Dump the account state as well
     577           0 :     dump_account_if_not_already_dumped( slot_ctx, &curr->elem.key, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
     578           0 :   }
     579             : 
     580             :   // BlockContext -> EpochContext -> vote_accounts_t (vote accounts at epoch T)
     581           0 :   dump_vote_accounts( slot_ctx,
     582           0 :                       &epoch_ctx->epoch_bank.stakes.vote_accounts,
     583           0 :                       spad,
     584           0 :                       &block_context->epoch_ctx.vote_accounts_t,
     585           0 :                       &block_context->epoch_ctx.vote_accounts_t_count,
     586           0 :                       block_context->acct_states,
     587           0 :                       &block_context->acct_states_count );
     588             : 
     589             :   // BlockContext -> EpochContext -> vote_accounts_t_1 (vote accounts at epoch T-1)
     590           0 :   dump_vote_accounts( slot_ctx,
     591           0 :                       &epoch_ctx->epoch_bank.next_epoch_stakes,
     592           0 :                       spad,
     593           0 :                       &block_context->epoch_ctx.vote_accounts_t_1,
     594           0 :                       &block_context->epoch_ctx.vote_accounts_t_1_count,
     595           0 :                       block_context->acct_states,
     596           0 :                       &block_context->acct_states_count );
     597             : 
     598             :   // BlockContext -> EpochContext -> vote_accounts_t_2 (vote accounts at epoch T-2)
     599           0 :   dump_vote_accounts( slot_ctx,
     600           0 :                       &slot_ctx->slot_bank.epoch_stakes,
     601           0 :                       spad,
     602           0 :                       &block_context->epoch_ctx.vote_accounts_t_2,
     603           0 :                       &block_context->epoch_ctx.vote_accounts_t_2_count,
     604           0 :                       block_context->acct_states,
     605           0 :                       &block_context->acct_states_count );
     606           0 : }
     607             : 
     608             : static void
     609             : create_block_context_protobuf_from_block_tx_only( fd_exec_test_block_context_t *  block_context,
     610             :                                                   fd_runtime_block_info_t const * block_info,
     611             :                                                   fd_exec_slot_ctx_t const *      slot_ctx,
     612           0 :                                                   fd_spad_t *                     spad ) {
     613             :   /* BlockContext -> microblocks */
     614           0 :   block_context->microblocks_count = 0U;
     615           0 :   block_context->microblocks       = fd_spad_alloc( spad, alignof(fd_exec_test_microblock_t), block_info->microblock_cnt * sizeof(fd_exec_test_microblock_t) );
     616             : 
     617             :   /* BlockContext -> acct_states
     618             :      Allocate additional space for the remaining accounts */
     619           0 :   fd_exec_test_acct_state_t * current_accounts = block_context->acct_states;
     620           0 :   block_context->acct_states                   = fd_spad_alloc( spad,
     621           0 :                                                                 alignof(fd_exec_test_acct_state_t),
     622           0 :                                                                 ( block_info->txn_cnt*1 + (ulong)block_context->acct_states_count ) *
     623           0 :                                                                 sizeof(fd_exec_test_acct_state_t) );
     624           0 :   fd_memcpy( block_context->acct_states, current_accounts, block_context->acct_states_count * sizeof(fd_exec_test_acct_state_t) );
     625             : 
     626             :   /* BlockContext -> slot_ctx -> poh
     627             :      This currently needs to be done because POH verification is done after epoch boundary processing. That should probably be changed */
     628           0 :   fd_memcpy( block_context->slot_ctx.poh, &slot_ctx->slot_bank.poh, sizeof(fd_pubkey_t) );
     629             : 
     630             :   /* When iterating over microblocks batches and microblocks, we flatten the batches for the output block context (essentially just one big batch with several microblocks) */
     631           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
     632           0 :     fd_microblock_batch_info_t const * microblock_batch = &block_info->microblock_batch_infos[i];
     633             : 
     634           0 :     for( ulong j=0UL; j<microblock_batch->microblock_cnt; j++ ) {
     635           0 :       fd_microblock_info_t const * microblock_info = &microblock_batch->microblock_infos[j];
     636           0 :       fd_exec_test_microblock_t * out_block        = &block_context->microblocks[block_context->microblocks_count++];
     637           0 :       ulong txn_cnt                                = microblock_info->microblock.hdr->txn_cnt;
     638             : 
     639           0 :       out_block->txns_count = (pb_size_t)txn_cnt;
     640           0 :       out_block->txns       = fd_spad_alloc( spad, alignof(fd_exec_test_sanitized_transaction_t), txn_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
     641             : 
     642             :       /* BlockContext -> microblocks -> txns */
     643           0 :       for( ulong k=0UL; k<txn_cnt; k++ ) {
     644           0 :         fd_txn_p_t const * txn_ptr      = &microblock_info->txns[k];
     645           0 :         fd_txn_t const * txn_descriptor = TXN( txn_ptr );
     646           0 :         dump_sanitized_transaction( slot_ctx->acc_mgr, slot_ctx->funk_txn, txn_descriptor, txn_ptr->payload, spad, &out_block->txns[k] );
     647             : 
     648             :         /* BlockContext -> acct_states */
     649             :         /* Dump account + alut + programdata accounts (if applicable). There's a lot more brute force work since none of the borrowed accounts are set up yet. We have to:
     650             :            1. Dump the raw txn account keys
     651             :            2. Dump the ALUT accounts
     652             :            3. Dump all referenced accounts in the ALUTs
     653             :            4. Dump any executable accounts
     654             :            5. Dump any sysvars + builtin accounts (occurs outside of this loop) */
     655             : 
     656             :         // 1. Dump any account keys that are referenced by transactions
     657           0 :         fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload );
     658           0 :         for( ushort l=0; l<txn_descriptor->acct_addr_cnt; l++ ) {
     659           0 :           fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] );
     660           0 :           dump_account_if_not_already_dumped( slot_ctx, account_key, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
     661           0 :         }
     662             : 
     663             :         // 2 + 3. Dump any ALUT accounts + any accounts referenced in the ALUTs
     664           0 :         fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     665           0 :         for( ushort l=0; l<txn_descriptor->addr_table_lookup_cnt; l++ ) {
     666           0 :           fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l];
     667           0 :           dump_lut_account_and_contained_accounts( slot_ctx, txn_ptr->payload, lookup_table, spad, block_context->acct_states, &block_context->acct_states_count );
     668           0 :         }
     669             : 
     670             :         // 4. Go through all dumped accounts and dump any executable accounts
     671           0 :         ulong dumped_accounts = block_context->acct_states_count;
     672           0 :         for( ulong l=0; l<dumped_accounts; l++ ) {
     673           0 :           fd_exec_test_acct_state_t const * maybe_program_account = &block_context->acct_states[l];
     674           0 :           dump_executable_account_if_exists( slot_ctx, maybe_program_account, spad, block_context->acct_states, &block_context->acct_states_count );
     675           0 :         }
     676           0 :       }
     677           0 :     }
     678           0 :   }
     679           0 : }
     680             : 
     681             : static void
     682             : create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_msg,
     683             :                                       fd_exec_txn_ctx_t *          txn_ctx,
     684           0 :                                       fd_spad_t *                  spad ) {
     685           0 :   fd_txn_t const * txn_descriptor = txn_ctx->txn_descriptor;
     686           0 :   uchar const *    txn_payload    = (uchar const *) txn_ctx->_txn_raw->raw;
     687             : 
     688             :   /* We don't want to store builtins in account shared data */
     689           0 :   fd_pubkey_t const loaded_builtins[] = {
     690           0 :     fd_solana_system_program_id,
     691           0 :     fd_solana_vote_program_id,
     692           0 :     fd_solana_stake_program_id,
     693           0 :     fd_solana_config_program_id,
     694             :     // fd_solana_zk_token_proof_program_id,
     695           0 :     fd_solana_bpf_loader_v4_program_id,
     696           0 :     fd_solana_address_lookup_table_program_id,
     697           0 :     fd_solana_bpf_loader_deprecated_program_id,
     698           0 :     fd_solana_bpf_loader_program_id,
     699           0 :     fd_solana_bpf_loader_upgradeable_program_id,
     700           0 :     fd_solana_compute_budget_program_id,
     701           0 :     fd_solana_keccak_secp_256k_program_id,
     702           0 :     fd_solana_secp256r1_program_id,
     703           0 :     fd_solana_zk_elgamal_proof_program_id,
     704           0 :     fd_solana_ed25519_sig_verify_program_id,
     705           0 :     fd_solana_spl_native_mint_id,
     706           0 :   };
     707           0 :   const ulong num_loaded_builtins = (sizeof(loaded_builtins) / sizeof(fd_pubkey_t));
     708             : 
     709             :   /* Prepare sysvar cache accounts */
     710           0 :   fd_pubkey_t const fd_relevant_sysvar_ids[] = {
     711           0 :     fd_sysvar_recent_block_hashes_id,
     712           0 :     fd_sysvar_clock_id,
     713           0 :     fd_sysvar_slot_history_id,
     714           0 :     fd_sysvar_slot_hashes_id,
     715           0 :     fd_sysvar_epoch_schedule_id,
     716           0 :     fd_sysvar_epoch_rewards_id,
     717           0 :     fd_sysvar_fees_id,
     718           0 :     fd_sysvar_rent_id,
     719           0 :     fd_sysvar_stake_history_id,
     720           0 :     fd_sysvar_last_restart_slot_id,
     721           0 :   };
     722           0 :   const ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t));
     723             : 
     724             :   /* Transaction Context -> account_shared_data
     725             :      Contains:
     726             :       - Account data for regular accounts
     727             :       - Account data for LUT accounts
     728             :       - Account data for executable accounts
     729             :       - Account data for (almost) all sysvars
     730             :   */
     731             :   // Dump regular accounts first
     732           0 :   txn_context_msg->account_shared_data_count = 0;
     733           0 :   txn_context_msg->account_shared_data = fd_spad_alloc( spad,
     734           0 :                                                         alignof(fd_exec_test_acct_state_t),
     735           0 :                                                         (txn_ctx->accounts_cnt * 2 + txn_descriptor->addr_table_lookup_cnt + num_sysvar_entries) * sizeof(fd_exec_test_acct_state_t) );
     736           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; ++i ) {
     737           0 :     FD_TXN_ACCOUNT_DECL( txn_account );
     738           0 :     int ret = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, &txn_ctx->account_keys[i], txn_account );
     739           0 :     if( FD_UNLIKELY(ret != FD_ACC_MGR_SUCCESS) ) {
     740           0 :       continue;
     741           0 :     }
     742             : 
     743             :     // Make sure account is not a builtin
     744           0 :     bool is_builtin = false;
     745           0 :     for( ulong j = 0; j < num_loaded_builtins; ++j ) {
     746           0 :       if( 0 == memcmp( &txn_ctx->account_keys[i], &loaded_builtins[j], sizeof(fd_pubkey_t) ) ) {
     747           0 :         is_builtin = true;
     748           0 :         break;
     749           0 :       }
     750           0 :     }
     751           0 :     if( !is_builtin ) {
     752           0 :       dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     753           0 :     }
     754           0 :   }
     755             : 
     756             :   // For executable accounts, we need to set up dummy borrowed accounts by cluttering txn ctx state and resetting it after
     757             :   // TODO: Revisit this hacky approach
     758           0 :   txn_ctx->spad = spad;
     759           0 :   fd_spad_push( txn_ctx->spad );
     760           0 :   fd_executor_setup_borrowed_accounts_for_txn( txn_ctx );
     761             : 
     762             :   // Dump executable accounts
     763           0 :   for( ulong i = 0; i < txn_ctx->executable_cnt; ++i ) {
     764           0 :     if( !txn_ctx->executable_accounts[i].const_meta ) {
     765           0 :       continue;
     766           0 :     }
     767           0 :     dump_account_state( &txn_ctx->executable_accounts[i], &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     768           0 :   }
     769             : 
     770             :   // Reset state
     771           0 :   txn_ctx->funk_txn = NULL;
     772           0 :   txn_ctx->executable_cnt = 0;
     773           0 :   fd_spad_pop( txn_ctx->spad );
     774             : 
     775             :   // Dump LUT accounts
     776           0 :   fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     777           0 :   for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
     778           0 :     FD_TXN_ACCOUNT_DECL( txn_account );
     779           0 :     fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + address_lookup_tables[i].addr_off);
     780           0 :     int ret = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, alut_key, txn_account );
     781           0 :     if( FD_UNLIKELY(ret != FD_ACC_MGR_SUCCESS) ) {
     782           0 :       continue;
     783           0 :     }
     784           0 :     dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     785           0 :   }
     786             : 
     787             :   // Dump sysvars
     788           0 :   for( ulong i = 0; i < num_sysvar_entries; i++ ) {
     789           0 :     FD_TXN_ACCOUNT_DECL( txn_account );
     790           0 :     int ret = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, &fd_relevant_sysvar_ids[i], txn_account );
     791           0 :     if( ret != FD_ACC_MGR_SUCCESS ) {
     792           0 :       continue;
     793           0 :     }
     794             : 
     795             :     // Make sure the account doesn't exist in the output accounts yet
     796           0 :     int account_exists = 0;
     797           0 :     for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) {
     798           0 :       if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_relevant_sysvar_ids[i].uc, sizeof(fd_pubkey_t) ) ) {
     799           0 :         account_exists = true;
     800           0 :         break;
     801           0 :       }
     802           0 :     }
     803             :     // Copy it into output
     804           0 :     if (!account_exists) {
     805           0 :       dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
     806           0 :     }
     807           0 :   }
     808             : 
     809             :   /* Transaction Context -> tx */
     810           0 :   txn_context_msg->has_tx = true;
     811           0 :   fd_exec_test_sanitized_transaction_t * sanitized_transaction = &txn_context_msg->tx;
     812           0 :   dump_sanitized_transaction( txn_ctx->acc_mgr, txn_ctx->funk_txn, txn_descriptor, txn_payload, spad, sanitized_transaction );
     813             : 
     814             :   /* Transaction Context -> blockhash_queue
     815             :      NOTE: Agave's implementation of register_hash incorrectly allows the blockhash queue to hold max_age + 1 (max 301)
     816             :      entries. We have this incorrect logic implemented in fd_sysvar_recent_hashes:register_blockhash and it's not a
     817             :      huge issue, but something to keep in mind. */
     818           0 :   pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc(
     819           0 :                                                       spad,
     820           0 :                                                       alignof(pb_bytes_array_t *),
     821           0 :                                                       PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASH_QUEUE_MAX_ENTRIES + 1) * sizeof(pb_bytes_array_t *)) );
     822           0 :   txn_context_msg->blockhash_queue = output_blockhash_queue;
     823           0 :   dump_blockhash_queue( &txn_ctx->block_hash_queue, spad, output_blockhash_queue, &txn_context_msg->blockhash_queue_count );
     824             : 
     825             :   /* Transaction Context -> epoch_ctx */
     826           0 :   txn_context_msg->has_epoch_ctx = true;
     827           0 :   txn_context_msg->epoch_ctx.has_features = true;
     828           0 :   dump_sorted_features( &txn_ctx->features, &txn_context_msg->epoch_ctx.features, spad );
     829             : 
     830             :   /* Transaction Context -> slot_ctx */
     831           0 :   txn_context_msg->has_slot_ctx  = true;
     832           0 :   txn_context_msg->slot_ctx.slot = txn_ctx->slot;
     833           0 : }
     834             : 
     835             : static void
     836             : create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * instr_context,
     837             :                                                  fd_exec_txn_ctx_t const *      txn_ctx,
     838           0 :                                                  fd_instr_info_t const *        instr ) {
     839             :   /* Prepare sysvar cache accounts */
     840           0 :   fd_pubkey_t const fd_relevant_sysvar_ids[] = {
     841           0 :     fd_sysvar_recent_block_hashes_id,
     842           0 :     fd_sysvar_clock_id,
     843           0 :     fd_sysvar_slot_history_id,
     844           0 :     fd_sysvar_slot_hashes_id,
     845           0 :     fd_sysvar_epoch_schedule_id,
     846           0 :     fd_sysvar_epoch_rewards_id,
     847           0 :     fd_sysvar_fees_id,
     848           0 :     fd_sysvar_rent_id,
     849           0 :     fd_sysvar_stake_history_id,
     850           0 :     fd_sysvar_last_restart_slot_id,
     851           0 :     fd_sysvar_instructions_id,
     852           0 :   };
     853           0 :   const ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t));
     854             : 
     855             :   /* Program ID */
     856           0 :   fd_memcpy( instr_context->program_id, instr->program_id_pubkey.uc, sizeof(fd_pubkey_t) );
     857             : 
     858             :   /* Accounts */
     859           0 :   instr_context->accounts_count = (pb_size_t) txn_ctx->accounts_cnt;
     860           0 :   instr_context->accounts = fd_spad_alloc( txn_ctx->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));
     861           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
     862             :     // Copy account information over
     863           0 :     fd_txn_account_t const *    txn_account    = &txn_ctx->accounts[i];
     864           0 :     fd_exec_test_acct_state_t * output_account = &instr_context->accounts[i];
     865           0 :     dump_account_state( txn_account, output_account, txn_ctx->spad );
     866           0 :   }
     867             : 
     868             :   /* Add sysvar cache variables */
     869           0 :   for( ulong i = 0; i < num_sysvar_entries; i++ ) {
     870           0 :     FD_TXN_ACCOUNT_DECL( txn_account );
     871           0 :     int ret = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, &fd_relevant_sysvar_ids[i], txn_account );
     872           0 :     if( ret != FD_ACC_MGR_SUCCESS ) {
     873           0 :       continue;
     874           0 :     }
     875             :     // Make sure the account doesn't exist in the output accounts yet
     876           0 :     int account_exists = 0;
     877           0 :     for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) {
     878           0 :       if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_relevant_sysvar_ids[i].uc, sizeof(fd_pubkey_t) ) ) {
     879           0 :         account_exists = true;
     880           0 :         break;
     881           0 :       }
     882           0 :     }
     883             : 
     884             :     // Copy it into output
     885           0 :     if (!account_exists) {
     886           0 :       fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
     887           0 :       dump_account_state( txn_account, output_account, txn_ctx->spad );
     888           0 :     }
     889           0 :   }
     890             : 
     891             :   /* Add executable accounts */
     892           0 :   for( ulong i = 0; i < txn_ctx->executable_cnt; i++ ) {
     893           0 :     FD_TXN_ACCOUNT_DECL( txn_account );
     894           0 :     int ret = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, txn_ctx->executable_accounts[i].pubkey, txn_account );
     895           0 :     if( ret != FD_ACC_MGR_SUCCESS ) {
     896           0 :       continue;
     897           0 :     }
     898             :     // Make sure the account doesn't exist in the output accounts yet
     899           0 :     bool account_exists = false;
     900           0 :     for( ulong j = 0; j < instr_context->accounts_count; j++ ) {
     901           0 :       if( 0 == memcmp( instr_context->accounts[j].address, txn_ctx->executable_accounts[i].pubkey->uc, sizeof(fd_pubkey_t) ) ) {
     902           0 :         account_exists = true;
     903           0 :         break;
     904           0 :       }
     905           0 :     }
     906             :     // Copy it into output
     907           0 :     if( !account_exists ) {
     908           0 :       fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
     909           0 :       dump_account_state( txn_account, output_account, txn_ctx->spad );
     910           0 :     }
     911           0 :   }
     912             : 
     913             :   /* Instruction Accounts */
     914           0 :   instr_context->instr_accounts_count = (pb_size_t) instr->acct_cnt;
     915           0 :   instr_context->instr_accounts = fd_spad_alloc( txn_ctx->spad, alignof(fd_exec_test_instr_acct_t), instr_context->instr_accounts_count * sizeof(fd_exec_test_instr_acct_t) );
     916           0 :   for( ushort i = 0; i < instr->acct_cnt; i++ ) {
     917           0 :     fd_exec_test_instr_acct_t * output_instr_account = &instr_context->instr_accounts[i];
     918             : 
     919           0 :     uchar account_flag = instr->acct_flags[i];
     920           0 :     bool is_writable = account_flag & FD_INSTR_ACCT_FLAGS_IS_WRITABLE;
     921           0 :     bool is_signer = account_flag & FD_INSTR_ACCT_FLAGS_IS_SIGNER;
     922             : 
     923           0 :     output_instr_account->index = instr->acct_txn_idxs[i];
     924           0 :     output_instr_account->is_writable = is_writable;
     925           0 :     output_instr_account->is_signer = is_signer;
     926           0 :   }
     927             : 
     928             :   /* Data */
     929           0 :   instr_context->data = fd_spad_alloc( txn_ctx->spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( instr->data_sz ) );
     930           0 :   instr_context->data->size = (pb_size_t) instr->data_sz;
     931           0 :   fd_memcpy( instr_context->data->bytes, instr->data, instr->data_sz );
     932             : 
     933             :   /* Compute Units */
     934           0 :   instr_context->cu_avail = txn_ctx->compute_meter;
     935             : 
     936             :   /* Slot Context */
     937           0 :   instr_context->has_slot_context = true;
     938             : 
     939             :   /* Epoch Context */
     940           0 :   instr_context->has_epoch_context = true;
     941           0 :   instr_context->epoch_context.has_features = true;
     942           0 :   dump_sorted_features( &txn_ctx->features, &instr_context->epoch_context.features, txn_ctx->spad );
     943           0 : }
     944             : 
     945             : /***** PUBLIC APIs *****/
     946             : 
     947             : void
     948             : fd_dump_instr_to_protobuf( fd_exec_txn_ctx_t * txn_ctx,
     949             :                            fd_instr_info_t *   instr,
     950           0 :                            ushort              instruction_idx ) {
     951           0 :   FD_SPAD_FRAME_BEGIN( txn_ctx->spad ) {
     952             :     // Get base58-encoded tx signature
     953           0 :     const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
     954           0 :     fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
     955           0 :     char encoded_signature[FD_BASE58_ENCODED_64_SZ];
     956           0 :     ulong out_size;
     957           0 :     fd_base58_encode_64( signature, &out_size, encoded_signature );
     958             : 
     959           0 :     if (txn_ctx->capture_ctx->dump_proto_sig_filter) {
     960           0 :       ulong filter_strlen = (ulong) strlen(txn_ctx->capture_ctx->dump_proto_sig_filter);
     961             : 
     962             :       // Terminate early if the signature does not match
     963           0 :       if( memcmp( txn_ctx->capture_ctx->dump_proto_sig_filter, encoded_signature, filter_strlen < out_size ? filter_strlen : out_size ) ) {
     964           0 :         return;
     965           0 :       }
     966           0 :     }
     967             : 
     968           0 :     fd_exec_test_instr_context_t instr_context = FD_EXEC_TEST_INSTR_CONTEXT_INIT_DEFAULT;
     969           0 :     create_instr_context_protobuf_from_instructions( &instr_context, txn_ctx, instr );
     970             : 
     971             :     /* Output to file */
     972           0 :     ulong out_buf_size = 100 * 1024 * 1024;
     973           0 :     uint8_t * out = fd_spad_alloc( txn_ctx->spad, alignof(uchar) , out_buf_size );
     974           0 :     pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
     975           0 :     if (pb_encode(&stream, FD_EXEC_TEST_INSTR_CONTEXT_FIELDS, &instr_context)) {
     976           0 :       char output_filepath[256]; fd_memset(output_filepath, 0, sizeof(output_filepath));
     977           0 :       char * position = fd_cstr_init(output_filepath);
     978           0 :       position = fd_cstr_append_cstr(position, txn_ctx->capture_ctx->dump_proto_output_dir);
     979           0 :       position = fd_cstr_append_cstr(position, "/instr-");
     980           0 :       position = fd_cstr_append_cstr(position, encoded_signature);
     981           0 :       position = fd_cstr_append_cstr(position, "-");
     982           0 :       position = fd_cstr_append_ushort_as_text(position, '0', 0, instruction_idx, 3); // Assume max 3 digits
     983           0 :       position = fd_cstr_append_cstr(position, ".bin");
     984           0 :       fd_cstr_fini(position);
     985             : 
     986           0 :       FILE * file = fopen(output_filepath, "wb");
     987           0 :       if( file ) {
     988           0 :         fwrite( out, 1, stream.bytes_written, file );
     989           0 :         fclose( file );
     990           0 :       }
     991           0 :     }
     992           0 :   } FD_SPAD_FRAME_END;
     993           0 : }
     994             : 
     995             : void
     996           0 : fd_dump_txn_to_protobuf( fd_exec_txn_ctx_t * txn_ctx, fd_spad_t * spad ) {
     997           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
     998             :     // Get base58-encoded tx signature
     999           0 :     const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
    1000           0 :     fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
    1001           0 :     char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1002           0 :     ulong out_size;
    1003           0 :     fd_base58_encode_64( signature, &out_size, encoded_signature );
    1004             : 
    1005           0 :     if( txn_ctx->capture_ctx->dump_proto_sig_filter ) {
    1006             :       // Terminate early if the signature does not match
    1007           0 :       if( strcmp( txn_ctx->capture_ctx->dump_proto_sig_filter, encoded_signature ) ) {
    1008           0 :         return;
    1009           0 :       }
    1010           0 :     }
    1011             : 
    1012           0 :     fd_exec_test_txn_context_t txn_context_msg = FD_EXEC_TEST_TXN_CONTEXT_INIT_DEFAULT;
    1013           0 :     create_txn_context_protobuf_from_txn( &txn_context_msg, txn_ctx, spad );
    1014             : 
    1015             :     /* Output to file */
    1016           0 :     ulong out_buf_size = 100 * 1024 * 1024;
    1017           0 :     uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
    1018           0 :     pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
    1019           0 :     if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_context_msg ) ) {
    1020           0 :       char output_filepath[256]; fd_memset( output_filepath, 0, sizeof(output_filepath) );
    1021           0 :       char * position = fd_cstr_init( output_filepath );
    1022           0 :       position = fd_cstr_append_cstr( position, txn_ctx->capture_ctx->dump_proto_output_dir );
    1023           0 :       position = fd_cstr_append_cstr( position, "/txn-" );
    1024           0 :       position = fd_cstr_append_cstr( position, encoded_signature );
    1025           0 :       position = fd_cstr_append_cstr(position, ".bin");
    1026           0 :       fd_cstr_fini(position);
    1027             : 
    1028           0 :       FILE * file = fopen(output_filepath, "wb");
    1029           0 :       if( file ) {
    1030           0 :         fwrite( out, 1, stream.bytes_written, file );
    1031           0 :         fclose( file );
    1032           0 :       }
    1033           0 :     }
    1034           0 :   } FD_SPAD_FRAME_END;
    1035           0 : }
    1036             : 
    1037             : void fd_dump_block_to_protobuf( fd_exec_slot_ctx_t const *     slot_ctx,
    1038             :                                 fd_capture_ctx_t const *       capture_ctx,
    1039             :                                 fd_spad_t *                    spad,
    1040           0 :                                 fd_exec_test_block_context_t * block_context_msg /* output */ ) {
    1041             :   /* No spad frame because these allocations must persist beyond the lifetime of this function call */
    1042           0 :   if( FD_UNLIKELY( capture_ctx==NULL ) ) {
    1043           0 :     FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." ));
    1044           0 :     return;
    1045           0 :   }
    1046           0 :   create_block_context_protobuf_from_block( block_context_msg, slot_ctx, spad );
    1047           0 : }
    1048             : 
    1049             : void
    1050             : fd_dump_block_to_protobuf_tx_only( fd_runtime_block_info_t const * block_info,
    1051             :                                    fd_exec_slot_ctx_t const *      slot_ctx,
    1052             :                                    fd_capture_ctx_t const *        capture_ctx,
    1053             :                                    fd_spad_t *                     spad,
    1054           0 :                                    fd_exec_test_block_context_t *  block_context_msg ) {
    1055           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1056           0 :     if( FD_UNLIKELY( capture_ctx==NULL ) ) {
    1057           0 :       FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." ));
    1058           0 :       return;
    1059           0 :     }
    1060             : 
    1061           0 :     if( FD_UNLIKELY( block_info==NULL ) ) {
    1062           0 :       FD_LOG_WARNING(( "Block info may not be NULL when dumping blocks." ));
    1063           0 :       return;
    1064           0 :     }
    1065             : 
    1066           0 :     create_block_context_protobuf_from_block_tx_only( block_context_msg, block_info, slot_ctx, spad );
    1067             : 
    1068             :     /* Output to file */
    1069           0 :     ulong out_buf_size = 3UL<<30UL; /* 3 GB */
    1070           0 :     uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
    1071           0 :     pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
    1072           0 :     if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, block_context_msg ) ) {
    1073           0 :       char output_filepath[256]; fd_memset( output_filepath, 0, sizeof(output_filepath) );
    1074           0 :       char * position = fd_cstr_init( output_filepath );
    1075           0 :       position = fd_cstr_append_printf( position, "%s/block-%lu.bin", capture_ctx->dump_proto_output_dir, slot_ctx->slot_bank.slot );
    1076           0 :       fd_cstr_fini( position );
    1077             : 
    1078           0 :       FILE * file = fopen(output_filepath, "wb");
    1079           0 :       if( file ) {
    1080           0 :         fwrite( out, 1, stream.bytes_written, file );
    1081           0 :         fclose( file );
    1082           0 :       }
    1083           0 :     }
    1084           0 :   } FD_SPAD_FRAME_END;
    1085           0 : }
    1086             : 
    1087             : void
    1088             : fd_dump_vm_cpi_state( fd_vm_t *    vm,
    1089             :                       char const * fn_name,
    1090             :                       ulong        instruction_va,
    1091             :                       ulong        acct_infos_va,
    1092             :                       ulong        acct_info_cnt,
    1093             :                       ulong        signers_seeds_va,
    1094           0 :                       ulong        signers_seeds_cnt ) {
    1095           0 :   char filename[100];
    1096           0 :   fd_instr_info_t const *instr = vm->instr_ctx->instr;
    1097           0 :   sprintf(filename, "vm_cpi_state/%lu_%lu%lu_%hu.sysctx", fd_tile_id(), instr->program_id_pubkey.ul[0], instr->program_id_pubkey.ul[1], instr->data_sz);
    1098             : 
    1099             :   // Check if file exists
    1100           0 :   if( access (filename, F_OK) != -1 ) {
    1101           0 :     return;
    1102           0 :   }
    1103             : 
    1104           0 :   fd_exec_test_syscall_context_t sys_ctx = FD_EXEC_TEST_SYSCALL_CONTEXT_INIT_ZERO;
    1105           0 :   sys_ctx.has_instr_ctx = 1;
    1106           0 :   sys_ctx.has_vm_ctx = 1;
    1107           0 :   sys_ctx.has_syscall_invocation = 1;
    1108             : 
    1109             :   // Copy function name
    1110           0 :   sys_ctx.syscall_invocation.function_name.size = fd_uint_min( (uint) strlen(fn_name), sizeof(sys_ctx.syscall_invocation.function_name.bytes) );
    1111           0 :   fd_memcpy( sys_ctx.syscall_invocation.function_name.bytes,
    1112           0 :              fn_name,
    1113           0 :              sys_ctx.syscall_invocation.function_name.size );
    1114             : 
    1115             :   // VM Ctx integral fields
    1116           0 :   sys_ctx.vm_ctx.r1 = instruction_va;
    1117           0 :   sys_ctx.vm_ctx.r2 = acct_infos_va;
    1118           0 :   sys_ctx.vm_ctx.r3 = acct_info_cnt;
    1119           0 :   sys_ctx.vm_ctx.r4 = signers_seeds_va;
    1120           0 :   sys_ctx.vm_ctx.r5 = signers_seeds_cnt;
    1121             : 
    1122           0 :   sys_ctx.vm_ctx.rodata_text_section_length = vm->text_sz;
    1123           0 :   sys_ctx.vm_ctx.rodata_text_section_offset = vm->text_off;
    1124             : 
    1125           0 :   sys_ctx.vm_ctx.heap_max = vm->heap_max; /* should be equiv. to txn_ctx->heap_sz */
    1126             : 
    1127           0 :   FD_SPAD_FRAME_BEGIN( vm->instr_ctx->txn_ctx->spad ) {
    1128           0 :     sys_ctx.vm_ctx.rodata = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( vm->rodata_sz ) );
    1129           0 :     sys_ctx.vm_ctx.rodata->size = (pb_size_t) vm->rodata_sz;
    1130           0 :     fd_memcpy( sys_ctx.vm_ctx.rodata->bytes, vm->rodata, vm->rodata_sz );
    1131             : 
    1132           0 :     pb_size_t stack_sz = (pb_size_t) ( (vm->frame_cnt + 1)*FD_VM_STACK_GUARD_SZ*2 );
    1133           0 :     sys_ctx.syscall_invocation.stack_prefix = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( stack_sz ) );
    1134           0 :     sys_ctx.syscall_invocation.stack_prefix->size = stack_sz;
    1135           0 :     fd_memcpy( sys_ctx.syscall_invocation.stack_prefix->bytes, vm->stack, stack_sz );
    1136             : 
    1137           0 :     sys_ctx.syscall_invocation.heap_prefix = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
    1138           0 :     sys_ctx.syscall_invocation.heap_prefix->size = (pb_size_t) vm->instr_ctx->txn_ctx->heap_size;
    1139           0 :     fd_memcpy( sys_ctx.syscall_invocation.heap_prefix->bytes, vm->heap, vm->instr_ctx->txn_ctx->heap_size );
    1140             : 
    1141           0 :     create_instr_context_protobuf_from_instructions( &sys_ctx.instr_ctx,
    1142           0 :                                                         vm->instr_ctx->txn_ctx,
    1143           0 :                                                         vm->instr_ctx->instr );
    1144             : 
    1145             :     // Serialize the protobuf to file (using mmap)
    1146           0 :     size_t pb_alloc_size = 100 * 1024 * 1024; // 100MB (largest so far is 19MB)
    1147           0 :     FILE *f = fopen(filename, "wb+");
    1148           0 :     if( ftruncate(fileno(f), (off_t) pb_alloc_size) != 0 ) {
    1149           0 :       FD_LOG_WARNING(("Failed to resize file %s", filename));
    1150           0 :       fclose(f);
    1151           0 :       return;
    1152           0 :     }
    1153             : 
    1154           0 :     uchar *pb_alloc = mmap( NULL,
    1155           0 :                             pb_alloc_size,
    1156           0 :                             PROT_READ | PROT_WRITE,
    1157           0 :                             MAP_SHARED,
    1158           0 :                             fileno(f),
    1159           0 :                             0 /* offset */);
    1160           0 :     if( pb_alloc == MAP_FAILED ) {
    1161           0 :       FD_LOG_WARNING(( "Failed to mmap file %d", errno ));
    1162           0 :       fclose(f);
    1163           0 :       return;
    1164           0 :     }
    1165             : 
    1166           0 :     pb_ostream_t stream = pb_ostream_from_buffer(pb_alloc, pb_alloc_size);
    1167           0 :     if( !pb_encode( &stream, FD_EXEC_TEST_SYSCALL_CONTEXT_FIELDS, &sys_ctx ) ) {
    1168           0 :       FD_LOG_WARNING(( "Failed to encode instruction context" ));
    1169           0 :     }
    1170             :     // resize file to actual size
    1171           0 :     if( ftruncate( fileno(f), (off_t) stream.bytes_written ) != 0 ) {
    1172           0 :       FD_LOG_WARNING(( "Failed to resize file %s", filename ));
    1173           0 :     }
    1174             : 
    1175           0 :     fclose(f);
    1176             : 
    1177           0 :   } FD_SPAD_FRAME_END;
    1178           0 : }

Generated by: LCOV version 1.14