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