Skip to content

Commit 25a78b9

Browse files
2 parents 59fc48a + 13a65bf commit 25a78b9

File tree

10 files changed

+132
-34
lines changed

10 files changed

+132
-34
lines changed

agave

src/disco/shred/fd_shred_tile.c

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ FD_STATIC_ASSERT( sizeof(fd_entry_batch_meta_t)==56UL, poh_shred_mtu );
104104

105105
#define FD_SHRED_ADD_SHRED_EXTRA_RETVAL_CNT 2
106106

107+
/* Number of entries in the block_ids table. Each entry is 32 byte.
108+
This table is used to keep track of block ids that we create
109+
when we're leader, so that we can access them whenever we need
110+
a *parent* block id for a new block. Larger table allows to
111+
retrieve older parent block ids. Currently it's set for worst
112+
case parent offset of USHORT_MAX (max allowed in a shred),
113+
making the total table 2MiB.
114+
See also comment on chained_merkle_root. */
115+
#define BLOCK_IDS_TABLE_CNT USHORT_MAX
116+
107117
/* See note on parallelization above. Currently we process all batches in tile 0. */
108118
#if 1
109119
#define SHOULD_PROCESS_THESE_SHREDS ( ctx->round_robin_id==0 )
@@ -236,7 +246,8 @@ typedef struct {
236246
/* too large to be left in the stack */
237247
fd_shred_dest_idx_t scratchpad_dests[ FD_SHRED_DEST_MAX_FANOUT*(FD_REEDSOL_DATA_SHREDS_MAX+FD_REEDSOL_PARITY_SHREDS_MAX) ];
238248

239-
uchar chained_merkle_root[ FD_SHRED_MERKLE_ROOT_SZ ];
249+
uchar * chained_merkle_root;
250+
uchar block_ids[ BLOCK_IDS_TABLE_CNT ][ FD_SHRED_MERKLE_ROOT_SZ ];
240251
} fd_shred_ctx_t;
241252

242253
FD_FN_CONST static inline ulong
@@ -449,16 +460,46 @@ during_frag( fd_shred_ctx_t * ctx,
449460
ctx->batch_cnt = 0UL;
450461
ctx->slot = target_slot;
451462

452-
/* Only copy parent_block_id to chained_merkle_root at the beginning
453-
of a new slot*/
463+
/* At the beginning of a new slot, prepare chained_merkle_root.
464+
chained_merkle_root is initialized at the block_id of the parent
465+
block, there's two cases:
466+
467+
1. block_id is passed in by the poh tile:
468+
- it's always passed when parent block had a different leader
469+
- it may be passed when we were leader for parent block (there
470+
are race conditions when it's not passed)
471+
472+
2. block_id is taken from block_ids table if we were the leader
473+
for the parent block (when we were NOT the leader, because of
474+
equivocation, we can't store block_id in the table)
475+
476+
chained_merkle_root is stored in block_ids table at target_slot
477+
and it's progressively updated as more microblocks are received.
478+
As a result, when we move to a new slot, the block_ids table at
479+
the old slot will contain the block_id.
480+
481+
The block_ids table is designed to protect against the race condition
482+
case in 1., therefore the table may not be set in some cases, e.g. if
483+
a validator (re)starts, but in those cases we don't expect the race
484+
condition to apply. */
485+
ctx->chained_merkle_root = ctx->block_ids[ target_slot % BLOCK_IDS_TABLE_CNT ];
454486
if( FD_UNLIKELY( SHOULD_PROCESS_THESE_SHREDS ) ) {
455-
/* chained_merkle_root is set as the merkle root of the last FEC set
456-
of the parent block (and passed in by POH tile) */
457487
if( FD_LIKELY( entry_meta->parent_block_id_valid ) ) {
488+
/* 1. Initialize chained_merkle_root sent from poh tile */
458489
memcpy( ctx->chained_merkle_root, entry_meta->parent_block_id, FD_SHRED_MERKLE_ROOT_SZ );
459490
} else {
460-
ctx->metrics->invalid_block_id_cnt++;
461-
memset( ctx->chained_merkle_root, 0, FD_SHRED_MERKLE_ROOT_SZ );
491+
ulong parent_slot = target_slot - entry_meta->parent_offset;
492+
fd_epoch_leaders_t const * lsched = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, parent_slot );
493+
fd_pubkey_t const * slot_leader = fd_epoch_leaders_get( lsched, parent_slot );
494+
495+
if( lsched && slot_leader && fd_memeq( slot_leader, ctx->identity_key, sizeof(fd_pubkey_t) ) ) {
496+
/* 2. Initialize chained_merkle_root from block_ids table, if we were the leader */
497+
memcpy( ctx->chained_merkle_root, ctx->block_ids[ parent_slot % BLOCK_IDS_TABLE_CNT ], FD_SHRED_MERKLE_ROOT_SZ );
498+
} else {
499+
/* This should never happen, log a metric and set chained_merkle_root to 0 */
500+
ctx->metrics->invalid_block_id_cnt++;
501+
memset( ctx->chained_merkle_root, 0, FD_SHRED_MERKLE_ROOT_SZ );
502+
}
462503
}
463504
}
464505
}
@@ -921,7 +962,7 @@ after_frag( fd_shred_ctx_t * ctx,
921962

922963
long shacq_start, shacq_end, shrel_end;
923964
FD_STORE_SHACQ_TIMED( ctx->store, shacq_start, shacq_end );
924-
fd_store_fec_t * fec = fd_store_insert( ctx->store, (uint)ctx->round_robin_id, (fd_hash_t *)fd_type_pun( &out_merkle_root ) );
965+
fd_store_fec_t * fec = fd_store_insert( ctx->store, ctx->round_robin_id, (fd_hash_t *)fd_type_pun( &out_merkle_root ) );
925966
FD_STORE_SHREL_TIMED( ctx->store, shrel_end );
926967

927968
for( ulong i=0UL; i<set->data_shred_cnt; i++ ) {
@@ -1303,6 +1344,8 @@ unprivileged_init( fd_topo_t * topo,
13031344
ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
13041345
if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
13051346
FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
1347+
1348+
memset( ctx->block_ids, 0, sizeof(ctx->block_ids) );
13061349
}
13071350

13081351
static ulong

src/disco/stem/fd_stem.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,6 @@
190190
#define STEM_LAZY (0L)
191191
#endif
192192

193-
#define STEM_SHUTDOWN_SEQ (ULONG_MAX-1UL)
194-
195193
static inline void
196194
STEM_(in_update)( fd_stem_tile_in_t * in ) {
197195
fd_fseq_update( in->fseq, in->seq );
@@ -433,10 +431,6 @@ STEM_(run1)( ulong in_cnt,
433431
ulong out_idx = cons_out[ cons_idx ];
434432
ulong cons_cr_avail = (ulong)fd_long_max( (long)out_depth[ out_idx ]-fd_long_max( fd_seq_diff( out_seq[ out_idx ], cons_seq[ cons_idx ] ), 0L ), 0L );
435433

436-
/* If a reliable consumer exits, they can set the credit
437-
return fseq to STEM_SHUTDOWN_SEQ to indicate they are no
438-
longer actively consuming. */
439-
cons_cr_avail = fd_ulong_if( cons_seq[ cons_idx ]==STEM_SHUTDOWN_SEQ, out_depth[ out_idx ], cons_cr_avail );
440434
slowest_cons = fd_ulong_if( cons_cr_avail<min_cr_avail, cons_idx, slowest_cons );
441435

442436
cr_avail[ out_idx ] = fd_ulong_min( cr_avail[ out_idx ], cons_cr_avail );
@@ -789,7 +783,7 @@ STEM_(run)( fd_topo_t * topo,
789783
ulong fseq_id = tile->in_link_fseq_obj_id[ i ];
790784
ulong * fseq = fd_fseq_join( fd_topo_obj_laddr( topo, fseq_id ) );
791785
FD_TEST( fseq );
792-
fd_fseq_update( fseq, STEM_SHUTDOWN_SEQ );
786+
fd_fseq_update( fseq, ULONG_MAX );
793787
}
794788
}
795789
}

src/disco/store/fd_store.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ fd_store_new( void * shmem, ulong fec_max, ulong part_cnt ) {
3838
}
3939

4040
fd_memset( shmem, 0, footprint );
41-
fec_max = fd_ulong_pow2_up( fec_max ); /* required by map_chain */
4241

4342
/* This seed value is very important. We have fec_max chains in the
4443
map, which means the size of each partition of buckets should be
@@ -66,6 +65,10 @@ fd_store_new( void * shmem, ulong fec_max, ulong part_cnt ) {
6665
store->root = null;
6766

6867
fd_store_pool_t pool = fd_store_pool( store );
68+
if( FD_UNLIKELY( !fd_store_pool_new( shpool ) ) ) {
69+
FD_LOG_WARNING(( "fd_store_pool_new failed" ));
70+
return NULL;
71+
}
6972
fd_store_pool_reset( &pool, 0 );
7073

7174
FD_COMPILER_MFENCE();
@@ -115,7 +118,8 @@ fd_store_leave( fd_store_t const * store ) {
115118
}
116119

117120
void *
118-
fd_store_delete( void * store ) {
121+
fd_store_delete( void * shstore ) {
122+
fd_store_t * store = shstore;
119123

120124
if( FD_UNLIKELY( !store ) ) {
121125
FD_LOG_WARNING(( "NULL store" ));
@@ -127,6 +131,15 @@ fd_store_delete( void * store ) {
127131
return NULL;
128132
}
129133

134+
if( FD_UNLIKELY( store->magic!=FD_STORE_MAGIC ) ) {
135+
FD_LOG_WARNING(( "bad magic" ));
136+
return NULL;
137+
}
138+
139+
FD_COMPILER_MFENCE();
140+
FD_VOLATILE( store->magic ) = 0UL;
141+
FD_COMPILER_MFENCE();
142+
130143
return store;
131144
}
132145

@@ -207,14 +220,14 @@ fd_store_publish( fd_store_t * store,
207220
any descendants of those ancestors. */
208221

209222
while( FD_LIKELY( head ) ) {
210-
fd_store_fec_t * child = fd_store_pool_ele( &pool, head->child ); /* left-child */
223+
fd_store_fec_t * child = fd_store_pool_ele( &pool, head->child ); /* left-child */
211224
while( FD_LIKELY( child ) ) { /* iterate over children */
212225
if( FD_LIKELY( child != newr ) ) { /* stop at new root */
213226
tail->next = fd_store_map_idx_remove( map, &child->key, null, fec0 ); /* remove node from map to reuse `.next` */
214-
tail = fd_store_pool_ele( &pool, tail->next ); /* push onto BFS queue (so descendants can be pruned) */
227+
tail = fd_store_pool_ele( &pool, tail->next ); /* push onto BFS queue (so descendants can be pruned) */
215228
tail->next = null; /* clear map next */
216229
}
217-
child = fd_store_pool_ele( &pool, child->sibling ); /* right-sibling */
230+
child = fd_store_pool_ele( &pool, child->sibling ); /* right-sibling */
218231
}
219232
fd_store_fec_t * next = fd_store_pool_ele( &pool, head->next ); /* pophead */
220233
int err = fd_store_pool_release( &pool, head, BLOCKING ); /* release */
@@ -231,12 +244,18 @@ fd_store_publish( fd_store_t * store,
231244

232245
fd_store_t *
233246
fd_store_clear( fd_store_t * store ) {
247+
234248
fd_store_map_t * map = fd_store_map( store );
235249
fd_store_pool_t pool = fd_store_pool( store );
236250
fd_store_fec_t * fec0 = fd_store_fec0( store );
237251

238252
fd_store_fec_t * head = fd_store_root( store );
239253
fd_store_fec_t * tail = head;
254+
255+
# if FD_STORE_USE_HANDHOLDING
256+
if ( FD_UNLIKELY( !head ) ) { FD_LOG_WARNING(( "calling clear on an empty store" )); return store; }
257+
# endif
258+
240259
for( fd_store_map_iter_t iter = fd_store_map_iter_init( map, fec0 );
241260
!fd_store_map_iter_done( iter, map, fec0 );
242261
iter = fd_store_map_iter_next( iter, map, fec0 ) ) {
@@ -283,6 +302,8 @@ fd_store_verify( fd_store_t * store ) {
283302
return -1;
284303
}
285304
}
305+
fd_store_pool_t pool = fd_store_pool_const( store );
306+
if( FD_UNLIKELY( fd_store_pool_verify( &pool )==-1 ) ) return -1;
286307
return fd_store_map_verify( map, store->fec_max, fec0 );
287308
}
288309

@@ -296,7 +317,7 @@ print( fd_store_t const * store, fd_store_fec_t const * fec, int space, const ch
296317

297318
if( space > 0 ) printf( "\n" );
298319
for( int i = 0; i < space; i++ ) printf( " " );
299-
printf( "%s%s", prefix, FD_BASE58_ENC_32_ALLOCA( &fec->key ) );
320+
printf( "%s%s", prefix, FD_BASE58_ENC_32_ALLOCA( &fec->key.mr ) );
300321

301322
fd_store_fec_t const * curr = fd_store_pool_ele_const( &pool, fec->child );
302323
char new_prefix[1024]; /* FIXME size this correctly */

src/disco/store/fd_store.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
#define MAP_KEY_HASH(key,seed) ((ulong)key->mr.ul[0]%seed + (key)->part*seed)
9595
```
9696
where `key` is a key type that includes the merkle root (32 bytes)
97-
and the partition index (4 bytes) that is equivalent to the Shred
97+
and the partition index (8 bytes) that is equivalent to the Shred
9898
tile index doing the insertion. seed, on initialization, is the
9999
number of chains/buckets in the map_chain divided by the number of
100100
partitions. In effect, seed is the size of each partition. For
@@ -126,7 +126,7 @@
126126
hash chain within that slot (which modifies what the head of the
127127
chain points to as well as the now-previous head in the hash chain's
128128
`.next` field, but does not touch application data). With fencing
129-
enabled (FD_MAP_INSERT_FENCING), it is guaranteed the consumer either
129+
enabled (MAP_INSERT_FENCE), it is guaranteed the consumer either
130130
reads the head before or after the update. If it reads before, that
131131
is safe, it would just check the key (if no match, iterate down the
132132
chain etc.) If it reads after, it is also safe because the new
@@ -171,15 +171,15 @@
171171
#define FD_STORE_DATA_MAX (63985UL) /* TODO fixed-32 */
172172

173173
/* fd_store_fec describes a store element (FEC set). The pointer fields
174-
  implement a left-child, right-sibling n-ary tree. */
174+
implement a left-child, right-sibling n-ary tree. */
175175

176176
struct __attribute__((packed)) fd_store_key {
177177
fd_hash_t mr;
178178
ulong part; /* partition index of the inserter */
179179
};
180180
typedef struct fd_store_key fd_store_key_t;
181181

182-
struct __attribute__((aligned(128UL))) fd_store_fec {
182+
struct __attribute__((aligned(FD_STORE_ALIGN))) fd_store_fec {
183183

184184
/* Keys */
185185

@@ -233,7 +233,7 @@ FD_PROTOTYPES_BEGIN
233233

234234
/* fd_store_{align,footprint} return the required alignment and
235235
footprint of a memory region suitable for use as store with up to
236-
fec_max elements. */
236+
fec_max elements. fec_max is an integer power-of-two. */
237237

238238
FD_FN_CONST static inline ulong
239239
fd_store_align( void ) {
@@ -242,6 +242,7 @@ fd_store_align( void ) {
242242

243243
FD_FN_CONST static inline ulong
244244
fd_store_footprint( ulong fec_max ) {
245+
if( FD_UNLIKELY( !fd_ulong_is_pow2( fec_max ) ) ) return 0UL;
245246
return FD_LAYOUT_FINI(
246247
FD_LAYOUT_APPEND(
247248
FD_LAYOUT_APPEND(
@@ -257,7 +258,8 @@ fd_store_footprint( ulong fec_max ) {
257258

258259
/* fd_store_new formats an unused memory region for use as a store.
259260
mem is a non-NULL pointer to this region in the local address space
260-
with the required footprint and alignment. */
261+
with the required footprint and alignment. fec_max is an integer
262+
power-of-two. */
261263

262264
void *
263265
fd_store_new( void * shmem, ulong fec_max, ulong part_cnt );
@@ -286,7 +288,7 @@ fd_store_leave( fd_store_t const * store );
286288
caller. */
287289

288290
void *
289-
fd_store_delete( void * store );
291+
fd_store_delete( void * shstore );
290292

291293
/* Accessors */
292294

@@ -451,7 +453,9 @@ fd_store_publish( fd_store_t * store,
451453
fd_hash_t * merkle_root );
452454

453455
/* fd_store_clear clears the store. All elements are removed from the
454-
map and released back into the pool. Does not zero-out fields. */
456+
map and released back into the pool. Does not zero-out fields.
457+
458+
IMPORTANT SAFETY TIP! the store must be non-empty. */
455459

456460
fd_store_t *
457461
fd_store_clear( fd_store_t * store );

src/discof/poh/fd_poh_tile.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,8 +1255,6 @@ fd_ext_poh_reset( ulong completed_bank_slot, /* The slot that successful
12551255
if( FD_LIKELY( parent_block_id!=NULL ) ) {
12561256
ctx->parent_slot = completed_bank_slot;
12571257
memcpy( ctx->parent_block_id, parent_block_id, 32UL );
1258-
} else {
1259-
FD_LOG_WARNING(( "fd_ext_poh_reset(block_id=null,reset_slot=%lu,parent_slot=%lu) - ignored", completed_bank_slot, ctx->parent_slot ));
12601258
}
12611259
ctx->slot = completed_bank_slot+1UL;
12621260
ctx->hashcnt = 0UL;

src/discof/restore/Local.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ $(call make-unit-test,test_ssmanifest_parser,utils/test_ssmanifest_parser,fd_dis
1616
ifdef FD_HAS_HOSTED
1717
$(call make-fuzz-test,fuzz_snapshot_parser,utils/fuzz_snapshot_parser,fd_discof fd_flamenco fd_ballet fd_util)
1818
$(call make-fuzz-test,fuzz_ssmanifest_parser,utils/fuzz_ssmanifest_parser,fd_discof fd_flamenco fd_ballet fd_util)
19+
$(call make-fuzz-test,fuzz_ssarchive_parser,utils/fuzz_ssarchive_parser,fd_discof fd_flamenco fd_ballet fd_util)
1920
endif
2021

2122
endif
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "fd_ssarchive.h"
2+
3+
#if !FD_HAS_HOSTED
4+
#error "This target requires FD_HAS_HOSTED"
5+
#endif
6+
7+
#include <stdlib.h>
8+
#include <assert.h>
9+
10+
int
11+
LLVMFuzzerInitialize( int * argc,
12+
char *** argv ) {
13+
/* Set up shell without signal handlers */
14+
putenv( "FD_LOG_BACKTRACE=0" );
15+
fd_boot( argc, argv );
16+
atexit( fd_halt );
17+
fd_log_level_core_set ( 4 );
18+
fd_log_level_logfile_set( 4 );
19+
20+
return 0;
21+
}
22+
23+
int
24+
LLVMFuzzerTestOneInput( uchar const * data,
25+
ulong size ) {
26+
ulong full_slot;
27+
ulong incremental_slot;
28+
uchar decoded_hash[ FD_HASH_FOOTPRINT ];
29+
30+
char* name_cstr = (char *)malloc( size + 1UL );
31+
assert( name_cstr );
32+
fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( name_cstr ), (char const *)data, size ) );
33+
34+
fd_ssarchive_parse_filename( name_cstr, &full_slot, &incremental_slot, decoded_hash );
35+
36+
free( name_cstr );
37+
return 0;
38+
}

src/discoh/poh/fd_poh_tile.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,8 +1259,6 @@ fd_ext_poh_reset( ulong completed_bank_slot, /* The slot that successful
12591259
if( FD_LIKELY( parent_block_id!=NULL ) ) {
12601260
ctx->parent_slot = completed_bank_slot;
12611261
memcpy( ctx->parent_block_id, parent_block_id, 32UL );
1262-
} else {
1263-
FD_LOG_WARNING(( "fd_ext_poh_reset(block_id=null,reset_slot=%lu,parent_slot=%lu) - ignored", completed_bank_slot, ctx->parent_slot ));
12641262
}
12651263
ctx->slot = completed_bank_slot+1UL;
12661264
ctx->hashcnt = 0UL;

src/flamenco/leaders/fd_leaders.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ fd_epoch_leaders_delete( void * shleaders );
150150
FD_FN_PURE static inline fd_pubkey_t const *
151151
fd_epoch_leaders_get( fd_epoch_leaders_t const * leaders,
152152
ulong slot ) {
153+
if( FD_UNLIKELY( leaders==NULL ) ) return NULL;
153154
ulong slot_delta = slot - leaders->slot0;
154155
if( FD_UNLIKELY( slot < leaders->slot0 ) ) return NULL;
155156
if( FD_UNLIKELY( slot_delta>=leaders->slot_cnt ) ) return NULL;

0 commit comments

Comments
 (0)