LCOV - code coverage report
Current view: top level - app/shredcap - fd_blockstore2shredcap.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 97 0.0 %
Date: 2026-08-14 04:54:57 Functions: 0 5 0.0 %

          Line data    Source code
       1             : #include "../../discof/backtest/fd_backtest_src.h"
       2             : #include "../../discof/backtest/fd_shredcap.h"
       3             : #include "../../flamenco/gossip/fd_gossip_message.h"
       4             : #include "../../ballet/shred/fd_shred.h"
       5             : #include "../../util/fd_util.h"
       6             : #include "../../util/net/fd_pcapng.h"
       7             : #include "../../util/net/fd_ip4.h"
       8             : #include "../../util/net/fd_udp.h"
       9             : #include "../../discof/backtest/fd_libc_zstd.h"
      10             : 
      11             : #include <errno.h>
      12             : #include <stdio.h>
      13             : #include <fcntl.h>
      14             : 
      15             : /* fd_rocksdb_src_create constructs a ledger shred source reading an
      16             :    Agave RocksDB blockstore directory.  Returns a newly allocated
      17             :    backtest_src object on success, or NULL on failure (logs warning).
      18             :    Defined in fd_rocksdb_src.c.  Deliberately not part of fd_discof or
      19             :    the shared fd_backtest_src dispatcher: this converter is the only
      20             :    tool that links RocksDB; firedancer-dev only ingests pcap/shredcap
      21             :    captures. */
      22             : 
      23             : fd_backt_src_t *
      24             : fd_rocksdb_src_create( fd_backtest_src_opts_t const * opts );
      25             : 
      26             : /* Hardcoded constants */
      27             : 
      28           0 : #define IF_IDX_NET      (0)
      29           0 : #define IF_IDX_SHREDCAP (1)
      30           0 : #define SHRED_PORT      ((ushort)8003)
      31             : 
      32             : static int
      33           0 : usage( int rc ) {
      34           0 :   fputs(
      35           0 :     "\n"
      36           0 :     "Usage: fd_blockstore2shredcap --rocksdb <path> --out <path>\n"
      37           0 :     "\n"
      38           0 :     "Extract rooted blocks from Agave RocksDB.\n"
      39           0 :     "Produces shredcap 0.1 (pcapng) file containing shreds and bank hashes.\n"
      40           0 :     "\n"
      41           0 :     "  --rocksdb    <path>  Agave RocksDB directory\n"
      42           0 :     "  --out        <path>  File path to new shredcap file (fails if file already exists)\n"
      43           0 :     "  --start-slot <n>     Start slot (inclusive)\n"
      44           0 :     "  --end-slot   <n>     End slot (inclusive)\n"
      45           0 : #   if FD_HAS_ZSTD
      46           0 :     "  --zstd            Output compressed .pcapng.zst stream instead of raw pcapng\n"
      47           0 :     "  --zstd-level      Zstandard compression level\n"
      48           0 : #   endif
      49           0 :     "\n",
      50           0 :     stderr
      51           0 :   );
      52           0 :   return rc;
      53           0 : }
      54             : 
      55             : static void
      56             : write_bank_hash( FILE *      pcap,
      57             :                  ulong       slot,
      58             :                  ulong       shred_cnt,
      59           0 :                  uchar const bank_hash[32] ) {
      60           0 :   struct __attribute__((packed)) {
      61           0 :     uint type;
      62           0 :     fd_shredcap_bank_hash_v0_t bank_hash_rec;
      63           0 :   } packet;
      64           0 :   memset( &packet, 0, sizeof(packet) );
      65             : 
      66           0 :   packet.type = FD_SHREDCAP_TYPE_BANK_HASH_V0;
      67           0 :   fd_shredcap_bank_hash_v0_t * bank_hash_rec = &packet.bank_hash_rec;
      68           0 :   bank_hash_rec->slot           = slot;
      69           0 :   bank_hash_rec->data_shred_cnt = shred_cnt;
      70           0 :   memcpy( bank_hash_rec->bank_hash, bank_hash, 32UL );
      71             : 
      72           0 :   fd_pcapng_fwrite_pkt1( pcap, &packet, sizeof(packet), NULL, 0UL, IF_IDX_SHREDCAP, 0L );
      73           0 : }
      74             : 
      75             : static void
      76             : write_rooted_slot( FILE * pcap,
      77           0 :                    ulong  slot ) {
      78           0 :   struct __attribute__((packed)) {
      79           0 :     uint type;
      80           0 :     fd_shredcap_root_slot_v0_t root_slot_rec;
      81           0 :   } packet;
      82           0 :   memset( &packet, 0, sizeof(packet) );
      83             : 
      84           0 :   packet.type               = FD_SHREDCAP_TYPE_ROOT_SLOT_V0;
      85           0 :   packet.root_slot_rec.slot = slot;
      86             : 
      87           0 :   fd_pcapng_fwrite_pkt1( pcap, &packet, sizeof(packet), NULL, 0UL, IF_IDX_SHREDCAP, 0L );
      88           0 : }
      89             : 
      90             : static void
      91             : maybe_write_bank_hash( FILE *           pcap,
      92             :                        fd_backt_src_t * src,
      93             :                        ulong            slot,
      94           0 :                        ulong            shred_cnt ) {
      95           0 :   fd_backt_slot_info_t info;
      96           0 :   if( FD_UNLIKELY( !fd_backtest_src_slot_info( src, &info, slot ) ) ) return;
      97           0 :   if( FD_UNLIKELY( !info.bank_hash_set ) ) return;
      98           0 :   write_bank_hash( pcap, slot, shred_cnt, info.bank_hash.uc );
      99           0 :   if( info.rooted ) write_rooted_slot( pcap, slot );
     100           0 : }
     101             : 
     102             : static void
     103             : write_shred( FILE *       pcap,
     104           0 :              void const * shred ) {
     105           0 :   ulong shred_sz = fd_shred_sz( shred );
     106           0 :   FD_TEST( shred_sz<=FD_SHRED_MAX_SZ );
     107             : 
     108           0 :   struct __attribute__((packed)) {
     109           0 :     fd_ip4_hdr_t ip4;
     110           0 :     fd_udp_hdr_t udp;
     111           0 :     uchar shred[ FD_SHRED_MAX_SZ ];
     112           0 :   } packet;
     113             : 
     114           0 :   packet.ip4 = (fd_ip4_hdr_t) {
     115           0 :     .verihl       = FD_IP4_VERIHL( 4, 5 ),
     116           0 :     .tos          = 0,
     117           0 :     .net_tot_len  = fd_ushort_bswap( (ushort)( 28+shred_sz ) ),
     118           0 :     .net_id       = 0,
     119           0 :     .net_frag_off = fd_ushort_bswap( FD_IP4_HDR_FRAG_OFF_DF ),
     120           0 :     .ttl          = 64,
     121           0 :     .protocol     = FD_IP4_HDR_PROTOCOL_UDP,
     122           0 :     .check        = 0,
     123           0 :     .saddr        = FD_IP4_ADDR( 127,0,0,1 ),
     124           0 :     .daddr        = FD_IP4_ADDR( 127,0,0,1 ),
     125           0 :   };
     126           0 :   packet.ip4.check = fd_ip4_hdr_check_fast( &packet.ip4 );
     127           0 :   packet.udp = (fd_udp_hdr_t) {
     128           0 :     .net_sport = fd_ushort_bswap( 42424 ),
     129           0 :     .net_dport = fd_ushort_bswap( SHRED_PORT ),
     130           0 :     .net_len   = fd_ushort_bswap( (ushort)( 8+shred_sz ) ),
     131           0 :     .check     = 0,
     132           0 :   };
     133           0 :   fd_memcpy( packet.shred, shred, shred_sz );
     134             : 
     135           0 :   struct __attribute__((packed)) {
     136           0 :     ushort option_type;
     137           0 :     ushort option_sz;
     138           0 :     uint   pen;
     139           0 :     ushort magic;
     140           0 :     ushort gossip_tag;
     141           0 :   } option = {
     142           0 :     .option_type = 2989,   /* Custom Option containing binary octets, copyable */
     143           0 :     .option_sz   = 8,
     144           0 :     .pen         = 31592,  /* Jump Trading, LLC */
     145           0 :     .magic       = 0x4071, /* SOL! */
     146           0 :     .gossip_tag  = FD_GOSSIP_CONTACT_INFO_SOCKET_TVU
     147           0 :   };
     148             : 
     149           0 :   fd_pcapng_fwrite_pkt1( pcap, &packet, 28UL+shred_sz, &option, sizeof(option), IF_IDX_NET, 0L );
     150           0 : }
     151             : 
     152             : int
     153             : main( int     argc,
     154             :       char ** argv ) {
     155             :   if( fd_env_strip_cmdline_contains( &argc, &argv, "--help" ) ) return usage( 0 );
     156             : 
     157             :   char const * rocksdb_path = fd_env_strip_cmdline_cstr( &argc, &argv, "--rocksdb", NULL, NULL );
     158             :   char const * out_path     = fd_env_strip_cmdline_cstr( &argc, &argv, "--out",     NULL, NULL );
     159             :   char const * out_short    = fd_env_strip_cmdline_cstr( &argc, &argv, "--o",       NULL, NULL );
     160             :   if( !out_path ) out_path = out_short;
     161             : 
     162             :   int   use_zstd   = fd_env_strip_cmdline_contains( &argc, &argv, "--zstd"                      );
     163             :   int   zstd_level = fd_env_strip_cmdline_int     ( &argc, &argv, "--zstd-level", NULL,       3 );
     164             :   ulong zstd_bufsz = fd_env_strip_cmdline_ulong   ( &argc, &argv, "--zstd-bufsz", NULL, 4UL<<20 ); /* 4MB default */
     165             : # if !FD_HAS_ZSTD
     166             :   if( use_zstd ) FD_LOG_ERR(( "This build does not support ZSTD compression" ));
     167             :   (void)zstd_level;
     168             : # endif
     169             : 
     170             :   ulong start_slot = fd_env_strip_cmdline_ulong( &argc, &argv, "--start-slot", NULL, 0UL       );
     171             :   ulong end_slot   = fd_env_strip_cmdline_ulong( &argc, &argv, "--end-slot",   NULL, ULONG_MAX );
     172             : 
     173             :   if( FD_UNLIKELY( !rocksdb_path ) ) {
     174             :     fputs( "Error: --rocksdb not specified\n", stderr );
     175             :     return usage( 1 );
     176             :   }
     177             :   if( FD_UNLIKELY( !out_path ) ) {
     178             :     fputs( "Error: --out not specified\n", stderr );
     179             :     return usage( 1 );
     180             :   }
     181             : 
     182             :   fd_boot( &argc, &argv );
     183             : 
     184             :   fd_backtest_src_opts_t src_opts = {
     185             :     .path        = rocksdb_path,
     186             :     .rooted_only = 1,
     187             :     .code_shreds = 0,
     188             :   };
     189             :   fd_backt_src_t * src = fd_rocksdb_src_create( &src_opts );
     190             :   if( FD_UNLIKELY( !src ) ) FD_LOG_ERR(( "failed to open RocksDB at %s", rocksdb_path ));
     191             : 
     192             :   int out_fd = open( out_path, O_WRONLY|O_CREAT|O_EXCL, 0644 );
     193             :   if( FD_UNLIKELY( out_fd<0 ) ) FD_LOG_ERR(( "failed to create file %s (%i-%s)", out_path, errno, fd_io_strerror( errno ) ));
     194             :   FILE * out = fdopen( out_fd, "wb" );
     195             :   if( FD_UNLIKELY( !out ) ) FD_LOG_ERR(( "fdopen failed on %s (%i-%s)", out_path, errno, fd_io_strerror( errno ) ));
     196             : 
     197             : # if FD_HAS_ZSTD
     198             :   if( use_zstd ) {
     199             :     out = fd_zstd_wstream_open( out, zstd_level, zstd_bufsz );
     200             :     if( FD_UNLIKELY( !out ) ) FD_LOG_ERR(( "failed to initialize ZSTD compression" ));
     201             :   }
     202             : # endif
     203             : 
     204             :   /* Write pcapng header */
     205             :   {
     206             :     fd_pcapng_shb_opts_t shb_opts;
     207             :     fd_pcapng_shb_defaults( &shb_opts );
     208             :     if( FD_UNLIKELY( !fd_pcapng_fwrite_shb( &shb_opts, out ) ) ) FD_LOG_ERR(( "pcap write error" ));
     209             :   }
     210             :   uint idb_cnt = 0U;
     211             :   {
     212             :     fd_pcapng_idb_opts_t idb_opts = {
     213             :       .name     = "lo",
     214             :       .ip4_addr = { 127,0,0,1 }
     215             :     };
     216             :     if( FD_UNLIKELY( !fd_pcapng_fwrite_idb( FD_PCAPNG_LINKTYPE_IPV4, &idb_opts, out ) ) ) FD_LOG_ERR(( "pcap write error" ));
     217             :     FD_TEST( idb_cnt++==IF_IDX_NET );
     218             :   }
     219             :   {
     220             :     fd_pcapng_idb_opts_t idb_opts = {
     221             :       .name = "shredcap0",
     222             :     };
     223             :     if( FD_UNLIKELY( !fd_pcapng_fwrite_idb( FD_PCAPNG_LINKTYPE_USER0, &idb_opts, out ) ) ) FD_LOG_ERR(( "pcap write error" ));
     224             :     FD_TEST( idb_cnt++==IF_IDX_SHREDCAP );
     225             :   }
     226             : 
     227             :   ulong slot_cnt  = 0UL;
     228             :   ulong cur_slot  = ULONG_MAX;
     229             :   ulong buf_cnt   = 0UL;
     230             :   uchar raw[ FD_SHRED_MAX_SZ ];
     231             : 
     232             :   for(;;) {
     233             :     ulong sz = fd_backtest_src_shred( src, raw, sizeof(raw) );
     234             :     if( FD_UNLIKELY( sz==ULONG_MAX ) ) break;
     235             :     if( FD_UNLIKELY( sz==0UL      ) ) continue;
     236             : 
     237             :     fd_shred_t const * shred = fd_shred_parse( raw, sz, FD_SHRED_BLK_MAX );
     238             :     if( FD_UNLIKELY( !shred ) ) {
     239             :       FD_LOG_WARNING(( "skipping unparseable shred" ));
     240             :       continue;
     241             :     }
     242             : 
     243             :     ulong slot = shred->slot;
     244             : 
     245             :     if( FD_UNLIKELY( slot!=cur_slot ) ) {
     246             :       if( cur_slot!=ULONG_MAX && cur_slot>=start_slot && cur_slot<=end_slot && buf_cnt>0UL ) {
     247             :         maybe_write_bank_hash( out, src, cur_slot, buf_cnt );
     248             :         slot_cnt++;
     249             :       }
     250             :       cur_slot = slot;
     251             :       buf_cnt  = 0UL;
     252             :     }
     253             : 
     254             :     if( slot>end_slot ) break;
     255             :     if( slot<start_slot ) continue;
     256             : 
     257             :     write_shred( out, raw );
     258             :     buf_cnt++;
     259             :   }
     260             : 
     261             :   /* Write bank hash for last slot */
     262             :   if( cur_slot!=ULONG_MAX && cur_slot>=start_slot && cur_slot<=end_slot && buf_cnt>0UL ) {
     263             :     maybe_write_bank_hash( out, src, cur_slot, buf_cnt );
     264             :     slot_cnt++;
     265             :   }
     266             : 
     267             :   long off = ftell( out );
     268             :   FD_LOG_NOTICE(( "%s: wrote %lu slots, %ld bytes", out_path, slot_cnt, off ));
     269             : 
     270             :   fd_backtest_src_destroy( src );
     271             :   if( FD_UNLIKELY( 0!=fclose( out ) ) ) {
     272             :     FD_LOG_ERR(( "fclose failed on %s (%i-%s), output file may be corrupt", out_path, errno, fd_io_strerror( errno ) ));
     273             :   }
     274             : 
     275             :   fd_halt();
     276             :   return 0;
     277             : }

Generated by: LCOV version 1.14