diff --git a/src/app/firedancer/topology.c b/src/app/firedancer/topology.c index 10e40d74ebf..bd8aa790471 100644 --- a/src/app/firedancer/topology.c +++ b/src/app/firedancer/topology.c @@ -447,7 +447,6 @@ fd_topo_initialize( config_t * config ) { fd_topob_wksp( topo, "banks" ); fd_topob_wksp( topo, "banks_locks" ); fd_topob_wksp( topo, "store" )->core_dump_level = FD_TOPO_CORE_DUMP_LEVEL_FULL; - fd_topob_wksp( topo, "executed_txn" ); fd_topob_wksp( topo, "gossip_sign" ); fd_topob_wksp( topo, "sign_gossip" ); @@ -461,8 +460,6 @@ fd_topo_initialize( config_t * config ) { fd_topob_wksp( topo, "txsend_sign" ); fd_topob_wksp( topo, "sign_txsend" ); - fd_topob_wksp( topo, "execrp_sig" ); - fd_topob_wksp( topo, "execrp_replay" ); if( FD_LIKELY( snapshots_enabled ) ) { @@ -586,7 +583,8 @@ fd_topo_initialize( config_t * config ) { /**/ fd_topob_link( topo, "dedup_resolv", "dedup_resolv", 65536UL, FD_TPU_PARSED_MTU, 1UL ); FOR(resolv_tile_cnt) fd_topob_link( topo, "resolv_pack", "resolv_pack", 65536UL, FD_TPU_RESOLVED_MTU, 1UL ); /**/ fd_topob_link( topo, "replay_epoch", "replay_epoch", 128UL, FD_EPOCH_OUT_MTU, 1UL ); /* TODO: This should be 2 but requires fixing STEM_BURST */ - /**/ fd_topob_link( topo, "replay_out", "replay_out", 8192UL, sizeof(fd_replay_message_t), 1UL ); + /**/ fd_topob_link( topo, "replay_out", "replay_out", 65536UL, sizeof(fd_replay_message_t), 1UL ); + fd_topob_link( topo, "replay_execrp", "replay_execrp", 16384UL, sizeof(fd_execrp_task_msg_t), 1UL ); /**/ fd_topob_link( topo, "pack_poh", "pack_poh", 4096UL, sizeof(fd_done_packing_t), 1UL ); /* pack_execle is shared across all execle, so if one executor stalls due to complex transactions, the buffer needs to be large so that @@ -599,7 +597,6 @@ fd_topo_initialize( config_t * config ) { /**/ fd_topob_link( topo, "poh_shred", "poh_shred", 16384UL, USHORT_MAX, 1UL ); /**/ fd_topob_link( topo, "poh_replay", "poh_replay", 4096UL, sizeof(fd_poh_leader_slot_ended_t), 1UL ); FOR(resolv_tile_cnt) fd_topob_link( topo, "resolv_replay", "resolv_replay", 4096UL, sizeof(fd_resolv_slot_exchanged_t), 1UL ); - /**/ fd_topob_link( topo, "executed_txn", "executed_txn", 16384UL, 64UL, 1UL ); /* TODO: Rename this ... */ FOR(shred_tile_cnt) fd_topob_link( topo, "shred_sign", "shred_sign", 128UL, 32UL, 1UL ); FOR(shred_tile_cnt) fd_topob_link( topo, "sign_shred", "sign_shred", 128UL, sizeof(fd_ed25519_sig_t), 1UL ); @@ -618,9 +615,6 @@ fd_topo_initialize( config_t * config ) { /**/ fd_topob_link( topo, "tower_out", "tower_out", 16384UL, sizeof(fd_tower_msg_t), 2UL ); /* conf + slot_done. see explanation in fd_tower_tile.h for link_depth */ /**/ fd_topob_link( topo, "txsend_out", "txsend_out", 128UL, FD_TPU_RAW_MTU, 1UL ); - fd_topob_link( topo, "replay_execrp", "replay_execrp", 16384UL, sizeof(fd_execrp_task_msg_t), 1UL ); - - FOR(execrp_tile_cnt) fd_topob_link( topo, "execrp_sig", "execrp_sig", 16384UL, 64UL, 1UL ); FOR(execrp_tile_cnt) fd_topob_link( topo, "execrp_replay", "execrp_replay", 16384UL, sizeof(fd_execrp_task_done_msg_t), 1UL ); ushort parsed_tile_to_cpu[ FD_TILE_MAX ]; @@ -891,8 +885,9 @@ fd_topo_initialize( config_t * config ) { /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_out", 0UL ); /**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_epoch", 0UL ); - /**/ fd_topob_tile_out( topo, "replay", 0UL, "executed_txn", 0UL ); /**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_execrp", 0UL ); + FOR(execrp_tile_cnt) fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "execrp_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "poh_replay", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "txsend_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); FOR(resolv_tile_cnt) fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "resolv_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); @@ -900,8 +895,8 @@ fd_topo_initialize( config_t * config ) { fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); } - /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "poh_replay", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); FOR(execrp_tile_cnt) fd_topob_tile_in ( topo, "execrp", i, "metric_in", "replay_execrp", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "execrp_replay", i ); /**/ fd_topob_tile_in ( topo, "tower", 0UL, "metric_in", "dedup_resolv", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_in ( topo, "tower", 0UL, "metric_in", "replay_epoch", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); @@ -927,7 +922,7 @@ fd_topo_initialize( config_t * config ) { /**/ fd_topob_tile_in( topo, "verify", 0UL, "metric_in", "txsend_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); FOR(verify_tile_cnt) fd_topob_tile_out( topo, "verify", i, "verify_dedup", i ); FOR(verify_tile_cnt) fd_topob_tile_in( topo, "dedup", 0UL, "metric_in", "verify_dedup", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); - /**/ fd_topob_tile_in( topo, "dedup", 0UL, "metric_in", "executed_txn", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + /**/ fd_topob_tile_in( topo, "dedup", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_out( topo, "dedup", 0UL, "dedup_resolv", 0UL ); FOR(resolv_tile_cnt) fd_topob_tile_in( topo, "resolv", i, "metric_in", "dedup_resolv", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); FOR(resolv_tile_cnt) fd_topob_tile_in( topo, "resolv", i, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); @@ -935,7 +930,6 @@ fd_topo_initialize( config_t * config ) { FOR(resolv_tile_cnt) fd_topob_tile_out( topo, "resolv", i, "resolv_replay", i ); /**/ fd_topob_tile_in( topo, "pack", 0UL, "metric_in", "resolv_pack", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /**/ fd_topob_tile_in( topo, "pack", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); - /**/ fd_topob_tile_in( topo, "pack", 0UL, "metric_in", "executed_txn", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); fd_topob_tile_out( topo, "pack", 0UL, "pack_execle", 0UL ); fd_topob_tile_out( topo, "pack", 0UL, "pack_poh" , 0UL ); if( FD_LIKELY( config->tiles.pack.use_consumed_cus ) ) { @@ -959,12 +953,6 @@ fd_topo_initialize( config_t * config ) { FOR(shred_tile_cnt) fd_topob_tile_in ( topo, "shred", i, "metric_in", "poh_shred", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); FOR(shred_tile_cnt) fd_topob_tile_out( topo, "shred", i, "shred_net", i ); - FOR(execrp_tile_cnt) fd_topob_tile_in ( topo, "dedup", 0UL, "metric_in", "execrp_sig", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); - FOR(execrp_tile_cnt) fd_topob_tile_in ( topo, "pack", 0UL, "metric_in", "execrp_sig", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); - FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "execrp_sig", i ); - FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "execrp_replay", i ); - FOR(execrp_tile_cnt) fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "execrp_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); - if( FD_LIKELY( telemetry_enabled ) ) { fd_topob_wksp( topo, "event" ); fd_topob_wksp( topo, "event_sign" ); diff --git a/src/disco/dedup/fd_dedup_tile.c b/src/disco/dedup/fd_dedup_tile.c index 3ba70a803c3..a51b6865139 100644 --- a/src/disco/dedup/fd_dedup_tile.c +++ b/src/disco/dedup/fd_dedup_tile.c @@ -5,6 +5,8 @@ #include "../topo/fd_topo.h" #include "../metrics/fd_metrics.h" +#include "../../discof/replay/fd_replay_tile.h" + /* fd_dedup provides services to deduplicate multiple streams of input fragments and present them to a mix of reliable and unreliable consumers as though they were generated by a single multi-stream @@ -13,6 +15,7 @@ #define IN_KIND_GOSSIP (0UL) #define IN_KIND_VERIFY (1UL) #define IN_KIND_EXECUTED_TXN (2UL) +#define IN_KIND_REPLAY (3UL) /* fd_dedup_in_ctx_t is a context object for each in (producer) mcache connected to the dedup tile. */ @@ -95,7 +98,7 @@ static inline void during_frag( fd_dedup_ctx_t * ctx, ulong in_idx, ulong seq FD_PARAM_UNUSED, - ulong sig FD_PARAM_UNUSED, + ulong sig, ulong chunk, ulong sz, ulong ctl FD_PARAM_UNUSED ) { @@ -114,7 +117,16 @@ during_frag( fd_dedup_ctx_t * ctx, if( FD_UNLIKELY( txnm->payload_sz>FD_TPU_MTU ) ) { FD_LOG_ERR(( "vote txn payload size %hu exceeds max %lu", txnm->payload_sz, FD_TPU_MTU )); } - } else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_EXECUTED_TXN ) ) { + } else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_REPLAY ) ) { + if( FD_LIKELY( sig==REPLAY_SIG_TXN_EXECUTED ) ) { + fd_replay_txn_executed_t * txn_executed = fd_type_pun( src ); + if( FD_UNLIKELY( !txn_executed->is_committable ) ) return; + ulong ha_dedup_tag = fd_hash( ctx->hashmap_seed, fd_txn_get_signatures( TXN(txn_executed->txn), txn_executed->txn->payload ), FD_TXN_SIGNATURE_SZ ); + int _is_dup; + FD_TCACHE_INSERT( _is_dup, *ctx->tcache_sync, ctx->tcache_ring, ctx->tcache_depth, ctx->tcache_map, ctx->tcache_map_cnt, ha_dedup_tag ); + (void)_is_dup; + } + } else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_EXECUTED_TXN ) ) { /* Frankendancer-only */ if( FD_UNLIKELY( sz!=FD_TXN_SIGNATURE_SZ ) ) FD_LOG_ERR(( "received an executed transaction signature message with the wrong size %lu", sz )); /* Executed txns just have their signature inserted into the tcache so we can dedup them easily. */ @@ -149,6 +161,7 @@ after_frag( fd_dedup_ctx_t * ctx, (void)_tspub; if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_EXECUTED_TXN ) ) return; + if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_REPLAY ) ) return; fd_txn_m_t * txnm = (fd_txn_m_t *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk ); FD_TEST( txnm->payload_sz<=FD_TPU_MTU ); @@ -262,8 +275,8 @@ unprivileged_init( fd_topo_t * topo, ctx->in_kind[ i ] = IN_KIND_VERIFY; } else if( !strcmp( link->name, "executed_txn" ) ) { ctx->in_kind[ i ] = IN_KIND_EXECUTED_TXN; - } else if( !strcmp( link->name, "execrp_sig" ) ) { - ctx->in_kind[ i ] = IN_KIND_EXECUTED_TXN; + } else if( !strcmp( link->name, "replay_out" ) ) { + ctx->in_kind[ i ] = IN_KIND_REPLAY; } else { FD_LOG_ERR(( "unexpected link name %s", link->name )); } diff --git a/src/disco/pack/fd_pack_tile.c b/src/disco/pack/fd_pack_tile.c index 16a4c8d5534..4d63b3959f2 100644 --- a/src/disco/pack/fd_pack_tile.c +++ b/src/disco/pack/fd_pack_tile.c @@ -135,6 +135,7 @@ typedef struct { int is_bundle; /* is the current transaction a bundle */ uchar executed_txn_sig[ 64UL ]; + uchar txn_committed; /* One of the FD_PACK_STRATEGY_* values defined above */ int strategy; @@ -855,6 +856,13 @@ during_frag( fd_pack_ctx_t * ctx, switch( ctx->in_kind[ in_idx ] ) { case IN_KIND_REPLAY: { + if( FD_LIKELY( sig==REPLAY_SIG_TXN_EXECUTED ) ) { + fd_replay_txn_executed_t const * txn_executed = fd_type_pun_const( dcache_entry ); + ctx->txn_committed = !!txn_executed->is_committable; + if( FD_UNLIKELY( !txn_executed->is_committable ) ) return; + memcpy( ctx->executed_txn_sig, fd_txn_get_signatures( TXN(txn_executed->txn), txn_executed->txn->payload ), FD_TXN_SIGNATURE_SZ ); + return; + } if( FD_LIKELY( sig!=REPLAY_SIG_BECAME_LEADER ) ) return; /* There was a leader transition. Handle it. */ @@ -1014,6 +1022,10 @@ after_frag( fd_pack_ctx_t * ctx, ulong leader_slot = ULONG_MAX; switch( ctx->in_kind[ in_idx ] ) { case IN_KIND_REPLAY: + if( FD_LIKELY( sig==REPLAY_SIG_TXN_EXECUTED && ctx->txn_committed ) ) { + ulong deleted = fd_pack_delete_transaction( ctx->pack, fd_type_pun( ctx->executed_txn_sig ) ); + FD_MCNT_INC( PACK, TRANSACTION_ALREADY_EXECUTED, deleted ); + } if( FD_UNLIKELY( sig!=REPLAY_SIG_BECAME_LEADER ) ) return; leader_slot = ctx->_became_leader->slot; @@ -1234,7 +1246,6 @@ unprivileged_init( fd_topo_t * topo, else if( FD_LIKELY( !strcmp( link->name, "sign_pack" ) ) ) ctx->in_kind[ i ] = IN_KIND_SIGN; else if( FD_LIKELY( !strcmp( link->name, "replay_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY; else if( FD_LIKELY( !strcmp( link->name, "executed_txn" ) ) ) ctx->in_kind[ i ] = IN_KIND_EXECUTED_TXN; - else if( FD_LIKELY( !strcmp( link->name, "execrp_sig" ) ) ) ctx->in_kind[ i ] = IN_KIND_EXECUTED_TXN; else FD_LOG_ERR(( "pack tile has unexpected input link %lu %s", i, link->name )); } diff --git a/src/discof/execrp/fd_execrp_tile.c b/src/discof/execrp/fd_execrp_tile.c index cf0e52b1e65..24cd6bf058c 100644 --- a/src/discof/execrp/fd_execrp_tile.c +++ b/src/discof/execrp/fd_execrp_tile.c @@ -36,7 +36,6 @@ struct fd_execrp_tile { /* link-related data structures. */ link_ctx_t replay_in[ 1 ]; link_ctx_t execrp_replay_out[ 1 ]; /* TODO: Remove with solcap v2 */ - link_ctx_t execrp_sig_out[ 1 ]; fd_sha512_t sha_mem[ FD_TXN_ACTUAL_SIG_MAX ]; fd_sha512_t * sha_lj[ FD_TXN_ACTUAL_SIG_MAX ]; @@ -147,11 +146,13 @@ publish_txn_finalized_msg( fd_execrp_tile_t * ctx, fd_execrp_task_done_msg_t * msg = fd_chunk_to_laddr( ctx->execrp_replay_out->mem, ctx->execrp_replay_out->chunk ); msg->bank_idx = ctx->bank->data->idx; msg->txn_exec->txn_idx = ctx->txn_idx; - msg->txn_exec->err = !ctx->txn_out.err.is_committable; + msg->txn_exec->is_committable = ctx->txn_out.err.is_committable; + msg->txn_exec->is_fees_only = ctx->txn_out.err.is_fees_only; + msg->txn_exec->txn_err = ctx->txn_out.err.txn_err; msg->txn_exec->slot = ctx->slot; msg->txn_exec->start_shred_idx = ctx->txn_in.txn->start_shred_idx; msg->txn_exec->end_shred_idx = ctx->txn_in.txn->end_shred_idx; - if( FD_UNLIKELY( msg->txn_exec->err ) ) { + if( FD_UNLIKELY( !msg->txn_exec->is_committable ) ) { uchar * signature = (uchar *)ctx->txn_in.txn->payload + TXN( ctx->txn_in.txn )->signature_off; FD_BASE58_ENCODE_64_BYTES( signature, signature_b58 ); FD_LOG_WARNING(( "block marked dead (slot=%lu) because of invalid transaction (signature=%s) (txn_err=%d)", ctx->slot, signature_b58, ctx->txn_out.err.txn_err )); @@ -210,16 +211,6 @@ returnable_frag( fd_execrp_tile_t * ctx, ctx->accdb->base.ro_active, ctx->accdb->base.rw_active )); } - if( FD_LIKELY( ctx->execrp_sig_out->idx!=ULONG_MAX ) ) { - /* Copy the txn signature to the signature out link so the - dedup/pack tiles can drop already executed transactions. */ - memcpy( fd_chunk_to_laddr( ctx->execrp_sig_out->mem, ctx->execrp_sig_out->chunk ), - (uchar *)ctx->txn_in.txn->payload + TXN( ctx->txn_in.txn )->signature_off, - 64UL ); - fd_stem_publish( stem, ctx->execrp_sig_out->idx, 0UL, ctx->execrp_sig_out->chunk, 64UL, 0UL, 0UL, 0UL ); - ctx->execrp_sig_out->chunk = fd_dcache_compact_next( ctx->execrp_sig_out->chunk, 64UL, ctx->execrp_sig_out->chunk0, ctx->execrp_sig_out->wmark ); - } - /* Notify replay. */ ctx->txn_idx = msg->txn_idx; ctx->dispatch_time_comp = tspub; @@ -325,15 +316,6 @@ unprivileged_init( fd_topo_t * topo, ctx->execrp_replay_out->chunk = ctx->execrp_replay_out->chunk0; } - ctx->execrp_sig_out->idx = fd_topo_find_tile_out_link( topo, tile, "execrp_sig", ctx->tile_idx ); - if( FD_LIKELY( ctx->execrp_sig_out->idx!=ULONG_MAX ) ) { - fd_topo_link_t * execrp_sig_link = &topo->links[ tile->out_link_id[ ctx->execrp_sig_out->idx ] ]; - ctx->execrp_sig_out->mem = topo->workspaces[ topo->objs[ execrp_sig_link->dcache_obj_id ].wksp_id ].wksp; - ctx->execrp_sig_out->chunk0 = fd_dcache_compact_chunk0( ctx->execrp_sig_out->mem, execrp_sig_link->dcache ); - ctx->execrp_sig_out->wmark = fd_dcache_compact_wmark( ctx->execrp_sig_out->mem, execrp_sig_link->dcache, execrp_sig_link->mtu ); - ctx->execrp_sig_out->chunk = ctx->execrp_sig_out->chunk0; - } - /********************************************************************/ /* banks */ /********************************************************************/ diff --git a/src/discof/replay/fd_execrp.h b/src/discof/replay/fd_execrp.h index 7b6d567e2b4..d3d5657b038 100644 --- a/src/discof/replay/fd_execrp.h +++ b/src/discof/replay/fd_execrp.h @@ -60,7 +60,28 @@ typedef union fd_execrp_task_msg fd_execrp_task_msg_t; struct fd_execrp_txn_exec_done_msg { ulong txn_idx; - int err; + + /* These flags form a nested series of if statements. + if( is_committable ) { + if( is_fees_only ) { + instructions will not be executed + txn_err will be non-zero and will be one of the account loader errors + } else { + instructions will execute + if( txn_err is non-zero ) { + there's likely an instruction error + } else { + transaction executed successfully + https://github.com/anza-xyz/agave/blob/v3.1.8/svm/src/transaction_execution_result.rs#L26 + } + } + } else { + either failed before account loading, or failed cost tracker + } + */ + int is_committable; + int is_fees_only; + int txn_err; /* used by monitoring tools */ ulong slot; diff --git a/src/discof/replay/fd_replay_tile.c b/src/discof/replay/fd_replay_tile.c index bd17403b27c..82639dc1920 100644 --- a/src/discof/replay/fd_replay_tile.c +++ b/src/discof/replay/fd_replay_tile.c @@ -163,10 +163,7 @@ struct fd_replay_tile { ulong reasm_seed; fd_reasm_t * reasm; - /* Replay state machine. */ - fd_sched_t * sched; - ulong exec_cnt; - fd_replay_out_link_t exec_out[ 1 ]; /* Sending work down to exec tiles */ + fd_sched_t * sched; ulong vote_tracker_seed; fd_vote_tracker_t * vote_tracker; @@ -380,6 +377,8 @@ struct fd_replay_tile { int in_kind[ 128 ]; fd_replay_in_link_t in[ 128 ]; + fd_replay_out_link_t exec_out[ 1 ]; + fd_replay_out_link_t replay_out[1]; fd_replay_out_link_t epoch_out[1]; @@ -795,6 +794,25 @@ publish_slot_dead( fd_replay_tile_t * ctx, ctx->replay_out->chunk = fd_dcache_compact_next( ctx->replay_out->chunk, sizeof(fd_replay_slot_dead_t), ctx->replay_out->chunk0, ctx->replay_out->wmark ); } +static void +publish_txn_executed( fd_replay_tile_t * ctx, + fd_stem_context_t * stem, + ulong txn_idx ) { + fd_sched_txn_info_t * txn_info = fd_sched_get_txn_info( ctx->sched, txn_idx ); + fd_replay_txn_executed_t * txn_executed = fd_type_pun( fd_chunk_to_laddr( ctx->replay_out->mem, ctx->replay_out->chunk ) ); + *txn_executed->txn = *fd_sched_get_txn( ctx->sched, txn_idx ); + txn_executed->txn_err = txn_info->txn_err; + txn_executed->is_committable = !!(txn_info->flags&FD_SCHED_TXN_IS_COMMITTABLE); + txn_executed->is_fees_only = !!(txn_info->flags&FD_SCHED_TXN_IS_FEES_ONLY); + txn_executed->tick_parsed = txn_info->tick_parsed; + txn_executed->tick_sigverify_disp = txn_info->tick_sigverify_disp; + txn_executed->tick_sigverify_done = txn_info->tick_sigverify_done; + txn_executed->tick_exec_disp = txn_info->tick_exec_disp; + txn_executed->tick_exec_done = txn_info->tick_exec_done; + fd_stem_publish( stem, ctx->replay_out->idx, REPLAY_SIG_TXN_EXECUTED, ctx->replay_out->chunk, sizeof(*txn_executed), 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) ); + ctx->replay_out->chunk = fd_dcache_compact_next( ctx->replay_out->chunk, sizeof(*txn_executed), ctx->replay_out->chunk0, ctx->replay_out->wmark ); +} + static void replay_block_finalize( fd_replay_tile_t * ctx, fd_stem_context_t * stem, @@ -2034,7 +2052,7 @@ process_exec_task_done( fd_replay_tile_t * ctx, *fd_bank_has_identity_vote_modify( bank ) += 1; } } - if( FD_UNLIKELY( msg->txn_exec->err && !(bank->data->flags&FD_BANK_FLAGS_DEAD) ) ) { + if( FD_UNLIKELY( !msg->txn_exec->is_committable && !(bank->data->flags&FD_BANK_FLAGS_DEAD) ) ) { /* Every transaction in a valid block has to execute. Otherwise, we should mark the block as dead. */ fd_banks_mark_bank_dead( ctx->banks, bank ); @@ -2051,9 +2069,26 @@ process_exec_task_done( fd_replay_tile_t * ctx, } int res = fd_sched_task_done( ctx->sched, FD_SCHED_TT_TXN_EXEC, msg->txn_exec->txn_idx, exec_tile_idx, NULL ); FD_TEST( res==0 ); + fd_sched_txn_info_t * txn_info = fd_sched_get_txn_info( ctx->sched, msg->txn_exec->txn_idx ); + txn_info->flags |= FD_SCHED_TXN_EXEC_DONE; + if( FD_LIKELY( !(txn_info->flags&FD_SCHED_TXN_SIGVERIFY_DONE)||!txn_info->txn_err ) ) { /* Set execution status if sigverify hasn't happened yet or if sigverify was a success. */ + txn_info->txn_err = msg->txn_exec->txn_err; + txn_info->flags |= fd_ulong_if( msg->txn_exec->is_committable, FD_SCHED_TXN_IS_COMMITTABLE, 0UL ); + txn_info->flags |= fd_ulong_if( msg->txn_exec->is_fees_only, FD_SCHED_TXN_IS_FEES_ONLY, 0UL ); + } + if( FD_UNLIKELY( (txn_info->flags&FD_SCHED_TXN_REPLAY_DONE)==FD_SCHED_TXN_REPLAY_DONE ) ) { /* UNLIKELY because generally exec happens before sigverify. */ + publish_txn_executed( ctx, stem, msg->txn_exec->txn_idx ); + } break; } case FD_EXECRP_TT_TXN_SIGVERIFY: { + fd_sched_txn_info_t * txn_info = fd_sched_get_txn_info( ctx->sched, msg->txn_sigverify->txn_idx ); + txn_info->flags |= FD_SCHED_TXN_SIGVERIFY_DONE; + if( FD_UNLIKELY( msg->txn_sigverify->err ) ) { + txn_info->txn_err = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE; + txn_info->flags &= ~FD_SCHED_TXN_IS_COMMITTABLE; + txn_info->flags &= ~FD_SCHED_TXN_IS_FEES_ONLY; + } if( FD_UNLIKELY( msg->txn_sigverify->err && !(bank->data->flags&FD_BANK_FLAGS_DEAD) ) ) { /* Every transaction in a valid block has to sigverify. Otherwise, we should mark the block as dead. Also freeze the @@ -2072,6 +2107,9 @@ process_exec_task_done( fd_replay_tile_t * ctx, } int res = fd_sched_task_done( ctx->sched, FD_SCHED_TT_TXN_SIGVERIFY, msg->txn_sigverify->txn_idx, exec_tile_idx, NULL ); FD_TEST( res==0 ); + if( FD_LIKELY( (txn_info->flags&FD_SCHED_TXN_REPLAY_DONE)==FD_SCHED_TXN_REPLAY_DONE ) ) { + publish_txn_executed( ctx, stem, msg->txn_exec->txn_idx ); + } break; } case FD_EXECRP_TT_POH_HASH: { @@ -2617,8 +2655,6 @@ unprivileged_init( fd_topo_t * topo, } # endif - ctx->exec_cnt = fd_topo_tile_name_cnt( topo, "execrp" ); - ctx->is_booted = 0; ctx->larger_max_cost_per_block = tile->replay.larger_max_cost_per_block; @@ -2626,7 +2662,7 @@ unprivileged_init( fd_topo_t * topo, ctx->reasm = fd_reasm_join( fd_reasm_new( reasm_mem, tile->replay.fec_max, ctx->reasm_seed ) ); FD_TEST( ctx->reasm ); - ctx->sched = fd_sched_join( fd_sched_new( sched_mem, tile->replay.sched_depth, tile->replay.max_live_slots, ctx->exec_cnt ) ); + ctx->sched = fd_sched_join( fd_sched_new( sched_mem, tile->replay.sched_depth, tile->replay.max_live_slots, fd_topo_tile_name_cnt( topo, "execrp" ) ) ); FD_TEST( ctx->sched ); FD_TEST( fd_vinyl_req_pool_new( vinyl_req_pool_mem, 1UL, 1UL ) ); @@ -2686,26 +2722,16 @@ unprivileged_init( fd_topo_t * topo, } *ctx->epoch_out = out1( topo, tile, "replay_epoch" ); FD_TEST( ctx->epoch_out->idx!=ULONG_MAX ); - *ctx->replay_out = out1( topo, tile, "replay_out" ); FD_TEST( ctx->replay_out->idx!=ULONG_MAX ); - - ulong idx = fd_topo_find_tile_out_link( topo, tile, "replay_execrp", 0UL ); - FD_TEST( idx!=ULONG_MAX ); - fd_topo_link_t * link = &topo->links[ tile->out_link_id[ idx ] ]; - - fd_replay_out_link_t * exec_out = ctx->exec_out; - exec_out->idx = idx; - exec_out->mem = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp; - exec_out->chunk0 = fd_dcache_compact_chunk0( exec_out->mem, link->dcache ); - exec_out->wmark = fd_dcache_compact_wmark( exec_out->mem, link->dcache, link->mtu ); - exec_out->chunk = exec_out->chunk0; + *ctx->replay_out = out1( topo, tile, "replay_out" ); FD_TEST( ctx->replay_out->idx!=ULONG_MAX ); + *ctx->exec_out = out1( topo, tile, "replay_execrp" ); FD_TEST( ctx->exec_out->idx!=ULONG_MAX ); ctx->gui_enabled = fd_topo_find_tile( topo, "gui", 0UL )!=ULONG_MAX; ctx->rpc_enabled = fd_topo_find_tile( topo, "rpc", 0UL )!=ULONG_MAX; if( FD_UNLIKELY( strcmp( "", tile->replay.solcap_capture ) ) ) { - idx = fd_topo_find_tile_out_link( topo, tile, "cap_repl", 0UL ); + ulong idx = fd_topo_find_tile_out_link( topo, tile, "cap_repl", 0UL ); FD_TEST( idx!=ULONG_MAX ); - link = &topo->links[ tile->out_link_id[ idx ] ]; + fd_topo_link_t * link = &topo->links[ tile->out_link_id[ idx ] ]; fd_capture_link_buf_t * cap_repl_out = ctx->cap_repl_out; diff --git a/src/discof/replay/fd_replay_tile.h b/src/discof/replay/fd_replay_tile.h index 1e6218d6904..4446736e69e 100644 --- a/src/discof/replay/fd_replay_tile.h +++ b/src/discof/replay/fd_replay_tile.h @@ -11,6 +11,7 @@ #define REPLAY_SIG_RESET (3) #define REPLAY_SIG_BECAME_LEADER (4) #define REPLAY_SIG_OC_ADVANCED (5) +#define REPLAY_SIG_TXN_EXECUTED (6) struct fd_replay_slot_completed { ulong slot; @@ -86,11 +87,25 @@ struct fd_replay_root_advanced { }; typedef struct fd_replay_root_advanced fd_replay_root_advanced_t; +struct fd_replay_txn_executed { + fd_txn_p_t txn[ 1 ]; + int is_committable; + int is_fees_only; + int txn_err; + long tick_parsed; + long tick_sigverify_disp; + long tick_sigverify_done; + long tick_exec_disp; + long tick_exec_done; +}; +typedef struct fd_replay_txn_executed fd_replay_txn_executed_t; + union fd_replay_message { fd_replay_slot_completed_t slot_completed; fd_replay_root_advanced_t root_advanced; fd_poh_reset_t reset; fd_became_leader_t became_leader; + fd_replay_txn_executed_t txn_executed; }; typedef union fd_replay_message fd_replay_message_t; diff --git a/src/discof/replay/fd_sched.c b/src/discof/replay/fd_sched.c index 083fe476969..25da1a37cd6 100644 --- a/src/discof/replay/fd_sched.c +++ b/src/discof/replay/fd_sched.c @@ -108,9 +108,6 @@ struct fd_sched_block { ulong txn_pool_max_popcnt; /* Peak transaction pool occupancy during the time this block was replaying. */ ulong block_pool_max_popcnt; /* Peak block pool occupancy. */ ulong txn_idx[ FD_MAX_TXN_PER_SLOT ]; /* Indexed by parse order. */ - long txn_disp_ticks[ FD_MAX_TXN_PER_SLOT ]; /* Indexed by parse order. */ - long txn_done_ticks[ FD_MAX_TXN_PER_SLOT ]; /* Indexed by parse order. */ - fd_ed25519_sig_t txn_sigs[ FD_MAX_TXN_PER_SLOT ]; /* Indexed by parse order. */ /* PoH verify. */ fd_hash_t start_hash[ 1 ]; @@ -213,37 +210,37 @@ typedef struct fd_sched_metrics fd_sched_metrics_t; #include "../../util/tmpl/fd_deque_dynamic.c" struct fd_sched { - char print_buf[ FD_SCHED_MAX_PRINT_BUF_SZ ]; - ulong print_buf_sz; - fd_sched_metrics_t metrics[ 1 ]; - ulong canary; /* == FD_SCHED_MAGIC */ - ulong depth; /* Immutable. */ - ulong block_cnt_max; /* Immutable. */ - ulong exec_cnt; /* Immutable. */ - long txn_in_flight_last_tick; - ulong root_idx; - fd_rdisp_t * rdisp; - ulong txn_exec_ready_bitset[ 1 ]; - ulong sigverify_ready_bitset[ 1 ]; - ulong poh_ready_bitset[ 1 ]; - ulong active_bank_idx; /* Index of the actively replayed block, or ULONG_MAX if no block is - actively replayed; has to have a transaction to dispatch; staged - blocks that have no transactions to dispatch are not eligible for - being active. */ - ulong last_active_bank_idx; - ulong staged_bitset; /* Bit i set if staging lane i is occupied. */ - ulong staged_head_bank_idx[ FD_SCHED_MAX_STAGING_LANES ]; /* Head of the linear chain in each staging lane, ignored if bit i is - not set in the bitset. */ - ulong txn_pool_free_cnt; - fd_txn_p_t * txn_pool; - uint * txn_idx_to_parse_idx; - ulong tile_to_bank_idx[ FD_SCHED_MAX_EXEC_TILE_CNT ]; /* Index of the bank that the exec tile is executing against. */ - txn_bitset_t exec_done_set[ txn_bitset_word_cnt ]; /* Indexed by txn_idx. */ - txn_bitset_t sigverify_done_set[ txn_bitset_word_cnt ]; /* Indexed by txn_idx. */ - txn_bitset_t poh_mixin_done_set[ txn_bitset_word_cnt ]; /* Indexed by txn_idx. */ - fd_sched_block_t * block_pool; /* Just a flat array. */ - ulong block_pool_popcnt; - ulong * ref_q; + char print_buf[ FD_SCHED_MAX_PRINT_BUF_SZ ]; + ulong print_buf_sz; + fd_sched_metrics_t metrics[ 1 ]; + ulong canary; /* == FD_SCHED_MAGIC */ + ulong depth; /* Immutable. */ + ulong block_cnt_max; /* Immutable. */ + ulong exec_cnt; /* Immutable. */ + long txn_in_flight_last_tick; + ulong root_idx; + fd_rdisp_t * rdisp; + ulong txn_exec_ready_bitset[ 1 ]; + ulong sigverify_ready_bitset[ 1 ]; + ulong poh_ready_bitset[ 1 ]; + ulong active_bank_idx; /* Index of the actively replayed block, or ULONG_MAX if no block is + actively replayed; has to have a transaction to dispatch; staged + blocks that have no transactions to dispatch are not eligible for + being active. */ + ulong last_active_bank_idx; + ulong staged_bitset; /* Bit i set if staging lane i is occupied. */ + ulong staged_head_bank_idx[ FD_SCHED_MAX_STAGING_LANES ]; /* Head of the linear chain in each staging lane, ignored if bit i is + not set in the bitset. */ + ulong txn_pool_free_cnt; + fd_txn_p_t * txn_pool; + fd_sched_txn_info_t * txn_info_pool; + ulong tile_to_bank_idx[ FD_SCHED_MAX_EXEC_TILE_CNT ]; /* Index of the bank that the exec tile is executing against. */ + txn_bitset_t exec_done_set[ txn_bitset_word_cnt ]; /* Indexed by txn_idx. */ + txn_bitset_t sigverify_done_set[ txn_bitset_word_cnt ]; /* Indexed by txn_idx. */ + txn_bitset_t poh_mixin_done_set[ txn_bitset_word_cnt ]; /* Indexed by txn_idx. */ + fd_sched_block_t * block_pool; /* Just a flat array. */ + ulong block_pool_popcnt; + ulong * ref_q; }; typedef struct fd_sched fd_sched_t; @@ -425,20 +422,6 @@ fd_sched_printf( fd_sched_t * sched, sched->print_buf_sz += len; } -FD_FN_UNUSED static void -log_block_txns( fd_sched_t * sched, fd_sched_block_t * block ) { - for( ulong i=0UL; itxn_parsed_cnt; i++ ) { - sched->print_buf_sz = 0UL; - FD_BASE58_ENCODE_64_BYTES( block->txn_sigs[ i ], sig_str ); - long disp_tick = block->txn_disp_ticks[ i ]; - long done_tick = block->txn_done_ticks[ i ]; - if( FD_LIKELY( disp_tick!=LONG_MAX && done_tick!=LONG_MAX ) ) fd_sched_printf( sched, "['%s',%ld,%ld],", sig_str, disp_tick, done_tick ); - else if( FD_LIKELY( disp_tick!=LONG_MAX ) ) fd_sched_printf( sched, "['%s',%ld,None],", sig_str, disp_tick ); - else fd_sched_printf( sched, "['%s',None,None],", sig_str ); - FD_LOG_DEBUG(( "%s", sched->print_buf )); - } -} - FD_FN_UNUSED static void print_histogram( fd_sched_t * sched, fd_histf_t * hist, ulong converter, char * title ) { fd_sched_printf( sched, " +---------------------+----------------------+--------------+\n" ); @@ -556,12 +539,12 @@ fd_sched_footprint( ulong depth, if( FD_UNLIKELY( depthFD_SCHED_MAX_DEPTH ) ) return 0UL; /* bad depth */ if( FD_UNLIKELY( !block_cnt_max ) ) return 0UL; /* bad block_cnt_max */ ulong l = FD_LAYOUT_INIT; - l = FD_LAYOUT_APPEND( l, fd_sched_align(), sizeof(fd_sched_t) ); - l = FD_LAYOUT_APPEND( l, fd_rdisp_align(), fd_rdisp_footprint( depth, block_cnt_max ) ); /* dispatcher */ - l = FD_LAYOUT_APPEND( l, alignof(fd_sched_block_t), block_cnt_max*sizeof(fd_sched_block_t) ); /* block pool */ - l = FD_LAYOUT_APPEND( l, ref_q_align(), ref_q_footprint( block_cnt_max ) ); - l = FD_LAYOUT_APPEND( l, alignof(fd_txn_p_t), depth*sizeof(fd_txn_p_t) ); /* txn_pool */ - l = FD_LAYOUT_APPEND( l, sizeof(uint), depth*sizeof(uint) ); /* txn_idx_to_parse_idx */ + l = FD_LAYOUT_APPEND( l, fd_sched_align(), sizeof(fd_sched_t) ); + l = FD_LAYOUT_APPEND( l, fd_rdisp_align(), fd_rdisp_footprint( depth, block_cnt_max ) ); /* dispatcher */ + l = FD_LAYOUT_APPEND( l, alignof(fd_sched_block_t), block_cnt_max*sizeof(fd_sched_block_t) ); /* block pool */ + l = FD_LAYOUT_APPEND( l, ref_q_align(), ref_q_footprint( block_cnt_max ) ); + l = FD_LAYOUT_APPEND( l, alignof(fd_txn_p_t), depth*sizeof(fd_txn_p_t) ); /* txn_pool */ + l = FD_LAYOUT_APPEND( l, alignof(fd_sched_txn_info_t), depth*sizeof(fd_sched_txn_info_t) ); /* txn_info_pool */ return FD_LAYOUT_FINI( l, fd_sched_align() ); } @@ -581,7 +564,7 @@ fd_sched_new( void * mem, return NULL; } - if( FD_UNLIKELY( depth<32UL || depth>FD_SCHED_MAX_DEPTH ) ) { + if( FD_UNLIKELY( depthFD_SCHED_MAX_DEPTH ) ) { FD_LOG_WARNING(( "bad depth (%lu)", depth )); return NULL; } @@ -597,16 +580,16 @@ fd_sched_new( void * mem, } FD_SCRATCH_ALLOC_INIT( l, mem ); - fd_sched_t * sched = FD_SCRATCH_ALLOC_APPEND( l, fd_sched_align(), sizeof(fd_sched_t) ); - void * _rdisp = FD_SCRATCH_ALLOC_APPEND( l, fd_rdisp_align(), fd_rdisp_footprint( depth, block_cnt_max ) ); - void * _bpool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_sched_block_t), block_cnt_max*sizeof(fd_sched_block_t) ); - void * _ref_q = FD_SCRATCH_ALLOC_APPEND( l, ref_q_align(), ref_q_footprint( block_cnt_max ) ); - fd_txn_p_t * txn_pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txn_p_t), depth*sizeof(fd_txn_p_t) ); - uint * txn_idx_to_parse_idx = FD_SCRATCH_ALLOC_APPEND( l, sizeof(uint), depth*sizeof(uint) ); + fd_sched_t * sched = FD_SCRATCH_ALLOC_APPEND( l, fd_sched_align(), sizeof(fd_sched_t) ); + void * _rdisp = FD_SCRATCH_ALLOC_APPEND( l, fd_rdisp_align(), fd_rdisp_footprint( depth, block_cnt_max ) ); + void * _bpool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_sched_block_t), block_cnt_max*sizeof(fd_sched_block_t) ); + void * _ref_q = FD_SCRATCH_ALLOC_APPEND( l, ref_q_align(), ref_q_footprint( block_cnt_max ) ); + fd_txn_p_t * _txn_pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txn_p_t), depth*sizeof(fd_txn_p_t) ); + fd_sched_txn_info_t * _txn_info_pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_sched_txn_info_t), depth*sizeof(fd_sched_txn_info_t) ); FD_SCRATCH_ALLOC_FINI( l, fd_sched_align() ); - sched->txn_pool = txn_pool; - sched->txn_idx_to_parse_idx = txn_idx_to_parse_idx; + sched->txn_pool = _txn_pool; + sched->txn_info_pool = _txn_info_pool; ulong seed = ((ulong)fd_tickcount()) ^ FD_SCHED_MAGIC; fd_rdisp_new( _rdisp, depth, block_cnt_max, seed ); @@ -1064,6 +1047,7 @@ fd_sched_task_next_ready( fd_sched_t * sched, fd_sched_task_t * out ) { ulong sigverify_queued_cnt = block->txn_parsed_cnt-block->txn_sigverify_in_flight_cnt-block->txn_sigverify_done_cnt; if( FD_LIKELY( sigverify_queued_cnt>0UL && fd_ulong_popcnt( sigverify_ready_bitset )>fd_int_if( block->txn_exec_in_flight_cnt>0U, 0, 1 ) ) ) { dispatch_sigverify( sched, block, bank_idx, fd_ulong_find_lsb( sigverify_ready_bitset ), out ); + sched->txn_info_pool[ out->txn_sigverify->txn_idx ].tick_sigverify_disp = fd_tickcount(); return 1UL; } return 0UL; @@ -1082,7 +1066,7 @@ fd_sched_task_next_ready( fd_sched_t * sched, fd_sched_task_t * out ) { sched->metrics->txn_weighted_in_flight_cnt += delta*txn_exec_busy_cnt; sched->txn_in_flight_last_tick = now; - block->txn_disp_ticks[ sched->txn_idx_to_parse_idx[ out->txn_exec->txn_idx ] ] = now; + sched->txn_info_pool[ out->txn_exec->txn_idx ].tick_exec_disp = now; sched->txn_exec_ready_bitset[ 0 ] = fd_ulong_clear_bit( exec_ready_bitset0, (int)exec_tile_idx0); sched->tile_to_bank_idx[ exec_tile_idx0 ] = bank_idx; @@ -1148,6 +1132,7 @@ fd_sched_task_next_ready( fd_sched_t * sched, fd_sched_task_t * out ) { ulong sigverify_queued_cnt = block->txn_parsed_cnt-block->txn_sigverify_in_flight_cnt-block->txn_sigverify_done_cnt; if( FD_LIKELY( sigverify_queued_cnt>0UL && fd_ulong_popcnt( sigverify_ready_bitset )>fd_int_if( block->fec_eos||block->txn_exec_in_flight_cnt>0U||sched->exec_cnt==1UL, 0, 1 ) ) ) { dispatch_sigverify( sched, block, bank_idx, fd_ulong_find_lsb( sigverify_ready_bitset ), out ); + sched->txn_info_pool[ out->txn_sigverify->txn_idx ].tick_sigverify_disp = fd_tickcount(); return 1UL; } @@ -1268,7 +1253,7 @@ fd_sched_task_done( fd_sched_t * sched, ulong task_type, ulong txn_idx, ulong ex sched->metrics->txn_weighted_in_flight_cnt += delta*txn_exec_busy_cnt; sched->txn_in_flight_last_tick = now; - block->txn_done_ticks[ sched->txn_idx_to_parse_idx[ txn_idx ] ] = now; + sched->txn_info_pool[ txn_idx ].tick_exec_done = now; block->txn_exec_done_cnt++; block->txn_exec_in_flight_cnt--; @@ -1276,6 +1261,7 @@ fd_sched_task_done( fd_sched_t * sched, ulong task_type, ulong txn_idx, ulong ex sched->txn_exec_ready_bitset[ 0 ] = fd_ulong_set_bit( sched->txn_exec_ready_bitset[ 0 ], exec_tile_idx ); sched->metrics->txn_exec_done_cnt++; txn_bitset_insert( sched->exec_done_set, txn_idx ); + sched->txn_info_pool[ txn_idx ].flags |= FD_SCHED_TXN_EXEC_DONE; if( txn_bitset_test( sched->sigverify_done_set, txn_idx ) && txn_bitset_test( sched->poh_mixin_done_set, txn_idx ) ) { /* Release the txn_idx if all tasks on it are done. This is guaranteed to only happen once per transaction because @@ -1290,12 +1276,14 @@ fd_sched_task_done( fd_sched_t * sched, ulong task_type, ulong txn_idx, ulong ex break; } case FD_SCHED_TT_TXN_SIGVERIFY: { + sched->txn_info_pool[ txn_idx ].tick_sigverify_done = fd_tickcount(); block->txn_sigverify_done_cnt++; block->txn_sigverify_in_flight_cnt--; FD_TEST( !fd_ulong_extract_bit( sched->sigverify_ready_bitset[ 0 ], exec_tile_idx ) ); sched->sigverify_ready_bitset[ 0 ] = fd_ulong_set_bit( sched->sigverify_ready_bitset[ 0 ], exec_tile_idx ); sched->metrics->txn_sigverify_done_cnt++; txn_bitset_insert( sched->sigverify_done_set, txn_idx ); + sched->txn_info_pool[ txn_idx ].flags |= FD_SCHED_TXN_SIGVERIFY_DONE; if( txn_bitset_test( sched->exec_done_set, txn_idx ) && txn_bitset_test( sched->poh_mixin_done_set, txn_idx ) ) { /* Release the txn_idx if all tasks on it are done. This is guaranteed to only happen once per transaction because @@ -1402,7 +1390,6 @@ fd_sched_block_abandon( fd_sched_t * sched, ulong bank_idx ) { sched->print_buf_sz = 0UL; print_all( sched, block ); FD_LOG_DEBUG(( "%s", sched->print_buf )); - log_block_txns( sched, block ); subtree_abandon( sched, block ); try_activate_block( sched ); @@ -1576,12 +1563,21 @@ fd_sched_set_poh_params( fd_sched_t * sched, ulong bank_idx, ulong tick_height, fd_txn_p_t * fd_sched_get_txn( fd_sched_t * sched, ulong txn_idx ) { FD_TEST( sched->canary==FD_SCHED_MAGIC ); - if( FD_UNLIKELY( txn_idx>=FD_SCHED_MAX_DEPTH ) ) { + if( FD_UNLIKELY( txn_idx>=sched->depth ) ) { return NULL; } return sched->txn_pool+txn_idx; } +fd_sched_txn_info_t * +fd_sched_get_txn_info( fd_sched_t * sched, ulong txn_idx ) { + FD_TEST( sched->canary==FD_SCHED_MAGIC ); + if( FD_UNLIKELY( txn_idx>=sched->depth ) ) { + return NULL; + } + return sched->txn_info_pool+txn_idx; +} + fd_hash_t * fd_sched_get_poh( fd_sched_t * sched, ulong bank_idx ) { FD_TEST( sched->canary==FD_SCHED_MAGIC ); @@ -1924,11 +1920,14 @@ fd_sched_parse_txn( fd_sched_t * sched, fd_sched_block_t * block, fd_sched_alut_ txn_bitset_remove( sched->exec_done_set, txn_idx ); txn_bitset_remove( sched->sigverify_done_set, txn_idx ); txn_bitset_remove( sched->poh_mixin_done_set, txn_idx ); - sched->txn_idx_to_parse_idx[ txn_idx ] = block->txn_parsed_cnt; - memcpy( block->txn_sigs[ block->txn_parsed_cnt ], fd_txn_get_signatures( TXN(txn_p), txn_p->payload ), FD_TXN_SIGNATURE_SZ ); + sched->txn_info_pool[ txn_idx ].flags = 0UL; + sched->txn_info_pool[ txn_idx ].txn_err = 0; + sched->txn_info_pool[ txn_idx ].tick_parsed = fd_tickcount(); + sched->txn_info_pool[ txn_idx ].tick_sigverify_disp = LONG_MAX; + sched->txn_info_pool[ txn_idx ].tick_sigverify_done = LONG_MAX; + sched->txn_info_pool[ txn_idx ].tick_exec_disp = LONG_MAX; + sched->txn_info_pool[ txn_idx ].tick_exec_done = LONG_MAX; block->txn_idx[ block->txn_parsed_cnt ] = txn_idx; - block->txn_disp_ticks[ block->txn_parsed_cnt ] = LONG_MAX; - block->txn_done_ticks[ block->txn_parsed_cnt ] = LONG_MAX; block->fec_buf_soff += (uint)pay_sz; block->txn_parsed_cnt++; #if FD_SCHED_SKIP_SIGVERIFY diff --git a/src/discof/replay/fd_sched.h b/src/discof/replay/fd_sched.h index 75d6d09d09a..315a76a4f8c 100644 --- a/src/discof/replay/fd_sched.h +++ b/src/discof/replay/fd_sched.h @@ -84,6 +84,24 @@ struct fd_sched_fec { }; typedef struct fd_sched_fec fd_sched_fec_t; +/* The state of a transaction. Non mutually exclusive. */ +#define FD_SCHED_TXN_EXEC_DONE (0x0001UL) +#define FD_SCHED_TXN_SIGVERIFY_DONE (0x0002UL) +#define FD_SCHED_TXN_IS_COMMITTABLE (0x0004UL) +#define FD_SCHED_TXN_IS_FEES_ONLY (0x0008UL) +#define FD_SCHED_TXN_REPLAY_DONE (FD_SCHED_TXN_EXEC_DONE|FD_SCHED_TXN_SIGVERIFY_DONE) + +struct fd_sched_txn_info { + ulong flags; + int txn_err; + long tick_parsed; + long tick_sigverify_disp; + long tick_sigverify_done; + long tick_exec_disp; + long tick_exec_done; +}; +typedef struct fd_sched_txn_info fd_sched_txn_info_t; + /* The scheduler may return one of the following types of tasks for the replay tile. @@ -266,7 +284,12 @@ fd_sched_task_next_ready( fd_sched_t * sched, fd_sched_task_t * out ); pruned the moment in-flight task count hits 0 due to the last task completing. Then, in the immediate ensuing stem run loop, sched_pruned_next() will return the index for the corresponding bank - so the refcnt can be decremented for sched. */ + so the refcnt can be decremented for sched. + + The transaction at the given index may be freed upon return from this + function. Nonetheless, as long as there is no intervening FEC + ingestion, it would still be safe to query the transaction using + get_txn(). */ int fd_sched_task_done( fd_sched_t * sched, ulong task_type, ulong txn_idx, ulong exec_idx, void * data ); @@ -326,6 +349,9 @@ fd_sched_set_poh_params( fd_sched_t * sched, ulong bank_idx, ulong tick_height, fd_txn_p_t * fd_sched_get_txn( fd_sched_t * sched, ulong txn_idx ); +fd_sched_txn_info_t * +fd_sched_get_txn_info( fd_sched_t * sched, ulong txn_idx ); + fd_hash_t * fd_sched_get_poh( fd_sched_t * sched, ulong bank_idx );