Skip to content

Commit df592ac

Browse files
committed
refactor(reasm): overwrite bad CMRs using slot and parent_off
1 parent a468fff commit df592ac

File tree

7 files changed

+153
-11
lines changed

7 files changed

+153
-11
lines changed

src/discof/forest/fd_forest.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -892,8 +892,7 @@ ancestry_print3( fd_forest_t const * forest, fd_forest_ele_t const * ele, int sp
892892

893893
void
894894
fd_forest_ancestry_print( fd_forest_t const * forest ) {
895-
FD_LOG_NOTICE(("\n\n[Ancestry]\n\n" ) );
896-
895+
printf(("\n\n[Ancestry]\n" ) );
897896
ancestry_print3( forest, fd_forest_pool_ele_const( fd_forest_pool_const( forest ), forest->root ), 0, "[", NULL, 0 );
898897
}
899898

@@ -913,7 +912,7 @@ fd_forest_frontier_print( fd_forest_t const * forest ) {
913912

914913
void
915914
fd_forest_orphaned_print( fd_forest_t const * forest ) {
916-
printf( "\n\n[Orphaned]\n" );
915+
printf( "\n[Orphaned]\n" );
917916
fd_forest_orphaned_t const * orphaned = fd_forest_orphaned_const( forest );
918917
fd_forest_ele_t const * pool = fd_forest_pool_const( forest );
919918
for( fd_forest_orphaned_iter_t iter = fd_forest_orphaned_iter_init( orphaned, pool );
@@ -928,10 +927,11 @@ void
928927
fd_forest_print( fd_forest_t const * forest ) {
929928
if( FD_UNLIKELY( forest->root == ULONG_MAX ) ) return;
930929
# if FD_FOREST_PRINT
930+
FD_LOG_NOTICE(("\n\n[Forest]" ) );
931931
fd_forest_ancestry_print( forest );
932932
fd_forest_frontier_print( forest );
933933
fd_forest_orphaned_print( forest );
934-
printf("\n\n");
934+
printf("\n");
935935
# endif
936936
}
937937

src/discof/forest/test_forest.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ main( int argc, char ** argv ) {
639639
test_publish_incremental( wksp );
640640
test_out_of_order( wksp );
641641
test_forks( wksp );
642-
// test_print_tree( wksp );
642+
test_print_tree( wksp );
643643
// test_large_print_tree( wksp);
644644
test_linear_forest_iterator( wksp );
645645
test_branched_forest_iterator( wksp );

src/discof/reasm/fd_reasm.c

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ fd_reasm_align( void ) {
88

99
ulong
1010
fd_reasm_footprint( ulong fec_max ) {
11+
int lgf_max = fd_ulong_find_msb( fd_ulong_pow2_up( fec_max ) );
1112
return FD_LAYOUT_FINI(
1213
FD_LAYOUT_APPEND(
1314
FD_LAYOUT_APPEND(
@@ -17,6 +18,7 @@ fd_reasm_footprint( ulong fec_max ) {
1718
FD_LAYOUT_APPEND(
1819
FD_LAYOUT_APPEND(
1920
FD_LAYOUT_APPEND(
21+
FD_LAYOUT_APPEND(
2022
FD_LAYOUT_INIT,
2123
alignof(fd_reasm_t), sizeof(fd_reasm_t) ),
2224
pool_align(), pool_footprint ( fec_max ) ),
@@ -26,6 +28,7 @@ fd_reasm_footprint( ulong fec_max ) {
2628
subtrees_align(), subtrees_footprint( fec_max ) ),
2729
bfs_align(), bfs_footprint ( fec_max ) ),
2830
out_align(), out_footprint ( fec_max ) ),
31+
slot_mr_align(), slot_mr_footprint ( lgf_max ) ),
2932
fd_reasm_align() );
3033
}
3134

@@ -56,6 +59,8 @@ fd_reasm_new( void * shmem, ulong fec_max, ulong seed ) {
5659

5760
fd_memset( shmem, 0, footprint );
5861

62+
int lgf_max = fd_ulong_find_msb( fd_ulong_pow2_up( fec_max ) );
63+
5964
fd_reasm_t * reasm;
6065
FD_SCRATCH_ALLOC_INIT( l, shmem );
6166
reasm = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_reasm_t), sizeof(fd_reasm_t) );
@@ -66,6 +71,7 @@ fd_reasm_new( void * shmem, ulong fec_max, ulong seed ) {
6671
void * subtrees = FD_SCRATCH_ALLOC_APPEND( l, subtrees_align(), subtrees_footprint( fec_max ) );
6772
void * bfs = FD_SCRATCH_ALLOC_APPEND( l, bfs_align(), bfs_footprint ( fec_max ) );
6873
void * out = FD_SCRATCH_ALLOC_APPEND( l, out_align(), out_footprint ( fec_max ) );
74+
void * slot_mr = FD_SCRATCH_ALLOC_APPEND( l, slot_mr_align(), slot_mr_footprint ( lgf_max ) );
6975
FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_reasm_align() ) == (ulong)shmem + footprint );
7076

7177
reasm->root = pool_idx_null( pool );
@@ -76,6 +82,7 @@ fd_reasm_new( void * shmem, ulong fec_max, ulong seed ) {
7682
reasm->subtrees = subtrees_new ( subtrees, fec_max, seed );
7783
reasm->bfs = bfs_new ( bfs, fec_max );
7884
reasm->out = out_new ( out, fec_max );
85+
reasm->slot_mr = slot_mr_new ( slot_mr, lgf_max );
7986

8087
return shmem;
8188
}
@@ -96,6 +103,7 @@ fd_reasm_join( void * shreasm ) {
96103
reasm->subtrees = subtrees_join( reasm->subtrees );
97104
reasm->bfs = bfs_join ( reasm->bfs );
98105
reasm->out = out_join ( reasm->out );
106+
reasm->slot_mr = slot_mr_join ( reasm->slot_mr );
99107

100108
return reasm;
101109
}
@@ -150,7 +158,10 @@ fd_reasm_init( fd_reasm_t * reasm, fd_hash_t const * merkle_root, ulong slot ) {
150158
fec->fec_set_idx = 0;
151159
fec->data_cnt = 0;
152160
fec->data_complete = 0;
153-
fec->slot_complete = 0;
161+
fec->slot_complete = 1;
162+
163+
slot_mr_t * slot_mr = slot_mr_insert( reasm->slot_mr, slot );
164+
slot_mr->block_id = fec->key;
154165

155166
/* Set this dummy FEC as the root and add it to the frontier. */
156167

@@ -188,6 +199,20 @@ fd_reasm_query( fd_reasm_t * reasm,
188199
return fec;
189200
}
190201

202+
static void
203+
check_cmr( fd_reasm_t * reasm, fd_reasm_fec_t * child ) {
204+
if( FD_UNLIKELY( child->fec_set_idx==0 && !fd_reasm_query( reasm, &child->cmr ) ) ) {
205+
slot_mr_t * slot_mr_parent = slot_mr_query( reasm->slot_mr, child->slot - child->parent_off, NULL );
206+
if( FD_LIKELY( slot_mr_parent ) ) {
207+
fd_reasm_fec_t * parent = fd_reasm_query( reasm, &slot_mr_parent->block_id );
208+
if( FD_LIKELY( parent ) ) {
209+
FD_LOG_NOTICE(( "overwriting bad block_id for FEC slot: %lu fec_set_idx: %u from %s to %s", child->slot, child->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &child->cmr ), FD_BASE58_ENC_32_ALLOCA( &parent->key ) ));
210+
child->cmr = parent->key; /* use the parent's merkle root */
211+
}
212+
}
213+
}
214+
}
215+
191216
static void
192217
link( fd_reasm_t * reasm,
193218
fd_reasm_fec_t * parent,
@@ -212,7 +237,7 @@ fd_reasm_insert( fd_reasm_t * reasm,
212237
ushort data_cnt,
213238
int data_complete,
214239
int slot_complete ) {
215-
// FD_LOG_NOTICE(( "inserting %s %lu %u %u %d %d", FD_BASE58_ENC_32_ALLOCA( merkle_root ), slot, fec_set_idx, data_cnt, data_complete, slot_complete ));
240+
// FD_LOG_NOTICE(( "inserting (%lu %u) %s. %u %d %d", slot, fec_set_idx, FD_BASE58_ENC_32_ALLOCA( merkle_root ), data_cnt, data_complete, slot_complete ));
216241

217242
# if FD_REASM_USE_HANDHOLDING
218243
FD_TEST( pool_free( reasm->pool ) );
@@ -244,6 +269,29 @@ fd_reasm_insert( fd_reasm_t * reasm,
244269
fec->data_complete = data_complete;
245270
fec->slot_complete = slot_complete;
246271

272+
/* This is a gross case reasm needs to handle because Agave currently
273+
does not validate chained merkle roots across slots ie. if a leader
274+
sends a bad chained merkle root on a slot boundary, the cluster might
275+
converge on the leader's block anyways. So we overwrite the chained
276+
merkle root based on the slot and parent_off metadata. There are two
277+
cases: 1. we receive the parent before the child. In this case we
278+
just overwrite the child's CMR. 2. we receive the child before the
279+
parent. In this case every time we receive a new FEC set we need to
280+
check the orphan roots for whether we can link the orphan to the new
281+
FEC via slot metadata, since the chained merkle root metadata on that
282+
orphan root might be wrong. */
283+
284+
if( FD_UNLIKELY( slot_complete ) ) {
285+
slot_mr_t * slot_mr = slot_mr_query( reasm->slot_mr, slot, NULL );
286+
if( FD_UNLIKELY( slot_mr ) ) {
287+
FD_LOG_WARNING(( "equivocating block_id for FEC slot: %lu fec_set_idx: %u prev: %s curr: %s", fec->slot, fec->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &slot_mr->block_id ), FD_BASE58_ENC_32_ALLOCA( &fec->key ) )); /* it's possible there's equivocation... */
288+
} else {
289+
slot_mr = slot_mr_insert( reasm->slot_mr, slot );
290+
slot_mr->block_id = fec->key;
291+
}
292+
}
293+
check_cmr( reasm, fec ); /* handle receiving parent before child */
294+
247295
/* First, we search for the parent of this new FEC and link if found.
248296
The new FEC set may result in a new leaf or a new orphan tree root
249297
so we need to check that. */
@@ -286,6 +334,7 @@ fd_reasm_insert( fd_reasm_t * reasm,
286334
}
287335
while( FD_LIKELY( !bfs_empty( bfs ) ) ) {
288336
fd_reasm_fec_t * orphan_root = pool_ele( reasm->pool, bfs_pop_head( bfs ) );
337+
check_cmr( reasm, orphan_root ); /* handle receiving child before parent */
289338
if( FD_LIKELY( orphan_root && 0==memcmp( orphan_root->cmr.uc, fec->key.uc, sizeof(fd_hash_t) ) ) ) { /* this orphan_root is a direct child of fec */
290339
link( reasm, fec, orphan_root );
291340
if( FD_UNLIKELY( is_root ) ) { /* this is an orphan tree */
@@ -367,6 +416,10 @@ fd_reasm_publish( fd_reasm_t * reasm, fd_hash_t const * merkle_root ) {
367416
}
368417
fd_reasm_fec_t * next = pool_ele( pool, head->next ); /* pophead */
369418
pool_ele_release( pool, head ); /* release */
419+
420+
slot_mr_t * slot_mr = slot_mr_query( reasm->slot_mr, head->slot, NULL );
421+
if( FD_UNLIKELY( slot_mr ) ) slot_mr_remove( reasm->slot_mr, slot_mr );
422+
370423
head = next; /* advance */
371424
}
372425
newr->parent = null; /* unlink old root */
@@ -376,7 +429,7 @@ fd_reasm_publish( fd_reasm_t * reasm, fd_hash_t const * merkle_root ) {
376429

377430
#include <stdio.h>
378431

379-
static void
432+
FD_FN_UNUSED static void
380433
print( fd_reasm_t const * reasm, fd_reasm_fec_t const * fec, int space, const char * prefix ) {
381434
fd_reasm_fec_t * pool = reasm->pool;
382435

@@ -402,7 +455,32 @@ print( fd_reasm_t const * reasm, fd_reasm_fec_t const * fec, int space, const ch
402455

403456
void
404457
fd_reasm_print( fd_reasm_t const * reasm ) {
405-
FD_LOG_NOTICE( ( "\n\n[reasm]" ) );
406-
print( reasm, pool_ele_const( reasm->pool, reasm->root ), 0, "" );
458+
FD_LOG_NOTICE( ( "\n\n[Reasm]" ) );
459+
fd_reasm_fec_t * pool = reasm->pool;
460+
461+
printf(("\n\n[Frontier]\n" ) );
462+
frontier_t * frontier = reasm->frontier;
463+
for( frontier_iter_t iter = frontier_iter_init( frontier, pool );
464+
!frontier_iter_done( iter, frontier, pool );
465+
iter = frontier_iter_next( iter, frontier, pool ) ) {
466+
fd_reasm_fec_t const * fec = pool_ele_const( reasm->pool, iter.ele_idx );
467+
printf( "(%lu, %u) %s\n", fec->slot, fec->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &fec->key ) );
468+
}
469+
470+
printf(("\n\n[Subtrees]\n" ) );
471+
subtrees_t * subtrees = reasm->subtrees;
472+
for( subtrees_iter_t iter = subtrees_iter_init( subtrees, pool );
473+
!subtrees_iter_done( iter, subtrees, pool );
474+
iter = subtrees_iter_next( iter, subtrees, pool ) ) {
475+
fd_reasm_fec_t const * fec = pool_ele_const( reasm->pool, iter.ele_idx );
476+
printf( "(%lu, %u) %s\n", fec->slot, fec->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &fec->key ) );
477+
}
478+
479+
// print( reasm, pool_ele_const( reasm->pool, reasm->root ), 0, "" );
480+
// printf( "\n\n" );
481+
// for( out_iter_t iter = out_iter_init( reasm->out ); !out_iter_done( reasm->out, iter ); iter = out_iter_next( reasm->out, iter ) ) {
482+
// ulong * idx = out_iter_ele( reasm->out, iter );
483+
// printf( "%s\n", FD_BASE58_ENC_32_ALLOCA( pool_ele_const( reasm->pool, *idx ) ) );
484+
// }
407485
printf( "\n\n" );
408486
}

src/discof/reasm/fd_reasm_private.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@
4040
#define DEQUE_T ulong
4141
#include "../../util/tmpl/fd_deque_dynamic.c"
4242

43+
typedef struct {
44+
ulong slot;
45+
fd_hash_t block_id;
46+
} slot_mr_t;
47+
48+
#define MAP_NAME slot_mr
49+
#define MAP_T slot_mr_t
50+
#define MAP_KEY slot
51+
#define MAP_KEY_NULL ULONG_MAX
52+
#define MAP_KEY_INVAL(k) ((k)==MAP_KEY_NULL)
53+
#define MAP_MEMOIZE 0
54+
#include "../../util/tmpl/fd_map_dynamic.c"
55+
4356
struct __attribute__((aligned(128UL))) fd_reasm {
4457
ulong root; /* pool idx of the root FEC set */
4558
ulong slot0; /* special initialization slot. chains first FEC */
@@ -50,4 +63,5 @@ struct __attribute__((aligned(128UL))) fd_reasm {
5063
subtrees_t * subtrees; /* map of mr->fec. roots of the orphaned subtrees */
5164
ulong * bfs; /* internal queue of pool idxs for BFS */
5265
ulong * out; /* delivery queue of pool idxs to output */
66+
slot_mr_t * slot_mr; /* map of slot->mr */
5367
};

src/discof/reasm/test_reasm.c

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ test_insert( fd_wksp_t * wksp ) {
107107
}
108108

109109
void
110-
test_publish( fd_wksp_t * wksp ){
110+
test_publish( fd_wksp_t * wksp ) {
111111
ulong fec_max = 32;
112112
void * mem = fd_wksp_alloc_laddr( wksp, fd_reasm_align(), fd_reasm_footprint( fec_max ), 1UL );
113113
fd_reasm_t * reasm = fd_reasm_join( fd_reasm_new( mem, fec_max, 0UL ) );
@@ -181,6 +181,44 @@ test_publish( fd_wksp_t * wksp ){
181181
fd_wksp_free_laddr( fd_reasm_delete( fd_reasm_leave( reasm ) ) );
182182
}
183183

184+
void
185+
test_slot_mr( fd_wksp_t * wksp ) {
186+
ulong fec_max = 32;
187+
void * mem = fd_wksp_alloc_laddr( wksp, fd_reasm_align(), fd_reasm_footprint( fec_max ), 1UL );
188+
fd_reasm_t * reasm = fd_reasm_join( fd_reasm_new( mem, fec_max, 0UL ) );
189+
FD_TEST( reasm );
190+
FD_TEST( reasm->slot_mr );
191+
192+
fd_reasm_fec_t * pool = reasm->pool;
193+
// ulong null = pool_idx_null( pool );
194+
195+
ancestry_t * ancestry = reasm->ancestry;
196+
frontier_t * frontier = reasm->frontier;
197+
subtrees_t * subtrees = reasm->subtrees;
198+
199+
fd_hash_t mr0[1] = {{{ 0 }}};
200+
fd_hash_t mr1[1] = {{{ 1 }}};
201+
fd_hash_t mr2[1] = {{{ 2 }}};
202+
fd_hash_t mr3[1] = {{{ 3 }}};
203+
fd_hash_t mr4[1] = {{{ 4 }}};
204+
205+
fd_reasm_init( reasm, mr1, 1 );
206+
fd_reasm_fec_t * fec1 = fd_reasm_query( reasm, mr1 );
207+
FD_TEST( fec1 );
208+
FD_TEST( frontier_ele_query( frontier, &fec1->key, NULL, pool ) == fec1 );
209+
210+
fd_reasm_fec_t * fec2 = fd_reasm_insert( reasm, mr2, mr0, 2, 0, 1, 32, 0, 1 ); /* insert with bad mr0 should be mr1 */
211+
FD_TEST( ancestry_ele_query( ancestry, &fec1->key, NULL, pool ) ); /* successfully chains anyways */
212+
FD_TEST( frontier_ele_query( frontier, &fec2->key, NULL, pool ) );
213+
214+
fd_reasm_fec_t * fec4 = fd_reasm_insert( reasm, mr4, mr0, 4, 0, 1, 32, 0, 1 ); /* insert with bad mr0 should be mr3 */
215+
FD_TEST( subtrees_ele_query( subtrees, &fec4->key, NULL, pool ) ); /* orphaned */
216+
217+
fd_reasm_fec_t * fec3 = fd_reasm_insert( reasm, mr3, mr2, 3, 0, 1, 32, 0, 1 );
218+
FD_TEST( ancestry_ele_query( ancestry, &fec3->key, NULL, pool ) ); /* mr3 should chain to 2 */
219+
FD_TEST( frontier_ele_query( frontier, &fec4->key, NULL, pool ) ); /* mr4 should have chained */
220+
}
221+
184222
int
185223
main( int argc, char ** argv ) {
186224
fd_boot( &argc, &argv );
@@ -193,6 +231,7 @@ main( int argc, char ** argv ) {
193231

194232
test_insert( wksp );
195233
test_publish( wksp );
234+
test_slot_mr( wksp );
196235

197236
ulong sig = fd_disco_repair_replay_sig( 3508496, 1, 32, 128 );
198237
FD_TEST( fd_disco_repair_replay_sig_slot( sig ) == 3508496 );

src/discof/repair/fd_repair_tile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,7 @@ during_housekeeping( fd_repair_tile_ctx_t * ctx ) {
11081108
long now = fd_log_wallclock();
11091109
if( FD_UNLIKELY( now - ctx->tsprint > (long)10e9 ) ) {
11101110
fd_forest_print( ctx->forest );
1111+
fd_reasm_print( ctx->reasm );
11111112
ctx->tsprint = fd_log_wallclock();
11121113
}
11131114
}

src/discof/replay/fd_replay_tile.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,6 +1447,16 @@ handle_new_slice( fd_replay_tile_ctx_t * ctx, fd_stem_context_t * stem ) {
14471447
ulong slice_sz = 0;
14481448
for( ulong i = 0; i < slice.merkles_cnt; i++ ) {
14491449
fd_store_fec_t * fec = fd_store_query( ctx->store, &slice.merkles[i] );
1450+
if( FD_UNLIKELY( !fec ) ) {
1451+
1452+
/* The only case in which a FEC is not found in the store after
1453+
repair has notified is if the FEC was on a minority fork that
1454+
has already been published away. In this case we abandon the
1455+
entire slice because it is no longer relevant. */
1456+
1457+
FD_LOG_WARNING(( "store fec for slot: %lu is on minority fork already pruned by publish. abandoning slice. root: %lu. pruned merkle: %s", slice.slot, ctx->root, FD_BASE58_ENC_32_ALLOCA( &slice.merkles[i] ) ));
1458+
return;
1459+
}
14501460
FD_TEST( fec );
14511461
memcpy( ctx->slice_exec_ctx.buf + slice_sz, fec->data, fec->data_sz );
14521462
slice_sz += fec->data_sz;

0 commit comments

Comments
 (0)