github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/core/txpool/txpool2_test.go (about) 1 // Copyright 2023 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 package txpool 17 18 import ( 19 "crypto/ecdsa" 20 "math/big" 21 "testing" 22 23 "github.com/tacshi/go-ethereum/common" 24 "github.com/tacshi/go-ethereum/core/rawdb" 25 "github.com/tacshi/go-ethereum/core/state" 26 "github.com/tacshi/go-ethereum/core/types" 27 "github.com/tacshi/go-ethereum/crypto" 28 "github.com/tacshi/go-ethereum/event" 29 ) 30 31 func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction { 32 tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(value), gaslimit, gasprice, nil), types.HomesteadSigner{}, key) 33 return tx 34 } 35 36 func count(t *testing.T, pool *TxPool) (pending int, queued int) { 37 t.Helper() 38 pending, queued = pool.stats() 39 if err := validatePoolInternals(pool); err != nil { 40 t.Fatalf("pool internal state corrupted: %v", err) 41 } 42 return pending, queued 43 } 44 45 func fillPool(t *testing.T, pool *TxPool) { 46 t.Helper() 47 // Create a number of test accounts, fund them and make transactions 48 executableTxs := types.Transactions{} 49 nonExecutableTxs := types.Transactions{} 50 for i := 0; i < 384; i++ { 51 key, _ := crypto.GenerateKey() 52 pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000)) 53 // Add executable ones 54 for j := 0; j < int(pool.config.AccountSlots); j++ { 55 executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key)) 56 } 57 } 58 // Import the batch and verify that limits have been enforced 59 pool.AddRemotesSync(executableTxs) 60 pool.AddRemotesSync(nonExecutableTxs) 61 pending, queued := pool.Stats() 62 slots := pool.all.Slots() 63 // sanity-check that the test prerequisites are ok (pending full) 64 if have, want := pending, slots; have != want { 65 t.Fatalf("have %d, want %d", have, want) 66 } 67 if have, want := queued, 0; have != want { 68 t.Fatalf("have %d, want %d", have, want) 69 } 70 71 t.Logf("pool.config: GlobalSlots=%d, GlobalQueue=%d\n", pool.config.GlobalSlots, pool.config.GlobalQueue) 72 t.Logf("pending: %d queued: %d, all: %d\n", pending, queued, slots) 73 } 74 75 // Tests that if a batch high-priced of non-executables arrive, they do not kick out 76 // executable transactions 77 func TestTransactionFutureAttack(t *testing.T) { 78 t.Parallel() 79 80 // Create the pool to test the limit enforcement with 81 statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 82 blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} 83 config := testTxPoolConfig 84 config.GlobalQueue = 100 85 config.GlobalSlots = 100 86 pool := NewTxPool(config, eip1559Config, blockchain) 87 defer pool.Stop() 88 fillPool(t, pool) 89 pending, _ := pool.Stats() 90 // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops 91 { 92 key, _ := crypto.GenerateKey() 93 pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) 94 futureTxs := types.Transactions{} 95 for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { 96 futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key)) 97 } 98 for i := 0; i < 5; i++ { 99 pool.AddRemotesSync(futureTxs) 100 newPending, newQueued := count(t, pool) 101 t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots()) 102 } 103 } 104 newPending, _ := pool.Stats() 105 // Pending should not have been touched 106 if have, want := newPending, pending; have < want { 107 t.Errorf("wrong pending-count, have %d, want %d (GlobalSlots: %d)", 108 have, want, pool.config.GlobalSlots) 109 } 110 } 111 112 // Tests that if a batch high-priced of non-executables arrive, they do not kick out 113 // executable transactions 114 func TestTransactionFuture1559(t *testing.T) { 115 t.Parallel() 116 // Create the pool to test the pricing enforcement with 117 statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 118 blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} 119 pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain) 120 defer pool.Stop() 121 122 // Create a number of test accounts, fund them and make transactions 123 fillPool(t, pool) 124 pending, _ := pool.Stats() 125 126 // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops 127 { 128 key, _ := crypto.GenerateKey() 129 pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) 130 futureTxs := types.Transactions{} 131 for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { 132 futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key)) 133 } 134 pool.AddRemotesSync(futureTxs) 135 } 136 newPending, _ := pool.Stats() 137 // Pending should not have been touched 138 if have, want := newPending, pending; have != want { 139 t.Errorf("Wrong pending-count, have %d, want %d (GlobalSlots: %d)", 140 have, want, pool.config.GlobalSlots) 141 } 142 } 143 144 // Tests that if a batch of balance-overdraft txs arrive, they do not kick out 145 // executable transactions 146 func TestTransactionZAttack(t *testing.T) { 147 t.Parallel() 148 // Create the pool to test the pricing enforcement with 149 statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 150 blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} 151 pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain) 152 defer pool.Stop() 153 // Create a number of test accounts, fund them and make transactions 154 fillPool(t, pool) 155 156 countInvalidPending := func() int { 157 t.Helper() 158 var ivpendingNum int 159 pendingtxs, _ := pool.Content() 160 for account, txs := range pendingtxs { 161 cur_balance := new(big.Int).Set(pool.currentState.GetBalance(account)) 162 for _, tx := range txs { 163 if cur_balance.Cmp(tx.Value()) <= 0 { 164 ivpendingNum++ 165 } else { 166 cur_balance.Sub(cur_balance, tx.Value()) 167 } 168 } 169 } 170 if err := validatePoolInternals(pool); err != nil { 171 t.Fatalf("pool internal state corrupted: %v", err) 172 } 173 return ivpendingNum 174 } 175 ivPending := countInvalidPending() 176 t.Logf("invalid pending: %d\n", ivPending) 177 178 // Now, DETER-Z attack starts, let's add a bunch of expensive non-executables (from N accounts) along with balance-overdraft txs (from one account), and see if the pending-count drops 179 for j := 0; j < int(pool.config.GlobalQueue); j++ { 180 futureTxs := types.Transactions{} 181 key, _ := crypto.GenerateKey() 182 pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) 183 futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key)) 184 pool.AddRemotesSync(futureTxs) 185 } 186 187 overDraftTxs := types.Transactions{} 188 { 189 key, _ := crypto.GenerateKey() 190 pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) 191 for j := 0; j < int(pool.config.GlobalSlots); j++ { 192 overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 60000000000, 21000, big.NewInt(500), key)) 193 } 194 } 195 pool.AddRemotesSync(overDraftTxs) 196 pool.AddRemotesSync(overDraftTxs) 197 pool.AddRemotesSync(overDraftTxs) 198 pool.AddRemotesSync(overDraftTxs) 199 pool.AddRemotesSync(overDraftTxs) 200 201 newPending, newQueued := count(t, pool) 202 newIvPending := countInvalidPending() 203 t.Logf("pool.all.Slots(): %d\n", pool.all.Slots()) 204 t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots()) 205 t.Logf("invalid pending: %d\n", newIvPending) 206 207 // Pending should not have been touched 208 if newIvPending != ivPending { 209 t.Errorf("Wrong invalid pending-count, have %d, want %d (GlobalSlots: %d, queued: %d)", 210 newIvPending, ivPending, pool.config.GlobalSlots, newQueued) 211 } 212 }