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  }