beacon/engine, core, eth/catalyst: verify if a block satisfies the inclusion list constraints

This commit is contained in:
Jihoon Song 2025-02-25 15:43:41 +09:00
parent 1986e8134b
commit dffe6d31a5
9 changed files with 195 additions and 67 deletions

View file

@ -237,8 +237,8 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
// and that the blockhash of the constructed block matches the parameters. Nil // and that the blockhash of the constructed block matches the parameters. Nil
// Withdrawals value will propagate through the returned block. Empty // Withdrawals value will propagate through the returned block. Empty
// Withdrawals value must be passed via non-nil, length 0 value in data. // Withdrawals value must be passed via non-nil, length 0 value in data.
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, inclusionListTxs []*types.Transaction) (*types.Block, error) {
block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests) block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests, inclusionListTxs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -251,7 +251,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
// ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used // ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used
// for stateless execution, so it skips checking if the executable data hashes to // 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). // 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) { func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, inclusionListTxs []*types.Transaction) (*types.Block, error) {
txs, err := decodeTransactions(data.Transactions) txs, err := decodeTransactions(data.Transactions)
if err != nil { if err != nil {
return nil, err return nil, err
@ -317,7 +317,8 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
} }
return types.NewBlockWithHeader(header). return types.NewBlockWithHeader(header).
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}). WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}).
WithWitness(data.ExecutionWitness), WithWitness(data.ExecutionWitness).
WithInclusionListTransactions(inclusionListTxs),
nil nil
} }

View file

@ -1741,7 +1741,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
} }
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large) _, n, _, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
return n, err return n, err
} }
@ -1753,10 +1753,10 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// racey behaviour. If a sidechain import is in progress, and the historic state // racey behaviour. If a sidechain import is in progress, and the historic state
// is imported, but then new canon-head is added before the actual sidechain // is imported, but then new canon-head is added before the actual sidechain
// completes, then the historic state could be pruned again // completes, then the historic state could be pruned again
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) { func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, *bool, error) {
// If the chain is terminating, don't even bother starting up. // If the chain is terminating, don't even bother starting up.
if bc.insertStopped() { if bc.insertStopped() {
return nil, 0, nil return nil, 0, nil, nil
} }
if atomic.AddInt32(&bc.blockProcCounter, 1) == 1 { if atomic.AddInt32(&bc.blockProcCounter, 1) == 1 {
@ -1821,7 +1821,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
for block != nil && bc.skipBlock(err, it) { for block != nil && bc.skipBlock(err, it) {
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash()) log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
if err := bc.writeKnownBlock(block); err != nil { if err := bc.writeKnownBlock(block); err != nil {
return nil, it.index, err return nil, it.index, nil, err
} }
lastCanon = block lastCanon = block
@ -1840,7 +1840,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
// We're post-merge and the parent is pruned, try to recover the parent state // We're post-merge and the parent is pruned, try to recover the parent state
log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash()) log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash())
_, err := bc.recoverAncestors(block, makeWitness) _, err := bc.recoverAncestors(block, makeWitness)
return nil, it.index, err return nil, it.index, nil, err
} }
// Some other error(except ErrKnownBlock) occurred, abort. // Some other error(except ErrKnownBlock) occurred, abort.
// ErrKnownBlock is allowed here since some known blocks // ErrKnownBlock is allowed here since some known blocks
@ -1848,7 +1848,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
case err != nil && !errors.Is(err, ErrKnownBlock): case err != nil && !errors.Is(err, ErrKnownBlock):
stats.ignored += len(it.chain) stats.ignored += len(it.chain)
bc.reportBlock(block, nil, err) bc.reportBlock(block, nil, err)
return nil, it.index, err return nil, it.index, nil, err
} }
// Track the singleton witness from this chain insertion (if any) // Track the singleton witness from this chain insertion (if any)
var witness *stateless.Witness var witness *stateless.Witness
@ -1889,7 +1889,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
"hash", block.Hash(), "number", block.NumberU64()) "hash", block.Hash(), "number", block.NumberU64())
} }
if err := bc.writeKnownBlock(block); err != nil { if err := bc.writeKnownBlock(block); err != nil {
return nil, it.index, err return nil, it.index, nil, err
} }
stats.processed++ stats.processed++
if bc.logger != nil && bc.logger.OnSkippedBlock != nil { if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
@ -1913,7 +1913,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
start := time.Now() start := time.Now()
res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1) res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
if err != nil { if err != nil {
return nil, it.index, err return nil, it.index, nil, err
} }
// Report the import stats before returning the various results // Report the import stats before returning the various results
stats.processed++ stats.processed++
@ -1934,7 +1934,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
// After merge we expect few side chains. Simply count // After merge we expect few side chains. Simply count
// all blocks the CL gives us for GC processing time // all blocks the CL gives us for GC processing time
bc.gcproc += res.procTime bc.gcproc += res.procTime
return witness, it.index, nil // Direct block insertion of a single block return witness, it.index, res.inclusionListSatisfied, nil // Direct block insertion of a single block
} }
switch res.status { switch res.status {
case CanonStatTy: case CanonStatTy:
@ -1965,7 +1965,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
} }
stats.ignored += it.remaining() stats.ignored += it.remaining()
return witness, it.index, err return witness, it.index, nil, err
} }
// blockProcessingResult is a summary of block processing // blockProcessingResult is a summary of block processing
@ -1975,6 +1975,7 @@ type blockProcessingResult struct {
procTime time.Duration procTime time.Duration
status WriteStatus status WriteStatus
witness *stateless.Witness witness *stateless.Witness
inclusionListSatisfied *bool
} }
func (bpr *blockProcessingResult) Witness() *stateless.Witness { func (bpr *blockProcessingResult) Witness() *stateless.Witness {
@ -2183,6 +2184,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
procTime: proctime, procTime: proctime,
status: status, status: status,
witness: witness, witness: witness,
inclusionListSatisfied: res.InclusionListSatisfied,
}, nil }, nil
} }
@ -2193,7 +2195,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
// The method writes all (header-and-body-valid) blocks to disk, then tries to // The method writes all (header-and-body-valid) blocks to disk, then tries to
// switch over to the new chain if the TD exceeded the current chain. // switch over to the new chain if the TD exceeded the current chain.
// insertSideChain is only used pre-merge. // insertSideChain is only used pre-merge.
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, error) { func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, *bool, error) {
var current = bc.CurrentBlock() var current = bc.CurrentBlock()
// The first sidechain block error is already verified to be ErrPrunedAncestor. // The first sidechain block error is already verified to be ErrPrunedAncestor.
@ -2222,13 +2224,13 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
// If someone legitimately side-mines blocks, they would still be imported as usual. However, // If someone legitimately side-mines blocks, they would still be imported as usual. However,
// we cannot risk writing unverified blocks to disk when they obviously target the pruning // we cannot risk writing unverified blocks to disk when they obviously target the pruning
// mechanism. // mechanism.
return nil, it.index, errors.New("sidechain ghost-state attack") return nil, it.index, nil, errors.New("sidechain ghost-state attack")
} }
} }
if !bc.HasBlock(block.Hash(), block.NumberU64()) { if !bc.HasBlock(block.Hash(), block.NumberU64()) {
start := time.Now() start := time.Now()
if err := bc.writeBlockWithoutState(block); err != nil { if err := bc.writeBlockWithoutState(block); err != nil {
return nil, it.index, err return nil, it.index, nil, err
} }
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(), log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
@ -2245,7 +2247,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
for parent != nil && !bc.HasState(parent.Root) { for parent != nil && !bc.HasState(parent.Root) {
if bc.stateRecoverable(parent.Root) { if bc.stateRecoverable(parent.Root) {
if err := bc.triedb.Recover(parent.Root); err != nil { if err := bc.triedb.Recover(parent.Root); err != nil {
return nil, 0, err return nil, 0, nil, err
} }
break break
} }
@ -2255,7 +2257,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1) parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
} }
if parent == nil { if parent == nil {
return nil, it.index, errors.New("missing parent") return nil, it.index, nil, errors.New("missing parent")
} }
// Import all the pruned blocks to make the state available // Import all the pruned blocks to make the state available
var ( var (
@ -2274,15 +2276,15 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
// memory here. // memory here.
if len(blocks) >= 2048 || memory > 64*1024*1024 { if len(blocks) >= 2048 || memory > 64*1024*1024 {
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64()) log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
if _, _, err := bc.insertChain(blocks, true, false); err != nil { if _, _, _, err := bc.insertChain(blocks, true, false); err != nil {
return nil, 0, err return nil, 0, nil, err
} }
blocks, memory = blocks[:0], 0 blocks, memory = blocks[:0], 0
// If the chain is terminating, stop processing blocks // If the chain is terminating, stop processing blocks
if bc.insertStopped() { if bc.insertStopped() {
log.Debug("Abort during blocks processing") log.Debug("Abort during blocks processing")
return nil, 0, nil return nil, 0, nil, nil
} }
} }
} }
@ -2290,7 +2292,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64()) log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
return bc.insertChain(blocks, true, makeWitness) return bc.insertChain(blocks, true, makeWitness)
} }
return nil, 0, nil return nil, 0, nil, nil
} }
// recoverAncestors finds the closest ancestor with available state and re-execute // recoverAncestors finds the closest ancestor with available state and re-execute
@ -2337,7 +2339,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co
} else { } else {
b = bc.GetBlock(hashes[i], numbers[i]) b = bc.GetBlock(hashes[i], numbers[i])
} }
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil { if _, _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil {
return b.ParentHash(), err return b.ParentHash(), err
} }
} }
@ -2564,14 +2566,14 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
// The key difference between the InsertChain is it won't do the canonical chain // The key difference between the InsertChain is it won't do the canonical chain
// updating. It relies on the additional SetCanonical call to finalize the entire // updating. It relies on the additional SetCanonical call to finalize the entire
// procedure. // procedure.
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) { func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, *bool, error) {
if !bc.chainmu.TryLock() { if !bc.chainmu.TryLock() {
return nil, errChainStopped return nil, nil, errChainStopped
} }
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness) witness, _, inclusionListSatisfied, err := bc.insertChain(types.Blocks{block}, false, makeWitness)
return witness, err return witness, inclusionListSatisfied, err
} }
// SetCanonical rewinds the chain to set the new head block as the specified // SetCanonical rewinds the chain to set the new head block as the specified

View file

@ -3456,7 +3456,7 @@ func testSetCanonical(t *testing.T, scheme string) {
gen.AddTx(tx) gen.AddTx(tx)
}) })
for _, block := range side { for _, block := range side {
_, err := chain.InsertBlockWithoutSetHead(block, false) _, _, err := chain.InsertBlockWithoutSetHead(block, false)
if err != nil { if err != nil {
t.Fatalf("Failed to insert into chain: %v", err) t.Fatalf("Failed to insert into chain: %v", err)
} }

View file

@ -108,6 +108,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...) allLogs = append(allLogs, receipt.Logs...)
} }
isInclusionListSatisfied := ValidateInclusionListTransactions(evm, block, statedb, block.InclusionListTransactions())
// Read requests if Prague is enabled. // Read requests if Prague is enabled.
var requests [][]byte var requests [][]byte
if config.IsPrague(block.Number(), block.Time()) { if config.IsPrague(block.Number(), block.Time()) {
@ -134,6 +137,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
Requests: requests, Requests: requests,
Logs: allLogs, Logs: allLogs,
GasUsed: *usedGas, GasUsed: *usedGas,
InclusionListSatisfied: &isInclusionListSatisfied,
}, nil }, nil
} }
@ -315,6 +319,66 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
return nil return nil
} }
// ValidateInclusionListTransactions verifies that all transactions in the inclusion list
// are either included in the block or cannot be appended at the end of the block.
// If any appendable transaction is found, the block fails to meet the inclusion list constraints.
func ValidateInclusionListTransactions(evm *vm.EVM, block *types.Block, statedb *state.StateDB, inclusionListTxs []*types.Transaction) bool {
// Create a map of transaction hashes present in the block.
isIncludedTx := make(map[common.Hash]bool)
for _, tx := range block.Transactions() {
isIncludedTx[tx.Hash()] = true
}
// Get the block's gas limit and gas left.
gasLimit := block.GasLimit()
gasLeft := gasLimit - block.GasUsed()
// Make a singer to get transaction senders.
signer := types.MakeSigner(evm.ChainConfig(), block.Number(), block.Time())
// Iterate over each transaction in the inclusion list and check if it is either included in the block or cannot be placed at the end of the block.
for _, tx := range inclusionListTxs {
// Check if the transaction is included in the block.
if isIncludedTx[tx.Hash()] {
continue
}
// EIP-7805 doesn't support blob transactions in inclusion list
if tx.Type() == types.BlobTxType {
continue
}
// Check if there is not enough gas left to execute the transaction.
if tx.Gas() > gasLeft {
continue
}
// Get the transaction sender.
from, err := types.Sender(signer, tx)
if err != nil {
continue
}
// Check if the sender has not enough balance to cover the transaction cost.
balance := statedb.GetBalance(from).ToBig()
cost := tx.Cost()
if balance.Cmp(cost) < 0 {
continue
}
// Check if the sender has a nonce that doesn't match with the transaction nonce.
nonce := statedb.GetNonce(from)
if nonce != tx.Nonce() {
continue
}
// This transaction could have been appended at the end of the block. The block fails to satisfy the inclusion list constraints.
return false
}
return true
}
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5") var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
// ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by // ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by

View file

@ -57,4 +57,5 @@ type ProcessResult struct {
Requests [][]byte Requests [][]byte
Logs []*types.Log Logs []*types.Log
GasUsed uint64 GasUsed uint64
InclusionListSatisfied *bool
} }

View file

@ -214,6 +214,11 @@ type Block struct {
// that process it. // that process it.
witness *ExecutionWitness witness *ExecutionWitness
// inclusion list transactions are not an encoded part of the block body.
// It is held in Block in order for easy relaying to the places
// that process it.
inclusionListTxs Transactions
// caches // caches
hash atomic.Pointer[common.Hash] hash atomic.Pointer[common.Hash]
size atomic.Uint64 size atomic.Uint64
@ -432,6 +437,9 @@ func (b *Block) BlobGasUsed() *uint64 {
// ExecutionWitness returns the verkle execution witneess + proof for a block // ExecutionWitness returns the verkle execution witneess + proof for a block
func (b *Block) ExecutionWitness() *ExecutionWitness { return b.witness } func (b *Block) ExecutionWitness() *ExecutionWitness { return b.witness }
// InclusionListTransactions returns the inclusion list transactions for a block
func (b *Block) InclusionListTransactions() Transactions { return b.inclusionListTxs }
// Size returns the true RLP encoded storage size of the block, either by encoding // Size returns the true RLP encoded storage size of the block, either by encoding
// and returning it, or returning a previously cached value. // and returning it, or returning a previously cached value.
func (b *Block) Size() uint64 { func (b *Block) Size() uint64 {
@ -495,6 +503,7 @@ func (b *Block) WithSeal(header *Header) *Block {
uncles: b.uncles, uncles: b.uncles,
withdrawals: b.withdrawals, withdrawals: b.withdrawals,
witness: b.witness, witness: b.witness,
inclusionListTxs: b.inclusionListTxs,
} }
} }
@ -507,6 +516,7 @@ func (b *Block) WithBody(body Body) *Block {
uncles: make([]*Header, len(body.Uncles)), uncles: make([]*Header, len(body.Uncles)),
withdrawals: slices.Clone(body.Withdrawals), withdrawals: slices.Clone(body.Withdrawals),
witness: b.witness, witness: b.witness,
inclusionListTxs: b.inclusionListTxs,
} }
for i := range body.Uncles { for i := range body.Uncles {
block.uncles[i] = CopyHeader(body.Uncles[i]) block.uncles[i] = CopyHeader(body.Uncles[i])
@ -521,6 +531,18 @@ func (b *Block) WithWitness(witness *ExecutionWitness) *Block {
uncles: b.uncles, uncles: b.uncles,
withdrawals: b.withdrawals, withdrawals: b.withdrawals,
witness: witness, witness: witness,
inclusionListTxs: b.inclusionListTxs,
}
}
func (b *Block) WithInclusionListTransactions(inclusionListTxs Transactions) *Block {
return &Block{
header: b.header,
transactions: b.transactions,
uncles: b.uncles,
withdrawals: b.withdrawals,
witness: b.witness,
inclusionListTxs: inclusionListTxs,
} }
} }

View file

@ -20,6 +20,7 @@ package catalyst
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"reflect" "reflect"
"strconv" "strconv"
"sync" "sync"
@ -31,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"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/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/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
@ -768,7 +770,8 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
defer api.newPayloadLock.Unlock() defer api.newPayloadLock.Unlock()
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, requests) inclusionListTxs := api.getValidInclusionListTransactions(inclusionList)
block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, requests, inclusionListTxs)
if err != nil { if err != nil {
bgu := "nil" bgu := "nil"
if params.BlobGasUsed != nil { if params.BlobGasUsed != nil {
@ -841,7 +844,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil
} }
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number()) log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number())
proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness) proofs, inclusionListSatisfied, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness)
if err != nil { if err != nil {
log.Warn("NewPayload: inserting block failed", "error", err) log.Warn("NewPayload: inserting block failed", "error", err)
@ -852,6 +855,13 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
return api.invalid(err, parent.Header()), nil return api.invalid(err, parent.Header()), nil
} }
if inclusionListSatisfied != nil && !*inclusionListSatisfied {
return engine.PayloadStatusV1{
Status: engine.INCLUSION_LIST_UNSATISFIED,
LatestValidHash: nil,
ValidationError: nil,
}, nil
}
hash := block.Hash() hash := block.Hash()
// If witness collection was requested, inject that into the result too // If witness collection was requested, inject that into the result too
@ -1110,6 +1120,34 @@ func (api *ConsensusAPI) GetPayloadBodiesByRangeV2(start, count hexutil.Uint64)
return api.getBodiesByRange(start, count) return api.getBodiesByRange(start, count)
} }
func (api *ConsensusAPI) getValidInclusionListTransactions(inclusionList types.InclusionList) []*types.Transaction {
txs := types.InclusionListToTransactions(inclusionList)
head := api.eth.BlockChain().CurrentBlock()
signer := types.MakeSigner(api.config(), head.Number, head.Time)
validTxs := make([]*types.Transaction, 0, len(txs))
for _, tx := range txs {
opts := &txpool.ValidationOptions{
Config: api.config(),
Accept: 0 |
1<<types.LegacyTxType |
1<<types.AccessListTxType |
1<<types.DynamicFeeTxType |
1<<types.SetCodeTxType,
MaxSize: params.MaxBytesPerInclusionList,
MinTip: new(big.Int).SetUint64(ethconfig.Defaults.TxPool.PriceLimit),
}
if err := txpool.ValidateTransaction(tx, head, signer, opts); err != nil {
log.Warn("invalid inclusion list transaction", "hash", tx.Hash(), "error", err)
} else {
validTxs = append(validTxs, tx)
}
}
return validTxs
}
func (api *ConsensusAPI) getBodiesByRange(start, count hexutil.Uint64) ([]*engine.ExecutionPayloadBody, error) { func (api *ConsensusAPI) getBodiesByRange(start, count hexutil.Uint64) ([]*engine.ExecutionPayloadBody, error) {
if start == 0 || count == 0 { if start == 0 || count == 0 {
return nil, engine.InvalidParams.With(fmt.Errorf("invalid start or count, start: %v count: %v", start, count)) return nil, engine.InvalidParams.With(fmt.Errorf("invalid start or count, start: %v count: %v", start, count))

View file

@ -310,7 +310,7 @@ func TestEth2NewBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Failed to create the executable data, block %d: %v", i, err) t.Fatalf("Failed to create the executable data, block %d: %v", i, err)
} }
block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil) block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to convert executable data to block %v", err) t.Fatalf("Failed to convert executable data to block %v", err)
} }
@ -352,7 +352,7 @@ func TestEth2NewBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Failed to create the executable data %v", err) t.Fatalf("Failed to create the executable data %v", err)
} }
block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil) block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to convert executable data to block %v", err) t.Fatalf("Failed to convert executable data to block %v", err)
} }
@ -947,7 +947,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
t.Fatal(testErr) t.Fatal(testErr)
} }
} }
block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil) block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to convert executable data to block %v", err) t.Fatalf("Failed to convert executable data to block %v", err)
} }
@ -1541,7 +1541,7 @@ func TestBlockToPayloadWithBlobs(t *testing.T) {
if got := len(envelope.BlobsBundle.Blobs); got != want { if got := len(envelope.BlobsBundle.Blobs); got != want {
t.Fatalf("invalid number of blobs: got %v, want %v", got, want) t.Fatalf("invalid number of blobs: got %v, want %v", got, want)
} }
_, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1), nil, nil) _, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1), nil, nil, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

View file

@ -240,7 +240,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData,
func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash)
block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requests) block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requests, nil)
if err != nil { if err != nil {
bgu := "nil" bgu := "nil"
if params.BlobGasUsed != nil { if params.BlobGasUsed != nil {