diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go index 336162a8cd..a4ba09bcba 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -98,8 +98,8 @@ type TransactionPool struct { // exceed the txPoolMaxSize. This flag is reset to false OnNewBlock stateproofOverflowed bool - testBlockEvaluator *eval.TestBlockEvaluator - testBlockEvaluatorMu deadlock.RWMutex + txnGroupTester *eval.TransactionGroupTester + txnGroupTesterMu deadlock.RWMutex // shutdown is set to true when the pool is being shut down. It is checked in exported methods // to prevent pool operations like remember and recomputing the block evaluator @@ -117,36 +117,6 @@ type BlockEvaluator interface { ResetTxnBytes() } -// testEvalContext implements the eval.TestEvalContext interface. It allows for concurrent -// calls to TestTransactionGroup for candidate transactions calling transactionPool.Test. -type testEvalContext struct { - ledger *ledger.Ledger - block bookkeeping.Block - proto config.ConsensusParams - specials transactions.SpecialAddresses -} - -func newTestBlockEvaluator(ledger *ledger.Ledger, block bookkeeping.Block) *eval.TestBlockEvaluator { - return &eval.TestBlockEvaluator{ - TestEvalContext: &testEvalContext{ - ledger: ledger, - block: block, - proto: config.Consensus[block.CurrentProtocol], - specials: transactions.SpecialAddresses{ - FeeSink: block.FeeSink, - RewardsPool: block.RewardsPool, - }, - }} -} - -func (c *testEvalContext) Proto() config.ConsensusParams { return c.proto } -func (c *testEvalContext) Specials() transactions.SpecialAddresses { return c.specials } -func (c *testEvalContext) TxnContext() transactions.TxnContext { return c.block } -func (c *testEvalContext) CheckDup(firstValid, lastValid basics.Round, txid transactions.Txid, txl ledgercore.Txlease) error { - // will call txTail.checkDup, which uses an RLock for concurrent access. - return c.ledger.CheckDup(c.proto, c.block.BlockHeader.Round, firstValid, lastValid, txid, txl) -} - // VotingAccountSupplier provides a list of possible participating account addresses valid for a given round. type VotingAccountSupplier interface { VotingAccountsForRound(basics.Round) []basics.Address @@ -429,14 +399,14 @@ func (pool *TransactionPool) Test(txgroup []transactions.SignedTxn) error { return err } - pool.testBlockEvaluatorMu.RLock() - defer pool.testBlockEvaluatorMu.RUnlock() + pool.txnGroupTesterMu.RLock() + defer pool.txnGroupTesterMu.RUnlock() - if pool.testBlockEvaluator == nil { + if pool.txnGroupTester == nil { return fmt.Errorf("Test: testEvalCtx is nil") } - return pool.testBlockEvaluator.TestTransactionGroup(txgroup) + return pool.txnGroupTester.TestTransactionGroup(txgroup) } type poolIngestParams struct { @@ -783,9 +753,19 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIDs map[transact return } - pool.testBlockEvaluatorMu.Lock() - pool.testBlockEvaluator = newTestBlockEvaluator(pool.ledger, next) - pool.testBlockEvaluatorMu.Unlock() + pool.txnGroupTesterMu.Lock() + pool.txnGroupTester = &eval.TransactionGroupTester{ + CheckDup: func(firstValid, lastValid basics.Round, txid transactions.Txid, txl ledgercore.Txlease) error { + return pool.ledger.CheckDup(config.Consensus[next.CurrentProtocol], next.BlockHeader.Round, firstValid, lastValid, txid, txl) + }, + TxnContext: next, + Proto: config.Consensus[next.CurrentProtocol], + Specials: transactions.SpecialAddresses{ + FeeSink: next.FeeSink, + RewardsPool: next.RewardsPool, + }, + } + pool.txnGroupTesterMu.Unlock() var asmStats telemetryspec.AssembleBlockMetrics asmStats.StartCount = len(txgroups) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 7376f563c1..5355b49a7a 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -904,50 +904,37 @@ func (eval *BlockEvaluator) ResetTxnBytes() { eval.blockTxBytes = 0 } -// TestEvalContext defines the evaluation context required by TestBlockEvaluator -// to check for well-formedness and duplicate detection. -type TestEvalContext interface { - Proto() config.ConsensusParams - Specials() transactions.SpecialAddresses - TxnContext() transactions.TxnContext - CheckDup(firstValid, lastValid basics.Round, txid transactions.Txid, txl ledgercore.Txlease) error -} - -// Proto implements the TestEvalContext interface. -func (eval *BlockEvaluator) Proto() config.ConsensusParams { return eval.proto } - -// Specials implements the TestEvalContext interface. -func (eval *BlockEvaluator) Specials() transactions.SpecialAddresses { return eval.specials } - -// TxnContext implements the TestEvalContext interface. -func (eval *BlockEvaluator) TxnContext() transactions.TxnContext { return eval.block } - -// CheckDup implements the TestEvalContext interface. -func (eval *BlockEvaluator) CheckDup(firstValid, lastValid basics.Round, txid transactions.Txid, txl ledgercore.Txlease) error { - return eval.state.checkDup(firstValid, lastValid, txid, txl) -} - // TestTransactionGroup is only called by tests. func (eval *BlockEvaluator) TestTransactionGroup(txgroup []transactions.SignedTxn) error { - return TestBlockEvaluator{eval}.TestTransactionGroup(txgroup) + return TransactionGroupTester{ + Proto: eval.proto, + Specials: eval.specials, + TxnContext: eval.block, + CheckDup: eval.state.checkDup, + }.TestTransactionGroup(txgroup) } -// TestBlockEvaluator uses a TestEvalContext to perform basic transaction checks. -type TestBlockEvaluator struct{ TestEvalContext } +// TransactionGroupTester performs basic transaction checks for well-formedness and duplicate detection. +type TransactionGroupTester struct { + CheckDup func(firstValid, lastValid basics.Round, txid transactions.Txid, txl ledgercore.Txlease) error + TxnContext transactions.TxnContext + Proto config.ConsensusParams + Specials transactions.SpecialAddresses +} // TestTransactionGroup performs basic duplicate detection and well-formedness checks // on a transaction group, but does not actually add the transactions to the block // evaluator, or modify the block evaluator state in any other visible way. // It uses a TestEvalContext to access needed recent ledger state. -func (eval TestBlockEvaluator) TestTransactionGroup(txgroup []transactions.SignedTxn) error { +func (eval TransactionGroupTester) TestTransactionGroup(txgroup []transactions.SignedTxn) error { // Nothing to do if there are no transactions. if len(txgroup) == 0 { return nil } - if len(txgroup) > eval.Proto().MaxTxGroupSize { + if len(txgroup) > eval.Proto.MaxTxGroupSize { return &ledgercore.TxGroupMalformedError{ - Msg: fmt.Sprintf("group size %d exceeds maximum %d", len(txgroup), eval.Proto().MaxTxGroupSize), + Msg: fmt.Sprintf("group size %d exceeds maximum %d", len(txgroup), eval.Proto.MaxTxGroupSize), Reason: ledgercore.TxGroupMalformedErrorReasonExceedMaxSize, } } @@ -998,14 +985,14 @@ func (eval TestBlockEvaluator) TestTransactionGroup(txgroup []transactions.Signe // TestTransaction performs basic duplicate detection and well-formedness checks // on a single transaction, but does not actually add the transaction to the block // evaluator, or modify the block evaluator state in any other visible way. -func (eval TestBlockEvaluator) TestTransaction(txn transactions.SignedTxn) error { +func (eval TransactionGroupTester) TestTransaction(txn transactions.SignedTxn) error { // Transaction valid (not expired)? - err := txn.Txn.Alive(eval.TxnContext()) + err := txn.Txn.Alive(eval.TxnContext) if err != nil { return err } - err = txn.Txn.WellFormed(eval.Specials(), eval.Proto()) + err = txn.Txn.WellFormed(eval.Specials, eval.Proto) if err != nil { txnErr := ledgercore.TxnNotWellFormedError(fmt.Sprintf("transaction %v: malformed: %v", txn.ID(), err)) return &txnErr