mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
Merge 3b852842f9 into deda47f6a1
This commit is contained in:
commit
6dcf9d0d03
8 changed files with 462 additions and 64 deletions
|
|
@ -262,6 +262,18 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
|
|||
// for stateless execution, so it skips checking if the executable data hashes to
|
||||
// the requested hash (stateless has to *compute* the root hash, it's not given).
|
||||
func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) {
|
||||
var requestsHash *common.Hash
|
||||
if requests != nil {
|
||||
h := types.CalcRequestsHash(requests)
|
||||
requestsHash = &h
|
||||
}
|
||||
|
||||
return ExecutableDataToBlockNoHashWithRequestsHash(data, versionedHashes, beaconRoot, requestsHash)
|
||||
}
|
||||
|
||||
// ExecutableDataToBlockNoHashWithRequestsHash does the same thing as
|
||||
// ExecutableDataToBlockNoHash, but it takes a hash of the requests.
|
||||
func ExecutableDataToBlockNoHashWithRequestsHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requestsHash *common.Hash) (*types.Block, error) {
|
||||
txs, err := DecodeTransactions(data.Transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -297,12 +309,6 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
|||
withdrawalsRoot = &h
|
||||
}
|
||||
|
||||
var requestsHash *common.Hash
|
||||
if requests != nil {
|
||||
h := types.CalcRequestsHash(requests)
|
||||
requestsHash = &h
|
||||
}
|
||||
|
||||
header := &types.Header{
|
||||
ParentHash: data.ParentHash,
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
|
|
|
|||
|
|
@ -75,5 +75,5 @@ func (api *testingAPI) BuildBlockV1(parentHash common.Hash, payloadAttributes en
|
|||
Withdrawals: payloadAttributes.Withdrawals,
|
||||
BeaconRoot: payloadAttributes.BeaconRoot,
|
||||
}
|
||||
return api.eth.Miner().BuildTestingPayload(args, txs, buildEmpty, extra)
|
||||
return api.eth.Miner().BuildTestingPayload(args, txs, buildEmpty, extra, nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,6 +90,19 @@ func New(eth Backend, config Config, engine consensus.Engine) *Miner {
|
|||
}
|
||||
}
|
||||
|
||||
// NewWithoutBackend creates a miner with the provided config. It is used in
|
||||
// a testing context.
|
||||
func NewWithoutBackend(chain *core.BlockChain, pool *txpool.TxPool, config Config, engine consensus.Engine) *Miner {
|
||||
return &Miner{
|
||||
config: &config,
|
||||
chainConfig: chain.Config(),
|
||||
engine: engine,
|
||||
txpool: pool,
|
||||
chain: chain,
|
||||
pending: &pending{},
|
||||
}
|
||||
}
|
||||
|
||||
// Pending returns the currently pending block and associated receipts, logs
|
||||
// and statedb. The returned values can be nil in case the pending block is
|
||||
// not initialized.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -339,9 +340,7 @@ func (payload *Payload) updateSpanForDelivery(bSpan trace.Span) {
|
|||
)
|
||||
}
|
||||
|
||||
// BuildTestingPayload is for testing_buildBlockV*. It creates a block with the exact content given
|
||||
// by the parameters instead of using the locally available transactions.
|
||||
func (miner *Miner) BuildTestingPayload(args *BuildPayloadArgs, transactions []*types.Transaction, empty bool, extraData []byte) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
func (miner *Miner) buildPayloadWithOverrides(args *BuildPayloadArgs, transactions []*types.Transaction, empty bool, extraData []byte, gasLimit *uint64) (*newPayloadResult, error) {
|
||||
fullParams := &generateParams{
|
||||
timestamp: args.Timestamp,
|
||||
forceTime: true,
|
||||
|
|
@ -355,10 +354,43 @@ func (miner *Miner) BuildTestingPayload(args *BuildPayloadArgs, transactions []*
|
|||
forceOverrides: true,
|
||||
overrideExtraData: extraData,
|
||||
overrideTxs: transactions,
|
||||
overrideGasLimit: gasLimit,
|
||||
}
|
||||
res := miner.generateWork(context.Background(), fullParams, false)
|
||||
if res.err != nil {
|
||||
return nil, res.err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// BuildPayloadWithOverrides creates a block with the content given by the parameters instead
|
||||
// of using locally-available transactions. It attempts to include any transactions in the exact
|
||||
// order they are specified.
|
||||
func (miner *Miner) BuildPayloadWithOverrides(args *BuildPayloadArgs, transactions []*types.Transaction, empty bool, extraData []byte, gasLimit *uint64) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
res, err := miner.buildPayloadWithOverrides(args, transactions, empty, extraData, gasLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return engine.BlockToExecutableData(res.block, new(big.Int), res.sidecars, res.requests), nil
|
||||
}
|
||||
|
||||
// BuildTestingPayload is for testing_buildBlockV*. It creates a block with the exact content given
|
||||
// by the parameters instead of using the locally available transactions.
|
||||
// If a block cannot be constructed with the given parameters, an error is returned.
|
||||
func (miner *Miner) BuildTestingPayload(args *BuildPayloadArgs, transactions []*types.Transaction, empty bool, extraData []byte, gasLimit *uint64) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
res, err := miner.buildPayloadWithOverrides(args, transactions, empty, extraData, gasLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// validate that the constructed block contains exactly the transactions specified, and in the specified order.
|
||||
if len(res.block.Transactions()) != len(transactions) {
|
||||
return nil, fmt.Errorf("constructed payload contained different number of txs than specified. want=%d, got=%d\n", len(transactions), len(res.block.Transactions()))
|
||||
}
|
||||
for i, expectedTx := range transactions {
|
||||
if res.block.Transactions()[i].Hash() != expectedTx.Hash() {
|
||||
return nil, fmt.Errorf("constructed block contained unexpected transaction at index %d: %x. expected %x\n", i, res.block.Transactions()[i].Hash(), expectedTx.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
return engine.BlockToExecutableData(res.block, new(big.Int), res.sidecars, res.requests), nil
|
||||
}
|
||||
|
|
|
|||
265
miner/tx_source.go
Normal file
265
miner/tx_source.go
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2026 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 (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// resolveableTransaction represents a transaction of which some of the fields
|
||||
// are available, but the full transaction will be resolved from some source.
|
||||
type resolveableTransaction interface {
|
||||
Hash() common.Hash
|
||||
Resolve() *types.Transaction
|
||||
Gas() uint64
|
||||
BlobGas() uint64
|
||||
}
|
||||
|
||||
// lazyTransaction wraps a txpool.LazyTransaction and implements resolveableTransaction.
|
||||
type lazyTransaction struct {
|
||||
*txpool.LazyTransaction
|
||||
}
|
||||
|
||||
func (l *lazyTransaction) Resolve() *types.Transaction {
|
||||
return l.LazyTransaction.Resolve()
|
||||
}
|
||||
|
||||
func (l *lazyTransaction) Gas() uint64 {
|
||||
return l.LazyTransaction.Gas
|
||||
}
|
||||
|
||||
func (l *lazyTransaction) Hash() common.Hash {
|
||||
return l.LazyTransaction.Hash
|
||||
}
|
||||
|
||||
func (l *lazyTransaction) BlobGas() uint64 {
|
||||
return l.LazyTransaction.BlobGas
|
||||
}
|
||||
|
||||
// resolvedTransaction implements resolveableTransaction for a transaction
|
||||
// that was resolved in the first place. used for building payloads with
|
||||
// tx set overrides
|
||||
type resolvedTransaction struct {
|
||||
*types.Transaction
|
||||
}
|
||||
|
||||
func (r *resolvedTransaction) Gas() uint64 {
|
||||
return r.Transaction.Gas()
|
||||
}
|
||||
|
||||
func (r *resolvedTransaction) Resolve() *types.Transaction {
|
||||
return r.Transaction
|
||||
}
|
||||
|
||||
// transactionQueue represents a list of transactions
|
||||
// which are queued for inclusion.
|
||||
type transactionQueue interface {
|
||||
// Shift removes all transactions from the sender of the
|
||||
// current highest-priority transaction for inclusion
|
||||
Shift()
|
||||
// Pop removes the highest-priority transaction from
|
||||
// the queue.
|
||||
Pop()
|
||||
// HasBlobTxs returns true if the sender of the current
|
||||
// highest-priority transaction has queued blob transactions.
|
||||
HasBlobTxs() bool
|
||||
// ClearBlobTxs removes all blob txs from the sender of the
|
||||
// current highest-priority transaction from the queue.
|
||||
ClearBlobTxs()
|
||||
}
|
||||
|
||||
// transactionSource is a source that the miner can
|
||||
type transactionSource interface {
|
||||
// Peek returns the next transaction which should be evaluated for inclusion
|
||||
// and a transactionQueue for the sender account.
|
||||
Peek() (resolveableTransaction, transactionQueue)
|
||||
}
|
||||
|
||||
// plainTxQueue implements transactionQueue for a set of non-blob transactions
|
||||
type plainTxQueue struct {
|
||||
txs *transactionsByPriceAndNonce
|
||||
}
|
||||
|
||||
func (q *plainTxQueue) Shift() { q.txs.Shift() }
|
||||
func (q *plainTxQueue) Pop() { q.txs.Pop() }
|
||||
|
||||
// HasBlobTxs always returns false: plain queues never carry blob transactions.
|
||||
func (q *plainTxQueue) HasBlobTxs() bool { return false }
|
||||
|
||||
// ClearBlobTxs is a no-op for plain queues and always returns false.
|
||||
func (q *plainTxQueue) ClearBlobTxs() {}
|
||||
|
||||
// blobTxQueue implements transactionQueue for a set of blob transactions
|
||||
type blobTxQueue struct {
|
||||
txs *transactionsByPriceAndNonce
|
||||
}
|
||||
|
||||
func (q *blobTxQueue) Shift() {
|
||||
if q.txs != nil {
|
||||
q.txs.Shift()
|
||||
}
|
||||
}
|
||||
|
||||
func (q *blobTxQueue) Pop() {
|
||||
if q.txs != nil {
|
||||
q.txs.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
// HasBlobTxs always returns true: this queue exclusively holds blob transactions.
|
||||
func (q *blobTxQueue) HasBlobTxs() bool { return true }
|
||||
|
||||
func (q *blobTxQueue) ClearBlobTxs() {
|
||||
if q.txs != nil {
|
||||
q.txs.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
// feeOrderedTxSource implements transactionSource for the standard strategy
|
||||
// of transaction inclusion: prioritize by profitability.
|
||||
type feeOrderedTxSource struct {
|
||||
plainQueue *plainTxQueue
|
||||
blobQueue *blobTxQueue
|
||||
}
|
||||
|
||||
// newFeeOrderedTxSource creates a feeOrderedTxSource from separate plain and blob
|
||||
// transaction sets. Either argument may be nil (e.g. when there are no blob
|
||||
// transactions pending).
|
||||
func newFeeOrderedTxSource(plain, blob *transactionsByPriceAndNonce) *feeOrderedTxSource {
|
||||
s := &feeOrderedTxSource{
|
||||
plainQueue: &plainTxQueue{txs: plain},
|
||||
blobQueue: &blobTxQueue{txs: blob},
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Peek returns the next most profitable queued transaction
|
||||
func (s *feeOrderedTxSource) Peek() (resolveableTransaction, transactionQueue) {
|
||||
pTx, pTip := s.plainQueue.txs.Peek()
|
||||
bTx, bTip := s.blobQueue.txs.Peek()
|
||||
|
||||
switch {
|
||||
case pTx == nil && bTx == nil:
|
||||
return nil, nil
|
||||
case pTx == nil:
|
||||
return &lazyTransaction{bTx}, s.blobQueue
|
||||
case bTx == nil:
|
||||
return &lazyTransaction{pTx}, s.plainQueue
|
||||
default:
|
||||
if bTip.Gt(pTip) {
|
||||
return &lazyTransaction{bTx}, s.blobQueue
|
||||
} else {
|
||||
return &lazyTransaction{pTx}, s.plainQueue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// orderedTxSource implements transactionSource and transactionQueue.
|
||||
// The transaction set and order is based on a pre-determined list.
|
||||
type orderedTxSource struct {
|
||||
// txs ordered as they are intended to be included in a payload
|
||||
txs types.Transactions
|
||||
// txs ordered by sender and nonce
|
||||
orderedTxs map[common.Address][]*types.Transaction
|
||||
signer types.Signer
|
||||
}
|
||||
|
||||
func newOrderedTxSource(txs types.Transactions, signer types.Signer) (*orderedTxSource, error) {
|
||||
orderedTxs := make(map[common.Address][]*types.Transaction)
|
||||
for _, tx := range txs {
|
||||
from, _ := signer.Sender(tx)
|
||||
if _, ok := orderedTxs[from]; !ok {
|
||||
orderedTxs[from] = []*types.Transaction{tx}
|
||||
continue
|
||||
}
|
||||
|
||||
// validate that no transactions from the same sender have conflicting nonces.
|
||||
senderTxs := orderedTxs[from]
|
||||
for _, senderTx := range senderTxs {
|
||||
if tx.Nonce() == senderTx.Nonce() {
|
||||
return nil, fmt.Errorf("conflicting transaction nonces")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
insertionPoint int
|
||||
senderTx *types.Transaction
|
||||
)
|
||||
for insertionPoint, senderTx = range senderTxs {
|
||||
if senderTx.Nonce() > tx.Nonce() {
|
||||
break
|
||||
}
|
||||
}
|
||||
orderedTxs[from] = slices.Insert(senderTxs, insertionPoint, tx)
|
||||
}
|
||||
return &orderedTxSource{txs: txs, signer: signer, orderedTxs: orderedTxs}, nil
|
||||
}
|
||||
|
||||
func (s *orderedTxSource) Pop() {
|
||||
from, _ := s.signer.Sender(s.txs[0])
|
||||
s.txs = slices.DeleteFunc(s.txs, func(tx *types.Transaction) bool {
|
||||
txSender, _ := s.signer.Sender(tx)
|
||||
return txSender == from
|
||||
})
|
||||
delete(s.orderedTxs, from)
|
||||
}
|
||||
|
||||
func (s *orderedTxSource) Shift() {
|
||||
delTx := s.txs[0]
|
||||
sender, _ := s.signer.Sender(delTx)
|
||||
|
||||
// application order of txs might not be by nonce,
|
||||
// so deletion from the nonce-ordered set requires
|
||||
// search
|
||||
s.orderedTxs[sender] = slices.DeleteFunc(s.orderedTxs[sender], func(tx *types.Transaction) bool {
|
||||
return delTx.Hash() == tx.Hash()
|
||||
})
|
||||
|
||||
s.txs = s.txs[1:]
|
||||
}
|
||||
|
||||
func (s *orderedTxSource) HasBlobTxs() bool {
|
||||
for _, tx := range s.txs {
|
||||
if tx.Type() == types.BlobTxType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *orderedTxSource) ClearBlobTxs() {
|
||||
var remainingTxs types.Transactions
|
||||
for _, tx := range s.txs {
|
||||
if tx.Type() != types.BlobTxType {
|
||||
remainingTxs = append(remainingTxs, tx)
|
||||
}
|
||||
}
|
||||
s.txs = remainingTxs
|
||||
}
|
||||
|
||||
func (s *orderedTxSource) Peek() (resolveableTransaction, transactionQueue) {
|
||||
if len(s.txs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return &resolvedTransaction{s.txs[0]}, s
|
||||
}
|
||||
|
|
@ -124,6 +124,7 @@ type generateParams struct {
|
|||
forceOverrides bool // Flag whether we should overwrite extraData and transactions
|
||||
overrideExtraData []byte
|
||||
overrideTxs []*types.Transaction
|
||||
overrideGasLimit *uint64
|
||||
}
|
||||
|
||||
// generateWork generates a sealing block based on the given parameters.
|
||||
|
|
@ -160,15 +161,15 @@ func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams,
|
|||
work.size += uint64(genParam.withdrawals.Size())
|
||||
|
||||
if !genParam.noTxs {
|
||||
// If forceOverrides is true and overrideTxs is not empty, commit the override transactions
|
||||
// If forceOverrides is true, commit the override transactions
|
||||
// otherwise, fill the block with the current transactions from the txpool
|
||||
if genParam.forceOverrides && len(genParam.overrideTxs) > 0 {
|
||||
for _, tx := range genParam.overrideTxs {
|
||||
work.state.SetTxContext(tx.Hash(), work.tcount)
|
||||
if err := miner.commitTransaction(ctx, work, tx); err != nil {
|
||||
// all passed transactions HAVE to be valid at this point
|
||||
return &newPayloadResult{err: err}
|
||||
}
|
||||
if genParam.forceOverrides {
|
||||
overrideSource, err := newOrderedTxSource(genParam.overrideTxs, work.signer)
|
||||
if err != nil {
|
||||
return &newPayloadResult{err: err}
|
||||
}
|
||||
if err := miner.commitTransactions(ctx, work, overrideSource, new(atomic.Int32)); err != nil {
|
||||
log.Warn("block building failed", "err", err)
|
||||
}
|
||||
} else {
|
||||
interrupt := new(atomic.Int32)
|
||||
|
|
@ -281,6 +282,9 @@ func (miner *Miner) prepareWork(ctx context.Context, genParams *generateParams,
|
|||
header.GasLimit = core.CalcGasLimit(parentGasLimit, miner.config.GasCeil)
|
||||
}
|
||||
}
|
||||
if genParams.overrideGasLimit != nil {
|
||||
header.GasLimit = *genParams.overrideGasLimit
|
||||
}
|
||||
// Run the consensus preparation with the default or customized consensus engine.
|
||||
// Note that the `header.Time` may be changed.
|
||||
if err := miner.engine.Prepare(miner.chain, header); err != nil {
|
||||
|
|
@ -410,7 +414,12 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
|
|||
return receipt, nil
|
||||
}
|
||||
|
||||
func (miner *Miner) commitTransactions(ctx context.Context, env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
|
||||
func (miner *Miner) commitTransactions(
|
||||
ctx context.Context,
|
||||
env *environment,
|
||||
allTxs transactionSource,
|
||||
interrupt *atomic.Int32,
|
||||
) error {
|
||||
ctx, _, spanEnd := telemetry.StartSpan(ctx, "miner.commitTransactions")
|
||||
defer spanEnd(nil)
|
||||
isCancun := miner.chainConfig.IsCancun(env.header.Number, env.header.Time)
|
||||
|
|
@ -426,39 +435,20 @@ func (miner *Miner) commitTransactions(ctx context.Context, env *environment, pl
|
|||
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
|
||||
break
|
||||
}
|
||||
// If we don't have enough blob space for any further blob transactions,
|
||||
// skip that list altogether
|
||||
if !blobTxs.Empty() && env.blobs >= miner.maxBlobsPerBlock(env.header.Time) {
|
||||
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.
|
||||
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
|
||||
}
|
||||
}
|
||||
ltx, txs := allTxs.Peek()
|
||||
if ltx == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if txs.HasBlobTxs() && env.blobs >= miner.maxBlobsPerBlock(env.header.Time) {
|
||||
log.Trace("Not enough blob space for further blob transactions")
|
||||
txs.ClearBlobTxs()
|
||||
continue
|
||||
}
|
||||
// If we don't have enough space for the next transaction, skip the account.
|
||||
if env.gasPool.Gas() < ltx.Gas {
|
||||
log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas)
|
||||
if env.gasPool.Gas() < ltx.Gas() {
|
||||
log.Trace("Not enough gas left for transaction", "hash", ltx.Hash(), "left", env.gasPool.Gas(), "needed", ltx.Gas())
|
||||
txs.Pop()
|
||||
continue
|
||||
}
|
||||
|
|
@ -468,8 +458,8 @@ func (miner *Miner) commitTransactions(ctx context.Context, env *environment, pl
|
|||
// a defined schedule, so we need to verify it's safe to call.
|
||||
if isCancun {
|
||||
left := miner.maxBlobsPerBlock(env.header.Time) - env.blobs
|
||||
if left < int(ltx.BlobGas/params.BlobTxBlobGasPerBlob) {
|
||||
log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas/params.BlobTxBlobGasPerBlob)
|
||||
if left < int(ltx.BlobGas()/params.BlobTxBlobGasPerBlob) {
|
||||
log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash(), "left", left, "needed", ltx.BlobGas()/params.BlobTxBlobGasPerBlob)
|
||||
txs.Pop()
|
||||
continue
|
||||
}
|
||||
|
|
@ -478,7 +468,7 @@ func (miner *Miner) commitTransactions(ctx context.Context, env *environment, pl
|
|||
// Transaction seems to fit, pull it up from the pool
|
||||
tx := ltx.Resolve()
|
||||
if tx == nil {
|
||||
log.Trace("Ignoring evicted transaction", "hash", ltx.Hash)
|
||||
log.Trace("Ignoring evicted transaction", "hash", ltx.Hash())
|
||||
txs.Pop()
|
||||
continue
|
||||
}
|
||||
|
|
@ -495,7 +485,7 @@ func (miner *Miner) commitTransactions(ctx context.Context, env *environment, pl
|
|||
// Check whether the tx is replay protected. If we're not in the EIP155 hf
|
||||
// phase, start ignoring the sender until we do.
|
||||
if tx.Protected() && !miner.chainConfig.IsEIP155(env.header.Number) {
|
||||
log.Trace("Ignoring replay protected transaction", "hash", ltx.Hash, "eip155", miner.chainConfig.EIP155Block)
|
||||
log.Trace("Ignoring replay protected transaction", "hash", ltx.Hash(), "eip155", miner.chainConfig.EIP155Block)
|
||||
txs.Pop()
|
||||
continue
|
||||
}
|
||||
|
|
@ -506,7 +496,7 @@ func (miner *Miner) commitTransactions(ctx context.Context, env *environment, pl
|
|||
switch {
|
||||
case errors.Is(err, core.ErrNonceTooLow):
|
||||
// New head notification data race between the transaction pool and miner, shift
|
||||
log.Trace("Skipping transaction with low nonce", "hash", ltx.Hash, "sender", from, "nonce", tx.Nonce())
|
||||
log.Trace("Skipping transaction with low nonce", "hash", ltx.Hash(), "sender", from, "nonce", tx.Nonce())
|
||||
txs.Shift()
|
||||
|
||||
case errors.Is(err, nil):
|
||||
|
|
@ -516,7 +506,7 @@ func (miner *Miner) commitTransactions(ctx context.Context, env *environment, pl
|
|||
default:
|
||||
// Transaction is regarded as invalid, drop all consecutive transactions from
|
||||
// the same sender because of `nonce-too-high` clause.
|
||||
log.Debug("Transaction failed, account skipped", "hash", ltx.Hash, "err", err)
|
||||
log.Debug("Transaction failed, account skipped", "hash", ltx.Hash(), "err", err)
|
||||
txs.Pop()
|
||||
}
|
||||
}
|
||||
|
|
@ -581,7 +571,7 @@ func (miner *Miner) fillTransactions(ctx context.Context, interrupt *atomic.Int3
|
|||
plainTxs := newTransactionsByPriceAndNonce(env.signer, prioPlainTxs, env.header.BaseFee)
|
||||
blobTxs := newTransactionsByPriceAndNonce(env.signer, prioBlobTxs, env.header.BaseFee)
|
||||
|
||||
if err := miner.commitTransactions(ctx, env, plainTxs, blobTxs, interrupt); err != nil {
|
||||
if err := miner.commitTransactions(ctx, env, newFeeOrderedTxSource(plainTxs, blobTxs), interrupt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -589,7 +579,7 @@ func (miner *Miner) fillTransactions(ctx context.Context, interrupt *atomic.Int3
|
|||
plainTxs := newTransactionsByPriceAndNonce(env.signer, normalPlainTxs, env.header.BaseFee)
|
||||
blobTxs := newTransactionsByPriceAndNonce(env.signer, normalBlobTxs, env.header.BaseFee)
|
||||
|
||||
if err := miner.commitTransactions(ctx, env, plainTxs, blobTxs, interrupt); err != nil {
|
||||
if err := miner.commitTransactions(ctx, env, newFeeOrderedTxSource(plainTxs, blobTxs), interrupt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,26 @@ func TestBlockchain(t *testing.T) {
|
|||
bt.skipLoad(`.*bcFrontierToHomestead/blockChainFrontierWithLargerTDvsHomesteadBlockchain.json`)
|
||||
bt.skipLoad(`.*bcFrontierToHomestead/blockChainFrontierWithLargerTDvsHomesteadBlockchain2.json`)
|
||||
|
||||
/*
|
||||
skip these for the payload building only:
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_at_rlp_limit_with_logs.json (0.45s)
|
||||
block_test.go:110: test with config {snapshotter:false, scheme:hash} failed: mismatch in block hash. wanted 51a93fb2f038278424742e6ceaf476f83fd30ab84580216f08d124e258554b00, got 8dddbd82ec3e09b201230202207069fa64b6fb21a89a3b5e8ab2421adac933ea
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_at_rlp_size_limit_boundary.json (1.30s)
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_at_rlp_size_limit_boundary.json/tests/osaka/eip7934_block_rlp_limit/test_max_block_rlp_size.py::test_block_at_rlp_size_limit_boundary[fork_Osaka-blockchain_test-max_rlp_size] (0.10s)
|
||||
block_test.go:110: test with config {snapshotter:false, scheme:hash} failed: mismatch in block hash. wanted bf2a96a93b4b519a36cc17ba7e5b9de7a2a8efef8f15f00e97d63c067fcae532, got 99be2c1ff6a5ab3b7c80eca8ed12e933417efc4c9fbc73a23873017a4888ac30
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_at_rlp_size_limit_boundary.json/tests/osaka/eip7934_block_rlp_limit/test_max_block_rlp_size.py::test_block_at_rlp_size_limit_boundary[fork_Osaka-blockchain_test-max_rlp_size_minus_1_byte] (0.09s)
|
||||
block_test.go:110: test with config {snapshotter:false, scheme:hash} failed: mismatch in block hash. wanted f01227ceacf73b2fa7492cc36f307535d6734a0de468b42a136bd39561a613ec, got 5a5019a9d576c93e55ad94e2fe2b6ba8881d96c9df4b7d4ea8c4780520fa232e
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_rlp_size_at_limit_with_all_typed_transactions.json (2.51s)
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_rlp_size_at_limit_with_all_typed_transactions.json/tests/osaka/eip7934_block_rlp_limit/test_max_block_rlp_size.py::test_block_rlp_size_at_limit_with_all_typed_transactions[fork_Osaka-typed_transaction_0-blockchain_test] (0.09s)
|
||||
block_test.go:110: test with config {snapshotter:false, scheme:hash} failed: mismatch in block hash. wanted e15a14788a8b960fa9951cdd8f059b9c53ac50ca12c2d63255a437fb2a10a93f, got 194fbad30f9fe3f0e60627a0fcc6eec78884ce117e9d13e04b7a44f8e14c0547
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_rlp_size_at_limit_with_all_typed_transactions.json/tests/osaka/eip7934_block_rlp_limit/test_max_block_rlp_size.py::test_block_rlp_size_at_limit_with_all_typed_transactions[fork_Osaka-typed_transaction_1-blockchain_test] (0.09s)
|
||||
block_test.go:110: test with config {snapshotter:false, scheme:hash} failed: mismatch in block hash. wanted 5ad6568d4e64aa88faa2ad73a249bc7fcd9a36a23629ee6e914a05fa75a87293, got 0f9f047ddfc220c18b53cfc82871a67c4166eebda65bb0c137af87cf20e6c25a
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_rlp_size_at_limit_with_all_typed_transactions.json/tests/osaka/eip7934_block_rlp_limit/test_max_block_rlp_size.py::test_block_rlp_size_at_limit_with_all_typed_transactions[fork_Osaka-typed_transaction_2-blockchain_test] (0.09s)
|
||||
block_test.go:110: test with config {snapshotter:false, scheme:hash} failed: mismatch in block hash. wanted 1570cd16ac3b096866bf5765fdb28571140f70392bda5ac7fbb034e5e9a93acb, got 0d254beb06fe3b4d4ffbe9cd6fa914f402a809e8f60ce311f849431e485ba8d9
|
||||
--- FAIL: TestExecutionSpecBlocktests/osaka/eip7934_block_rlp_limit/test_block_rlp_size_at_limit_with_all_typed_transactions.json/tests/osaka/eip7934_block_rlp_limit/test_max_block_rlp_size.py::test_block_rlp_size_at_limit_with_all_typed_transactions[fork_Osaka-typed_transaction_4-blockchain_test] (0.11s)
|
||||
block_test.go:110: test with config {snapshotter:false, scheme:hash} failed: mismatch in block hash. wanted 8a12b02aaf555c15e83ac9831205b35006c94e6c8aec7d2c99a135507fa4103b, got d65726606a9c28a68cea095b2682872d88cb535ff295b135d156f28d3c2c59f7
|
||||
*/
|
||||
|
||||
// With chain history removal, TDs become unavailable, this transition tests based on TTD are unrunnable
|
||||
bt.skipLoad(`.*bcArrowGlacierToParis/powToPosBlockRejection.json`)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ import (
|
|||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/engine"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
|
|
@ -230,21 +234,48 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
|
|||
}
|
||||
}
|
||||
|
||||
// return a payload version for post-merge hard-forks. otherwise return an error.
|
||||
func getPayloadVersion(header *types.Header, chainConfig *params.ChainConfig) engine.PayloadVersion {
|
||||
switch {
|
||||
case chainConfig.IsPostMerge(header.Number.Uint64(), header.Time):
|
||||
return engine.PayloadV1
|
||||
case chainConfig.IsShanghai(header.Number, header.Time):
|
||||
return engine.PayloadV2
|
||||
case chainConfig.IsPrague(header.Number, header.Time):
|
||||
case chainConfig.IsCancun(header.Number, header.Time):
|
||||
return engine.PayloadV3
|
||||
}
|
||||
panic("invalid post-merge fork")
|
||||
}
|
||||
|
||||
/*
|
||||
See https://ethereum-tests.readthedocs.io/en/latest/blockchain-ref.html
|
||||
|
||||
Whether a block is valid or not is a bit subtle, it's defined by presence of
|
||||
blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
|
||||
invalid and we must verify that we do not accept it.
|
||||
Whether a block is valid or not is a bit subtle, it's defined by presence of
|
||||
blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
|
||||
invalid and we must verify that we do not accept it.
|
||||
|
||||
Since some tests mix valid and invalid blocks we need to check this for every block.
|
||||
Since some tests mix valid and invalid blocks we need to check this for every block.
|
||||
|
||||
If a block is invalid it does not necessarily fail the test, if it's invalidness is
|
||||
expected we are expected to ignore it and continue processing and then validate the
|
||||
post state.
|
||||
If a block is invalid it does not necessarily fail the test, if it's invalidness is
|
||||
expected we are expected to ignore it and continue processing and then validate the
|
||||
post state.
|
||||
|
||||
If the block is post-merge, insertBlocks will construct payloads via the miner,
|
||||
validating that they match exactly blocks in the test which:
|
||||
- are past the merge block
|
||||
- are not specified as invalid by the test
|
||||
- do not contain blob transactions
|
||||
*/
|
||||
func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
|
||||
validBlocks := make([]btBlock, 0)
|
||||
var (
|
||||
pool *txpool.TxPool
|
||||
config = miner.Config{}
|
||||
consensusEngine = beacon.New(ethash.NewFaker())
|
||||
builder = miner.NewWithoutBackend(blockchain, pool, config, consensusEngine)
|
||||
validBlocks = make([]btBlock, 0)
|
||||
)
|
||||
|
||||
// insert the test blocks, which will execute all transactions
|
||||
for bi, b := range t.json.Blocks {
|
||||
cb, err := b.decode()
|
||||
|
|
@ -257,7 +288,48 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
|
|||
}
|
||||
}
|
||||
// RLP decoding worked, try to insert into chain:
|
||||
blocks := types.Blocks{cb}
|
||||
var blocks types.Blocks
|
||||
|
||||
var blockHasBlobTxs bool
|
||||
for _, tx := range cb.Transactions() {
|
||||
if tx.BlobHashes() != nil {
|
||||
blockHasBlobTxs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// test payload building on post-merge blocks that are valid and don't contain blob transactions
|
||||
if !blockHasBlobTxs && b.BlockHeader != nil && (b.BlockHeader.Difficulty == nil || b.BlockHeader.Difficulty.Cmp(common.Big0) == 0) {
|
||||
payloadVersion := getPayloadVersion(cb.Header(), blockchain.Config())
|
||||
|
||||
gasLimit := cb.GasLimit()
|
||||
args := miner.BuildPayloadArgs{
|
||||
Parent: cb.ParentHash(),
|
||||
Timestamp: cb.Time(),
|
||||
FeeRecipient: cb.Coinbase(),
|
||||
Random: cb.MixDigest(),
|
||||
Withdrawals: cb.Withdrawals(),
|
||||
BeaconRoot: cb.BeaconRoot(),
|
||||
SlotNum: cb.SlotNumber(),
|
||||
Version: payloadVersion,
|
||||
}
|
||||
envelope, err := builder.BuildPayloadWithOverrides(&args, cb.Transactions(), false, cb.Extra(), &gasLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
constructedBlock, err := engine.ExecutableDataToBlockNoHashWithRequestsHash(*envelope.ExecutionPayload, nil, cb.BeaconRoot(), cb.RequestsHash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if constructedBlock.Hash() != cb.Hash() {
|
||||
return nil, fmt.Errorf("mismatch in block hash. wanted %x, got %x", cb.Hash(), constructedBlock.Hash())
|
||||
}
|
||||
blocks = types.Blocks{constructedBlock}
|
||||
} else {
|
||||
blocks = types.Blocks{cb}
|
||||
}
|
||||
i, err := blockchain.InsertChain(blocks)
|
||||
if err != nil {
|
||||
if b.BlockHeader == nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue