LCOV - code coverage report
Current view: top level - flamenco/runtime/tests - fd_txn_harness.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 243 274 88.7 %
Date: 2026-11-11 12:40:43 Functions: 7 7 100.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 "../sysvar/fd_sysvar_epoch_schedule.h"
       7             : #include "../../alpenglow/fd_alpenglow.h"
       8             : #include "../../progcache/fd_progcache_admin.h"
       9             : #include "../../log_collector/fd_log_collector.h"
      10             : #include "../../../ballet/txn/fd_compact_u16.h"
      11             : #include "../../../disco/pack/fd_chkdup.h"
      12             : 
      13             : /* Macros to append data to construct a serialized transaction
      14             :    without exceeding bounds */
      15      361223 : #define FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _to_add, _sz ) __extension__({ \
      16      361223 :    if( FD_UNLIKELY( (*_cur_data)+_sz>_begin+FD_TXN_MTU ) ) return ULONG_MAX;          \
      17      361223 :    fd_memcpy( *_cur_data, _to_add, _sz );                                             \
      18      361223 :    *_cur_data += _sz;                                                                 \
      19      361223 : })
      20             : 
      21       80378 : #define FD_CHECKED_ADD_CU16_TO_TXN_DATA( _begin, _cur_data, _to_add ) __extension__({ \
      22       80378 :    do {                                                                               \
      23       80378 :       uchar _buf[3];                                                                  \
      24       80378 :       ulong _sz = (ulong)fd_cu16_enc( (ushort)_to_add, _buf );                        \
      25       80378 :       FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _buf, _sz );                     \
      26       80378 :    } while(0);                                                                        \
      27       80378 : })
      28             : 
      29             : static void
      30        7715 : fd_solfuzz_txn_ctx_destroy( fd_solfuzz_runner_t * runner ) {
      31        7715 :   fd_progcache_reset( runner->progcache->join );
      32             : 
      33             :   /* Purge the fork attached in ctx_create so the accdb fork pool slot
      34             :      is released back for reuse.  Without this, repeated harness
      35             :      invocations (e.g. under a fuzzer) exhaust max_live_slots and the
      36             :      next attach_child returns NULL, causing a segfault inside accdb. */
      37        7715 :   fd_accdb_purge( runner->accdb, runner->bank->accdb_fork_id );
      38        7715 :   int charge_busy = 0;
      39        7715 :   fd_accdb_background( runner->accdb, &charge_busy );
      40             : 
      41        7715 : }
      42             : 
      43             : /* Creates transaction execution context for a single test case.
      44             :    Returns a parsed txn descriptor on success and NULL on failure. */
      45             : static fd_txn_p_t *
      46             : fd_solfuzz_pb_txn_ctx_create( fd_solfuzz_runner_t *              runner,
      47        7724 :                               fd_exec_test_txn_context_t const * test_ctx ) {
      48        7724 :   fd_accdb_t * accdb = runner->accdb;
      49             : 
      50        7724 :   ulong slot = fd_solfuzz_pb_get_slot( test_ctx->account_shared_data, test_ctx->account_shared_data_count );
      51             : 
      52             :   /* Initialize bank from input txn bank */
      53        7724 :   fd_banks_clear_bank( runner->banks, runner->bank );
      54        7724 :   runner->bank->f.slot            = slot;
      55        7724 :   runner->bank->accdb_fork_id     = fd_accdb_attach_child( accdb, runner->root_fork_id );
      56        7724 :   runner->bank->parent_accdb_fork_id = runner->bank->accdb_fork_id;
      57        7724 :   runner->bank->progcache_fork_id = fd_progcache_attach_child( runner->progcache->join, fd_progcache_fork_id_initial() );
      58             : 
      59        7724 :   FD_TEST( test_ctx->has_bank );
      60        7724 :   fd_exec_test_txn_bank_t const * txn_bank = &test_ctx->bank;
      61             : 
      62             :   /* Blockhash queue */
      63        7724 :   fd_solfuzz_pb_restore_blockhash_queue( runner->bank, txn_bank->blockhash_queue, txn_bank->blockhash_queue_count );
      64             : 
      65             :   /* RBH lamports per signature. In the Agave harness this is set inside
      66             :      the fee rate governor itself. */
      67        7724 :   runner->bank->f.rbh_lamports_per_sig = txn_bank->rbh_lamports_per_signature;
      68             : 
      69             :   /* Fee rate governor */
      70        7724 :   FD_TEST( txn_bank->has_fee_rate_governor );
      71        7724 :   fd_solfuzz_pb_restore_fee_rate_governor( runner->bank, &txn_bank->fee_rate_governor );
      72             : 
      73             :   /* Parent slot */
      74        7724 :   runner->bank->f.parent_slot = slot-1UL;
      75             : 
      76             :   /* Total epoch stake */
      77        7724 :   runner->bank->f.total_epoch_stake = txn_bank->total_epoch_stake;
      78             : 
      79             :   /* Features */
      80        7724 :   FD_TEST( txn_bank->has_features );
      81        7724 :   fd_exec_test_feature_set_t const * feature_set = &txn_bank->features;
      82        7724 :   fd_features_t * features_bm = &runner->bank->f.features;
      83        7724 :   FD_TEST( fd_solfuzz_pb_restore_features( features_bm, feature_set ) );
      84             : 
      85             :   /* Load account states into accdb (note this is different from the account keys):
      86             :     Account state = accounts to populate Funk
      87             :     Account keys = account keys that the transaction needs */
      88      213133 :   for( ulong i = 0; i < test_ctx->account_shared_data_count; i++ ) {
      89             :     /* Load the accounts into the account manager
      90             :        Borrowed accounts get reset anyways - we just need to load the account somewhere */
      91      205409 :     fd_solfuzz_pb_load_account( runner->runtime, accdb, runner->bank->accdb_fork_id, &test_ctx->account_shared_data[i], i );
      92      205409 :   }
      93             : 
      94        7724 :   runner->bank->f.ticks_per_slot             = 64;
      95        7724 :   runner->bank->f.slot_params                = FD_SLOT_PARAMS_400MS;
      96        7724 :   runner->bank->f.slot_params.slots_per_year = SECONDS_PER_YEAR * (1000000000.0 / (double)6250000) / (double)(runner->bank->f.ticks_per_slot);
      97        7724 :   runner->bank->f.slot_params_default        = runner->bank->f.slot_params;
      98             : 
      99             :   /* Restore sysvars from account context */
     100        7724 :   fd_sysvar_cache_t * sysvar_cache = runner->runtime->sysvar_cache;
     101        7724 :   fd_sysvar_cache_restore_fuzz( sysvar_cache, runner->accdb, runner->bank->accdb_fork_id );
     102        7724 :   runner->runtime->sysvar_bank_seq = runner->bank->bank_seq;
     103             : 
     104             :   /* Epoch schedule */
     105        7724 :   FD_TEST( fd_sysvar_cache_epoch_schedule_read( sysvar_cache, &runner->bank->f.epoch_schedule ) );
     106             : 
     107             :   /* Epoch */
     108        7724 :   runner->bank->f.epoch = fd_slot_to_epoch( &runner->bank->f.epoch_schedule, slot, NULL );
     109             : 
     110             :   /* Rent */
     111        7724 :   FD_TEST( fd_sysvar_cache_rent_read( sysvar_cache, &runner->bank->f.rent ) );
     112             : 
     113             :   /* Alpenglow migration slot */
     114        7724 :   runner->bank->f.alpenglow_migration_slot = fd_alpenglow_migration_slot( runner->bank, accdb );
     115             : 
     116             :   /* Create the raw txn (https://solana.com/docs/core/transactions#transaction-size) */
     117        7724 :   fd_txn_p_t * txn    = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
     118        7724 :   ulong        msg_sz = fd_solfuzz_pb_txn_serialize( txn->payload, &test_ctx->tx );
     119        7724 :   if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
     120           0 :     return NULL;
     121           0 :   }
     122             : 
     123             :   /* Set up txn descriptor from raw data */
     124        7724 :   if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
     125           0 :     return NULL;
     126           0 :   }
     127             : 
     128             :   /* V1 transactions with duplicate addresses fail sanitization in
     129             :      Agave, so we should drop these. */
     130        7724 :   fd_txn_t const * txn_desc = TXN( txn );
     131        7724 :   if( txn_desc->transaction_version==FD_TXN_V1 ) {
     132           1 :     fd_rng_t    rng[1];
     133           1 :     fd_chkdup_t chkdup[1];
     134           1 :     fd_chkdup_t * chk = fd_chkdup_join( fd_chkdup_new( chkdup, fd_rng_join( fd_rng_new( rng, 0U, 0UL ) ) ) );
     135           1 :     fd_acct_addr_t const * addrs = fd_txn_get_acct_addrs( txn_desc, txn->payload );
     136           1 :     if( FD_UNLIKELY( fd_chkdup_check( chk, addrs, txn_desc->acct_addr_cnt, NULL, 0UL ) ) ) {
     137           0 :       return NULL;
     138           0 :     }
     139           1 :   }
     140             : 
     141        7724 :   txn->payload_sz = (ushort)msg_sz;
     142             : 
     143        7724 :   return txn;
     144        7724 : }
     145             : 
     146             : static uchar
     147        9181 : fd_solfuzz_pb_txn_version( fd_exec_test_transaction_message_t const * message ) {
     148        9181 :   switch( message->version ) {
     149        1716 :   case FD_EXEC_TEST_TRANSACTION_VERSION_TRANSACTION_VERSION_LEGACY: return FD_TXN_VLEGACY;
     150           1 :   case FD_EXEC_TEST_TRANSACTION_VERSION_TRANSACTION_VERSION_V1:     return FD_TXN_V1;
     151        7464 :   default:                                                          return FD_TXN_V0;
     152        9181 :   }
     153        9181 : }
     154             : 
     155             : /* Serializes a Transaction V1 (SIMD-0385): message first, then exactly
     156             :    num_required_signatures signatures.  An absent v1_config is an empty
     157             :    config mask. */
     158             : static ulong
     159             : fd_solfuzz_pb_txn_serialize_v1( uchar *                                      txn_raw_begin,
     160           1 :                                 fd_exec_test_sanitized_transaction_t const * tx ) {
     161           1 :   uchar * txn_raw_cur_ptr = txn_raw_begin;
     162           1 :   fd_exec_test_transaction_config_t const empty_cfg = {0};
     163           1 :   fd_exec_test_transaction_config_t const * cfg = tx->message.has_v1_config ? &tx->message.v1_config : &empty_cfg;
     164             : 
     165             :   /* Version byte: high bit set, version 1 */
     166           1 :   uchar version_byte = (uchar)(0x80U | FD_TXN_V1);
     167           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &version_byte, sizeof(uchar) );
     168             : 
     169             :   /* Header (3 bytes).  V1 has no separate signature count: exactly
     170             :      num_required_signatures signatures are appended at the end. */
     171           0 :   uchar num_required_signatures = fd_uchar_max( 1, (uchar)tx->message.header.num_required_signatures );
     172           1 :   uchar ro_signed_cnt   = (uchar)tx->message.header.num_readonly_signed_accounts;
     173           1 :   uchar ro_unsigned_cnt = (uchar)tx->message.header.num_readonly_unsigned_accounts;
     174           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
     175           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &ro_signed_cnt,           sizeof(uchar) );
     176           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &ro_unsigned_cnt,         sizeof(uchar) );
     177             : 
     178             :   /* Config mask (u32 LE): bits 0+1 priority fee, 2 CU limit, 3 loaded
     179             :      accounts data size limit, 4 heap size.  Field presence == bit set. */
     180           0 :   uint config_mask = 0U;
     181           1 :   if( cfg->has_priority_fee                    ) config_mask |= 0x03U;
     182           1 :   if( cfg->has_compute_unit_limit              ) config_mask |= 0x04U;
     183           1 :   if( cfg->has_loaded_accounts_data_size_limit ) config_mask |= 0x08U;
     184           1 :   if( cfg->has_heap_size                       ) config_mask |= 0x10U;
     185           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &config_mask, sizeof(uint) );
     186             : 
     187             :   /* Recent blockhash */
     188           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.recent_blockhash, sizeof(fd_hash_t) );
     189             : 
     190             :   /* Instruction count and static account count (u8 each) */
     191           0 :   uchar instr_cnt     = (uchar)tx->message.instructions_count;
     192           1 :   uchar acct_addr_cnt = (uchar)tx->message.account_keys_count;
     193           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &instr_cnt,     sizeof(uchar) );
     194           1 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &acct_addr_cnt, sizeof(uchar) );
     195             : 
     196             :   /* Static account keys */
     197          28 :   for( uchar i=0; i<acct_addr_cnt; i++ ) {
     198          27 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
     199          27 :   }
     200             : 
     201             :   /* Config values, only the present ones, in ascending mask-bit order */
     202           1 :   if( cfg->has_priority_fee ) {
     203           0 :     ulong v = cfg->priority_fee;
     204           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(ulong) );
     205           0 :   }
     206           1 :   if( cfg->has_compute_unit_limit ) {
     207           0 :     uint v = cfg->compute_unit_limit;
     208           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(uint) );
     209           0 :   }
     210           1 :   if( cfg->has_loaded_accounts_data_size_limit ) {
     211           0 :     uint v = cfg->loaded_accounts_data_size_limit;
     212           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(uint) );
     213           0 :   }
     214           1 :   if( cfg->has_heap_size ) {
     215           0 :     uint v = cfg->heap_size;
     216           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(uint) );
     217           0 :   }
     218             : 
     219             :   /* Instruction headers: program_id (u8), account count (u8), data length (u16 LE) */
     220           4 :   for( uchar i=0; i<instr_cnt; i++ ) {
     221           3 :     fd_exec_test_compiled_instruction_t const * ix = &tx->message.instructions[i];
     222           3 :     uchar  program_id_index = (uchar)ix->program_id_index;
     223           3 :     uchar  acct_cnt         = (uchar)ix->accounts_count;
     224           3 :     ushort data_len         = (ushort)( ix->data ? ix->data->size : 0U );
     225           3 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &program_id_index, sizeof(uchar)  );
     226           3 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &acct_cnt,         sizeof(uchar)  );
     227           3 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &data_len,         sizeof(ushort) );
     228           3 :   }
     229             : 
     230             :   /* Instruction payloads: account indices then data, same truncation as the headers */
     231           4 :   for( uchar i=0; i<instr_cnt; i++ ) {
     232           3 :     fd_exec_test_compiled_instruction_t const * ix = &tx->message.instructions[i];
     233           3 :     uchar  acct_cnt = (uchar)ix->accounts_count;
     234           3 :     ushort data_len = (ushort)( ix->data ? ix->data->size : 0U );
     235          30 :     for( uchar j=0; j<acct_cnt; j++ ) {
     236          27 :       uchar account_index = (uchar)ix->accounts[j];
     237          27 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
     238          27 :     }
     239           3 :     if( data_len ) FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, ix->data->bytes, data_len );
     240           3 :   }
     241             : 
     242             :   /* Signatures: exactly num_required_signatures of them (zero-filled if
     243             :      fewer were provided, extras ignored) */
     244           2 :   for( uchar i=0; i<num_required_signatures; i++ ) {
     245           1 :     fd_signature_t sig = {0};
     246           1 :     if( i<tx->signatures_count && tx->signatures && tx->signatures[i] ) sig = FD_LOAD( fd_signature_t, tx->signatures[i]->bytes );
     247           1 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &sig, FD_TXN_SIGNATURE_SZ );
     248           1 :   }
     249             : 
     250           1 :   return (ulong)(txn_raw_cur_ptr - txn_raw_begin);
     251           1 : }
     252             : 
     253             : ulong
     254             : fd_solfuzz_pb_txn_serialize( uchar *                                      txn_raw_begin,
     255        9182 :                              fd_exec_test_sanitized_transaction_t const * tx ) {
     256        9182 :   uchar * txn_raw_cur_ptr = txn_raw_begin;
     257             : 
     258        9182 :   uchar version = fd_solfuzz_pb_txn_version( &tx->message );
     259        9182 :   if( version==FD_TXN_V1 ) return fd_solfuzz_pb_txn_serialize_v1( txn_raw_begin, tx );
     260             : 
     261             :   /* Compact array of signatures (https://solana.com/docs/core/transactions#transaction)
     262             :      Note that although documentation interchangeably refers to the signature cnt as a compact-u16
     263             :      and a u8, the max signature cnt is capped at 48 (due to txn size limits), so u8 and compact-u16
     264             :      is represented the same way anyways and can be parsed identically. */
     265             :   // Note: always create a valid txn with 1+ signatures, add an empty signature if none is provided
     266        9181 :   uchar signature_cnt = fd_uchar_max( 1, (uchar) tx->signatures_count );
     267        9181 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &signature_cnt, sizeof(uchar) );
     268       32477 :   for( uchar i = 0; i < signature_cnt; ++i ) {
     269       23296 :     fd_signature_t sig = {0};
     270       23296 :     if( tx->signatures && tx->signatures[i] ) sig = FD_LOAD( fd_signature_t, tx->signatures[i]->bytes );
     271       23296 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &sig, FD_TXN_SIGNATURE_SZ );
     272       23296 :   }
     273             : 
     274             :   /* Message */
     275             :   /* For v0 transactions, the highest bit of the num_required_signatures is set, and an extra byte is used for the version.
     276             :      https://solanacookbook.com/guides/versioned-transactions.html#versioned-transactions-transactionv0
     277             : 
     278             :      We will always create a transaction with at least 1 signature, and cap the signature count to 127 to avoid
     279             :      collisions with the header_b0 tag. */
     280        9181 :   uchar num_required_signatures = fd_uchar_max( 1, fd_uchar_min( 127, (uchar) tx->message.header.num_required_signatures ) );
     281        9181 :   if( version!=FD_TXN_VLEGACY ) {
     282        7460 :     uchar header_b0 = (uchar) 0x80UL;
     283        7460 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &header_b0, sizeof(uchar) );
     284        7460 :   }
     285             : 
     286             :   /* Header (3 bytes) (https://solana.com/docs/core/transactions#message-header) */
     287       18362 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
     288        9181 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_signed_accounts, sizeof(uchar) );
     289        9181 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_unsigned_accounts, sizeof(uchar) );
     290             : 
     291             :   /* Compact array of account addresses (https://solana.com/docs/core/transactions#compact-array-format) */
     292             :   // Array length is a compact u16
     293           0 :   ushort num_acct_keys = (ushort) tx->message.account_keys_count;
     294        9181 :   FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, num_acct_keys );
     295       54888 :   for( ushort i = 0; i < num_acct_keys; ++i ) {
     296       45707 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
     297       45707 :   }
     298             : 
     299             :   /* Recent blockhash (32 bytes) (https://solana.com/docs/core/transactions#recent-blockhash) */
     300             :   // Note: add an empty blockhash if none is provided
     301       18362 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.recent_blockhash, sizeof(fd_hash_t) );
     302             : 
     303             :   /* Compact array of instructions (https://solana.com/docs/core/transactions#array-of-instructions) */
     304             :   // Instruction count is a compact u16
     305           0 :   ushort instr_count = (ushort) tx->message.instructions_count;
     306       18362 :   FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, instr_count );
     307       30786 :   for( ushort i = 0; i < instr_count; ++i ) {
     308             :     // Program ID index
     309       21605 :     uchar program_id_index = (uchar) tx->message.instructions[i].program_id_index;
     310       21605 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &program_id_index, sizeof(uchar) );
     311             : 
     312             :     // Compact array of account addresses
     313           0 :     ushort acct_count = (ushort) tx->message.instructions[i].accounts_count;
     314       21605 :     FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, acct_count );
     315       88614 :     for( ushort j = 0; j < acct_count; ++j ) {
     316       67009 :       uchar account_index = (uchar) tx->message.instructions[i].accounts[j];
     317       67009 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
     318       67009 :     }
     319             : 
     320             :     // Compact array of 8-bit data
     321       21605 :     fd_pb_bytes_array_t * data = tx->message.instructions[i].data;
     322       21605 :     ushort data_len;
     323       21605 :     if( data ) {
     324       19573 :       data_len = (ushort) data->size;
     325       19573 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
     326       39146 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data->bytes, data_len );
     327       39146 :     } else {
     328        2032 :       data_len = 0;
     329        2032 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
     330        2032 :     }
     331       21605 :   }
     332             : 
     333             :   /* Address table lookups (N/A for legacy transactions) */
     334        9181 :   ushort addr_table_cnt = 0;
     335        9181 :   if( version!=FD_TXN_VLEGACY ) {
     336             :     /* Compact array of address table lookups (https://solanacookbook.com/guides/versioned-transactions.html#compact-array-of-address-table-lookups) */
     337             :     // NOTE: The diagram is slightly wrong - the account key is a 32 byte pubkey, not a u8
     338        7456 :     addr_table_cnt = (ushort) tx->message.address_table_lookups_count;
     339        7456 :     FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, addr_table_cnt );
     340       13131 :     for( ushort i = 0; i < addr_table_cnt; ++i ) {
     341             :       // Account key
     342        5675 :       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) );
     343             : 
     344             :       // Compact array of writable indexes
     345           0 :       ushort writable_count = (ushort) tx->message.address_table_lookups[i].writable_indexes_count;
     346        5675 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, writable_count );
     347       27731 :       for( ushort j = 0; j < writable_count; ++j ) {
     348       22056 :         uchar writable_index = (uchar) tx->message.address_table_lookups[i].writable_indexes[j];
     349       22056 :         FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &writable_index, sizeof(uchar) );
     350       22056 :       }
     351             : 
     352             :       // Compact array of readonly indexes
     353        5675 :       ushort readonly_count = (ushort) tx->message.address_table_lookups[i].readonly_indexes_count;
     354        5675 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, readonly_count );
     355       28159 :       for( ushort j = 0; j < readonly_count; ++j ) {
     356       22484 :         uchar readonly_index = (uchar) tx->message.address_table_lookups[i].readonly_indexes[j];
     357       22484 :         FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &readonly_index, sizeof(uchar) );
     358       22484 :       }
     359        5675 :     }
     360        7456 :   }
     361             : 
     362        9181 :   return (ulong)(txn_raw_cur_ptr - txn_raw_begin);
     363        9181 : }
     364             : 
     365             : void
     366             : fd_solfuzz_txn_ctx_exec( fd_solfuzz_runner_t * runner,
     367             :                          fd_runtime_t *        runtime,
     368             :                          fd_txn_in_t const *   txn_in,
     369             :                          int *                 exec_res,
     370             :                          fd_txn_out_t *        txn_out,
     371        8175 :                          int                   reclaim_accounts ) {
     372             : 
     373        8175 :   txn_out->err.is_committable = 1;
     374             : 
     375        8175 :   runtime->log.enable_vm_tracing = runner->enable_vm_tracing;
     376        8175 :   uchar * tracing_mem = NULL;
     377        8175 :   if( runner->enable_vm_tracing ) {
     378           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 );
     379           0 :   }
     380             : 
     381        8175 :   runtime->accdb                 = runner->accdb;
     382        8175 :   runtime->progcache             = runner->progcache;
     383        8175 :   runtime->status_cache          = NULL;
     384        8175 :   runtime->log.tracing_mem       = tracing_mem;
     385        8175 :   runtime->log.dumping_mem       = NULL;
     386        8175 :   runtime->log.capture_ctx       = NULL;
     387        8175 :   runtime->log.dump_proto_ctx    = NULL;
     388        8175 :   runtime->log.txn_dump_ctx      = NULL;
     389        8175 :   runtime->fuzz.enabled          = 1;
     390        8175 :   runtime->fuzz.reclaim_accounts = reclaim_accounts;
     391             : 
     392        8175 :   fd_runtime_prepare_and_execute_txn( runtime, runner->bank, txn_in, txn_out );
     393        8175 :   *exec_res = txn_out->err.txn_err;
     394        8175 : }
     395             : 
     396             : ulong
     397             : fd_solfuzz_pb_txn_run( fd_solfuzz_runner_t * runner,
     398             :                        void const *          input_,
     399             :                        void **               output_,
     400             :                        void *                output_buf,
     401        7724 :                        ulong                 output_bufsz ) {
     402        7724 :   fd_exec_test_txn_context_t const * input  = fd_type_pun_const( input_ );
     403        7724 :   fd_exec_test_txn_result_t **       output = fd_type_pun( output_ );
     404             : 
     405        7724 :   FD_SPAD_FRAME_BEGIN( runner->spad ) {
     406             : 
     407             :     /* Setup the transaction context */
     408        7724 :     fd_txn_p_t * txn = fd_solfuzz_pb_txn_ctx_create( runner, input );
     409        7724 :     if( FD_UNLIKELY( txn==NULL ) ) {
     410           0 :       fd_solfuzz_txn_ctx_destroy( runner );
     411           0 :       return 0UL;
     412           0 :     }
     413             : 
     414             :     /* Execute the transaction against the runtime */
     415        7724 :     int exec_res = 0;
     416        7724 :     fd_runtime_t *       runtime = runner->runtime;
     417        7724 :     fd_txn_in_t *        txn_in  = fd_spad_alloc( runner->spad, alignof(fd_txn_in_t), sizeof(fd_txn_in_t) );
     418        7724 :     fd_txn_out_t *       txn_out = fd_spad_alloc( runner->spad, alignof(fd_txn_out_t), sizeof(fd_txn_out_t) );
     419        7724 :     fd_log_collector_t * log     = fd_spad_alloc( runner->spad, alignof(fd_log_collector_t), sizeof(fd_log_collector_t) );
     420        7724 :     runtime->log.log_collector = log;
     421        7724 :     txn_in->txn = txn;
     422        7724 :     txn_in->bundle.is_bundle = 0;
     423        7724 :     fd_solfuzz_txn_ctx_exec( runner, runtime, txn_in, &exec_res, txn_out, 0 );
     424             : 
     425             :     /* Build result directly into the caller-owned output_buf */
     426        7724 :     fd_exec_test_txn_result_t * txn_result = NULL;
     427        7724 :     ulong result_sz = create_txn_result_protobuf_from_txn(
     428        7724 :         &txn_result,
     429        7724 :         output_buf,
     430        7724 :         output_bufsz,
     431        7724 :         txn_in,
     432        7724 :         txn_out,
     433        7724 :         exec_res
     434        7724 :     );
     435             : 
     436        7724 :     fd_solfuzz_direct_mapping_handle_cu_exhaustion(
     437        7724 :         runner, txn_out->details.compute_budget.compute_meter, exec_res,
     438        7724 :         txn_result->modified_accounts, txn_result->modified_accounts_count );
     439             : 
     440        7724 :     txn_out->err.is_committable = 0;
     441        7724 :     fd_runtime_cancel_txn( runner->runtime, NULL, NULL, txn_out );
     442        7724 :     fd_solfuzz_txn_ctx_destroy( runner );
     443             : 
     444        7724 :     *output = txn_result;
     445        7724 :     return result_sz;
     446        7724 :   } FD_SPAD_FRAME_END;
     447           0 : }

Generated by: LCOV version 1.14