github.com/theQRL/go-zond@v0.2.1/core/txpool/legacypool/legacypool_test.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package legacypool 18 19 import ( 20 crand "crypto/rand" 21 "errors" 22 "fmt" 23 "math/big" 24 "math/rand" 25 "os" 26 "sync" 27 "sync/atomic" 28 "testing" 29 "time" 30 31 "github.com/theQRL/go-qrllib/dilithium" 32 "github.com/theQRL/go-zond/common" 33 "github.com/theQRL/go-zond/core" 34 "github.com/theQRL/go-zond/core/rawdb" 35 "github.com/theQRL/go-zond/core/state" 36 "github.com/theQRL/go-zond/core/txpool" 37 "github.com/theQRL/go-zond/core/types" 38 "github.com/theQRL/go-zond/crypto" 39 "github.com/theQRL/go-zond/event" 40 "github.com/theQRL/go-zond/params" 41 "github.com/theQRL/go-zond/trie" 42 ) 43 44 var ( 45 // testTxPoolConfig is a transaction pool configuration without stateful disk 46 // sideeffects used during testing. 47 testTxPoolConfig Config 48 49 // eip1559Config is a chain config with EIP-1559 enabled at block 0. 50 eip1559Config *params.ChainConfig 51 ) 52 53 func init() { 54 testTxPoolConfig = DefaultConfig 55 testTxPoolConfig.Journal = "" 56 57 cpy := *params.TestChainConfig 58 eip1559Config = &cpy 59 } 60 61 type testBlockChain struct { 62 config *params.ChainConfig 63 gasLimit atomic.Uint64 64 statedb *state.StateDB 65 chainHeadFeed *event.Feed 66 } 67 68 func newTestBlockChain(config *params.ChainConfig, gasLimit uint64, statedb *state.StateDB, chainHeadFeed *event.Feed) *testBlockChain { 69 bc := testBlockChain{config: config, statedb: statedb, chainHeadFeed: new(event.Feed)} 70 bc.gasLimit.Store(gasLimit) 71 return &bc 72 } 73 74 func (bc *testBlockChain) Config() *params.ChainConfig { 75 return bc.config 76 } 77 78 func (bc *testBlockChain) CurrentBlock() *types.Header { 79 return &types.Header{ 80 Number: new(big.Int), 81 GasLimit: bc.gasLimit.Load(), 82 } 83 } 84 85 func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { 86 return types.NewBlock(bc.CurrentBlock(), nil, nil, trie.NewStackTrie(nil)) 87 } 88 89 func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { 90 return bc.statedb, nil 91 } 92 93 func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { 94 return bc.chainHeadFeed.Subscribe(ch) 95 } 96 97 func transaction(nonce uint64, gaslimit uint64, key *dilithium.Dilithium) *types.Transaction { 98 return dynamicFeeTx(nonce, gaslimit, big.NewInt(1), big.NewInt(1), key) 99 } 100 101 func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int, key *dilithium.Dilithium) *types.Transaction { 102 tx, _ := types.SignNewTx(key, types.LatestSignerForChainID(params.TestChainConfig.ChainID), &types.DynamicFeeTx{ 103 ChainID: params.TestChainConfig.ChainID, 104 Nonce: nonce, 105 GasTipCap: tip, 106 GasFeeCap: gasFee, 107 Gas: gaslimit, 108 To: &common.Address{}, 109 Value: big.NewInt(100), 110 Data: nil, 111 AccessList: nil, 112 }) 113 return tx 114 } 115 116 func dynamicFeeDataTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int, key *dilithium.Dilithium, bytes uint64) *types.Transaction { 117 data := make([]byte, bytes) 118 crand.Read(data) 119 120 tx, _ := types.SignNewTx(key, types.ShanghaiSigner{ChainId: big.NewInt(1)}, &types.DynamicFeeTx{ 121 ChainID: params.TestChainConfig.ChainID, 122 Nonce: nonce, 123 GasTipCap: tip, 124 GasFeeCap: gasFee, 125 Gas: gaslimit, 126 To: &common.Address{}, 127 Value: big.NewInt(0), 128 Data: data, 129 AccessList: nil, 130 }) 131 return tx 132 } 133 134 func makeAddressReserver() txpool.AddressReserver { 135 var ( 136 reserved = make(map[common.Address]struct{}) 137 lock sync.Mutex 138 ) 139 return func(addr common.Address, reserve bool) error { 140 lock.Lock() 141 defer lock.Unlock() 142 143 _, exists := reserved[addr] 144 if reserve { 145 if exists { 146 panic("already reserved") 147 } 148 reserved[addr] = struct{}{} 149 return nil 150 } 151 if !exists { 152 panic("not reserved") 153 } 154 delete(reserved, addr) 155 return nil 156 } 157 } 158 159 func setupPool() (*LegacyPool, *dilithium.Dilithium) { 160 return setupPoolWithConfig(params.TestChainConfig) 161 } 162 163 func setupPoolWithConfig(config *params.ChainConfig) (*LegacyPool, *dilithium.Dilithium) { 164 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 165 blockchain := newTestBlockChain(config, 10000000, statedb, new(event.Feed)) 166 167 key, _ := crypto.GenerateDilithiumKey() 168 pool := New(testTxPoolConfig, blockchain) 169 if err := pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()); err != nil { 170 panic(err) 171 } 172 // wait for the pool to initialize 173 <-pool.initDoneCh 174 return pool, key 175 } 176 177 // validatePoolInternals checks various consistency invariants within the pool. 178 func validatePoolInternals(pool *LegacyPool) error { 179 pool.mu.RLock() 180 defer pool.mu.RUnlock() 181 182 // Ensure the total transaction set is consistent with pending + queued 183 pending, queued := pool.stats() 184 if total := pool.all.Count(); total != pending+queued { 185 return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued) 186 } 187 pool.priced.Reheap() 188 priced, remote := pool.priced.urgent.Len()+pool.priced.floating.Len(), pool.all.RemoteCount() 189 if priced != remote { 190 return fmt.Errorf("total priced transaction count %d != %d", priced, remote) 191 } 192 // Ensure the next nonce to assign is the correct one 193 for addr, txs := range pool.pending { 194 // Find the last transaction 195 var last uint64 196 for nonce := range txs.txs.items { 197 if last < nonce { 198 last = nonce 199 } 200 } 201 if nonce := pool.pendingNonces.get(addr); nonce != last+1 { 202 return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1) 203 } 204 } 205 return nil 206 } 207 208 // validateEvents checks that the correct number of transaction addition events 209 // were fired on the pool's event feed. 210 func validateEvents(events chan core.NewTxsEvent, count int) error { 211 var received []*types.Transaction 212 213 for len(received) < count { 214 select { 215 case ev := <-events: 216 received = append(received, ev.Txs...) 217 case <-time.After(time.Second): 218 return fmt.Errorf("event #%d not fired", len(received)) 219 } 220 } 221 if len(received) > count { 222 return fmt.Errorf("more than %d events fired: %v", count, received[count:]) 223 } 224 select { 225 case ev := <-events: 226 return fmt.Errorf("more than %d events fired: %v", count, ev.Txs) 227 228 case <-time.After(50 * time.Millisecond): 229 // This branch should be "default", but it's a data race between goroutines, 230 // reading the event channel and pushing into it, so better wait a bit ensuring 231 // really nothing gets injected. 232 } 233 return nil 234 } 235 236 func deriveSender(tx *types.Transaction) (common.Address, error) { 237 return types.Sender(types.ShanghaiSigner{ChainId: big.NewInt(1)}, tx) 238 } 239 240 type testChain struct { 241 *testBlockChain 242 address common.Address 243 trigger *bool 244 } 245 246 // testChain.State() is used multiple times to reset the pending state. 247 // when simulate is true it will create a state that indicates 248 // that tx0 and tx1 are included in the chain. 249 func (c *testChain) State() (*state.StateDB, error) { 250 // delay "state change" by one. The tx pool fetches the 251 // state multiple times and by delaying it a bit we simulate 252 // a state change between those fetches. 253 stdb := c.statedb 254 if *c.trigger { 255 c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 256 // simulate that the new head block included tx0 and tx1 257 c.statedb.SetNonce(c.address, 2) 258 c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether)) 259 *c.trigger = false 260 } 261 return stdb, nil 262 } 263 264 // This test simulates a scenario where a new block is imported during a 265 // state reset and tests whether the pending state is in sync with the 266 // block head event that initiated the resetState(). 267 func TestStateChangeDuringReset(t *testing.T) { 268 t.Parallel() 269 270 var ( 271 key, _ = crypto.GenerateDilithiumKey() 272 address = key.GetAddress() 273 statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 274 trigger = false 275 ) 276 277 // setup pool with 2 transaction in it 278 statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether)) 279 blockchain := &testChain{newTestBlockChain(params.TestChainConfig, 1000000000, statedb, new(event.Feed)), address, &trigger} 280 281 tx0 := transaction(0, 100000, key) 282 tx1 := transaction(1, 100000, key) 283 284 pool := New(testTxPoolConfig, blockchain) 285 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 286 defer pool.Close() 287 288 nonce := pool.Nonce(address) 289 if nonce != 0 { 290 t.Fatalf("Invalid nonce, want 0, got %d", nonce) 291 } 292 293 pool.addRemotesSync([]*types.Transaction{tx0, tx1}) 294 295 nonce = pool.Nonce(address) 296 if nonce != 2 { 297 t.Fatalf("Invalid nonce, want 2, got %d", nonce) 298 } 299 300 // trigger state change in the background 301 trigger = true 302 <-pool.requestReset(nil, nil) 303 304 nonce = pool.Nonce(address) 305 if nonce != 2 { 306 t.Fatalf("Invalid nonce, want 2, got %d", nonce) 307 } 308 } 309 310 func testAddBalance(pool *LegacyPool, addr common.Address, amount *big.Int) { 311 pool.mu.Lock() 312 pool.currentState.AddBalance(addr, amount) 313 pool.mu.Unlock() 314 } 315 316 func testSetNonce(pool *LegacyPool, addr common.Address, nonce uint64) { 317 pool.mu.Lock() 318 pool.currentState.SetNonce(addr, nonce) 319 pool.mu.Unlock() 320 } 321 322 func TestInvalidTransactions(t *testing.T) { 323 t.Parallel() 324 325 pool, key := setupPool() 326 defer pool.Close() 327 328 tx := transaction(0, 100, key) 329 from, _ := deriveSender(tx) 330 331 // Intrinsic gas too low 332 testAddBalance(pool, from, big.NewInt(1)) 333 if err, want := pool.addRemote(tx), core.ErrIntrinsicGas; !errors.Is(err, want) { 334 t.Errorf("want %v have %v", want, err) 335 } 336 337 // Insufficient funds 338 tx = transaction(0, 100000, key) 339 if err, want := pool.addRemote(tx), core.ErrInsufficientFunds; !errors.Is(err, want) { 340 t.Errorf("want %v have %v", want, err) 341 } 342 343 testSetNonce(pool, from, 1) 344 testAddBalance(pool, from, big.NewInt(0xffffffffffffff)) 345 tx = transaction(0, 100000, key) 346 if err, want := pool.addRemote(tx), core.ErrNonceTooLow; !errors.Is(err, want) { 347 t.Errorf("want %v have %v", want, err) 348 } 349 350 tx = transaction(1, 100000, key) 351 pool.gasTip.Store(big.NewInt(1000)) 352 if err, want := pool.addRemote(tx), txpool.ErrUnderpriced; !errors.Is(err, want) { 353 t.Errorf("want %v have %v", want, err) 354 } 355 if err := pool.addLocal(tx); err != nil { 356 t.Error("expected", nil, "got", err) 357 } 358 } 359 360 func TestQueue(t *testing.T) { 361 t.Parallel() 362 363 pool, key := setupPool() 364 defer pool.Close() 365 366 tx := transaction(0, 100, key) 367 from, _ := deriveSender(tx) 368 testAddBalance(pool, from, big.NewInt(1000)) 369 <-pool.requestReset(nil, nil) 370 371 pool.enqueueTx(tx.Hash(), tx, false, true) 372 <-pool.requestPromoteExecutables(newAccountSet(pool.signer, from)) 373 if len(pool.pending) != 1 { 374 t.Error("expected valid txs to be 1 is", len(pool.pending)) 375 } 376 377 tx = transaction(1, 100, key) 378 from, _ = deriveSender(tx) 379 testSetNonce(pool, from, 2) 380 pool.enqueueTx(tx.Hash(), tx, false, true) 381 382 <-pool.requestPromoteExecutables(newAccountSet(pool.signer, from)) 383 if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok { 384 t.Error("expected transaction to be in tx pool") 385 } 386 if len(pool.queue) > 0 { 387 t.Error("expected transaction queue to be empty. is", len(pool.queue)) 388 } 389 } 390 391 func TestQueue2(t *testing.T) { 392 t.Parallel() 393 394 pool, key := setupPool() 395 defer pool.Close() 396 397 tx1 := transaction(0, 100, key) 398 tx2 := transaction(10, 100, key) 399 tx3 := transaction(11, 100, key) 400 from, _ := deriveSender(tx1) 401 testAddBalance(pool, from, big.NewInt(1000)) 402 pool.reset(nil, nil) 403 404 pool.enqueueTx(tx1.Hash(), tx1, false, true) 405 pool.enqueueTx(tx2.Hash(), tx2, false, true) 406 pool.enqueueTx(tx3.Hash(), tx3, false, true) 407 408 pool.promoteExecutables([]common.Address{from}) 409 if len(pool.pending) != 1 { 410 t.Error("expected pending length to be 1, got", len(pool.pending)) 411 } 412 if pool.queue[from].Len() != 2 { 413 t.Error("expected len(queue) == 2, got", pool.queue[from].Len()) 414 } 415 } 416 417 func TestNegativeValue(t *testing.T) { 418 t.Parallel() 419 420 pool, key := setupPool() 421 defer pool.Close() 422 423 tx := types.NewTx(&types.DynamicFeeTx{ 424 Nonce: 0, 425 To: &common.Address{}, 426 Value: big.NewInt(-1), 427 Gas: 100, 428 Data: nil, 429 }) 430 signedTx, _ := types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, key) 431 from, _ := deriveSender(signedTx) 432 testAddBalance(pool, from, big.NewInt(1)) 433 if err := pool.addRemote(signedTx); err != txpool.ErrNegativeValue { 434 t.Error("expected", txpool.ErrNegativeValue, "got", err) 435 } 436 } 437 438 func TestTipAboveFeeCap(t *testing.T) { 439 t.Parallel() 440 441 pool, key := setupPoolWithConfig(eip1559Config) 442 defer pool.Close() 443 444 tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key) 445 446 if err := pool.addRemote(tx); err != core.ErrTipAboveFeeCap { 447 t.Error("expected", core.ErrTipAboveFeeCap, "got", err) 448 } 449 } 450 451 func TestVeryHighValues(t *testing.T) { 452 t.Parallel() 453 454 pool, key := setupPoolWithConfig(eip1559Config) 455 defer pool.Close() 456 457 veryBigNumber := big.NewInt(1) 458 veryBigNumber.Lsh(veryBigNumber, 300) 459 460 tx := dynamicFeeTx(0, 100, big.NewInt(1), veryBigNumber, key) 461 if err := pool.addRemote(tx); err != core.ErrTipVeryHigh { 462 t.Error("expected", core.ErrTipVeryHigh, "got", err) 463 } 464 465 tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key) 466 if err := pool.addRemote(tx2); err != core.ErrFeeCapVeryHigh { 467 t.Error("expected", core.ErrFeeCapVeryHigh, "got", err) 468 } 469 } 470 471 func TestChainFork(t *testing.T) { 472 t.Parallel() 473 474 pool, key := setupPool() 475 defer pool.Close() 476 477 addr := key.GetAddress() 478 resetState := func() { 479 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 480 statedb.AddBalance(addr, big.NewInt(100000000000000)) 481 482 pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) 483 <-pool.requestReset(nil, nil) 484 } 485 resetState() 486 487 tx := transaction(0, 100000, key) 488 if _, err := pool.add(tx, false); err != nil { 489 t.Error("didn't expect error", err) 490 } 491 pool.removeTx(tx.Hash(), true, true) 492 493 // reset the pool's internal state 494 resetState() 495 if _, err := pool.add(tx, false); err != nil { 496 t.Error("didn't expect error", err) 497 } 498 } 499 500 func TestDoubleNonce(t *testing.T) { 501 t.Parallel() 502 503 pool, key := setupPool() 504 defer pool.Close() 505 506 addr := key.GetAddress() 507 resetState := func() { 508 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 509 statedb.AddBalance(addr, big.NewInt(100000000000000)) 510 511 pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) 512 <-pool.requestReset(nil, nil) 513 } 514 resetState() 515 516 signer := types.ShanghaiSigner{ChainId: big.NewInt(1)} 517 tx1, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{Nonce: 0, To: &common.Address{}, Value: big.NewInt(100), Gas: 100000, GasFeeCap: big.NewInt(1), GasTipCap: big.NewInt(1), Data: nil}), signer, key) 518 tx2, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{Nonce: 0, To: &common.Address{}, Value: big.NewInt(100), Gas: 1000000, GasFeeCap: big.NewInt(2), GasTipCap: big.NewInt(2), Data: nil}), signer, key) 519 tx3, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{Nonce: 0, To: &common.Address{}, Value: big.NewInt(100), Gas: 1000000, GasFeeCap: big.NewInt(1), GasTipCap: big.NewInt(1), Data: nil}), signer, key) 520 521 // Add the first two transaction, ensure higher priced stays only 522 if replace, err := pool.add(tx1, false); err != nil || replace { 523 t.Errorf("first transaction insert failed (%v) or reported replacement (%v)", err, replace) 524 } 525 if replace, err := pool.add(tx2, false); err != nil || !replace { 526 t.Errorf("second transaction insert failed (%v) or not reported replacement (%v)", err, replace) 527 } 528 <-pool.requestPromoteExecutables(newAccountSet(signer, addr)) 529 if pool.pending[addr].Len() != 1 { 530 t.Error("expected 1 pending transactions, got", pool.pending[addr].Len()) 531 } 532 if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() { 533 t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash()) 534 } 535 536 // Add the third transaction and ensure it's not saved (smaller price) 537 pool.add(tx3, false) 538 <-pool.requestPromoteExecutables(newAccountSet(signer, addr)) 539 if pool.pending[addr].Len() != 1 { 540 t.Error("expected 1 pending transactions, got", pool.pending[addr].Len()) 541 } 542 if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() { 543 t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash()) 544 } 545 // Ensure the total transaction count is correct 546 if pool.all.Count() != 1 { 547 t.Error("expected 1 total transactions, got", pool.all.Count()) 548 } 549 } 550 551 func TestMissingNonce(t *testing.T) { 552 t.Parallel() 553 554 pool, key := setupPool() 555 defer pool.Close() 556 557 addr := key.GetAddress() 558 testAddBalance(pool, addr, big.NewInt(100000000000000)) 559 tx := transaction(1, 100000, key) 560 if _, err := pool.add(tx, false); err != nil { 561 t.Error("didn't expect error", err) 562 } 563 if len(pool.pending) != 0 { 564 t.Error("expected 0 pending transactions, got", len(pool.pending)) 565 } 566 if pool.queue[addr].Len() != 1 { 567 t.Error("expected 1 queued transaction, got", pool.queue[addr].Len()) 568 } 569 if pool.all.Count() != 1 { 570 t.Error("expected 1 total transactions, got", pool.all.Count()) 571 } 572 } 573 574 func TestNonceRecovery(t *testing.T) { 575 t.Parallel() 576 577 const n = 10 578 pool, key := setupPool() 579 defer pool.Close() 580 581 addr := key.GetAddress() 582 testSetNonce(pool, addr, n) 583 testAddBalance(pool, addr, big.NewInt(100000000000000)) 584 <-pool.requestReset(nil, nil) 585 586 tx := transaction(n, 100000, key) 587 if err := pool.addRemote(tx); err != nil { 588 t.Error(err) 589 } 590 // simulate some weird re-order of transactions and missing nonce(s) 591 testSetNonce(pool, addr, n-1) 592 <-pool.requestReset(nil, nil) 593 if fn := pool.Nonce(addr); fn != n-1 { 594 t.Errorf("expected nonce to be %d, got %d", n-1, fn) 595 } 596 } 597 598 // Tests that if an account runs out of funds, any pending and queued transactions 599 // are dropped. 600 func TestDropping(t *testing.T) { 601 t.Parallel() 602 603 // Create a test account and fund it 604 pool, key := setupPool() 605 defer pool.Close() 606 607 account := key.GetAddress() 608 testAddBalance(pool, account, big.NewInt(1000)) 609 610 // Add some pending and some queued transactions 611 var ( 612 tx0 = transaction(0, 100, key) 613 tx1 = transaction(1, 200, key) 614 tx2 = transaction(2, 300, key) 615 tx10 = transaction(10, 100, key) 616 tx11 = transaction(11, 200, key) 617 tx12 = transaction(12, 300, key) 618 ) 619 pool.all.Add(tx0, false) 620 pool.priced.Put(tx0, false) 621 pool.promoteTx(account, tx0.Hash(), tx0) 622 623 pool.all.Add(tx1, false) 624 pool.priced.Put(tx1, false) 625 pool.promoteTx(account, tx1.Hash(), tx1) 626 627 pool.all.Add(tx2, false) 628 pool.priced.Put(tx2, false) 629 pool.promoteTx(account, tx2.Hash(), tx2) 630 631 pool.enqueueTx(tx10.Hash(), tx10, false, true) 632 pool.enqueueTx(tx11.Hash(), tx11, false, true) 633 pool.enqueueTx(tx12.Hash(), tx12, false, true) 634 635 // Check that pre and post validations leave the pool as is 636 if pool.pending[account].Len() != 3 { 637 t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3) 638 } 639 if pool.queue[account].Len() != 3 { 640 t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3) 641 } 642 if pool.all.Count() != 6 { 643 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6) 644 } 645 <-pool.requestReset(nil, nil) 646 if pool.pending[account].Len() != 3 { 647 t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3) 648 } 649 if pool.queue[account].Len() != 3 { 650 t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3) 651 } 652 if pool.all.Count() != 6 { 653 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6) 654 } 655 // Reduce the balance of the account, and check that invalidated transactions are dropped 656 testAddBalance(pool, account, big.NewInt(-650)) 657 <-pool.requestReset(nil, nil) 658 659 if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok { 660 t.Errorf("funded pending transaction missing: %v", tx0) 661 } 662 if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; !ok { 663 t.Errorf("funded pending transaction missing: %v", tx0) 664 } 665 if _, ok := pool.pending[account].txs.items[tx2.Nonce()]; ok { 666 t.Errorf("out-of-fund pending transaction present: %v", tx1) 667 } 668 if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok { 669 t.Errorf("funded queued transaction missing: %v", tx10) 670 } 671 if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; !ok { 672 t.Errorf("funded queued transaction missing: %v", tx10) 673 } 674 if _, ok := pool.queue[account].txs.items[tx12.Nonce()]; ok { 675 t.Errorf("out-of-fund queued transaction present: %v", tx11) 676 } 677 if pool.all.Count() != 4 { 678 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 4) 679 } 680 // Reduce the block gas limit, check that invalidated transactions are dropped 681 pool.chain.(*testBlockChain).gasLimit.Store(100) 682 <-pool.requestReset(nil, nil) 683 684 if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok { 685 t.Errorf("funded pending transaction missing: %v", tx0) 686 } 687 if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok { 688 t.Errorf("over-gased pending transaction present: %v", tx1) 689 } 690 if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok { 691 t.Errorf("funded queued transaction missing: %v", tx10) 692 } 693 if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok { 694 t.Errorf("over-gased queued transaction present: %v", tx11) 695 } 696 if pool.all.Count() != 2 { 697 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 2) 698 } 699 } 700 701 // Tests that if a transaction is dropped from the current pending pool (e.g. out 702 // of fund), all consecutive (still valid, but not executable) transactions are 703 // postponed back into the future queue to prevent broadcasting them. 704 func TestPostponing(t *testing.T) { 705 t.Parallel() 706 707 // Create the pool to test the postponing with 708 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 709 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 710 711 pool := New(testTxPoolConfig, blockchain) 712 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 713 defer pool.Close() 714 715 // Create two test accounts to produce different gap profiles with 716 keys := make([]*dilithium.Dilithium, 2) 717 accs := make([]common.Address, len(keys)) 718 719 for i := 0; i < len(keys); i++ { 720 keys[i], _ = crypto.GenerateDilithiumKey() 721 accs[i] = keys[i].GetAddress() 722 723 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(50100)) 724 } 725 // Add a batch consecutive pending transactions for validation 726 txs := []*types.Transaction{} 727 for i, key := range keys { 728 for j := 0; j < 100; j++ { 729 var tx *types.Transaction 730 if (i+j)%2 == 0 { 731 tx = transaction(uint64(j), 25000, key) 732 } else { 733 tx = transaction(uint64(j), 50000, key) 734 } 735 txs = append(txs, tx) 736 } 737 } 738 for i, err := range pool.addRemotesSync(txs) { 739 if err != nil { 740 t.Fatalf("tx %d: failed to add transactions: %v", i, err) 741 } 742 } 743 // Check that pre and post validations leave the pool as is 744 if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) { 745 t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs)) 746 } 747 if len(pool.queue) != 0 { 748 t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0) 749 } 750 if pool.all.Count() != len(txs) { 751 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs)) 752 } 753 <-pool.requestReset(nil, nil) 754 if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) { 755 t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs)) 756 } 757 if len(pool.queue) != 0 { 758 t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0) 759 } 760 if pool.all.Count() != len(txs) { 761 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs)) 762 } 763 // Reduce the balance of the account, and check that transactions are reorganised 764 for _, addr := range accs { 765 testAddBalance(pool, addr, big.NewInt(-1)) 766 } 767 <-pool.requestReset(nil, nil) 768 769 // The first account's first transaction remains valid, check that subsequent 770 // ones are either filtered out, or queued up for later. 771 if _, ok := pool.pending[accs[0]].txs.items[txs[0].Nonce()]; !ok { 772 t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txs[0]) 773 } 774 if _, ok := pool.queue[accs[0]].txs.items[txs[0].Nonce()]; ok { 775 t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txs[0]) 776 } 777 for i, tx := range txs[1:100] { 778 if i%2 == 1 { 779 if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok { 780 t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx) 781 } 782 if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; !ok { 783 t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx) 784 } 785 } else { 786 if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok { 787 t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx) 788 } 789 if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; ok { 790 t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx) 791 } 792 } 793 } 794 // The second account's first transaction got invalid, check that all transactions 795 // are either filtered out, or queued up for later. 796 if pool.pending[accs[1]] != nil { 797 t.Errorf("invalidated account still has pending transactions") 798 } 799 for i, tx := range txs[100:] { 800 if i%2 == 1 { 801 if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; !ok { 802 t.Errorf("tx %d: valid but future transaction missing from future queue: %v", 100+i, tx) 803 } 804 } else { 805 if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; ok { 806 t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", 100+i, tx) 807 } 808 } 809 } 810 if pool.all.Count() != len(txs)/2 { 811 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs)/2) 812 } 813 } 814 815 // Tests that if the transaction pool has both executable and non-executable 816 // transactions from an origin account, filling the nonce gap moves all queued 817 // ones into the pending pool. 818 func TestGapFilling(t *testing.T) { 819 t.Parallel() 820 821 // Create a test account and fund it 822 pool, key := setupPool() 823 defer pool.Close() 824 825 account := key.GetAddress() 826 testAddBalance(pool, account, big.NewInt(1000000)) 827 828 // Keep track of transaction events to ensure all executables get announced 829 events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue+5) 830 sub := pool.txFeed.Subscribe(events) 831 defer sub.Unsubscribe() 832 833 // Create a pending and a queued transaction with a nonce-gap in between 834 pool.addRemotesSync([]*types.Transaction{ 835 transaction(0, 100000, key), 836 transaction(2, 100000, key), 837 }) 838 pending, queued := pool.Stats() 839 if pending != 1 { 840 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1) 841 } 842 if queued != 1 { 843 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) 844 } 845 if err := validateEvents(events, 1); err != nil { 846 t.Fatalf("original event firing failed: %v", err) 847 } 848 if err := validatePoolInternals(pool); err != nil { 849 t.Fatalf("pool internal state corrupted: %v", err) 850 } 851 // Fill the nonce gap and ensure all transactions become pending 852 if err := pool.addRemoteSync(transaction(1, 100000, key)); err != nil { 853 t.Fatalf("failed to add gapped transaction: %v", err) 854 } 855 pending, queued = pool.Stats() 856 if pending != 3 { 857 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) 858 } 859 if queued != 0 { 860 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 861 } 862 if err := validateEvents(events, 2); err != nil { 863 t.Fatalf("gap-filling event firing failed: %v", err) 864 } 865 if err := validatePoolInternals(pool); err != nil { 866 t.Fatalf("pool internal state corrupted: %v", err) 867 } 868 } 869 870 // Tests that if the transaction count belonging to a single account goes above 871 // some threshold, the higher transactions are dropped to prevent DOS attacks. 872 func TestQueueAccountLimiting(t *testing.T) { 873 t.Parallel() 874 875 // Create a test account and fund it 876 pool, key := setupPool() 877 defer pool.Close() 878 879 account := key.GetAddress() 880 testAddBalance(pool, account, big.NewInt(1000000)) 881 882 // Keep queuing up transactions and make sure all above a limit are dropped 883 for i := uint64(1); i <= testTxPoolConfig.AccountQueue+5; i++ { 884 if err := pool.addRemoteSync(transaction(i, 100000, key)); err != nil { 885 t.Fatalf("tx %d: failed to add transaction: %v", i, err) 886 } 887 if len(pool.pending) != 0 { 888 t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0) 889 } 890 if i <= testTxPoolConfig.AccountQueue { 891 if pool.queue[account].Len() != int(i) { 892 t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i) 893 } 894 } else { 895 if pool.queue[account].Len() != int(testTxPoolConfig.AccountQueue) { 896 t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), testTxPoolConfig.AccountQueue) 897 } 898 } 899 } 900 if pool.all.Count() != int(testTxPoolConfig.AccountQueue) { 901 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), testTxPoolConfig.AccountQueue) 902 } 903 } 904 905 // Tests that if the transaction count belonging to multiple accounts go above 906 // some threshold, the higher transactions are dropped to prevent DOS attacks. 907 // 908 // This logic should not hold for local transactions, unless the local tracking 909 // mechanism is disabled. 910 func TestQueueGlobalLimiting(t *testing.T) { 911 testQueueGlobalLimiting(t, false) 912 } 913 func TestQueueGlobalLimitingNoLocals(t *testing.T) { 914 testQueueGlobalLimiting(t, true) 915 } 916 917 func testQueueGlobalLimiting(t *testing.T, nolocals bool) { 918 t.Parallel() 919 920 // Create the pool to test the limit enforcement with 921 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 922 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 923 924 config := testTxPoolConfig 925 config.NoLocals = nolocals 926 config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible) 927 928 pool := New(config, blockchain) 929 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 930 defer pool.Close() 931 932 // Create a number of test accounts and fund them (last one will be the local) 933 keys := make([]*dilithium.Dilithium, 5) 934 for i := 0; i < len(keys); i++ { 935 keys[i], _ = crypto.GenerateDilithiumKey() 936 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(1000000)) 937 } 938 local := keys[len(keys)-1] 939 940 // Generate and queue a batch of transactions 941 nonces := make(map[common.Address]uint64) 942 943 txs := make(types.Transactions, 0, 3*config.GlobalQueue) 944 for len(txs) < cap(txs) { 945 key := keys[rand.Intn(len(keys)-1)] // skip adding transactions with the local account 946 addr := key.GetAddress() 947 948 txs = append(txs, transaction(nonces[addr]+1, 100000, key)) 949 nonces[addr]++ 950 } 951 // Import the batch and verify that limits have been enforced 952 pool.addRemotesSync(txs) 953 954 queued := 0 955 for addr, list := range pool.queue { 956 if list.Len() > int(config.AccountQueue) { 957 t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), config.AccountQueue) 958 } 959 queued += list.Len() 960 } 961 if queued > int(config.GlobalQueue) { 962 t.Fatalf("total transactions overflow allowance: %d > %d", queued, config.GlobalQueue) 963 } 964 // Generate a batch of transactions from the local account and import them 965 txs = txs[:0] 966 for i := uint64(0); i < 3*config.GlobalQueue; i++ { 967 txs = append(txs, transaction(i+1, 100000, local)) 968 } 969 pool.addLocals(txs) 970 971 // If locals are disabled, the previous eviction algorithm should apply here too 972 if nolocals { 973 queued := 0 974 for addr, list := range pool.queue { 975 if list.Len() > int(config.AccountQueue) { 976 t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), config.AccountQueue) 977 } 978 queued += list.Len() 979 } 980 if queued > int(config.GlobalQueue) { 981 t.Fatalf("total transactions overflow allowance: %d > %d", queued, config.GlobalQueue) 982 } 983 } else { 984 // Local exemptions are enabled, make sure the local account owned the queue 985 if len(pool.queue) != 1 { 986 t.Errorf("multiple accounts in queue: have %v, want %v", len(pool.queue), 1) 987 } 988 // Also ensure no local transactions are ever dropped, even if above global limits 989 if queued := pool.queue[local.GetAddress()].Len(); uint64(queued) != 3*config.GlobalQueue { 990 t.Fatalf("local account queued transaction count mismatch: have %v, want %v", queued, 3*config.GlobalQueue) 991 } 992 } 993 } 994 995 // Tests that if an account remains idle for a prolonged amount of time, any 996 // non-executable transactions queued up are dropped to prevent wasting resources 997 // on shuffling them around. 998 // 999 // This logic should not hold for local transactions, unless the local tracking 1000 // mechanism is disabled. 1001 func TestQueueTimeLimiting(t *testing.T) { 1002 testQueueTimeLimiting(t, false) 1003 } 1004 1005 func TestQueueTimeLimitingNoLocals(t *testing.T) { 1006 testQueueTimeLimiting(t, true) 1007 } 1008 1009 func testQueueTimeLimiting(t *testing.T, nolocals bool) { 1010 // Reduce the eviction interval to a testable amount 1011 defer func(old time.Duration) { evictionInterval = old }(evictionInterval) 1012 evictionInterval = time.Millisecond * 100 1013 1014 // Create the pool to test the non-expiration enforcement 1015 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1016 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 1017 1018 config := testTxPoolConfig 1019 config.Lifetime = time.Second 1020 config.NoLocals = nolocals 1021 1022 pool := New(config, blockchain) 1023 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1024 defer pool.Close() 1025 1026 // Create two test accounts to ensure remotes expire but locals do not 1027 local, _ := crypto.GenerateDilithiumKey() 1028 remote, _ := crypto.GenerateDilithiumKey() 1029 1030 testAddBalance(pool, local.GetAddress(), big.NewInt(1000000000)) 1031 testAddBalance(pool, remote.GetAddress(), big.NewInt(1000000000)) 1032 1033 // Add the two transactions and ensure they both are queued up 1034 if err := pool.addLocal(dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), local)); err != nil { 1035 t.Fatalf("failed to add local transaction: %v", err) 1036 } 1037 if err := pool.addRemote(dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), remote)); err != nil { 1038 t.Fatalf("failed to add remote transaction: %v", err) 1039 } 1040 pending, queued := pool.Stats() 1041 if pending != 0 { 1042 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) 1043 } 1044 if queued != 2 { 1045 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) 1046 } 1047 if err := validatePoolInternals(pool); err != nil { 1048 t.Fatalf("pool internal state corrupted: %v", err) 1049 } 1050 1051 // Allow the eviction interval to run 1052 time.Sleep(2 * evictionInterval) 1053 1054 // Transactions should not be evicted from the queue yet since lifetime duration has not passed 1055 pending, queued = pool.Stats() 1056 if pending != 0 { 1057 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) 1058 } 1059 if queued != 2 { 1060 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) 1061 } 1062 if err := validatePoolInternals(pool); err != nil { 1063 t.Fatalf("pool internal state corrupted: %v", err) 1064 } 1065 1066 // Wait a bit for eviction to run and clean up any leftovers, and ensure only the local remains 1067 time.Sleep(2 * config.Lifetime) 1068 1069 pending, queued = pool.Stats() 1070 if pending != 0 { 1071 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) 1072 } 1073 if nolocals { 1074 if queued != 0 { 1075 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 1076 } 1077 } else { 1078 if queued != 1 { 1079 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) 1080 } 1081 } 1082 if err := validatePoolInternals(pool); err != nil { 1083 t.Fatalf("pool internal state corrupted: %v", err) 1084 } 1085 1086 // remove current transactions and increase nonce to prepare for a reset and cleanup 1087 statedb.SetNonce(remote.GetAddress(), 2) 1088 statedb.SetNonce(local.GetAddress(), 2) 1089 <-pool.requestReset(nil, nil) 1090 1091 // make sure queue, pending are cleared 1092 pending, queued = pool.Stats() 1093 if pending != 0 { 1094 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) 1095 } 1096 if queued != 0 { 1097 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 1098 } 1099 if err := validatePoolInternals(pool); err != nil { 1100 t.Fatalf("pool internal state corrupted: %v", err) 1101 } 1102 1103 // Queue gapped transactions 1104 if err := pool.addLocal(dynamicFeeTx(4, 100000, big.NewInt(1), big.NewInt(1), local)); err != nil { 1105 t.Fatalf("failed to add remote transaction: %v", err) 1106 } 1107 if err := pool.addRemoteSync(dynamicFeeTx(4, 100000, big.NewInt(1), big.NewInt(1), remote)); err != nil { 1108 t.Fatalf("failed to add remote transaction: %v", err) 1109 } 1110 time.Sleep(5 * evictionInterval) // A half lifetime pass 1111 1112 // Queue executable transactions, the life cycle should be restarted. 1113 if err := pool.addLocal(dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), local)); err != nil { 1114 t.Fatalf("failed to add remote transaction: %v", err) 1115 } 1116 if err := pool.addRemoteSync(dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), remote)); err != nil { 1117 t.Fatalf("failed to add remote transaction: %v", err) 1118 } 1119 time.Sleep(6 * evictionInterval) 1120 1121 // All gapped transactions shouldn't be kicked out 1122 pending, queued = pool.Stats() 1123 if pending != 2 { 1124 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) 1125 } 1126 if queued != 2 { 1127 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) 1128 } 1129 if err := validatePoolInternals(pool); err != nil { 1130 t.Fatalf("pool internal state corrupted: %v", err) 1131 } 1132 1133 // The whole life time pass after last promotion, kick out stale transactions 1134 time.Sleep(2 * config.Lifetime) 1135 pending, queued = pool.Stats() 1136 if pending != 2 { 1137 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) 1138 } 1139 if nolocals { 1140 if queued != 0 { 1141 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 1142 } 1143 } else { 1144 if queued != 1 { 1145 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) 1146 } 1147 } 1148 if err := validatePoolInternals(pool); err != nil { 1149 t.Fatalf("pool internal state corrupted: %v", err) 1150 } 1151 } 1152 1153 // Tests that even if the transaction count belonging to a single account goes 1154 // above some threshold, as long as the transactions are executable, they are 1155 // accepted. 1156 func TestPendingLimiting(t *testing.T) { 1157 t.Parallel() 1158 1159 // Create a test account and fund it 1160 pool, key := setupPool() 1161 defer pool.Close() 1162 1163 account := key.GetAddress() 1164 testAddBalance(pool, account, big.NewInt(1000000000000)) 1165 1166 // Keep track of transaction events to ensure all executables get announced 1167 events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue+5) 1168 sub := pool.txFeed.Subscribe(events) 1169 defer sub.Unsubscribe() 1170 1171 // Keep queuing up transactions and make sure all above a limit are dropped 1172 for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ { 1173 if err := pool.addRemoteSync(transaction(i, 100000, key)); err != nil { 1174 t.Fatalf("tx %d: failed to add transaction: %v", i, err) 1175 } 1176 if pool.pending[account].Len() != int(i)+1 { 1177 t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, pool.pending[account].Len(), i+1) 1178 } 1179 if len(pool.queue) != 0 { 1180 t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0) 1181 } 1182 } 1183 if pool.all.Count() != int(testTxPoolConfig.AccountQueue+5) { 1184 t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), testTxPoolConfig.AccountQueue+5) 1185 } 1186 if err := validateEvents(events, int(testTxPoolConfig.AccountQueue+5)); err != nil { 1187 t.Fatalf("event firing failed: %v", err) 1188 } 1189 if err := validatePoolInternals(pool); err != nil { 1190 t.Fatalf("pool internal state corrupted: %v", err) 1191 } 1192 } 1193 1194 // Tests that if the transaction count belonging to multiple accounts go above 1195 // some hard threshold, the higher transactions are dropped to prevent DOS 1196 // attacks. 1197 func TestPendingGlobalLimiting(t *testing.T) { 1198 t.Parallel() 1199 1200 // Create the pool to test the limit enforcement with 1201 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1202 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 1203 1204 config := testTxPoolConfig 1205 config.GlobalSlots = config.AccountSlots * 10 1206 1207 pool := New(config, blockchain) 1208 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1209 defer pool.Close() 1210 1211 // Create a number of test accounts and fund them 1212 keys := make([]*dilithium.Dilithium, 5) 1213 for i := 0; i < len(keys); i++ { 1214 keys[i], _ = crypto.GenerateDilithiumKey() 1215 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(1000000)) 1216 } 1217 // Generate and queue a batch of transactions 1218 nonces := make(map[common.Address]uint64) 1219 1220 txs := types.Transactions{} 1221 for _, key := range keys { 1222 addr := key.GetAddress() 1223 for j := 0; j < int(config.GlobalSlots)/len(keys)*2; j++ { 1224 txs = append(txs, transaction(nonces[addr], 100000, key)) 1225 nonces[addr]++ 1226 } 1227 } 1228 // Import the batch and verify that limits have been enforced 1229 pool.addRemotesSync(txs) 1230 1231 pending := 0 1232 for _, list := range pool.pending { 1233 pending += list.Len() 1234 } 1235 if pending > int(config.GlobalSlots) { 1236 t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, config.GlobalSlots) 1237 } 1238 if err := validatePoolInternals(pool); err != nil { 1239 t.Fatalf("pool internal state corrupted: %v", err) 1240 } 1241 } 1242 1243 // Test the limit on transaction size is enforced correctly. 1244 // This test verifies every transaction having allowed size 1245 // is added to the pool, and longer transactions are rejected. 1246 func TestAllowedTxSize(t *testing.T) { 1247 t.Parallel() 1248 1249 // Create a test account and fund it 1250 pool, key := setupPool() 1251 defer pool.Close() 1252 1253 account := key.GetAddress() 1254 testAddBalance(pool, account, big.NewInt(1000000000)) 1255 1256 // Compute maximal data size for transactions (lower bound). 1257 // 1258 // It is assumed the fields in the transaction (except of the data) are: 1259 // - nonce <= 32 bytes 1260 // - gasTip <= 32 bytes 1261 // - gasLimit <= 32 bytes 1262 // - recipient == 20 bytes 1263 // - value <= 32 bytes 1264 // - signature == 4595 bytes 1265 // - publicKey == 2592 bytes 1266 // All those fields are summed up to at most 7335 bytes. 1267 baseSize := uint64(7335) 1268 dataSize := txMaxSize - baseSize 1269 // Try adding a transaction with maximal allowed size 1270 tx := dynamicFeeDataTx(0, pool.currentHead.Load().GasLimit, big.NewInt(1), big.NewInt(1), key, dataSize) 1271 if err := pool.addRemoteSync(tx); err != nil { 1272 t.Fatalf("failed to add transaction of size %d, close to maximal: %v", int(tx.Size()), err) 1273 } 1274 // Try adding a transaction with random allowed size 1275 if err := pool.addRemoteSync(dynamicFeeDataTx(1, pool.currentHead.Load().GasLimit, big.NewInt(1), big.NewInt(1), key, uint64(rand.Intn(int(dataSize))))); err != nil { 1276 t.Fatalf("failed to add transaction of random allowed size: %v", err) 1277 } 1278 // Try adding a transaction of minimal not allowed size 1279 if err := pool.addRemoteSync(dynamicFeeDataTx(2, pool.currentHead.Load().GasLimit, big.NewInt(1), big.NewInt(1), key, txMaxSize)); err == nil { 1280 t.Fatalf("expected rejection on slightly oversize transaction") 1281 } 1282 // Try adding a transaction of random not allowed size 1283 if err := pool.addRemoteSync(dynamicFeeDataTx(2, pool.currentHead.Load().GasLimit, big.NewInt(1), big.NewInt(1), key, dataSize+1+uint64(rand.Intn(10*txMaxSize)))); err == nil { 1284 t.Fatalf("expected rejection on oversize transaction") 1285 } 1286 // Run some sanity checks on the pool internals 1287 pending, queued := pool.Stats() 1288 if pending != 2 { 1289 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) 1290 } 1291 if queued != 0 { 1292 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 1293 } 1294 if err := validatePoolInternals(pool); err != nil { 1295 t.Fatalf("pool internal state corrupted: %v", err) 1296 } 1297 } 1298 1299 // Tests that if transactions start being capped, transactions are also removed from 'all' 1300 func TestCapClearsFromAll(t *testing.T) { 1301 t.Parallel() 1302 1303 // Create the pool to test the limit enforcement with 1304 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1305 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 1306 1307 config := testTxPoolConfig 1308 config.AccountSlots = 2 1309 config.AccountQueue = 2 1310 config.GlobalSlots = 8 1311 1312 pool := New(config, blockchain) 1313 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1314 defer pool.Close() 1315 1316 // Create a number of test accounts and fund them 1317 key, _ := crypto.GenerateDilithiumKey() 1318 addr := key.GetAddress() 1319 testAddBalance(pool, addr, big.NewInt(1000000)) 1320 1321 txs := types.Transactions{} 1322 for j := 0; j < int(config.GlobalSlots)*2; j++ { 1323 txs = append(txs, transaction(uint64(j), 100000, key)) 1324 } 1325 // Import the batch and verify that limits have been enforced 1326 pool.addRemotes(txs) 1327 if err := validatePoolInternals(pool); err != nil { 1328 t.Fatalf("pool internal state corrupted: %v", err) 1329 } 1330 } 1331 1332 // Tests that if the transaction count belonging to multiple accounts go above 1333 // some hard threshold, if they are under the minimum guaranteed slot count then 1334 // the transactions are still kept. 1335 func TestPendingMinimumAllowance(t *testing.T) { 1336 t.Parallel() 1337 1338 // Create the pool to test the limit enforcement with 1339 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1340 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 1341 1342 config := testTxPoolConfig 1343 config.GlobalSlots = 1 1344 1345 pool := New(config, blockchain) 1346 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1347 defer pool.Close() 1348 1349 // Create a number of test accounts and fund them 1350 keys := make([]*dilithium.Dilithium, 5) 1351 for i := 0; i < len(keys); i++ { 1352 keys[i], _ = crypto.GenerateDilithiumKey() 1353 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(1000000)) 1354 } 1355 // Generate and queue a batch of transactions 1356 nonces := make(map[common.Address]uint64) 1357 1358 txs := types.Transactions{} 1359 for _, key := range keys { 1360 addr := key.GetAddress() 1361 for j := 0; j < int(config.AccountSlots)*2; j++ { 1362 txs = append(txs, transaction(nonces[addr], 100000, key)) 1363 nonces[addr]++ 1364 } 1365 } 1366 // Import the batch and verify that limits have been enforced 1367 pool.addRemotesSync(txs) 1368 1369 for addr, list := range pool.pending { 1370 if list.Len() != int(config.AccountSlots) { 1371 t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), config.AccountSlots) 1372 } 1373 } 1374 if err := validatePoolInternals(pool); err != nil { 1375 t.Fatalf("pool internal state corrupted: %v", err) 1376 } 1377 } 1378 1379 func TestMinGasPriceEnforced(t *testing.T) { 1380 t.Parallel() 1381 1382 // Create the pool to test the pricing enforcement with 1383 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1384 blockchain := newTestBlockChain(eip1559Config, 10000000, statedb, new(event.Feed)) 1385 1386 txPoolConfig := DefaultConfig 1387 txPoolConfig.NoLocals = true 1388 pool := New(txPoolConfig, blockchain) 1389 pool.Init(new(big.Int).SetUint64(txPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1390 defer pool.Close() 1391 1392 key, _ := crypto.GenerateDilithiumKey() 1393 testAddBalance(pool, common.Address(key.GetAddress()), big.NewInt(1000000)) 1394 1395 tx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(2), key) 1396 pool.SetGasTip(big.NewInt(tx.GasPrice().Int64() + 1)) 1397 1398 if err := pool.addLocal(tx); !errors.Is(err, txpool.ErrUnderpriced) { 1399 t.Fatalf("Min tip not enforced") 1400 } 1401 1402 if err := pool.Add([]*types.Transaction{tx}, true, false)[0]; !errors.Is(err, txpool.ErrUnderpriced) { 1403 t.Fatalf("Min tip not enforced") 1404 } 1405 1406 tx = dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), key) 1407 pool.SetGasTip(big.NewInt(tx.GasTipCap().Int64() + 1)) 1408 1409 if err := pool.addLocal(tx); !errors.Is(err, txpool.ErrUnderpriced) { 1410 t.Fatalf("Min tip not enforced") 1411 } 1412 1413 if err := pool.Add([]*types.Transaction{tx}, true, false)[0]; !errors.Is(err, txpool.ErrUnderpriced) { 1414 t.Fatalf("Min tip not enforced") 1415 } 1416 // Make sure the tx is accepted if locals are enabled 1417 pool.config.NoLocals = false 1418 if err := pool.Add([]*types.Transaction{tx}, true, false)[0]; err != nil { 1419 t.Fatalf("Min tip enforced with locals enabled, error: %v", err) 1420 } 1421 } 1422 1423 // Tests that setting the transaction pool gas price to a higher value correctly 1424 // discards everything cheaper than that and moves any 1425 // gapped transactions back from the pending pool to the queue. 1426 // 1427 // Note, local transactions are never allowed to be dropped. 1428 func TestRepricingDynamicFee(t *testing.T) { 1429 t.Parallel() 1430 1431 // Create the pool to test the pricing enforcement with 1432 pool, _ := setupPoolWithConfig(eip1559Config) 1433 defer pool.Close() 1434 1435 // Keep track of transaction events to ensure all executables get announced 1436 events := make(chan core.NewTxsEvent, 32) 1437 sub := pool.txFeed.Subscribe(events) 1438 defer sub.Unsubscribe() 1439 1440 // Create a number of test accounts and fund them 1441 keys := make([]*dilithium.Dilithium, 4) 1442 for i := 0; i < len(keys); i++ { 1443 keys[i], _ = crypto.GenerateDilithiumKey() 1444 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(1000000)) 1445 } 1446 // Generate and queue a batch of transactions, both pending and queued 1447 txs := types.Transactions{} 1448 1449 txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(2), keys[0])) 1450 txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(2), big.NewInt(2), keys[0])) 1451 txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), keys[0])) 1452 1453 txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1])) 1454 txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(3), big.NewInt(2), keys[1])) 1455 txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(3), big.NewInt(2), keys[1])) 1456 1457 txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(2), keys[2])) 1458 txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2])) 1459 txs = append(txs, dynamicFeeTx(3, 100000, big.NewInt(2), big.NewInt(2), keys[2])) 1460 1461 ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[3]) 1462 1463 // Import the batch and that both pending and queued transactions match up 1464 pool.addRemotesSync(txs) 1465 pool.addLocal(ltx) 1466 1467 pending, queued := pool.Stats() 1468 if pending != 7 { 1469 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7) 1470 } 1471 if queued != 3 { 1472 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3) 1473 } 1474 if err := validateEvents(events, 7); err != nil { 1475 t.Fatalf("original event firing failed: %v", err) 1476 } 1477 if err := validatePoolInternals(pool); err != nil { 1478 t.Fatalf("pool internal state corrupted: %v", err) 1479 } 1480 // Reprice the pool and check that underpriced transactions get dropped 1481 pool.SetGasTip(big.NewInt(2)) 1482 1483 pending, queued = pool.Stats() 1484 if pending != 2 { 1485 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) 1486 } 1487 if queued != 5 { 1488 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 5) 1489 } 1490 if err := validateEvents(events, 0); err != nil { 1491 t.Fatalf("reprice event firing failed: %v", err) 1492 } 1493 if err := validatePoolInternals(pool); err != nil { 1494 t.Fatalf("pool internal state corrupted: %v", err) 1495 } 1496 // Check that we can't add the old transactions back 1497 tx := dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), keys[0]) 1498 if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrUnderpriced) { 1499 t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) 1500 } 1501 tx = dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1]) 1502 if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrUnderpriced) { 1503 t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) 1504 } 1505 tx = dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2]) 1506 if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrUnderpriced) { 1507 t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) 1508 } 1509 if err := validateEvents(events, 0); err != nil { 1510 t.Fatalf("post-reprice event firing failed: %v", err) 1511 } 1512 if err := validatePoolInternals(pool); err != nil { 1513 t.Fatalf("pool internal state corrupted: %v", err) 1514 } 1515 // However we can add local underpriced transactions 1516 tx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), keys[3]) 1517 if err := pool.addLocal(tx); err != nil { 1518 t.Fatalf("failed to add underpriced local transaction: %v", err) 1519 } 1520 if pending, _ = pool.Stats(); pending != 3 { 1521 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) 1522 } 1523 if err := validateEvents(events, 1); err != nil { 1524 t.Fatalf("post-reprice local event firing failed: %v", err) 1525 } 1526 if err := validatePoolInternals(pool); err != nil { 1527 t.Fatalf("pool internal state corrupted: %v", err) 1528 } 1529 // And we can fill gaps with properly priced transactions 1530 tx = dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(2), keys[0]) 1531 if err := pool.addRemote(tx); err != nil { 1532 t.Fatalf("failed to add pending transaction: %v", err) 1533 } 1534 tx = dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[1]) 1535 if err := pool.addRemote(tx); err != nil { 1536 t.Fatalf("failed to add pending transaction: %v", err) 1537 } 1538 tx = dynamicFeeTx(2, 100000, big.NewInt(2), big.NewInt(2), keys[2]) 1539 if err := pool.addRemoteSync(tx); err != nil { 1540 t.Fatalf("failed to add queued transaction: %v", err) 1541 } 1542 if err := validateEvents(events, 5); err != nil { 1543 t.Fatalf("post-reprice event firing failed: %v", err) 1544 } 1545 if err := validatePoolInternals(pool); err != nil { 1546 t.Fatalf("pool internal state corrupted: %v", err) 1547 } 1548 } 1549 1550 // Tests that setting the transaction pool gas price to a higher value does not 1551 // remove local transactions. 1552 func TestRepricingKeepsLocals(t *testing.T) { 1553 t.Parallel() 1554 1555 // Create the pool to test the pricing enforcement with 1556 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1557 blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) 1558 1559 pool := New(testTxPoolConfig, blockchain) 1560 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1561 defer pool.Close() 1562 1563 // Create a number of test accounts and fund them 1564 keys := make([]*dilithium.Dilithium, 3) 1565 for i := 0; i < len(keys); i++ { 1566 keys[i], _ = crypto.GenerateDilithiumKey() 1567 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(100000*1000000)) 1568 } 1569 // Create transaction (both pending and queued) with a linearly growing gasprice 1570 for i := uint64(0); i < 500; i++ { 1571 // Add pending dynamic fee transaction. 1572 pendingTx := dynamicFeeTx(i, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1]) 1573 if err := pool.addLocal(pendingTx); err != nil { 1574 t.Fatal(err) 1575 } 1576 // Add queued dynamic fee transaction. 1577 queuedTx := dynamicFeeTx(i+501, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1]) 1578 if err := pool.addLocal(queuedTx); err != nil { 1579 t.Fatal(err) 1580 } 1581 } 1582 pending, queued := pool.Stats() 1583 expPending, expQueued := 500, 500 1584 validate := func() { 1585 pending, queued = pool.Stats() 1586 if pending != expPending { 1587 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, expPending) 1588 } 1589 if queued != expQueued { 1590 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, expQueued) 1591 } 1592 1593 if err := validatePoolInternals(pool); err != nil { 1594 t.Fatalf("pool internal state corrupted: %v", err) 1595 } 1596 } 1597 validate() 1598 1599 // Reprice the pool and check that nothing is dropped 1600 pool.SetGasTip(big.NewInt(2)) 1601 validate() 1602 1603 pool.SetGasTip(big.NewInt(2)) 1604 pool.SetGasTip(big.NewInt(4)) 1605 pool.SetGasTip(big.NewInt(8)) 1606 pool.SetGasTip(big.NewInt(100)) 1607 validate() 1608 } 1609 1610 // Tests that more expensive transactions push out cheap ones from the pool, but 1611 // without producing instability by creating gaps that start jumping transactions 1612 // back and forth between queued/pending. 1613 func TestStableUnderpricing(t *testing.T) { 1614 t.Parallel() 1615 1616 // Create the pool to test the pricing enforcement with 1617 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1618 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 1619 1620 config := testTxPoolConfig 1621 config.GlobalSlots = 128 1622 config.GlobalQueue = 0 1623 1624 pool := New(config, blockchain) 1625 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1626 defer pool.Close() 1627 1628 // Keep track of transaction events to ensure all executables get announced 1629 events := make(chan core.NewTxsEvent, 32) 1630 sub := pool.txFeed.Subscribe(events) 1631 defer sub.Unsubscribe() 1632 1633 // Create a number of test accounts and fund them 1634 keys := make([]*dilithium.Dilithium, 2) 1635 for i := 0; i < len(keys); i++ { 1636 keys[i], _ = crypto.GenerateDilithiumKey() 1637 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(1000000)) 1638 } 1639 // Fill up the entire queue with the same transaction price points 1640 txs := types.Transactions{} 1641 for i := uint64(0); i < config.GlobalSlots; i++ { 1642 txs = append(txs, dynamicFeeTx(i, 100000, big.NewInt(1), big.NewInt(1), keys[0])) 1643 } 1644 pool.addRemotesSync(txs) 1645 1646 pending, queued := pool.Stats() 1647 if pending != int(config.GlobalSlots) { 1648 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots) 1649 } 1650 if queued != 0 { 1651 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 1652 } 1653 if err := validateEvents(events, int(config.GlobalSlots)); err != nil { 1654 t.Fatalf("original event firing failed: %v", err) 1655 } 1656 if err := validatePoolInternals(pool); err != nil { 1657 t.Fatalf("pool internal state corrupted: %v", err) 1658 } 1659 // Ensure that adding high priced transactions drops a cheap, but doesn't produce a gap 1660 if err := pool.addRemoteSync(dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(3), keys[1])); err != nil { 1661 t.Fatalf("failed to add well priced transaction: %v", err) 1662 } 1663 pending, queued = pool.Stats() 1664 if pending != int(config.GlobalSlots) { 1665 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots) 1666 } 1667 if queued != 0 { 1668 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 1669 } 1670 if err := validateEvents(events, 1); err != nil { 1671 t.Fatalf("additional event firing failed: %v", err) 1672 } 1673 if err := validatePoolInternals(pool); err != nil { 1674 t.Fatalf("pool internal state corrupted: %v", err) 1675 } 1676 } 1677 1678 // Tests that when the pool reaches its global transaction limit, underpriced 1679 // transactions are gradually shifted out for more 1680 // expensive ones and any gapped pending transactions are moved into the queue. 1681 // 1682 // Note, local transactions are never allowed to be dropped. 1683 func TestUnderpricingDynamicFee(t *testing.T) { 1684 t.Parallel() 1685 1686 pool, _ := setupPoolWithConfig(eip1559Config) 1687 defer pool.Close() 1688 1689 pool.config.GlobalSlots = 2 1690 pool.config.GlobalQueue = 2 1691 1692 // Keep track of transaction events to ensure all executables get announced 1693 events := make(chan core.NewTxsEvent, 32) 1694 sub := pool.txFeed.Subscribe(events) 1695 defer sub.Unsubscribe() 1696 1697 // Create a number of test accounts and fund them 1698 keys := make([]*dilithium.Dilithium, 4) 1699 for i := 0; i < len(keys); i++ { 1700 keys[i], _ = dilithium.New() 1701 testAddBalance(pool, common.Address(keys[i].GetAddress()), big.NewInt(1000000)) 1702 } 1703 1704 // Generate and queue a batch of transactions, both pending and queued 1705 txs := types.Transactions{} 1706 1707 txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[0])) 1708 txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(2), keys[0])) 1709 txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(1), keys[1])) 1710 1711 ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[2]) 1712 1713 // Import the batch and that both pending and queued transactions match up 1714 pool.addRemotes(txs) // Pend K0:0, K0:1; Que K1:1 1715 pool.addLocal(ltx) // +K2:0 => Pend K0:0, K0:1, K2:0; Que K1:1 1716 1717 pending, queued := pool.Stats() 1718 if pending != 3 { 1719 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) 1720 } 1721 if queued != 1 { 1722 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) 1723 } 1724 if err := validateEvents(events, 3); err != nil { 1725 t.Fatalf("original event firing failed: %v", err) 1726 } 1727 if err := validatePoolInternals(pool); err != nil { 1728 t.Fatalf("pool internal state corrupted: %v", err) 1729 } 1730 1731 // Ensure that adding an underpriced transaction fails 1732 tx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1]) 1733 if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrUnderpriced) { // Pend K0:0, K0:1, K2:0; Que K1:1 1734 t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) 1735 } 1736 1737 // Ensure that adding high priced transactions drops cheap ones, but not own 1738 tx = dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(2), keys[1]) 1739 if err := pool.addRemote(tx); err != nil { // +K1:0, -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que - 1740 t.Fatalf("failed to add well priced transaction: %v", err) 1741 } 1742 1743 tx = dynamicFeeTx(1, 100000, big.NewInt(3), big.NewInt(3), keys[1]) 1744 if err := pool.addRemoteSync(tx); err != nil { // +K1:2, -K0:1 => Pend K0:0 K1:0, K2:0; Que K1:2 1745 t.Fatalf("failed to add well priced transaction: %v", err) 1746 } 1747 tx = dynamicFeeTx(2, 100000, big.NewInt(4), big.NewInt(1), keys[1]) 1748 if err := pool.addRemoteSync(tx); err != nil { // +K1:3, -K1:0 => Pend K0:0 K2:0; Que K1:2 K1:3 1749 t.Fatalf("failed to add well priced transaction: %v", err) 1750 } 1751 pending, queued = pool.Stats() 1752 if pending != 2 { 1753 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) 1754 } 1755 if queued != 2 { 1756 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) 1757 } 1758 if err := validateEvents(events, 2); err != nil { 1759 t.Fatalf("additional event firing failed: %v", err) 1760 } 1761 if err := validatePoolInternals(pool); err != nil { 1762 t.Fatalf("pool internal state corrupted: %v", err) 1763 } 1764 // Ensure that adding local transactions can push out even higher priced ones 1765 ltx = dynamicFeeTx(1, 100000, big.NewInt(0), big.NewInt(0), keys[2]) 1766 if err := pool.addLocal(ltx); err != nil { 1767 t.Fatalf("failed to append underpriced local transaction: %v", err) 1768 } 1769 ltx = dynamicFeeTx(0, 100000, big.NewInt(0), big.NewInt(0), keys[3]) 1770 if err := pool.addLocal(ltx); err != nil { 1771 t.Fatalf("failed to add new underpriced local transaction: %v", err) 1772 } 1773 pending, queued = pool.Stats() 1774 if pending != 3 { 1775 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) 1776 } 1777 if queued != 1 { 1778 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) 1779 } 1780 if err := validateEvents(events, 2); err != nil { 1781 t.Fatalf("local event firing failed: %v", err) 1782 } 1783 if err := validatePoolInternals(pool); err != nil { 1784 t.Fatalf("pool internal state corrupted: %v", err) 1785 } 1786 } 1787 1788 // Tests whether highest fee cap transaction is retained after a batch of high effective 1789 // tip transactions are added and vice versa 1790 func TestDualHeapEviction(t *testing.T) { 1791 t.Parallel() 1792 1793 pool, _ := setupPoolWithConfig(eip1559Config) 1794 defer pool.Close() 1795 1796 pool.config.GlobalSlots = 10 1797 pool.config.GlobalQueue = 10 1798 1799 var ( 1800 highTip, highCap *types.Transaction 1801 baseFee int 1802 ) 1803 1804 check := func(tx *types.Transaction, name string) { 1805 if pool.all.GetRemote(tx.Hash()) == nil { 1806 t.Fatalf("highest %s transaction evicted from the pool", name) 1807 } 1808 } 1809 1810 add := func(urgent bool) { 1811 for i := 0; i < 20; i++ { 1812 var tx *types.Transaction 1813 // Create a test accounts and fund it 1814 key, _ := crypto.GenerateDilithiumKey() 1815 testAddBalance(pool, key.GetAddress(), big.NewInt(1000000000000)) 1816 if urgent { 1817 tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key) 1818 highTip = tx 1819 } else { 1820 tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key) 1821 highCap = tx 1822 } 1823 pool.addRemotesSync([]*types.Transaction{tx}) 1824 } 1825 pending, queued := pool.Stats() 1826 if pending+queued != 20 { 1827 t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 10) 1828 } 1829 } 1830 1831 add(false) 1832 for baseFee = 0; baseFee <= 1000; baseFee += 100 { 1833 pool.priced.SetBaseFee(big.NewInt(int64(baseFee))) 1834 add(true) 1835 check(highCap, "fee cap") 1836 add(false) 1837 check(highTip, "effective tip") 1838 } 1839 1840 if err := validatePoolInternals(pool); err != nil { 1841 t.Fatalf("pool internal state corrupted: %v", err) 1842 } 1843 } 1844 1845 // Tests that the pool rejects duplicate transactions. 1846 func TestDeduplication(t *testing.T) { 1847 t.Parallel() 1848 1849 // Create the pool to test the pricing enforcement with 1850 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 1851 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 1852 1853 pool := New(testTxPoolConfig, blockchain) 1854 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 1855 defer pool.Close() 1856 1857 // Create a test account to add transactions with 1858 key, _ := crypto.GenerateDilithiumKey() 1859 testAddBalance(pool, key.GetAddress(), big.NewInt(1000000000)) 1860 1861 // Create a batch of transactions and add a few of them 1862 txs := make([]*types.Transaction, 16) 1863 for i := 0; i < len(txs); i++ { 1864 txs[i] = dynamicFeeTx(uint64(i), 100000, big.NewInt(1), big.NewInt(1), key) 1865 } 1866 var firsts []*types.Transaction 1867 for i := 0; i < len(txs); i += 2 { 1868 firsts = append(firsts, txs[i]) 1869 } 1870 errs := pool.addRemotesSync(firsts) 1871 if len(errs) != len(firsts) { 1872 t.Fatalf("first add mismatching result count: have %d, want %d", len(errs), len(firsts)) 1873 } 1874 for i, err := range errs { 1875 if err != nil { 1876 t.Errorf("add %d failed: %v", i, err) 1877 } 1878 } 1879 pending, queued := pool.Stats() 1880 if pending != 1 { 1881 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1) 1882 } 1883 if queued != len(txs)/2-1 { 1884 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, len(txs)/2-1) 1885 } 1886 // Try to add all of them now and ensure previous ones error out as knowns 1887 errs = pool.addRemotesSync(txs) 1888 if len(errs) != len(txs) { 1889 t.Fatalf("all add mismatching result count: have %d, want %d", len(errs), len(txs)) 1890 } 1891 for i, err := range errs { 1892 if i%2 == 0 && err == nil { 1893 t.Errorf("add %d succeeded, should have failed as known", i) 1894 } 1895 if i%2 == 1 && err != nil { 1896 t.Errorf("add %d failed: %v", i, err) 1897 } 1898 } 1899 pending, queued = pool.Stats() 1900 if pending != len(txs) { 1901 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, len(txs)) 1902 } 1903 if queued != 0 { 1904 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 1905 } 1906 if err := validatePoolInternals(pool); err != nil { 1907 t.Fatalf("pool internal state corrupted: %v", err) 1908 } 1909 } 1910 1911 // Tests that the pool rejects replacement dynamic fee transactions that don't 1912 // meet the minimum price bump required. 1913 func TestReplacementDynamicFee(t *testing.T) { 1914 t.Parallel() 1915 1916 // Create the pool to test the pricing enforcement with 1917 pool, key := setupPoolWithConfig(eip1559Config) 1918 defer pool.Close() 1919 testAddBalance(pool, key.GetAddress(), big.NewInt(1000000000)) 1920 1921 // Keep track of transaction events to ensure all executables get announced 1922 events := make(chan core.NewTxsEvent, 32) 1923 sub := pool.txFeed.Subscribe(events) 1924 defer sub.Unsubscribe() 1925 1926 // Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too) 1927 gasFeeCap := int64(100) 1928 feeCapThreshold := (gasFeeCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100 1929 gasTipCap := int64(60) 1930 tipThreshold := (gasTipCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100 1931 1932 // Run the following identical checks for both the pending and queue pools: 1933 // 1. Send initial tx => accept 1934 // 2. Don't bump tip or fee cap => discard 1935 // 3. Bump both more than min => accept 1936 // 4. Check events match expected (2 new executable txs during pending, 0 during queue) 1937 // 5. Send new tx with larger tip and gasFeeCap => accept 1938 // 6. Bump tip max allowed so it's still underpriced => discard 1939 // 7. Bump fee cap max allowed so it's still underpriced => discard 1940 // 8. Bump tip min for acceptance => discard 1941 // 9. Bump feecap min for acceptance => discard 1942 // 10. Bump feecap and tip min for acceptance => accept 1943 // 11. Check events match expected (2 new executable txs during pending, 0 during queue) 1944 stages := []string{"pending", "queued"} 1945 for _, stage := range stages { 1946 // Since state is empty, 0 nonce txs are "executable" and can go 1947 // into pending immediately. 2 nonce txs are "gapped" 1948 nonce := uint64(0) 1949 if stage == "queued" { 1950 nonce = 2 1951 } 1952 1953 // 1. Send initial tx => accept 1954 tx := dynamicFeeTx(nonce, 100000, big.NewInt(2), big.NewInt(1), key) 1955 if err := pool.addRemoteSync(tx); err != nil { 1956 t.Fatalf("failed to add original cheap %s transaction: %v", stage, err) 1957 } 1958 // 2. Don't bump tip or feecap => discard 1959 tx = dynamicFeeTx(nonce, 100001, big.NewInt(2), big.NewInt(1), key) 1960 if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { 1961 t.Fatalf("original cheap %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) 1962 } 1963 // 3. Bump both more than min => accept 1964 tx = dynamicFeeTx(nonce, 100000, big.NewInt(3), big.NewInt(2), key) 1965 if err := pool.addRemote(tx); err != nil { 1966 t.Fatalf("failed to replace original cheap %s transaction: %v", stage, err) 1967 } 1968 // 4. Check events match expected (2 new executable txs during pending, 0 during queue) 1969 count := 2 1970 if stage == "queued" { 1971 count = 0 1972 } 1973 if err := validateEvents(events, count); err != nil { 1974 t.Fatalf("cheap %s replacement event firing failed: %v", stage, err) 1975 } 1976 // 5. Send new tx with larger tip and feeCap => accept 1977 tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(gasTipCap), key) 1978 if err := pool.addRemoteSync(tx); err != nil { 1979 t.Fatalf("failed to add original proper %s transaction: %v", stage, err) 1980 } 1981 // 6. Bump tip max allowed so it's still underpriced => discard 1982 tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold-1), key) 1983 if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { 1984 t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) 1985 } 1986 // 7. Bump fee cap max allowed so it's still underpriced => discard 1987 tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold-1), big.NewInt(gasTipCap), key) 1988 if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { 1989 t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) 1990 } 1991 // 8. Bump tip min for acceptance => accept 1992 tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold), key) 1993 if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { 1994 t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) 1995 } 1996 // 9. Bump fee cap min for acceptance => accept 1997 tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(gasTipCap), key) 1998 if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { 1999 t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) 2000 } 2001 // 10. Check events match expected (3 new executable txs during pending, 0 during queue) 2002 tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(tipThreshold), key) 2003 if err := pool.addRemote(tx); err != nil { 2004 t.Fatalf("failed to replace original cheap %s transaction: %v", stage, err) 2005 } 2006 // 11. Check events match expected (3 new executable txs during pending, 0 during queue) 2007 count = 2 2008 if stage == "queued" { 2009 count = 0 2010 } 2011 if err := validateEvents(events, count); err != nil { 2012 t.Fatalf("replacement %s event firing failed: %v", stage, err) 2013 } 2014 } 2015 2016 if err := validatePoolInternals(pool); err != nil { 2017 t.Fatalf("pool internal state corrupted: %v", err) 2018 } 2019 } 2020 2021 // Tests that local transactions are journaled to disk, but remote transactions 2022 // get discarded between restarts. 2023 func TestJournaling(t *testing.T) { testJournaling(t, false) } 2024 func TestJournalingNoLocals(t *testing.T) { testJournaling(t, true) } 2025 2026 func testJournaling(t *testing.T, nolocals bool) { 2027 t.Parallel() 2028 2029 // Create a temporary file for the journal 2030 file, err := os.CreateTemp("", "") 2031 if err != nil { 2032 t.Fatalf("failed to create temporary journal: %v", err) 2033 } 2034 journal := file.Name() 2035 defer os.Remove(journal) 2036 2037 // Clean up the temporary file, we only need the path for now 2038 file.Close() 2039 os.Remove(journal) 2040 2041 // Create the original pool to inject transaction into the journal 2042 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 2043 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 2044 2045 config := testTxPoolConfig 2046 config.NoLocals = nolocals 2047 config.Journal = journal 2048 config.Rejournal = time.Second 2049 2050 pool := New(config, blockchain) 2051 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 2052 2053 // Create two test accounts to ensure remotes expire but locals do not 2054 local, _ := crypto.GenerateDilithiumKey() 2055 remote, _ := crypto.GenerateDilithiumKey() 2056 2057 testAddBalance(pool, local.GetAddress(), big.NewInt(1000000000)) 2058 testAddBalance(pool, remote.GetAddress(), big.NewInt(1000000000)) 2059 2060 // Add three local and a remote transactions and ensure they are queued up 2061 if err := pool.addLocal(dynamicFeeTx(0, 100000, big.NewInt(1), big.NewInt(1), local)); err != nil { 2062 t.Fatalf("failed to add local transaction: %v", err) 2063 } 2064 if err := pool.addLocal(dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), local)); err != nil { 2065 t.Fatalf("failed to add local transaction: %v", err) 2066 } 2067 if err := pool.addLocal(dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), local)); err != nil { 2068 t.Fatalf("failed to add local transaction: %v", err) 2069 } 2070 if err := pool.addRemoteSync(dynamicFeeTx(0, 100000, big.NewInt(1), big.NewInt(1), remote)); err != nil { 2071 t.Fatalf("failed to add remote transaction: %v", err) 2072 } 2073 pending, queued := pool.Stats() 2074 if pending != 4 { 2075 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4) 2076 } 2077 if queued != 0 { 2078 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 2079 } 2080 if err := validatePoolInternals(pool); err != nil { 2081 t.Fatalf("pool internal state corrupted: %v", err) 2082 } 2083 // Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive 2084 pool.Close() 2085 statedb.SetNonce(local.GetAddress(), 1) 2086 blockchain = newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 2087 2088 pool = New(config, blockchain) 2089 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 2090 2091 pending, queued = pool.Stats() 2092 if queued != 0 { 2093 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 2094 } 2095 if nolocals { 2096 if pending != 0 { 2097 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) 2098 } 2099 } else { 2100 if pending != 2 { 2101 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) 2102 } 2103 } 2104 if err := validatePoolInternals(pool); err != nil { 2105 t.Fatalf("pool internal state corrupted: %v", err) 2106 } 2107 // Bump the nonce temporarily and ensure the newly invalidated transaction is removed 2108 statedb.SetNonce(local.GetAddress(), 2) 2109 <-pool.requestReset(nil, nil) 2110 time.Sleep(2 * config.Rejournal) 2111 pool.Close() 2112 2113 statedb.SetNonce(local.GetAddress(), 1) 2114 blockchain = newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 2115 pool = New(config, blockchain) 2116 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 2117 2118 pending, queued = pool.Stats() 2119 if pending != 0 { 2120 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) 2121 } 2122 if nolocals { 2123 if queued != 0 { 2124 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) 2125 } 2126 } else { 2127 if queued != 1 { 2128 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) 2129 } 2130 } 2131 if err := validatePoolInternals(pool); err != nil { 2132 t.Fatalf("pool internal state corrupted: %v", err) 2133 } 2134 pool.Close() 2135 } 2136 2137 // TestStatusCheck tests that the pool can correctly retrieve the 2138 // pending status of individual transactions. 2139 func TestStatusCheck(t *testing.T) { 2140 t.Parallel() 2141 2142 // Create the pool to test the status retrievals with 2143 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 2144 blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) 2145 2146 pool := New(testTxPoolConfig, blockchain) 2147 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 2148 defer pool.Close() 2149 2150 // Create the test accounts to check various transaction statuses with 2151 keys := make([]*dilithium.Dilithium, 3) 2152 for i := 0; i < len(keys); i++ { 2153 keys[i], _ = crypto.GenerateDilithiumKey() 2154 testAddBalance(pool, keys[i].GetAddress(), big.NewInt(1000000)) 2155 } 2156 // Generate and queue a batch of transactions, both pending and queued 2157 txs := types.Transactions{} 2158 2159 txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(1), big.NewInt(1), keys[0])) // Pending only 2160 txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(1), big.NewInt(1), keys[1])) // Pending and queued 2161 txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[1])) 2162 txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2])) // Queued only 2163 2164 // Import the transaction and ensure they are correctly added 2165 pool.addRemotesSync(txs) 2166 2167 pending, queued := pool.Stats() 2168 if pending != 2 { 2169 t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) 2170 } 2171 if queued != 2 { 2172 t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) 2173 } 2174 if err := validatePoolInternals(pool); err != nil { 2175 t.Fatalf("pool internal state corrupted: %v", err) 2176 } 2177 // Retrieve the status of each transaction and validate them 2178 hashes := make([]common.Hash, len(txs)) 2179 for i, tx := range txs { 2180 hashes[i] = tx.Hash() 2181 } 2182 hashes = append(hashes, common.Hash{}) 2183 expect := []txpool.TxStatus{txpool.TxStatusPending, txpool.TxStatusPending, txpool.TxStatusQueued, txpool.TxStatusQueued, txpool.TxStatusUnknown} 2184 2185 for i := 0; i < len(hashes); i++ { 2186 if status := pool.Status(hashes[i]); status != expect[i] { 2187 t.Errorf("transaction %d: status mismatch: have %v, want %v", i, status, expect[i]) 2188 } 2189 } 2190 } 2191 2192 // Test the transaction slots consumption is computed correctly 2193 func TestSlotCount(t *testing.T) { 2194 t.Parallel() 2195 2196 key, _ := crypto.GenerateDilithiumKey() 2197 2198 // Check that an empty transaction consumes a single slot 2199 smallTx := dynamicFeeDataTx(0, 0, big.NewInt(0), big.NewInt(0), key, 0) 2200 if slots := numSlots(smallTx); slots != 1 { 2201 t.Fatalf("small transactions slot count mismatch: have %d want %d", slots, 1) 2202 } 2203 // Check that a large transaction consumes the correct number of slots 2204 bigTx := dynamicFeeDataTx(0, 0, big.NewInt(0), big.NewInt(0), key, uint64(10*txSlotSize)) 2205 if slots := numSlots(bigTx); slots != 11 { 2206 t.Fatalf("big transactions slot count mismatch: have %d want %d", slots, 11) 2207 } 2208 } 2209 2210 // Benchmarks the speed of validating the contents of the pending queue of the 2211 // transaction pool. 2212 func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) } 2213 func BenchmarkPendingDemotion1000(b *testing.B) { benchmarkPendingDemotion(b, 1000) } 2214 func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 10000) } 2215 2216 func benchmarkPendingDemotion(b *testing.B, size int) { 2217 // Add a batch of transactions to a pool one by one 2218 pool, key := setupPool() 2219 defer pool.Close() 2220 2221 account := key.GetAddress() 2222 testAddBalance(pool, account, big.NewInt(1000000)) 2223 2224 for i := 0; i < size; i++ { 2225 tx := transaction(uint64(i), 100000, key) 2226 pool.promoteTx(account, tx.Hash(), tx) 2227 } 2228 // Benchmark the speed of pool validation 2229 b.ResetTimer() 2230 for i := 0; i < b.N; i++ { 2231 pool.demoteUnexecutables() 2232 } 2233 } 2234 2235 // Benchmarks the speed of scheduling the contents of the future queue of the 2236 // transaction pool. 2237 func BenchmarkFuturePromotion100(b *testing.B) { benchmarkFuturePromotion(b, 100) } 2238 func BenchmarkFuturePromotion1000(b *testing.B) { benchmarkFuturePromotion(b, 1000) } 2239 func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 10000) } 2240 2241 func benchmarkFuturePromotion(b *testing.B, size int) { 2242 // Add a batch of transactions to a pool one by one 2243 pool, key := setupPool() 2244 defer pool.Close() 2245 2246 account := key.GetAddress() 2247 testAddBalance(pool, account, big.NewInt(1000000)) 2248 2249 for i := 0; i < size; i++ { 2250 tx := transaction(uint64(1+i), 100000, key) 2251 pool.enqueueTx(tx.Hash(), tx, false, true) 2252 } 2253 // Benchmark the speed of pool validation 2254 b.ResetTimer() 2255 for i := 0; i < b.N; i++ { 2256 pool.promoteExecutables(nil) 2257 } 2258 } 2259 2260 // Benchmarks the speed of batched transaction insertion. 2261 func BenchmarkBatchInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, false) } 2262 func BenchmarkBatchInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, false) } 2263 func BenchmarkBatchInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, false) } 2264 2265 func BenchmarkBatchLocalInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, true) } 2266 func BenchmarkBatchLocalInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, true) } 2267 func BenchmarkBatchLocalInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, true) } 2268 2269 func benchmarkBatchInsert(b *testing.B, size int, local bool) { 2270 // Generate a batch of transactions to enqueue into the pool 2271 pool, key := setupPool() 2272 defer pool.Close() 2273 2274 account := key.GetAddress() 2275 testAddBalance(pool, account, big.NewInt(1000000000000000000)) 2276 2277 batches := make([]types.Transactions, b.N) 2278 for i := 0; i < b.N; i++ { 2279 batches[i] = make(types.Transactions, size) 2280 for j := 0; j < size; j++ { 2281 batches[i][j] = transaction(uint64(size*i+j), 100000, key) 2282 } 2283 } 2284 // Benchmark importing the transactions into the queue 2285 b.ResetTimer() 2286 for _, batch := range batches { 2287 if local { 2288 pool.addLocals(batch) 2289 } else { 2290 pool.addRemotes(batch) 2291 } 2292 } 2293 } 2294 2295 func BenchmarkInsertRemoteWithAllLocals(b *testing.B) { 2296 // Allocate keys for testing 2297 key, _ := crypto.GenerateDilithiumKey() 2298 account := key.GetAddress() 2299 2300 remoteKey, _ := crypto.GenerateDilithiumKey() 2301 remoteAddr := remoteKey.GetAddress() 2302 2303 locals := make([]*types.Transaction, 4096+1024) // Occupy all slots 2304 for i := 0; i < len(locals); i++ { 2305 locals[i] = transaction(uint64(i), 100000, key) 2306 } 2307 remotes := make([]*types.Transaction, 1000) 2308 for i := 0; i < len(remotes); i++ { 2309 remotes[i] = dynamicFeeTx(uint64(i), 100000, big.NewInt(2), big.NewInt(0), remoteKey) // Higher gasprice 2310 } 2311 // Benchmark importing the transactions into the queue 2312 b.ResetTimer() 2313 for i := 0; i < b.N; i++ { 2314 b.StopTimer() 2315 pool, _ := setupPool() 2316 testAddBalance(pool, account, big.NewInt(100000000)) 2317 for _, local := range locals { 2318 pool.addLocal(local) 2319 } 2320 b.StartTimer() 2321 // Assign a high enough balance for testing 2322 testAddBalance(pool, remoteAddr, big.NewInt(100000000)) 2323 for i := 0; i < len(remotes); i++ { 2324 pool.addRemotes([]*types.Transaction{remotes[i]}) 2325 } 2326 pool.Close() 2327 } 2328 } 2329 2330 // Benchmarks the speed of batch transaction insertion in case of multiple accounts. 2331 func BenchmarkMultiAccountBatchInsert(b *testing.B) { 2332 // Generate a batch of transactions to enqueue into the pool 2333 pool, _ := setupPool() 2334 defer pool.Close() 2335 b.ReportAllocs() 2336 batches := make(types.Transactions, b.N) 2337 for i := 0; i < b.N; i++ { 2338 key, _ := crypto.GenerateDilithiumKey() 2339 account := key.GetAddress() 2340 pool.currentState.AddBalance(account, big.NewInt(1000000)) 2341 tx := transaction(uint64(0), 100000, key) 2342 batches[i] = tx 2343 } 2344 // Benchmark importing the transactions into the queue 2345 b.ResetTimer() 2346 for _, tx := range batches { 2347 pool.addRemotesSync([]*types.Transaction{tx}) 2348 } 2349 }