mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 13:21:37 +00:00
Switch LazyTransaction gas caps from *big.Int to *uint256.Int and convert once at pending retrieval time. In miner ordering, keep fee comparisons on uint256 and precompute TRC21 gas price in uint256 form to avoid repeated big-int conversions in heap comparisons. Also update affected tests and call sites in legacypool/worker/helper paths. Compatibility: LazyTransaction exported field types changed (GasFeeCap/GasTipCap).
265 lines
9.5 KiB
Go
265 lines
9.5 KiB
Go
// Copyright 2014 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package miner
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"math/big"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/XinFinOrg/XDPoSChain/common"
|
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
|
"github.com/holiman/uint256"
|
|
)
|
|
|
|
func TestTransactionPriceNonceSortLegacy(t *testing.T) {
|
|
testTransactionPriceNonceSort(t, nil)
|
|
}
|
|
|
|
func TestTransactionPriceNonceSort1559(t *testing.T) {
|
|
testTransactionPriceNonceSort(t, big.NewInt(0))
|
|
testTransactionPriceNonceSort(t, big.NewInt(5))
|
|
testTransactionPriceNonceSort(t, big.NewInt(50))
|
|
}
|
|
|
|
// Tests that transactions can be correctly sorted according to their price in
|
|
// decreasing order, but at the same time with increasing nonces when issued by
|
|
// the same account.
|
|
func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
|
|
// Generate a batch of accounts to start with
|
|
keys := make([]*ecdsa.PrivateKey, 25)
|
|
for i := 0; i < len(keys); i++ {
|
|
keys[i], _ = crypto.GenerateKey()
|
|
}
|
|
signer := types.LatestSignerForChainID(common.Big1)
|
|
|
|
// Generate a batch of transactions with overlapping values, but shifted nonces
|
|
groups := map[common.Address][]*txpool.LazyTransaction{}
|
|
expectedCount := 0
|
|
for start, key := range keys {
|
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
|
count := 25
|
|
for i := 0; i < 25; i++ {
|
|
var tx *types.Transaction
|
|
gasFeeCap := rand.Intn(50)
|
|
if baseFee == nil {
|
|
tx = types.NewTx(&types.LegacyTx{
|
|
Nonce: uint64(start + i),
|
|
To: &common.Address{},
|
|
Value: big.NewInt(100),
|
|
Gas: 100,
|
|
GasPrice: big.NewInt(int64(gasFeeCap)),
|
|
Data: nil,
|
|
})
|
|
} else {
|
|
tx = types.NewTx(&types.DynamicFeeTx{
|
|
Nonce: uint64(start + i),
|
|
To: &common.Address{},
|
|
Value: big.NewInt(100),
|
|
Gas: 100,
|
|
GasFeeCap: big.NewInt(int64(gasFeeCap)),
|
|
GasTipCap: big.NewInt(int64(rand.Intn(gasFeeCap + 1))),
|
|
Data: nil,
|
|
})
|
|
if count == 25 && int64(gasFeeCap) < baseFee.Int64() {
|
|
count = i
|
|
}
|
|
}
|
|
tx, err := types.SignTx(tx, signer, key)
|
|
if err != nil {
|
|
t.Fatalf("failed to sign tx: %s", err)
|
|
}
|
|
groups[addr] = append(groups[addr], &txpool.LazyTransaction{
|
|
Hash: tx.Hash(),
|
|
Tx: tx,
|
|
Time: tx.Time(),
|
|
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
|
|
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
|
|
})
|
|
}
|
|
expectedCount += count
|
|
}
|
|
// Sort the transactions and cross check the nonce ordering
|
|
txset, _ := newTransactionsByPriceAndNonce(signer, groups, map[common.Address]*big.Int{}, baseFee)
|
|
|
|
txs := types.Transactions{}
|
|
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
|
|
txs = append(txs, tx.Tx)
|
|
txset.Shift()
|
|
}
|
|
if len(txs) != expectedCount {
|
|
t.Errorf("expected %d transactions, found %d", expectedCount, len(txs))
|
|
}
|
|
for i, txi := range txs {
|
|
fromi, _ := types.Sender(signer, txi)
|
|
|
|
// Make sure the nonce order is valid
|
|
for j, txj := range txs[i+1:] {
|
|
fromj, _ := types.Sender(signer, txj)
|
|
if fromi == fromj && txi.Nonce() > txj.Nonce() {
|
|
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())
|
|
}
|
|
}
|
|
// If the next tx has different from account, the price must be lower than the current one
|
|
if i+1 < len(txs) {
|
|
next := txs[i+1]
|
|
fromNext, _ := types.Sender(signer, next)
|
|
tip, err := txi.EffectiveGasTip(baseFee)
|
|
nextTip, nextErr := next.EffectiveGasTip(baseFee)
|
|
if err != nil || nextErr != nil {
|
|
t.Errorf("error calculating effective tip: %v, %v", err, nextErr)
|
|
}
|
|
if fromi != fromNext && tip.Cmp(nextTip) < 0 {
|
|
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())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests that if multiple transactions have the same price, the ones seen earlier
|
|
// are prioritized to avoid network spam attacks aiming for a specific ordering.
|
|
func TestTransactionTimeSort(t *testing.T) {
|
|
// Generate a batch of accounts to start with
|
|
keys := make([]*ecdsa.PrivateKey, 5)
|
|
for i := 0; i < len(keys); i++ {
|
|
keys[i], _ = crypto.GenerateKey()
|
|
}
|
|
signer := types.HomesteadSigner{}
|
|
|
|
// Generate a batch of transactions with overlapping prices, but different creation times
|
|
groups := map[common.Address][]*txpool.LazyTransaction{}
|
|
for start, key := range keys {
|
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
|
|
|
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key)
|
|
tx.SetTime(time.Unix(0, int64(len(keys)-start)))
|
|
|
|
groups[addr] = append(groups[addr], &txpool.LazyTransaction{
|
|
Hash: tx.Hash(),
|
|
Tx: tx,
|
|
Time: tx.Time(),
|
|
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
|
|
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
|
|
})
|
|
}
|
|
// Sort the transactions and cross check the nonce ordering
|
|
txset, _ := newTransactionsByPriceAndNonce(signer, groups, map[common.Address]*big.Int{}, nil)
|
|
|
|
txs := types.Transactions{}
|
|
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
|
|
txs = append(txs, tx.Tx)
|
|
txset.Shift()
|
|
}
|
|
if len(txs) != len(keys) {
|
|
t.Errorf("expected %d transactions, found %d", len(keys), len(txs))
|
|
}
|
|
for i, txi := range txs {
|
|
fromi, _ := types.Sender(signer, txi)
|
|
if i+1 < len(txs) {
|
|
next := txs[i+1]
|
|
fromNext, _ := types.Sender(signer, next)
|
|
|
|
if txi.GasPrice().Cmp(next.GasPrice()) < 0 {
|
|
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())
|
|
}
|
|
// Make sure time order is ascending if the txs have the same gas price
|
|
if txi.GasPrice().Cmp(next.GasPrice()) == 0 && txi.Time().After(next.Time()) {
|
|
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())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNewTransactionsByPriceAndNonce_SpecialSeparation uses table-driven tests to verify separation of special and normal transactions.
|
|
func TestNewTransactionsByPriceAndNonce_SpecialSeparation(t *testing.T) {
|
|
signer := types.HomesteadSigner{}
|
|
|
|
genNormalTx := func(nonce uint64, key *ecdsa.PrivateKey) *txpool.LazyTransaction {
|
|
tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0x1234567890123456789012345678901234567890"), big.NewInt(1), 21000, big.NewInt(1), nil), signer, key)
|
|
return &txpool.LazyTransaction{Tx: tx, Hash: tx.Hash(), Time: tx.Time(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), GasTipCap: uint256.MustFromBig(tx.GasTipCap())}
|
|
}
|
|
genSpecialTx := func(nonce uint64, key *ecdsa.PrivateKey) *txpool.LazyTransaction {
|
|
tx, _ := types.SignTx(types.NewTransaction(nonce, common.BlockSignersBinary, big.NewInt(1), 21000, big.NewInt(1), nil), signer, key)
|
|
return &txpool.LazyTransaction{Tx: tx, Hash: tx.Hash(), Time: tx.Time(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), GasTipCap: uint256.MustFromBig(tx.GasTipCap())}
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
normalCount int
|
|
specialCount int
|
|
expectNormal int
|
|
expectSpecial int
|
|
}{
|
|
{"no transactions", 0, 0, 0, 0},
|
|
{"only 1 normal", 1, 0, 1, 0},
|
|
{"only 2 normal", 2, 0, 2, 0},
|
|
{"only 3 normal", 3, 0, 3, 0},
|
|
{"only 1 special", 0, 1, 0, 1},
|
|
{"only 2 special", 0, 2, 0, 2},
|
|
{"only 3 special", 0, 3, 0, 3},
|
|
{"1 normal, 1 special", 1, 1, 1, 1},
|
|
{"2 normal, 2 special", 2, 2, 2, 2},
|
|
{"3 normal, 3 special", 3, 3, 3, 3},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
key, _ := crypto.GenerateKey()
|
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
|
txs := make([]*txpool.LazyTransaction, 0, tc.normalCount+tc.specialCount)
|
|
for i := 0; i < tc.normalCount; i++ {
|
|
txs = append(txs, genNormalTx(uint64(i), key))
|
|
}
|
|
for i := 0; i < tc.specialCount; i++ {
|
|
txs = append(txs, genSpecialTx(uint64(tc.normalCount+i), key))
|
|
}
|
|
group := map[common.Address][]*txpool.LazyTransaction{}
|
|
if len(txs) > 0 {
|
|
group[addr] = txs
|
|
}
|
|
txset, specialTxs := newTransactionsByPriceAndNonce(signer, group, map[common.Address]*big.Int{}, nil)
|
|
|
|
// Check special transactions
|
|
if len(specialTxs) != tc.expectSpecial {
|
|
t.Errorf("expected %d special txs, got %d", tc.expectSpecial, len(specialTxs))
|
|
}
|
|
for _, tx := range specialTxs {
|
|
if tx.To() == nil || *tx.To() != common.BlockSignersBinary {
|
|
t.Errorf("specialTxs contains non-special tx: %v", tx)
|
|
}
|
|
}
|
|
|
|
// Check normal transactions
|
|
normalCount := 0
|
|
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
|
|
resolved := tx.Resolve()
|
|
if resolved == nil || resolved.To() == nil || *resolved.To() == common.BlockSignersBinary {
|
|
t.Errorf("txset contains special or nil-to tx: %v", resolved)
|
|
}
|
|
normalCount++
|
|
txset.Shift()
|
|
}
|
|
if normalCount != tc.expectNormal {
|
|
t.Errorf("expected %d normal txs, got %d", tc.expectNormal, normalCount)
|
|
}
|
|
})
|
|
}
|
|
}
|