Merge branch 'ethereum:master' into portal

This commit is contained in:
Chen Kai 2024-02-20 18:40:35 +08:00 committed by GitHub
commit 1221812f67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 257 additions and 95 deletions

View file

@ -371,7 +371,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres
} }
p.head, p.state = head, state p.head, p.state = head, state
// Index all transactions on disk and delete anything inprocessable // Index all transactions on disk and delete anything unprocessable
var fails []uint64 var fails []uint64
index := func(id uint64, size uint32, blob []byte) { index := func(id uint64, size uint32, blob []byte) {
if p.parseTransaction(id, size, blob) != nil { if p.parseTransaction(id, size, blob) != nil {
@ -1446,7 +1446,12 @@ func (p *BlobPool) drop() {
// //
// The transactions can also be pre-filtered by the dynamic fee components to // The transactions can also be pre-filtered by the dynamic fee components to
// reduce allocations and load on downstream subsystems. // reduce allocations and load on downstream subsystems.
func (p *BlobPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *uint256.Int) map[common.Address][]*txpool.LazyTransaction { func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction {
// If only plain transactions are requested, this pool is unsuitable as it
// contains none, don't even bother.
if filter.OnlyPlainTxs {
return nil
}
// Track the amount of time waiting to retrieve the list of pending blob txs // Track the amount of time waiting to retrieve the list of pending blob txs
// from the pool and the amount of time actually spent on assembling the data. // from the pool and the amount of time actually spent on assembling the data.
// The latter will be pretty much moot, but we've kept it to have symmetric // The latter will be pretty much moot, but we've kept it to have symmetric
@ -1456,29 +1461,30 @@ func (p *BlobPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *u
pendwaitHist.Update(time.Since(pendStart).Nanoseconds()) pendwaitHist.Update(time.Since(pendStart).Nanoseconds())
defer p.lock.RUnlock() defer p.lock.RUnlock()
defer func(start time.Time) { execStart := time.Now()
pendtimeHist.Update(time.Since(start).Nanoseconds()) defer func() {
}(time.Now()) pendtimeHist.Update(time.Since(execStart).Nanoseconds())
}()
pending := make(map[common.Address][]*txpool.LazyTransaction) pending := make(map[common.Address][]*txpool.LazyTransaction, len(p.index))
for addr, txs := range p.index { for addr, txs := range p.index {
var lazies []*txpool.LazyTransaction lazies := make([]*txpool.LazyTransaction, 0, len(txs))
for _, tx := range txs { for _, tx := range txs {
// If transaction filtering was requested, discard badly priced ones // If transaction filtering was requested, discard badly priced ones
if minTip != nil && baseFee != nil { if filter.MinTip != nil && filter.BaseFee != nil {
if tx.execFeeCap.Lt(baseFee) { if tx.execFeeCap.Lt(filter.BaseFee) {
break // basefee too low, cannot be included, discard rest of txs from the account break // basefee too low, cannot be included, discard rest of txs from the account
} }
tip := new(uint256.Int).Sub(tx.execFeeCap, baseFee) tip := new(uint256.Int).Sub(tx.execFeeCap, filter.BaseFee)
if tip.Gt(tx.execTipCap) { if tip.Gt(tx.execTipCap) {
tip = tx.execTipCap tip = tx.execTipCap
} }
if tip.Lt(minTip) { if tip.Lt(filter.MinTip) {
break // allowed or remaining tip too low, cannot be included, discard rest of txs from the account break // allowed or remaining tip too low, cannot be included, discard rest of txs from the account
} }
} }
if blobFee != nil { if filter.BlobFee != nil {
if tx.blobFeeCap.Lt(blobFee) { if tx.blobFeeCap.Lt(filter.BlobFee) {
break // blobfee too low, cannot be included, discard rest of txs from the account break // blobfee too low, cannot be included, discard rest of txs from the account
} }
} }
@ -1486,9 +1492,9 @@ func (p *BlobPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *u
lazies = append(lazies, &txpool.LazyTransaction{ lazies = append(lazies, &txpool.LazyTransaction{
Pool: p, Pool: p,
Hash: tx.hash, Hash: tx.hash,
Time: time.Now(), // TODO(karalabe): Maybe save these and use that? Time: execStart, // TODO(karalabe): Maybe save these and use that?
GasFeeCap: tx.execFeeCap.ToBig(), GasFeeCap: tx.execFeeCap,
GasTipCap: tx.execTipCap.ToBig(), GasTipCap: tx.execTipCap,
Gas: tx.execGas, Gas: tx.execGas,
BlobGas: tx.blobGas, BlobGas: tx.blobGas,
}) })

View file

@ -185,7 +185,7 @@ func makeTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64,
return types.MustSignNewTx(key, types.LatestSigner(testChainConfig), blobtx) return types.MustSignNewTx(key, types.LatestSigner(testChainConfig), blobtx)
} }
// makeUnsignedTx is a utility method to construct a random blob tranasaction // makeUnsignedTx is a utility method to construct a random blob transaction
// without signing it. // without signing it.
func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64) *types.BlobTx { func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64) *types.BlobTx {
return &types.BlobTx{ return &types.BlobTx{
@ -391,7 +391,7 @@ func TestOpenDrops(t *testing.T) {
id, _ := store.Put(blob) id, _ := store.Put(blob)
filled[id] = struct{}{} filled[id] = struct{}{}
} }
// Insert a sequence of transactions with partially passed nonces to veirfy // Insert a sequence of transactions with partially passed nonces to verify
// that the included part of the set will get dropped (case 4). // that the included part of the set will get dropped (case 4).
var ( var (
overlapper, _ = crypto.GenerateKey() overlapper, _ = crypto.GenerateKey()
@ -1288,3 +1288,65 @@ func TestAdd(t *testing.T) {
pool.Close() pool.Close()
} }
} }
// Benchmarks the time it takes to assemble the lazy pending transaction list
// from the pool contents.
func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) }
func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) }
func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) }
func benchmarkPoolPending(b *testing.B, datacap uint64) {
// Calculate the maximum number of transaction that would fit into the pool
// and generate a set of random accounts to seed them with.
capacity := datacap / params.BlobTxBlobGasPerBlob
var (
basefee = uint64(1050)
blobfee = uint64(105)
signer = types.LatestSigner(testChainConfig)
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
chain = &testBlockChain{
config: testChainConfig,
basefee: uint256.NewInt(basefee),
blobfee: uint256.NewInt(blobfee),
statedb: statedb,
}
pool = New(Config{Datadir: ""}, chain)
)
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
b.Fatalf("failed to create blob pool: %v", err)
}
// Fill the pool up with one random transaction from each account with the
// same price and everything to maximize the worst case scenario
for i := 0; i < int(capacity); i++ {
blobtx := makeUnsignedTx(0, 10, basefee+10, blobfee)
blobtx.R = uint256.NewInt(1)
blobtx.S = uint256.NewInt(uint64(100 + i))
blobtx.V = uint256.NewInt(0)
tx := types.NewTx(blobtx)
addr, err := types.Sender(signer, tx)
if err != nil {
b.Fatal(err)
}
statedb.AddBalance(addr, uint256.NewInt(1_000_000_000))
pool.add(tx)
}
statedb.Commit(0, true)
defer pool.Close()
// Benchmark assembling the pending
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
p := pool.Pending(txpool.PendingFilter{
MinTip: uint256.NewInt(1),
BaseFee: chain.basefee,
BlobFee: chain.blobfee,
})
if len(p) != int(capacity) {
b.Fatalf("have %d want %d", len(p), capacity)
}
}
}

View file

@ -30,7 +30,7 @@ import (
// transaction from each account to determine which account to evict from. // transaction from each account to determine which account to evict from.
// //
// The heap internally tracks a slice of cheapest transactions from each account // The heap internally tracks a slice of cheapest transactions from each account
// and a mapping from addresses to indices for direct removals/udates. // and a mapping from addresses to indices for direct removals/updates.
// //
// The goal of the heap is to decide which account has the worst bottleneck to // The goal of the heap is to decide which account has the worst bottleneck to
// evict transactions from. // evict transactions from.

View file

@ -64,7 +64,7 @@ func BenchmarkDynamicFeeJumpCalculation(b *testing.B) {
// Benchmarks how many priority recalculations can be done. // Benchmarks how many priority recalculations can be done.
func BenchmarkPriorityCalculation(b *testing.B) { func BenchmarkPriorityCalculation(b *testing.B) {
// The basefee and blob fee is constant for all transactions across a block, // The basefee and blob fee is constant for all transactions across a block,
// so we can assume theit absolute jump counts can be pre-computed. // so we can assume their absolute jump counts can be pre-computed.
basefee := uint256.NewInt(17_200_000_000) // 17.2 Gwei is the 22.03.2023 zero-emission basefee, random number basefee := uint256.NewInt(17_200_000_000) // 17.2 Gwei is the 22.03.2023 zero-emission basefee, random number
blobfee := uint256.NewInt(123_456_789_000) // Completely random, no idea what this will be blobfee := uint256.NewInt(123_456_789_000) // Completely random, no idea what this will be

View file

@ -522,7 +522,12 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction,
// //
// The transactions can also be pre-filtered by the dynamic fee components to // The transactions can also be pre-filtered by the dynamic fee components to
// reduce allocations and load on downstream subsystems. // reduce allocations and load on downstream subsystems.
func (pool *LegacyPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *uint256.Int) map[common.Address][]*txpool.LazyTransaction { func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction {
// If only blob transactions are requested, this pool is unsuitable as it
// contains none, don't even bother.
if filter.OnlyBlobTxs {
return nil
}
pool.mu.Lock() pool.mu.Lock()
defer pool.mu.Unlock() defer pool.mu.Unlock()
@ -531,13 +536,12 @@ func (pool *LegacyPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobF
minTipBig *big.Int minTipBig *big.Int
baseFeeBig *big.Int baseFeeBig *big.Int
) )
if minTip != nil { if filter.MinTip != nil {
minTipBig = minTip.ToBig() minTipBig = filter.MinTip.ToBig()
} }
if baseFee != nil { if filter.BaseFee != nil {
baseFeeBig = baseFee.ToBig() baseFeeBig = filter.BaseFee.ToBig()
} }
pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending)) pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending))
for addr, list := range pool.pending { for addr, list := range pool.pending {
txs := list.Flatten() txs := list.Flatten()
@ -559,8 +563,8 @@ func (pool *LegacyPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobF
Hash: txs[i].Hash(), Hash: txs[i].Hash(),
Tx: txs[i], Tx: txs[i],
Time: txs[i].Time(), Time: txs[i].Time(),
GasFeeCap: txs[i].GasFeeCap(), GasFeeCap: uint256.MustFromBig(txs[i].GasFeeCap()),
GasTipCap: txs[i].GasTipCap(), GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()),
Gas: txs[i].Gas(), Gas: txs[i].Gas(),
BlobGas: txs[i].BlobGas(), BlobGas: txs[i].BlobGas(),
} }

View file

@ -35,9 +35,9 @@ type LazyTransaction struct {
Hash common.Hash // Transaction hash to pull up if needed Hash common.Hash // Transaction hash to pull up if needed
Tx *types.Transaction // Transaction if already resolved Tx *types.Transaction // Transaction if already resolved
Time time.Time // Time when the transaction was first seen Time time.Time // Time when the transaction was first seen
GasFeeCap *big.Int // Maximum fee per gas the transaction may consume GasFeeCap *uint256.Int // Maximum fee per gas the transaction may consume
GasTipCap *big.Int // Maximum miner tip per gas the transaction can pay GasTipCap *uint256.Int // Maximum miner tip per gas the transaction can pay
Gas uint64 // Amount of gas required by the transaction Gas uint64 // Amount of gas required by the transaction
BlobGas uint64 // Amount of blob gas required by the transaction BlobGas uint64 // Amount of blob gas required by the transaction
@ -70,6 +70,21 @@ type LazyResolver interface {
// may request (and relinquish) exclusive access to certain addresses. // may request (and relinquish) exclusive access to certain addresses.
type AddressReserver func(addr common.Address, reserve bool) error type AddressReserver func(addr common.Address, reserve bool) error
// PendingFilter is a collection of filter rules to allow retrieving a subset
// of transactions for announcement or mining.
//
// Note, the entries here are not arbitrary useful filters, rather each one has
// a very specific call site in mind and each one can be evaluated very cheaply
// by the pool implementations. Only add new ones that satisfy those constraints.
type PendingFilter struct {
MinTip *uint256.Int // Minimum miner tip required to include a transaction
BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction
BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction
OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling)
OnlyBlobTxs bool // Return only blob transactions (block blob-space filling)
}
// SubPool represents a specialized transaction pool that lives on its own (e.g. // SubPool represents a specialized transaction pool that lives on its own (e.g.
// blob pool). Since independent of how many specialized pools we have, they do // blob pool). Since independent of how many specialized pools we have, they do
// need to be updated in lockstep and assemble into one coherent view for block // need to be updated in lockstep and assemble into one coherent view for block
@ -118,7 +133,7 @@ type SubPool interface {
// //
// The transactions can also be pre-filtered by the dynamic fee components to // The transactions can also be pre-filtered by the dynamic fee components to
// reduce allocations and load on downstream subsystems. // reduce allocations and load on downstream subsystems.
Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *uint256.Int) map[common.Address][]*LazyTransaction Pending(filter PendingFilter) map[common.Address][]*LazyTransaction
// SubscribeTransactions subscribes to new transaction events. The subscriber // SubscribeTransactions subscribes to new transaction events. The subscriber
// can decide whether to receive notifications only for newly seen transactions // can decide whether to receive notifications only for newly seen transactions

View file

@ -28,7 +28,6 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/holiman/uint256"
) )
// TxStatus is the current status of a transaction as seen by the pool. // TxStatus is the current status of a transaction as seen by the pool.
@ -357,10 +356,10 @@ func (p *TxPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
// //
// The transactions can also be pre-filtered by the dynamic fee components to // The transactions can also be pre-filtered by the dynamic fee components to
// reduce allocations and load on downstream subsystems. // reduce allocations and load on downstream subsystems.
func (p *TxPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *uint256.Int) map[common.Address][]*LazyTransaction { func (p *TxPool) Pending(filter PendingFilter) map[common.Address][]*LazyTransaction {
txs := make(map[common.Address][]*LazyTransaction) txs := make(map[common.Address][]*LazyTransaction)
for _, subpool := range p.subpools { for _, subpool := range p.subpools {
for addr, set := range subpool.Pending(minTip, baseFee, blobFee) { for addr, set := range subpool.Pending(filter) {
txs[addr] = set txs[addr] = set
} }
} }

View file

@ -17,6 +17,8 @@
package vm package vm
import ( import (
"math"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -359,7 +361,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
) )
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow { if overflow {
uint64CodeOffset = 0xffffffffffffffff uint64CodeOffset = math.MaxUint64
} }
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64()) codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
@ -377,7 +379,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
) )
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow { if overflow {
uint64CodeOffset = 0xffffffffffffffff uint64CodeOffset = math.MaxUint64
} }
addr := common.Address(a.Bytes20()) addr := common.Address(a.Bytes20())
codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())

View file

@ -187,7 +187,12 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
// outside of this function, as part of the dynamic gas, and that will make it // outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers. // also become correctly reported to tracers.
contract.Gas += coldCost contract.Gas += coldCost
return gas + coldCost, nil
var overflow bool
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
} }
} }

View file

@ -292,7 +292,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
} }
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
pending := b.eth.txPool.Pending(nil, nil, nil) pending := b.eth.txPool.Pending(txpool.PendingFilter{})
var txs types.Transactions var txs types.Transactions
for _, batch := range pending { for _, batch := range pending {
for _, lazy := range batch { for _, lazy := range batch {

View file

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -263,7 +264,7 @@ func (c *SimulatedBeacon) Rollback() {
// Fork sets the head to the provided hash. // Fork sets the head to the provided hash.
func (c *SimulatedBeacon) Fork(parentHash common.Hash) error { func (c *SimulatedBeacon) Fork(parentHash common.Hash) error {
if len(c.eth.TxPool().Pending(nil, nil, nil)) != 0 { if len(c.eth.TxPool().Pending(txpool.PendingFilter{})) != 0 {
return errors.New("pending block dirty") return errors.New("pending block dirty")
} }
parent := c.eth.BlockChain().GetBlockByHash(parentHash) parent := c.eth.BlockChain().GetBlockByHash(parentHash)
@ -275,7 +276,7 @@ func (c *SimulatedBeacon) Fork(parentHash common.Hash) error {
// AdjustTime creates a new block with an adjusted timestamp. // AdjustTime creates a new block with an adjusted timestamp.
func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error { func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error {
if len(c.eth.TxPool().Pending(nil, nil, nil)) != 0 { if len(c.eth.TxPool().Pending(txpool.PendingFilter{})) != 0 {
return errors.New("could not adjust time on non-empty block") return errors.New("could not adjust time on non-empty block")
} }
parent := c.eth.BlockChain().CurrentBlock() parent := c.eth.BlockChain().CurrentBlock()

View file

@ -42,7 +42,6 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256"
) )
const ( const (
@ -74,7 +73,7 @@ type txPool interface {
// Pending should return pending transactions. // Pending should return pending transactions.
// The slice should be modifiable by the caller. // The slice should be modifiable by the caller.
Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *uint256.Int) map[common.Address][]*txpool.LazyTransaction Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction
// SubscribeTransactions subscribes to new transaction events. The subscriber // SubscribeTransactions subscribes to new transaction events. The subscriber
// can decide whether to receive notifications only for newly seen transactions // can decide whether to receive notifications only for newly seen transactions

View file

@ -93,7 +93,7 @@ func (p *testTxPool) Add(txs []*types.Transaction, local bool, sync bool) []erro
} }
// Pending returns all the transactions known to the pool // Pending returns all the transactions known to the pool
func (p *testTxPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *uint256.Int) map[common.Address][]*txpool.LazyTransaction { func (p *testTxPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction {
p.lock.RLock() p.lock.RLock()
defer p.lock.RUnlock() defer p.lock.RUnlock()
@ -112,8 +112,8 @@ func (p *testTxPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: tx, Tx: tx,
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
GasTipCap: tx.GasTipCap(), GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(), Gas: tx.Gas(),
BlobGas: tx.BlobGas(), BlobGas: tx.BlobGas(),
}) })

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -36,7 +37,7 @@ const (
// syncTransactions starts sending all currently pending transactions to the given peer. // syncTransactions starts sending all currently pending transactions to the given peer.
func (h *handler) syncTransactions(p *eth.Peer) { func (h *handler) syncTransactions(p *eth.Peer) {
var hashes []common.Hash var hashes []common.Hash
for _, batch := range h.txpool.Pending(nil, nil, nil) { for _, batch := range h.txpool.Pending(txpool.PendingFilter{OnlyPlainTxs: true}) {
for _, tx := range batch { for _, tx := range batch {
hashes = append(hashes, tx.Hash) hashes = append(hashes, tx.Hash)
} }

View file

@ -611,6 +611,10 @@ func (s *Service) reportBlock(conn *connWrapper, block *types.Block) error {
// Gather the block details from the header or block chain // Gather the block details from the header or block chain
details := s.assembleBlockStats(block) details := s.assembleBlockStats(block)
// Short circuit if the block detail is not available.
if details == nil {
return nil
}
// Assemble the block report and send it to the server // Assemble the block report and send it to the server
log.Trace("Sending new block to ethstats", "number", details.Number, "hash", details.Hash) log.Trace("Sending new block to ethstats", "number", details.Number, "hash", details.Hash)
@ -638,10 +642,16 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats {
// check if backend is a full node // check if backend is a full node
fullBackend, ok := s.backend.(fullNodeBackend) fullBackend, ok := s.backend.(fullNodeBackend)
if ok { if ok {
// Retrieve current chain head if no block is given.
if block == nil { if block == nil {
head := fullBackend.CurrentBlock() head := fullBackend.CurrentBlock()
block, _ = fullBackend.BlockByNumber(context.Background(), rpc.BlockNumber(head.Number.Uint64())) block, _ = fullBackend.BlockByNumber(context.Background(), rpc.BlockNumber(head.Number.Uint64()))
} }
// Short circuit if no block is available. It might happen when
// the blockchain is reorging.
if block == nil {
return nil
}
header = block.Header() header = block.Header()
td = fullBackend.GetTd(context.Background(), header.Hash()) td = fullBackend.GetTd(context.Background(), header.Hash())

View file

@ -1818,6 +1818,7 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha
tx *types.Transaction tx *types.Transaction
err error err error
) )
b.SetPoS()
switch i { switch i {
case 0: case 0:
// transfer 1000wei // transfer 1000wei
@ -1866,7 +1867,6 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha
b.AddTx(tx) b.AddTx(tx)
txHashes[i] = tx.Hash() txHashes[i] = tx.Hash()
} }
b.SetPoS()
}) })
return backend, txHashes return backend, txHashes
} }

View file

@ -21,28 +21,31 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256"
) )
// txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap // txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap
type txWithMinerFee struct { type txWithMinerFee struct {
tx *txpool.LazyTransaction tx *txpool.LazyTransaction
from common.Address from common.Address
fees *big.Int fees *uint256.Int
} }
// newTxWithMinerFee creates a wrapped transaction, calculating the effective // newTxWithMinerFee creates a wrapped transaction, calculating the effective
// miner gasTipCap if a base fee is provided. // miner gasTipCap if a base fee is provided.
// Returns error in case of a negative effective miner gasTipCap. // Returns error in case of a negative effective miner gasTipCap.
func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *big.Int) (*txWithMinerFee, error) { func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *uint256.Int) (*txWithMinerFee, error) {
tip := new(big.Int).Set(tx.GasTipCap) tip := new(uint256.Int).Set(tx.GasTipCap)
if baseFee != nil { if baseFee != nil {
if tx.GasFeeCap.Cmp(baseFee) < 0 { if tx.GasFeeCap.Cmp(baseFee) < 0 {
return nil, types.ErrGasFeeCapTooLow return nil, types.ErrGasFeeCapTooLow
} }
tip = math.BigMin(tx.GasTipCap, new(big.Int).Sub(tx.GasFeeCap, baseFee)) tip = new(uint256.Int).Sub(tx.GasFeeCap, baseFee)
if tip.Gt(tx.GasTipCap) {
tip = tx.GasTipCap
}
} }
return &txWithMinerFee{ return &txWithMinerFee{
tx: tx, tx: tx,
@ -87,7 +90,7 @@ type transactionsByPriceAndNonce struct {
txs map[common.Address][]*txpool.LazyTransaction // Per account nonce-sorted list of transactions txs map[common.Address][]*txpool.LazyTransaction // Per account nonce-sorted list of transactions
heads txByPriceAndTime // Next transaction for each unique account (price heap) heads txByPriceAndTime // Next transaction for each unique account (price heap)
signer types.Signer // Signer for the set of transactions signer types.Signer // Signer for the set of transactions
baseFee *big.Int // Current base fee baseFee *uint256.Int // Current base fee
} }
// newTransactionsByPriceAndNonce creates a transaction set that can retrieve // newTransactionsByPriceAndNonce creates a transaction set that can retrieve
@ -96,10 +99,15 @@ type transactionsByPriceAndNonce struct {
// Note, the input map is reowned so the caller should not interact any more with // Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor. // if after providing it to the constructor.
func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address][]*txpool.LazyTransaction, baseFee *big.Int) *transactionsByPriceAndNonce { func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address][]*txpool.LazyTransaction, baseFee *big.Int) *transactionsByPriceAndNonce {
// Convert the basefee from header format to uint256 format
var baseFeeUint *uint256.Int
if baseFee != nil {
baseFeeUint = uint256.MustFromBig(baseFee)
}
// Initialize a price and received time based heap with the head transactions // Initialize a price and received time based heap with the head transactions
heads := make(txByPriceAndTime, 0, len(txs)) heads := make(txByPriceAndTime, 0, len(txs))
for from, accTxs := range txs { for from, accTxs := range txs {
wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFee) wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFeeUint)
if err != nil { if err != nil {
delete(txs, from) delete(txs, from)
continue continue
@ -114,12 +122,12 @@ func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address]
txs: txs, txs: txs,
heads: heads, heads: heads,
signer: signer, signer: signer,
baseFee: baseFee, baseFee: baseFeeUint,
} }
} }
// Peek returns the next transaction by price. // Peek returns the next transaction by price.
func (t *transactionsByPriceAndNonce) Peek() (*txpool.LazyTransaction, *big.Int) { func (t *transactionsByPriceAndNonce) Peek() (*txpool.LazyTransaction, *uint256.Int) {
if len(t.heads) == 0 { if len(t.heads) == 0 {
return nil, nil return nil, nil
} }
@ -145,3 +153,14 @@ func (t *transactionsByPriceAndNonce) Shift() {
func (t *transactionsByPriceAndNonce) Pop() { func (t *transactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads) heap.Pop(&t.heads)
} }
// Empty returns if the price heap is empty. It can be used to check it simpler
// than calling peek and checking for nil return.
func (t *transactionsByPriceAndNonce) Empty() bool {
return len(t.heads) == 0
}
// Clear removes the entire content of the heap.
func (t *transactionsByPriceAndNonce) Clear() {
t.heads, t.txs = nil, nil
}

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/holiman/uint256"
) )
func TestTransactionPriceNonceSortLegacy(t *testing.T) { func TestTransactionPriceNonceSortLegacy(t *testing.T) {
@ -92,8 +93,8 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: tx, Tx: tx,
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
GasTipCap: tx.GasTipCap(), GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(), Gas: tx.Gas(),
BlobGas: tx.BlobGas(), BlobGas: tx.BlobGas(),
}) })
@ -160,8 +161,8 @@ func TestTransactionTimeSort(t *testing.T) {
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: tx, Tx: tx,
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
GasTipCap: tx.GasTipCap(), GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(), Gas: tx.Gas(),
BlobGas: tx.BlobGas(), BlobGas: tx.BlobGas(),
}) })

View file

@ -206,7 +206,7 @@ type worker struct {
mu sync.RWMutex // The lock used to protect the coinbase and extra fields mu sync.RWMutex // The lock used to protect the coinbase and extra fields
coinbase common.Address coinbase common.Address
extra []byte extra []byte
tip *big.Int // Minimum tip needed for non-local transaction to include them tip *uint256.Int // Minimum tip needed for non-local transaction to include them
pendingMu sync.RWMutex pendingMu sync.RWMutex
pendingTasks map[common.Hash]*task pendingTasks map[common.Hash]*task
@ -253,7 +253,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
isLocalBlock: isLocalBlock, isLocalBlock: isLocalBlock,
coinbase: config.Etherbase, coinbase: config.Etherbase,
extra: config.ExtraData, extra: config.ExtraData,
tip: config.GasPrice, tip: uint256.MustFromBig(config.GasPrice),
pendingTasks: make(map[common.Hash]*task), pendingTasks: make(map[common.Hash]*task),
txsCh: make(chan core.NewTxsEvent, txChanSize), txsCh: make(chan core.NewTxsEvent, txChanSize),
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
@ -334,7 +334,7 @@ func (w *worker) setExtra(extra []byte) {
func (w *worker) setGasTip(tip *big.Int) { func (w *worker) setGasTip(tip *big.Int) {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
w.tip = tip w.tip = uint256.MustFromBig(tip)
} }
// setRecommitInterval updates the interval for miner sealing work recommitting. // setRecommitInterval updates the interval for miner sealing work recommitting.
@ -556,15 +556,17 @@ func (w *worker) mainLoop() {
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: nil, // Do *not* set this! We need to resolve it later to pull blobs in Tx: nil, // Do *not* set this! We need to resolve it later to pull blobs in
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
GasTipCap: tx.GasTipCap(), GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(), Gas: tx.Gas(),
BlobGas: tx.BlobGas(), BlobGas: tx.BlobGas(),
}) })
} }
txset := newTransactionsByPriceAndNonce(w.current.signer, txs, w.current.header.BaseFee) plainTxs := newTransactionsByPriceAndNonce(w.current.signer, txs, w.current.header.BaseFee) // Mixed bag of everrything, yolo
blobTxs := newTransactionsByPriceAndNonce(w.current.signer, nil, w.current.header.BaseFee) // Empty bag, don't bother optimising
tcount := w.current.tcount tcount := w.current.tcount
w.commitTransactions(w.current, txset, nil, new(big.Int)) w.commitTransactions(w.current, plainTxs, blobTxs, nil)
// Only update the snapshot if any new transactions were added // Only update the snapshot if any new transactions were added
// to the pending block // to the pending block
@ -802,7 +804,7 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction) (*typ
return receipt, err return receipt, err
} }
func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32, minTip *big.Int) error { func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
gasLimit := env.header.GasLimit gasLimit := env.header.GasLimit
if env.gasPool == nil { if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit) env.gasPool = new(core.GasPool).AddGas(gasLimit)
@ -821,8 +823,33 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
break break
} }
// If we don't have enough blob space for any further blob transactions,
// skip that list altogether
if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= params.MaxBlobGasPerBlock {
log.Trace("Not enough blob space for further blob transactions")
blobTxs.Clear()
// Fall though to pick up any plain txs
}
// Retrieve the next transaction and abort if all done. // Retrieve the next transaction and abort if all done.
ltx, tip := txs.Peek() var (
ltx *txpool.LazyTransaction
txs *transactionsByPriceAndNonce
)
pltx, ptip := plainTxs.Peek()
bltx, btip := blobTxs.Peek()
switch {
case pltx == nil:
txs, ltx = blobTxs, bltx
case bltx == nil:
txs, ltx = plainTxs, pltx
default:
if ptip.Lt(btip) {
txs, ltx = blobTxs, bltx
} else {
txs, ltx = plainTxs, pltx
}
}
if ltx == nil { if ltx == nil {
break break
} }
@ -837,11 +864,6 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
txs.Pop() txs.Pop()
continue continue
} }
// If we don't receive enough tip for the next transaction, skip the account
if tip.Cmp(minTip) < 0 {
log.Trace("Not enough tip for transaction", "hash", ltx.Hash, "tip", tip, "needed", minTip)
break // If the next-best is too low, surely no better will be available
}
// Transaction seems to fit, pull it up from the pool // Transaction seems to fit, pull it up from the pool
tx := ltx.Resolve() tx := ltx.Resolve()
if tx == nil { if tx == nil {
@ -1005,35 +1027,49 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
w.mu.RUnlock() w.mu.RUnlock()
// Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees // Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees
var baseFee *uint256.Int filter := txpool.PendingFilter{
MinTip: tip,
}
if env.header.BaseFee != nil { if env.header.BaseFee != nil {
baseFee = uint256.MustFromBig(env.header.BaseFee) filter.BaseFee = uint256.MustFromBig(env.header.BaseFee)
} }
var blobFee *uint256.Int
if env.header.ExcessBlobGas != nil { if env.header.ExcessBlobGas != nil {
blobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas)) filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas))
} }
pending := w.eth.TxPool().Pending(uint256.MustFromBig(tip), baseFee, blobFee) filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false
pendingPlainTxs := w.eth.TxPool().Pending(filter)
filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true
pendingBlobTxs := w.eth.TxPool().Pending(filter)
// Split the pending transactions into locals and remotes. // Split the pending transactions into locals and remotes.
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending localPlainTxs, remotePlainTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingPlainTxs
localBlobTxs, remoteBlobTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingBlobTxs
for _, account := range w.eth.TxPool().Locals() { for _, account := range w.eth.TxPool().Locals() {
if txs := remoteTxs[account]; len(txs) > 0 { if txs := remotePlainTxs[account]; len(txs) > 0 {
delete(remoteTxs, account) delete(remotePlainTxs, account)
localTxs[account] = txs localPlainTxs[account] = txs
}
if txs := remoteBlobTxs[account]; len(txs) > 0 {
delete(remoteBlobTxs, account)
localBlobTxs[account] = txs
} }
} }
// Fill the block with all available pending transactions. // Fill the block with all available pending transactions.
if len(localTxs) > 0 { if len(localPlainTxs) > 0 || len(localBlobTxs) > 0 {
txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) plainTxs := newTransactionsByPriceAndNonce(env.signer, localPlainTxs, env.header.BaseFee)
if err := w.commitTransactions(env, txs, interrupt, new(big.Int)); err != nil { blobTxs := newTransactionsByPriceAndNonce(env.signer, localBlobTxs, env.header.BaseFee)
if err := w.commitTransactions(env, plainTxs, blobTxs, interrupt); err != nil {
return err return err
} }
} }
if len(remoteTxs) > 0 { if len(remotePlainTxs) > 0 || len(remoteBlobTxs) > 0 {
txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) plainTxs := newTransactionsByPriceAndNonce(env.signer, remotePlainTxs, env.header.BaseFee)
if err := w.commitTransactions(env, txs, interrupt, tip); err != nil { blobTxs := newTransactionsByPriceAndNonce(env.signer, remoteBlobTxs, env.header.BaseFee)
if err := w.commitTransactions(env, plainTxs, blobTxs, interrupt); err != nil {
return err return err
} }
} }

View file

@ -910,6 +910,8 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
if chainID == nil { if chainID == nil {
chainID = new(big.Int) chainID = new(big.Int)
} }
// disallow setting Merge out of order
isMerge = isMerge && c.IsLondon(num)
return Rules{ return Rules{
ChainID: new(big.Int).Set(chainID), ChainID: new(big.Int).Set(chainID),
IsHomestead: c.IsHomestead(num), IsHomestead: c.IsHomestead(num),
@ -923,9 +925,9 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
IsBerlin: c.IsBerlin(num), IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num), IsLondon: c.IsLondon(num),
IsMerge: isMerge, IsMerge: isMerge,
IsShanghai: c.IsShanghai(num, timestamp), IsShanghai: isMerge && c.IsShanghai(num, timestamp),
IsCancun: c.IsCancun(num, timestamp), IsCancun: isMerge && c.IsCancun(num, timestamp),
IsPrague: c.IsPrague(num, timestamp), IsPrague: isMerge && c.IsPrague(num, timestamp),
IsVerkle: c.IsVerkle(num, timestamp), IsVerkle: isMerge && c.IsVerkle(num, timestamp),
} }
} }