github.com/klaytn/klaytn@v1.12.1/blockchain/tx_list_test.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2016 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from core/tx_list_test.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package blockchain
    22  
    23  import (
    24  	"math/big"
    25  	"math/rand"
    26  	"reflect"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/klaytn/klaytn/blockchain/types"
    31  	"github.com/klaytn/klaytn/crypto"
    32  	"github.com/stretchr/testify/assert"
    33  )
    34  
    35  // Tests that transactions can be added to strict lists and list contents and
    36  // nonce boundaries are correctly maintained.
    37  func TestStrictTxListAdd(t *testing.T) {
    38  	// Generate a list of transactions to insert
    39  	key, _ := crypto.GenerateKey()
    40  
    41  	txs := make(types.Transactions, 1024)
    42  	for i := 0; i < len(txs); i++ {
    43  		txs[i] = transaction(uint64(i), 0, key)
    44  	}
    45  	// Insert the transactions in a random order
    46  	list := newTxList(true)
    47  	for _, v := range rand.Perm(len(txs)) {
    48  		list.Add(txs[v], DefaultTxPoolConfig.PriceBump, false)
    49  	}
    50  	// Verify internal state
    51  	if len(list.txs.items) != len(txs) {
    52  		t.Errorf("transaction count mismatch: have %d, want %d", len(list.txs.items), len(txs))
    53  	}
    54  	for i, tx := range txs {
    55  		if list.txs.items[tx.Nonce()] != tx {
    56  			t.Errorf("item %d: transaction mismatch: have %v, want %v", i, list.txs.items[tx.Nonce()], tx)
    57  		}
    58  	}
    59  }
    60  
    61  // TestTxListReadyWithGasPriceBasic check whether ReadyWithGasPrice() works well.
    62  // It makes a slice of 10 transactions and executes ReadyWithGasPrice() with baseFee 30.
    63  func TestTxListReadyWithGasPriceBasic(t *testing.T) {
    64  	// Start nonce : 3
    65  	// baseFee : 30
    66  	// Transaction[0:9] has gasPrice 50
    67  	// The result of executing ReadyWithGasPrice will be Transaction[0:9].
    68  	startNonce := 3
    69  	expectedBaseFee := big.NewInt(30)
    70  	nTxs := 10
    71  	// Generate a list of transactions to insert
    72  	key, _ := crypto.GenerateKey()
    73  
    74  	txs := make(types.Transactions, nTxs)
    75  	nonce := startNonce
    76  	for i := 0; i < len(txs); i++ {
    77  		txs[i] = pricedTransaction(uint64(nonce), 0, big.NewInt(50), key)
    78  		nonce++
    79  	}
    80  
    81  	// Insert the transactions in a random order
    82  	list := newTxList(true)
    83  	rand.Seed(time.Now().UnixNano())
    84  	for _, v := range rand.Perm(len(txs)) {
    85  		list.Add(txs[v], DefaultTxPoolConfig.PriceBump, true)
    86  	}
    87  
    88  	ready := list.ReadyWithGasPrice(uint64(startNonce), expectedBaseFee)
    89  
    90  	if ready.Len() != nTxs {
    91  		t.Error("expected number of filtered txs", nTxs, "got", ready.Len())
    92  	}
    93  
    94  	nonce = startNonce
    95  	for i := 0; i < len(ready); i++ {
    96  		if result := reflect.DeepEqual(ready[i], txs[i]); !result {
    97  			t.Error("ready hash : ", ready[i].Hash(), "tx[i] hash : ", txs[i].Hash())
    98  		}
    99  
   100  		if list.txs.items[uint64(nonce)] != nil {
   101  			t.Error("Not deleted in txList. nonce : ", nonce, "tx hash : ", list.txs.items[uint64(nonce)].Hash())
   102  		}
   103  		nonce++
   104  	}
   105  }
   106  
   107  // TestTxListReadyWithGasPricePartialFilter check whether ReadyWithGasPrice() works well.
   108  // It makes a slice has 10 transactions and executes ReadyWithGasPrice() with baseFee 20.
   109  //
   110  // It checks whether filtering works well if there is a transaction
   111  // with a gasPrice lower than the baseFee among Tx.
   112  func TestTxListReadyWithGasPricePartialFilter(t *testing.T) {
   113  	// Start nonce : 3
   114  	// baseFee : 20
   115  	// Transaction[0:6] has gasPrice 30
   116  	// Transaction[7] has gasPrice 10
   117  	// Transaction[8:9] has gasPrice 50
   118  	// The result of executing ReadyWithGasPrice will be Transaction[0:6].
   119  	startNonce := 3
   120  	expectedBaseFee := big.NewInt(20)
   121  	nTxs := 10
   122  
   123  	// Generate a list of transactions to insert
   124  	key, _ := crypto.GenerateKey()
   125  
   126  	txs := make(types.Transactions, nTxs)
   127  	nonce := startNonce
   128  	for i := 0; i < len(txs); i++ {
   129  		// Set the gasPrice of transaction lower than expectedBaseFee.
   130  		if i == 7 {
   131  			txs[i] = pricedTransaction(uint64(nonce), 0, big.NewInt(10), key)
   132  		} else if i > 7 {
   133  			txs[i] = pricedTransaction(uint64(nonce), 0, big.NewInt(50), key)
   134  		} else {
   135  			txs[i] = pricedTransaction(uint64(nonce), 0, big.NewInt(30), key)
   136  		}
   137  
   138  		nonce++
   139  	}
   140  
   141  	// Insert the transactions in a random order
   142  	list := newTxList(true)
   143  	for _, v := range rand.Perm(len(txs)) {
   144  		list.Add(txs[v], DefaultTxPoolConfig.PriceBump, true)
   145  	}
   146  
   147  	ready := list.ReadyWithGasPrice(uint64(startNonce), expectedBaseFee)
   148  
   149  	if ready.Len() != 7 {
   150  		t.Error("expected filtered txs length", 7, "got", ready.Len())
   151  	}
   152  
   153  	nonce = startNonce
   154  	for i := 0; i < len(ready); i++ {
   155  		if result := reflect.DeepEqual(ready[i], txs[i]); result == false {
   156  			t.Error("ready hash : ", ready[i].Hash(), "tx[i] hash : ", txs[i].Hash())
   157  		}
   158  
   159  		if list.txs.items[uint64(nonce)] != nil {
   160  			t.Error("Not deleted in txList. nonce : ", nonce, "tx hash : ", list.txs.items[uint64(nonce)].Hash())
   161  		}
   162  		nonce++
   163  	}
   164  }
   165  
   166  // TestSubstituteTxByGasPrice tests if the new tx has been successfully replaced
   167  // if it has the same nonce and greater gas price as the old tx.
   168  func TestSubstituteTxByGasPrice(t *testing.T) {
   169  	// Generate a list of transactions to insert
   170  	key, _ := crypto.GenerateKey()
   171  	txList := newTxList(false)
   172  
   173  	oldTx := pricedTransaction(0, 21000, big.NewInt(50), key)
   174  	newTx := pricedTransaction(0, 21000, big.NewInt(60), key)
   175  
   176  	if result, _ := txList.Add(oldTx, DefaultTxPoolConfig.PriceBump, true); !result {
   177  		t.Error("it cannot add tx in tx list.")
   178  	}
   179  
   180  	result, replaced := txList.Add(newTx, DefaultTxPoolConfig.PriceBump, true)
   181  	if !result {
   182  		t.Error("it cannot replace tx in tx list.")
   183  	}
   184  
   185  	assert.Equal(t, replaced, oldTx)
   186  }
   187  
   188  // TestSubstituteTransactionAbort checks if a new tx aborts a new transaction
   189  // if it has the same nonce and lower gas price as the old tx.
   190  func TestSubstituteTransactionAbort(t *testing.T) {
   191  	// Generate a list of transactions to insert
   192  	key, _ := crypto.GenerateKey()
   193  	txList := newTxList(false)
   194  
   195  	oldTx := pricedTransaction(0, 21000, big.NewInt(50), key)
   196  	newTx := pricedTransaction(0, 21000, big.NewInt(40), key)
   197  
   198  	if result, _ := txList.Add(oldTx, DefaultTxPoolConfig.PriceBump, true); !result {
   199  		t.Error("it cannot add tx in tx list.")
   200  	}
   201  
   202  	if result, replaced := txList.Add(newTx, DefaultTxPoolConfig.PriceBump, true); result || replaced != nil {
   203  		t.Error("Expected to not substitute by a tx with lower gas price")
   204  	}
   205  }