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