Skip to content

Commit 789d5be

Browse files
committed
backtest: replay from shredcap files
1 parent 5115d66 commit 789d5be

File tree

16 files changed

+464
-92
lines changed

16 files changed

+464
-92
lines changed

contrib/codeql/nightly/TileUnionMismatch.ql

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,4 @@ where
3535
not tileName.matches("%_" + t.toString() + "_%") and
3636
/* net "inheritance" case */
3737
not t.toString() = "net"
38-
/* backtest is called archiver */
39-
and not (t.toString() = "archiver" and tileName = "fd_backtest_tile.c")
4038
select t, t.getLocation().getFile().getBaseName()

src/app/firedancer-dev/main.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,7 @@ fd_topo_run_tile_t * TILES[] = {
151151
&fd_tile_archiver_writer,
152152
&fd_tile_archiver_playback,
153153
&fd_tile_shredcap,
154-
#if FD_HAS_ROCKSDB
155154
&fd_tile_backtest,
156-
#endif
157155
&fd_tile_bencho,
158156
&fd_tile_benchg,
159157
&fd_tile_benchs,

src/app/firedancer/topology.c

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,29 +1430,21 @@ fd_topo_configure_tile( fd_topo_tile_t * tile,
14301430

14311431
} else if( FD_UNLIKELY( !strcmp( tile->name, "backt" ) ) ) {
14321432

1433-
tile->archiver.end_slot = config->tiles.archiver.end_slot;
1434-
strncpy( tile->archiver.ingest_mode, config->tiles.archiver.ingest_mode, sizeof(tile->archiver.ingest_mode) );
1435-
if( FD_UNLIKELY( 0==strlen( tile->archiver.ingest_mode ) ) ) {
1436-
FD_LOG_ERR(( "`archiver.ingest_mode` not specified in toml" ));
1437-
}
1433+
tile->backtest.end_slot = config->tiles.archiver.end_slot;
14381434

14391435
/* Validate arguments based on the ingest mode */
1440-
if( !strcmp( tile->archiver.ingest_mode, "rocksdb" ) ) {
1441-
strncpy( tile->archiver.rocksdb_path, config->tiles.archiver.rocksdb_path, PATH_MAX );
1442-
if( FD_UNLIKELY( 0==strlen( tile->archiver.rocksdb_path ) ) ) {
1436+
if( !strcmp( config->tiles.archiver.ingest_mode, "rocksdb" ) ) {
1437+
strncpy( tile->backtest.rocksdb_path, config->tiles.archiver.rocksdb_path, PATH_MAX );
1438+
if( FD_UNLIKELY( 0==strlen( tile->backtest.rocksdb_path ) ) ) {
14431439
FD_LOG_ERR(( "`archiver.rocksdb_path` not specified in toml" ));
14441440
}
1445-
} else if( !strcmp( tile->archiver.ingest_mode, "shredcap" ) ) {
1446-
strncpy( tile->archiver.shredcap_path, config->tiles.archiver.shredcap_path, PATH_MAX );
1447-
if( FD_UNLIKELY( 0==strlen( tile->archiver.shredcap_path ) ) ) {
1441+
} else if( !strcmp( config->tiles.archiver.ingest_mode, "shredcap" ) ) {
1442+
strncpy( tile->backtest.shredcap_path, config->tiles.archiver.shredcap_path, PATH_MAX );
1443+
if( FD_UNLIKELY( 0==strlen( tile->backtest.shredcap_path ) ) ) {
14481444
FD_LOG_ERR(( "`archiver.shredcap_path` not specified in toml" ));
14491445
}
1450-
strncpy( tile->archiver.bank_hash_path, config->tiles.archiver.bank_hash_path, PATH_MAX );
1451-
if( FD_UNLIKELY( 0==strlen( tile->archiver.bank_hash_path ) ) ) {
1452-
FD_LOG_ERR(( "`archiver.bank_hash_path` not specified in toml" ));
1453-
}
14541446
} else {
1455-
FD_LOG_ERR(( "Invalid ingest mode: %s", tile->archiver.ingest_mode ));
1447+
FD_LOG_ERR(( "Invalid ingest mode: %s", config->tiles.archiver.ingest_mode ));
14561448
}
14571449

14581450
} else if( FD_UNLIKELY( !strcmp( tile->name, "scap" ) ) ) {

src/app/shared/fd_config.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,6 @@ struct fd_config {
478478
ulong end_slot;
479479
char rocksdb_path[ PATH_MAX ];
480480
char shredcap_path[ PATH_MAX ];
481-
char bank_hash_path[ PATH_MAX ];
482481
char ingest_mode[ 32 ];
483482
} archiver;
484483

src/app/shared/fd_config_parse.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,6 @@ fd_config_extract_pod( uchar * pod,
264264
CFG_POP ( ulong, tiles.archiver.end_slot );
265265
CFG_POP ( cstr, tiles.archiver.rocksdb_path );
266266
CFG_POP ( cstr, tiles.archiver.shredcap_path );
267-
CFG_POP ( cstr, tiles.archiver.bank_hash_path );
268267
CFG_POP ( cstr, tiles.archiver.ingest_mode );
269268

270269
if( FD_UNLIKELY( config->is_firedancer ) ) {

src/disco/topo/fd_topo.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,14 +485,18 @@ struct fd_topo_tile {
485485
struct {
486486
ulong end_slot;
487487
char rocksdb_path[ PATH_MAX ];
488-
char shredcap_path[ PATH_MAX ];
489-
char bank_hash_path[ PATH_MAX ];
490488
char ingest_mode[ 32 ];
491489

492490
/* Set internally by the archiver tile */
493491
int archive_fd;
494492
} archiver;
495493

494+
struct {
495+
ulong end_slot;
496+
char rocksdb_path[ PATH_MAX ];
497+
char shredcap_path[ PATH_MAX ];
498+
} backtest;
499+
496500
struct {
497501
int hard_fork_fatal;
498502
ulong max_live_slots;

src/discof/backtest/Local.mk

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
$(call add-objs,fd_backtest_shredcap,fd_discof)
2+
$(call make-unit-test,test_backtest_shredcap,test_backtest_shredcap,fd_discof fd_ballet fd_util)
3+
4+
ifdef FD_HAS_ALLOCA
5+
$(call add-objs,fd_backtest_tile,fd_discof)
6+
endif
7+
18
ifdef FD_HAS_ROCKSDB
2-
$(call add-objs,fd_backtest_rocksdb fd_backtest_tile,fd_discof)
9+
$(call add-objs,fd_backtest_rocksdb,fd_discof)
310
$(call make-bin,fd_blockstore2shredcap,fd_blockstore2shredcap,fd_discof fd_flamenco fd_ballet fd_util,$(ROCKSDB_LIBS))
4-
else
5-
$(warning "rocksdb not installed, skipping backtest")
611
endif

src/discof/backtest/fd_backtest_rocksdb.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
struct fd_backtest_rocksdb_private;
77
typedef struct fd_backtest_rocksdb_private fd_backtest_rocksdb_t;
88

9+
#if FD_HAS_ROCKSDB
10+
911
#define FD_BACKTEST_ROCKSDB_MAGIC (0xF17EDA2CE58AC810) /* FIREDANCE BACKT V0 */
1012

1113
FD_PROTOTYPES_BEGIN
@@ -43,4 +45,6 @@ fd_backtest_rocksdb_bank_hash( fd_backtest_rocksdb_t * db,
4345

4446
FD_PROTOTYPES_END
4547

48+
#endif /* FD_HAS_ROCKSDB */
49+
4650
#endif /* HEADER_fd_src_discof_backtest_fd_backtest_rocksdb_h */
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#include "fd_backtest_shredcap.h"
2+
#include "../../ballet/shred/fd_shred.h"
3+
#include "../../util/net/fd_eth.h"
4+
#include "../../util/net/fd_ip4.h"
5+
#include "../../util/net/fd_udp.h"
6+
#include "../../util/net/fd_pcapng.h"
7+
#include "fd_shredcap.h"
8+
#include <stdio.h>
9+
10+
struct fd_backtest_shredcap_private {
11+
FILE * file;
12+
void * iter_mem;
13+
fd_pcapng_iter_t * iter;
14+
ulong slot;
15+
uchar bank_hash[32];
16+
};
17+
18+
FD_FN_CONST ulong
19+
fd_backtest_shredcap_align( void ) {
20+
return fd_ulong_max( alignof(fd_backtest_shredcap_t), fd_pcapng_iter_align() );
21+
}
22+
23+
FD_FN_CONST ulong
24+
fd_backtest_shredcap_footprint( void ) {
25+
ulong l = FD_LAYOUT_INIT;
26+
l = FD_LAYOUT_APPEND( l, alignof(fd_backtest_shredcap_t), sizeof(fd_backtest_shredcap_t) );
27+
l = FD_LAYOUT_APPEND( l, fd_pcapng_iter_align(), fd_pcapng_iter_footprint() );
28+
return FD_LAYOUT_FINI( l, fd_backtest_shredcap_align() );
29+
}
30+
31+
fd_backtest_shredcap_t *
32+
fd_backtest_shredcap_new( void * shmem,
33+
char const * path ) {
34+
FD_SCRATCH_ALLOC_INIT( l, shmem );
35+
fd_backtest_shredcap_t * db = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_backtest_shredcap_t), sizeof(fd_backtest_shredcap_t) );
36+
void * iter_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_pcapng_iter_align(), fd_pcapng_iter_footprint() );
37+
FD_SCRATCH_ALLOC_FINI( l, fd_backtest_shredcap_align() );
38+
memset( db, 0, sizeof(fd_backtest_shredcap_t) );
39+
40+
FILE * file = fopen( path, "rb" );
41+
if( FD_UNLIKELY( !file ) ) {
42+
FD_LOG_WARNING(( "fopen(%s,rb) failed", path ));
43+
return NULL;
44+
}
45+
46+
db->file = file;
47+
db->iter_mem = iter_mem;
48+
db->iter = NULL;
49+
return db;
50+
}
51+
52+
void *
53+
fd_backtest_shredcap_delete( fd_backtest_shredcap_t * db ) {
54+
FD_TEST( db->file );
55+
if( FD_UNLIKELY( 0!=fclose( db->file ) ) ) FD_LOG_ERR(( "fclose failed" ));
56+
memset( db, 0, sizeof(fd_backtest_shredcap_t) );
57+
return (void *)db;
58+
}
59+
60+
#define SEEK_UNTIL( DB, COND ) \
61+
__extension__({ \
62+
fd_pcapng_iter_t * iter = (DB)->iter; \
63+
fd_pcapng_frame_t * frame; \
64+
for(;;) { \
65+
frame = fd_pcapng_iter_next( iter ); \
66+
if( !frame ) break; \
67+
if( COND ) break; \
68+
} \
69+
frame; \
70+
})
71+
72+
static fd_shredcap_bank_hash_v0_t *
73+
frame_peek_bank_hash( fd_pcapng_frame_t const * frame,
74+
fd_shredcap_bank_hash_v0_t * out ) {
75+
if( frame->type!=FD_PCAPNG_FRAME_ENHANCED ) return NULL;
76+
if( !frame->idb ) return NULL;
77+
fd_pcapng_idb_desc_t const * idb = frame->idb;
78+
if( idb->link_type!=FD_PCAPNG_LINKTYPE_USER0 ) return NULL;
79+
if( 0!=strcmp( idb->opts.name, "shredcap0" ) ) return NULL;
80+
if( frame->data_sz<sizeof(uint)+sizeof(fd_shredcap_bank_hash_v0_t) ) return NULL;
81+
uint type = FD_LOAD( uint, frame->data );
82+
if( type!=FD_SHREDCAP_TYPE_BANK_HASH_V0 ) return NULL;
83+
memcpy( out, frame->data+sizeof(uint), sizeof(fd_shredcap_bank_hash_v0_t) );
84+
return out;
85+
}
86+
87+
static ulong
88+
frame_peek_root_slot( fd_pcapng_frame_t const * frame ) {
89+
fd_shredcap_bank_hash_v0_t bh;
90+
if( !frame_peek_bank_hash( frame, &bh ) ) return ULONG_MAX;
91+
return bh.slot;
92+
}
93+
94+
void
95+
fd_backtest_shredcap_init( fd_backtest_shredcap_t * db,
96+
ulong root_slot ) {
97+
if( FD_UNLIKELY( fseek( db->file, 0L, SEEK_SET )!=0L ) ) {
98+
FD_LOG_ERR(( "fseek failed" ));
99+
}
100+
db->iter = fd_pcapng_iter_new( db->iter_mem, db->file );
101+
102+
fd_pcapng_iter_next( db->iter );
103+
SEEK_UNTIL( db, __extension__({
104+
ulong found = frame_peek_root_slot( frame );
105+
found!=ULONG_MAX && found>=root_slot;
106+
}));
107+
}
108+
109+
int
110+
fd_backtest_shredcap_next_root_slot( fd_backtest_shredcap_t * db,
111+
ulong * root_slot,
112+
ulong * shred_cnt ) {
113+
fd_pcapng_frame_t const * frame = SEEK_UNTIL( db, (frame_peek_root_slot( frame )!=ULONG_MAX) );
114+
if( FD_UNLIKELY( !frame ) ) return 0;
115+
116+
fd_shredcap_bank_hash_v0_t bh;
117+
if( FD_UNLIKELY( !frame_peek_bank_hash( frame, &bh ) ) ) {
118+
/* FIXME gracefully skip over unexpected control packets */
119+
FD_LOG_ERR(( "expected bank_hash frame, found something else" ));
120+
}
121+
122+
*root_slot = db->slot = bh.slot;
123+
*shred_cnt = bh.data_shred_cnt;
124+
memcpy( db->bank_hash, bh.bank_hash, 32 );
125+
return 1;
126+
}
127+
128+
static uchar const *
129+
find_ip4_hdr( fd_pcapng_frame_t const * frame,
130+
ulong * psz ) {
131+
*psz = 0UL;
132+
if( !frame->idb ) return NULL;
133+
134+
FD_TEST( frame->type==FD_PCAPNG_FRAME_ENHANCED );
135+
FD_TEST( frame->idb );
136+
switch( frame->idb->link_type ) {
137+
case FD_PCAPNG_LINKTYPE_USER0:
138+
/* FIXME gracefully skip over unexpected control packets */
139+
FD_LOG_ERR(( "expected shred, got control frag" ));
140+
case FD_PCAPNG_LINKTYPE_ETHERNET: {
141+
FD_TEST( frame->data_sz>=sizeof(fd_eth_hdr_t) );
142+
fd_eth_hdr_t const * eth_hdr = (fd_eth_hdr_t const *)frame->data;
143+
FD_TEST( eth_hdr->net_type==fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) );
144+
*psz = frame->data_sz - sizeof(fd_eth_hdr_t);
145+
return frame->data + sizeof(fd_eth_hdr_t);
146+
}
147+
case FD_PCAPNG_LINKTYPE_RAW:
148+
case FD_PCAPNG_LINKTYPE_IPV4:
149+
*psz = frame->data_sz;
150+
return frame->data;
151+
default:
152+
return NULL;
153+
}
154+
}
155+
156+
static uchar const *
157+
find_udp_payload( fd_pcapng_frame_t const * frame,
158+
ulong * psz ) {
159+
*psz = 0UL;
160+
ulong ip4_sz;
161+
uchar const * raw = find_ip4_hdr( frame, &ip4_sz );
162+
fd_ip4_hdr_t const * ip4 = (fd_ip4_hdr_t const *)raw;
163+
if( FD_UNLIKELY( !raw ) ) return NULL;
164+
if( FD_UNLIKELY( ip4_sz<sizeof(fd_ip4_hdr_t)+sizeof(fd_udp_hdr_t) ) ) return NULL;
165+
if( FD_UNLIKELY( FD_IP4_GET_VERSION( *ip4 )!=4 ) ) return NULL;
166+
ulong ip4_hdr_len = FD_IP4_GET_LEN( *ip4 );
167+
if( FD_UNLIKELY( ip4_sz<ip4_hdr_len+sizeof(fd_udp_hdr_t) ) ) return NULL;
168+
if( FD_UNLIKELY( ip4->protocol!=FD_IP4_HDR_PROTOCOL_UDP ) ) return NULL;
169+
*psz = ip4_sz - ip4_hdr_len - sizeof(fd_udp_hdr_t);
170+
return raw + ip4_hdr_len + sizeof(fd_udp_hdr_t);
171+
}
172+
173+
void const *
174+
fd_backtest_shredcap_shred( fd_backtest_shredcap_t * db,
175+
ulong slot,
176+
ulong shred_idx ) {
177+
fd_pcapng_frame_t const * frame = fd_pcapng_iter_next( db->iter );
178+
if( FD_UNLIKELY( !frame ) ) return NULL;
179+
180+
ulong shred_sz = 0UL;
181+
fd_shred_t const * shred = fd_type_pun_const( find_udp_payload( frame, &shred_sz ) );
182+
ulong shred_type = fd_shred_type( shred->variant );
183+
if( FD_UNLIKELY( !shred_sz || !fd_shred_is_data( shred_type ) ) ) FD_LOG_ERR(( "failed to read shred %lu:%lu", slot, shred_idx ));
184+
if( FD_UNLIKELY( fd_shred_sz( shred )<shred_sz ) ) FD_LOG_ERR(( "corrupt shred %lu:%lu", slot, shred_idx ));
185+
if( FD_UNLIKELY( shred->slot!=slot ) ) FD_LOG_ERR(( "expected shred slot %lu, got %lu", slot, shred->slot ));
186+
if( FD_UNLIKELY( shred->idx!=shred_idx ) ) FD_LOG_ERR(( "expected shred idx %lu, got %u", shred_idx, shred->idx ));
187+
return shred;
188+
}
189+
190+
uchar const *
191+
fd_backtest_shredcap_bank_hash( fd_backtest_shredcap_t * db,
192+
ulong slot ) {
193+
FD_TEST( slot==db->slot );
194+
return db->bank_hash;
195+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#ifndef HEADER_fd_src_discof_backtest_fd_backtest_shredcap_h
2+
#define HEADER_fd_src_discof_backtest_fd_backtest_shredcap_h
3+
4+
/* fd_backtest_shredcap.h is a drop-in replacement for
5+
fd_backtest_rocksdb.h. It reads from shredcap files, see shredcap.md */
6+
7+
#include "../../util/fd_util_base.h"
8+
9+
struct fd_backtest_shredcap_private;
10+
typedef struct fd_backtest_shredcap_private fd_backtest_shredcap_t;
11+
12+
FD_PROTOTYPES_BEGIN
13+
14+
FD_FN_CONST ulong
15+
fd_backtest_shredcap_align( void );
16+
17+
FD_FN_CONST ulong
18+
fd_backtest_shredcap_footprint( void );
19+
20+
fd_backtest_shredcap_t *
21+
fd_backtest_shredcap_new( void * shmem,
22+
char const * path );
23+
24+
void *
25+
fd_backtest_shredcap_delete( fd_backtest_shredcap_t * db );
26+
27+
void
28+
fd_backtest_shredcap_init( fd_backtest_shredcap_t * db,
29+
ulong root_slot );
30+
31+
int
32+
fd_backtest_shredcap_next_root_slot( fd_backtest_shredcap_t * db,
33+
ulong * root_slot,
34+
ulong * shred_cnt );
35+
36+
void const *
37+
fd_backtest_shredcap_shred( fd_backtest_shredcap_t * db,
38+
ulong slot,
39+
ulong shred_idx );
40+
41+
uchar const *
42+
fd_backtest_shredcap_bank_hash( fd_backtest_shredcap_t * db,
43+
ulong slot );
44+
45+
FD_PROTOTYPES_END
46+
47+
#endif /* HEADER_fd_src_discof_backtest_fd_backtest_shredcap_h */

0 commit comments

Comments
 (0)