github.com/theQRL/go-zond@v0.1.1/miner/ordering_test.go (about)

     1  // Copyright 2014 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 miner
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"math/big"
    22  	"math/rand"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/theQRL/go-zond/common"
    27  	"github.com/theQRL/go-zond/core/txpool"
    28  	"github.com/theQRL/go-zond/core/types"
    29  	"github.com/theQRL/go-zond/crypto"
    30  )
    31  
    32  func TestTransactionPriceNonceSortLegacy(t *testing.T) {
    33  	testTransactionPriceNonceSort(t, nil)
    34  }
    35  
    36  func TestTransactionPriceNonceSort1559(t *testing.T) {
    37  	testTransactionPriceNonceSort(t, big.NewInt(0))
    38  	testTransactionPriceNonceSort(t, big.NewInt(5))
    39  	testTransactionPriceNonceSort(t, big.NewInt(50))
    40  }
    41  
    42  // Tests that transactions can be correctly sorted according to their price in
    43  // decreasing order, but at the same time with increasing nonces when issued by
    44  // the same account.
    45  func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
    46  	// Generate a batch of accounts to start with
    47  	keys := make([]*ecdsa.PrivateKey, 25)
    48  	for i := 0; i < len(keys); i++ {
    49  		keys[i], _ = crypto.GenerateKey()
    50  	}
    51  	signer := types.LatestSignerForChainID(common.Big1)
    52  
    53  	// Generate a batch of transactions with overlapping values, but shifted nonces
    54  	groups := map[common.Address][]*txpool.LazyTransaction{}
    55  	expectedCount := 0
    56  	for start, key := range keys {
    57  		addr := crypto.PubkeyToAddress(key.PublicKey)
    58  		count := 25
    59  		for i := 0; i < 25; i++ {
    60  			var tx *types.Transaction
    61  			gasFeeCap := rand.Intn(50)
    62  			if baseFee == nil {
    63  				tx = types.NewTx(&types.LegacyTx{
    64  					Nonce:    uint64(start + i),
    65  					To:       &common.Address{},
    66  					Value:    big.NewInt(100),
    67  					Gas:      100,
    68  					GasPrice: big.NewInt(int64(gasFeeCap)),
    69  					Data:     nil,
    70  				})
    71  			} else {
    72  				tx = types.NewTx(&types.DynamicFeeTx{
    73  					Nonce:     uint64(start + i),
    74  					To:        &common.Address{},
    75  					Value:     big.NewInt(100),
    76  					Gas:       100,
    77  					GasFeeCap: big.NewInt(int64(gasFeeCap)),
    78  					GasTipCap: big.NewInt(int64(rand.Intn(gasFeeCap + 1))),
    79  					Data:      nil,
    80  				})
    81  				if count == 25 && int64(gasFeeCap) < baseFee.Int64() {
    82  					count = i
    83  				}
    84  			}
    85  			tx, err := types.SignTx(tx, signer, key)
    86  			if err != nil {
    87  				t.Fatalf("failed to sign tx: %s", err)
    88  			}
    89  			groups[addr] = append(groups[addr], &txpool.LazyTransaction{
    90  				Hash:      tx.Hash(),
    91  				Tx:        tx,
    92  				Time:      tx.Time(),
    93  				GasFeeCap: tx.GasFeeCap(),
    94  				GasTipCap: tx.GasTipCap(),
    95  			})
    96  		}
    97  		expectedCount += count
    98  	}
    99  	// Sort the transactions and cross check the nonce ordering
   100  	txset := newTransactionsByPriceAndNonce(signer, groups, baseFee)
   101  
   102  	txs := types.Transactions{}
   103  	for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
   104  		txs = append(txs, tx.Tx)
   105  		txset.Shift()
   106  	}
   107  	if len(txs) != expectedCount {
   108  		t.Errorf("expected %d transactions, found %d", expectedCount, len(txs))
   109  	}
   110  	for i, txi := range txs {
   111  		fromi, _ := types.Sender(signer, txi)
   112  
   113  		// Make sure the nonce order is valid
   114  		for j, txj := range txs[i+1:] {
   115  			fromj, _ := types.Sender(signer, txj)
   116  			if fromi == fromj && txi.Nonce() > txj.Nonce() {
   117  				t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
   118  			}
   119  		}
   120  		// If the next tx has different from account, the price must be lower than the current one
   121  		if i+1 < len(txs) {
   122  			next := txs[i+1]
   123  			fromNext, _ := types.Sender(signer, next)
   124  			tip, err := txi.EffectiveGasTip(baseFee)
   125  			nextTip, nextErr := next.EffectiveGasTip(baseFee)
   126  			if err != nil || nextErr != nil {
   127  				t.Errorf("error calculating effective tip: %v, %v", err, nextErr)
   128  			}
   129  			if fromi != fromNext && tip.Cmp(nextTip) < 0 {
   130  				t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
   131  			}
   132  		}
   133  	}
   134  }
   135  
   136  // Tests that if multiple transactions have the same price, the ones seen earlier
   137  // are prioritized to avoid network spam attacks aiming for a specific ordering.
   138  func TestTransactionTimeSort(t *testing.T) {
   139  	// Generate a batch of accounts to start with
   140  	keys := make([]*ecdsa.PrivateKey, 5)
   141  	for i := 0; i < len(keys); i++ {
   142  		keys[i], _ = crypto.GenerateKey()
   143  	}
   144  	signer := types.HomesteadSigner{}
   145  
   146  	// Generate a batch of transactions with overlapping prices, but different creation times
   147  	groups := map[common.Address][]*txpool.LazyTransaction{}
   148  	for start, key := range keys {
   149  		addr := crypto.PubkeyToAddress(key.PublicKey)
   150  
   151  		tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key)
   152  		tx.SetTime(time.Unix(0, int64(len(keys)-start)))
   153  
   154  		groups[addr] = append(groups[addr], &txpool.LazyTransaction{
   155  			Hash:      tx.Hash(),
   156  			Tx:        tx,
   157  			Time:      tx.Time(),
   158  			GasFeeCap: tx.GasFeeCap(),
   159  			GasTipCap: tx.GasTipCap(),
   160  		})
   161  	}
   162  	// Sort the transactions and cross check the nonce ordering
   163  	txset := newTransactionsByPriceAndNonce(signer, groups, nil)
   164  
   165  	txs := types.Transactions{}
   166  	for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
   167  		txs = append(txs, tx.Tx)
   168  		txset.Shift()
   169  	}
   170  	if len(txs) != len(keys) {
   171  		t.Errorf("expected %d transactions, found %d", len(keys), len(txs))
   172  	}
   173  	for i, txi := range txs {
   174  		fromi, _ := types.Sender(signer, txi)
   175  		if i+1 < len(txs) {
   176  			next := txs[i+1]
   177  			fromNext, _ := types.Sender(signer, next)
   178  
   179  			if txi.GasPrice().Cmp(next.GasPrice()) < 0 {
   180  				t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
   181  			}
   182  			// Make sure time order is ascending if the txs have the same gas price
   183  			if txi.GasPrice().Cmp(next.GasPrice()) == 0 && txi.Time().After(next.Time()) {
   184  				t.Errorf("invalid received time ordering: tx #%d (A=%x T=%v) > tx #%d (A=%x T=%v)", i, fromi[:4], txi.Time(), i+1, fromNext[:4], next.Time())
   185  			}
   186  		}
   187  	}
   188  }