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 841 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 20 0.0 %

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

Generated by: LCOV version 1.14