LCOV - code coverage report
Current view: top level - flamenco/runtime/tests - fd_txn_harness.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 226 0.0 %
Date: 2026-02-27 05:40:51 Functions: 0 6 0.0 %

          Line data    Source code
       1             : #include "fd_solfuzz.h"
       2             : #include "fd_solfuzz_private.h"
       3             : #include "fd_txn_harness.h"
       4             : #include "fd_dump_pb.h"
       5             : #include "../fd_runtime.h"
       6             : #include "../../accdb/fd_accdb_admin_v1.h"
       7             : #include "../../accdb/fd_accdb_impl_v1.h"
       8             : #include "../../log_collector/fd_log_collector.h"
       9             : #include "../fd_system_ids.h"
      10             : 
      11             : /* Macros to append data to construct a serialized transaction
      12             :    without exceeding bounds */
      13           0 : #define FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _to_add, _sz ) __extension__({ \
      14           0 :    if( FD_UNLIKELY( (*_cur_data)+_sz>_begin+FD_TXN_MTU ) ) return ULONG_MAX;          \
      15           0 :    fd_memcpy( *_cur_data, _to_add, _sz );                                             \
      16           0 :    *_cur_data += _sz;                                                                 \
      17           0 : })
      18             : 
      19           0 : #define FD_CHECKED_ADD_CU16_TO_TXN_DATA( _begin, _cur_data, _to_add ) __extension__({ \
      20           0 :    do {                                                                               \
      21           0 :       uchar _buf[3];                                                                  \
      22           0 :       fd_bincode_encode_ctx_t _encode_ctx = { .data = _buf, .dataend = _buf+3 };      \
      23           0 :       fd_bincode_compact_u16_encode( &_to_add, &_encode_ctx );                        \
      24           0 :       ulong _sz = (ulong) ((uchar *)_encode_ctx.data - _buf );                        \
      25           0 :       FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _buf, _sz );                     \
      26           0 :    } while(0);                                                                        \
      27           0 : })
      28             : 
      29             : /* Retrieves the slot number from the clock sysvar account within the
      30             :    txn context.  Throws FD_LOG_ERR if the clock sysvar is not found
      31             :    or is malformed. */
      32             : static ulong
      33           0 : fd_solfuzz_pb_txn_ctx_get_slot( fd_exec_test_txn_context_t const * test_ctx ) {
      34           0 :   for( ulong i=0UL; i<test_ctx->account_shared_data_count; i++ ) {
      35           0 :     if( !memcmp( &test_ctx->account_shared_data[i].address, &fd_sysvar_clock_id, sizeof(fd_pubkey_t) ) ) {
      36           0 :       FD_TEST( test_ctx->account_shared_data[i].data->size==sizeof(fd_sol_sysvar_clock_t) );
      37           0 :       return FD_LOAD( ulong, test_ctx->account_shared_data[i].data->bytes );
      38           0 :     }
      39           0 :   }
      40           0 :   FD_LOG_ERR(( "invariant violation: clock sysvar account not found in txn context" ));
      41           0 : }
      42             : 
      43             : static void
      44           0 : fd_solfuzz_txn_ctx_destroy( fd_solfuzz_runner_t * runner ) {
      45           0 :   fd_accdb_v1_clear( runner->accdb_admin );
      46           0 :   fd_progcache_clear( runner->progcache_admin );
      47             : 
      48             :   /* In order to check for leaks in the workspace, we need to compact the
      49             :      allocators. Without doing this, empty superblocks may be retained
      50             :      by the fd_alloc instance, which mean we cannot check for leaks. */
      51           0 :   fd_alloc_compact( fd_accdb_user_v1_funk( runner->accdb )->alloc );
      52           0 :   fd_alloc_compact( runner->progcache_admin->funk->alloc );
      53           0 : }
      54             : 
      55             : /* Creates transaction execution context for a single test case.
      56             :    Returns a parsed txn descriptor on success and NULL on failure. */
      57             : static fd_txn_p_t *
      58             : fd_solfuzz_pb_txn_ctx_create( fd_solfuzz_runner_t *              runner,
      59           0 :                               fd_exec_test_txn_context_t const * test_ctx ) {
      60           0 :   fd_accdb_user_t * accdb = runner->accdb;
      61             : 
      62             :   /* Set up the funk transaction */
      63           0 :   ulong             slot = fd_solfuzz_pb_txn_ctx_get_slot( test_ctx );
      64           0 :   fd_funk_txn_xid_t xid  = { .ul = { slot, runner->bank->data->idx } };
      65           0 :   fd_funk_txn_xid_t parent_xid; fd_funk_txn_xid_set_root( &parent_xid );
      66           0 :   fd_accdb_attach_child        ( runner->accdb_admin,     &parent_xid, &xid );
      67           0 :   fd_progcache_txn_attach_child( runner->progcache_admin, &parent_xid, &xid );
      68             : 
      69             :   /* Initialize bank from input txn bank */
      70           0 :   fd_banks_clear_bank( runner->banks, runner->bank, 64UL );
      71           0 :   FD_TEST( test_ctx->has_bank );
      72           0 :   fd_exec_test_txn_bank_t const * txn_bank = &test_ctx->bank;
      73             : 
      74             :   /* Initialize blockhash queue */
      75           0 :   ulong blockhash_seed; FD_TEST( fd_rng_secure( &blockhash_seed, sizeof(ulong) ) );
      76           0 :   fd_blockhashes_t * blockhashes = fd_blockhashes_init( fd_bank_block_hash_queue_modify( runner->bank ), blockhash_seed );
      77           0 :   for( uint i=0UL; i<txn_bank->blockhash_queue_count; i++ ) {
      78           0 :     fd_exec_test_blockhash_queue_entry_t const * entry = &txn_bank->blockhash_queue[i];
      79             : 
      80           0 :     fd_hash_t hash                   = FD_LOAD( fd_hash_t, entry->blockhash );
      81           0 :     ulong     lamports_per_signature = entry->lamports_per_signature;
      82             : 
      83           0 :     fd_blockhash_info_t * blockhash = fd_blockhashes_push_new( blockhashes, &hash );
      84           0 :     blockhash->fee_calculator = (fd_fee_calculator_t){
      85           0 :       .lamports_per_signature = lamports_per_signature
      86           0 :     };
      87           0 :   }
      88             : 
      89             :   /* RBH lamports per signature. In the Agave harness this is set inside
      90             :      the fee rate governor itself. */
      91           0 :   fd_bank_rbh_lamports_per_sig_set( runner->bank, txn_bank->rbh_lamports_per_signature );
      92             : 
      93             :   /* Fee rate governor */
      94           0 :   FD_TEST( txn_bank->has_fee_rate_governor );
      95           0 :   fd_fee_rate_governor_t * fee_rate_governor = fd_bank_fee_rate_governor_modify( runner->bank );
      96           0 :   *fee_rate_governor = (fd_fee_rate_governor_t){
      97           0 :     .target_lamports_per_signature = txn_bank->fee_rate_governor.target_lamports_per_signature,
      98           0 :     .target_signatures_per_slot    = txn_bank->fee_rate_governor.target_signatures_per_slot,
      99           0 :     .min_lamports_per_signature    = txn_bank->fee_rate_governor.min_lamports_per_signature,
     100           0 :     .max_lamports_per_signature    = txn_bank->fee_rate_governor.max_lamports_per_signature,
     101           0 :     .burn_percent                  = (uchar)txn_bank->fee_rate_governor.burn_percent,
     102           0 :   };
     103             : 
     104             :   /* Slot and parent slot */
     105           0 :   fd_bank_slot_set( runner->bank, slot );
     106           0 :   fd_bank_parent_slot_set( runner->bank, slot-1UL );
     107             : 
     108             :   /* Total epoch stake */
     109           0 :   fd_bank_total_epoch_stake_set( runner->bank, txn_bank->total_epoch_stake );
     110             : 
     111             :   /* Epoch schedule */
     112           0 :   FD_TEST( txn_bank->has_epoch_schedule );
     113           0 :   fd_epoch_schedule_t * epoch_schedule = fd_bank_epoch_schedule_modify( runner->bank );
     114           0 :   *epoch_schedule = (fd_epoch_schedule_t){
     115           0 :     .slots_per_epoch             = txn_bank->epoch_schedule.slots_per_epoch,
     116           0 :     .leader_schedule_slot_offset = txn_bank->epoch_schedule.leader_schedule_slot_offset,
     117           0 :     .warmup                     = txn_bank->epoch_schedule.warmup,
     118           0 :     .first_normal_epoch         = txn_bank->epoch_schedule.first_normal_epoch,
     119           0 :     .first_normal_slot          = txn_bank->epoch_schedule.first_normal_slot
     120           0 :   };
     121             : 
     122             :   /* Rent */
     123           0 :   FD_TEST( txn_bank->has_rent );
     124           0 :   fd_rent_t * rent = fd_bank_rent_modify( runner->bank );
     125           0 :   *rent = (fd_rent_t){
     126           0 :     .lamports_per_uint8_year = txn_bank->rent.lamports_per_byte_year,
     127           0 :     .exemption_threshold     = txn_bank->rent.exemption_threshold,
     128           0 :     .burn_percent            = (uchar)txn_bank->rent.burn_percent
     129           0 :   };
     130             : 
     131             :   /* Features */
     132           0 :   FD_TEST( txn_bank->has_features );
     133           0 :   fd_exec_test_feature_set_t const * feature_set = &txn_bank->features;
     134           0 :   fd_features_t * features_bm = fd_bank_features_modify( runner->bank );
     135           0 :   if( !fd_solfuzz_pb_restore_features( features_bm, feature_set ) ) {
     136           0 :     return NULL;
     137           0 :   }
     138             : 
     139             :   /* Epoch */
     140           0 :   fd_bank_epoch_set( runner->bank, txn_bank->epoch );
     141             : 
     142             :   /* Load account states into funk (note this is different from the account keys):
     143             :     Account state = accounts to populate Funk
     144             :     Account keys = account keys that the transaction needs */
     145           0 :   for( ulong i = 0; i < test_ctx->account_shared_data_count; i++ ) {
     146             :     /* Load the accounts into the account manager
     147             :        Borrowed accounts get reset anyways - we just need to load the account somewhere */
     148           0 :     fd_solfuzz_pb_load_account( runner->runtime, accdb, &xid, &test_ctx->account_shared_data[i], i );
     149           0 :   }
     150             : 
     151             : 
     152           0 :   fd_bank_ticks_per_slot_set( runner->bank, 64 );
     153           0 :   fd_bank_slots_per_year_set( runner->bank, SECONDS_PER_YEAR * (1000000000.0 / (double)6250000) / (double)(fd_bank_ticks_per_slot_get( runner->bank )) );
     154             : 
     155             :   /* Restore sysvars from account context */
     156           0 :   fd_sysvar_cache_restore_fuzz( runner->bank, runner->accdb, &xid );
     157             : 
     158             :   /* Create the raw txn (https://solana.com/docs/core/transactions#transaction-size) */
     159           0 :   fd_txn_p_t * txn    = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
     160           0 :   ulong        msg_sz = fd_solfuzz_pb_txn_serialize( txn->payload, &test_ctx->tx );
     161           0 :   if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
     162           0 :     return NULL;
     163           0 :   }
     164             : 
     165             :   /* Set up txn descriptor from raw data */
     166           0 :   if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
     167           0 :     return NULL;
     168           0 :   }
     169             : 
     170           0 :   txn->payload_sz = msg_sz;
     171             : 
     172           0 :   return txn;
     173           0 : }
     174             : 
     175             : ulong
     176             : fd_solfuzz_pb_txn_serialize( uchar *                                      txn_raw_begin,
     177           0 :                              fd_exec_test_sanitized_transaction_t const * tx ) {
     178           0 :   uchar * txn_raw_cur_ptr = txn_raw_begin;
     179             : 
     180             :   /* Compact array of signatures (https://solana.com/docs/core/transactions#transaction)
     181             :      Note that although documentation interchangably refers to the signature cnt as a compact-u16
     182             :      and a u8, the max signature cnt is capped at 48 (due to txn size limits), so u8 and compact-u16
     183             :      is represented the same way anyways and can be parsed identically. */
     184             :   // Note: always create a valid txn with 1+ signatures, add an empty signature if none is provided
     185           0 :   uchar signature_cnt = fd_uchar_max( 1, (uchar) tx->signatures_count );
     186           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &signature_cnt, sizeof(uchar) );
     187           0 :   for( uchar i = 0; i < signature_cnt; ++i ) {
     188           0 :     fd_signature_t sig = {0};
     189           0 :     if( tx->signatures && tx->signatures[i] ) sig = FD_LOAD( fd_signature_t, tx->signatures[i]->bytes );
     190           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &sig, FD_TXN_SIGNATURE_SZ );
     191           0 :   }
     192             : 
     193             :   /* Message */
     194             :   /* For v0 transactions, the highest bit of the num_required_signatures is set, and an extra byte is used for the version.
     195             :      https://solanacookbook.com/guides/versioned-transactions.html#versioned-transactions-transactionv0
     196             : 
     197             :      We will always create a transaction with at least 1 signature, and cap the signature count to 127 to avoid
     198             :      collisions with the header_b0 tag. */
     199           0 :   uchar num_required_signatures = fd_uchar_max( 1, fd_uchar_min( 127, (uchar) tx->message.header.num_required_signatures ) );
     200           0 :   if( !tx->message.is_legacy ) {
     201           0 :     uchar header_b0 = (uchar) 0x80UL;
     202           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &header_b0, sizeof(uchar) );
     203           0 :   }
     204             : 
     205             :   /* Header (3 bytes) (https://solana.com/docs/core/transactions#message-header) */
     206           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
     207           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_signed_accounts, sizeof(uchar) );
     208           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_unsigned_accounts, sizeof(uchar) );
     209             : 
     210             :   /* Compact array of account addresses (https://solana.com/docs/core/transactions#compact-array-format) */
     211             :   // Array length is a compact u16
     212           0 :   ushort num_acct_keys = (ushort) tx->message.account_keys_count;
     213           0 :   FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, num_acct_keys );
     214           0 :   for( ushort i = 0; i < num_acct_keys; ++i ) {
     215           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
     216           0 :   }
     217             : 
     218             :   /* Recent blockhash (32 bytes) (https://solana.com/docs/core/transactions#recent-blockhash) */
     219             :   // Note: add an empty blockhash if none is provided
     220           0 :   fd_hash_t msg_rbh = {0};
     221           0 :   if( tx->message.recent_blockhash ) msg_rbh = FD_LOAD( fd_hash_t, tx->message.recent_blockhash->bytes );
     222           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &msg_rbh, sizeof(fd_hash_t) );
     223             : 
     224             :   /* Compact array of instructions (https://solana.com/docs/core/transactions#array-of-instructions) */
     225             :   // Instruction count is a compact u16
     226           0 :   ushort instr_count = (ushort) tx->message.instructions_count;
     227           0 :   FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, instr_count );
     228           0 :   for( ushort i = 0; i < instr_count; ++i ) {
     229             :     // Program ID index
     230           0 :     uchar program_id_index = (uchar) tx->message.instructions[i].program_id_index;
     231           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &program_id_index, sizeof(uchar) );
     232             : 
     233             :     // Compact array of account addresses
     234           0 :     ushort acct_count = (ushort) tx->message.instructions[i].accounts_count;
     235           0 :     FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, acct_count );
     236           0 :     for( ushort j = 0; j < acct_count; ++j ) {
     237           0 :       uchar account_index = (uchar) tx->message.instructions[i].accounts[j];
     238           0 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
     239           0 :     }
     240             : 
     241             :     // Compact array of 8-bit data
     242           0 :     pb_bytes_array_t * data = tx->message.instructions[i].data;
     243           0 :     ushort data_len;
     244           0 :     if( data ) {
     245           0 :       data_len = (ushort) data->size;
     246           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
     247           0 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data->bytes, data_len );
     248           0 :     } else {
     249           0 :       data_len = 0;
     250           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
     251           0 :     }
     252           0 :   }
     253             : 
     254             :   /* Address table lookups (N/A for legacy transactions) */
     255           0 :   ushort addr_table_cnt = 0;
     256           0 :   if( !tx->message.is_legacy ) {
     257             :     /* Compact array of address table lookups (https://solanacookbook.com/guides/versioned-transactions.html#compact-array-of-address-table-lookups) */
     258             :     // NOTE: The diagram is slightly wrong - the account key is a 32 byte pubkey, not a u8
     259           0 :     addr_table_cnt = (ushort) tx->message.address_table_lookups_count;
     260           0 :     FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, addr_table_cnt );
     261           0 :     for( ushort i = 0; i < addr_table_cnt; ++i ) {
     262             :       // Account key
     263           0 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.address_table_lookups[i].account_key, sizeof(fd_pubkey_t) );
     264             : 
     265             :       // Compact array of writable indexes
     266           0 :       ushort writable_count = (ushort) tx->message.address_table_lookups[i].writable_indexes_count;
     267           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, writable_count );
     268           0 :       for( ushort j = 0; j < writable_count; ++j ) {
     269           0 :         uchar writable_index = (uchar) tx->message.address_table_lookups[i].writable_indexes[j];
     270           0 :         FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &writable_index, sizeof(uchar) );
     271           0 :       }
     272             : 
     273             :       // Compact array of readonly indexes
     274           0 :       ushort readonly_count = (ushort) tx->message.address_table_lookups[i].readonly_indexes_count;
     275           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, readonly_count );
     276           0 :       for( ushort j = 0; j < readonly_count; ++j ) {
     277           0 :         uchar readonly_index = (uchar) tx->message.address_table_lookups[i].readonly_indexes[j];
     278           0 :         FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &readonly_index, sizeof(uchar) );
     279           0 :       }
     280           0 :     }
     281           0 :   }
     282             : 
     283           0 :   return (ulong)(txn_raw_cur_ptr - txn_raw_begin);
     284           0 : }
     285             : 
     286             : void
     287             : fd_solfuzz_txn_ctx_exec( fd_solfuzz_runner_t * runner,
     288             :                          fd_runtime_t *        runtime,
     289             :                          fd_txn_in_t const *   txn_in,
     290             :                          int *                 exec_res,
     291           0 :                          fd_txn_out_t *        txn_out ) {
     292             : 
     293           0 :   txn_out->err.is_committable = 1;
     294             : 
     295           0 :   runtime->log.enable_vm_tracing = runner->enable_vm_tracing;
     296           0 :   uchar * tracing_mem = NULL;
     297           0 :   if( runner->enable_vm_tracing ) {
     298           0 :     tracing_mem = fd_spad_alloc_check( runner->spad, FD_RUNTIME_VM_TRACE_STATIC_ALIGN, FD_RUNTIME_VM_TRACE_STATIC_FOOTPRINT * FD_MAX_INSTRUCTION_STACK_DEPTH );
     299           0 :   }
     300             : 
     301           0 :   runtime->accdb              = runner->accdb;
     302           0 :   runtime->progcache          = runner->progcache;
     303           0 :   runtime->status_cache       = NULL;
     304           0 :   runtime->log.tracing_mem    = tracing_mem;
     305           0 :   runtime->log.dumping_mem    = NULL;
     306           0 :   runtime->log.capture_ctx    = NULL;
     307           0 :   runtime->log.dump_proto_ctx = NULL;
     308           0 :   runtime->log.txn_dump_ctx   = NULL;
     309             : 
     310           0 :   fd_runtime_prepare_and_execute_txn( runtime, runner->bank, txn_in, txn_out );
     311           0 :   *exec_res = txn_out->err.txn_err;
     312           0 : }
     313             : 
     314             : ulong
     315             : fd_solfuzz_pb_txn_run( fd_solfuzz_runner_t * runner,
     316             :                        void const *          input_,
     317             :                        void **               output_,
     318             :                        void *                output_buf,
     319           0 :                        ulong                 output_bufsz ) {
     320           0 :   fd_exec_test_txn_context_t const * input  = fd_type_pun_const( input_ );
     321           0 :   fd_exec_test_txn_result_t **       output = fd_type_pun( output_ );
     322             : 
     323           0 :   FD_SPAD_FRAME_BEGIN( runner->spad ) {
     324             : 
     325             :     /* Setup the transaction context */
     326           0 :     fd_txn_p_t * txn = fd_solfuzz_pb_txn_ctx_create( runner, input );
     327           0 :     if( FD_UNLIKELY( txn==NULL ) ) {
     328           0 :       fd_solfuzz_txn_ctx_destroy( runner );
     329           0 :       return 0UL;
     330           0 :     }
     331             : 
     332             :     /* Execute the transaction against the runtime */
     333           0 :     int exec_res = 0;
     334           0 :     fd_runtime_t *       runtime = runner->runtime;
     335           0 :     fd_txn_in_t *        txn_in  = fd_spad_alloc( runner->spad, alignof(fd_txn_in_t), sizeof(fd_txn_in_t) );
     336           0 :     fd_txn_out_t *       txn_out = fd_spad_alloc( runner->spad, alignof(fd_txn_out_t), sizeof(fd_txn_out_t) );
     337           0 :     fd_log_collector_t * log     = fd_spad_alloc( runner->spad, alignof(fd_log_collector_t), sizeof(fd_log_collector_t) );
     338           0 :     runtime->log.log_collector = log;
     339           0 :     runtime->acc_pool = runner->acc_pool;
     340           0 :     txn_in->txn = txn;
     341           0 :     txn_in->bundle.is_bundle = 0;
     342           0 :     fd_solfuzz_txn_ctx_exec( runner, runtime, txn_in, &exec_res, txn_out );
     343             : 
     344             :     /* Build result directly into the caller-owned output_buf */
     345           0 :     fd_exec_test_txn_result_t * txn_result = NULL;
     346           0 :     ulong result_sz = create_txn_result_protobuf_from_txn(
     347           0 :         &txn_result,
     348           0 :         output_buf,
     349           0 :         output_bufsz,
     350           0 :         txn_in,
     351           0 :         txn_out,
     352           0 :         runner->bank,
     353           0 :         exec_res
     354           0 :     );
     355             : 
     356           0 :     txn_out->err.is_committable = 0;
     357           0 :     fd_runtime_cancel_txn( runner->runtime, txn_out );
     358           0 :     fd_solfuzz_txn_ctx_destroy( runner );
     359             : 
     360           0 :     *output = txn_result;
     361           0 :     return result_sz;
     362           0 :   } FD_SPAD_FRAME_END;
     363           0 : }

Generated by: LCOV version 1.14