This commit is contained in:
jwasinger 2026-04-10 21:54:16 -07:00 committed by GitHub
commit 6dcf9d0d03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 462 additions and 64 deletions

View file

@ -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,

View file

@ -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)
}

View file

@ -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.

View file

@ -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
View 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
}

View file

@ -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
}
}

View file

@ -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`)

View file

@ -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 {