Skip to content

Commit

Permalink
kill pools.testEvalContext struct and eval.TestEvalContext interface
Browse files Browse the repository at this point in the history
  • Loading branch information
cce committed Nov 8, 2024
1 parent 38edb5c commit 59293c2
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 71 deletions.
58 changes: 19 additions & 39 deletions data/pools/transactionPool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
51 changes: 19 additions & 32 deletions ledger/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 59293c2

Please sign in to comment.