Skip to content

Commit b09a797

Browse files
committed
add TestBlockEvaluator for methods to hang off of
1 parent a14f3a4 commit b09a797

File tree

2 files changed

+39
-38
lines changed

2 files changed

+39
-38
lines changed

data/pools/transactionPool.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ type TransactionPool struct {
9898
// exceed the txPoolMaxSize. This flag is reset to false OnNewBlock
9999
stateproofOverflowed bool
100100

101-
testEvalCtx *testEvalContext
102-
testEvalCtxMu deadlock.RWMutex
101+
testBlockEvaluator *eval.TestBlockEvaluator
102+
testBlockEvaluatorMu deadlock.RWMutex
103103

104104
// shutdown is set to true when the pool is being shut down. It is checked in exported methods
105105
// to prevent pool operations like remember and recomputing the block evaluator
@@ -109,7 +109,6 @@ type TransactionPool struct {
109109

110110
// BlockEvaluator defines the block evaluator interface exposed by the ledger package.
111111
type BlockEvaluator interface {
112-
TestTransactionGroup(txgroup []transactions.SignedTxn) error
113112
Round() basics.Round
114113
PaySetSize() int
115114
TransactionGroup(txads []transactions.SignedTxnWithAD) error
@@ -118,30 +117,33 @@ type BlockEvaluator interface {
118117
ResetTxnBytes()
119118
}
120119

121-
// testEvalContext implements the eval.TestEvalContext interface.
120+
// testEvalContext implements the eval.TestEvalContext interface. It allows for concurrent
121+
// calls to TestTransactionGroup for candidate transactions calling transactionPool.Test.
122122
type testEvalContext struct {
123123
ledger *ledger.Ledger
124124
block bookkeeping.Block
125125
proto config.ConsensusParams
126126
specials transactions.SpecialAddresses
127127
}
128128

129-
func newTestEvalCtx(ledger *ledger.Ledger, block bookkeeping.Block) *testEvalContext {
130-
return &testEvalContext{
131-
ledger: ledger,
132-
block: block,
133-
proto: config.Consensus[block.CurrentProtocol],
134-
specials: transactions.SpecialAddresses{
135-
FeeSink: block.FeeSink,
136-
RewardsPool: block.RewardsPool,
137-
},
138-
}
129+
func newTestBlockEvaluator(ledger *ledger.Ledger, block bookkeeping.Block) *eval.TestBlockEvaluator {
130+
return &eval.TestBlockEvaluator{
131+
TestEvalContext: &testEvalContext{
132+
ledger: ledger,
133+
block: block,
134+
proto: config.Consensus[block.CurrentProtocol],
135+
specials: transactions.SpecialAddresses{
136+
FeeSink: block.FeeSink,
137+
RewardsPool: block.RewardsPool,
138+
},
139+
}}
139140
}
140141

141142
func (c *testEvalContext) Proto() config.ConsensusParams { return c.proto }
142143
func (c *testEvalContext) Specials() transactions.SpecialAddresses { return c.specials }
143144
func (c *testEvalContext) TxnContext() transactions.TxnContext { return c.block }
144145
func (c *testEvalContext) CheckDup(firstValid, lastValid basics.Round, txid transactions.Txid, txl ledgercore.Txlease) error {
146+
// will call txTail.checkDup, which uses an RLock for concurrent access.
145147
return c.ledger.CheckDup(c.proto, c.block.BlockHeader.Round, firstValid, lastValid, txid, txl)
146148
}
147149

@@ -421,19 +423,20 @@ func (pool *TransactionPool) checkSufficientFee(txgroup []transactions.SignedTxn
421423

422424
// Test performs basic duplicate detection and well-formedness checks
423425
// on a transaction group without storing the group.
426+
// It may be called concurrently.
424427
func (pool *TransactionPool) Test(txgroup []transactions.SignedTxn) error {
425428
if err := pool.checkPendingQueueSize(txgroup); err != nil {
426429
return err
427430
}
428431

429-
pool.testEvalCtxMu.RLock()
430-
defer pool.testEvalCtxMu.RUnlock()
432+
pool.testBlockEvaluatorMu.RLock()
433+
defer pool.testBlockEvaluatorMu.RUnlock()
431434

432-
if pool.testEvalCtx == nil {
435+
if pool.testBlockEvaluator == nil {
433436
return fmt.Errorf("Test: testEvalCtx is nil")
434437
}
435438

436-
return eval.TestTransactionGroup(pool.testEvalCtx, txgroup)
439+
return pool.testBlockEvaluator.TestTransactionGroup(txgroup)
437440
}
438441

439442
type poolIngestParams struct {
@@ -780,9 +783,9 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIDs map[transact
780783
return
781784
}
782785

783-
pool.testEvalCtxMu.Lock()
784-
pool.testEvalCtx = newTestEvalCtx(pool.ledger, next)
785-
pool.testEvalCtxMu.Unlock()
786+
pool.testBlockEvaluatorMu.Lock()
787+
pool.testBlockEvaluator = newTestBlockEvaluator(pool.ledger, next)
788+
pool.testBlockEvaluatorMu.Unlock()
786789

787790
var asmStats telemetryspec.AssembleBlockMetrics
788791
asmStats.StartCount = len(txgroups)

ledger/eval/eval.go

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -904,9 +904,8 @@ func (eval *BlockEvaluator) ResetTxnBytes() {
904904
eval.blockTxBytes = 0
905905
}
906906

907-
// TestEvalContext defines the evaluation context required to check for
908-
// well-formedness and duplicate detection used by TestTransactionGroup
909-
// and TestTransaction.
907+
// TestEvalContext defines the evaluation context required by TestBlockEvaluator
908+
// to check for well-formedness and duplicate detection.
910909
type TestEvalContext interface {
911910
Proto() config.ConsensusParams
912911
Specials() transactions.SpecialAddresses
@@ -928,16 +927,19 @@ func (eval *BlockEvaluator) CheckDup(firstValid, lastValid basics.Round, txid tr
928927
return eval.state.checkDup(firstValid, lastValid, txid, txl)
929928
}
930929

931-
// TestTransactionGroup performs basic duplicate detection and well-formedness checks
932-
// on a transaction group, but does not actually add the transactions to the block
933-
// evaluator, or modify the block evaluator state in any other visible way.
930+
// TestTransactionGroup is only called by tests.
934931
func (eval *BlockEvaluator) TestTransactionGroup(txgroup []transactions.SignedTxn) error {
935-
return TestTransactionGroup(eval, txgroup)
932+
return TestBlockEvaluator{eval}.TestTransactionGroup(txgroup)
936933
}
937934

935+
// TestBlockEvaluator uses a TestEvalContext to perform basic transaction checks.
936+
type TestBlockEvaluator struct{ TestEvalContext }
937+
938938
// TestTransactionGroup performs basic duplicate detection and well-formedness checks
939-
// on a transaction group.
940-
func TestTransactionGroup(eval TestEvalContext, txgroup []transactions.SignedTxn) error {
939+
// on a transaction group, but does not actually add the transactions to the block
940+
// evaluator, or modify the block evaluator state in any other visible way.
941+
// It uses a TestEvalContext to access needed recent ledger state.
942+
func (eval TestBlockEvaluator) TestTransactionGroup(txgroup []transactions.SignedTxn) error {
941943
// Nothing to do if there are no transactions.
942944
if len(txgroup) == 0 {
943945
return nil
@@ -952,7 +954,7 @@ func TestTransactionGroup(eval TestEvalContext, txgroup []transactions.SignedTxn
952954

953955
var group transactions.TxGroup
954956
for gi, txn := range txgroup {
955-
err := TestTransaction(eval, txn)
957+
err := eval.TestTransaction(txn)
956958
if err != nil {
957959
return err
958960
}
@@ -996,13 +998,7 @@ func TestTransactionGroup(eval TestEvalContext, txgroup []transactions.SignedTxn
996998
// TestTransaction performs basic duplicate detection and well-formedness checks
997999
// on a single transaction, but does not actually add the transaction to the block
9981000
// evaluator, or modify the block evaluator state in any other visible way.
999-
func (eval *BlockEvaluator) TestTransaction(txn transactions.SignedTxn) error {
1000-
return TestTransaction(eval, txn)
1001-
}
1002-
1003-
// TestTransaction performs basic duplicate detection and well-formedness checks
1004-
// on a single transaction.
1005-
func TestTransaction(eval TestEvalContext, txn transactions.SignedTxn) error {
1001+
func (eval TestBlockEvaluator) TestTransaction(txn transactions.SignedTxn) error {
10061002
// Transaction valid (not expired)?
10071003
err := txn.Txn.Alive(eval.TxnContext())
10081004
if err != nil {
@@ -1017,6 +1013,7 @@ func TestTransaction(eval TestEvalContext, txn transactions.SignedTxn) error {
10171013

10181014
// Transaction already in the ledger?
10191015
txid := txn.ID()
1016+
// BlockEvaluator.transaction will check again using cow.checkDup later, if the pool tries to add this transaction to the block.
10201017
err = eval.CheckDup(txn.Txn.First(), txn.Txn.Last(), txid, ledgercore.Txlease{Sender: txn.Txn.Sender, Lease: txn.Txn.Lease})
10211018
if err != nil {
10221019
return err
@@ -1199,6 +1196,7 @@ func (eval *BlockEvaluator) transaction(txn transactions.SignedTxn, evalParams *
11991196
}
12001197

12011198
// Transaction already in the ledger?
1199+
// this checks against the txns added to this evaluator; testTransaction currently only checks against committed txns.
12021200
err = cow.checkDup(txn.Txn.First(), txn.Txn.Last(), txid, ledgercore.Txlease{Sender: txn.Txn.Sender, Lease: txn.Txn.Lease})
12031201
if err != nil {
12041202
return err

0 commit comments

Comments
 (0)