@@ -104,6 +104,16 @@ FD_STATIC_ASSERT( sizeof(fd_entry_batch_meta_t)==56UL, poh_shred_mtu );
104
104
105
105
#define FD_SHRED_ADD_SHRED_EXTRA_RETVAL_CNT 2
106
106
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
+
107
117
/* See note on parallelization above. Currently we process all batches in tile 0. */
108
118
#if 1
109
119
#define SHOULD_PROCESS_THESE_SHREDS ( ctx->round_robin_id==0 )
@@ -236,7 +246,8 @@ typedef struct {
236
246
/* too large to be left in the stack */
237
247
fd_shred_dest_idx_t scratchpad_dests [ FD_SHRED_DEST_MAX_FANOUT * (FD_REEDSOL_DATA_SHREDS_MAX + FD_REEDSOL_PARITY_SHREDS_MAX ) ];
238
248
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 ];
240
251
} fd_shred_ctx_t ;
241
252
242
253
FD_FN_CONST static inline ulong
@@ -449,16 +460,46 @@ during_frag( fd_shred_ctx_t * ctx,
449
460
ctx -> batch_cnt = 0UL ;
450
461
ctx -> slot = target_slot ;
451
462
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 ];
454
486
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) */
457
487
if ( FD_LIKELY ( entry_meta -> parent_block_id_valid ) ) {
488
+ /* 1. Initialize chained_merkle_root sent from poh tile */
458
489
memcpy ( ctx -> chained_merkle_root , entry_meta -> parent_block_id , FD_SHRED_MERKLE_ROOT_SZ );
459
490
} 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
+ }
462
503
}
463
504
}
464
505
}
@@ -1303,6 +1344,8 @@ unprivileged_init( fd_topo_t * topo,
1303
1344
ulong scratch_top = FD_SCRATCH_ALLOC_FINI ( l , 1UL );
1304
1345
if ( FD_UNLIKELY ( scratch_top > (ulong )scratch + scratch_footprint ( tile ) ) )
1305
1346
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 ) );
1306
1349
}
1307
1350
1308
1351
static ulong
0 commit comments