eip-7547: implement NewInclusionListV1

This commit is contained in:
Marius van der Wijden 2024-03-11 09:05:55 +01:00
parent bca6c40709
commit 4cce9f9970
28 changed files with 446 additions and 159 deletions

View file

@ -57,23 +57,24 @@ type payloadAttributesMarshaling struct {
// ExecutableData is the data necessary to execute an EL payload. // ExecutableData is the data necessary to execute an EL payload.
type ExecutableData struct { type ExecutableData struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot common.Hash `json:"stateRoot" gencodec:"required"` StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom []byte `json:"logsBloom" gencodec:"required"` LogsBloom []byte `json:"logsBloom" gencodec:"required"`
Random common.Hash `json:"prevRandao" gencodec:"required"` Random common.Hash `json:"prevRandao" gencodec:"required"`
Number uint64 `json:"blockNumber" gencodec:"required"` Number uint64 `json:"blockNumber" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasLimit uint64 `json:"gasLimit" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Timestamp uint64 `json:"timestamp" gencodec:"required"` Timestamp uint64 `json:"timestamp" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"` ExtraData []byte `json:"extraData" gencodec:"required"`
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
BlockHash common.Hash `json:"blockHash" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Transactions [][]byte `json:"transactions" gencodec:"required"` Transactions [][]byte `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *uint64 `json:"blobGasUsed"` BlobGasUsed *uint64 `json:"blobGasUsed"`
ExcessBlobGas *uint64 `json:"excessBlobGas"` ExcessBlobGas *uint64 `json:"excessBlobGas"`
InclusionListSummary *types.InclusionListSummary `json:"inclusionListSummary"`
} }
// JSON type overrides for executableData. // JSON type overrides for executableData.
@ -175,7 +176,7 @@ func encodeTransactions(txs []*types.Transaction) [][]byte {
return enc return enc
} }
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { func DecodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
var txs = make([]*types.Transaction, len(enc)) var txs = make([]*types.Transaction, len(enc))
for i, encTx := range enc { for i, encTx := range enc {
var tx types.Transaction var tx types.Transaction
@ -199,7 +200,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
// 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 params. // Withdrawals value must be passed via non-nil, length 0 value in params.
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) { func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
txs, err := decodeTransactions(params.Transactions) txs, err := DecodeTransactions(params.Transactions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -233,28 +234,34 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil)) h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
withdrawalsRoot = &h withdrawalsRoot = &h
} }
header := &types.Header{ var inclusionRoot *common.Hash
ParentHash: params.ParentHash, if params.InclusionListSummary != nil {
UncleHash: types.EmptyUncleHash, h := types.DeriveSha(types.InclusionList{List: params.InclusionListSummary.Summary}, trie.NewStackTrie(nil))
Coinbase: params.FeeRecipient, inclusionRoot = &h
Root: params.StateRoot,
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
ReceiptHash: params.ReceiptsRoot,
Bloom: types.BytesToBloom(params.LogsBloom),
Difficulty: common.Big0,
Number: new(big.Int).SetUint64(params.Number),
GasLimit: params.GasLimit,
GasUsed: params.GasUsed,
Time: params.Timestamp,
BaseFee: params.BaseFeePerGas,
Extra: params.ExtraData,
MixDigest: params.Random,
WithdrawalsHash: withdrawalsRoot,
ExcessBlobGas: params.ExcessBlobGas,
BlobGasUsed: params.BlobGasUsed,
ParentBeaconRoot: beaconRoot,
} }
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals) header := &types.Header{
ParentHash: params.ParentHash,
UncleHash: types.EmptyUncleHash,
Coinbase: params.FeeRecipient,
Root: params.StateRoot,
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
ReceiptHash: params.ReceiptsRoot,
Bloom: types.BytesToBloom(params.LogsBloom),
Difficulty: common.Big0,
Number: new(big.Int).SetUint64(params.Number),
GasLimit: params.GasLimit,
GasUsed: params.GasUsed,
Time: params.Timestamp,
BaseFee: params.BaseFeePerGas,
Extra: params.ExtraData,
MixDigest: params.Random,
WithdrawalsHash: withdrawalsRoot,
ExcessBlobGas: params.ExcessBlobGas,
BlobGasUsed: params.BlobGasUsed,
ParentBeaconRoot: beaconRoot,
InclusionListSummaryRoot: inclusionRoot,
}
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals).WithInclusionList(params.InclusionListSummary.Summary)
if block.Hash() != params.BlockHash { if block.Hash() != params.BlockHash {
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
} }
@ -321,3 +328,9 @@ type ClientVersionV1 struct {
func (v *ClientVersionV1) String() string { func (v *ClientVersionV1) String() string {
return fmt.Sprintf("%s-%s-%s-%s", v.Code, v.Name, v.Version, v.Commit) return fmt.Sprintf("%s-%s-%s-%s", v.Code, v.Name, v.Version, v.Commit)
} }
// InclusionListStatusV1 is returned by engine_newInclusionListV1
type InclusionListStatusV1 struct {
Status string `json:"status"`
ValidationError *string `json:"validationError"`
}

View file

@ -216,6 +216,10 @@ func initGenesis(ctx *cli.Context) error {
v := ctx.Uint64(utils.OverrideCancun.Name) v := ctx.Uint64(utils.OverrideCancun.Name)
overrides.OverrideCancun = &v overrides.OverrideCancun = &v
} }
if ctx.IsSet(utils.OverridePrague.Name) {
v := ctx.Uint64(utils.OverridePrague.Name)
overrides.OverridePrague = &v
}
if ctx.IsSet(utils.OverrideVerkle.Name) { if ctx.IsSet(utils.OverrideVerkle.Name) {
v := ctx.Uint64(utils.OverrideVerkle.Name) v := ctx.Uint64(utils.OverrideVerkle.Name)
overrides.OverrideVerkle = &v overrides.OverrideVerkle = &v

View file

@ -175,6 +175,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
v := ctx.Uint64(utils.OverrideCancun.Name) v := ctx.Uint64(utils.OverrideCancun.Name)
cfg.Eth.OverrideCancun = &v cfg.Eth.OverrideCancun = &v
} }
if ctx.IsSet(utils.OverridePrague.Name) {
v := ctx.Uint64(utils.OverridePrague.Name)
cfg.Eth.OverridePrague = &v
}
if ctx.IsSet(utils.OverrideVerkle.Name) { if ctx.IsSet(utils.OverrideVerkle.Name) {
v := ctx.Uint64(utils.OverrideVerkle.Name) v := ctx.Uint64(utils.OverrideVerkle.Name)
cfg.Eth.OverrideVerkle = &v cfg.Eth.OverrideVerkle = &v

View file

@ -65,6 +65,7 @@ var (
utils.USBFlag, utils.USBFlag,
utils.SmartCardDaemonPathFlag, utils.SmartCardDaemonPathFlag,
utils.OverrideCancun, utils.OverrideCancun,
utils.OverridePrague,
utils.OverrideVerkle, utils.OverrideVerkle,
utils.EnablePersonal, utils.EnablePersonal,
utils.TxPoolLocalsFlag, utils.TxPoolLocalsFlag,

View file

@ -248,6 +248,11 @@ var (
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting", Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
Category: flags.EthCategory, Category: flags.EthCategory,
} }
OverridePrague = &cli.Uint64Flag{
Name: "override.prague",
Usage: "Manually specify the Prague fork timestamp, overriding the bundled setting",
Category: flags.EthCategory,
}
OverrideVerkle = &cli.Uint64Flag{ OverrideVerkle = &cli.Uint64Flag{
Name: "override.verkle", Name: "override.verkle",
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting", Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",

View file

@ -0,0 +1,57 @@
package eip7547
import (
"math"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256"
)
func VerifyInclusionList(bc *core.BlockChain, parent *types.Block, signer types.Signer, txs []*types.Transaction) error {
statedb, err := bc.StateAt(parent.Root())
if err != nil {
return err
}
for _, tx := range txs {
sender, _ := signer.Sender(tx)
nonce := statedb.GetNonce(sender)
balance := statedb.GetBalance(sender)
opts := &txpool.ValidationOptions{
Config: bc.Config(),
Accept: 0 |
1<<types.LegacyTxType |
1<<types.AccessListTxType |
1<<types.DynamicFeeTxType,
MaxSize: 128 * 1024,
MinTip: new(big.Int),
}
if err := txpool.ValidateTransaction(tx, parent.Header(), signer, opts); err != nil {
return err
}
stateOpts := &txpool.ValidationOptionsWithState{
State: statedb,
FirstNonceGap: func(addr common.Address) uint64 {
return statedb.GetNonce(addr) // Nonce gaps are not permitted
},
UsedAndLeftSlots: func(addr common.Address) (int, int) {
return 0, math.MaxInt
},
ExistingExpenditure: func(addr common.Address) *big.Int {
return new(big.Int)
},
ExistingCost: func(addr common.Address, nonce uint64) *big.Int {
return new(big.Int)
},
}
if err := txpool.ValidateTransactionWithState(tx, signer, stateOpts); err != nil {
return err
}
statedb.SetNonce(sender, nonce+1)
statedb.SetBalance(sender, balance.Sub(balance, uint256.MustFromBig(tx.Cost())))
}
return nil
}

View file

@ -19,21 +19,21 @@ var _ = (*genesisSpecMarshaling)(nil)
// MarshalJSON marshals as JSON. // MarshalJSON marshals as JSON.
func (g Genesis) MarshalJSON() ([]byte, error) { func (g Genesis) MarshalJSON() ([]byte, error) {
type Genesis struct { type Genesis struct {
Config *params.ChainConfig `json:"config"` Config *params.ChainConfig `json:"config"`
Nonce math.HexOrDecimal64 `json:"nonce"` Nonce math.HexOrDecimal64 `json:"nonce"`
Timestamp math.HexOrDecimal64 `json:"timestamp"` Timestamp math.HexOrDecimal64 `json:"timestamp"`
ExtraData hexutil.Bytes `json:"extraData"` ExtraData hexutil.Bytes `json:"extraData"`
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
Mixhash common.Hash `json:"mixHash"` Mixhash common.Hash `json:"mixHash"`
Coinbase common.Address `json:"coinbase"` Coinbase common.Address `json:"coinbase"`
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
Number math.HexOrDecimal64 `json:"number"` Number math.HexOrDecimal64 `json:"number"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"` GasUsed math.HexOrDecimal64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"` ParentHash common.Hash `json:"parentHash"`
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
} }
var enc Genesis var enc Genesis
enc.Config = g.Config enc.Config = g.Config
@ -62,21 +62,21 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON. // UnmarshalJSON unmarshals from JSON.
func (g *Genesis) UnmarshalJSON(input []byte) error { func (g *Genesis) UnmarshalJSON(input []byte) error {
type Genesis struct { type Genesis struct {
Config *params.ChainConfig `json:"config"` Config *params.ChainConfig `json:"config"`
Nonce *math.HexOrDecimal64 `json:"nonce"` Nonce *math.HexOrDecimal64 `json:"nonce"`
Timestamp *math.HexOrDecimal64 `json:"timestamp"` Timestamp *math.HexOrDecimal64 `json:"timestamp"`
ExtraData *hexutil.Bytes `json:"extraData"` ExtraData *hexutil.Bytes `json:"extraData"`
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
Mixhash *common.Hash `json:"mixHash"` Mixhash *common.Hash `json:"mixHash"`
Coinbase *common.Address `json:"coinbase"` Coinbase *common.Address `json:"coinbase"`
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
Number *math.HexOrDecimal64 `json:"number"` Number *math.HexOrDecimal64 `json:"number"`
GasUsed *math.HexOrDecimal64 `json:"gasUsed"` GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
ParentHash *common.Hash `json:"parentHash"` ParentHash *common.Hash `json:"parentHash"`
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
} }
var dec Genesis var dec Genesis
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {

View file

@ -209,6 +209,7 @@ func (e *GenesisMismatchError) Error() string {
// ChainOverrides contains the changes to chain config. // ChainOverrides contains the changes to chain config.
type ChainOverrides struct { type ChainOverrides struct {
OverrideCancun *uint64 OverrideCancun *uint64
OverridePrague *uint64
OverrideVerkle *uint64 OverrideVerkle *uint64
} }
@ -238,6 +239,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
if overrides != nil && overrides.OverrideCancun != nil { if overrides != nil && overrides.OverrideCancun != nil {
config.CancunTime = overrides.OverrideCancun config.CancunTime = overrides.OverrideCancun
} }
if overrides != nil && overrides.OverridePrague != nil {
config.PragueTime = overrides.OverridePrague
}
if overrides != nil && overrides.OverrideVerkle != nil { if overrides != nil && overrides.OverrideVerkle != nil {
config.VerkleTime = overrides.OverrideVerkle config.VerkleTime = overrides.OverrideVerkle
} }

View file

@ -753,7 +753,7 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
if body == nil { if body == nil {
return nil return nil
} }
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithWithdrawals(body.Withdrawals) return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithWithdrawals(body.Withdrawals).WithInclusionList(body.InclusionListSummary)
} }
// WriteBlock serializes a block into the database, header and body separately. // WriteBlock serializes a block into the database, header and body separately.
@ -843,7 +843,7 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
} }
for _, bad := range badBlocks { for _, bad := range badBlocks {
if bad.Header.Hash() == hash { if bad.Header.Hash() == hash {
return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals) return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals).WithInclusionList(bad.Body.InclusionListSummary)
} }
} }
return nil return nil
@ -862,7 +862,7 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
} }
var blocks []*types.Block var blocks []*types.Block
for _, bad := range badBlocks { for _, bad := range badBlocks {
blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals)) blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals).WithInclusionList(bad.Body.InclusionListSummary))
} }
return blocks return blocks
} }

View file

@ -1645,6 +1645,9 @@ func (p *BlobPool) Locals() []common.Address {
return []common.Address{} return []common.Address{}
} }
// There is no notion of local accounts in the blob pool.
func (p *BlobPool) AddLocalAddr(common.Address) {}
// Status returns the known status (unknown/pending/queued) of a transaction // Status returns the known status (unknown/pending/queued) of a transaction
// identified by their hashes. // identified by their hashes.
func (p *BlobPool) Status(hash common.Hash) txpool.TxStatus { func (p *BlobPool) Status(hash common.Hash) txpool.TxStatus {

View file

@ -1959,3 +1959,8 @@ func (t *lookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
func numSlots(tx *types.Transaction) int { func numSlots(tx *types.Transaction) int {
return int((tx.Size() + txSlotSize - 1) / txSlotSize) return int((tx.Size() + txSlotSize - 1) / txSlotSize)
} }
func (p *LegacyPool) AddLocalAddr(addr common.Address) {
p.locals.add(addr)
// TODO go through all txs to mark as local now
}

View file

@ -159,6 +159,9 @@ type SubPool interface {
// Locals retrieves the accounts currently considered local by the pool. // Locals retrieves the accounts currently considered local by the pool.
Locals() []common.Address Locals() []common.Address
// AddLocalAddr marks an account as local.
AddLocalAddr(common.Address)
// Status returns the known status (unknown/pending/queued) of a transaction // Status returns the known status (unknown/pending/queued) of a transaction
// identified by their hashes. // identified by their hashes.
Status(hash common.Hash) TxStatus Status(hash common.Hash) TxStatus

View file

@ -464,6 +464,12 @@ func (p *TxPool) Status(hash common.Hash) TxStatus {
return TxStatusUnknown return TxStatusUnknown
} }
func (p *TxPool) MarkLocal(addr common.Address) {
for _, pool := range p.subpools {
pool.AddLocalAddr(addr)
}
}
// Sync is a helper method for unit tests or simulator runs where the chain events // Sync is a helper method for unit tests or simulator runs where the chain events
// are arriving in quick succession, without any time in between them to run the // are arriving in quick succession, without any time in between them to run the
// internal background reset operations. This method will run an explicit reset // internal background reset operations. This method will run an explicit reset

View file

@ -93,6 +93,9 @@ type Header struct {
// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
// InclusionListSummaryRoot was added by EIP-7547 and is ignored in legacy headers.
InclusionListSummaryRoot *common.Hash `json:"inclusionListSummaryRoot" rlp:"optional"`
} }
// field type overrides for gencodec // field type overrides for gencodec
@ -168,9 +171,10 @@ func (h *Header) EmptyReceipts() bool {
// Body is a simple (mutable, non-safe) data container for storing and moving // Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together. // a block's data contents (transactions and uncles) together.
type Body struct { type Body struct {
Transactions []*Transaction Transactions []*Transaction
Uncles []*Header Uncles []*Header
Withdrawals []*Withdrawal `rlp:"optional"` Withdrawals []*Withdrawal `rlp:"optional"`
InclusionListSummary []*InclusionListEntry
} }
// Block represents an Ethereum block. // Block represents an Ethereum block.
@ -191,10 +195,11 @@ type Body struct {
// - We do not copy body data on access because it does not affect the caches, and also // - We do not copy body data on access because it does not affect the caches, and also
// because it would be too expensive. // because it would be too expensive.
type Block struct { type Block struct {
header *Header header *Header
uncles []*Header uncles []*Header
transactions Transactions transactions Transactions
withdrawals Withdrawals withdrawals Withdrawals
inclusionListSummary []*InclusionListEntry
// caches // caches
hash atomic.Value hash atomic.Value
@ -208,10 +213,11 @@ type Block struct {
// "external" block encoding. used for eth protocol, etc. // "external" block encoding. used for eth protocol, etc.
type extblock struct { type extblock struct {
Header *Header Header *Header
Txs []*Transaction Txs []*Transaction
Uncles []*Header Uncles []*Header
Withdrawals []*Withdrawal `rlp:"optional"` Withdrawals []*Withdrawal `rlp:"optional"`
InclusionListSummary []*InclusionListEntry
} }
// NewBlock creates a new block. The input data is copied, changes to header and to the // NewBlock creates a new block. The input data is copied, changes to header and to the
@ -332,15 +338,16 @@ func (b *Block) EncodeRLP(w io.Writer) error {
// Body returns the non-header content of the block. // Body returns the non-header content of the block.
// Note the returned data is not an independent copy. // Note the returned data is not an independent copy.
func (b *Block) Body() *Body { func (b *Block) Body() *Body {
return &Body{b.transactions, b.uncles, b.withdrawals} return &Body{b.transactions, b.uncles, b.withdrawals, b.inclusionListSummary}
} }
// Accessors for body data. These do not return a copy because the content // Accessors for body data. These do not return a copy because the content
// of the body slices does not affect the cached hash/size in block. // of the body slices does not affect the cached hash/size in block.
func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Uncles() []*Header { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions } func (b *Block) Transactions() Transactions { return b.transactions }
func (b *Block) Withdrawals() Withdrawals { return b.withdrawals } func (b *Block) Withdrawals() Withdrawals { return b.withdrawals }
func (b *Block) InclusionListSummary() []*InclusionListEntry { return b.inclusionListSummary }
func (b *Block) Transaction(hash common.Hash) *Transaction { func (b *Block) Transaction(hash common.Hash) *Transaction {
for _, transaction := range b.transactions { for _, transaction := range b.transactions {
@ -482,6 +489,20 @@ func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block {
return block return block
} }
// WithInclusionList returns a copy of the block containing the given inclusionList.
func (b *Block) WithInclusionList(summary []*InclusionListEntry) *Block {
block := &Block{
header: b.header,
transactions: b.transactions,
uncles: b.uncles,
}
if summary != nil {
block.inclusionListSummary = make([]*InclusionListEntry, len(summary))
copy(block.inclusionListSummary, summary)
}
return block
}
// Hash returns the keccak256 hash of b's header. // Hash returns the keccak256 hash of b's header.
// The hash is computed on the first call and cached thereafter. // The hash is computed on the first call and cached thereafter.
func (b *Block) Hash() common.Hash { func (b *Block) Hash() common.Hash {

View file

@ -16,27 +16,28 @@ var _ = (*headerMarshaling)(nil)
// MarshalJSON marshals as JSON. // MarshalJSON marshals as JSON.
func (h Header) MarshalJSON() ([]byte, error) { func (h Header) MarshalJSON() ([]byte, error) {
type Header struct { type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner"` Coinbase common.Address `json:"miner"`
Root common.Hash `json:"stateRoot" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"`
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Extra hexutil.Bytes `json:"extraData" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash"` MixDigest common.Hash `json:"mixHash"`
Nonce BlockNonce `json:"nonce"` Nonce BlockNonce `json:"nonce"`
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
Hash common.Hash `json:"hash"` InclusionListSummaryRoot *common.Hash `json:"inclusionListSummaryRoot" rlp:"optional"`
Hash common.Hash `json:"hash"`
} }
var enc Header var enc Header
enc.ParentHash = h.ParentHash enc.ParentHash = h.ParentHash
@ -59,6 +60,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed) enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed)
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas) enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
enc.ParentBeaconRoot = h.ParentBeaconRoot enc.ParentBeaconRoot = h.ParentBeaconRoot
enc.InclusionListSummaryRoot = h.InclusionListSummaryRoot
enc.Hash = h.Hash() enc.Hash = h.Hash()
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -66,26 +68,27 @@ func (h Header) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON. // UnmarshalJSON unmarshals from JSON.
func (h *Header) UnmarshalJSON(input []byte) error { func (h *Header) UnmarshalJSON(input []byte) error {
type Header struct { type Header struct {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"` ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase *common.Address `json:"miner"` Coinbase *common.Address `json:"miner"`
Root *common.Hash `json:"stateRoot" gencodec:"required"` Root *common.Hash `json:"stateRoot" gencodec:"required"`
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom *Bloom `json:"logsBloom" gencodec:"required"` Bloom *Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest *common.Hash `json:"mixHash"` MixDigest *common.Hash `json:"mixHash"`
Nonce *BlockNonce `json:"nonce"` Nonce *BlockNonce `json:"nonce"`
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
InclusionListSummaryRoot *common.Hash `json:"inclusionListSummaryRoot" rlp:"optional"`
} }
var dec Header var dec Header
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -163,5 +166,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
if dec.ParentBeaconRoot != nil { if dec.ParentBeaconRoot != nil {
h.ParentBeaconRoot = dec.ParentBeaconRoot h.ParentBeaconRoot = dec.ParentBeaconRoot
} }
if dec.InclusionListSummaryRoot != nil {
h.InclusionListSummaryRoot = dec.InclusionListSummaryRoot
}
return nil return nil
} }

View file

@ -42,7 +42,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
_tmp3 := obj.BlobGasUsed != nil _tmp3 := obj.BlobGasUsed != nil
_tmp4 := obj.ExcessBlobGas != nil _tmp4 := obj.ExcessBlobGas != nil
_tmp5 := obj.ParentBeaconRoot != nil _tmp5 := obj.ParentBeaconRoot != nil
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 { _tmp6 := obj.InclusionListSummaryRoot != nil
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 {
if obj.BaseFee == nil { if obj.BaseFee == nil {
w.Write(rlp.EmptyString) w.Write(rlp.EmptyString)
} else { } else {
@ -52,34 +53,41 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
w.WriteBigInt(obj.BaseFee) w.WriteBigInt(obj.BaseFee)
} }
} }
if _tmp2 || _tmp3 || _tmp4 || _tmp5 { if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 {
if obj.WithdrawalsHash == nil { if obj.WithdrawalsHash == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteBytes(obj.WithdrawalsHash[:]) w.WriteBytes(obj.WithdrawalsHash[:])
} }
} }
if _tmp3 || _tmp4 || _tmp5 { if _tmp3 || _tmp4 || _tmp5 || _tmp6 {
if obj.BlobGasUsed == nil { if obj.BlobGasUsed == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteUint64((*obj.BlobGasUsed)) w.WriteUint64((*obj.BlobGasUsed))
} }
} }
if _tmp4 || _tmp5 { if _tmp4 || _tmp5 || _tmp6 {
if obj.ExcessBlobGas == nil { if obj.ExcessBlobGas == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteUint64((*obj.ExcessBlobGas)) w.WriteUint64((*obj.ExcessBlobGas))
} }
} }
if _tmp5 { if _tmp5 || _tmp6 {
if obj.ParentBeaconRoot == nil { if obj.ParentBeaconRoot == nil {
w.Write([]byte{0x80}) w.Write([]byte{0x80})
} else { } else {
w.WriteBytes(obj.ParentBeaconRoot[:]) w.WriteBytes(obj.ParentBeaconRoot[:])
} }
} }
if _tmp6 {
if obj.InclusionListSummaryRoot == nil {
w.Write([]byte{0x80})
} else {
w.WriteBytes(obj.InclusionListSummaryRoot[:])
}
}
w.ListEnd(_tmp0) w.ListEnd(_tmp0)
return w.Flush() return w.Flush()
} }

View file

@ -0,0 +1,54 @@
// Copyright 2024 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 types
import (
"bytes"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
//go:generate go run github.com/fjl/gencodec -type InclusionList gen_inclusion_rlp.go
//go:generate go run ../../rlp/rlpgen -type InclusionList -out gen_inculsion.go
type InclusionListEntry struct {
Address common.Address
Nonce uint64
}
type InclusionListSummary struct {
Slot uint64
ProposerIndex uint64
ParentHash common.Hash
Summary []*InclusionListEntry
}
// InclusionList represents a validator InclusionList from the consensus layer.
type InclusionList struct {
List []*InclusionListEntry
}
// Len returns the length of s.
func (s InclusionList) Len() int { return len(s.List) }
// EncodeIndex encodes the i'th InclusionList to w. Note that this does not check for errors
// because we assume that *InclusionList will only ever contain valid InclusionLists that were either
// constructed by decoding or via public API in this package.
func (s InclusionList) EncodeIndex(i int, w *bytes.Buffer) {
rlp.Encode(w, s.List[i])
}

View file

@ -29,7 +29,7 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
case rules.IsVerkle: case rules.IsVerkle:
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet") return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
case rules.IsPrague: case rules.IsPrague:
return newCancunInstructionSet(), errors.New("prague-fork not defined yet") return newCancunInstructionSet(), nil
case rules.IsCancun: case rules.IsCancun:
return newCancunInstructionSet(), nil return newCancunInstructionSet(), nil
case rules.IsShanghai: case rules.IsShanghai:

View file

@ -204,6 +204,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if config.OverrideCancun != nil { if config.OverrideCancun != nil {
overrides.OverrideCancun = config.OverrideCancun overrides.OverrideCancun = config.OverrideCancun
} }
if config.OverridePrague != nil {
overrides.OverridePrague = config.OverridePrague
}
if config.OverrideVerkle != nil { if config.OverrideVerkle != nil {
overrides.OverrideVerkle = config.OverrideVerkle overrides.OverrideVerkle = config.OverrideVerkle
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip7547"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"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"
@ -896,3 +897,66 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBodyV1 {
Withdrawals: withdrawals, Withdrawals: withdrawals,
} }
} }
func (api *ConsensusAPI) NewInclusionListV1(addresses []common.Address, transactions [][]byte, parentBlockHash common.Hash) (*engine.InclusionListStatusV1, error) {
if len(addresses) != len(transactions) {
return inclusionListError("number of transactions do not match addresses"), nil
}
txs, err := engine.DecodeTransactions(transactions)
if err != nil {
return nil, fmt.Errorf("could not decode transactions: %v", err)
}
signer := types.LatestSignerForChainID(api.eth.BlockChain().Config().ChainID)
var maxGas uint64
for index, tx := range txs {
sender, err := signer.Sender(tx)
if err != nil {
return inclusionListError(fmt.Sprintf("Invalid signer at index %v, error: %v", index, err)), nil
}
if addresses[index] != sender {
return inclusionListError(fmt.Sprintf("Invalid sender at index %v, got %v want %v", index, sender, addresses[index])), nil
}
maxGas += tx.Gas()
}
if maxGas != params.InclusionListMaxGas {
return inclusionListError(fmt.Sprintf("Invalid inclusion list max gas, got %v want %v", maxGas, params.InclusionListMaxGas)), nil
}
block := api.eth.BlockChain().GetBlockByHash(parentBlockHash)
if block == nil {
return inclusionListError("Parent block not found"), nil
}
if err := eip7547.VerifyInclusionList(api.eth.BlockChain(), block, signer, txs); err != nil {
return inclusionListError(err.Error()), nil
}
return &engine.InclusionListStatusV1{Status: engine.VALID}, nil
}
func (api *ConsensusAPI) GetInclusionListV1() ([]common.Address, [][]byte, error) {
return []common.Address{}, [][]byte{}, nil
}
func inclusionListError(errorMsg string) *engine.InclusionListStatusV1 {
return &engine.InclusionListStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}
}
func (c *ConsensusAPI) SetLocalAccounts(locals []common.Address) error {
for _, addr := range locals {
c.eth.TxPool().MarkLocal(addr)
}
return nil
}
func (c *ConsensusAPI) GetLocalTransactions() ([][]byte, error) {
var res [][]byte
for _, addr := range c.eth.TxPool().Locals() {
pending, _ := c.eth.TxPool().ContentFrom(addr)
for _, tx := range pending {
m, err := tx.MarshalJSON()
if err != nil {
return [][]byte{}, nil
}
res = append(res, m)
}
}
return res, nil
}

View file

@ -1500,7 +1500,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
) )
blocks := make([]*types.Block, len(results)) blocks := make([]*types.Block, len(results))
for i, result := range results { for i, result := range results {
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals) blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals).WithInclusionList(result.InclusionListSummary)
} }
// Downloaded blocks are always regarded as trusted after the // Downloaded blocks are always regarded as trusted after the
// transition. Because the downloaded chain is guided by the // transition. Because the downloaded chain is guided by the
@ -1718,7 +1718,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
blocks := make([]*types.Block, len(results)) blocks := make([]*types.Block, len(results))
receipts := make([]types.Receipts, len(results)) receipts := make([]types.Receipts, len(results))
for i, result := range results { for i, result := range results {
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals) blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals).WithInclusionList(result.InclusionListSummary)
receipts[i] = result.Receipts receipts[i] = result.Receipts
} }
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil { if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
@ -1729,7 +1729,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
} }
func (d *Downloader) commitPivotBlock(result *fetchResult) error { func (d *Downloader) commitPivotBlock(result *fetchResult) error {
block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals) block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals).WithInclusionList(result.InclusionListSummary)
log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash()) log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash())
// Commit the pivot block as the new head, will require full sync from here on // Commit the pivot block as the new head, will require full sync from here on

View file

@ -89,10 +89,10 @@ func (q *bodyQueue) request(peer *peerConnection, req *fetchRequest, resCh chan
// deliver is responsible for taking a generic response packet from the concurrent // deliver is responsible for taking a generic response packet from the concurrent
// fetcher, unpacking the body data and delivering it to the downloader's queue. // fetcher, unpacking the body data and delivering it to the downloader's queue.
func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) { func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesResponse).Unpack() txs, uncles, withdrawals, ilSummaries := packet.Res.(*eth.BlockBodiesResponse).Unpack()
hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes} hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal, inclusionListSummaryRoot hashes}
accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2]) accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2], ilSummaries, hashsets[3])
switch { switch {
case err == nil && len(txs) == 0: case err == nil && len(txs) == 0:
peer.log.Trace("Requested bodies delivered") peer.log.Trace("Requested bodies delivered")

View file

@ -65,11 +65,12 @@ type fetchRequest struct {
type fetchResult struct { type fetchResult struct {
pending atomic.Int32 // Flag telling what deliveries are outstanding pending atomic.Int32 // Flag telling what deliveries are outstanding
Header *types.Header Header *types.Header
Uncles []*types.Header Uncles []*types.Header
Transactions types.Transactions Transactions types.Transactions
Receipts types.Receipts Receipts types.Receipts
Withdrawals types.Withdrawals Withdrawals types.Withdrawals
InclusionListSummary []*types.InclusionListEntry
} }
func newFetchResult(header *types.Header, fastSync bool) *fetchResult { func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
@ -774,7 +775,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []comm
// also wakes any threads waiting for data delivery. // also wakes any threads waiting for data delivery.
func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash, func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash,
uncleLists [][]*types.Header, uncleListHashes []common.Hash, uncleLists [][]*types.Header, uncleListHashes []common.Hash,
withdrawalLists [][]*types.Withdrawal, withdrawalListHashes []common.Hash) (int, error) { withdrawalLists [][]*types.Withdrawal, withdrawalListHashes []common.Hash, ilSummaries [][]*types.InclusionListEntry, ilSummaryHashes []common.Hash) (int, error) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
@ -798,6 +799,19 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
return errInvalidBody return errInvalidBody
} }
} }
if header.InclusionListSummaryRoot == nil {
// nil hash means that inclusionListSummaries should not be present in body
if ilSummaries[index] != nil {
return errInvalidBody
}
} else {
if ilSummaries[index] == nil {
return errInvalidBody
}
if ilSummaryHashes[index] != *header.InclusionListSummaryRoot {
return errInvalidBody
}
}
// Blocks must have a number of blobs corresponding to the header gas usage, // Blocks must have a number of blobs corresponding to the header gas usage,
// and zero before the Cancun hardfork. // and zero before the Cancun hardfork.
var blobs int var blobs int
@ -836,6 +850,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
result.Transactions = txLists[index] result.Transactions = txLists[index]
result.Uncles = uncleLists[index] result.Uncles = uncleLists[index]
result.Withdrawals = withdrawalLists[index] result.Withdrawals = withdrawalLists[index]
result.InclusionListSummary = ilSummaries[index]
result.SetBodyDone() result.SetBodyDone()
} }
return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool, return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,

View file

@ -341,7 +341,7 @@ func XTestDelivery(t *testing.T) {
uncleHashes[i] = types.CalcUncleHash(uncles) uncleHashes[i] = types.CalcUncleHash(uncles)
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
_, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil) _, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil, nil, nil)
if err != nil { if err != nil {
fmt.Printf("delivered %d bodies %v\n", len(txset), err) fmt.Printf("delivered %d bodies %v\n", len(txset), err)
} }

View file

@ -157,6 +157,9 @@ type Config struct {
// OverrideCancun (TODO: remove after the fork) // OverrideCancun (TODO: remove after the fork)
OverrideCancun *uint64 `toml:",omitempty"` OverrideCancun *uint64 `toml:",omitempty"`
// OverrideCancun (TODO: remove after the fork)
OverridePrague *uint64 `toml:",omitempty"`
// OverrideVerkle (TODO: remove after the fork) // OverrideVerkle (TODO: remove after the fork)
OverrideVerkle *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"`
} }

View file

@ -310,9 +310,10 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
} }
metadata := func() interface{} { metadata := func() interface{} {
var ( var (
txsHashes = make([]common.Hash, len(res.BlockBodiesResponse)) txsHashes = make([]common.Hash, len(res.BlockBodiesResponse))
uncleHashes = make([]common.Hash, len(res.BlockBodiesResponse)) uncleHashes = make([]common.Hash, len(res.BlockBodiesResponse))
withdrawalHashes = make([]common.Hash, len(res.BlockBodiesResponse)) withdrawalHashes = make([]common.Hash, len(res.BlockBodiesResponse))
inclusionListSummaryRoots = make([]common.Hash, len(res.BlockBodiesResponse))
) )
hasher := trie.NewStackTrie(nil) hasher := trie.NewStackTrie(nil)
for i, body := range res.BlockBodiesResponse { for i, body := range res.BlockBodiesResponse {
@ -321,8 +322,11 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
if body.Withdrawals != nil { if body.Withdrawals != nil {
withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher) withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher)
} }
if body.InclusionListSummary != nil {
inclusionListSummaryRoots[i] = types.DeriveSha(types.InclusionList{List: body.InclusionListSummary}, hasher)
}
} }
return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes} return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes, inclusionListSummaryRoots}
} }
return peer.dispatchResponse(&Response{ return peer.dispatchResponse(&Response{
id: res.RequestId, id: res.RequestId,

View file

@ -221,24 +221,26 @@ type BlockBodiesRLPPacket struct {
// BlockBody represents the data content of a single block. // BlockBody represents the data content of a single block.
type BlockBody struct { type BlockBody struct {
Transactions []*types.Transaction // Transactions contained within a block Transactions []*types.Transaction // Transactions contained within a block
Uncles []*types.Header // Uncles contained within a block Uncles []*types.Header // Uncles contained within a block
Withdrawals []*types.Withdrawal `rlp:"optional"` // Withdrawals contained within a block Withdrawals []*types.Withdrawal `rlp:"optional"` // Withdrawals contained within a block
InclusionListSummary []*types.InclusionListEntry `rlp:"optional"` // InclusionListSummary contained within a block
} }
// Unpack retrieves the transactions and uncles from the range packet and returns // Unpack retrieves the transactions and uncles from the range packet and returns
// them in a split flat format that's more consistent with the internal data structures. // them in a split flat format that's more consistent with the internal data structures.
func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal) { func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal, [][]*types.InclusionListEntry) {
// TODO(matt): add support for withdrawals to fetchers // TODO(matt): add support for withdrawals to fetchers
var ( var (
txset = make([][]*types.Transaction, len(*p)) txset = make([][]*types.Transaction, len(*p))
uncleset = make([][]*types.Header, len(*p)) uncleset = make([][]*types.Header, len(*p))
withdrawalset = make([][]*types.Withdrawal, len(*p)) withdrawalset = make([][]*types.Withdrawal, len(*p))
inclusionListSummarySet = make([][]*types.InclusionListEntry, len(*p))
) )
for i, body := range *p { for i, body := range *p {
txset[i], uncleset[i], withdrawalset[i] = body.Transactions, body.Uncles, body.Withdrawals txset[i], uncleset[i], withdrawalset[i], inclusionListSummarySet[i] = body.Transactions, body.Uncles, body.Withdrawals, body.InclusionListSummary
} }
return txset, uncleset, withdrawalset return txset, uncleset, withdrawalset, inclusionListSummarySet
} }
// GetReceiptsRequest represents a block receipts query. // GetReceiptsRequest represents a block receipts query.

View file

@ -173,6 +173,8 @@ const (
BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing) BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing)
MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block
InclusionListMaxGas = 10_000_000 // Max gas for inclusion list part of the block
) )
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations