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 883 0.0 %
Date: 2026-03-05 05:37:44 Functions: 0 25 0.0 %

          Line data    Source code
       1             : #include "fd_dump_pb.h"
       2             : #include "generated/block.pb.h"
       3             : #include "generated/invoke.pb.h"
       4             : #include "generated/txn.pb.h"
       5             : #include "generated/vm.pb.h"
       6             : #include "../fd_system_ids.h"
       7             : #include "../fd_bank.h"
       8             : #include "../fd_runtime.h"
       9             : #include "../program/fd_precompiles.h"
      10             : #include "../program/fd_address_lookup_table_program.h"
      11             : #include "../../../ballet/nanopb/pb_encode.h"
      12             : #include "../../accdb/fd_accdb_sync.h"
      13             : #include "../../progcache/fd_prog_load.h"
      14             : 
      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             : struct fd_dump_account_key_node {
      25             :   fd_pubkey_t key;
      26             :   ulong       redblack_parent;
      27             :   ulong       redblack_left;
      28             :   ulong       redblack_right;
      29             :   int         redblack_color;
      30             : };
      31             : typedef struct fd_dump_account_key_node fd_dump_account_key_node_t;
      32           0 : #define REDBLK_T fd_dump_account_key_node_t
      33             : #define REDBLK_NAME fd_dump_account_key_map
      34           0 : long fd_dump_account_key_map_compare( fd_dump_account_key_node_t * left, fd_dump_account_key_node_t * right ) {
      35           0 :   return memcmp( left->key.uc, right->key.uc, sizeof(fd_pubkey_t) );
      36           0 : }
      37             : #include "../../../util/tmpl/fd_redblack.c"
      38             : 
      39             : /***** CONSTANTS *****/
      40             : static fd_pubkey_t const * fd_dump_sysvar_ids[] = {
      41             :   &fd_sysvar_recent_block_hashes_id,
      42             :   &fd_sysvar_clock_id,
      43             :   &fd_sysvar_slot_history_id,
      44             :   &fd_sysvar_slot_hashes_id,
      45             :   &fd_sysvar_epoch_schedule_id,
      46             :   &fd_sysvar_epoch_rewards_id,
      47             :   &fd_sysvar_fees_id,
      48             :   &fd_sysvar_rent_id,
      49             :   &fd_sysvar_stake_history_id,
      50             :   &fd_sysvar_last_restart_slot_id,
      51             :   &fd_sysvar_instructions_id,
      52             : };
      53             : static ulong const num_sysvar_entries = (sizeof(fd_dump_sysvar_ids) / sizeof(fd_pubkey_t *));
      54             : 
      55             : static fd_pubkey_t const * fd_dump_builtin_ids[] = {
      56             :   &fd_solana_system_program_id,
      57             :   &fd_solana_vote_program_id,
      58             :   &fd_solana_stake_program_id,
      59             :   &fd_solana_bpf_loader_v4_program_id,
      60             :   &fd_solana_bpf_loader_deprecated_program_id,
      61             :   &fd_solana_bpf_loader_program_id,
      62             :   &fd_solana_bpf_loader_upgradeable_program_id,
      63             :   &fd_solana_compute_budget_program_id,
      64             :   &fd_solana_keccak_secp_256k_program_id,
      65             :   &fd_solana_secp256r1_program_id,
      66             :   &fd_solana_zk_elgamal_proof_program_id,
      67             :   &fd_solana_ed25519_sig_verify_program_id,
      68             : };
      69             : static ulong const num_loaded_builtins = (sizeof(fd_dump_builtin_ids) / sizeof(fd_pubkey_t *));
      70             : 
      71             : /***** UTILITY FUNCTIONS *****/
      72             : 
      73             : /** FEATURE DUMPING **/
      74             : static void
      75             : dump_sorted_features( fd_features_t const *        features,
      76             :                       fd_exec_test_feature_set_t * output_feature_set,
      77           0 :                       fd_spad_t *                  spad ) {
      78             :   /* NOTE: Caller must have a spad frame prepared */
      79           0 :   uint64_t * unsorted_features = fd_spad_alloc( spad, alignof(uint64_t), FD_FEATURE_ID_CNT * sizeof(uint64_t) );
      80           0 :   ulong num_features = 0;
      81           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 ) ) {
      82           0 :     if (features->f[current_feature->index] != FD_FEATURE_DISABLED) {
      83           0 :       unsorted_features[num_features++] = (uint64_t) current_feature->id.ul[0];
      84           0 :     }
      85           0 :   }
      86             :   // Sort the features
      87           0 :   void * scratch = fd_spad_alloc( spad, sort_uint64_t_stable_scratch_align(), sort_uint64_t_stable_scratch_footprint(num_features) );
      88           0 :   uint64_t * sorted_features = sort_uint64_t_stable_fast( unsorted_features, num_features, scratch );
      89             : 
      90             :   // Set feature set in message
      91           0 :   output_feature_set->features_count = (pb_size_t) num_features;
      92           0 :   output_feature_set->features       = sorted_features;
      93           0 : }
      94             : 
      95             : /** ACCOUNT DUMPING **/
      96             : static void
      97             : dump_account_state( fd_pubkey_t const *         account_key,
      98             :                     fd_account_meta_t const *   account_meta,
      99             :                     fd_exec_test_acct_state_t * output_account,
     100           0 :                     fd_spad_t *                 spad ) {
     101             :     // Address
     102           0 :     fd_memcpy(output_account->address, account_key, sizeof(fd_pubkey_t));
     103             : 
     104             :     // Lamports
     105           0 :     output_account->lamports = (uint64_t)account_meta->lamports;
     106             : 
     107             :     // Data
     108           0 :     output_account->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( account_meta->dlen ) );
     109           0 :     output_account->data->size = (pb_size_t) account_meta->dlen;
     110           0 :     fd_memcpy(output_account->data->bytes, fd_account_data( account_meta ), account_meta->dlen );
     111             : 
     112             :     // Executable
     113           0 :     output_account->executable = (bool)account_meta->executable;
     114             : 
     115             :     // Owner
     116           0 :     fd_memcpy(output_account->owner, account_meta->owner, sizeof(fd_pubkey_t));
     117           0 : }
     118             : 
     119             : static uchar
     120             : account_already_dumped( fd_exec_test_acct_state_t const * dumped_accounts,
     121             :                         ulong                             dumped_cnt,
     122           0 :                         fd_pubkey_t const *               account_key ) {
     123           0 :   for( ulong i=0UL; i<dumped_cnt; i++ ) {
     124           0 :     if( !memcmp( account_key, dumped_accounts[i].address, sizeof(fd_pubkey_t) ) ) {
     125           0 :       return 1;
     126           0 :     }
     127           0 :   }
     128           0 :   return 0;
     129           0 : }
     130             : 
     131             : /* Dumps a borrowed account if it exists and has not been dumped yet.
     132             :    Sets up the output borrowed account if it exists. Returns 0 if the
     133             :    account exists, 1 otherwise.
     134             :    TODO: This can be optimized by using a set. */
     135             : static uchar
     136             : dump_account_if_not_already_dumped( fd_accdb_user_t *           accdb,
     137             :                                     fd_funk_txn_xid_t const *   xid,
     138             :                                     fd_pubkey_t const *         account_key,
     139             :                                     fd_spad_t *                 spad,
     140             :                                     fd_exec_test_acct_state_t * out_acct_states,
     141             :                                     pb_size_t *                 out_acct_states_cnt,
     142           0 :                                     fd_accdb_ro_t *             out_ro ) {
     143           0 :   fd_accdb_ro_t ro[1];
     144           0 :   if( !fd_accdb_open_ro( accdb, ro, xid, account_key ) ) {
     145           0 :     return 1;
     146           0 :   }
     147             : 
     148           0 :   if( !account_already_dumped( out_acct_states, *out_acct_states_cnt, account_key ) ) {
     149           0 :     dump_account_state( account_key, ro->meta, &out_acct_states[*out_acct_states_cnt], spad );
     150           0 :     (*out_acct_states_cnt)++;
     151           0 :   }
     152             : 
     153           0 :   if( out_ro ) {
     154           0 :     *out_ro = *ro;
     155           0 :   } else {
     156           0 :     fd_accdb_close_ro( accdb, ro );
     157           0 :   }
     158           0 :   return 0;
     159           0 : }
     160             : 
     161             : static void
     162             : dump_executable_account_if_exists( fd_accdb_user_t *                 accdb,
     163             :                                    fd_funk_txn_xid_t const *         xid,
     164             :                                    fd_exec_test_acct_state_t const * program_account,
     165             :                                    fd_spad_t *                       spad,
     166             :                                    fd_exec_test_acct_state_t *       out_account_states,
     167           0 :                                    pb_size_t *                       out_account_states_count ) {
     168           0 :   if( FD_LIKELY( memcmp( program_account->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     169           0 :     return;
     170           0 :   }
     171             : 
     172           0 :   int err;
     173           0 :   fd_bpf_upgradeable_loader_state_t * program_loader_state = fd_bincode_decode_spad(
     174           0 :       bpf_upgradeable_loader_state,
     175           0 :       spad,
     176           0 :       program_account->data->bytes,
     177           0 :       program_account->data->size,
     178           0 :       &err );
     179           0 :   if( FD_UNLIKELY( err ) ) return;
     180             : 
     181           0 :   if( !fd_bpf_upgradeable_loader_state_is_program( program_loader_state ) ) {
     182           0 :     return;
     183           0 :   }
     184             : 
     185           0 :   fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
     186           0 :   dump_account_if_not_already_dumped( accdb, xid, programdata_acc, spad, out_account_states, out_account_states_count, NULL );
     187           0 : }
     188             : 
     189             : static void
     190             : dump_sanitized_transaction( fd_accdb_user_t *                      accdb,
     191             :                             fd_funk_txn_xid_t const *              xid,
     192             :                             fd_txn_t const *                       txn_descriptor,
     193             :                             uchar const *                          txn_payload,
     194             :                             fd_spad_t *                            spad,
     195           0 :                             fd_exec_test_sanitized_transaction_t * sanitized_transaction ) {
     196           0 :   fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     197             : 
     198             :   /* Transaction Context -> tx -> message */
     199           0 :   sanitized_transaction->has_message = true;
     200           0 :   fd_exec_test_transaction_message_t * message = &sanitized_transaction->message;
     201             : 
     202             :   /* Transaction Context -> tx -> message -> is_legacy */
     203           0 :   message->is_legacy = txn_descriptor->transaction_version == FD_TXN_VLEGACY;
     204             : 
     205             :   /* Transaction Context -> tx -> message -> header */
     206           0 :   message->has_header = true;
     207           0 :   fd_exec_test_message_header_t * header = &message->header;
     208             : 
     209             :   /* Transaction Context -> tx -> message -> header -> num_required_signatures */
     210           0 :   header->num_required_signatures = txn_descriptor->signature_cnt;
     211             : 
     212             :   /* Transaction Context -> tx -> message -> header -> num_readonly_signed_accounts */
     213           0 :   header->num_readonly_signed_accounts = txn_descriptor->readonly_signed_cnt;
     214             : 
     215             :   /* Transaction Context -> tx -> message -> header -> num_readonly_unsigned_accounts */
     216           0 :   header->num_readonly_unsigned_accounts = txn_descriptor->readonly_unsigned_cnt;
     217             : 
     218             :   /* Transaction Context -> tx -> message -> account_keys */
     219           0 :   message->account_keys_count = txn_descriptor->acct_addr_cnt;
     220           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 *)) );
     221           0 :   fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_payload );
     222           0 :   for( ulong i = 0; i < txn_descriptor->acct_addr_cnt; i++ ) {
     223           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)) );
     224           0 :     account_key->size = sizeof(fd_pubkey_t);
     225           0 :     memcpy( account_key->bytes, &account_keys[i], sizeof(fd_pubkey_t) );
     226           0 :     message->account_keys[i] = account_key;
     227           0 :   }
     228             : 
     229             :   /* Transaction Context -> tx -> message -> recent_blockhash */
     230           0 :   uchar const * recent_blockhash = fd_txn_get_recent_blockhash( txn_descriptor, txn_payload );
     231           0 :   message->recent_blockhash = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_hash_t)) );
     232           0 :   message->recent_blockhash->size = sizeof(fd_hash_t);
     233           0 :   memcpy( message->recent_blockhash->bytes, recent_blockhash, sizeof(fd_hash_t) );
     234             : 
     235             :   /* Transaction Context -> tx -> message -> instructions */
     236           0 :   message->instructions_count = txn_descriptor->instr_cnt;
     237           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) );
     238           0 :   for( ulong i = 0; i < txn_descriptor->instr_cnt; ++i ) {
     239           0 :     fd_txn_instr_t instr = txn_descriptor->instr[i];
     240           0 :     fd_exec_test_compiled_instruction_t * compiled_instruction = &message->instructions[i];
     241             : 
     242             :     // compiled instruction -> program_id_index
     243           0 :     compiled_instruction->program_id_index = instr.program_id;
     244             : 
     245             :     // compiled instruction -> accounts
     246           0 :     compiled_instruction->accounts_count = instr.acct_cnt;
     247           0 :     compiled_instruction->accounts = fd_spad_alloc( spad, alignof(uint32_t), instr.acct_cnt * sizeof(uint32_t) );
     248           0 :     uchar const * instr_accounts = fd_txn_get_instr_accts( &instr, txn_payload );
     249           0 :     for( ulong j = 0; j < instr.acct_cnt; ++j ) {
     250           0 :       uchar instr_acct_index = instr_accounts[j];
     251           0 :       compiled_instruction->accounts[j] = instr_acct_index;
     252           0 :     }
     253             : 
     254             :     // compiled instruction -> data
     255           0 :     uchar const * instr_data = fd_txn_get_instr_data( &instr, txn_payload );
     256           0 :     compiled_instruction->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(instr.data_sz) );
     257           0 :     compiled_instruction->data->size = instr.data_sz;
     258           0 :     memcpy( compiled_instruction->data->bytes, instr_data, instr.data_sz );
     259           0 :   }
     260             : 
     261             :   /* ALUT stuff (non-legacy) */
     262           0 :   message->address_table_lookups_count = 0;
     263           0 :   if( !message->is_legacy ) {
     264             :     /* Transaction Context -> tx -> message -> address_table_lookups */
     265           0 :     message->address_table_lookups_count = txn_descriptor->addr_table_lookup_cnt;
     266           0 :     message->address_table_lookups = fd_spad_alloc( spad,
     267           0 :                                                     alignof(fd_exec_test_message_address_table_lookup_t),
     268           0 :                                                     txn_descriptor->addr_table_lookup_cnt * sizeof(fd_exec_test_message_address_table_lookup_t) );
     269           0 :     for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
     270             :       // alut -> account_key
     271           0 :       fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + address_lookup_tables[i].addr_off);
     272           0 :       memcpy( message->address_table_lookups[i].account_key, alut_key, sizeof(fd_pubkey_t) );
     273             : 
     274             :       // Access ALUT account data to access its keys
     275           0 :       fd_accdb_ro_t addr_lut_ro[1];
     276           0 :       if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, addr_lut_ro, xid, alut_key ) ) ) {
     277           0 :         FD_LOG_ERR(( "addr lut not found" ));
     278           0 :       }
     279             : 
     280             :       // alut -> writable_indexes
     281           0 :       message->address_table_lookups[i].writable_indexes_count = address_lookup_tables[i].writable_cnt;
     282           0 :       message->address_table_lookups[i].writable_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].writable_cnt * sizeof(uint32_t) );
     283           0 :       uchar * writable_indexes = (uchar *) (txn_payload + address_lookup_tables[i].writable_off);
     284           0 :       for( ulong j = 0; j < address_lookup_tables[i].writable_cnt; ++j ) {
     285           0 :         message->address_table_lookups[i].writable_indexes[j] = writable_indexes[j];
     286           0 :       }
     287             : 
     288             :       // alut -> readonly_indexes
     289           0 :       message->address_table_lookups[i].readonly_indexes_count = address_lookup_tables[i].readonly_cnt;
     290           0 :       message->address_table_lookups[i].readonly_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].readonly_cnt * sizeof(uint32_t) );
     291           0 :       uchar * readonly_indexes = (uchar *) (txn_payload + address_lookup_tables[i].readonly_off);
     292           0 :       for( ulong j = 0; j < address_lookup_tables[i].readonly_cnt; ++j ) {
     293           0 :         message->address_table_lookups[i].readonly_indexes[j] = readonly_indexes[j];
     294           0 :       }
     295             : 
     296           0 :       fd_accdb_close_ro( accdb, addr_lut_ro );
     297           0 :     }
     298           0 :   }
     299             : 
     300             :   /* Transaction Context -> tx -> message_hash */
     301             :   // Skip because it does not matter what's in here
     302             : 
     303             :   /* Transaction Context -> tx -> signatures */
     304           0 :   sanitized_transaction->signatures_count = txn_descriptor->signature_cnt;
     305           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 *)) );
     306           0 :   fd_ed25519_sig_t const * signatures = fd_txn_get_signatures( txn_descriptor, txn_payload );
     307           0 :   for( uchar i = 0; i < txn_descriptor->signature_cnt; ++i ) {
     308           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)) );
     309           0 :     signature->size = sizeof(fd_ed25519_sig_t);
     310           0 :     memcpy( signature->bytes, &signatures[i], sizeof(fd_ed25519_sig_t) );
     311           0 :     sanitized_transaction->signatures[i] = signature;
     312           0 :   }
     313           0 : }
     314             : 
     315             : static void
     316             : dump_blockhash_queue( fd_blockhashes_t const * queue,
     317             :                       fd_spad_t *              spad,
     318             :                       pb_bytes_array_t **      output_blockhash_queue,
     319           0 :                       pb_size_t *              output_blockhash_queue_count ) {
     320           0 :   ulong bhq_size = fd_ulong_min( FD_BLOCKHASHES_MAX, fd_blockhash_deq_cnt( queue->d.deque ) );
     321             : 
     322             :   // Iterate over all block hashes in the queue and save them in the output
     323           0 :   pb_size_t cnt = 0U;
     324           0 :   for( fd_blockhash_deq_iter_t iter=fd_blockhash_deq_iter_init_rev( queue->d.deque );
     325           0 :        !fd_blockhash_deq_iter_done_rev( queue->d.deque, iter ) && cnt<FD_BLOCKHASHES_MAX;
     326           0 :        iter=fd_blockhash_deq_iter_prev( queue->d.deque, iter ), cnt++ ) {
     327           0 :     fd_blockhash_info_t const * ele              = fd_blockhash_deq_iter_ele_const( queue->d.deque, iter );
     328           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)) );
     329           0 :     output_blockhash->size = sizeof(fd_hash_t);
     330           0 :     fd_memcpy( output_blockhash->bytes, &ele->hash, sizeof(fd_hash_t) );
     331           0 :     output_blockhash_queue[ bhq_size-cnt-1UL ] = output_blockhash;
     332           0 :   }
     333             : 
     334           0 :   *output_blockhash_queue_count = cnt;
     335           0 : }
     336             : 
     337             : static void
     338             : dump_txn_bank( fd_bank_t *                  bank,
     339             :                fd_spad_t *                  spad,
     340           0 :                fd_exec_test_txn_context_t * txn_context ) {
     341           0 :   txn_context->has_bank              = true;
     342           0 :   fd_exec_test_txn_bank_t * txn_bank = &txn_context->bank;
     343             : 
     344             :   /* TxnBank -> blockhash_queue */
     345           0 :   fd_blockhashes_t const * bhq      = fd_bank_block_hash_queue_query( bank );
     346           0 :   ulong                    bhq_size = fd_ulong_min( FD_BLOCKHASHES_MAX, fd_blockhash_deq_cnt( bhq->d.deque ) );
     347           0 :   txn_bank->blockhash_queue         = fd_spad_alloc( spad, alignof(fd_exec_test_blockhash_queue_entry_t), bhq_size * sizeof(fd_exec_test_blockhash_queue_entry_t) );
     348           0 :   txn_bank->blockhash_queue_count   = (uint)bhq_size;
     349             : 
     350           0 :   ulong cnt = 0UL;
     351           0 :   for( fd_blockhash_deq_iter_t iter=fd_blockhash_deq_iter_init_rev( bhq->d.deque );
     352           0 :        !fd_blockhash_deq_iter_done_rev( bhq->d.deque, iter ) && cnt<bhq_size;
     353           0 :        iter=fd_blockhash_deq_iter_prev( bhq->d.deque, iter ), cnt++ ) {
     354           0 :     fd_blockhash_info_t const * ele              = fd_blockhash_deq_iter_ele_const( bhq->d.deque, iter );
     355           0 :     fd_exec_test_blockhash_queue_entry_t * entry = &txn_bank->blockhash_queue[bhq_size-cnt-1UL];
     356           0 :     fd_memcpy( entry->blockhash, ele->hash.uc, sizeof(fd_hash_t) );
     357           0 :     entry->lamports_per_signature = ele->fee_calculator.lamports_per_signature;
     358           0 :   }
     359             : 
     360             :   /* TxnBank -> rbh_lamports_per_signature */
     361           0 :   txn_bank->rbh_lamports_per_signature = (uint)fd_bank_rbh_lamports_per_sig_get( bank );
     362             : 
     363             :   /* TxnBank -> fee_rate_governor */
     364           0 :   fd_fee_rate_governor_t const * fee_rate_governor = fd_bank_fee_rate_governor_query( bank );
     365           0 :   txn_bank->has_fee_rate_governor = true;
     366           0 :   txn_bank->fee_rate_governor = (fd_exec_test_fee_rate_governor_t){
     367           0 :     .target_lamports_per_signature = fee_rate_governor->target_lamports_per_signature,
     368           0 :     .target_signatures_per_slot    = fee_rate_governor->target_signatures_per_slot,
     369           0 :     .min_lamports_per_signature    = fee_rate_governor->min_lamports_per_signature,
     370           0 :     .max_lamports_per_signature    = fee_rate_governor->max_lamports_per_signature,
     371           0 :     .burn_percent                  = fee_rate_governor->burn_percent,
     372           0 :   };
     373             : 
     374             :   /* TxnBank -> total_epoch_stake */
     375           0 :   txn_bank->total_epoch_stake = fd_bank_total_epoch_stake_get( bank );
     376             : 
     377             :   /* TxnBank -> epoch_schedule */
     378           0 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
     379           0 :   txn_bank->has_epoch_schedule = true;
     380           0 :   txn_bank->epoch_schedule = (fd_exec_test_epoch_schedule_t){
     381           0 :     .slots_per_epoch             = epoch_schedule->slots_per_epoch,
     382           0 :     .leader_schedule_slot_offset = epoch_schedule->leader_schedule_slot_offset,
     383           0 :     .warmup                      = epoch_schedule->warmup,
     384           0 :     .first_normal_epoch          = epoch_schedule->first_normal_epoch,
     385           0 :     .first_normal_slot           = epoch_schedule->first_normal_slot,
     386           0 :   };
     387             : 
     388             :   /* TxnBank -> rent */
     389           0 :   fd_rent_t const * rent = fd_bank_rent_query( bank );
     390           0 :   txn_bank->has_rent = true;
     391           0 :   txn_bank->rent = (fd_exec_test_rent_t){
     392           0 :     .lamports_per_byte_year = rent->lamports_per_uint8_year,
     393           0 :     .exemption_threshold    = rent->exemption_threshold,
     394           0 :     .burn_percent           = rent->burn_percent,
     395           0 :   };
     396             : 
     397             :   /* TxnBank -> features */
     398           0 :   txn_bank->has_features = true;
     399           0 :   dump_sorted_features( fd_bank_features_query( bank ), &txn_bank->features, spad );
     400           0 : }
     401             : 
     402             : /** SECONDARY FUNCTIONS **/
     403             : 
     404             : /* add_account_to_dumped_accounts adds an account to the dumped accounts
     405             :    set if it does not exist already. Returns 0 if the account already
     406             :    exists, and 1 if the account was added successfully.
     407             : 
     408             :    TODO: Txn dumping should be optimized to use these functions. */
     409             : static uchar
     410             : add_account_to_dumped_accounts( fd_dump_account_key_node_t *  pool,
     411             :                                 fd_dump_account_key_node_t ** root,
     412           0 :                                 fd_pubkey_t const *           pubkey ) {
     413             :   /* If the key already exists, return early. */
     414           0 :   fd_dump_account_key_node_t node = {
     415           0 :     .key = *pubkey,
     416           0 :   };
     417           0 :   if( fd_dump_account_key_map_find( pool, *root, &node ) ) {
     418           0 :     return 0;
     419           0 :   }
     420             : 
     421           0 :   fd_dump_account_key_node_t * new_node = fd_dump_account_key_map_acquire( pool );
     422           0 :   new_node->key = *pubkey;
     423           0 :   fd_dump_account_key_map_insert( pool, root, new_node );
     424           0 :   return 1;
     425           0 : }
     426             : 
     427             : /* add_account_and_programdata_to_dumped_accounts adds an account and
     428             :    its programdata account (if the account is a v3 program) to the
     429             :    dumped accounts set if they do not exist already. */
     430             : static void
     431             : add_account_and_programdata_to_dumped_accounts( fd_accdb_user_t *             accdb,
     432             :                                                 fd_funk_txn_xid_t const *     xid,
     433             :                                                 fd_dump_account_key_node_t *  pool,
     434             :                                                 fd_dump_account_key_node_t ** root,
     435           0 :                                                 fd_pubkey_t const *           pubkey ) {
     436             :   /* Add the current account to the dumped accounts set. We can save
     437             :      some time by enforcing an invariant that "if current account was
     438             :      dumped, then programdata account was also dumped," so we save
     439             :      ourselves a call to Funk. */
     440           0 :   uchar ret = add_account_to_dumped_accounts( pool, root, pubkey );
     441           0 :   if( ret==0 ) return;
     442             : 
     443             :   /* Read the account from Funk to see if its a program account and if
     444             :      it needs to be dumped. */
     445           0 :   fd_accdb_ro_t program_account[1];
     446           0 :   if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, program_account, xid, pubkey ) ) ) {
     447           0 :     return;
     448           0 :   }
     449             : 
     450             :   /* Return if its not owned by the v3 loader */
     451           0 :   if( FD_LIKELY( !fd_pubkey_eq( fd_accdb_ref_owner( program_account ), &fd_solana_bpf_loader_upgradeable_program_id ) ) ) {
     452           0 :     fd_accdb_close_ro( accdb, program_account );
     453           0 :     return;
     454           0 :   }
     455             : 
     456             :   /* Get the program account state */
     457           0 :   fd_bpf_upgradeable_loader_state_t program_account_state[1];
     458           0 :   if( FD_UNLIKELY( !fd_bincode_decode_static(
     459           0 :       bpf_upgradeable_loader_state,
     460           0 :       program_account_state,
     461           0 :       fd_accdb_ref_data_const( program_account ),
     462           0 :       fd_accdb_ref_data_sz   ( program_account ),
     463           0 :       NULL ) ) ) {
     464           0 :     fd_accdb_close_ro( accdb, program_account );
     465           0 :     return;
     466           0 :   }
     467           0 :   if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) {
     468           0 :     fd_accdb_close_ro( accdb, program_account );
     469           0 :     return;
     470           0 :   }
     471             : 
     472             :   /* Dump the programdata address */
     473           0 :   add_account_to_dumped_accounts( pool, root, &program_account_state->inner.program.programdata_address );
     474           0 :   fd_accdb_close_ro( accdb, program_account );
     475           0 : }
     476             : 
     477             : /* add_lut_account_to_dumped_accounts adds an address lookup table
     478             :    account AND all pubkeys in the lookup table to the dumped accounts
     479             :    set if they do not exist already. */
     480             : static void
     481             : add_lut_accounts_to_dumped_accounts( fd_accdb_user_t *             accdb,
     482             :                                      fd_funk_txn_xid_t const *     xid,
     483             :                                      fd_dump_account_key_node_t *  pool,
     484             :                                      fd_dump_account_key_node_t ** root,
     485           0 :                                      fd_pubkey_t const *           pubkey ) {
     486             :   /* Add the current account to the dumped accounts set. */
     487           0 :   add_account_to_dumped_accounts( pool, root, pubkey );
     488             : 
     489             :   /* Read the account and dump all pubkeys within the lookup table. */
     490           0 :   fd_accdb_ro_t lut_account[1];
     491           0 :   if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, lut_account, xid, pubkey ) ) ) {
     492           0 :     return;
     493           0 :   }
     494             : 
     495           0 :   uchar const  * data     = fd_accdb_ref_data_const( lut_account );
     496           0 :   ulong          data_len = fd_accdb_ref_data_sz   ( lut_account );
     497             : 
     498             :   /* Decode the ALUT account and dump all pubkeys within the lookup
     499             :      table. */
     500           0 :   if( data_len<FD_LOOKUP_TABLE_META_SIZE || (data_len&0x1fUL) ) {
     501           0 :     fd_accdb_close_ro( accdb, lut_account );
     502           0 :     return;
     503           0 :   }
     504           0 :   fd_pubkey_t const * lookup_addrs     = fd_type_pun_const( data+FD_LOOKUP_TABLE_META_SIZE );
     505           0 :   ulong               lookup_addrs_cnt = ( data_len-FD_LOOKUP_TABLE_META_SIZE)>>5UL; // = (dlen - 56) / 32
     506           0 :   for( ulong i=0UL; i<lookup_addrs_cnt; i++ ) {
     507           0 :     fd_pubkey_t const * referenced_pubkey = &lookup_addrs[i];
     508           0 :     add_account_and_programdata_to_dumped_accounts( accdb, xid, pool, root, referenced_pubkey );
     509           0 :   }
     510           0 :   fd_accdb_close_ro( accdb, lut_account );
     511           0 : }
     512             : 
     513             : /* FIXME: Block dumping currently is not supported and needs to be
     514             :    reworked. */
     515             : // /* create_synthetic_vote_account_from_vote_state creates a synthetic
     516             : //    vote account from a vote state cache element. It fills in default
     517             : //    values for unspecified fields and encodes the vote state into
     518             : //    out_vote_account's data field. */
     519             : // static void
     520             : // create_synthetic_vote_account_from_vote_state( fd_vote_state_ele_t const *   vote_state,
     521             : //                                                fd_spad_t *                   spad,
     522             : //                                                fd_exec_test_vote_account_t * out_vote_account,
     523             : //                                                int                           is_t_1 ) {
     524             : //   out_vote_account->has_vote_account = true;
     525             : //   fd_memcpy( out_vote_account->vote_account.address, &vote_state->vote_account, sizeof(fd_pubkey_t) );
     526             : //   out_vote_account->vote_account.executable = false;
     527             : //   out_vote_account->vote_account.lamports = 100000UL;
     528             : //   fd_memcpy( out_vote_account->vote_account.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) );
     529             : //   out_vote_account->stake = is_t_1 ? vote_state->stake_t_1 : vote_state->stake_t_2;
     530             : 
     531             : //   /* Construct the vote account data. Fill in missing fields with
     532             : //      arbitrary defaults (since they're not used anyways) */
     533             : //   fd_vote_state_versioned_t vsv = {
     534             : //     .discriminant = fd_vote_state_versioned_enum_v3,
     535             : //     .inner = {
     536             : //       .v3 = {
     537             : //         .node_pubkey           = vote_state->node_account,
     538             : //         .authorized_withdrawer = vote_state->node_account,
     539             : //         .commission            = 0,
     540             : //         .root_slot             = 0UL,
     541             : //         .has_root_slot         = 0,
     542             : //         .last_timestamp        = {
     543             : //           .timestamp           = vote_state->last_vote_timestamp,
     544             : //           .slot                = vote_state->last_vote_slot,
     545             : //         },
     546             : //       }
     547             : //     }
     548             : //   };
     549             : //   fd_vote_state_v3_t * synthetic_vote_state = &vsv.inner.v3;
     550             : 
     551             : //   /* Create synthetic landed votes */
     552             : //   synthetic_vote_state->votes = deq_fd_landed_vote_t_join(
     553             : //       deq_fd_landed_vote_t_new(
     554             : //           fd_spad_alloc(
     555             : //               spad,
     556             : //               deq_fd_landed_vote_t_align(),
     557             : //               deq_fd_landed_vote_t_footprint( 32UL ) ),
     558             : //           32UL ) );
     559             : //   for( ulong i=0UL; i<32UL; i++ ) {
     560             : //     fd_landed_vote_t elem = {0};
     561             : //     deq_fd_landed_vote_t_push_tail( synthetic_vote_state->votes, elem );
     562             : //   }
     563             : 
     564             : //   /* Populate authoritzed voters */
     565             : //   void * authorized_voters_pool_mem  = fd_spad_alloc(
     566             : //       spad,
     567             : //       fd_vote_authorized_voters_pool_align(),
     568             : //       fd_vote_authorized_voters_pool_footprint( 5UL ) );
     569             : //   void * authorized_voters_treap_mem = fd_spad_alloc(
     570             : //       spad,
     571             : //       fd_vote_authorized_voters_treap_align(),
     572             : //       fd_vote_authorized_voters_treap_footprint( 5UL ) );
     573             : //   synthetic_vote_state->authorized_voters.pool  = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( authorized_voters_pool_mem, 5UL ) );
     574             : //   synthetic_vote_state->authorized_voters.treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( authorized_voters_treap_mem, 5UL ) );
     575             : 
     576             : //   /* Encode the synthetic vote state */
     577             : //   ulong encoded_sz                          = fd_vote_state_versioned_size( &vsv );
     578             : //   out_vote_account->vote_account.data       = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( encoded_sz ) );
     579             : //   out_vote_account->vote_account.data->size = (pb_size_t)encoded_sz;
     580             : 
     581             : //   fd_bincode_encode_ctx_t encode_ctx = {
     582             : //     .data    = out_vote_account->vote_account.data->bytes,
     583             : //     .dataend = out_vote_account->vote_account.data->bytes+encoded_sz,
     584             : //   };
     585             : //   fd_vote_state_versioned_encode( &vsv, &encode_ctx );
     586             : // }
     587             : 
     588             : // static void FD_FN_UNUSED
     589             : // dump_prior_vote_accounts( fd_vote_states_t const *      vote_states,
     590             : //                           fd_dump_account_key_node_t *  dumped_accounts_pool,
     591             : //                           fd_dump_account_key_node_t ** dumped_accounts_root,
     592             : //                           fd_exec_test_vote_account_t * out_vote_accounts,
     593             : //                           pb_size_t *                   out_vote_accounts_count,
     594             : //                           fd_spad_t *                   spad,
     595             : //                           int                           is_t_1 ) {
     596             : 
     597             : //   fd_vote_states_iter_t iter_[1];
     598             : //   for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( iter_, vote_states );
     599             : //                                      !fd_vote_states_iter_done( iter );
     600             : //                                       fd_vote_states_iter_next( iter ) ) {
     601             : //     fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter );
     602             : //     add_account_to_dumped_accounts( dumped_accounts_pool, dumped_accounts_root, &vote_state->vote_account );
     603             : 
     604             : //     create_synthetic_vote_account_from_vote_state(
     605             : //         vote_state,
     606             : //         spad,
     607             : //         &out_vote_accounts[(*out_vote_accounts_count)++],
     608             : //         is_t_1 );
     609             : //   }
     610             : // }
     611             : 
     612             : static void
     613             : create_block_context_protobuf_from_block( fd_block_dump_ctx_t * dump_ctx,
     614             :                                           fd_banks_t *          banks,
     615             :                                           fd_bank_t *           bank,
     616           0 :                                           fd_accdb_user_t *     accdb ) {
     617             :   /* We should use the bank fields and funk txn from the parent slot in
     618             :      order to capture the block context from before the current block
     619             :      was executed, since dumping is happening in the block finalize
     620             :      step. */
     621           0 :   fd_bank_t parent_bank[1];
     622           0 :   fd_banks_get_parent( parent_bank, banks, bank );
     623           0 :   ulong                          current_slot   = fd_bank_slot_get( bank );
     624           0 :   ulong                          parent_slot    = fd_bank_slot_get( parent_bank );
     625           0 :   fd_funk_txn_xid_t              parent_xid     = { .ul = { parent_slot, parent_bank->data->idx } };
     626           0 :   fd_exec_test_block_context_t * block_context  = &dump_ctx->block_context;
     627           0 :   ulong                          dump_txn_count = dump_ctx->txns_to_dump_cnt;
     628           0 :   fd_spad_t *                    spad           = dump_ctx->spad;
     629             : 
     630             :   /* Get vote and stake delegation infos */
     631           0 :   fd_vote_stakes_t * vote_stakes        = fd_bank_vote_stakes_locking_modify( parent_bank );
     632           0 :   ulong              vote_account_t_cnt = fd_vote_stakes_ele_cnt( vote_stakes, parent_bank->data->vote_stakes_fork_id );
     633             : 
     634           0 :   fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_frontier_query( banks, parent_bank );
     635           0 :   ulong                          stake_account_cnt = fd_stake_delegations_cnt( stake_delegations );
     636             : 
     637             :   /* Collect account states in a temporary set before iterating over
     638             :      them and dumping them out. */
     639           0 :   ulong                        total_num_accounts   = num_sysvar_entries +  /* Sysvars */
     640           0 :                                                       num_loaded_builtins + /* Builtins */
     641           0 :                                                       stake_account_cnt +   /* Stake accounts */
     642           0 :                                                       vote_account_t_cnt +  /* Current vote accounts */
     643           0 :                                                       dump_txn_count*128UL; /* Txn accounts upper bound */
     644           0 :   void *                       dumped_accounts_mem  = fd_spad_alloc( spad, fd_dump_account_key_map_align(), fd_dump_account_key_map_footprint( total_num_accounts ) );
     645           0 :   fd_dump_account_key_node_t * dumped_accounts_pool = fd_dump_account_key_map_join( fd_dump_account_key_map_new( dumped_accounts_mem, total_num_accounts ) );
     646           0 :   fd_dump_account_key_node_t * dumped_accounts_root = NULL;
     647             : 
     648             :   /* BlockContext -> txns */
     649           0 :   block_context->txns_count = (pb_size_t)dump_txn_count;
     650           0 :   block_context->txns       = fd_spad_alloc( spad, alignof(fd_exec_test_sanitized_transaction_t), dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
     651           0 :   fd_memset( block_context->txns, 0, dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
     652             : 
     653             :   /* Dump sanitized transactions from the transaction descriptors */
     654           0 :   for( ulong i=0UL; i<dump_ctx->txns_to_dump_cnt; i++ ) {
     655           0 :     fd_txn_p_t const * txn_ptr        = &dump_ctx->txns_to_dump[i];
     656           0 :     fd_txn_t const *   txn_descriptor = TXN( txn_ptr );
     657           0 :     dump_sanitized_transaction( accdb, &parent_xid, txn_descriptor, txn_ptr->payload, spad, &block_context->txns[i] );
     658             : 
     659             :     /* Dump account + alut + programdata accounts (if applicable).
     660             :        1. Dump the raw txn account keys
     661             :        2. Dump the ALUT accounts
     662             :        3. Dump all referenced accounts in the ALUTs
     663             :        4. Dump any executable accounts */
     664             : 
     665             :     // 1 + 4. Dump any account keys that are referenced by transactions
     666             :     // + any programdata accounts (if applicable).
     667           0 :     fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload );
     668           0 :     for( ushort l=0; l<txn_descriptor->acct_addr_cnt; l++ ) {
     669           0 :       fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] );
     670           0 :       add_account_and_programdata_to_dumped_accounts( accdb, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, account_key );
     671           0 :     }
     672             : 
     673             :     // 2 + 3 + 4. Dump any ALUT accounts + any accounts referenced in
     674             :     // the ALUTs + any programdata accounts (if applicable).
     675           0 :     fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     676           0 :     for( ushort l=0; l<txn_descriptor->addr_table_lookup_cnt; l++ ) {
     677           0 :       fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l];
     678           0 :       fd_pubkey_t const *            lut_key      = fd_type_pun_const( txn_ptr->payload+lookup_table->addr_off );
     679           0 :       add_lut_accounts_to_dumped_accounts( accdb, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, lut_key );
     680           0 :     }
     681           0 :   }
     682             : 
     683             :   /* Dump sysvars */
     684           0 :   for( ulong i=0UL; i<num_sysvar_entries; i++ ) {
     685           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, fd_dump_sysvar_ids[i] );
     686           0 :   }
     687             : 
     688             :   /* Dump builtins */
     689           0 :   for( ulong i=0UL; i<num_loaded_builtins; i++ ) {
     690           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, fd_dump_builtin_ids[i] );
     691           0 :   }
     692             : 
     693             :   /* Dump stake accounts for this epoch */
     694           0 :   fd_stake_delegations_iter_t iter_[1];
     695           0 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
     696           0 :        !fd_stake_delegations_iter_done( iter );
     697           0 :        fd_stake_delegations_iter_next( iter ) ) {
     698           0 :     fd_stake_delegation_t * stake_delegation = fd_stake_delegations_iter_ele( iter );
     699           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &stake_delegation->stake_account );
     700           0 :   }
     701             : 
     702           0 :   ushort fork_idx = parent_bank->data->vote_stakes_fork_id;
     703           0 :   uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
     704           0 :   for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_fork_iter_init( vote_stakes, fork_idx, iter_mem );
     705           0 :        !fd_vote_stakes_fork_iter_done( vote_stakes, fork_idx, iter );
     706           0 :        fd_vote_stakes_fork_iter_next( vote_stakes, fork_idx, iter ) ) {
     707           0 :     fd_pubkey_t pubkey;
     708           0 :     fd_vote_stakes_fork_iter_ele( vote_stakes, fork_idx, iter, &pubkey, NULL, NULL, NULL, NULL );
     709           0 :     add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &pubkey );
     710           0 :   }
     711           0 :   fd_bank_vote_stakes_end_locking_modify( parent_bank );
     712             : 
     713             :   /* BlockContext -> acct_states
     714             :      Iterate over the set and dump all the account keys in one pass. */
     715           0 :   block_context->acct_states_count = 0U;
     716           0 :   block_context->acct_states       = fd_spad_alloc(
     717           0 :       spad,
     718           0 :       alignof(fd_exec_test_acct_state_t),
     719           0 :       fd_dump_account_key_map_size( dumped_accounts_pool, dumped_accounts_root )*sizeof(fd_exec_test_acct_state_t) );
     720           0 :   for( fd_dump_account_key_node_t * node = fd_dump_account_key_map_minimum( dumped_accounts_pool, dumped_accounts_root );
     721           0 :                                     node;
     722           0 :                                     node = fd_dump_account_key_map_successor( dumped_accounts_pool, node ) ) {
     723           0 :     fd_accdb_ro_t ro[1];
     724           0 :     if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, ro, &parent_xid, &node->key ) ) ) {
     725           0 :       continue;
     726           0 :     }
     727           0 :     dump_account_state(
     728           0 :         fd_accdb_ref_address( ro ),
     729           0 :         ro->meta,
     730           0 :         &block_context->acct_states[block_context->acct_states_count++],
     731           0 :         spad );
     732           0 :     fd_accdb_close_ro( accdb, ro );
     733           0 :   }
     734             : 
     735             :   /* BlockContext -> blockhash_queue */
     736           0 :   fd_blockhashes_t const * bhq   = fd_bank_block_hash_queue_query( parent_bank );
     737           0 :   block_context->blockhash_queue = fd_spad_alloc(
     738           0 :       spad,
     739           0 :       alignof(pb_bytes_array_t *),
     740           0 :       PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) );
     741           0 :   block_context->blockhash_queue_count = 0U;
     742           0 :   dump_blockhash_queue( bhq, spad, block_context->blockhash_queue, &block_context->blockhash_queue_count );
     743             : 
     744             :   /* BlockContext -> SlotContext */
     745           0 :   block_context->has_slot_ctx                       = true;
     746           0 :   block_context->slot_ctx.slot                      = current_slot;
     747           0 :   block_context->slot_ctx.block_height              = fd_bank_block_height_get( bank );
     748           0 :   block_context->slot_ctx.prev_slot                 = fd_bank_parent_slot_get( bank );
     749             : 
     750             :   // We need to store the POH hash for the current block since we don't
     751             :   // recalculate it in the harnesses.
     752           0 :   fd_memcpy( block_context->slot_ctx.poh, fd_bank_poh_query( bank ), sizeof(fd_pubkey_t) );
     753           0 :   fd_memcpy( block_context->slot_ctx.parent_bank_hash, fd_bank_bank_hash_query( parent_bank ), sizeof(fd_pubkey_t) );
     754             : 
     755           0 :   fd_lthash_value_t const * parent_lthash = fd_bank_lthash_locking_query( parent_bank );
     756           0 :   fd_memcpy( block_context->slot_ctx.parent_lthash, parent_lthash, sizeof(fd_lthash_value_t) );
     757           0 :   fd_bank_lthash_end_locking_query( parent_bank );
     758             : 
     759           0 :   block_context->slot_ctx.prev_epoch_capitalization = fd_bank_capitalization_get( parent_bank );
     760             : 
     761             :   /* BlockContext -> SlotContext -> fee_rate_governor */
     762           0 :   fd_fee_rate_governor_t const * fee_rate_governor = fd_bank_fee_rate_governor_query( parent_bank );
     763           0 :   block_context->slot_ctx.has_fee_rate_governor     = true;
     764           0 :   block_context->slot_ctx.fee_rate_governor         = (fd_exec_test_fee_rate_governor_t){
     765           0 :       .target_lamports_per_signature = fee_rate_governor->target_lamports_per_signature,
     766           0 :       .target_signatures_per_slot    = fee_rate_governor->target_signatures_per_slot,
     767           0 :       .min_lamports_per_signature    = fee_rate_governor->min_lamports_per_signature,
     768           0 :       .max_lamports_per_signature    = fee_rate_governor->max_lamports_per_signature,
     769           0 :       .burn_percent                  = fee_rate_governor->burn_percent,
     770           0 :   };
     771             : 
     772             :   /* BlockContext -> EpochContext */
     773           0 :   block_context->has_epoch_ctx                        = true;
     774           0 :   block_context->epoch_ctx.has_features               = true;
     775           0 :   dump_sorted_features( fd_bank_features_query( parent_bank ), &block_context->epoch_ctx.features, spad );
     776           0 :   block_context->epoch_ctx.hashes_per_tick            = fd_bank_hashes_per_tick_get( parent_bank );
     777           0 :   block_context->epoch_ctx.ticks_per_slot             = fd_bank_ticks_per_slot_get( parent_bank );
     778           0 :   block_context->epoch_ctx.has_inflation              = true;
     779             : 
     780           0 :   fd_inflation_t const * inflation = fd_bank_inflation_query( parent_bank );
     781           0 :   block_context->epoch_ctx.inflation                  = (fd_exec_test_inflation_t) {
     782           0 :       .initial         = inflation->initial,
     783           0 :       .terminal        = inflation->terminal,
     784           0 :       .taper           = inflation->taper,
     785           0 :       .foundation      = inflation->foundation,
     786           0 :       .foundation_term = inflation->foundation_term,
     787           0 :   };
     788           0 :   block_context->epoch_ctx.genesis_creation_time      = fd_bank_genesis_creation_time_get( parent_bank );
     789           0 : }
     790             : 
     791             : static void
     792             : create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_msg,
     793             :                                       fd_runtime_t *               runtime,
     794             :                                       fd_bank_t *                  bank,
     795             :                                       fd_txn_in_t const *          txn_in,
     796             :                                       fd_txn_out_t *               txn_out,
     797           0 :                                       fd_spad_t *                  spad ) {
     798           0 :   fd_txn_t const * txn_descriptor = TXN( txn_in->txn );
     799           0 :   uchar const *    txn_payload    = (uchar const *) txn_in->txn->payload;
     800             : 
     801             :   /* Transaction Context -> account_shared_data
     802             :      Contains:
     803             :       - Account data for regular accounts
     804             :       - Account data for LUT accounts
     805             :       - Account data for executable accounts
     806             :       - Account data for (almost) all sysvars */
     807           0 :   txn_context_msg->account_shared_data_count = 0;
     808           0 :   txn_context_msg->account_shared_data = fd_spad_alloc( spad,
     809           0 :                                                         alignof(fd_exec_test_acct_state_t),
     810           0 :                                                         (256UL*2UL + txn_descriptor->addr_table_lookup_cnt + num_sysvar_entries) * sizeof(fd_exec_test_acct_state_t) );
     811           0 :   fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( bank ), bank->data->idx } };
     812             : 
     813             :   /* Dump regular accounts first */
     814           0 :   for( ulong i = 0; i < txn_out->accounts.cnt; ++i ) {
     815           0 :     dump_account_if_not_already_dumped(
     816           0 :       runtime->accdb,
     817           0 :       &xid,
     818           0 :       &txn_out->accounts.keys[i],
     819           0 :       spad,
     820           0 :       txn_context_msg->account_shared_data,
     821           0 :       &txn_context_msg->account_shared_data_count,
     822           0 :       NULL
     823           0 :     );
     824           0 :   }
     825             : 
     826             :   // Dump LUT accounts
     827           0 :   fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
     828           0 :   for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
     829           0 :     fd_txn_acct_addr_lut_t const * addr_lut  = &address_lookup_tables[i];
     830           0 :     fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + addr_lut->addr_off);
     831             : 
     832             :     // Dump the LUT account itself if not already dumped
     833           0 :     fd_accdb_ro_t ro[1];
     834           0 :     int ret = dump_account_if_not_already_dumped(
     835           0 :         runtime->accdb,
     836           0 :         &xid,
     837           0 :         alut_key,
     838           0 :         spad,
     839           0 :         txn_context_msg->account_shared_data,
     840           0 :         &txn_context_msg->account_shared_data_count,
     841           0 :         ro
     842           0 :     );
     843           0 :     if( FD_UNLIKELY( ret ) ) continue;
     844             : 
     845           0 :     uchar const * alut_data = fd_accdb_ref_data_const( ro );
     846           0 :     ulong         alut_sz   = fd_accdb_ref_data_sz   ( ro );
     847             : 
     848           0 :     if( FD_UNLIKELY( alut_sz<FD_LOOKUP_TABLE_META_SIZE ) ) {
     849             :       /* Skip over invalid address lookup tables */
     850           0 :       fd_accdb_close_ro( runtime->accdb, ro );
     851           0 :       continue;
     852           0 :     }
     853             : 
     854           0 :     fd_pubkey_t const * lookup_addrs     = fd_type_pun_const( alut_data+FD_LOOKUP_TABLE_META_SIZE );
     855           0 :     ulong               lookup_addrs_cnt = (alut_sz - FD_LOOKUP_TABLE_META_SIZE) / sizeof(fd_pubkey_t);
     856             : 
     857             :     /* Dump any account state refererenced in ALUTs */
     858           0 :     uchar const * writable_lut_idxs = txn_payload + addr_lut->writable_off;
     859           0 :     for( ulong j=0; j<addr_lut->writable_cnt; j++ ) {
     860           0 :       if( writable_lut_idxs[j] >= lookup_addrs_cnt ) {
     861           0 :         continue;
     862           0 :       }
     863           0 :       fd_pubkey_t const * referenced_addr = lookup_addrs + writable_lut_idxs[j];
     864           0 :       dump_account_if_not_already_dumped(
     865           0 :           runtime->accdb,
     866           0 :           &xid,
     867           0 :           referenced_addr,
     868           0 :           spad,
     869           0 :           txn_context_msg->account_shared_data,
     870           0 :           &txn_context_msg->account_shared_data_count,
     871           0 :           NULL
     872           0 :       );
     873           0 :     }
     874             : 
     875           0 :     uchar const * readonly_lut_idxs = txn_payload + addr_lut->readonly_off;
     876           0 :     for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
     877           0 :       if( readonly_lut_idxs[j] >= lookup_addrs_cnt ) {
     878           0 :         continue;
     879           0 :       }
     880           0 :       fd_pubkey_t const * referenced_addr = lookup_addrs + readonly_lut_idxs[j];
     881           0 :       dump_account_if_not_already_dumped(
     882           0 :           runtime->accdb,
     883           0 :           &xid,
     884           0 :           referenced_addr,
     885           0 :           spad,
     886           0 :           txn_context_msg->account_shared_data,
     887           0 :           &txn_context_msg->account_shared_data_count,
     888           0 :           NULL
     889           0 :       );
     890           0 :     }
     891             : 
     892           0 :     fd_accdb_close_ro( runtime->accdb, ro );
     893           0 :   }
     894             : 
     895             :   /* Dump the programdata accounts for any potential v3-owned program accounts */
     896           0 :   uint accounts_dumped_so_far = txn_context_msg->account_shared_data_count;
     897           0 :   for( uint i=0U; i<accounts_dumped_so_far; i++ ) {
     898           0 :     fd_exec_test_acct_state_t const * maybe_program_account = &txn_context_msg->account_shared_data[i];
     899           0 :     dump_executable_account_if_exists( runtime->accdb, &xid, maybe_program_account, spad, txn_context_msg->account_shared_data, &txn_context_msg->account_shared_data_count );
     900           0 :   }
     901             : 
     902             :   /* Dump sysvars */
     903           0 :   for( ulong i = 0; i < num_sysvar_entries; i++ ) {
     904           0 :     dump_account_if_not_already_dumped(
     905           0 :         runtime->accdb,
     906           0 :         &xid,
     907           0 :         fd_dump_sysvar_ids[i],
     908           0 :         spad,
     909           0 :         txn_context_msg->account_shared_data,
     910           0 :         &txn_context_msg->account_shared_data_count,
     911           0 :         NULL
     912           0 :     );
     913           0 :   }
     914             : 
     915             :   /* Transaction Context -> tx */
     916           0 :   txn_context_msg->has_tx = true;
     917           0 :   fd_exec_test_sanitized_transaction_t * sanitized_transaction = &txn_context_msg->tx;
     918           0 :   dump_sanitized_transaction( runtime->accdb, &xid, txn_descriptor, txn_payload, spad, sanitized_transaction );
     919             : 
     920             :   /* Transaction Context -> bank */
     921           0 :   dump_txn_bank( bank, spad, txn_context_msg );
     922           0 : }
     923             : 
     924             : static void
     925             : create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * instr_context,
     926             :                                                  fd_runtime_t *                 runtime,
     927             :                                                  fd_bank_t *                    bank,
     928             :                                                  fd_txn_out_t *                 txn_out,
     929             :                                                  fd_instr_info_t const *        instr,
     930           0 :                                                  fd_spad_t *                    spad ) {
     931             :   /* Program ID */
     932           0 :   fd_memcpy( instr_context->program_id, txn_out->accounts.keys[ instr->program_id ].uc, sizeof(fd_pubkey_t) );
     933             : 
     934           0 :   fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( bank ), bank->data->idx } };
     935             : 
     936             :   /* Accounts */
     937           0 :   instr_context->accounts_count = (pb_size_t) txn_out->accounts.cnt;
     938           0 :   instr_context->accounts = fd_spad_alloc( spad, alignof(fd_exec_test_acct_state_t), (instr_context->accounts_count + num_sysvar_entries + runtime->accounts.executable_cnt) * sizeof(fd_exec_test_acct_state_t));
     939           0 :   for( ulong i = 0; i < txn_out->accounts.cnt; i++ ) {
     940             :     // Copy account information over
     941           0 :     fd_account_meta_t * account_meta = txn_out->accounts.account[i].meta;
     942           0 :     fd_exec_test_acct_state_t * output_account = &instr_context->accounts[i];
     943           0 :     dump_account_state( &txn_out->accounts.keys[i], account_meta, output_account, spad );
     944           0 :   }
     945             : 
     946             :   /* Add sysvar cache variables */
     947           0 :   for( ulong i = 0; i < num_sysvar_entries; i++ ) {
     948           0 :     fd_accdb_ro_t ro[1];
     949           0 :     if( !fd_accdb_open_ro( runtime->accdb, ro, &xid, fd_dump_sysvar_ids[i] ) ) {
     950           0 :       continue;
     951           0 :     }
     952             :     // Make sure the account doesn't exist in the output accounts yet
     953           0 :     int account_exists = 0;
     954           0 :     for( ulong j = 0; j < txn_out->accounts.cnt; j++ ) {
     955           0 :       if( fd_pubkey_eq( &txn_out->accounts.keys[j], fd_dump_sysvar_ids[i] ) ) {
     956           0 :         account_exists = true;
     957           0 :         break;
     958           0 :       }
     959           0 :     }
     960             : 
     961             :     // Copy it into output
     962           0 :     if( !account_exists ) {
     963           0 :       fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
     964           0 :       dump_account_state( fd_accdb_ref_address( ro ), ro->meta, output_account, spad );
     965           0 :     }
     966           0 :     fd_accdb_close_ro( runtime->accdb, ro );
     967           0 :   }
     968             : 
     969             :   /* Add executable accounts */
     970           0 :   for( ulong i = 0; i < runtime->accounts.executable_cnt; i++ ) {
     971             :     // Make sure the account doesn't exist in the output accounts yet
     972           0 :     fd_accdb_ro_t const * ro = &runtime->accounts.executable[i];
     973           0 :     bool account_exists = false;
     974           0 :     for( ulong j = 0; j < instr_context->accounts_count; j++ ) {
     975           0 :       if( 0 == memcmp( instr_context->accounts[j].address, fd_accdb_ref_address( ro ), sizeof(fd_pubkey_t) ) ) {
     976           0 :         account_exists = true;
     977           0 :         break;
     978           0 :       }
     979           0 :     }
     980             :     // Copy it into output
     981           0 :     if( !account_exists ) {
     982           0 :       fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
     983           0 :       dump_account_state( fd_accdb_ref_address( ro ), ro->meta, output_account, spad );
     984           0 :     }
     985           0 :   }
     986             : 
     987             :   /* Instruction Accounts */
     988           0 :   instr_context->instr_accounts_count = (pb_size_t) instr->acct_cnt;
     989           0 :   instr_context->instr_accounts = fd_spad_alloc( spad, alignof(fd_exec_test_instr_acct_t), instr_context->instr_accounts_count * sizeof(fd_exec_test_instr_acct_t) );
     990           0 :   for( ushort i = 0; i < instr->acct_cnt; i++ ) {
     991           0 :     fd_exec_test_instr_acct_t * output_instr_account = &instr_context->instr_accounts[i];
     992             : 
     993           0 :     output_instr_account->index       = instr->accounts[i].index_in_transaction;
     994           0 :     output_instr_account->is_writable = instr->accounts[i].is_writable;
     995           0 :     output_instr_account->is_signer   = instr->accounts[i].is_signer;
     996           0 :   }
     997             : 
     998             :   /* Data */
     999           0 :   instr_context->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( instr->data_sz ) );
    1000           0 :   instr_context->data->size = (pb_size_t) instr->data_sz;
    1001           0 :   fd_memcpy( instr_context->data->bytes, instr->data, instr->data_sz );
    1002             : 
    1003             :   /* Compute Units */
    1004           0 :   instr_context->cu_avail = txn_out->details.compute_budget.compute_meter;
    1005             : 
    1006             :   /* Feature set */
    1007           0 :   instr_context->has_features = true;
    1008           0 :   dump_sorted_features( fd_bank_features_query( bank ), &instr_context->features, spad );
    1009           0 : }
    1010             : 
    1011             : /***** PUBLIC APIs *****/
    1012             : 
    1013             : void
    1014             : fd_dump_instr_to_protobuf( fd_runtime_t *      runtime,
    1015             :                            fd_bank_t *         bank,
    1016             :                            fd_txn_in_t const * txn_in,
    1017             :                            fd_txn_out_t *      txn_out,
    1018             :                            fd_instr_info_t *   instr,
    1019           0 :                            ushort              instruction_idx ) {
    1020             :   /* Check program ID filter, if it exists */
    1021           0 :   if( runtime->log.dump_proto_ctx->has_dump_instr_program_id_filter &&
    1022           0 :       memcmp( txn_out->accounts.keys[ instr->program_id ].uc, runtime->log.dump_proto_ctx->dump_instr_program_id_filter, sizeof(fd_pubkey_t) ) ) {
    1023           0 :     return;
    1024           0 :   }
    1025             : 
    1026           0 :   fd_spad_t * spad = fd_spad_join( fd_spad_new( runtime->log.dumping_mem, 1UL<<28UL ) );
    1027             : 
    1028           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1029             :     // Get base58-encoded tx signature
    1030           0 :     const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
    1031           0 :     char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1032           0 :     fd_base58_encode_64( signatures[0], NULL, encoded_signature );
    1033             : 
    1034           0 :     fd_exec_test_instr_context_t instr_context = FD_EXEC_TEST_INSTR_CONTEXT_INIT_DEFAULT;
    1035           0 :     create_instr_context_protobuf_from_instructions( &instr_context, runtime, bank, txn_out, instr, spad );
    1036             : 
    1037             :     /* Output to file */
    1038           0 :     ulong        out_buf_size = 100 * 1024 * 1024;
    1039           0 :     uint8_t *    out          = fd_spad_alloc( spad, alignof(uchar) , out_buf_size );
    1040           0 :     pb_ostream_t stream       = pb_ostream_from_buffer( out, out_buf_size );
    1041           0 :     if (pb_encode(&stream, FD_EXEC_TEST_INSTR_CONTEXT_FIELDS, &instr_context)) {
    1042           0 :       char output_filepath[ PATH_MAX ];
    1043           0 :       snprintf( output_filepath, PATH_MAX, "%s/instr-%s-%hu.instrctx", runtime->log.dump_proto_ctx->dump_proto_output_dir, encoded_signature, instruction_idx );
    1044           0 :       FILE * file = fopen(output_filepath, "wb");
    1045           0 :       if( file ) {
    1046           0 :         fwrite( out, 1, stream.bytes_written, file );
    1047           0 :         fclose( file );
    1048           0 :       }
    1049           0 :     }
    1050           0 :   } FD_SPAD_FRAME_END;
    1051           0 : }
    1052             : 
    1053             : /* Writes a single account state into the resulting_state field of a
    1054             :    TxnResult protobuf.  Sub-allocations for account data are bump-
    1055             :    allocated from the caller's scratch region via _l. */
    1056             : static void
    1057             : write_account_to_result( fd_pubkey_t const *              pubkey,
    1058             :                          fd_account_meta_t const *        meta,
    1059             :                          fd_exec_test_acct_state_t *      out_accounts,
    1060             :                          pb_size_t *                      out_accounts_cnt,
    1061             :                          ulong *                          scratch_cur,
    1062           0 :                          ulong                            scratch_end ) {
    1063           0 :   fd_exec_test_acct_state_t * out_acct = &out_accounts[ *out_accounts_cnt ];
    1064           0 :   (*out_accounts_cnt)++;
    1065             : 
    1066           0 :   memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
    1067           0 :   memcpy( out_acct->address, pubkey, sizeof(fd_pubkey_t) );
    1068           0 :   out_acct->lamports = meta->lamports;
    1069             : 
    1070           0 :   if( meta->dlen>0UL ) {
    1071           0 :     pb_bytes_array_t * data = (pb_bytes_array_t *)fd_ulong_align_up( *scratch_cur, alignof(pb_bytes_array_t) );
    1072           0 :     *scratch_cur = (ulong)data + PB_BYTES_ARRAY_T_ALLOCSIZE( meta->dlen );
    1073           0 :     if( FD_UNLIKELY( *scratch_cur > scratch_end ) ) abort();
    1074           0 :     data->size = (pb_size_t)meta->dlen;
    1075           0 :     fd_memcpy( data->bytes, fd_account_data( meta ), meta->dlen );
    1076           0 :     out_acct->data = data;
    1077           0 :   }
    1078             : 
    1079           0 :   out_acct->executable = meta->executable;
    1080           0 :   memcpy( out_acct->owner, meta->owner, sizeof(fd_pubkey_t) );
    1081           0 : }
    1082             : 
    1083             : ulong
    1084             : create_txn_result_protobuf_from_txn( fd_exec_test_txn_result_t ** txn_result_out,
    1085             :                                      void *                       out_buf,
    1086             :                                      ulong                        out_bufsz,
    1087             :                                      fd_txn_in_t const *          txn_in,
    1088             :                                      fd_txn_out_t *               txn_out,
    1089             :                                      fd_bank_t *                  bank,
    1090           0 :                                      int                          exec_res ) {
    1091           0 :   FD_SCRATCH_ALLOC_INIT( l, out_buf );
    1092           0 :   ulong out_end = (ulong)out_buf + out_bufsz;
    1093             : 
    1094           0 :   fd_exec_test_txn_result_t * txn_result =
    1095           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_txn_result_t),
    1096           0 :                                 sizeof(fd_exec_test_txn_result_t) );
    1097           0 :   if( FD_UNLIKELY( _l > out_end ) ) abort();
    1098           0 :   fd_memset( txn_result, 0, sizeof(fd_exec_test_txn_result_t) );
    1099             : 
    1100             :   /* Map nonce errors into the agave expected ones. */
    1101           0 :   if( FD_UNLIKELY( exec_res==FD_RUNTIME_TXN_ERR_BLOCKHASH_NONCE_ALREADY_ADVANCED ||
    1102           0 :                    exec_res==FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR ||
    1103           0 :                    exec_res==FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_WRONG_NONCE )) {
    1104           0 :     exec_res = FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
    1105           0 :   }
    1106             : 
    1107             :   /* Basic result fields */
    1108           0 :   txn_result->executed                  = txn_out->err.is_committable;
    1109           0 :   txn_result->sanitization_error        = !txn_out->err.is_committable;
    1110           0 :   txn_result->modified_accounts_count   = 0;
    1111           0 :   txn_result->rollback_accounts_count   = 0;
    1112           0 :   txn_result->is_ok                     = !exec_res;
    1113           0 :   txn_result->status                    = (uint32_t) -exec_res;
    1114           0 :   txn_result->instruction_error         = 0;
    1115           0 :   txn_result->instruction_error_index   = 0;
    1116           0 :   txn_result->custom_error              = 0;
    1117           0 :   txn_result->has_fee_details           = false;
    1118           0 :   txn_result->loaded_accounts_data_size = txn_out->details.loaded_accounts_data_size;
    1119             : 
    1120           0 :   if( txn_result->sanitization_error ) {
    1121           0 :     if( txn_out->err.is_fees_only ) {
    1122           0 :       txn_result->has_fee_details                = true;
    1123           0 :       txn_result->fee_details.prioritization_fee = txn_out->details.priority_fee;
    1124           0 :       txn_result->fee_details.transaction_fee    = txn_out->details.execution_fee;
    1125           0 :     }
    1126             : 
    1127           0 :     if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
    1128           0 :       txn_result->instruction_error       = (uint32_t) -txn_out->err.exec_err;
    1129           0 :       txn_result->instruction_error_index = (uint32_t) txn_out->err.exec_err_idx;
    1130           0 :       if( txn_out->err.exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
    1131           0 :         txn_result->custom_error = txn_out->err.custom_err;
    1132           0 :       }
    1133           0 :     }
    1134             : 
    1135           0 :     *txn_result_out = txn_result;
    1136           0 :     return FD_SCRATCH_ALLOC_FINI( l, 1UL ) - (ulong)out_buf;
    1137           0 :   }
    1138             : 
    1139             :   /* Capture instruction error code for executed transactions */
    1140           0 :   if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
    1141           0 :     fd_txn_t const * txn            = TXN( txn_in->txn );
    1142           0 :     int              instr_err_idx  = txn_out->err.exec_err_idx;
    1143           0 :     int              program_id_idx = txn->instr[instr_err_idx].program_id;
    1144             : 
    1145           0 :     txn_result->instruction_error       = (uint32_t) -txn_out->err.exec_err;
    1146           0 :     txn_result->instruction_error_index = (uint32_t) instr_err_idx;
    1147             : 
    1148           0 :     if( txn_out->err.exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR &&
    1149           0 :         fd_executor_lookup_native_precompile_program( &txn_out->accounts.keys[ program_id_idx ] )==NULL ) {
    1150           0 :       txn_result->custom_error = txn_out->err.custom_err;
    1151           0 :     }
    1152           0 :   }
    1153             : 
    1154           0 :   txn_result->has_fee_details                = true;
    1155           0 :   txn_result->fee_details.transaction_fee    = txn_out->details.execution_fee;
    1156           0 :   txn_result->fee_details.prioritization_fee = txn_out->details.priority_fee;
    1157           0 :   txn_result->executed_units                 = txn_out->details.compute_budget.compute_unit_limit - txn_out->details.compute_budget.compute_meter;
    1158             : 
    1159             :   /* Return data */
    1160           0 :   if( txn_out->details.return_data.len>0 ) {
    1161           0 :     txn_result->return_data = FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
    1162           0 :                                                        PB_BYTES_ARRAY_T_ALLOCSIZE( txn_out->details.return_data.len ) );
    1163           0 :     if( FD_UNLIKELY( _l > out_end ) ) abort();
    1164           0 :     txn_result->return_data->size = (pb_size_t)txn_out->details.return_data.len;
    1165           0 :     fd_memcpy( txn_result->return_data->bytes, txn_out->details.return_data.data, txn_out->details.return_data.len );
    1166           0 :   }
    1167             : 
    1168             :   /* Modified accounts */
    1169           0 :   txn_result->modified_accounts = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t), sizeof(fd_exec_test_acct_state_t) * txn_out->accounts.cnt );
    1170           0 :   txn_result->rollback_accounts = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t), sizeof(fd_exec_test_acct_state_t) * 2UL );
    1171           0 :   if( FD_UNLIKELY( _l > out_end ) ) abort();
    1172             : 
    1173           0 :   if( txn_out->err.is_fees_only || exec_res!=FD_RUNTIME_EXECUTE_SUCCESS ) {
    1174             :     /* If the transaction errored, capture the rollback accounts (fee payer and nonce). */
    1175           0 :     if( FD_LIKELY( txn_out->accounts.nonce_idx_in_txn!=FD_FEE_PAYER_TXN_IDX ) ) {
    1176           0 :       write_account_to_result(
    1177           0 :         &txn_out->accounts.keys[FD_FEE_PAYER_TXN_IDX],
    1178           0 :         txn_out->accounts.rollback_fee_payer,
    1179           0 :         txn_result->rollback_accounts,
    1180           0 :         &txn_result->rollback_accounts_count,
    1181           0 :         &_l,
    1182           0 :         out_end
    1183           0 :       );
    1184           0 :     }
    1185             : 
    1186           0 :     if( txn_out->accounts.nonce_idx_in_txn!=ULONG_MAX ) {
    1187           0 :       write_account_to_result(
    1188           0 :         &txn_out->accounts.keys[txn_out->accounts.nonce_idx_in_txn],
    1189           0 :         txn_out->accounts.rollback_nonce,
    1190           0 :         txn_result->rollback_accounts,
    1191           0 :         &txn_result->rollback_accounts_count,
    1192           0 :         &_l,
    1193           0 :         out_end
    1194           0 :       );
    1195           0 :     }
    1196           0 :   }
    1197             : 
    1198           0 :   if( !txn_out->err.is_fees_only ) {
    1199             :     /* Executed: capture fee payer and writable accounts. */
    1200           0 :     for( ulong j=0UL; j<txn_out->accounts.cnt; j++ ) {
    1201           0 :       if( !( fd_runtime_account_is_writable_idx( txn_in, txn_out, bank, (ushort)j ) ||
    1202           0 :              j==FD_FEE_PAYER_TXN_IDX ) ) {
    1203           0 :         continue;
    1204           0 :       }
    1205             : 
    1206           0 :       write_account_to_result(
    1207           0 :         &txn_out->accounts.keys[j],
    1208           0 :         txn_out->accounts.account[j].meta,
    1209           0 :         txn_result->modified_accounts,
    1210           0 :         &txn_result->modified_accounts_count,
    1211           0 :         &_l,
    1212           0 :         out_end
    1213           0 :       );
    1214           0 :     }
    1215           0 :   }
    1216             : 
    1217           0 :   *txn_result_out = txn_result;
    1218           0 :   return FD_SCRATCH_ALLOC_FINI( l, 1UL ) - (ulong)out_buf;
    1219           0 : }
    1220             : 
    1221             : void
    1222             : fd_dump_txn_to_protobuf( fd_runtime_t *      runtime,
    1223             :                          fd_bank_t *         bank,
    1224             :                          fd_txn_in_t const * txn_in,
    1225           0 :                          fd_txn_out_t *      txn_out ) {
    1226           0 :   fd_spad_t * spad = fd_spad_join( fd_spad_new( runtime->log.dumping_mem, 1UL<<28UL ) );
    1227             : 
    1228           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1229             :     // Get base58-encoded tx signature
    1230           0 :     const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
    1231           0 :     char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1232           0 :     fd_base58_encode_64( signatures[0], NULL, encoded_signature );
    1233             : 
    1234           0 :     fd_exec_test_txn_context_t txn_context_msg = FD_EXEC_TEST_TXN_CONTEXT_INIT_DEFAULT;
    1235           0 :     create_txn_context_protobuf_from_txn( &txn_context_msg, runtime, bank, txn_in, txn_out, spad );
    1236             : 
    1237             :     /* Output to file */
    1238           0 :     ulong        out_buf_size = 100UL<<20UL; // 100 MB
    1239           0 :     uchar *      out          = fd_spad_alloc( spad, alignof(uchar), out_buf_size );
    1240           0 :     pb_ostream_t stream       = pb_ostream_from_buffer( out, out_buf_size );
    1241           0 :     if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_context_msg ) ) {
    1242           0 :       char output_filepath[ PATH_MAX ];
    1243           0 :       snprintf( output_filepath, PATH_MAX, "%s/txn-%s.txnctx", runtime->log.dump_proto_ctx->dump_proto_output_dir, encoded_signature );
    1244           0 :       FILE * file = fopen(output_filepath, "wb");
    1245           0 :       if( file ) {
    1246           0 :         fwrite( out, 1, stream.bytes_written, file );
    1247           0 :         fclose( file );
    1248           0 :       }
    1249           0 :     }
    1250           0 :   } FD_SPAD_FRAME_END;
    1251           0 : }
    1252             : 
    1253             : void
    1254             : fd_dump_txn_context_to_protobuf( fd_txn_dump_ctx_t * txn_dump_ctx,
    1255             :                                  fd_runtime_t *      runtime,
    1256             :                                  fd_bank_t *         bank,
    1257             :                                  fd_txn_in_t const * txn_in,
    1258           0 :                                  fd_txn_out_t *      txn_out ) {
    1259           0 :   fd_txn_dump_context_reset( txn_dump_ctx );
    1260             : 
    1261           0 :   txn_dump_ctx->fixture.has_metadata = true;
    1262           0 :   strncpy(
    1263           0 :       txn_dump_ctx->fixture.metadata.fn_entrypoint,
    1264           0 :       "sol_compat_txn_execute_v1",
    1265           0 :       sizeof(txn_dump_ctx->fixture.metadata.fn_entrypoint)-1UL
    1266           0 :   );
    1267             : 
    1268           0 :   txn_dump_ctx->fixture.has_input = true;
    1269           0 :   create_txn_context_protobuf_from_txn( &txn_dump_ctx->fixture.input,
    1270           0 :                                         runtime, bank, txn_in, txn_out,
    1271           0 :                                         txn_dump_ctx->spad );
    1272           0 : }
    1273             : 
    1274             : void
    1275             : fd_dump_txn_result_to_protobuf( fd_txn_dump_ctx_t * txn_dump_ctx,
    1276             :                                 fd_txn_in_t const * txn_in,
    1277             :                                 fd_txn_out_t *      txn_out,
    1278             :                                 fd_bank_t *         bank,
    1279           0 :                                 int                 exec_res ) {
    1280           0 :   txn_dump_ctx->fixture.has_output = true;
    1281             : 
    1282           0 :   ulong  buf_sz = 100UL<<20UL;
    1283           0 :   void * buf    = fd_spad_alloc( txn_dump_ctx->spad, alignof(fd_exec_test_txn_result_t), buf_sz );
    1284           0 :   fd_exec_test_txn_result_t * result = NULL;
    1285           0 :   create_txn_result_protobuf_from_txn( &result, buf, buf_sz, txn_in, txn_out, bank, exec_res );
    1286           0 :   txn_dump_ctx->fixture.output = *result;
    1287           0 : }
    1288             : 
    1289             : void
    1290             : fd_dump_txn_fixture_to_file( fd_txn_dump_ctx_t *         txn_dump_ctx,
    1291             :                              fd_dump_proto_ctx_t const * dump_proto_ctx,
    1292           0 :                              fd_txn_in_t const *         txn_in ) {
    1293           0 :   const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
    1294           0 :   char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1295           0 :   fd_base58_encode_64( signatures[0], NULL, encoded_signature );
    1296             : 
    1297           0 :   FD_SPAD_FRAME_BEGIN( txn_dump_ctx->spad ) {
    1298           0 :     ulong        out_buf_size = 100UL<<20UL;
    1299           0 :     uchar *      out          = fd_spad_alloc( txn_dump_ctx->spad, alignof(uchar), out_buf_size );
    1300           0 :     pb_ostream_t stream       = pb_ostream_from_buffer( out, out_buf_size );
    1301             : 
    1302           0 :     char output_filepath[ PATH_MAX ];
    1303             : 
    1304           0 :     if( dump_proto_ctx->dump_txn_as_fixture ) {
    1305           0 :       if( pb_encode( &stream, FD_EXEC_TEST_TXN_FIXTURE_FIELDS, &txn_dump_ctx->fixture ) ) {
    1306           0 :         snprintf( output_filepath, PATH_MAX, "%s/txn-%s.fix", dump_proto_ctx->dump_proto_output_dir, encoded_signature );
    1307           0 :         FILE * file = fopen( output_filepath, "wb" );
    1308           0 :         if( file ) {
    1309           0 :           fwrite( out, 1, stream.bytes_written, file );
    1310           0 :           fclose( file );
    1311           0 :         }
    1312           0 :       }
    1313           0 :     } else {
    1314           0 :       if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_dump_ctx->fixture.input ) ) {
    1315           0 :         snprintf( output_filepath, PATH_MAX, "%s/txn-%s.txnctx", dump_proto_ctx->dump_proto_output_dir, encoded_signature );
    1316           0 :         FILE * file = fopen( output_filepath, "wb" );
    1317           0 :         if( file ) {
    1318           0 :           fwrite( out, 1, stream.bytes_written, file );
    1319           0 :           fclose( file );
    1320           0 :         }
    1321           0 :       }
    1322           0 :     }
    1323           0 :   } FD_SPAD_FRAME_END;
    1324           0 : }
    1325             : 
    1326             : void
    1327             : fd_dump_block_to_protobuf_collect_tx( fd_block_dump_ctx_t * dump_block_ctx,
    1328           0 :                                       fd_txn_p_t const *    txn ) {
    1329           0 :   if( FD_UNLIKELY( dump_block_ctx->txns_to_dump_cnt>=FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ) ) {
    1330           0 :     FD_LOG_ERR(( "Please increase FD_BLOCK_DUMP_CTX_MAX_TXN_CNT to dump more than %lu transactions.", FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ));
    1331           0 :     return;
    1332           0 :   }
    1333           0 :   fd_memcpy( &dump_block_ctx->txns_to_dump[dump_block_ctx->txns_to_dump_cnt++], txn, sizeof(fd_txn_p_t) );
    1334           0 : }
    1335             : 
    1336             : void
    1337             : fd_dump_block_to_protobuf( fd_block_dump_ctx_t *       dump_block_ctx,
    1338             :                            fd_banks_t *                banks,
    1339             :                            fd_bank_t *                 bank,
    1340             :                            fd_accdb_user_t *           accdb,
    1341           0 :                            fd_dump_proto_ctx_t const * dump_proto_ctx ) {
    1342           0 :   if( FD_UNLIKELY( dump_block_ctx==NULL ) ) {
    1343           0 :     FD_LOG_WARNING(( "Block dumping context may not be NULL when dumping blocks." ));
    1344           0 :     return;
    1345           0 :   }
    1346             : 
    1347           0 : FD_SPAD_FRAME_BEGIN( dump_block_ctx->spad ) {
    1348           0 :   if( FD_UNLIKELY( dump_proto_ctx==NULL ) ) {
    1349           0 :     FD_LOG_WARNING(( "Protobuf dumping context may not be NULL when dumping blocks." ));
    1350           0 :     return;
    1351           0 :   }
    1352             : 
    1353             :   /* Dump the block context */
    1354           0 :   create_block_context_protobuf_from_block( dump_block_ctx, banks, bank, accdb );
    1355             : 
    1356             :   /* Output to file */
    1357           0 :   ulong        out_buf_size = 1UL<<30UL; /* 1 GB */
    1358           0 :   uint8_t *    out          = fd_spad_alloc( dump_block_ctx->spad, alignof(uint8_t), out_buf_size );
    1359           0 :   pb_ostream_t stream       = pb_ostream_from_buffer( out, out_buf_size );
    1360           0 :   if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, &dump_block_ctx->block_context ) ) {
    1361           0 :     char output_filepath[ PATH_MAX ];
    1362           0 :     snprintf( output_filepath, PATH_MAX, "%s/block-%lu.blockctx", dump_proto_ctx->dump_proto_output_dir, fd_bank_slot_get( bank ) );
    1363           0 :     FILE * file = fopen(output_filepath, "wb");
    1364           0 :     if( file ) {
    1365           0 :       fwrite( out, 1, stream.bytes_written, file );
    1366           0 :       fclose( file );
    1367           0 :     }
    1368           0 :   }
    1369           0 : } FD_SPAD_FRAME_END;
    1370           0 : }
    1371             : 
    1372             : void
    1373             : fd_dump_vm_syscall_to_protobuf( fd_vm_t const * vm,
    1374           0 :                                 char const *    fn_name ) {
    1375           0 :   char const * syscall_name_filter = vm->instr_ctx->runtime->log.dump_proto_ctx->dump_syscall_name_filter;
    1376           0 :   if( syscall_name_filter && strlen( syscall_name_filter ) && strcmp( syscall_name_filter, fn_name ) ) {
    1377           0 :     return;
    1378           0 :   }
    1379             : 
    1380           0 :   fd_spad_t * spad = fd_spad_join( fd_spad_new( vm->instr_ctx->runtime->log.dumping_mem, 1UL<<28UL ) );
    1381             : 
    1382           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1383             : 
    1384           0 :   fd_ed25519_sig_t signature;
    1385           0 :   memcpy( signature, (uchar const *)vm->instr_ctx->txn_in->txn->payload + TXN( vm->instr_ctx->txn_in->txn )->signature_off, sizeof(fd_ed25519_sig_t) );
    1386           0 :   char encoded_signature[FD_BASE58_ENCODED_64_SZ];
    1387           0 :   fd_base58_encode_64( signature, NULL, encoded_signature );
    1388             : 
    1389           0 :   char filename[ PATH_MAX ];
    1390           0 :   snprintf( filename,
    1391           0 :           PATH_MAX,
    1392           0 :           "%s/syscall-%s-%s-%d-%hhu-%lu.sysctx",
    1393           0 :           vm->instr_ctx->runtime->log.dump_proto_ctx->dump_proto_output_dir,
    1394           0 :           fn_name,
    1395           0 :           encoded_signature,
    1396           0 :           vm->instr_ctx->runtime->instr.current_idx,
    1397           0 :           vm->instr_ctx->runtime->instr.stack_sz,
    1398           0 :           vm->cu );
    1399             : 
    1400             :   /* The generated filename should be unique for every call. Silently return otherwise. */
    1401           0 :   if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
    1402           0 :     return;
    1403           0 :   }
    1404             : 
    1405           0 :   fd_exec_test_syscall_context_t sys_ctx = FD_EXEC_TEST_SYSCALL_CONTEXT_INIT_ZERO;
    1406             : 
    1407             :   /* SyscallContext -> vm_ctx */
    1408           0 :   sys_ctx.has_vm_ctx = 1;
    1409             : 
    1410             :   /* SyscallContext -> vm_ctx -> heap_max */
    1411           0 :   sys_ctx.vm_ctx.heap_max = vm->heap_max; /* should be equiv. to txn_ctx->heap_sz */
    1412             : 
    1413             :   /* SyscallContext -> vm_ctx -> rodata */
    1414           0 :   sys_ctx.vm_ctx.rodata = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->rodata_sz ) );
    1415           0 :   sys_ctx.vm_ctx.rodata->size = (pb_size_t) vm->rodata_sz;
    1416           0 :   fd_memcpy( sys_ctx.vm_ctx.rodata->bytes, vm->rodata, vm->rodata_sz );
    1417             : 
    1418             :   /* SyscallContext -> vm_ctx -> r0-11 */
    1419           0 :   sys_ctx.vm_ctx.r0  = vm->reg[0];
    1420           0 :   sys_ctx.vm_ctx.r1  = vm->reg[1];
    1421           0 :   sys_ctx.vm_ctx.r2  = vm->reg[2];
    1422           0 :   sys_ctx.vm_ctx.r3  = vm->reg[3];
    1423           0 :   sys_ctx.vm_ctx.r4  = vm->reg[4];
    1424           0 :   sys_ctx.vm_ctx.r5  = vm->reg[5];
    1425           0 :   sys_ctx.vm_ctx.r6  = vm->reg[6];
    1426           0 :   sys_ctx.vm_ctx.r7  = vm->reg[7];
    1427           0 :   sys_ctx.vm_ctx.r8  = vm->reg[8];
    1428           0 :   sys_ctx.vm_ctx.r9  = vm->reg[9];
    1429           0 :   sys_ctx.vm_ctx.r10 = vm->reg[10];
    1430           0 :   sys_ctx.vm_ctx.r11 = vm->reg[11];
    1431             : 
    1432             :   /* SyscallContext -> vm_ctx -> entry_pc */
    1433           0 :   sys_ctx.vm_ctx.entry_pc = vm->entry_pc;
    1434             : 
    1435             :   /* SyscallContext -> vm_ctx -> return_data */
    1436           0 :   sys_ctx.vm_ctx.has_return_data = 1;
    1437             : 
    1438             :   /* SyscallContext -> vm_ctx -> return_data -> data */
    1439           0 :   sys_ctx.vm_ctx.return_data.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->instr_ctx->txn_out->details.return_data.len ) );
    1440           0 :   sys_ctx.vm_ctx.return_data.data->size = (pb_size_t)vm->instr_ctx->txn_out->details.return_data.len;
    1441           0 :   fd_memcpy( sys_ctx.vm_ctx.return_data.data->bytes, vm->instr_ctx->txn_out->details.return_data.data, vm->instr_ctx->txn_out->details.return_data.len );
    1442             : 
    1443             :   /* SyscallContext -> vm_ctx -> return_data -> program_id */
    1444           0 :   sys_ctx.vm_ctx.return_data.program_id = fd_spad_alloc( spad, alignof(pb_bytes_array_t), sizeof(fd_pubkey_t) );
    1445           0 :   sys_ctx.vm_ctx.return_data.program_id->size = sizeof(fd_pubkey_t);
    1446           0 :   fd_memcpy( sys_ctx.vm_ctx.return_data.program_id->bytes, vm->instr_ctx->txn_out->details.return_data.program_id.key, sizeof(fd_pubkey_t) );
    1447             : 
    1448             :   /* SyscallContext -> vm_ctx -> sbpf_version */
    1449           0 :   sys_ctx.vm_ctx.sbpf_version = (uint)vm->sbpf_version;
    1450             : 
    1451             :   /* SyscallContext -> instr_ctx */
    1452           0 :   sys_ctx.has_instr_ctx = 1;
    1453           0 :   create_instr_context_protobuf_from_instructions( &sys_ctx.instr_ctx,
    1454           0 :                                                    vm->instr_ctx->runtime,
    1455           0 :                                                    vm->instr_ctx->bank,
    1456           0 :                                                    vm->instr_ctx->txn_out,
    1457           0 :                                                    vm->instr_ctx->instr,
    1458           0 :                                                    spad );
    1459             : 
    1460             :   /* SyscallContext -> syscall_invocation */
    1461           0 :   sys_ctx.has_syscall_invocation = 1;
    1462             : 
    1463             :   /* SyscallContext -> syscall_invocation -> function_name */
    1464           0 :   sys_ctx.syscall_invocation.function_name.size = fd_uint_min( (uint) strlen(fn_name), sizeof(sys_ctx.syscall_invocation.function_name.bytes) );
    1465           0 :   fd_memcpy( sys_ctx.syscall_invocation.function_name.bytes,
    1466           0 :              fn_name,
    1467           0 :              sys_ctx.syscall_invocation.function_name.size );
    1468             : 
    1469             :   /* SyscallContext -> syscall_invocation -> heap_prefix */
    1470           0 :   sys_ctx.syscall_invocation.heap_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
    1471           0 :   sys_ctx.syscall_invocation.heap_prefix->size = (pb_size_t) vm->instr_ctx->txn_out->details.compute_budget.heap_size;
    1472           0 :   fd_memcpy( sys_ctx.syscall_invocation.heap_prefix->bytes, vm->heap, vm->instr_ctx->txn_out->details.compute_budget.heap_size );
    1473             : 
    1474             :   /* SyscallContext -> syscall_invocation -> stack_prefix */
    1475           0 :   pb_size_t stack_sz = (pb_size_t)FD_VM_STACK_MAX;
    1476           0 :   sys_ctx.syscall_invocation.stack_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( stack_sz ) );
    1477           0 :   sys_ctx.syscall_invocation.stack_prefix->size = stack_sz;
    1478           0 :   fd_memcpy( sys_ctx.syscall_invocation.stack_prefix->bytes, vm->stack, stack_sz );
    1479             : 
    1480             :   /* Output to file */
    1481           0 :   ulong out_buf_size = 1UL<<29UL; /* 128 MB */
    1482           0 :   uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
    1483           0 :   pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
    1484           0 :   if( pb_encode( &stream, FD_EXEC_TEST_SYSCALL_CONTEXT_FIELDS, &sys_ctx ) ) {
    1485           0 :     FILE * file = fopen(filename, "wb");
    1486           0 :     if( file ) {
    1487           0 :       fwrite( out, 1, stream.bytes_written, file );
    1488           0 :       fclose( file );
    1489           0 :     }
    1490           0 :   }
    1491           0 :   } FD_SPAD_FRAME_END;
    1492           0 : }

Generated by: LCOV version 1.14