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

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_runtime_tests_fd_dump_pb_h
       2             : #define HEADER_fd_src_flamenco_runtime_tests_fd_dump_pb_h
       3             : 
       4             : /* fd_dump_pb.h provides APIs for dumping syscalls, instructions,
       5             :    transactions, and blocks into a digestable and replayable Protobuf
       6             :    message. This is useful for debugging ledger test mismatches,
       7             :    collecting seed corpora, and gathering real data to test new
       8             :    harnesses.
       9             : 
      10             :    The following arguments can be added to the [capture] section of
      11             :    the TOML configuration file when running backtest:
      12             :       COMMON:
      13             :         - dump_proto_dir <output_dir>
      14             :             * Defines the output directory to dump Protobuf messages to
      15             :         - capture_start_slot <slot_number>
      16             :             * If present, defines the starting slot to dump Protobuf
      17             :               messages from
      18             : 
      19             :       HARNESS-SPECIFIC FILTERS:
      20             :         Instructions:
      21             :             - dump_instr_to_pb <0/1>
      22             :                 * If enabled, instructions will be dumped to the
      23             :                   specified output directory
      24             :                 * File name format is "instr-<base58_enc_sig>-<instruction_idx>.bin",
      25             :                   where instruction_idx is 1-indexed
      26             :                 * Each file represents a single instruction as a
      27             :                   serialized InstrContext Protobuf message
      28             :             - dump_instr_program_id_filter <base58_enc_program_id>
      29             :                 * If present, only instructions from the specified
      30             :                   program will be dumped
      31             : 
      32             :         Transactions:
      33             :             - dump_txn_to_pb <0/1>
      34             :                 * If enabled, transactions will be dumped to the
      35             :                   specified output directory
      36             :                 * By default, file name format is
      37             :                   "txn-<base58_enc_sig>.txnctx" containing a
      38             :                   serialized TxnContext Protobuf message
      39             :                 * If dump_txn_as_fixture is also set, the file format
      40             :                   is "txn-<base58_enc_sig>.fix" containing a
      41             :                   serialized TxnFixture (TxnContext + TxnResult)
      42             :             - dump_txn_as_fixture <0/1>
      43             :                 * If enabled (requires dump_txn_to_pb), transactions
      44             :                   are dumped as TxnFixture messages containing both
      45             :                   the input context (captured before execution) and
      46             :                   the output result (captured after execution)
      47             :                 * Useful for verifying transaction harness accuracy
      48             :                   against backtest results
      49             : 
      50             :         Blocks
      51             :             - dump_block_to_pb <0/1>
      52             :                 * If enabled, blocks will be dumped to the specified
      53             :                   output directory
      54             :                 * File name format is "block-<slot_number>.bin"
      55             :                 * Each file represents a single block as a serialized
      56             :                   BlockContext Protobuf message
      57             : 
      58             :         Syscalls:
      59             :             - dump_syscall_to_pb <0/1>
      60             :                 * If enabled, syscalls will be dumped to the specified
      61             :                   output directory
      62             :                 * File name format is "syscall-<fn_name>-<base58_enc_sig>-<program_id_idx>-<instr_stack_sz>-<cus_remaining>.bin"
      63             :             - dump_syscall_name_filter <fn_name>
      64             :                 * If present, only syscalls with the specified name will
      65             :                   be dumped, e.g. sol_memcpy_, sol_invoke_signed_rust
      66             : 
      67             :     Other notes:
      68             :         solana-conformance (https://github.com/firedancer-io/solana-conformance)
      69             :             * Allows decoding / executing / debugging of above Protobuf
      70             :               messages in an isolated environment
      71             :             * Allows execution result(s) comparison between Firedancer
      72             :               and Solana / Agave
      73             :             * See solana-conformance/README.md for functionality and use
      74             :               cases */
      75             : 
      76             : #include "../info/fd_instr_info.h"
      77             : #include "../../vm/fd_vm.h"
      78             : #include "generated/block.pb.h"
      79             : #include "../../../disco/fd_txn_p.h"
      80             : 
      81             : /* The amount of memory allocated towards dumping blocks from ledgers */
      82           0 : #define FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX (2UL<<30)
      83             : #define FD_BLOCK_DUMP_CTX_MAX_TXN_CNT  (10000UL)
      84             : 
      85             : FD_PROTOTYPES_BEGIN
      86             : 
      87             : /***** Dumping context *****/
      88             : 
      89             : /* Generic struct to hold options for dumping protobuf messages */
      90             : struct fd_dump_proto_ctx {
      91             :   /* General dumping options */
      92             :   char const *             dump_proto_output_dir;
      93             :   ulong                    dump_proto_start_slot;
      94             : 
      95             :   /* Instruction Capture */
      96             :   uint                     dump_instr_to_pb : 1;
      97             :   uint                     has_dump_instr_program_id_filter : 1;
      98             :   uchar                    dump_instr_program_id_filter[ 32 ];
      99             : 
     100             :   /* Transaction Capture */
     101             :   uint                     dump_txn_to_pb : 1;
     102             :   uint                     dump_txn_as_fixture : 1;
     103             : 
     104             :   /* Block Capture */
     105             :   uint                     dump_block_to_pb : 1;
     106             : 
     107             :   /* Syscall Capture */
     108             :   uint                     dump_syscall_to_pb : 1;
     109             :   char const *             dump_syscall_name_filter;
     110             : 
     111             : };
     112             : 
     113             : /* Persistent context for block dumping.  Maintains state about
     114             :    in-progress block dumping, such as any dynamic memory allocations
     115             :    (which live in the spad) and the block context message. */
     116             : struct fd_block_dump_ctx {
     117             :   /* Block context message */
     118             :   fd_exec_test_block_context_t block_context;
     119             : 
     120             :   /* Collected transactions to dump */
     121             :   fd_txn_p_t                   txns_to_dump[FD_BLOCK_DUMP_CTX_MAX_TXN_CNT];
     122             :   ulong                        txns_to_dump_cnt;
     123             : 
     124             :   /* Spad for dynamic memory allocations for the block context message*/
     125             :   fd_spad_t *                  spad;
     126             : };
     127             : typedef struct fd_block_dump_ctx fd_block_dump_ctx_t;
     128             : 
     129             : static inline ulong
     130           0 : fd_block_dump_context_align( void ) {
     131           0 :   return alignof(fd_block_dump_ctx_t);
     132           0 : }
     133             : 
     134             : static inline ulong
     135           0 : fd_block_dump_context_footprint( void ) {
     136           0 :   ulong l = FD_LAYOUT_INIT;
     137           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_block_dump_ctx_t), sizeof(fd_block_dump_ctx_t) );
     138           0 :   l = FD_LAYOUT_APPEND( l, fd_spad_align(), fd_spad_footprint( FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX ) );
     139           0 :   l = FD_LAYOUT_FINI( l, fd_spad_align() );
     140           0 :   return l;
     141           0 : }
     142             : 
     143             : static inline void *
     144           0 : fd_block_dump_context_new( void * mem ) {
     145           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     146           0 :   fd_block_dump_ctx_t * ctx  = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_block_dump_ctx_t), sizeof(fd_block_dump_ctx_t) );
     147           0 :   fd_spad_t *           spad = FD_SCRATCH_ALLOC_APPEND( l, fd_spad_align(),              fd_spad_footprint( FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX ) );
     148             : 
     149           0 :   ctx->spad             = fd_spad_new( spad, FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX );
     150           0 :   ctx->txns_to_dump_cnt = 0UL;
     151           0 :   return ctx;
     152           0 : }
     153             : 
     154             : static inline fd_block_dump_ctx_t *
     155           0 : fd_block_dump_context_join( void * mem ) {
     156           0 :   if( FD_UNLIKELY( !mem ) ) {
     157           0 :     FD_LOG_ERR(( "NULL mem" ));
     158           0 :     return NULL;
     159           0 :   }
     160             : 
     161           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_block_dump_context_align() ) ) ) {
     162           0 :     FD_LOG_ERR(( "misaligned mem" ));
     163           0 :     return NULL;
     164           0 :   }
     165             : 
     166           0 :   fd_block_dump_ctx_t * ctx = (fd_block_dump_ctx_t *)mem;
     167           0 :   ctx->spad                 = fd_spad_join( ctx->spad );
     168           0 :   return ctx;
     169           0 : }
     170             : 
     171             : static inline void *
     172           0 : fd_block_dump_context_delete( void * mem ) {
     173           0 :   if( FD_UNLIKELY( !mem ) ) {
     174           0 :     FD_LOG_WARNING(( "NULL mem" ));
     175           0 :     return NULL;
     176           0 :   }
     177           0 :   return mem;
     178           0 : }
     179             : 
     180             : static inline void *
     181           0 : fd_block_dump_context_leave( fd_block_dump_ctx_t * ctx ) {
     182           0 :   if( FD_UNLIKELY( !ctx ) ) {
     183           0 :     FD_LOG_WARNING(( "NULL ctx" ));
     184           0 :     return NULL;
     185           0 :   }
     186           0 :   return (void *)ctx;
     187           0 : }
     188             : 
     189             : /* Resets the block dump context to prepare for the next block. */
     190             : static inline void
     191           0 : fd_block_dump_context_reset( fd_block_dump_ctx_t * ctx ) {
     192           0 :   fd_memset( &ctx->block_context, 0, sizeof(ctx->block_context) );
     193           0 :   fd_spad_reset( ctx->spad );
     194           0 :   ctx->txns_to_dump_cnt = 0UL;
     195           0 : }
     196             : 
     197             : /* Persistent context for transaction dumping.  Holds the in-progress
     198             :    fixture message across the two dump phases (context before execution,
     199             :    effects after execution).  The spad is used for dynamic memory
     200             :    allocations referenced by the protobuf message. */
     201             : 
     202           0 : #define FD_TXN_DUMP_CTX_SPAD_MEM_MAX (1UL<<28) /* 256 MB */
     203             : 
     204             : struct fd_txn_dump_ctx {
     205             :   fd_exec_test_txn_fixture_t fixture;
     206             :   fd_spad_t *                spad;
     207             : };
     208             : typedef struct fd_txn_dump_ctx fd_txn_dump_ctx_t;
     209             : 
     210             : static inline ulong
     211           0 : fd_txn_dump_context_align( void ) {
     212           0 :   return alignof(fd_txn_dump_ctx_t);
     213           0 : }
     214             : 
     215             : static inline ulong
     216           0 : fd_txn_dump_context_footprint( void ) {
     217           0 :   ulong l = FD_LAYOUT_INIT;
     218           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txn_dump_ctx_t), sizeof(fd_txn_dump_ctx_t) );
     219           0 :   l = FD_LAYOUT_APPEND( l, fd_spad_align(), fd_spad_footprint( FD_TXN_DUMP_CTX_SPAD_MEM_MAX ) );
     220           0 :   l = FD_LAYOUT_FINI( l, fd_spad_align() );
     221           0 :   return l;
     222           0 : }
     223             : 
     224             : static inline void *
     225           0 : fd_txn_dump_context_new( void * mem ) {
     226           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     227           0 :   fd_txn_dump_ctx_t * ctx  = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txn_dump_ctx_t), sizeof(fd_txn_dump_ctx_t) );
     228           0 :   fd_spad_t *         spad = FD_SCRATCH_ALLOC_APPEND( l, fd_spad_align(),             fd_spad_footprint( FD_TXN_DUMP_CTX_SPAD_MEM_MAX ) );
     229             : 
     230           0 :   ctx->spad = fd_spad_new( spad, FD_TXN_DUMP_CTX_SPAD_MEM_MAX );
     231           0 :   fd_memset( &ctx->fixture, 0, sizeof(ctx->fixture) );
     232           0 :   return ctx;
     233           0 : }
     234             : 
     235             : static inline fd_txn_dump_ctx_t *
     236           0 : fd_txn_dump_context_join( void * mem ) {
     237           0 :   if( FD_UNLIKELY( !mem ) ) {
     238           0 :     FD_LOG_ERR(( "NULL mem" ));
     239           0 :     return NULL;
     240           0 :   }
     241             : 
     242           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_txn_dump_context_align() ) ) ) {
     243           0 :     FD_LOG_ERR(( "misaligned mem" ));
     244           0 :     return NULL;
     245           0 :   }
     246             : 
     247           0 :   fd_txn_dump_ctx_t * ctx = (fd_txn_dump_ctx_t *)mem;
     248           0 :   ctx->spad               = fd_spad_join( ctx->spad );
     249           0 :   return ctx;
     250           0 : }
     251             : 
     252             : static inline void *
     253           0 : fd_txn_dump_context_delete( void * mem ) {
     254           0 :   if( FD_UNLIKELY( !mem ) ) {
     255           0 :     FD_LOG_ERR(( "NULL mem" ));
     256           0 :     return NULL;
     257           0 :   }
     258           0 :   return mem;
     259           0 : }
     260             : 
     261             : static inline void *
     262           0 : fd_txn_dump_context_leave( fd_txn_dump_ctx_t * ctx ) {
     263           0 :   if( FD_UNLIKELY( !ctx ) ) {
     264           0 :     FD_LOG_ERR(( "NULL ctx" ));
     265           0 :     return NULL;
     266           0 :   }
     267           0 :   return (void *)ctx;
     268           0 : }
     269             : 
     270             : static inline void
     271           0 : fd_txn_dump_context_reset( fd_txn_dump_ctx_t * ctx ) {
     272           0 :   fd_memset( &ctx->fixture, 0, sizeof(ctx->fixture) );
     273           0 :   fd_spad_reset( ctx->spad );
     274           0 : }
     275             : 
     276             : /****** Actual dumping functions ******/
     277             : 
     278             : void
     279             : fd_dump_instr_to_protobuf( fd_runtime_t *      runtime,
     280             :                            fd_bank_t *         bank,
     281             :                            fd_txn_in_t const * txn_in,
     282             :                            fd_txn_out_t *      txn_out,
     283             :                            fd_instr_info_t *   instr,
     284             :                            ushort              instruction_idx );
     285             : 
     286             : void
     287             : fd_dump_txn_to_protobuf( fd_runtime_t *      runtime,
     288             :                          fd_bank_t *         bank,
     289             :                          fd_txn_in_t const * txn_in,
     290             :                          fd_txn_out_t *      txn_out );
     291             : 
     292             : /* Builds a TxnResult protobuf message from the transaction execution
     293             :    results into a caller-provided buffer.  The result struct and all
     294             :    sub-allocations (account data, return data) are bump-allocated
     295             :    within [out_buf, out_buf+out_bufsz).  Returns the number of bytes
     296             :    consumed from out_buf.  *txn_result_out is set to point to the
     297             :    result struct within out_buf.
     298             : 
     299             :    Shared between the transaction dumper and the fuzz harness. */
     300             : ulong
     301             : create_txn_result_protobuf_from_txn( fd_exec_test_txn_result_t ** txn_result_out,
     302             :                                      void *                       out_buf,
     303             :                                      ulong                        out_bufsz,
     304             :                                      fd_txn_in_t const *          txn_in,
     305             :                                      fd_txn_out_t *               txn_out,
     306             :                                      fd_bank_t *                  bank,
     307             :                                      int                          exec_res );
     308             : 
     309             : /* Transaction dumping (two-phase approach):
     310             : 
     311             :    Phase 1: fd_dump_txn_context_to_protobuf() captures the TxnContext
     312             :    before transaction execution into the txn dump context's fixture.
     313             : 
     314             :    Phase 2: fd_dump_txn_result_to_protobuf() captures the TxnResult
     315             :    after transaction execution into the txn dump context's fixture.
     316             : 
     317             :    fd_dump_txn_fixture_to_file() serializes and writes the fixture
     318             :    (or just the context) to disk.
     319             : 
     320             :    How it works in fd_runtime_prepare_and_execute_txn:
     321             : 
     322             :      fd_dump_txn_context_to_protobuf()   // before execution
     323             :      ... execute transaction ...
     324             :      fd_dump_txn_result_to_protobuf()    // after execution
     325             :      fd_dump_txn_fixture_to_file()       // write to disk */
     326             : void
     327             : fd_dump_txn_context_to_protobuf( fd_txn_dump_ctx_t * txn_dump_ctx,
     328             :                                  fd_runtime_t *      runtime,
     329             :                                  fd_bank_t *         bank,
     330             :                                  fd_txn_in_t const * txn_in,
     331             :                                  fd_txn_out_t *      txn_out );
     332             : 
     333             : void
     334             : fd_dump_txn_result_to_protobuf( fd_txn_dump_ctx_t * txn_dump_ctx,
     335             :                                 fd_txn_in_t const * txn_in,
     336             :                                 fd_txn_out_t *      txn_out,
     337             :                                 fd_bank_t *         bank,
     338             :                                 int                 exec_res );
     339             : 
     340             : void
     341             : fd_dump_txn_fixture_to_file( fd_txn_dump_ctx_t *       txn_dump_ctx,
     342             :                              fd_dump_proto_ctx_t const * dump_proto_ctx,
     343             :                              fd_txn_in_t const *         txn_in );
     344             : 
     345             : /* Block dumping is a little bit different than the other harnesses due
     346             :    to the architecture of our system.  Unlike the other dumping
     347             :    functions, blocks are dumped in two separate stages - transaction
     348             :    execution and block finalization.  Transactions are streamed into
     349             :    the exec tile as they come in from the dispatcher, so we maintain a
     350             :    running list of transaction descriptors to dump within the dumping
     351             :    context (using fd_dump_block_to_protobuf_collect_tx).  When the block
     352             :    is finalized, we take the accumulated transaction descriptors and
     353             :    convert them into Protobuf messages using
     354             :    fd_dump_block_to_protobuf, along with other fields in the slot /
     355             :    epoch context and any stake, vote, and transaction accounts.
     356             : 
     357             :    How it works in the replay tile:
     358             : 
     359             :    ...boot up backtest...
     360             :    unprivledged_init() {
     361             :      fd_block_dump_context_new()
     362             :    }
     363             : 
     364             :    ...start executing transactions...
     365             : 
     366             :    while( txns_to_execute ) {
     367             :      fd_dump_block_to_protobuf_collect_tx()
     368             :    }
     369             : 
     370             :    ...finalize the block...
     371             :    fd_dump_block_to_protobuf()
     372             :    fd_block_dump_context_reset() */
     373             : void
     374             : fd_dump_block_to_protobuf_collect_tx( fd_block_dump_ctx_t * dump_block_ctx,
     375             :                                       fd_txn_p_t const *    txn );
     376             : 
     377             : void
     378             : fd_dump_block_to_protobuf( fd_block_dump_ctx_t *       dump_block_ctx,
     379             :                            fd_banks_t *                banks,
     380             :                            fd_bank_t *                 bank,
     381             :                            fd_accdb_user_t *           accdb,
     382             :                            fd_dump_proto_ctx_t const * dump_proto_ctx );
     383             : 
     384             : void
     385             : fd_dump_vm_syscall_to_protobuf( fd_vm_t const * vm,
     386             :                                 char const *    fn_name );
     387             : 
     388             : FD_PROTOTYPES_END
     389             : 
     390             : #endif /* HEADER_fd_src_flamenco_runtime_tests_fd_dump_pb_h */

Generated by: LCOV version 1.14