core/txpool: fix global MinGasPrice override and single test failure, fix #1881 (#1880)

Since commit 845d3d49e (July 2023), MinGasPrice validation (250000000 wei /
0.25 Gwei) has been enforced for all non-special transactions in validateTx().
Later, commit 141cb75c (Dec 2025) refactored this validation logic into the
standalone ValidateTransactionWithState() function for code reusability.

However, the transaction pool test suite was never updated to comply with the
MinGasPrice requirement and continued using extremely low gas prices (1-6 wei),
causing test failures when run independently.

The root cause of this issue was that testQueueTimeLimiting() set the global
variable 'common.MinGasPrice = big.NewInt(0)' without restoring it. This global
variable modification created severe testing problems:

1. Intermittent, non-deterministic failures: The same test would randomly pass
   or fail without any code changes, depending on whether testQueueTimeLimiting
   had executed and set MinGasPrice=0 before other tests checked it

2. Race conditions in concurrent execution: Since tests use t.Parallel(), multiple
   tests could simultaneously access the shared global MinGasPrice variable,
   creating unpredictable timing-dependent behavior

3. Test order dependency: When running 'make test', if testQueueTimeLimiting
   executed early and set MinGasPrice=0, other tests with low gas prices would
   pass. Running the same tests individually or in a different order would fail
   with 'under min gas price' errors

4. Global state pollution: The MinGasPrice modification affected ALL concurrently
   running tests in the suite, violating test isolation principles and making
   debugging extremely difficult

5. Masked real validation issues: The global override hid the fact that test
   transactions violated MinGasPrice requirements that would be enforced in
   production, reducing test effectiveness

6. No cleanup mechanism: The changed value was never restored, permanently
   affecting subsequent tests and creating cascading failures

This intermittent behavior made CI/CD pipelines unreliable - tests could pass
locally but fail in CI, or pass on retry without changes, wasting developer time
investigating 'phantom' failures.

This commit systematically updates all affected test cases to use gas prices
that satisfy the MinGasPrice requirement:

- Replace transaction() helper calls with pricedTransaction() using gas prices
  >= 250000000 wei (MinGasPrice)
- Update dynamicFeeTx() calls to use gasFeeCap and gasTipCap >= 250000000 wei
- Scale account balances proportionally to cover the higher transaction costs
- Maintain test logic and relative price relationships between transactions

Tests fixed (36 total):
- TestStateChangeDuringReset
- TestInvalidTransactions
- TestChainFork
- TestDoubleNonce
- TestMissingNonce
- TestNonceRecovery
- TestPostponing
- TestGapFilling
- TestQueueAccountLimiting
- TestQueueGlobalLimiting
- TestQueueGlobalLimitingNoLocals
- TestQueueTimeLimiting
- TestQueueTimeLimitingNoLocals
- TestPendingLimiting
- TestPendingGlobalLimiting
- TestAllowedTxSize
- TestCapClearsFromAll
- TestPendingMinimumAllowance
- TestRepricing
- TestRepricingDynamicFee
- TestRepricingKeepsLocals
- TestPoolUnderpricing
- TestPoolStableUnderpricing
- TestUnderpricingDynamicFee
- TestDualHeapEviction
- TestDeduplication
- TestReplacement
- TestReplacementDynamicFee
- TestJournaling
- TestJournalingNoLocals
- TestStatusCheck
- TestDropping
- TestQueue
- TestQueue2
- TestNegativeValue
- TestSlotCount

All tests now pass consistently when run with: go test ./core/txpool -count=1
This commit is contained in:
Daniel Liu 2025-12-22 14:31:21 +08:00 committed by GitHub
parent 639531aae9
commit 1dd09427ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -267,8 +267,8 @@ func TestStateChangeDuringReset(t *testing.T) {
statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether), tracing.BalanceChangeUnspecified)
blockchain := &testChain{newTestBlockChain(1000000000, statedb, new(event.Feed)), address, &trigger}
tx0 := transaction(0, 100000, key)
tx1 := transaction(1, 100000, key)
tx0 := pricedTransaction(0, 100000, big.NewInt(250000000), key)
tx1 := pricedTransaction(1, 100000, big.NewInt(250000000), key)
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@ -335,8 +335,8 @@ func TestInvalidTransactions(t *testing.T) {
t.Errorf("want %v have %v", want, err)
}
tx = transaction(1, 100000, key)
pool.gasPrice = big.NewInt(1000)
tx = pricedTransaction(1, 100000, big.NewInt(300000000), key)
pool.gasPrice = big.NewInt(1000000000)
if err, want := pool.AddRemote(tx), ErrUnderpriced; !errors.Is(err, want) {
t.Errorf("want %v have %v", want, err)
}
@ -534,7 +534,7 @@ func TestChainFork(t *testing.T) {
}
resetState()
tx := transaction(0, 100000, key)
tx := pricedTransaction(0, 100000, big.NewInt(300000000), key)
if _, err := pool.add(tx, false); err != nil {
t.Error("didn't expect error", err)
}
@ -556,7 +556,7 @@ func TestDoubleNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
statedb.AddBalance(addr, big.NewInt(100000000000000), tracing.BalanceChangeUnspecified)
statedb.AddBalance(addr, big.NewInt(600000000000000), tracing.BalanceChangeUnspecified)
pool.chain = newTestBlockChain(1000000, statedb, new(event.Feed))
<-pool.requestReset(nil, nil)
@ -564,9 +564,11 @@ func TestDoubleNonce(t *testing.T) {
resetState()
signer := types.HomesteadSigner{}
tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), nil), signer, key)
tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(2), nil), signer, key)
tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(1), nil), signer, key)
// Gas prices are chosen to keep the original 1:2 price ratio (250,000,000 vs. 500,000,000 wei)
// while satisfying the chain's MinGasPrice requirement of 250,000,000 wei.
tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100000, big.NewInt(250000000), nil), signer, key)
tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(500000000), nil), signer, key)
tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(250000000), nil), signer, key)
// Add the first two transaction, ensure higher priced stays only
if replace, err := pool.add(tx1, false); err != nil || replace {
@ -606,7 +608,7 @@ func TestMissingNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
testAddBalance(pool, addr, big.NewInt(100000000000000))
tx := transaction(1, 100000, key)
tx := pricedTransaction(1, 100000, big.NewInt(300000000), key)
if _, err := pool.add(tx, false); err != nil {
t.Error("didn't expect error", err)
}
@ -633,7 +635,7 @@ func TestNonceRecovery(t *testing.T) {
testAddBalance(pool, addr, big.NewInt(100000000000000))
<-pool.requestReset(nil, nil)
tx := transaction(n, 100000, key)
tx := pricedTransaction(n, 100000, big.NewInt(300000000), key)
if err := pool.AddRemote(tx); err != nil {
t.Error(err)
}
@ -769,8 +771,7 @@ func TestPostponing(t *testing.T) {
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
accs[i] = crypto.PubkeyToAddress(keys[i].PublicKey)
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(113000000001000))
}
// Add a batch consecutive pending transactions for validation
txs := []*types.Transaction{}
@ -778,9 +779,9 @@ func TestPostponing(t *testing.T) {
for j := 0; j < 10; j++ {
var tx *types.Transaction
if (i+j)%2 == 0 {
tx = transaction(uint64(j), 25000, key)
tx = pricedTransaction(uint64(j), 25000, big.NewInt(300000000), key)
} else {
tx = transaction(uint64(j), 50000, key)
tx = pricedTransaction(uint64(j), 50000, big.NewInt(300000000), key)
}
txs = append(txs, tx)
}
@ -812,7 +813,7 @@ func TestPostponing(t *testing.T) {
}
// Reduce the balance of the account, and check that transactions are reorganised
for _, addr := range accs {
testAddBalance(pool, addr, big.NewInt(-1))
testAddBalance(pool, addr, big.NewInt(-105500000000000))
}
<-pool.requestReset(nil, nil)
@ -873,7 +874,7 @@ func TestGapFilling(t *testing.T) {
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
testAddBalance(pool, account, big.NewInt(1000000))
testAddBalance(pool, account, big.NewInt(90000000000000))
// Keep track of transaction events to ensure all executables get announced
events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue+5)
@ -882,8 +883,8 @@ func TestGapFilling(t *testing.T) {
// Create a pending and a queued transaction with a nonce-gap in between
pool.AddRemotesSync([]*types.Transaction{
transaction(0, 100000, key),
transaction(2, 100000, key),
pricedTransaction(0, 100000, big.NewInt(300000000), key),
pricedTransaction(2, 100000, big.NewInt(300000000), key),
})
pending, queued := pool.Stats()
if pending != 1 {
@ -899,7 +900,7 @@ func TestGapFilling(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Fill the nonce gap and ensure all transactions become pending
if err := pool.addRemoteSync(transaction(1, 100000, key)); err != nil {
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(300000000), key)); err != nil {
t.Fatalf("failed to add gapped transaction: %v", err)
}
pending, queued = pool.Stats()
@ -927,11 +928,11 @@ func TestQueueAccountLimiting(t *testing.T) {
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
testAddBalance(pool, account, big.NewInt(1000000))
testAddBalance(pool, account, big.NewInt(300000000000000))
testTxPoolConfig.AccountQueue = 10
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(1); i <= testTxPoolConfig.AccountQueue; i++ {
if err := pool.addRemoteSync(transaction(i, 100000, key)); err != nil {
if err := pool.addRemoteSync(pricedTransaction(i, 100000, big.NewInt(300000000), key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
if len(pool.pending) != 0 {
@ -960,6 +961,7 @@ func TestQueueAccountLimiting(t *testing.T) {
func TestQueueGlobalLimiting(t *testing.T) {
testQueueGlobalLimiting(t, false)
}
func TestQueueGlobalLimitingNoLocals(t *testing.T) {
testQueueGlobalLimiting(t, true)
}
@ -984,7 +986,7 @@ func testQueueGlobalLimiting(t *testing.T, nolocals bool) {
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(100000000000000))
}
local := keys[len(keys)-1]
@ -996,7 +998,7 @@ func testQueueGlobalLimiting(t *testing.T, nolocals bool) {
key := keys[rand.Intn(len(keys)-1)] // skip adding transactions with the local account
addr := crypto.PubkeyToAddress(key.PublicKey)
txs = append(txs, transaction(nonces[addr]+1, 100000, key))
txs = append(txs, pricedTransaction(nonces[addr]+1, 100000, big.NewInt(300000000), key))
nonces[addr]++
}
// Import the batch and verify that limits have been enforced
@ -1015,7 +1017,7 @@ func testQueueGlobalLimiting(t *testing.T, nolocals bool) {
// Generate a batch of transactions from the local account and import them
txs = txs[:0]
for i := uint64(0); i < 3*config.GlobalQueue; i++ {
txs = append(txs, transaction(i+1, 100000, local))
txs = append(txs, pricedTransaction(i+1, 100000, big.NewInt(300000000), local))
}
pool.AddLocals(txs)
@ -1055,7 +1057,6 @@ func TestQueueTimeLimitingNoLocals(t *testing.T) {
}
func testQueueTimeLimiting(t *testing.T, nolocals bool) {
common.MinGasPrice = big.NewInt(0)
// Reduce the eviction interval to a testable amount
defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
evictionInterval = time.Millisecond * 100
@ -1076,14 +1077,14 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
local, _ := crypto.GenerateKey()
remote, _ := crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(100000000000000))
testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(100000000000000))
// Add the two transactions and ensure they both are queued up
if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(300000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), remote)); err != nil {
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(300000000), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
pending, queued := pool.Stats()
@ -1150,19 +1151,19 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
}
// Queue gapped transactions
if err := pool.AddLocal(pricedTransaction(4, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(4, 100000, big.NewInt(300000000), local)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
if err := pool.addRemoteSync(pricedTransaction(4, 100000, big.NewInt(1), remote)); err != nil {
if err := pool.addRemoteSync(pricedTransaction(4, 100000, big.NewInt(300000000), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
time.Sleep(5 * evictionInterval) // A half lifetime pass
// Queue executable transactions, the life cycle should be restarted.
if err := pool.AddLocal(pricedTransaction(2, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(2, 100000, big.NewInt(300000000), local)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), remote)); err != nil {
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(300000000), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
time.Sleep(6 * evictionInterval)
@ -1210,7 +1211,7 @@ func TestPendingLimiting(t *testing.T) {
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
testAddBalance(pool, account, big.NewInt(1000000000000))
testAddBalance(pool, account, big.NewInt(400000000000000))
testTxPoolConfig.AccountQueue = 10
// Keep track of transaction events to ensure all executables get announced
@ -1220,7 +1221,7 @@ func TestPendingLimiting(t *testing.T) {
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(0); i < testTxPoolConfig.AccountQueue; i++ {
if err := pool.addRemoteSync(transaction(i, 100000, key)); err != nil {
if err := pool.addRemoteSync(pricedTransaction(i, 100000, big.NewInt(300000000), key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
if pool.pending[account].Len() != int(i)+1 {
@ -1301,7 +1302,7 @@ func TestAllowedTxSize(t *testing.T) {
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
testAddBalance(pool, account, big.NewInt(1000000000))
testAddBalance(pool, account, big.NewInt(10000000000000000))
// Compute maximal data size for transactions (lower bound).
//
@ -1317,20 +1318,20 @@ func TestAllowedTxSize(t *testing.T) {
dataSize := txMaxSize - baseSize
maxGas := pool.currentMaxGas.Load()
// Try adding a transaction with maximal allowed size
tx := pricedDataTransaction(0, pool.currentMaxGas.Load(), big.NewInt(1), key, dataSize)
tx := pricedDataTransaction(0, pool.currentMaxGas.Load(), big.NewInt(300000000), key, dataSize)
if err := pool.addRemoteSync(tx); err != nil {
t.Fatalf("failed to add transaction of size %d, close to maximal: %v", int(tx.Size()), err)
}
// Try adding a transaction with random allowed size
if err := pool.addRemoteSync(pricedDataTransaction(1, maxGas, big.NewInt(1), key, uint64(rand.Intn(int(dataSize))))); err != nil {
if err := pool.addRemoteSync(pricedDataTransaction(1, maxGas, big.NewInt(300000000), key, uint64(rand.Intn(int(dataSize))))); err != nil {
t.Fatalf("failed to add transaction of random allowed size: %v", err)
}
// Try adding a transaction of minimal not allowed size
if err := pool.addRemoteSync(pricedDataTransaction(2, maxGas, big.NewInt(1), key, txMaxSize)); err == nil {
if err := pool.addRemoteSync(pricedDataTransaction(2, maxGas, big.NewInt(300000000), key, txMaxSize)); err == nil {
t.Fatalf("expected rejection on slightly oversize transaction")
}
// Try adding a transaction of random not allowed size
if err := pool.addRemoteSync(pricedDataTransaction(2, maxGas, big.NewInt(1), key, dataSize+1+uint64(rand.Intn(int(10*txMaxSize))))); err == nil {
if err := pool.addRemoteSync(pricedDataTransaction(2, maxGas, big.NewInt(300000000), key, dataSize+1+uint64(rand.Intn(int(10*txMaxSize))))); err == nil {
t.Fatalf("expected rejection on oversize transaction")
}
// Run some sanity checks on the pool internals
@ -1451,24 +1452,24 @@ func TestRepricing(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(200000000000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(500000000), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(250000000), keys[0]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(500000000), keys[0]))
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[1]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[1]))
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(250000000), keys[1]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(500000000), keys[1]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(500000000), keys[1]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[2]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[2]))
txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[2]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(500000000), keys[2]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(250000000), keys[2]))
txs = append(txs, pricedTransaction(3, 100000, big.NewInt(500000000), keys[2]))
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[3])
ltx := pricedTransaction(0, 100000, big.NewInt(250000000), keys[3])
// Import the batch and that both pending and queued transactions match up
pool.AddRemotesSync(txs)
@ -1488,7 +1489,7 @@ func TestRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Reprice the pool and check that underpriced transactions get dropped
pool.SetGasPrice(big.NewInt(2))
pool.SetGasPrice(big.NewInt(500000000))
pending, queued = pool.Stats()
if pending != 2 {
@ -1504,13 +1505,13 @@ func TestRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Check that we can't add the old transactions back
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced {
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(250000000), keys[0])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(250000000), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[2])); err != ErrUnderpriced {
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(250000000), keys[2])); err != ErrUnderpriced {
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
if err := validateEvents(events, 0); err != nil {
@ -1520,7 +1521,7 @@ func TestRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
tx := pricedTransaction(1, 100000, big.NewInt(1), keys[3])
tx := pricedTransaction(1, 100000, big.NewInt(250000000), keys[3])
if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err)
}
@ -1534,13 +1535,13 @@ func TestRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// And we can fill gaps with properly priced transactions
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(2), keys[0])); err != nil {
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(500000000), keys[0])); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), keys[1])); err != nil {
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(500000000), keys[1])); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), keys[2])); err != nil {
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(500000000), keys[2])); err != nil {
t.Fatalf("failed to add queued transaction: %v", err)
}
if err := validateEvents(events, 5); err != nil {
@ -1572,24 +1573,24 @@ func TestRepricingDynamicFee(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(200000000000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(350000000), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(300000000), keys[0]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(350000000), keys[0]))
txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1]))
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(3), big.NewInt(2), keys[1]))
txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(3), big.NewInt(2), keys[1]))
txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(350000000), big.NewInt(300000000), keys[1]))
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(400000000), big.NewInt(350000000), keys[1]))
txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(400000000), big.NewInt(350000000), keys[1]))
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(2), keys[2]))
txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2]))
txs = append(txs, dynamicFeeTx(3, 100000, big.NewInt(2), big.NewInt(2), keys[2]))
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(350000000), big.NewInt(350000000), keys[2]))
txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(300000000), big.NewInt(300000000), keys[2]))
txs = append(txs, dynamicFeeTx(3, 100000, big.NewInt(350000000), big.NewInt(350000000), keys[2]))
ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[3])
ltx := dynamicFeeTx(0, 100000, big.NewInt(350000000), big.NewInt(300000000), keys[3])
// Import the batch and that both pending and queued transactions match up
pool.AddRemotesSync(txs)
@ -1609,7 +1610,7 @@ func TestRepricingDynamicFee(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Reprice the pool and check that underpriced transactions get dropped
pool.SetGasPrice(big.NewInt(2))
pool.SetGasPrice(big.NewInt(350000000))
pending, queued = pool.Stats()
if pending != 2 {
@ -1625,15 +1626,15 @@ func TestRepricingDynamicFee(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Check that we can't add the old transactions back
tx := pricedTransaction(1, 100000, big.NewInt(1), keys[0])
tx := pricedTransaction(1, 100000, big.NewInt(300000000), keys[0])
if err := pool.AddRemote(tx); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
tx = dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1])
tx = dynamicFeeTx(0, 100000, big.NewInt(350000000), big.NewInt(300000000), keys[1])
if err := pool.AddRemote(tx); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
tx = dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2])
tx = dynamicFeeTx(2, 100000, big.NewInt(300000000), big.NewInt(300000000), keys[2])
if err := pool.AddRemote(tx); err != ErrUnderpriced {
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
@ -1644,7 +1645,7 @@ func TestRepricingDynamicFee(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
tx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), keys[3])
tx = dynamicFeeTx(1, 100000, big.NewInt(300000000), big.NewInt(300000000), keys[3])
if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err)
}
@ -1658,15 +1659,15 @@ func TestRepricingDynamicFee(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// And we can fill gaps with properly priced transactions
tx = pricedTransaction(1, 100000, big.NewInt(2), keys[0])
tx = pricedTransaction(1, 100000, big.NewInt(350000000), keys[0])
if err := pool.AddRemote(tx); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
tx = dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[1])
tx = dynamicFeeTx(0, 100000, big.NewInt(400000000), big.NewInt(350000000), keys[1])
if err := pool.AddRemote(tx); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
tx = dynamicFeeTx(2, 100000, big.NewInt(2), big.NewInt(2), keys[2])
tx = dynamicFeeTx(2, 100000, big.NewInt(350000000), big.NewInt(350000000), keys[2])
if err := pool.AddRemote(tx); err != nil {
t.Fatalf("failed to add queued transaction: %v", err)
}
@ -1695,29 +1696,29 @@ func TestRepricingKeepsLocals(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 3)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(100000*1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1500000000000000))
}
// Create transaction (both pending and queued) with a linearly growing gasprice
// common.LimitThresholdNonceInQueue = 10
for i := uint64(0); i < 5; i++ {
// Add pending transaction.
pendingTx := pricedTransaction(i, 100000, big.NewInt(int64(i+1)), keys[2])
pendingTx := pricedTransaction(i, 100000, big.NewInt(int64((i+1)*250000000)), keys[2])
if err := pool.AddLocal(pendingTx); err != nil {
t.Fatal(err)
}
// Add queued transaction.
queuedTx := pricedTransaction(i+6, 100000, big.NewInt(int64(i+1)), keys[2])
queuedTx := pricedTransaction(i+6, 100000, big.NewInt(int64((i+1)*250000000)), keys[2])
if err := pool.AddLocal(queuedTx); err != nil {
t.Fatal(err)
}
// Add pending dynamic fee transaction.
pendingTx = dynamicFeeTx(i, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1])
pendingTx = dynamicFeeTx(i, 100000, big.NewInt(int64((i+1)*250000000)), big.NewInt(int64((i+1)*250000000)), keys[1])
if err := pool.AddLocal(pendingTx); err != nil {
t.Fatal(err)
}
// Add queued dynamic fee transaction.
queuedTx = dynamicFeeTx(i+6, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1])
queuedTx = dynamicFeeTx(i+6, 100000, big.NewInt(int64((i+1)*250000000)), big.NewInt(int64((i+1)*250000000)), keys[1])
if err := pool.AddLocal(queuedTx); err != nil {
t.Fatal(err)
}
@ -1740,13 +1741,13 @@ func TestRepricingKeepsLocals(t *testing.T) {
validate()
// Reprice the pool and check that nothing is dropped
pool.SetGasPrice(big.NewInt(2))
pool.SetGasPrice(big.NewInt(500000000))
validate()
pool.SetGasPrice(big.NewInt(2))
pool.SetGasPrice(big.NewInt(4))
pool.SetGasPrice(big.NewInt(8))
pool.SetGasPrice(big.NewInt(100))
pool.SetGasPrice(big.NewInt(500000000))
pool.SetGasPrice(big.NewInt(1000000000))
pool.SetGasPrice(big.NewInt(2000000000))
pool.SetGasPrice(big.NewInt(25000000000))
validate()
}
@ -1779,17 +1780,20 @@ func TestPoolUnderpricing(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(600000000000000))
}
// Generate and queue a batch of transactions, both pending and queued
// Gas prices follow an intentional stepped pattern (250M, 500M, 750M, 1000M, 1250M) to test
// transaction replacement and eviction logic based on price competition. The 1:5 ratio
// (250M to 1250M) ensures clear differentiation when testing underpricing behavior.
txs := types.Transactions{}
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(250000000), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(500000000), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[1]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(250000000), keys[1]))
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2])
ltx := pricedTransaction(0, 100000, big.NewInt(250000000), keys[2])
// Import the batch and that both pending and queued transactions match up
pool.AddRemotes(txs)
@ -1809,25 +1813,25 @@ func TestPoolUnderpricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding an underpriced transaction on block limit fails
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(250000000), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
// Replace a future transaction with a future transaction
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(2), keys[1])); err != nil { // +K1:1 => -K1:1 => Pend K0:0, K0:1, K2:0; Que K1:1
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(500000000), keys[1])); err != nil { // +K1:1 => -K1:1 => Pend K0:0, K0:1, K2:0; Que K1:1
t.Fatalf("failed to add well priced transaction: %v", err)
}
// Ensure that adding high priced transactions drops cheap ones, but not own
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil { // +K1:0 => -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que -
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(750000000), keys[1])); err != nil { // +K1:0 => -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que -
t.Fatalf("failed to add well priced transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(4), keys[1])); err != nil { // +K1:2 => -K0:0 => Pend K1:0, K2:0; Que K0:1 K1:2
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1000000000), keys[1])); err != nil { // +K1:2 => -K0:0 => Pend K1:0, K2:0; Que K0:1 K1:2
t.Fatalf("failed to add well priced transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(5), keys[1])); err != nil { // +K1:3 => -K0:1 => Pend K1:0, K2:0; Que K1:2 K1:3
if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(1250000000), keys[1])); err != nil { // +K1:3 => -K0:1 => Pend K1:0, K2:0; Que K1:2 K1:3
t.Fatalf("failed to add well priced transaction: %v", err)
}
// Ensure that replacing a pending transaction with a future transaction fails
if err := pool.AddRemote(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != ErrFutureReplacePending {
if err := pool.AddRemote(pricedTransaction(5, 100000, big.NewInt(1500000000), keys[1])); err != ErrFutureReplacePending {
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending)
}
pending, queued = pool.Stats()
@ -1844,11 +1848,11 @@ func TestPoolUnderpricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
ltx = pricedTransaction(1, 100000, big.NewInt(1), keys[2])
ltx = pricedTransaction(1, 100000, big.NewInt(250000000), keys[2])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to append underpriced local transaction: %v", err)
}
ltx = pricedTransaction(0, 100000, big.NewInt(1), keys[3])
ltx = pricedTransaction(0, 100000, big.NewInt(250000000), keys[3])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to add new underpriced local transaction: %v", err)
}
@ -1895,12 +1899,12 @@ func TestPoolStableUnderpricing(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 2)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(400000000000000))
}
// Fill up the entire queue with the same transaction price points
txs := types.Transactions{}
for i := uint64(0); i < config.GlobalSlots; i++ {
txs = append(txs, pricedTransaction(i, 100000, big.NewInt(1), keys[0]))
txs = append(txs, pricedTransaction(i, 100000, big.NewInt(300000000), keys[0]))
}
pool.AddRemotesSync(txs)
@ -1918,7 +1922,7 @@ func TestPoolStableUnderpricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding high priced transactions drops a cheap, but doesn't produce a gap
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil {
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(350000000), keys[1])); err != nil {
t.Fatalf("failed to add well priced transaction: %v", err)
}
pending, queued = pool.Stats()
@ -1959,17 +1963,17 @@ func TestUnderpricingDynamicFee(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(200000000000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[0]))
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(1), keys[1]))
txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(270000000), big.NewInt(260000000), keys[0]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(260000000), keys[0]))
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(260000000), big.NewInt(250000000), keys[1]))
ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[2])
ltx := dynamicFeeTx(0, 100000, big.NewInt(260000000), big.NewInt(250000000), keys[2])
// Import the batch and that both pending and queued transactions match up
pool.AddRemotes(txs) // Pend K0:0, K0:1; Que K1:1
@ -1990,22 +1994,22 @@ func TestUnderpricingDynamicFee(t *testing.T) {
}
// Ensure that adding an underpriced transaction fails
tx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1])
tx := dynamicFeeTx(0, 100000, big.NewInt(260000000), big.NewInt(250000000), keys[1])
if err := pool.AddRemote(tx); err != ErrUnderpriced { // Pend K0:0, K0:1, K2:0; Que K1:1
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
// Ensure that adding high priced transactions drops cheap ones, but not own
tx = pricedTransaction(0, 100000, big.NewInt(2), keys[1])
tx = pricedTransaction(0, 100000, big.NewInt(260000000), keys[1])
if err := pool.AddRemote(tx); err != nil { // +K1:0, -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que -
t.Fatalf("failed to add well priced transaction: %v", err)
}
tx = pricedTransaction(1, 100000, big.NewInt(3), keys[1])
tx = pricedTransaction(1, 100000, big.NewInt(270000000), keys[1])
if err := pool.AddRemote(tx); err != nil { // +K1:2, -K0:1 => Pend K0:0 K1:0, K2:0; Que K1:2
t.Fatalf("failed to add well priced transaction: %v", err)
}
tx = dynamicFeeTx(2, 100000, big.NewInt(4), big.NewInt(1), keys[1])
tx = dynamicFeeTx(2, 100000, big.NewInt(280000000), big.NewInt(250000000), keys[1])
if err := pool.AddRemote(tx); err != nil { // +K1:3, -K1:0 => Pend K0:0 K2:0; Que K1:2 K1:3
t.Fatalf("failed to add well priced transaction: %v", err)
}
@ -2023,11 +2027,11 @@ func TestUnderpricingDynamicFee(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
ltx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(0), keys[2])
ltx = dynamicFeeTx(1, 100000, big.NewInt(250000000), big.NewInt(250000000), keys[2])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to append underpriced local transaction: %v", err)
}
ltx = dynamicFeeTx(0, 100000, big.NewInt(1), big.NewInt(0), keys[3])
ltx = dynamicFeeTx(0, 100000, big.NewInt(250000000), big.NewInt(250000000), keys[3])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to add new underpriced local transaction: %v", err)
}
@ -2073,12 +2077,12 @@ func TestDualHeapEviction(t *testing.T) {
var tx *types.Transaction
// Create a test accounts and fund it
key, _ := crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000000))
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000000))
if urgent {
tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key)
tx = dynamicFeeTx(0, 100000, big.NewInt(int64(250000000+baseFee+1+i)), big.NewInt(int64(250000000+1+i)), key)
highTip = tx
} else {
tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key)
tx = dynamicFeeTx(0, 100000, big.NewInt(int64(250000000+baseFee+200+i)), big.NewInt(250000000), key)
highCap = tx
}
pool.AddRemotesSync([]*types.Transaction{tx})
@ -2116,12 +2120,12 @@ func TestDeduplication(t *testing.T) {
// Create a test account to add transactions with
key, _ := crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(300000000000000))
// Create a batch of transactions and add a few of them
txs := make([]*types.Transaction, common.LimitThresholdNonceInQueue)
for i := 0; i < len(txs); i++ {
txs[i] = pricedTransaction(uint64(i), 100000, big.NewInt(1), key)
txs[i] = pricedTransaction(uint64(i), 100000, big.NewInt(300000000), key)
}
var firsts []*types.Transaction
for i := 0; i < len(txs); i += 2 {
@ -2188,19 +2192,19 @@ func TestReplacement(t *testing.T) {
// Create a test account to add transactions with
key, _ := crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(500000000000000))
// Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
price := int64(100)
price := int64(500000000)
threshold := (price * (100 + int64(testTxPoolConfig.PriceBump))) / 100
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), key)); err != nil {
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(250000000), key)); err != nil {
t.Fatalf("failed to add original cheap pending transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(250000000), key)); err != ErrReplaceUnderpriced {
t.Fatalf("original cheap pending transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
}
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), key)); err != nil {
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(300000000), key)); err != nil {
t.Fatalf("failed to replace original cheap pending transaction: %v", err)
}
if err := validateEvents(events, 2); err != nil {
@ -2221,13 +2225,13 @@ func TestReplacement(t *testing.T) {
}
// Add queued transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), key)); err != nil {
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(250000000), key)); err != nil {
t.Fatalf("failed to add original cheap queued transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(250000000), key)); err != ErrReplaceUnderpriced {
t.Fatalf("original cheap queued transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), key)); err != nil {
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(300000000), key)); err != nil {
t.Fatalf("failed to replace original cheap queued transaction: %v", err)
}
@ -2257,7 +2261,7 @@ func TestReplacementDynamicFee(t *testing.T) {
// Create the pool to test the pricing enforcement with
pool, key := setupPoolWithConfig(eip1559Config)
defer pool.Stop()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(500000000000000))
// Keep track of transaction events to ensure all executables get announced
events := make(chan core.NewTxsEvent, 32)
@ -2265,9 +2269,9 @@ func TestReplacementDynamicFee(t *testing.T) {
defer sub.Unsubscribe()
// Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
gasFeeCap := int64(100)
gasFeeCap := int64(500000000)
feeCapThreshold := (gasFeeCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100
gasTipCap := int64(60)
gasTipCap := int64(400000000)
tipThreshold := (gasTipCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100
// Run the following identical checks for both the pending and queue pools:
@ -2292,17 +2296,17 @@ func TestReplacementDynamicFee(t *testing.T) {
}
// 1. Send initial tx => accept
tx := dynamicFeeTx(nonce, 100000, big.NewInt(2), big.NewInt(1), key)
tx := dynamicFeeTx(nonce, 100000, big.NewInt(300000000), big.NewInt(250000000), key)
if err := pool.addRemoteSync(tx); err != nil {
t.Fatalf("failed to add original cheap %s transaction: %v", stage, err)
}
// 2. Don't bump tip or feecap => discard
tx = dynamicFeeTx(nonce, 100001, big.NewInt(2), big.NewInt(1), key)
tx = dynamicFeeTx(nonce, 100001, big.NewInt(300000000), big.NewInt(250000000), key)
if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced {
t.Fatalf("original cheap %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced)
}
// 3. Bump both more than min => accept
tx = dynamicFeeTx(nonce, 100000, big.NewInt(3), big.NewInt(2), key)
tx = dynamicFeeTx(nonce, 100000, big.NewInt(350000000), big.NewInt(300000000), key)
if err := pool.AddRemote(tx); err != nil {
t.Fatalf("failed to replace original cheap %s transaction: %v", stage, err)
}
@ -2361,7 +2365,8 @@ func TestReplacementDynamicFee(t *testing.T) {
// Tests that local transactions are journaled to disk, but remote transactions
// get discarded between restarts.
func TestJournaling(t *testing.T) { testTransactionJournaling(t, false) }
func TestJournaling(t *testing.T) { testTransactionJournaling(t, false) }
func TestJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) }
func testTransactionJournaling(t *testing.T, nolocals bool) {
@ -2395,20 +2400,20 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
local, _ := crypto.GenerateKey()
remote, _ := crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(100000000000000))
testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(100000000000000))
// Add three local and a remote transactions and ensure they are queued up
if err := pool.AddLocal(pricedTransaction(0, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(0, 100000, big.NewInt(300000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(300000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.AddLocal(pricedTransaction(2, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(2, 100000, big.NewInt(300000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), remote)); err != nil {
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(300000000), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
pending, queued := pool.Stats()
@ -2490,15 +2495,15 @@ func TestStatusCheck(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 3)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(100000000000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[0])) // Pending only
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[1])) // Pending and queued
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[1]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[2])) // Queued only
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(300000000), keys[0])) // Pending only
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(300000000), keys[1])) // Pending and queued
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(300000000), keys[1]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(300000000), keys[2])) // Queued only
// Import the transaction and ensure they are correctly added
pool.AddRemotesSync(txs)