github.com/theQRL/go-zond@v0.2.1/core/txpool/legacypool/legacypool2_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 legacypool 17 18 import ( 19 "math/big" 20 "testing" 21 22 "github.com/theQRL/go-qrllib/dilithium" 23 "github.com/theQRL/go-zond/common" 24 "github.com/theQRL/go-zond/core/rawdb" 25 "github.com/theQRL/go-zond/core/state" 26 "github.com/theQRL/go-zond/core/types" 27 "github.com/theQRL/go-zond/crypto" 28 "github.com/theQRL/go-zond/event" 29 ) 30 31 func dynamicFeeValuedTransaction(nonce uint64, value int64, gaslimit uint64, gasFeeCap *big.Int, key *dilithium.Dilithium) *types.Transaction { 32 tx := types.NewTx(&types.DynamicFeeTx{ 33 Nonce: nonce, 34 To: &common.Address{}, 35 Value: big.NewInt(value), 36 Gas: gaslimit, 37 GasFeeCap: gasFeeCap, 38 Data: nil, 39 }) 40 signedTx, _ := types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, key) 41 return signedTx 42 } 43 44 func count(t *testing.T, pool *LegacyPool) (pending int, queued int) { 45 t.Helper() 46 pending, queued = pool.stats() 47 if err := validatePoolInternals(pool); err != nil { 48 t.Fatalf("pool internal state corrupted: %v", err) 49 } 50 return pending, queued 51 } 52 53 func fillPool(t testing.TB, pool *LegacyPool) { 54 t.Helper() 55 // Create a number of test accounts, fund them and make transactions 56 executableTxs := types.Transactions{} 57 nonExecutableTxs := types.Transactions{} 58 for i := 0; i < 384; i++ { 59 key, _ := crypto.GenerateDilithiumKey() 60 pool.currentState.AddBalance(key.GetAddress(), big.NewInt(10000000000)) 61 // Add executable ones 62 for j := 0; j < int(pool.config.AccountSlots); j++ { 63 executableTxs = append(executableTxs, dynamicFeeTx(uint64(j), 100000, big.NewInt(300), big.NewInt(0), key)) 64 } 65 } 66 // Import the batch and verify that limits have been enforced 67 pool.addRemotesSync(executableTxs) 68 pool.addRemotesSync(nonExecutableTxs) 69 pending, queued := pool.Stats() 70 slots := pool.all.Slots() 71 // sanity-check that the test prerequisites are ok (pending full) 72 if have, want := pending, slots; have != want { 73 t.Fatalf("have %d, want %d", have, want) 74 } 75 if have, want := queued, 0; have != want { 76 t.Fatalf("have %d, want %d", have, want) 77 } 78 79 t.Logf("pool.config: GlobalSlots=%d, GlobalQueue=%d\n", pool.config.GlobalSlots, pool.config.GlobalQueue) 80 t.Logf("pending: %d queued: %d, all: %d\n", pending, queued, slots) 81 } 82 83 // Tests that if a batch high-priced of non-executables arrive, they do not kick out 84 // executable transactions 85 func TestTransactionFutureAttack(t *testing.T) { 86 t.Parallel() 87 88 // Create the pool to test the limit enforcement with 89 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 90 blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) 91 config := testTxPoolConfig 92 config.GlobalQueue = 100 93 config.GlobalSlots = 100 94 pool := New(config, blockchain) 95 pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 96 defer pool.Close() 97 fillPool(t, pool) 98 pending, _ := pool.Stats() 99 // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops 100 { 101 key, _ := crypto.GenerateDilithiumKey() 102 pool.currentState.AddBalance(key.GetAddress(), big.NewInt(100000000000)) 103 futureTxs := types.Transactions{} 104 for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { 105 futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(500), big.NewInt(0), key)) 106 } 107 for i := 0; i < 5; i++ { 108 pool.addRemotesSync(futureTxs) 109 newPending, newQueued := count(t, pool) 110 t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots()) 111 } 112 } 113 newPending, _ := pool.Stats() 114 // Pending should not have been touched 115 if have, want := newPending, pending; have < want { 116 t.Errorf("wrong pending-count, have %d, want %d (GlobalSlots: %d)", 117 have, want, pool.config.GlobalSlots) 118 } 119 } 120 121 // Tests that if a batch high-priced of non-executables arrive, they do not kick out 122 // executable transactions 123 func TestTransactionFuture1559(t *testing.T) { 124 t.Parallel() 125 // Create the pool to test the pricing enforcement with 126 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 127 blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) 128 pool := New(testTxPoolConfig, blockchain) 129 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 130 defer pool.Close() 131 132 // Create a number of test accounts, fund them and make transactions 133 fillPool(t, pool) 134 pending, _ := pool.Stats() 135 136 // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops 137 { 138 key, _ := crypto.GenerateDilithiumKey() 139 pool.currentState.AddBalance(key.GetAddress(), big.NewInt(100000000000)) 140 futureTxs := types.Transactions{} 141 for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { 142 futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key)) 143 } 144 pool.addRemotesSync(futureTxs) 145 } 146 newPending, _ := pool.Stats() 147 // Pending should not have been touched 148 if have, want := newPending, pending; have != want { 149 t.Errorf("Wrong pending-count, have %d, want %d (GlobalSlots: %d)", 150 have, want, pool.config.GlobalSlots) 151 } 152 } 153 154 // Tests that if a batch of balance-overdraft txs arrive, they do not kick out 155 // executable transactions 156 func TestTransactionZAttack(t *testing.T) { 157 t.Parallel() 158 // Create the pool to test the pricing enforcement with 159 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 160 blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) 161 pool := New(testTxPoolConfig, blockchain) 162 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 163 defer pool.Close() 164 // Create a number of test accounts, fund them and make transactions 165 fillPool(t, pool) 166 167 countInvalidPending := func() int { 168 t.Helper() 169 var ivpendingNum int 170 pendingtxs, _ := pool.Content() 171 for account, txs := range pendingtxs { 172 cur_balance := new(big.Int).Set(pool.currentState.GetBalance(account)) 173 for _, tx := range txs { 174 if cur_balance.Cmp(tx.Value()) <= 0 { 175 ivpendingNum++ 176 } else { 177 cur_balance.Sub(cur_balance, tx.Value()) 178 } 179 } 180 } 181 if err := validatePoolInternals(pool); err != nil { 182 t.Fatalf("pool internal state corrupted: %v", err) 183 } 184 return ivpendingNum 185 } 186 ivPending := countInvalidPending() 187 t.Logf("invalid pending: %d\n", ivPending) 188 189 // 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 190 for j := 0; j < int(pool.config.GlobalQueue); j++ { 191 futureTxs := types.Transactions{} 192 key, _ := crypto.GenerateDilithiumKey() 193 pool.currentState.AddBalance(key.GetAddress(), big.NewInt(100000000000)) 194 futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 21000, big.NewInt(500), big.NewInt(0), key)) 195 pool.addRemotesSync(futureTxs) 196 } 197 198 overDraftTxs := types.Transactions{} 199 { 200 key, _ := crypto.GenerateDilithiumKey() 201 pool.currentState.AddBalance(key.GetAddress(), big.NewInt(100000000000)) 202 for j := 0; j < int(pool.config.GlobalSlots); j++ { 203 overDraftTxs = append(overDraftTxs, dynamicFeeValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key)) 204 } 205 } 206 pool.addRemotesSync(overDraftTxs) 207 pool.addRemotesSync(overDraftTxs) 208 pool.addRemotesSync(overDraftTxs) 209 pool.addRemotesSync(overDraftTxs) 210 pool.addRemotesSync(overDraftTxs) 211 212 newPending, newQueued := count(t, pool) 213 newIvPending := countInvalidPending() 214 t.Logf("pool.all.Slots(): %d\n", pool.all.Slots()) 215 t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots()) 216 t.Logf("invalid pending: %d\n", newIvPending) 217 218 // Pending should not have been touched 219 if newIvPending != ivPending { 220 t.Errorf("Wrong invalid pending-count, have %d, want %d (GlobalSlots: %d, queued: %d)", 221 newIvPending, ivPending, pool.config.GlobalSlots, newQueued) 222 } 223 } 224 225 func BenchmarkFutureAttack(b *testing.B) { 226 // Create the pool to test the limit enforcement with 227 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 228 blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) 229 config := testTxPoolConfig 230 config.GlobalQueue = 100 231 config.GlobalSlots = 100 232 pool := New(config, blockchain) 233 pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) 234 defer pool.Close() 235 fillPool(b, pool) 236 237 key, _ := crypto.GenerateDilithiumKey() 238 pool.currentState.AddBalance(key.GetAddress(), big.NewInt(100000000000)) 239 futureTxs := types.Transactions{} 240 241 for n := 0; n < b.N; n++ { 242 futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(n), 100000, big.NewInt(500), big.NewInt(0), key)) 243 } 244 b.ResetTimer() 245 for i := 0; i < 5; i++ { 246 pool.addRemotesSync(futureTxs) 247 } 248 }