From 4ff79e1e2108df2267841fb4a34b29cb76d38d54 Mon Sep 17 00:00:00 2001 From: Michael McGee Date: Mon, 30 Jun 2025 18:05:06 +0000 Subject: [PATCH] pack: pack is responsible for ending leader slots In full Firedancer the bank forks objects are implicitly refcounted by the leader pipeline, so they are acquired by pack and released by poh, because of this pack must "flush" the pipeline and give poh a way to guarantee the bank fork is not in use when it releases, which it can do by exactly sequencing the done packing messaage (must arrive after the bank microblocks). --- src/app/fdctl/topology.c | 6 +- src/disco/fd_disco_base.h | 9 +- src/disco/gui/fd_gui_tile.c | 8 +- src/disco/pack/fd_pack_tile.c | 174 ++++++++++++++++++--------------- src/disco/tiles.h | 1 + src/discof/poh/fd_poh_tile.c | 55 +++++------ src/discoh/bank/fd_bank_tile.c | 12 +-- src/discoh/poh/fd_poh_tile.c | 71 ++++++-------- 8 files changed, 169 insertions(+), 167 deletions(-) diff --git a/src/app/fdctl/topology.c b/src/app/fdctl/topology.c index c09006ff22..477b9b93d4 100644 --- a/src/app/fdctl/topology.c +++ b/src/app/fdctl/topology.c @@ -34,6 +34,7 @@ fd_topo_initialize( config_t * config ) { fd_topob_wksp( topo, "dedup_resolv" ); fd_topob_wksp( topo, "resolv_pack" ); fd_topob_wksp( topo, "pack_bank" ); + fd_topob_wksp( topo, "pack_poh" ); fd_topob_wksp( topo, "bank_pack" ); fd_topob_wksp( topo, "bank_poh" ); fd_topob_wksp( topo, "bank_busy" ); @@ -75,6 +76,7 @@ fd_topo_initialize( config_t * config ) { /* pack_bank is shared across all banks, so if one bank stalls due to complex transactions, the buffer neeeds to be large so that other banks can keep proceeding. */ /**/ fd_topob_link( topo, "pack_bank", "pack_bank", 65536UL, USHORT_MAX, 1UL ); + /**/ fd_topob_link( topo, "pack_poh", "pack_poh", 128UL, sizeof(fd_done_packing_t), 1UL ); FOR(bank_tile_cnt) fd_topob_link( topo, "bank_poh", "bank_poh", 16384UL, USHORT_MAX, 1UL ); FOR(bank_tile_cnt) fd_topob_link( topo, "bank_pack", "bank_pack", 16384UL, USHORT_MAX, 3UL ); /**/ fd_topob_link( topo, "poh_pack", "bank_poh", 128UL, sizeof(fd_became_leader_t), 1UL ); @@ -170,6 +172,7 @@ fd_topo_initialize( config_t * config ) { /**/ fd_topob_tile_in( topo, "pack", 0UL, "metric_in", "poh_pack", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_in( topo, "pack", 0UL, "metric_in", "executed_txn", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED ); fd_topob_tile_out( topo, "pack", 0UL, "pack_bank", 0UL ); + fd_topob_tile_out( topo, "pack", 0UL, "pack_poh", 0UL ); FOR(bank_tile_cnt) fd_topob_tile_in( topo, "bank", i, "metric_in", "pack_bank", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); FOR(bank_tile_cnt) fd_topob_tile_out( topo, "bank", i, "bank_poh", i ); FOR(bank_tile_cnt) fd_topob_tile_out( topo, "bank", i, "bank_pack", i ); @@ -177,7 +180,7 @@ fd_topo_initialize( config_t * config ) { if( FD_LIKELY( config->tiles.pack.use_consumed_cus ) ) FOR(bank_tile_cnt) fd_topob_tile_in( topo, "pack", 0UL, "metric_in", "bank_pack", i, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_in( topo, "poh", 0UL, "metric_in", "stake_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); - /**/ fd_topob_tile_in( topo, "poh", 0UL, "metric_in", "pack_bank", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + /**/ fd_topob_tile_in( topo, "poh", 0UL, "metric_in", "pack_poh", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_out( topo, "poh", 0UL, "poh_shred", 0UL ); /**/ fd_topob_tile_out( topo, "poh", 0UL, "poh_pack", 0UL ); FOR(shred_tile_cnt) for( ulong j=0UL; j> 32); } -FD_FN_CONST static inline ulong fd_disco_bank_sig_microblock_idx( ulong sig ) { return sig & 0xFFFFFFFFUL; } +FD_FN_CONST static inline ulong fd_disco_bank_sig_pack_idx( ulong sig ) { return sig & 0xFFFFFFFFUL; } /* TODO remove with store_int */ diff --git a/src/disco/gui/fd_gui_tile.c b/src/disco/gui/fd_gui_tile.c index 7893c9c97b..776275f603 100644 --- a/src/disco/gui/fd_gui_tile.c +++ b/src/disco/gui/fd_gui_tile.c @@ -43,7 +43,8 @@ extern uint const fdctl_commit_ref; #define IN_KIND_PLUGIN (0UL) #define IN_KIND_POH_PACK (1UL) #define IN_KIND_PACK_BANK (2UL) -#define IN_KIND_BANK_POH (3UL) +#define IN_KIND_PACK_POH (3UL) +#define IN_KIND_BANK_POH (4UL) FD_IMPORT_BINARY( firedancer_svg, "book/public/fire.svg" ); @@ -219,6 +220,8 @@ after_frag( fd_gui_ctx_t * ctx, FD_TEST( fd_disco_poh_sig_pkt_type( sig )==POH_PKT_TYPE_BECAME_LEADER ); fd_became_leader_t * became_leader = (fd_became_leader_t *)ctx->buf; fd_gui_became_leader( ctx->gui, fd_frag_meta_ts_decomp( tspub, fd_tickcount() ), fd_disco_poh_sig_slot( sig ), became_leader->slot_start_ns, became_leader->slot_end_ns, became_leader->limits.slot_max_cost, became_leader->max_microblocks_in_slot ); + } else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_PACK_POH ) ) { + fd_gui_unbecame_leader( ctx->gui, fd_frag_meta_ts_decomp( tspub, fd_tickcount() ), fd_disco_poh_sig_slot( sig ), ((fd_done_packing_t *)ctx->buf)->microblocks_in_slot ); } else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_PACK_BANK ) ) { if( FD_LIKELY( fd_disco_poh_sig_pkt_type( sig )==POH_PKT_TYPE_MICROBLOCK ) ) { FD_TEST( szmicroblock_idx, trailer->pack_txn_idx ); - } else if( FD_LIKELY( fd_disco_poh_sig_pkt_type( sig )==POH_PKT_TYPE_DONE_PACKING ) ) { - fd_gui_unbecame_leader( ctx->gui, fd_frag_meta_ts_decomp( tspub, fd_tickcount() ), fd_disco_poh_sig_slot( sig ), ((fd_done_packing_t *)ctx->buf)->microblocks_in_slot ); } else { FD_LOG_ERR(( "unexpected poh packet type %lu", fd_disco_poh_sig_pkt_type( sig ) )); } @@ -511,6 +512,7 @@ unprivileged_init( fd_topo_t * topo, if( FD_LIKELY( !strcmp( link->name, "plugin_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_PLUGIN; else if( FD_LIKELY( !strcmp( link->name, "poh_pack" ) ) ) ctx->in_kind[ i ] = IN_KIND_POH_PACK; else if( FD_LIKELY( !strcmp( link->name, "pack_bank" ) ) ) ctx->in_kind[ i ] = IN_KIND_PACK_BANK; + else if( FD_LIKELY( !strcmp( link->name, "pack_poh" ) ) ) ctx->in_kind[ i ] = IN_KIND_PACK_POH; else if( FD_LIKELY( !strcmp( link->name, "bank_poh" ) ) ) ctx->in_kind[ i ] = IN_KIND_BANK_POH; else FD_LOG_ERR(( "gui tile has unexpected input link %lu %s", i, link->name )); diff --git a/src/disco/pack/fd_pack_tile.c b/src/disco/pack/fd_pack_tile.c index 1877f77d30..9bad1f1a2d 100644 --- a/src/disco/pack/fd_pack_tile.c +++ b/src/disco/pack/fd_pack_tile.c @@ -137,12 +137,16 @@ typedef struct { ulong leader_slot; void const * leader_bank; + fd_became_leader_t _became_leader[1]; + /* The number of microblocks we have packed for the current leader slot. Will always be <= slot_max_microblocks. We must track this so that when we are done we can tell the PoH tile how many microblocks to expect in the slot. */ ulong slot_microblock_cnt; + uint pack_idx; + ulong pack_txn_cnt; /* total num transactions packed since startup */ /* The maximum number of microblocks that can be packed in this slot. @@ -238,10 +242,15 @@ typedef struct { least bank_ready_at[x]. */ long bank_ready_at[ FD_PACK_MAX_BANK_TILES ]; - fd_wksp_t * out_mem; - ulong out_chunk0; - ulong out_wmark; - ulong out_chunk; + fd_wksp_t * bank_out_mem; + ulong bank_out_chunk0; + ulong bank_out_wmark; + ulong bank_out_chunk; + + fd_wksp_t * poh_out_mem; + ulong poh_out_chunk0; + ulong poh_out_wmark; + ulong poh_out_chunk; ulong insert_result[ FD_PACK_INSERT_RETVAL_CNT ]; fd_histf_t schedule_duration[ 1 ]; @@ -527,18 +536,12 @@ after_credit( fd_pack_ctx_t * ctx, if( FD_UNLIKELY( ctx->approx_wallclock_ns>=ctx->slot_end_ns && ctx->leader_slot!=ULONG_MAX ) ) { *charge_busy = 1; - if( FD_UNLIKELY( ctx->slot_microblock_cntslot_max_microblocks )) { - /* As an optimization, The PoH tile will automatically end a slot - if it receives the maximum allowed microblocks, since it knows - there is nothing left to receive. In that case, we don't need - to send a DONE_PACKING notification, since they are already on - the next slot. If we did send one it would just get dropped. */ - fd_done_packing_t * done_packing = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk ); - done_packing->microblocks_in_slot = ctx->slot_microblock_cnt; + fd_done_packing_t * done_packing = fd_chunk_to_laddr( ctx->poh_out_mem, ctx->poh_out_chunk ); + done_packing->microblocks_in_slot = ctx->slot_microblock_cnt; - fd_stem_publish( stem, 0UL, fd_disco_poh_sig( ctx->leader_slot, POH_PKT_TYPE_DONE_PACKING, ULONG_MAX ), ctx->out_chunk, sizeof(fd_done_packing_t), 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) ); - ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_done_packing_t), ctx->out_chunk0, ctx->out_wmark ); - } + fd_stem_publish( stem, 1UL, fd_disco_bank_sig( ctx->leader_slot, ctx->pack_idx ), ctx->poh_out_chunk, sizeof(fd_done_packing_t), 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) ); + ctx->poh_out_chunk = fd_dcache_compact_next( ctx->poh_out_chunk, sizeof(fd_done_packing_t), ctx->poh_out_chunk0, ctx->poh_out_wmark ); + ctx->pack_idx++; log_end_block_metrics( ctx, now, "time" ); ctx->drain_banks = 1; @@ -682,7 +685,7 @@ after_credit( fd_pack_ctx_t * ctx, break; } - fd_txn_p_t * microblock_dst = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk ); + fd_txn_p_t * microblock_dst = fd_chunk_to_laddr( ctx->bank_out_mem, ctx->bank_out_chunk ); long schedule_duration = -fd_tickcount(); ulong schedule_cnt = fd_pack_schedule_next_microblock( ctx->pack, CUS_PER_MICROBLOCK, VOTE_FRACTION, (ulong)i, flags, microblock_dst ); schedule_duration += fd_tickcount(); @@ -693,11 +696,12 @@ after_credit( fd_pack_ctx_t * ctx, long now2 = fd_tickcount(); ulong tsorig = (ulong)fd_frag_meta_ts_comp( now ); /* A bound on when we observed bank was idle */ ulong tspub = (ulong)fd_frag_meta_ts_comp( now2 ); - ulong chunk = ctx->out_chunk; + ulong chunk = ctx->bank_out_chunk; ulong msg_sz = schedule_cnt*sizeof(fd_txn_p_t); fd_microblock_bank_trailer_t * trailer = (fd_microblock_bank_trailer_t*)(microblock_dst+schedule_cnt); trailer->bank = ctx->leader_bank; trailer->microblock_idx = ctx->slot_microblock_cnt; + trailer->pack_idx = ctx->pack_idx; trailer->pack_txn_idx = ctx->pack_txn_cnt; trailer->is_bundle = !!(microblock_dst->flags & FD_TXN_P_FLAGS_BUNDLE); @@ -705,8 +709,9 @@ after_credit( fd_pack_ctx_t * ctx, fd_stem_publish( stem, 0UL, sig, chunk, msg_sz+sizeof(fd_microblock_bank_trailer_t), 0UL, tsorig, tspub ); ctx->bank_expect[ i ] = stem->seqs[0]-1UL; ctx->bank_ready_at[i] = now2 + (long)ctx->microblock_duration_ticks; - ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, msg_sz+sizeof(fd_microblock_bank_trailer_t), ctx->out_chunk0, ctx->out_wmark ); + ctx->bank_out_chunk = fd_dcache_compact_next( ctx->bank_out_chunk, msg_sz+sizeof(fd_microblock_bank_trailer_t), ctx->bank_out_chunk0, ctx->bank_out_wmark ); ctx->slot_microblock_cnt += fd_ulong_if( trailer->is_bundle, schedule_cnt, 1UL ); + ctx->pack_idx += fd_uint_if( trailer->is_bundle, (uint)schedule_cnt, 1U ); ctx->pack_txn_cnt += schedule_cnt; ctx->bank_idle_bitset = fd_ulong_pop_lsb( ctx->bank_idle_bitset ); @@ -778,61 +783,7 @@ during_frag( fd_pack_ctx_t * ctx, if( FD_UNLIKELY( chunkin[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz!=sizeof(fd_became_leader_t) ) ) FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark )); - long now_ticks = fd_tickcount(); - long now_ns = fd_log_wallclock(); - - if( FD_UNLIKELY( ctx->leader_slot!=ULONG_MAX ) ) { - FD_LOG_WARNING(( "switching to slot %lu while packing for slot %lu. Draining bank tiles.", fd_disco_poh_sig_slot( sig ), ctx->leader_slot )); - log_end_block_metrics( ctx, now_ticks, "switch" ); - ctx->drain_banks = 1; - ctx->leader_slot = ULONG_MAX; - ctx->slot_microblock_cnt = 0UL; - fd_pack_end_block( ctx->pack ); - remove_ib( ctx ); - } - ctx->leader_slot = fd_disco_poh_sig_slot( sig ); - - ulong exp_cnt = fd_pack_expire_before( ctx->pack, fd_ulong_max( ctx->leader_slot, TRANSACTION_LIFETIME_SLOTS )-TRANSACTION_LIFETIME_SLOTS ); - FD_MCNT_INC( PACK, TRANSACTION_EXPIRED, exp_cnt ); - - fd_became_leader_t * became_leader = (fd_became_leader_t *)dcache_entry; - ctx->leader_bank = became_leader->bank; - ctx->slot_max_microblocks = became_leader->max_microblocks_in_slot; - /* Reserve some space in the block for ticks */ - ctx->slot_max_data = (ctx->larger_shred_limits_per_block ? LARGER_MAX_DATA_PER_BLOCK : FD_PACK_MAX_DATA_PER_BLOCK) - - 48UL*(became_leader->ticks_per_slot+became_leader->total_skipped_ticks); - - ctx->limits.slot_max_cost = became_leader->limits.slot_max_cost; - ctx->limits.slot_max_vote_cost = became_leader->limits.slot_max_vote_cost; - ctx->limits.slot_max_write_cost_per_acct = became_leader->limits.slot_max_write_cost_per_acct; - - /* ticks_per_ns is probably relatively stable over 400ms, but not - over several hours, so we need to compute the slot duration in - milliseconds first and then convert to ticks. This doesn't need - to be super accurate, but we don't want it to vary wildly. */ - long end_ticks = now_ticks + (long)((double)fd_long_max( became_leader->slot_end_ns - now_ns, 1L )*ctx->ticks_per_ns); - /* We may still get overrun, but then we'll never use this and just - reinitialize it the next time when we actually become leader. */ - fd_pack_pacing_init( ctx->pacer, now_ticks, end_ticks, (float)ctx->ticks_per_ns, ctx->limits.slot_max_cost ); - - if( FD_UNLIKELY( ctx->crank->enabled ) ) { - /* If we get overrun, we'll just never use these values, but the - old values aren't really useful either. */ - ctx->crank->epoch = became_leader->epoch; - *(ctx->crank->prev_config) = *(became_leader->bundle->config); - memcpy( ctx->crank->recent_blockhash, became_leader->bundle->last_blockhash, 32UL ); - memcpy( ctx->crank->tip_receiver_owner, became_leader->bundle->tip_receiver_owner, 32UL ); - } - - FD_LOG_INFO(( "pack_became_leader(slot=%lu,ends_at=%ld)", ctx->leader_slot, became_leader->slot_end_ns )); - - /* The dcache might get overrun, so set slot_end_ns to 0, so if it does - the slot will get skipped. Then update it in the `after_frag` case - below to the correct value. */ - ctx->slot_end_ns = 0L; - ctx->_slot_end_ns = became_leader->slot_end_ns; - - update_metric_state( ctx, fd_tickcount(), FD_PACK_METRIC_STATE_LEADER, 1 ); + fd_memcpy( ctx->_became_leader, dcache_entry, sizeof(fd_became_leader_t) ); return; } case IN_KIND_BANK: { @@ -971,6 +922,68 @@ after_frag( fd_pack_ctx_t * ctx, case IN_KIND_POH: { if( fd_disco_poh_sig_pkt_type( sig )!=POH_PKT_TYPE_BECAME_LEADER ) return; + long now_ticks = fd_tickcount(); + long now_ns = fd_log_wallclock(); + + if( FD_UNLIKELY( ctx->leader_slot!=ULONG_MAX ) ) { + fd_done_packing_t * done_packing = fd_chunk_to_laddr( ctx->poh_out_mem, ctx->poh_out_chunk ); + done_packing->microblocks_in_slot = ctx->slot_microblock_cnt; + + fd_stem_publish( stem, 1UL, fd_disco_bank_sig( ctx->leader_slot, ctx->pack_idx ), ctx->poh_out_chunk, sizeof(fd_done_packing_t), 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) ); + ctx->poh_out_chunk = fd_dcache_compact_next( ctx->poh_out_chunk, sizeof(fd_done_packing_t), ctx->poh_out_chunk0, ctx->poh_out_wmark ); + ctx->pack_idx++; + + FD_LOG_WARNING(( "switching to slot %lu while packing for slot %lu. Draining bank tiles.", fd_disco_poh_sig_slot( sig ), ctx->leader_slot )); + log_end_block_metrics( ctx, now_ticks, "switch" ); + ctx->drain_banks = 1; + ctx->leader_slot = ULONG_MAX; + ctx->slot_microblock_cnt = 0UL; + fd_pack_end_block( ctx->pack ); + remove_ib( ctx ); + } + ctx->leader_slot = fd_disco_poh_sig_slot( sig ); + + ulong exp_cnt = fd_pack_expire_before( ctx->pack, fd_ulong_max( ctx->leader_slot, TRANSACTION_LIFETIME_SLOTS )-TRANSACTION_LIFETIME_SLOTS ); + FD_MCNT_INC( PACK, TRANSACTION_EXPIRED, exp_cnt ); + + ctx->leader_bank = ctx->_became_leader->bank; + ctx->slot_max_microblocks = ctx->_became_leader->max_microblocks_in_slot; + /* Reserve some space in the block for ticks */ + ctx->slot_max_data = (ctx->larger_shred_limits_per_block ? LARGER_MAX_DATA_PER_BLOCK : FD_PACK_MAX_DATA_PER_BLOCK) + - 48UL*(ctx->_became_leader->ticks_per_slot+ctx->_became_leader->total_skipped_ticks); + + ctx->limits.slot_max_cost = ctx->_became_leader->limits.slot_max_cost; + ctx->limits.slot_max_vote_cost = ctx->_became_leader->limits.slot_max_vote_cost; + ctx->limits.slot_max_write_cost_per_acct = ctx->_became_leader->limits.slot_max_write_cost_per_acct; + + /* ticks_per_ns is probably relatively stable over 400ms, but not + over several hours, so we need to compute the slot duration in + milliseconds first and then convert to ticks. This doesn't need + to be super accurate, but we don't want it to vary wildly. */ + long end_ticks = now_ticks + (long)((double)fd_long_max( ctx->_became_leader->slot_end_ns - now_ns, 1L )*ctx->ticks_per_ns); + /* We may still get overrun, but then we'll never use this and just + reinitialize it the next time when we actually become leader. */ + fd_pack_pacing_init( ctx->pacer, now_ticks, end_ticks, (float)ctx->ticks_per_ns, ctx->limits.slot_max_cost ); + + if( FD_UNLIKELY( ctx->crank->enabled ) ) { + /* If we get overrun, we'll just never use these values, but the + old values aren't really useful either. */ + ctx->crank->epoch = ctx->_became_leader->epoch; + *(ctx->crank->prev_config) = *(ctx->_became_leader->bundle->config); + memcpy( ctx->crank->recent_blockhash, ctx->_became_leader->bundle->last_blockhash, 32UL ); + memcpy( ctx->crank->tip_receiver_owner, ctx->_became_leader->bundle->tip_receiver_owner, 32UL ); + } + + FD_LOG_INFO(( "pack_became_leader(slot=%lu,ends_at=%ld)", ctx->leader_slot, ctx->_became_leader->slot_end_ns )); + + /* The dcache might get overrun, so set slot_end_ns to 0, so if it does + the slot will get skipped. Then update it in the `after_frag` case + below to the correct value. */ + ctx->slot_end_ns = 0L; + ctx->_slot_end_ns = ctx->_became_leader->slot_end_ns; + + update_metric_state( ctx, fd_tickcount(), FD_PACK_METRIC_STATE_LEADER, 1 ); + ctx->slot_end_ns = ctx->_slot_end_ns; fd_pack_limits_t limits[ 1 ]; limits->max_cost_per_block = ctx->limits.slot_max_cost; @@ -1099,6 +1112,7 @@ unprivileged_init( fd_topo_t * topo, if( FD_UNLIKELY( tile->in_cnt>32UL ) ) FD_LOG_ERR(( "Too many input links (%lu>32) to pack tile", tile->in_cnt )); + FD_TEST( tile->in_cntin_kind )/sizeof( ctx->in_kind[0] ) ); for( ulong i=0UL; iin_cnt; i++ ) { fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ]; @@ -1178,6 +1192,7 @@ unprivileged_init( fd_topo_t * topo, ctx->max_pending_transactions = tile->pack.max_pending_transactions; ctx->leader_slot = ULONG_MAX; ctx->leader_bank = NULL; + ctx->pack_idx = 0UL; ctx->slot_microblock_cnt = 0UL; ctx->pack_txn_cnt = 0UL; ctx->slot_max_microblocks = 0UL; @@ -1229,10 +1244,15 @@ unprivileged_init( fd_topo_t * topo, ctx->in[ i ].wmark = fd_dcache_compact_wmark ( ctx->in[ i ].mem, link->dcache, link->mtu ); } - ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp; - ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache ); - ctx->out_wmark = fd_dcache_compact_wmark ( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu ); - ctx->out_chunk = ctx->out_chunk0; + ctx->bank_out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp; + ctx->bank_out_chunk0 = fd_dcache_compact_chunk0( ctx->bank_out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache ); + ctx->bank_out_wmark = fd_dcache_compact_wmark ( ctx->bank_out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu ); + ctx->bank_out_chunk = ctx->bank_out_chunk0; + + ctx->poh_out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 1 ] ].dcache_obj_id ].wksp_id ].wksp; + ctx->poh_out_chunk0 = fd_dcache_compact_chunk0( ctx->poh_out_mem, topo->links[ tile->out_link_id[ 1 ] ].dcache ); + ctx->poh_out_wmark = fd_dcache_compact_wmark ( ctx->poh_out_mem, topo->links[ tile->out_link_id[ 1 ] ].dcache, topo->links[ tile->out_link_id[ 1 ] ].mtu ); + ctx->poh_out_chunk = ctx->poh_out_chunk0; /* Initialize metrics storage */ memset( ctx->insert_result, '\0', FD_PACK_INSERT_RETVAL_CNT * sizeof(ulong) ); diff --git a/src/disco/tiles.h b/src/disco/tiles.h index b7986a5ef6..1f329e9d1f 100644 --- a/src/disco/tiles.h +++ b/src/disco/tiles.h @@ -151,6 +151,7 @@ struct fd_microblock_bank_trailer { banks. This is used by PoH to ensure microblocks get committed in the same order they are executed. */ ulong microblock_idx; + uint pack_idx; /* A sequentially increasing index of the first transaction in the microblock, across all slots ever processed by pack. This is used diff --git a/src/discof/poh/fd_poh_tile.c b/src/discof/poh/fd_poh_tile.c index 35fd251644..e02e33051d 100644 --- a/src/discof/poh/fd_poh_tile.c +++ b/src/discof/poh/fd_poh_tile.c @@ -1775,7 +1775,7 @@ before_frag( fd_poh_ctx_t * ctx, (void)seq; if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_BANK ) ) { - ulong microblock_idx = fd_disco_bank_sig_microblock_idx( sig ); + ulong microblock_idx = fd_disco_bank_sig_pack_idx( sig ); FD_TEST( microblock_idx>=ctx->expect_microblock_idx ); /* Return the fragment to stem so we can process it later, if it's @@ -1796,7 +1796,7 @@ during_frag( fd_poh_ctx_t * ctx, ulong chunk, ulong sz, ulong ctl FD_PARAM_UNUSED ) { - + (void)sig; ctx->skip_frag = 0; if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_STAKE ) ) { @@ -1809,16 +1809,13 @@ during_frag( fd_poh_ctx_t * ctx, return; } - ulong pkt_type; ulong slot; switch( ctx->in_kind[ in_idx ] ) { case IN_KIND_BANK: { - pkt_type = POH_PKT_TYPE_MICROBLOCK; slot = fd_disco_bank_sig_slot( sig ); break; } case IN_KIND_PACK: { - pkt_type = fd_disco_poh_sig_pkt_type( sig ); slot = fd_disco_poh_sig_slot( sig ); break; } @@ -1826,40 +1823,36 @@ during_frag( fd_poh_ctx_t * ctx, FD_LOG_ERR(( "unexpected in_kind %d", ctx->in_kind[ in_idx ] )); } - int is_frag_for_prior_leader_slot = 0; - if( FD_LIKELY( pkt_type==POH_PKT_TYPE_DONE_PACKING || pkt_type==POH_PKT_TYPE_MICROBLOCK ) ) { - /* The following sequence is possible... + /* The following sequence is possible... - 1. We become leader in slot 10 - 2. While leader, we switch to a fork that is on slot 8, where - we are leader - 3. We get the in-flight microblocks for slot 10 + 1. We become leader in slot 10 + 2. While leader, we switch to a fork that is on slot 8, where we + are leader + 3. We get the in-flight microblocks for slot 10 - These in-flight microblocks need to be dropped, so we check - against the high water mark (highwater_leader_slot) rather than - the current hashcnt here when determining what to drop. + These in-flight microblocks need to be dropped, so we check against + the high water mark (highwater_leader_slot) rather than the current + hashcnt here when determining what to drop. - We know if the slot is lower than the high water mark it's from a stale - leader slot, because we will not become leader for the same slot twice - even if we are reset back in time (to prevent duplicate blocks). */ - is_frag_for_prior_leader_slot = slothighwater_leader_slot; - } + We know if the slot is lower than the high water mark it's from a + stale leader slot, because we will not become leader for the same + slot twice even if we are reset back in time (to prevent duplicate + blocks). */ + int is_frag_for_prior_leader_slot = slothighwater_leader_slot; if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_PACK ) ) { /* We now know the real amount of microblocks published, so set an exact bound for once we receive them. */ ctx->skip_frag = 1; - if( pkt_type==POH_PKT_TYPE_DONE_PACKING ) { - if( FD_UNLIKELY( is_frag_for_prior_leader_slot ) ) return; - - FD_TEST( ctx->microblocks_lower_bound<=ctx->max_microblocks_per_slot ); - fd_done_packing_t const * done_packing = fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ); - FD_LOG_INFO(( "done_packing(slot=%lu,seen_microblocks=%lu,microblocks_in_slot=%lu)", - ctx->slot, - ctx->microblocks_lower_bound, - done_packing->microblocks_in_slot )); - ctx->microblocks_lower_bound += ctx->max_microblocks_per_slot - done_packing->microblocks_in_slot; - } + if( FD_UNLIKELY( is_frag_for_prior_leader_slot ) ) return; + + FD_TEST( ctx->microblocks_lower_bound<=ctx->max_microblocks_per_slot ); + fd_done_packing_t const * done_packing = fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ); + FD_LOG_INFO(( "done_packing(slot=%lu,seen_microblocks=%lu,microblocks_in_slot=%lu)", + ctx->slot, + ctx->microblocks_lower_bound, + done_packing->microblocks_in_slot )); + ctx->microblocks_lower_bound += ctx->max_microblocks_per_slot - done_packing->microblocks_in_slot; return; } else { if( FD_UNLIKELY( chunkin[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>USHORT_MAX ) ) diff --git a/src/discoh/bank/fd_bank_tile.c b/src/discoh/bank/fd_bank_tile.c index 451c6939eb..cef49085e1 100644 --- a/src/discoh/bank/fd_bank_tile.c +++ b/src/discoh/bank/fd_bank_tile.c @@ -23,7 +23,7 @@ typedef struct { uchar * txn_sidecar_mem; void const * _bank; - ulong _microblock_idx; + ulong _pack_idx; ulong _txn_idx; int _is_bundle; @@ -46,8 +46,6 @@ typedef struct { fd_pack_rebate_sum_t rebater[ 1 ]; struct { - ulong slot_acquire[ 3 ]; - ulong txn_load_address_lookup_tables[ 6 ]; ulong transaction_result[ 41 ]; ulong processing_failed; @@ -76,8 +74,6 @@ scratch_footprint( fd_topo_tile_t const * tile ) { static inline void metrics_write( fd_bank_ctx_t * ctx ) { - FD_MCNT_ENUM_COPY( BANK, SLOT_ACQUIRE, ctx->metrics.slot_acquire ); - FD_MCNT_ENUM_COPY( BANK, TRANSACTION_LOAD_ADDRESS_TABLES, ctx->metrics.txn_load_address_lookup_tables ); FD_MCNT_ENUM_COPY( BANK, TRANSACTION_RESULT, ctx->metrics.transaction_result ); @@ -130,7 +126,7 @@ during_frag( fd_bank_ctx_t * ctx, fd_memcpy( dst, src, sz-sizeof(fd_microblock_bank_trailer_t) ); fd_microblock_bank_trailer_t * trailer = (fd_microblock_bank_trailer_t *)( src+sz-sizeof(fd_microblock_bank_trailer_t) ); ctx->_bank = trailer->bank; - ctx->_microblock_idx = trailer->microblock_idx; + ctx->_pack_idx = trailer->pack_idx; ctx->_txn_idx = trailer->pack_txn_idx; ctx->_is_bundle = trailer->is_bundle; } @@ -336,7 +332,7 @@ handle_microblock( fd_bank_ctx_t * ctx, PoH should eventually flush the pipeline before ending the slot. */ metrics_write( ctx ); - ulong bank_sig = fd_disco_bank_sig( slot, ctx->_microblock_idx ); + ulong bank_sig = fd_disco_bank_sig( slot, ctx->_pack_idx ); /* We always need to publish, even if there are no successfully executed transactions so the PoH tile can keep an accurate count of microblocks @@ -489,7 +485,7 @@ handle_bundle( fd_bank_ctx_t * ctx, trailer->pack_txn_idx = ctx->_txn_idx + i; trailer->tips = tips[ i ]; - ulong bank_sig = fd_disco_bank_sig( slot, ctx->_microblock_idx+i ); + ulong bank_sig = fd_disco_bank_sig( slot, ctx->_pack_idx+i ); long tickcount = fd_tickcount(); long microblock_start_ticks = fd_frag_meta_ts_decomp( begin_tspub, tickcount ); diff --git a/src/discoh/poh/fd_poh_tile.c b/src/discoh/poh/fd_poh_tile.c index 57f8dea4ce..7623bd14ff 100644 --- a/src/discoh/poh/fd_poh_tile.c +++ b/src/discoh/poh/fd_poh_tile.c @@ -441,7 +441,7 @@ typedef struct { little bit over-strict, we just need to ensure that microblocks with conflicting accounts execute in order, but this is easiest to implement for now. */ - ulong expect_microblock_idx; + uint expect_pack_idx; /* The PoH tile must never drop microblocks that get committed by the bank, so it needs to always be able to mixin a microblock hash. @@ -1095,7 +1095,6 @@ fd_ext_poh_begin_leader( void const * bank, ctx->current_leader_bank = bank; ctx->microblocks_lower_bound = 0UL; ctx->cus_used = 0UL; - ctx->expect_microblock_idx = 0UL; ctx->limits.slot_max_cost = cus_block_limit; ctx->limits.slot_max_vote_cost = cus_vote_cost_limit; @@ -1775,16 +1774,12 @@ before_frag( fd_poh_ctx_t * ctx, ulong sig ) { (void)seq; - if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_BANK ) ) { - ulong microblock_idx = fd_disco_bank_sig_microblock_idx( sig ); - FD_TEST( microblock_idx>=ctx->expect_microblock_idx ); + if( FD_LIKELY( ctx->in_kind[ in_idx ]!=IN_KIND_BANK && ctx->in_kind[ in_idx ]!=IN_KIND_PACK ) ) return 0; - /* Return the fragment to stem so we can process it later, if it's - not next in the sequence. */ - if( FD_UNLIKELY( microblock_idx>ctx->expect_microblock_idx ) ) return -1; - - ctx->expect_microblock_idx++; - } + uint pack_idx = (uint)fd_disco_bank_sig_pack_idx( sig ); + FD_TEST( ((int)(pack_idx-ctx->expect_pack_idx))>=0L ); + if( FD_UNLIKELY( pack_idx!=ctx->expect_pack_idx ) ) return -1; + ctx->expect_pack_idx++; return 0; } @@ -1797,7 +1792,6 @@ during_frag( fd_poh_ctx_t * ctx, ulong chunk, ulong sz, ulong ctl FD_PARAM_UNUSED ) { - ctx->skip_frag = 0; if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_STAKE ) ) { @@ -1810,16 +1804,13 @@ during_frag( fd_poh_ctx_t * ctx, return; } - ulong pkt_type; ulong slot; switch( ctx->in_kind[ in_idx ] ) { case IN_KIND_BANK: { - pkt_type = POH_PKT_TYPE_MICROBLOCK; slot = fd_disco_bank_sig_slot( sig ); break; } case IN_KIND_PACK: { - pkt_type = fd_disco_poh_sig_pkt_type( sig ); slot = fd_disco_poh_sig_slot( sig ); break; } @@ -1827,40 +1818,35 @@ during_frag( fd_poh_ctx_t * ctx, FD_LOG_ERR(( "unexpected in_kind %d", ctx->in_kind[ in_idx ] )); } - int is_frag_for_prior_leader_slot = 0; - if( FD_LIKELY( pkt_type==POH_PKT_TYPE_DONE_PACKING || pkt_type==POH_PKT_TYPE_MICROBLOCK ) ) { - /* The following sequence is possible... + /* The following sequence is possible... - 1. We become leader in slot 10 - 2. While leader, we switch to a fork that is on slot 8, where - we are leader - 3. We get the in-flight microblocks for slot 10 + 1. We become leader in slot 10 + 2. While leader, we switch to a fork that is on slot 8, where + we are leader + 3. We get the in-flight microblocks for slot 10 - These in-flight microblocks need to be dropped, so we check - against the high water mark (highwater_leader_slot) rather than - the current hashcnt here when determining what to drop. + These in-flight microblocks need to be dropped, so we check + against the high water mark (highwater_leader_slot) rather than + the current hashcnt here when determining what to drop. - We know if the slot is lower than the high water mark it's from a stale - leader slot, because we will not become leader for the same slot twice - even if we are reset back in time (to prevent duplicate blocks). */ - is_frag_for_prior_leader_slot = slothighwater_leader_slot; - } + We know if the slot is lower than the high water mark it's from a stale + leader slot, because we will not become leader for the same slot twice + even if we are reset back in time (to prevent duplicate blocks). */ + int is_frag_for_prior_leader_slot = slothighwater_leader_slot; if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_PACK ) ) { /* We now know the real amount of microblocks published, so set an exact bound for once we receive them. */ ctx->skip_frag = 1; - if( pkt_type==POH_PKT_TYPE_DONE_PACKING ) { - if( FD_UNLIKELY( is_frag_for_prior_leader_slot ) ) return; - - FD_TEST( ctx->microblocks_lower_bound<=ctx->max_microblocks_per_slot ); - fd_done_packing_t const * done_packing = fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ); - FD_LOG_INFO(( "done_packing(slot=%lu,seen_microblocks=%lu,microblocks_in_slot=%lu)", - ctx->slot, - ctx->microblocks_lower_bound, - done_packing->microblocks_in_slot )); - ctx->microblocks_lower_bound += ctx->max_microblocks_per_slot - done_packing->microblocks_in_slot; - } + if( FD_UNLIKELY( is_frag_for_prior_leader_slot ) ) return; + + FD_TEST( ctx->microblocks_lower_bound<=ctx->max_microblocks_per_slot ); + fd_done_packing_t const * done_packing = fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ); + FD_LOG_INFO(( "done_packing(slot=%lu,seen_microblocks=%lu,microblocks_in_slot=%lu)", + ctx->slot, + ctx->microblocks_lower_bound, + done_packing->microblocks_in_slot )); + ctx->microblocks_lower_bound += ctx->max_microblocks_per_slot - done_packing->microblocks_in_slot; return; } else { if( FD_UNLIKELY( chunkin[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>USHORT_MAX ) ) @@ -2245,6 +2231,7 @@ unprivileged_init( fd_topo_t * topo, ctx->lagged_consecutive_leader_start = tile->poh.lagged_consecutive_leader_start; ctx->expect_sequential_leader_slot = ULONG_MAX; + ctx->expect_pack_idx = 0U; ctx->microblocks_lower_bound = 0UL; ctx->max_active_descendant = 0UL; @@ -2328,7 +2315,7 @@ unprivileged_init( fd_topo_t * topo, if( !strcmp( link->name, "stake_out" ) ) { ctx->in_kind[ i ] = IN_KIND_STAKE; - } else if( !strcmp( link->name, "pack_bank" ) ) { + } else if( !strcmp( link->name, "pack_poh" ) ) { ctx->in_kind[ i ] = IN_KIND_PACK; } else if( !strcmp( link->name, "bank_poh" ) ) { ctx->in_kind[ i ] = IN_KIND_BANK;