Skip to content

Commit b664bed

Browse files
authored
Merge pull request #14673 from holiman/txfix
core: add testcase for txpool
2 parents c98d9b4 + eebde1a commit b664bed

File tree

3 files changed

+200
-42
lines changed

3 files changed

+200
-42
lines changed

core/tx_pool.go

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,41 @@ import (
3535
)
3636

3737
var (
38-
// Transaction Pool Errors
39-
ErrInvalidSender = errors.New("invalid sender")
40-
ErrNonce = errors.New("nonce too low")
41-
ErrUnderpriced = errors.New("transaction underpriced")
38+
// ErrInvalidSender is returned if the transaction contains an invalid signature.
39+
ErrInvalidSender = errors.New("invalid sender")
40+
41+
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
42+
// one present in the local chain.
43+
ErrNonceTooLow = errors.New("nonce too low")
44+
45+
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
46+
// configured for the transaction pool.
47+
ErrUnderpriced = errors.New("transaction underpriced")
48+
49+
// ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced
50+
// with a different one without the required price bump.
4251
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
43-
ErrBalance = errors.New("insufficient balance")
44-
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
45-
ErrIntrinsicGas = errors.New("intrinsic gas too low")
46-
ErrGasLimit = errors.New("exceeds block gas limit")
47-
ErrNegativeValue = errors.New("negative value")
52+
53+
// ErrInsufficientFunds is returned if the total cost of executing a transaction
54+
// is higher than the balance of the user's account.
55+
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
56+
57+
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
58+
// than required to start the invocation.
59+
ErrIntrinsicGas = errors.New("intrinsic gas too low")
60+
61+
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
62+
// maximum allowance of the current block.
63+
ErrGasLimit = errors.New("exceeds block gas limit")
64+
65+
// ErrNegativeValue is a sanity error to ensure noone is able to specify a
66+
// transaction with a negative value.
67+
ErrNegativeValue = errors.New("negative value")
68+
69+
// ErrOversizedData is returned if the input data of a transaction is greater
70+
// than some meaningful limit a user might use. This is not a consensus error
71+
// making the transaction invalid, rather a DOS protection.
72+
ErrOversizedData = errors.New("oversized data")
4873
)
4974

5075
var (
@@ -54,16 +79,16 @@ var (
5479

5580
var (
5681
// Metrics for the pending pool
57-
pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard")
58-
pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace")
59-
pendingRLCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting
60-
pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds
82+
pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard")
83+
pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace")
84+
pendingRateLimitCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting
85+
pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds
6186

6287
// Metrics for the queued pool
63-
queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard")
64-
queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace")
65-
queuedRLCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting
66-
queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds
88+
queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard")
89+
queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace")
90+
queuedRateLimitCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting
91+
queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds
6792

6893
// General tx metrics
6994
invalidTxCounter = metrics.NewCounter("txpool/invalid")
@@ -374,7 +399,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
374399
}
375400
// Last but not least check for nonce errors
376401
if currentState.GetNonce(from) > tx.Nonce() {
377-
return ErrNonce
402+
return ErrNonceTooLow
378403
}
379404

380405
// Check the transaction doesn't exceed the current
@@ -395,12 +420,15 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
395420
if currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
396421
return ErrInsufficientFunds
397422
}
398-
399423
intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
400424
if tx.Gas().Cmp(intrGas) < 0 {
401425
return ErrIntrinsicGas
402426
}
403427

428+
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
429+
if tx.Size() > 32*1024 {
430+
return ErrOversizedData
431+
}
404432
return nil
405433
}
406434

@@ -638,8 +666,9 @@ func (pool *TxPool) removeTx(hash common.Hash) {
638666
}
639667
// Update the account nonce if needed
640668
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
641-
pool.pendingState.SetNonce(addr, tx.Nonce())
669+
pool.pendingState.SetNonce(addr, nonce)
642670
}
671+
return
643672
}
644673
}
645674
// Transaction is in the future queue
@@ -696,10 +725,10 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
696725
// Drop all transactions over the allowed limit
697726
for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
698727
hash := tx.Hash()
699-
log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
700728
delete(pool.all, hash)
701729
pool.priced.Removed()
702-
queuedRLCounter.Inc(1)
730+
queuedRateLimitCounter.Inc(1)
731+
log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
703732
}
704733
queued += uint64(list.Len())
705734

@@ -745,7 +774,18 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
745774
for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
746775
for i := 0; i < len(offenders)-1; i++ {
747776
list := pool.pending[offenders[i]]
748-
list.Cap(list.Len() - 1)
777+
for _, tx := range list.Cap(list.Len() - 1) {
778+
// Drop the transaction from the global pools too
779+
hash := tx.Hash()
780+
delete(pool.all, hash)
781+
pool.priced.Removed()
782+
783+
// Update the account nonce to the dropped transaction
784+
if nonce := tx.Nonce(); pool.pendingState.GetNonce(offenders[i]) > nonce {
785+
pool.pendingState.SetNonce(offenders[i], nonce)
786+
}
787+
log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
788+
}
749789
pending--
750790
}
751791
}
@@ -756,12 +796,23 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
756796
for pending > pool.config.GlobalSlots && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > pool.config.AccountSlots {
757797
for _, addr := range offenders {
758798
list := pool.pending[addr]
759-
list.Cap(list.Len() - 1)
799+
for _, tx := range list.Cap(list.Len() - 1) {
800+
// Drop the transaction from the global pools too
801+
hash := tx.Hash()
802+
delete(pool.all, hash)
803+
pool.priced.Removed()
804+
805+
// Update the account nonce to the dropped transaction
806+
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
807+
pool.pendingState.SetNonce(addr, nonce)
808+
}
809+
log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
810+
}
760811
pending--
761812
}
762813
}
763814
}
764-
pendingRLCounter.Inc(int64(pendingBeforeCap - pending))
815+
pendingRateLimitCounter.Inc(int64(pendingBeforeCap - pending))
765816
}
766817
// If we've queued more transactions than the hard limit, drop oldest ones
767818
if queued > pool.config.GlobalQueue {
@@ -785,15 +836,15 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
785836
pool.removeTx(tx.Hash())
786837
}
787838
drop -= size
788-
queuedRLCounter.Inc(int64(size))
839+
queuedRateLimitCounter.Inc(int64(size))
789840
continue
790841
}
791842
// Otherwise drop only last few transactions
792843
txs := list.Flatten()
793844
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
794845
pool.removeTx(txs[i].Hash())
795846
drop--
796-
queuedRLCounter.Inc(1)
847+
queuedRateLimitCounter.Inc(1)
797848
}
798849
}
799850
}

0 commit comments

Comments
 (0)