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

@ -74,6 +74,7 @@ type ExecutableData struct {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *uint64 `json:"blobGasUsed"`
ExcessBlobGas *uint64 `json:"excessBlobGas"`
InclusionListSummary *types.InclusionListSummary `json:"inclusionListSummary"`
}
// JSON type overrides for executableData.
@ -175,7 +176,7 @@ func encodeTransactions(txs []*types.Transaction) [][]byte {
return enc
}
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
func DecodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
var txs = make([]*types.Transaction, len(enc))
for i, encTx := range enc {
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 must be passed via non-nil, length 0 value in params.
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 {
return nil, err
}
@ -233,6 +234,11 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
withdrawalsRoot = &h
}
var inclusionRoot *common.Hash
if params.InclusionListSummary != nil {
h := types.DeriveSha(types.InclusionList{List: params.InclusionListSummary.Summary}, trie.NewStackTrie(nil))
inclusionRoot = &h
}
header := &types.Header{
ParentHash: params.ParentHash,
UncleHash: types.EmptyUncleHash,
@ -253,8 +259,9 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
ExcessBlobGas: params.ExcessBlobGas,
BlobGasUsed: params.BlobGasUsed,
ParentBeaconRoot: beaconRoot,
InclusionListSummaryRoot: inclusionRoot,
}
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals).WithInclusionList(params.InclusionListSummary.Summary)
if block.Hash() != params.BlockHash {
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 {
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)
overrides.OverrideCancun = &v
}
if ctx.IsSet(utils.OverridePrague.Name) {
v := ctx.Uint64(utils.OverridePrague.Name)
overrides.OverridePrague = &v
}
if ctx.IsSet(utils.OverrideVerkle.Name) {
v := ctx.Uint64(utils.OverrideVerkle.Name)
overrides.OverrideVerkle = &v

View file

@ -175,6 +175,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
v := ctx.Uint64(utils.OverrideCancun.Name)
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) {
v := ctx.Uint64(utils.OverrideVerkle.Name)
cfg.Eth.OverrideVerkle = &v

View file

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

View file

@ -248,6 +248,11 @@ var (
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
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{
Name: "override.verkle",
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

@ -209,6 +209,7 @@ func (e *GenesisMismatchError) Error() string {
// ChainOverrides contains the changes to chain config.
type ChainOverrides struct {
OverrideCancun *uint64
OverridePrague *uint64
OverrideVerkle *uint64
}
@ -238,6 +239,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
if overrides != nil && overrides.OverrideCancun != nil {
config.CancunTime = overrides.OverrideCancun
}
if overrides != nil && overrides.OverridePrague != nil {
config.PragueTime = overrides.OverridePrague
}
if overrides != nil && overrides.OverrideVerkle != nil {
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 {
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.
@ -843,7 +843,7 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
}
for _, bad := range badBlocks {
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
@ -862,7 +862,7 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
}
var blocks []*types.Block
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
}

View file

@ -1645,6 +1645,9 @@ func (p *BlobPool) Locals() []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
// identified by their hashes.
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 {
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() []common.Address
// AddLocalAddr marks an account as local.
AddLocalAddr(common.Address)
// Status returns the known status (unknown/pending/queued) of a transaction
// identified by their hashes.
Status(hash common.Hash) TxStatus

View file

@ -464,6 +464,12 @@ func (p *TxPool) Status(hash common.Hash) TxStatus {
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
// 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

View file

@ -93,6 +93,9 @@ type Header struct {
// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
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
@ -171,6 +174,7 @@ type Body struct {
Transactions []*Transaction
Uncles []*Header
Withdrawals []*Withdrawal `rlp:"optional"`
InclusionListSummary []*InclusionListEntry
}
// Block represents an Ethereum block.
@ -195,6 +199,7 @@ type Block struct {
uncles []*Header
transactions Transactions
withdrawals Withdrawals
inclusionListSummary []*InclusionListEntry
// caches
hash atomic.Value
@ -212,6 +217,7 @@ type extblock struct {
Txs []*Transaction
Uncles []*Header
Withdrawals []*Withdrawal `rlp:"optional"`
InclusionListSummary []*InclusionListEntry
}
// NewBlock creates a new block. The input data is copied, changes to header and to the
@ -332,7 +338,7 @@ func (b *Block) EncodeRLP(w io.Writer) error {
// Body returns the non-header content of the block.
// Note the returned data is not an independent copy.
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
@ -341,6 +347,7 @@ func (b *Block) Body() *Body {
func (b *Block) Uncles() []*Header { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions }
func (b *Block) Withdrawals() Withdrawals { return b.withdrawals }
func (b *Block) InclusionListSummary() []*InclusionListEntry { return b.inclusionListSummary }
func (b *Block) Transaction(hash common.Hash) *Transaction {
for _, transaction := range b.transactions {
@ -482,6 +489,20 @@ func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *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.
// The hash is computed on the first call and cached thereafter.
func (b *Block) Hash() common.Hash {

View file

@ -36,6 +36,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
InclusionListSummaryRoot *common.Hash `json:"inclusionListSummaryRoot" rlp:"optional"`
Hash common.Hash `json:"hash"`
}
var enc Header
@ -59,6 +60,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed)
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
enc.ParentBeaconRoot = h.ParentBeaconRoot
enc.InclusionListSummaryRoot = h.InclusionListSummaryRoot
enc.Hash = h.Hash()
return json.Marshal(&enc)
}
@ -86,6 +88,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
InclusionListSummaryRoot *common.Hash `json:"inclusionListSummaryRoot" rlp:"optional"`
}
var dec Header
if err := json.Unmarshal(input, &dec); err != nil {
@ -163,5 +166,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
if dec.ParentBeaconRoot != nil {
h.ParentBeaconRoot = dec.ParentBeaconRoot
}
if dec.InclusionListSummaryRoot != nil {
h.InclusionListSummaryRoot = dec.InclusionListSummaryRoot
}
return nil
}

View file

@ -42,7 +42,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
_tmp3 := obj.BlobGasUsed != nil
_tmp4 := obj.ExcessBlobGas != 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 {
w.Write(rlp.EmptyString)
} else {
@ -52,34 +53,41 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
w.WriteBigInt(obj.BaseFee)
}
}
if _tmp2 || _tmp3 || _tmp4 || _tmp5 {
if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 {
if obj.WithdrawalsHash == nil {
w.Write([]byte{0x80})
} else {
w.WriteBytes(obj.WithdrawalsHash[:])
}
}
if _tmp3 || _tmp4 || _tmp5 {
if _tmp3 || _tmp4 || _tmp5 || _tmp6 {
if obj.BlobGasUsed == nil {
w.Write([]byte{0x80})
} else {
w.WriteUint64((*obj.BlobGasUsed))
}
}
if _tmp4 || _tmp5 {
if _tmp4 || _tmp5 || _tmp6 {
if obj.ExcessBlobGas == nil {
w.Write([]byte{0x80})
} else {
w.WriteUint64((*obj.ExcessBlobGas))
}
}
if _tmp5 {
if _tmp5 || _tmp6 {
if obj.ParentBeaconRoot == nil {
w.Write([]byte{0x80})
} else {
w.WriteBytes(obj.ParentBeaconRoot[:])
}
}
if _tmp6 {
if obj.InclusionListSummaryRoot == nil {
w.Write([]byte{0x80})
} else {
w.WriteBytes(obj.InclusionListSummaryRoot[:])
}
}
w.ListEnd(_tmp0)
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:
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
case rules.IsPrague:
return newCancunInstructionSet(), errors.New("prague-fork not defined yet")
return newCancunInstructionSet(), nil
case rules.IsCancun:
return newCancunInstructionSet(), nil
case rules.IsShanghai:

View file

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

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"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/types"
"github.com/ethereum/go-ethereum/eth"
@ -896,3 +897,66 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBodyV1 {
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))
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
// 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))
receipts := make([]types.Receipts, len(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
}
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 {
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())
// 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
// fetcher, unpacking the body data and delivering it to the downloader's queue.
func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesResponse).Unpack()
hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes}
txs, uncles, withdrawals, ilSummaries := packet.Res.(*eth.BlockBodiesResponse).Unpack()
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 {
case err == nil && len(txs) == 0:
peer.log.Trace("Requested bodies delivered")

View file

@ -70,6 +70,7 @@ type fetchResult struct {
Transactions types.Transactions
Receipts types.Receipts
Withdrawals types.Withdrawals
InclusionListSummary []*types.InclusionListEntry
}
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.
func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []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()
defer q.lock.Unlock()
@ -798,6 +799,19 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
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,
// and zero before the Cancun hardfork.
var blobs int
@ -836,6 +850,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
result.Transactions = txLists[index]
result.Uncles = uncleLists[index]
result.Withdrawals = withdrawalLists[index]
result.InclusionListSummary = ilSummaries[index]
result.SetBodyDone()
}
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)
}
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 {
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 *uint64 `toml:",omitempty"`
// OverrideCancun (TODO: remove after the fork)
OverridePrague *uint64 `toml:",omitempty"`
// OverrideVerkle (TODO: remove after the fork)
OverrideVerkle *uint64 `toml:",omitempty"`
}

View file

@ -313,6 +313,7 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
txsHashes = make([]common.Hash, len(res.BlockBodiesResponse))
uncleHashes = make([]common.Hash, len(res.BlockBodiesResponse))
withdrawalHashes = make([]common.Hash, len(res.BlockBodiesResponse))
inclusionListSummaryRoots = make([]common.Hash, len(res.BlockBodiesResponse))
)
hasher := trie.NewStackTrie(nil)
for i, body := range res.BlockBodiesResponse {
@ -321,8 +322,11 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
if body.Withdrawals != nil {
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{
id: res.RequestId,

View file

@ -224,21 +224,23 @@ type BlockBody struct {
Transactions []*types.Transaction // Transactions contained within a block
Uncles []*types.Header // Uncles 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
// 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
var (
txset = make([][]*types.Transaction, len(*p))
uncleset = make([][]*types.Header, len(*p))
withdrawalset = make([][]*types.Withdrawal, len(*p))
inclusionListSummarySet = make([][]*types.InclusionListEntry, len(*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.

View file

@ -173,6 +173,8 @@ const (
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
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