From b75e1a691f7192e16fac05e6dc39966213604558 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Mon, 28 Nov 2022 16:44:44 -0800 Subject: [PATCH] copy/pasta more of the Withdrawals PR --- cmd/evm/internal/t8ntool/block.go | 2 +- cmd/evm/internal/t8ntool/gen_header.go | 4 +- consensus/beacon/consensus.go | 26 +++- consensus/clique/clique.go | 9 +- consensus/consensus.go | 4 +- consensus/ethash/consensus.go | 9 +- core/beacon/gen_blockparams.go | 37 +++--- core/beacon/gen_ed.go | 111 +++++++++-------- core/beacon/types.go | 120 ++++++++++--------- core/chain_makers.go | 39 +++++- core/rawdb/accessors_chain.go | 6 +- core/state_processor.go | 6 +- core/types/block.go | 3 +- eth/catalyst/api.go | 31 ++++- eth/catalyst/api_test.go | 30 ++--- eth/catalyst/queue.go | 4 +- eth/downloader/downloader_test.go | 7 +- eth/downloader/fetchers_concurrent_bodies.go | 6 +- eth/downloader/queue.go | 9 +- eth/downloader/queue_test.go | 2 +- eth/ethconfig/gen_config.go | 10 +- eth/fetcher/block_fetcher.go | 19 +-- eth/fetcher/block_fetcher_test.go | 62 ++++++++-- eth/protocols/eth/handler_test.go | 69 +++++++++-- eth/protocols/eth/handlers.go | 10 +- eth/protocols/eth/protocol.go | 12 +- les/catalyst/api.go | 6 +- les/catalyst/api_test.go | 4 +- miner/payload_building.go | 21 ++-- miner/payload_building_test.go | 2 +- miner/stress/beacon/main.go | 8 +- miner/worker.go | 40 ++++--- miner/worker_test.go | 4 +- tests/init.go | 18 +++ 34 files changed, 485 insertions(+), 265 deletions(-) diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go index 3a8b785ff08..5bf1a0d7151 100644 --- a/cmd/evm/internal/t8ntool/block.go +++ b/cmd/evm/internal/t8ntool/block.go @@ -158,7 +158,7 @@ func (i *bbInput) ToBlock() *types.Block { if header.Difficulty != nil { header.Difficulty = i.Header.Difficulty } - return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers) + return types.NewBlockWithHeader(header).WithBody2(i.Txs, i.Ommers, i.Withdrawals) } // SealBlock seals the given block using the configured engine. diff --git a/cmd/evm/internal/t8ntool/gen_header.go b/cmd/evm/internal/t8ntool/gen_header.go index c63da143152..df117701e09 100644 --- a/cmd/evm/internal/t8ntool/gen_header.go +++ b/cmd/evm/internal/t8ntool/gen_header.go @@ -34,7 +34,7 @@ func (h header) MarshalJSON() ([]byte, error) { MixDigest common.Hash `json:"mixHash"` Nonce *types.BlockNonce `json:"nonce"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoog" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` ExcessDataGas *big.Int `json:"excessDataGas" rlp:"optional"` } var enc header @@ -78,7 +78,7 @@ func (h *header) UnmarshalJSON(input []byte) error { MixDigest *common.Hash `json:"mixHash"` Nonce *types.BlockNonce `json:"nonce"` BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoog" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` ExcessDataGas *big.Int `json:"excessDataGas" rlp:"optional"` } var dec header diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index c3e1962bfe6..75e454852f2 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -269,6 +269,14 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa return err } } + shanghai := chain.Config().IsShanghai(header.Number) + if shanghai && header.WithdrawalsHash == nil { + return fmt.Errorf("missing withdrawalsHash") + } + // Verify existence / non-existence of withdrawalsHash. + if !shanghai && header.WithdrawalsHash != nil { + return fmt.Errorf("invalid withdrawalsHash: have %s, expected nil", header.WithdrawalsHash) + } if chain.Config().IsSharding(header.Number) { // Verify the header's EIP-4844 attributes. if err := misc.VerifyEip4844Header(chain.Config(), parent, header); err != nil { @@ -334,13 +342,19 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine, setting the final state on the header -func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { // Finalize is different with Prepare, it can be used in both block generation // and verification. So determine the consensus rules by header type. if !beacon.IsPoSHeader(header) { - beacon.ethone.Finalize(chain, header, state, txs, uncles) + beacon.ethone.Finalize(chain, header, state, txs, uncles, withdrawals) return } + // If withdrawals have been activated, process each one. + if chain.Config().IsShanghai(header.Number) { + for _, w := range withdrawals { + state.AddBalance(w.Address, w.Amount) + } + } // The block reward is no longer handled here. It's done by the // external consensus engine. header.Root = state.IntermediateRoot(true) @@ -355,15 +369,15 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, setting the final state and // assembling the block. -func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { // FinalizeAndAssemble is different with Prepare, it can be used in both block // generation and verification. So determine the consensus rules by header type. if !beacon.IsPoSHeader(header) { - return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts) + return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts, nil) } // Finalize and assemble the block - beacon.Finalize(chain, header, state, txs, uncles) - return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil + beacon.Finalize(chain, header, state, txs, uncles, withdrawals) + return types.NewBlock2(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)), nil } // Seal generates a new sealing request for the given input block and pushes diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 7e566c59910..c84697f821e 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -572,7 +572,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given. -func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { +func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { // No block rewards in PoA, so the state remains as is and uncles are dropped header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = types.CalcUncleHash(nil) @@ -587,9 +587,9 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. -func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { // Finalize block - c.Finalize(chain, header, state, txs, uncles) + c.Finalize(chain, header, state, txs, uncles, nil) // Assemble and return the final block for sealing return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil @@ -758,6 +758,9 @@ func encodeSigHeader(w io.Writer, header *types.Header) { if header.BaseFee != nil { enc = append(enc, header.BaseFee) } + if header.WithdrawalsHash != nil && *header.WithdrawalsHash != types.EmptyRootHash { + panic("unexpected withdrawal hash value in clique") + } if header.ExcessDataGas != nil { enc = append(enc, header.ExcessDataGas) } diff --git a/consensus/consensus.go b/consensus/consensus.go index af8ce98ff3b..190d5ae12c8 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -90,7 +90,7 @@ type Engine interface { // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, - uncles []*types.Header) + uncles []*types.Header, withdrawals []*types.Withdrawal) // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards) and assembles the final block. @@ -98,7 +98,7 @@ type Engine interface { // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, - uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) + uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 53a9d150b83..52e103e0abf 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -605,7 +605,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H // Finalize implements consensus.Engine, accumulating the block and uncle rewards, // setting the final state on the header -func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { +func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { // Accumulate any block and uncle rewards and commit the final state root accumulateRewards(chain.Config(), state, header, uncles) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) @@ -620,9 +620,9 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, accumulating the block and // uncle rewards, setting the final state and assembling the block. -func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { // Finalize block - ethash.Finalize(chain, header, state, txs, uncles) + ethash.Finalize(chain, header, state, txs, uncles, nil) // Header seems complete, assemble into a block and return return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil @@ -650,6 +650,9 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { if header.BaseFee != nil { enc = append(enc, header.BaseFee) } + if header.WithdrawalsHash != nil { + enc = append(enc, header.WithdrawalsHash) + } if header.ExcessDataGas != nil { enc = append(enc, header.ExcessDataGas) } diff --git a/core/beacon/gen_blockparams.go b/core/beacon/gen_blockparams.go index 0e2ea4bb133..a7df96e091c 100644 --- a/core/beacon/gen_blockparams.go +++ b/core/beacon/gen_blockparams.go @@ -8,46 +8,53 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" ) var _ = (*payloadAttributesMarshaling)(nil) // MarshalJSON marshals as JSON. -func (p PayloadAttributesV1) MarshalJSON() ([]byte, error) { - type PayloadAttributesV1 struct { - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` +func (p PayloadAttributes) MarshalJSON() ([]byte, error) { + type PayloadAttributes struct { + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` } - var enc PayloadAttributesV1 + var enc PayloadAttributes enc.Timestamp = hexutil.Uint64(p.Timestamp) enc.Random = p.Random enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient + enc.Withdrawals = p.Withdrawals return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. -func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error { - type PayloadAttributesV1 struct { - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Random *common.Hash `json:"prevRandao" gencodec:"required"` - SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"` +func (p *PayloadAttributes) UnmarshalJSON(input []byte) error { + type PayloadAttributes struct { + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Random *common.Hash `json:"prevRandao" gencodec:"required"` + SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` } - var dec PayloadAttributesV1 + var dec PayloadAttributes if err := json.Unmarshal(input, &dec); err != nil { return err } if dec.Timestamp == nil { - return errors.New("missing required field 'timestamp' for PayloadAttributesV1") + return errors.New("missing required field 'timestamp' for PayloadAttributes") } p.Timestamp = uint64(*dec.Timestamp) if dec.Random == nil { - return errors.New("missing required field 'prevRandao' for PayloadAttributesV1") + return errors.New("missing required field 'prevRandao' for PayloadAttributes") } p.Random = *dec.Random if dec.SuggestedFeeRecipient == nil { - return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributesV1") + return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributes") } p.SuggestedFeeRecipient = *dec.SuggestedFeeRecipient + if dec.Withdrawals != nil { + p.Withdrawals = dec.Withdrawals + } return nil } diff --git a/core/beacon/gen_ed.go b/core/beacon/gen_ed.go index dc1bd132335..921320eb53f 100644 --- a/core/beacon/gen_ed.go +++ b/core/beacon/gen_ed.go @@ -9,30 +9,32 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" ) var _ = (*executableDataMarshaling)(nil) // MarshalJSON marshals as JSON. -func (e ExecutableDataV1) MarshalJSON() ([]byte, error) { - type ExecutableDataV1 struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - ExcessDataGas *hexutil.Big `json:"excessDataGas" gencodec:"optional"` - } - var enc ExecutableDataV1 +func (e ExecutableData) MarshalJSON() ([]byte, error) { + type ExecutableData struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + ExcessDataGas *hexutil.Big `json:"excessDataGas"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + } + var enc ExecutableData enc.ParentHash = e.ParentHash enc.FeeRecipient = e.FeeRecipient enc.StateRoot = e.StateRoot @@ -53,86 +55,88 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) { } } enc.ExcessDataGas = (*hexutil.Big)(e.ExcessDataGas) + enc.Withdrawals = e.Withdrawals return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. -func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error { - type ExecutableDataV1 struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Random *common.Hash `json:"prevRandao" gencodec:"required"` - Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` - BlockHash *common.Hash `json:"blockHash" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - ExcessDataGas *hexutil.Big `json:"excessDataGas" gencodec:"optional"` - } - var dec ExecutableDataV1 +func (e *ExecutableData) UnmarshalJSON(input []byte) error { + type ExecutableData struct { + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random *common.Hash `json:"prevRandao" gencodec:"required"` + Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + BlockHash *common.Hash `json:"blockHash" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + ExcessDataGas *hexutil.Big `json:"excessDataGas"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + } + var dec ExecutableData if err := json.Unmarshal(input, &dec); err != nil { return err } if dec.ParentHash == nil { - return errors.New("missing required field 'parentHash' for ExecutableDataV1") + return errors.New("missing required field 'parentHash' for ExecutableData") } e.ParentHash = *dec.ParentHash if dec.FeeRecipient == nil { - return errors.New("missing required field 'feeRecipient' for ExecutableDataV1") + return errors.New("missing required field 'feeRecipient' for ExecutableData") } e.FeeRecipient = *dec.FeeRecipient if dec.StateRoot == nil { - return errors.New("missing required field 'stateRoot' for ExecutableDataV1") + return errors.New("missing required field 'stateRoot' for ExecutableData") } e.StateRoot = *dec.StateRoot if dec.ReceiptsRoot == nil { - return errors.New("missing required field 'receiptsRoot' for ExecutableDataV1") + return errors.New("missing required field 'receiptsRoot' for ExecutableData") } e.ReceiptsRoot = *dec.ReceiptsRoot if dec.LogsBloom == nil { - return errors.New("missing required field 'logsBloom' for ExecutableDataV1") + return errors.New("missing required field 'logsBloom' for ExecutableData") } e.LogsBloom = *dec.LogsBloom if dec.Random == nil { - return errors.New("missing required field 'prevRandao' for ExecutableDataV1") + return errors.New("missing required field 'prevRandao' for ExecutableData") } e.Random = *dec.Random if dec.Number == nil { - return errors.New("missing required field 'blockNumber' for ExecutableDataV1") + return errors.New("missing required field 'blockNumber' for ExecutableData") } e.Number = uint64(*dec.Number) if dec.GasLimit == nil { - return errors.New("missing required field 'gasLimit' for ExecutableDataV1") + return errors.New("missing required field 'gasLimit' for ExecutableData") } e.GasLimit = uint64(*dec.GasLimit) if dec.GasUsed == nil { - return errors.New("missing required field 'gasUsed' for ExecutableDataV1") + return errors.New("missing required field 'gasUsed' for ExecutableData") } e.GasUsed = uint64(*dec.GasUsed) if dec.Timestamp == nil { - return errors.New("missing required field 'timestamp' for ExecutableDataV1") + return errors.New("missing required field 'timestamp' for ExecutableData") } e.Timestamp = uint64(*dec.Timestamp) if dec.ExtraData == nil { - return errors.New("missing required field 'extraData' for ExecutableDataV1") + return errors.New("missing required field 'extraData' for ExecutableData") } e.ExtraData = *dec.ExtraData if dec.BaseFeePerGas == nil { - return errors.New("missing required field 'baseFeePerGas' for ExecutableDataV1") + return errors.New("missing required field 'baseFeePerGas' for ExecutableData") } e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas) if dec.BlockHash == nil { - return errors.New("missing required field 'blockHash' for ExecutableDataV1") + return errors.New("missing required field 'blockHash' for ExecutableData") } e.BlockHash = *dec.BlockHash if dec.Transactions == nil { - return errors.New("missing required field 'transactions' for ExecutableDataV1") + return errors.New("missing required field 'transactions' for ExecutableData") } e.Transactions = make([][]byte, len(dec.Transactions)) for k, v := range dec.Transactions { @@ -141,5 +145,8 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error { if dec.ExcessDataGas != nil { e.ExcessDataGas = (*big.Int)(dec.ExcessDataGas) } + if dec.Withdrawals != nil { + e.Withdrawals = dec.Withdrawals + } return nil } diff --git a/core/beacon/types.go b/core/beacon/types.go index 799f043681c..2dddff338f9 100644 --- a/core/beacon/types.go +++ b/core/beacon/types.go @@ -27,48 +27,49 @@ import ( "github.com/ethereum/go-ethereum/trie" ) -//go:generate go run github.com/fjl/gencodec -type PayloadAttributesV1 -field-override payloadAttributesMarshaling -out gen_blockparams.go +//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go -// PayloadAttributesV1 structure described at https://github.com/ethereum/execution-apis/pull/74 -type PayloadAttributesV1 struct { - Timestamp uint64 `json:"timestamp" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` +// PayloadAttributes describes the environment context in which a block should +// be built. +type PayloadAttributes struct { + Timestamp uint64 `json:"timestamp" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` } -// JSON type overrides for PayloadAttributesV1. +// JSON type overrides for PayloadAttributes. type payloadAttributesMarshaling struct { Timestamp hexutil.Uint64 } -// BlobsBundleV1 holds the blobs of an execution payload, to be retrieved separately -type BlobsBundleV1 struct { +// BlobsBundle holds the blobs of an execution payload, to be retrieved separately +type BlobsBundle struct { BlockHash common.Hash `json:"blockHash" gencodec:"required"` KZGs []types.KZGCommitment `json:"kzgs" gencodec:"required"` Blobs []types.Blob `json:"blobs" gencodec:"required"` } -//go:generate go run github.com/fjl/gencodec -type ExecutableDataV1 -field-override executableDataMarshaling -out gen_ed.go - -// ExecutableDataV1 structure described at https://github.com/ethereum/execution-apis/tree/main/src/engine/specification.md -type ExecutableDataV1 struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom []byte `json:"logsBloom" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - Number uint64 `json:"blockNumber" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` - Timestamp uint64 `json:"timestamp" gencodec:"required"` - ExtraData []byte `json:"extraData" gencodec:"required"` - BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - Transactions [][]byte `json:"transactions" gencodec:"required"` - - // New in EIP-4844 - ExcessDataGas *big.Int `json:"excessDataGas" gencodec:"optional"` +//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go + +// ExecutableData is the data necessary to execute an EL payload. +type ExecutableData struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom []byte `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + Number uint64 `json:"blockNumber" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Timestamp uint64 `json:"timestamp" gencodec:"required"` + ExtraData []byte `json:"extraData" gencodec:"required"` + BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + Transactions [][]byte `json:"transactions" gencodec:"required"` + ExcessDataGas *big.Int `json:"excessDataGas"` // New in EIP-4844 + Withdrawals []*types.Withdrawal `json:"withdrawals"` } // JSON type overrides for executableData. @@ -153,8 +154,10 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { // uncleHash = emptyUncleHash // difficulty = 0 // -// and that the blockhash of the constructed block matches the parameters. -func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) { +// and that the blockhash of the constructed block matches the parameters. Nil +// 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) (*types.Block, error) { txs, err := decodeTransactions(params.Transactions) if err != nil { return nil, err @@ -169,36 +172,45 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) { if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) { return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas) } + // Only set withdrawalsRoot if it is non-nil. This allows CLs to use + // ExecutableData before withdrawals are enabled by marshaling + // Withdrawals as the json null value. + var withdrawalsRoot *common.Hash + if params.Withdrawals != nil { + h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil)) + withdrawalsRoot = &h + } 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, - ExcessDataGas: params.ExcessDataGas, - Extra: params.ExtraData, - MixDigest: params.Random, + 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, + ExcessDataGas: params.ExcessDataGas, + Extra: params.ExtraData, + MixDigest: params.Random, + WithdrawalsHash: withdrawalsRoot, } - block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) + block := types.NewBlockWithHeader(header).WithBody2(txs, nil /* uncles */, params.Withdrawals) if block.Hash() != params.BlockHash { return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) } return block, nil } -// BlockToExecutableData constructs the executableDataV1 structure by filling the +// BlockToExecutableData constructs the executableData structure by filling the // fields from the given block. It assumes the given block is post-merge block. // Additional blob contents are provided as well. -func BlockToExecutableData(block *types.Block) *ExecutableDataV1 { - return &ExecutableDataV1{ +func BlockToExecutableData(block *types.Block) *ExecutableData { + return &ExecutableData{ BlockHash: block.Hash(), ParentHash: block.ParentHash(), FeeRecipient: block.Coinbase(), @@ -217,9 +229,9 @@ func BlockToExecutableData(block *types.Block) *ExecutableDataV1 { } } -func BlockToBlobData(block *types.Block) (*BlobsBundleV1, error) { +func BlockToBlobData(block *types.Block) (*BlobsBundle, error) { blockHash := block.Hash() - blobsBundle := &BlobsBundleV1{BlockHash: blockHash} + blobsBundle := &BlobsBundle{BlockHash: blockHash} for i, tx := range block.Transactions() { if tx.Type() == types.BlobTxType { versionedHashes, kzgs, blobs, aggProof := tx.BlobWrapData() diff --git a/core/chain_makers.go b/core/chain_makers.go index 064e33fd552..a30f78381f9 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -40,15 +40,30 @@ type BlockGen struct { header *types.Header statedb *state.StateDB - gasPool *GasPool - txs []*types.Transaction - receipts []*types.Receipt - uncles []*types.Header + gasPool *GasPool + txs []*types.Transaction + receipts []*types.Receipt + uncles []*types.Header + withdrawals []*types.Withdrawal config *params.ChainConfig engine consensus.Engine } +// AddWithdrawal adds a withdrawal to the generated block. +func (b *BlockGen) AddWithdrawal(w *types.Withdrawal) { + // The withdrawal will be assigned the next valid index. + var idx uint64 + for i := b.i - 1; i >= 0; i-- { + if wd := b.chain[i].Withdrawals(); len(wd) != 0 { + idx = wd[len(wd)-1].Index + 1 + break + } + } + w.Index = idx + b.withdrawals = append(b.withdrawals, w) +} + // SetCoinbase sets the coinbase of the generated block. // It can be called at most once. func (b *BlockGen) SetCoinbase(addr common.Address) { @@ -276,7 +291,14 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } if b.engine != nil { // Finalize and seal the block - block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts) + shanghai := config.IsShanghai(b.header.Number) + if shanghai && b.withdrawals == nil { + // need to make empty list to denote non-nil, but empty withdrawals to calc withdrawals hash + b.withdrawals = make([]*types.Withdrawal, 0) + } else if !shanghai && b.withdrawals != nil { + panic("withdrawals set before activation") + } + block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts, b.withdrawals) // Write state changes to db root, err := statedb.Commit(config.IsEIP158(b.header.Number)) @@ -397,4 +419,9 @@ func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil } func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil } func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } -func (cr *fakeChainReader) GetTd(hash common.Hash, number uint64) *big.Int { return nil } +func (cr *fakeChainReader) GetTd(hash common.Hash, number uint64) *big.Int { + if cr.config.TerminalTotalDifficultyPassed { + return cr.config.TerminalTotalDifficulty + } + return nil +} diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 881660aa8e8..16c2f60ad08 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -782,7 +782,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) + return types.NewBlockWithHeader(header).WithBody2(body.Transactions, body.Uncles, body.Withdrawals) } // WriteBlock serializes a block into the database, header and body separately. @@ -882,7 +882,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) + return types.NewBlockWithHeader(bad.Header).WithBody2(bad.Body.Transactions, bad.Body.Uncles, bad.Body.Withdrawals) } } return nil @@ -901,7 +901,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)) + blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody2(bad.Body.Transactions, bad.Body.Uncles, bad.Body.Withdrawals)) } return blocks } diff --git a/core/state_processor.go b/core/state_processor.go index 6f9fa125774..30347f84000 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -86,8 +86,12 @@ func (p *StateProcessor) Process(block *types.Block, excessDataGas *big.Int, sta receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } + // Fail if Shanghai not enabled and len(withdrawals) is non-zero. + if !p.config.IsShanghai(block.Number()) && len(block.Withdrawals()) != 0 { + return nil, nil, 0, fmt.Errorf("non-nil withdrawal before shanghai") + } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) + p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), block.Withdrawals()) return receipts, allLogs, *usedGas, nil } diff --git a/core/types/block.go b/core/types/block.go index 4a46afccd8c..6dfa6195ae6 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -197,6 +197,7 @@ func (h *Header) EmptyReceipts() bool { type Body struct { Transactions []*Transaction Uncles []*Header + Withdrawals []*Withdrawal `rlp:"optional"` } // Block represents an entire block in the Ethereum blockchain. @@ -491,7 +492,7 @@ func (b *Block) ExcessDataGas() *big.Int { func (b *Block) Header() *Header { return CopyHeader(b.header) } // Body returns the non-header content of the block. -func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } +func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles, b.withdrawals} } // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previously cached value. diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index b21970aa50f..b0f07902472 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -156,7 +156,19 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI { // // If there are payloadAttributes: we try to assemble a block with the payloadAttributes // and return its payloadID. -func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) { +func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) { + if payloadAttributes != nil && payloadAttributes.Withdrawals != nil { + return beacon.STATUS_INVALID, fmt.Errorf("withdrawals not supported in V1") + } + return api.forkchoiceUpdated(update, payloadAttributes) +} + +// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes. +func (api *ConsensusAPI) ForkchoiceUpdatedV2(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) { + return api.forkchoiceUpdated(update, payloadAttributes) +} + +func (api *ConsensusAPI) forkchoiceUpdated(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) { api.forkchoiceLock.Lock() defer api.forkchoiceLock.Unlock() @@ -335,7 +347,16 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config beacon.Transit } // GetPayloadV1 returns a cached payload by id. -func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) { +func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableData, error) { + data, err := api.GetPayloadV2(payloadID) + if err != nil { + return nil, err + } + return data, nil +} + +// GetPayloadV2 returns a cached payload by id. +func (api *ConsensusAPI) GetPayloadV2(payloadID beacon.PayloadID) (*beacon.ExecutableData, error) { log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID) data := api.localBlocks.get(payloadID) if data == nil { @@ -345,7 +366,7 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.Execu } // GetBlobsBundleV1 returns a bundle of all blob and corresponding KZG commitments by payload id -func (api *ConsensusAPI) GetBlobsBundleV1(payloadID beacon.PayloadID) (*beacon.BlobsBundleV1, error) { +func (api *ConsensusAPI) GetBlobsBundleV1(payloadID beacon.PayloadID) (*beacon.BlobsBundle, error) { log.Trace("Engine API request received", "method", "GetBlobsBundle") data, err := api.localBlocks.getBlobsBundle(payloadID) if err != nil { @@ -358,7 +379,7 @@ func (api *ConsensusAPI) GetBlobsBundleV1(payloadID beacon.PayloadID) (*beacon.B } // NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableData) (beacon.PayloadStatusV1, error) { // The locking here is, strictly, not required. Without these locks, this can happen: // // 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to @@ -375,7 +396,7 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa api.newPayloadLock.Lock() defer api.newPayloadLock.Unlock() - log.Trace("Engine API request received", "method", "ExecutePayload", "number", params.Number, "hash", params.BlockHash) + log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) block, err := beacon.ExecutableDataToBlock(params) if err != nil { log.Debug("Invalid NewPayload params", "params", params, "error", err) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index e97bf3287fb..75f1e211b95 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -90,7 +90,7 @@ func TestEth2AssembleBlock(t *testing.T) { t.Fatalf("error signing transaction, err=%v", err) } ethservice.TxPool().AddLocal(tx) - blockParams := beacon.PayloadAttributesV1{ + blockParams := beacon.PayloadAttributes{ Timestamp: blocks[9].Time() + 5, } // The miner needs to pick up on the txs in the pool, so a few retries might be @@ -102,7 +102,7 @@ func TestEth2AssembleBlock(t *testing.T) { // assembleWithTransactions tries to assemble a block, retrying until it has 'want', // number of transactions in it, or it has retried three times. -func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1, want int) (execData *beacon.ExecutableDataV1, err error) { +func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributes, want int) (execData *beacon.ExecutableData, err error) { for retries := 3; retries > 0; retries-- { execData, err = assembleBlock(api, parentHash, params) if err != nil { @@ -126,7 +126,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { // Put the 10th block's tx in the pool and produce a new block api.eth.TxPool().AddRemotesSync(blocks[9].Transactions()) - blockParams := beacon.PayloadAttributesV1{ + blockParams := beacon.PayloadAttributes{ Timestamp: blocks[8].Time() + 5, } // The miner needs to pick up on the txs in the pool, so a few retries might be @@ -165,7 +165,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { // Put the 10th block's tx in the pool and produce a new block ethservice.TxPool().AddLocals(blocks[9].Transactions()) - blockParams := beacon.PayloadAttributesV1{ + blockParams := beacon.PayloadAttributes{ Timestamp: blocks[8].Time() + 5, } fcState := beacon.ForkchoiceStateV1{ @@ -244,7 +244,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) { - params := beacon.PayloadAttributesV1{ + params := beacon.PayloadAttributes{ Timestamp: test.time, Random: crypto.Keccak256Hash([]byte{byte(123)}), SuggestedFeeRecipient: parent.Coinbase(), @@ -288,7 +288,7 @@ func TestEth2NewBlock(t *testing.T) { tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) ethservice.TxPool().AddLocal(tx) - execData, err := assembleWithTransactions(api, parent.Hash(), &beacon.PayloadAttributesV1{ + execData, err := assembleWithTransactions(api, parent.Hash(), &beacon.PayloadAttributes{ Timestamp: parent.Time() + 5, }, 1) if err != nil { @@ -330,7 +330,7 @@ func TestEth2NewBlock(t *testing.T) { ) parent = preMergeBlocks[len(preMergeBlocks)-1] for i := 0; i < 10; i++ { - execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{ + execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributes{ Timestamp: parent.Time() + 6, }) if err != nil { @@ -577,7 +577,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { }) ethservice.TxPool().AddRemotesSync([]*types.Transaction{tx}) var ( - params = beacon.PayloadAttributesV1{ + params = beacon.PayloadAttributes{ Timestamp: parent.Time() + 1, Random: crypto.Keccak256Hash([]byte{byte(i)}), SuggestedFeeRecipient: parent.Coinbase(), @@ -587,7 +587,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { SafeBlockHash: common.Hash{}, FinalizedBlockHash: common.Hash{}, } - payload *beacon.ExecutableDataV1 + payload *beacon.ExecutableData resp beacon.ForkChoiceResponse err error ) @@ -634,7 +634,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { } } -func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) { +func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributes) (*beacon.ExecutableData, error) { args := &miner.BuildPayloadArgs{ Parent: parentHash, Timestamp: params.Timestamp, @@ -708,8 +708,8 @@ func TestEmptyBlocks(t *testing.T) { } } -func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 { - params := beacon.PayloadAttributesV1{ +func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableData { + params := beacon.PayloadAttributes{ Timestamp: parent.Time() + 1, Random: crypto.Keccak256Hash([]byte{byte(1)}), SuggestedFeeRecipient: parent.Coinbase(), @@ -724,7 +724,7 @@ func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon // setBlockhash sets the blockhash of a modified ExecutableData. // Can be used to make modified payloads look valid. -func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 { +func setBlockhash(data *beacon.ExecutableData) *beacon.ExecutableData { txs, _ := decodeTransactions(data.Transactions) number := big.NewInt(0) number.SetUint64(data.Number) @@ -784,7 +784,7 @@ func TestTrickRemoteBlockCache(t *testing.T) { setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {}) commonAncestor = ethserviceA.BlockChain().CurrentBlock() - var invalidChain []*beacon.ExecutableDataV1 + var invalidChain []*beacon.ExecutableData // create a valid payload (P1) //payload1 := getNewPayload(t, apiA, commonAncestor) //invalidChain = append(invalidChain, payload1) @@ -911,7 +911,7 @@ func TestSimultaneousNewBlock(t *testing.T) { parent = preMergeBlocks[len(preMergeBlocks)-1] ) for i := 0; i < 10; i++ { - execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{ + execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributes{ Timestamp: parent.Time() + 5, }) if err != nil { diff --git a/eth/catalyst/queue.go b/eth/catalyst/queue.go index 45fc1250c50..c026e7b645c 100644 --- a/eth/catalyst/queue.go +++ b/eth/catalyst/queue.go @@ -70,7 +70,7 @@ func (q *payloadQueue) put(id beacon.PayloadID, payload *miner.Payload) { } // get retrieves a previously stored payload item or nil if it does not exist. -func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 { +func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableData { q.lock.RLock() defer q.lock.RUnlock() @@ -86,7 +86,7 @@ func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 { } // get retrieves a previously stored blobs bundle item or nil if it does not exist. -func (q *payloadQueue) getBlobsBundle(id beacon.PayloadID) (*beacon.BlobsBundleV1, error) { +func (q *payloadQueue) getBlobsBundle(id beacon.PayloadID) (*beacon.BlobsBundle, error) { q.lock.RLock() defer q.lock.RUnlock() diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 36d6795e7af..2f0c4acf788 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -273,8 +273,9 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et rlp.DecodeBytes(blob, bodies[i]) } var ( - txsHashes = make([]common.Hash, len(bodies)) - uncleHashes = make([]common.Hash, len(bodies)) + txsHashes = make([]common.Hash, len(bodies)) + uncleHashes = make([]common.Hash, len(bodies)) + withdrawalHashes = make([]common.Hash, len(bodies)) ) hasher := trie.NewStackTrie(nil) for i, body := range bodies { @@ -287,7 +288,7 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et res := ð.Response{ Req: req, Res: (*eth.BlockBodiesPacket)(&bodies), - Meta: [][]common.Hash{txsHashes, uncleHashes}, + Meta: [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes}, Time: 1, Done: make(chan error, 1), // Ignore the returned status } diff --git a/eth/downloader/fetchers_concurrent_bodies.go b/eth/downloader/fetchers_concurrent_bodies.go index e84206fe995..9440972c6d7 100644 --- a/eth/downloader/fetchers_concurrent_bodies.go +++ b/eth/downloader/fetchers_concurrent_bodies.go @@ -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 := packet.Res.(*eth.BlockBodiesPacket).Unpack() - hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes} + txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesPacket).Unpack() + hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes} - accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1]) + accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2]) switch { case err == nil && len(txs) == 0: peer.log.Trace("Requested bodies delivered") diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 60a83a7fb0d..61c61352d56 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -67,6 +67,7 @@ type fetchResult struct { Uncles []*types.Header Transactions types.Transactions Receipts types.Receipts + Withdrawals types.Withdrawals } func newFetchResult(header *types.Header, fastSync bool) *fetchResult { @@ -764,7 +765,9 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []comm // DeliverBodies injects a block body retrieval response into the results queue. // The method returns the number of blocks bodies accepted from the delivery and // 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) (int, error) { +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) { q.lock.Lock() defer q.lock.Unlock() @@ -775,12 +778,16 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH if uncleListHashes[index] != header.UncleHash { return errInvalidBody } + if header.WithdrawalsHash != nil && (withdrawalListHashes[index] != *header.WithdrawalsHash) { + return errInvalidBody + } return nil } reconstruct := func(index int, result *fetchResult) { result.Transactions = txLists[index] result.Uncles = uncleLists[index] + result.Withdrawals = withdrawalLists[index] result.SetBodyDone() } return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool, diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go index 8631b27c927..6babf944086 100644 --- a/eth/downloader/queue_test.go +++ b/eth/downloader/queue_test.go @@ -339,7 +339,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) + _, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil) if err != nil { fmt.Printf("delivered %d bodies %v\n", len(txset), err) } diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index a3dcf5a12f6..8594a29e512 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -64,7 +64,7 @@ func (c Config) MarshalTOML() (interface{}, error) { CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"` OverrideTerminalTotalDifficultyPassed *bool `toml:",omitempty"` - FullSyncTarget *types.Block + SyncTarget *types.Block } var enc Config enc.Genesis = c.Genesis @@ -111,7 +111,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.CheckpointOracle = c.CheckpointOracle enc.OverrideTerminalTotalDifficulty = c.OverrideTerminalTotalDifficulty enc.OverrideTerminalTotalDifficultyPassed = c.OverrideTerminalTotalDifficultyPassed - enc.FullSyncTarget = c.SyncTarget + enc.SyncTarget = c.SyncTarget return &enc, nil } @@ -162,7 +162,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"` OverrideTerminalTotalDifficultyPassed *bool `toml:",omitempty"` - FullSyncTarget *types.Block + SyncTarget *types.Block } var dec Config if err := unmarshal(&dec); err != nil { @@ -300,8 +300,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideTerminalTotalDifficultyPassed != nil { c.OverrideTerminalTotalDifficultyPassed = dec.OverrideTerminalTotalDifficultyPassed } - if dec.FullSyncTarget != nil { - c.SyncTarget = dec.FullSyncTarget + if dec.SyncTarget != nil { + c.SyncTarget = dec.SyncTarget } return nil } diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index bd1a34c83c0..1d256c29447 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -125,6 +125,7 @@ type bodyFilterTask struct { peer string // The source peer of block bodies transactions [][]*types.Transaction // Collection of transactions per block bodies uncles [][]*types.Header // Collection of uncles per block bodies + withdrawals [][]*types.Withdrawal // Collection of withdrawals per block bodies time time.Time // Arrival time of the blocks' contents } @@ -302,8 +303,8 @@ func (f *BlockFetcher) FilterHeaders(peer string, headers []*types.Header, time // FilterBodies extracts all the block bodies that were explicitly requested by // the fetcher, returning those that should be handled differently. -func (f *BlockFetcher) FilterBodies(peer string, transactions [][]*types.Transaction, uncles [][]*types.Header, time time.Time) ([][]*types.Transaction, [][]*types.Header) { - log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions), "uncles", len(uncles)) +func (f *BlockFetcher) FilterBodies(peer string, transactions [][]*types.Transaction, uncles [][]*types.Header, withdrawals [][]*types.Withdrawal, time time.Time) ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal) { + log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions), "uncles", len(uncles), "withdrawals", len(withdrawals)) // Send the filter channel to the fetcher filter := make(chan *bodyFilterTask) @@ -311,20 +312,20 @@ func (f *BlockFetcher) FilterBodies(peer string, transactions [][]*types.Transac select { case f.bodyFilter <- filter: case <-f.quit: - return nil, nil + return nil, nil, nil } // Request the filtering of the body list select { - case filter <- &bodyFilterTask{peer: peer, transactions: transactions, uncles: uncles, time: time}: + case filter <- &bodyFilterTask{peer: peer, transactions: transactions, uncles: uncles, withdrawals: withdrawals, time: time}: case <-f.quit: - return nil, nil + return nil, nil, nil } // Retrieve the bodies remaining after filtering select { case task := <-filter: - return task.transactions, task.uncles + return task.transactions, task.uncles, task.withdrawals case <-f.quit: - return nil, nil + return nil, nil, nil } } @@ -541,8 +542,8 @@ func (f *BlockFetcher) loop() { case res := <-resCh: res.Done <- nil - txs, uncles := res.Res.(*eth.BlockBodiesPacket).Unpack() - f.FilterBodies(peer, txs, uncles, time.Now()) + txs, uncles, wxs := res.Res.(*eth.BlockBodiesPacket).Unpack() + f.FilterBodies(peer, txs, uncles, wxs, time.Now()) case <-timeout.C: // The peer didn't respond in time. The request diff --git a/eth/fetcher/block_fetcher_test.go b/eth/fetcher/block_fetcher_test.go index 9e5693c02e5..a64f42fef7d 100644 --- a/eth/fetcher/block_fetcher_test.go +++ b/eth/fetcher/block_fetcher_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -36,24 +37,52 @@ import ( ) var ( - testdb = rawdb.NewMemoryDatabase() - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testAddress = crypto.PubkeyToAddress(testKey.PublicKey) - gspec = &core.Genesis{ - Config: params.TestChainConfig, + testdb = rawdb.NewMemoryDatabase() + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddress = crypto.PubkeyToAddress(testKey.PublicKey) + unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit, BaseFee: big.NewInt(params.InitialBaseFee)}, nil, nil, nil, trie.NewStackTrie(nil)) + gspec *core.Genesis + genesis *types.Block +) + +func init() { + config := ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + TerminalTotalDifficultyPassed: true, + Ethash: new(params.EthashConfig), + } + gspec = &core.Genesis{ + Config: config, Alloc: core.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}}, BaseFee: big.NewInt(params.InitialBaseFee), } - genesis = gspec.MustCommit(testdb) - unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit, BaseFee: big.NewInt(params.InitialBaseFee)}, nil, nil, nil, trie.NewStackTrie(nil)) -) + genesis = gspec.MustCommit(testdb) +} // makeChain creates a chain of n blocks starting at and including parent. // the returned hash chain is ordered head->parent. In addition, every 3rd block // contains a transaction and every 5th an uncle to allow testing correct block // reassembly. func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) { - blocks, _ := core.GenerateChain(gspec.Config, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(gspec.Config, parent, beacon.New(ethash.NewFaker()), testdb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // If the block number is multiple of 3, send a bonus transaction to the miner @@ -69,6 +98,12 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common if i > 0 && i%5 == 0 { block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 2).Hash(), Number: big.NewInt(int64(i - 1))}) } + // If the block number is a multiple of 7, add a withdrawal + if i > 0 && i%7 == 0 { + block.AddWithdrawal(&types.Withdrawal{Address: testAddress, Amount: big.NewInt(42)}) + block.AddWithdrawal(&types.Withdrawal{Address: testAddress, Amount: big.NewInt(43)}) + block.AddWithdrawal(&types.Withdrawal{Address: testAddress, Amount: big.NewInt(44)}) + } }) hashes := make([]common.Hash, n+1) hashes[len(hashes)-1] = parent.Hash() @@ -233,13 +268,17 @@ func (f *fetcherTester) makeBodyFetcher(peer string, blocks map[common.Hash]*typ // Create a function that returns blocks from the closure return func(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { // Gather the block bodies to return - transactions := make([][]*types.Transaction, 0, len(hashes)) - uncles := make([][]*types.Header, 0, len(hashes)) + var ( + transactions = make([][]*types.Transaction, 0, len(hashes)) + uncles = make([][]*types.Header, 0, len(hashes)) + withdrawals = make([][]*types.Withdrawal, 0, len(hashes)) + ) for _, hash := range hashes { if block, ok := closure[hash]; ok { transactions = append(transactions, block.Transactions()) uncles = append(uncles, block.Uncles()) + withdrawals = append(withdrawals, block.Withdrawals()) } } // Return on a new thread @@ -248,6 +287,7 @@ func (f *fetcherTester) makeBodyFetcher(peer string, blocks map[common.Hash]*typ bodies[i] = ð.BlockBody{ Transactions: txs, Uncles: uncles[i], + Withdrawals: withdrawals[i], } } req := ð.Request{ diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 5c3d1be0a12..a5b9f5f0bd3 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -23,6 +23,8 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -56,21 +58,53 @@ type testBackend struct { // newTestBackend creates an empty chain and wraps it into a mock backend. func newTestBackend(blocks int) *testBackend { - return newTestBackendWithGenerator(blocks, nil) + return newTestBackendWithGenerator(blocks, false, nil) } // newTestBackend creates a chain with a number of explicitly defined blocks and // wraps it into a mock backend. -func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen)) *testBackend { - // Create a database pre-initialize with a genesis block - db := rawdb.NewMemoryDatabase() +func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend { + var ( + // Create a database pre-initialize with a genesis block + db = rawdb.NewMemoryDatabase() + config = params.TestChainConfig + engine consensus.Engine = ethash.NewFaker() + ) + + if shanghai { + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + TerminalTotalDifficultyPassed: true, + Ethash: new(params.EthashConfig), + } + engine = beacon.New(ethash.NewFaker()) + } + gspec := &core.Genesis{ - Config: params.TestChainConfig, + Config: config, Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}}, } - chain, _ := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := core.NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil) - _, bs, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), blocks, generator) + _, bs, _ := core.GenerateChainWithGenesis(gspec, engine, blocks, generator) if _, err := chain.InsertChain(bs); err != nil { panic(err) } @@ -305,7 +339,17 @@ func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) } func testGetBlockBodies(t *testing.T, protocol uint) { t.Parallel() - backend := newTestBackend(maxBodiesServe + 15) + gen := func(n int, g *core.BlockGen) { + if n%2 == 0 { + w := &types.Withdrawal{ + Address: common.Address{0xaa}, + Amount: big.NewInt(42), + } + g.AddWithdrawal(w) + } + } + + backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen) defer backend.close() peer, _ := newTestPeer("peer", protocol, backend) @@ -355,7 +399,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) { block := backend.chain.GetBlockByNumber(uint64(num)) hashes = append(hashes, block.Hash()) if len(bodies) < tt.expected { - bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles(), Withdrawals: block.Withdrawals()}) } break } @@ -365,7 +409,8 @@ func testGetBlockBodies(t *testing.T, protocol uint) { hashes = append(hashes, hash) if tt.available[j] && len(bodies) < tt.expected { block := backend.chain.GetBlockByHash(hash) - bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles(), Withdrawals: block.Withdrawals()}) + } } // Send the hash request and verify the response @@ -426,7 +471,7 @@ func testGetNodeData(t *testing.T, protocol uint, drop bool) { } } // Assemble the test environment - backend := newTestBackendWithGenerator(4, generator) + backend := newTestBackendWithGenerator(4, false, generator) defer backend.close() peer, _ := newTestPeer("peer", protocol, backend) @@ -544,7 +589,7 @@ func testGetBlockReceipts(t *testing.T, protocol uint) { } } // Assemble the test environment - backend := newTestBackendWithGenerator(4, generator) + backend := newTestBackendWithGenerator(4, false, generator) defer backend.close() peer, _ := newTestPeer("peer", protocol, backend) diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 2a7bced407c..3530c9a35b2 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -379,15 +379,19 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { } metadata := func() interface{} { var ( - txsHashes = make([]common.Hash, len(res.BlockBodiesPacket)) - uncleHashes = make([]common.Hash, len(res.BlockBodiesPacket)) + txsHashes = make([]common.Hash, len(res.BlockBodiesPacket)) + uncleHashes = make([]common.Hash, len(res.BlockBodiesPacket)) + withdrawalHashes = make([]common.Hash, len(res.BlockBodiesPacket)) ) hasher := trie.NewStackTrie(nil) for i, body := range res.BlockBodiesPacket { txsHashes[i] = types.DeriveSha(types.Transactions(body.Transactions), hasher) uncleHashes[i] = types.CalcUncleHash(body.Uncles) + if body.Withdrawals != nil { + withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher) + } } - return [][]common.Hash{txsHashes, uncleHashes} + return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes} } return peer.dispatchResponse(&Response{ id: res.RequestId, diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index 3af10caebeb..a534a8e67be 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -248,19 +248,21 @@ type BlockBodiesRLPPacket66 struct { 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 } // 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 *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header) { +func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal) { var ( - txset = make([][]*types.Transaction, len(*p)) - uncleset = make([][]*types.Header, len(*p)) + txset = make([][]*types.Transaction, len(*p)) + uncleset = make([][]*types.Header, len(*p)) + withdrawalset = make([][]*types.Withdrawal, len(*p)) ) for i, body := range *p { - txset[i], uncleset[i] = body.Transactions, body.Uncles + txset[i], uncleset[i], withdrawalset[i] = body.Transactions, body.Uncles, body.Withdrawals } - return txset, uncleset + return txset, uncleset, withdrawalset } // GetNodeDataPacket represents a trie node data query. diff --git a/les/catalyst/api.go b/les/catalyst/api.go index 822e0af038a..b5957583289 100644 --- a/les/catalyst/api.go +++ b/les/catalyst/api.go @@ -70,7 +70,7 @@ func NewConsensusAPI(les *les.LightEthereum) *ConsensusAPI { // // If there are payloadAttributes: we return an error since block creation is not // supported in les mode. -func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) { +func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) { if heads.HeadBlockHash == (common.Hash{}) { log.Warn("Forkchoice requested update to zero hash") return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this? @@ -100,12 +100,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, pay } // GetPayloadV1 returns a cached payload by id. It's not supported in les mode. -func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) { +func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableData, error) { return nil, beacon.GenericServerError.With(errors.New("not supported in light client mode")) } // ExecutePayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) { +func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableData) (beacon.PayloadStatusV1, error) { block, err := beacon.ExecutableDataToBlock(params) if err != nil { return api.invalid(), err diff --git a/les/catalyst/api_test.go b/les/catalyst/api_test.go index dbf92721be7..e09e233c9a4 100644 --- a/les/catalyst/api_test.go +++ b/les/catalyst/api_test.go @@ -118,7 +118,7 @@ func TestExecutePayloadV1(t *testing.T) { BaseFee: block.BaseFee(), }, nil, nil, nil, trie.NewStackTrie(nil)) - _, err := api.ExecutePayloadV1(beacon.ExecutableDataV1{ + _, err := api.ExecutePayloadV1(beacon.ExecutableData{ ParentHash: fakeBlock.ParentHash(), FeeRecipient: fakeBlock.Coinbase(), StateRoot: fakeBlock.Root(), @@ -192,7 +192,7 @@ func TestShardingExecutePayloadV1(t *testing.T) { ExcessDataGas: block.ExcessDataGas(), }, nil, nil, nil, trie.NewStackTrie(nil)) - _, err := api.ExecutePayloadV1(beacon.ExecutableDataV1{ + _, err := api.ExecutePayloadV1(beacon.ExecutableData{ ParentHash: fakeBlock.ParentHash(), FeeRecipient: fakeBlock.Coinbase(), StateRoot: fakeBlock.Root(), diff --git a/miner/payload_building.go b/miner/payload_building.go index 6188579357b..af5d7e13274 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -34,10 +34,11 @@ import ( // Check engine-api specification for more details. // https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1 type BuildPayloadArgs struct { - Parent common.Hash // The parent block to build payload on top - Timestamp uint64 // The provided timestamp of generated payload - FeeRecipient common.Address // The provided recipient address for collecting transaction fee - Random common.Hash // The provided randomness value + Parent common.Hash // The parent block to build payload on top + Timestamp uint64 // The provided timestamp of generated payload + FeeRecipient common.Address // The provided recipient address for collecting transaction fee + Random common.Hash // The provided randomness value + Withdrawals types.Withdrawals // The provided withdrawals } // Id computes an 8-byte identifier by hashing the components of the payload arguments. @@ -107,7 +108,7 @@ func (payload *Payload) update(block *types.Block, fees *big.Int, elapsed time.D // Resolve returns the latest built payload and also terminates the background // thread for updating payload. It's safe to be called multiple times. -func (payload *Payload) Resolve() *beacon.ExecutableDataV1 { +func (payload *Payload) Resolve() *beacon.ExecutableData { payload.lock.Lock() defer payload.lock.Unlock() @@ -124,7 +125,7 @@ func (payload *Payload) Resolve() *beacon.ExecutableDataV1 { // ResolveToBlobsBundle returns the latest built blobs bundle payload and also terminates the // background thread for updating payload. It's safe to be called multiple times. -func (payload *Payload) ResolveToBlobsBundle() (*beacon.BlobsBundleV1, error) { +func (payload *Payload) ResolveToBlobsBundle() (*beacon.BlobsBundle, error) { payload.lock.Lock() defer payload.lock.Unlock() @@ -141,7 +142,7 @@ func (payload *Payload) ResolveToBlobsBundle() (*beacon.BlobsBundleV1, error) { // ResolveEmpty is basically identical to Resolve, but it expects empty block only. // It's only used in tests. -func (payload *Payload) ResolveEmpty() *beacon.ExecutableDataV1 { +func (payload *Payload) ResolveEmpty() *beacon.ExecutableData { payload.lock.Lock() defer payload.lock.Unlock() @@ -150,7 +151,7 @@ func (payload *Payload) ResolveEmpty() *beacon.ExecutableDataV1 { // ResolveFull is basically identical to Resolve, but it expects full block only. // It's only used in tests. -func (payload *Payload) ResolveFull() *beacon.ExecutableDataV1 { +func (payload *Payload) ResolveFull() *beacon.ExecutableData { payload.lock.Lock() defer payload.lock.Unlock() @@ -170,7 +171,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. - empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, true) + empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, true) if err != nil { return nil, err } @@ -194,7 +195,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { select { case <-timer.C: start := time.Now() - block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, false) + block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, false) if err == nil { payload.update(block, fees, time.Since(start)) } diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index 226ae71b4ad..76bce583c1a 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -47,7 +47,7 @@ func TestBuildPayload(t *testing.T) { if err != nil { t.Fatalf("Failed to build payload %v", err) } - verify := func(data *beacon.ExecutableDataV1, txs int) { + verify := func(data *beacon.ExecutableData, txs int) { if data.ParentHash != b.chain.CurrentBlock().Hash() { t.Fatal("Unexpect parent hash") } diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 7dabc97c003..bd500453d2b 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -142,7 +142,7 @@ func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode } } -func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*beacon.ExecutableDataV1, error) { +func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*beacon.ExecutableData, error) { if n.typ != eth2MiningNode { return nil, errors.New("invalid node type") } @@ -150,7 +150,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) if timestamp <= parentTimestamp { timestamp = parentTimestamp + 1 } - payloadAttribute := beacon.PayloadAttributesV1{ + payloadAttribute := beacon.PayloadAttributes{ Timestamp: timestamp, Random: common.Hash{}, SuggestedFeeRecipient: common.HexToAddress("0xdeadbeef"), @@ -168,7 +168,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) return n.api.GetPayloadV1(*payload.PayloadID) } -func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error { +func (n *ethNode) insertBlock(eb beacon.ExecutableData) error { if !eth2types(n.typ) { return errors.New("invalid node type") } @@ -194,7 +194,7 @@ func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error { } } -func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed beacon.ExecutableDataV1) error { +func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed beacon.ExecutableData) error { if !eth2types(n.typ) { return errors.New("invalid node type") } diff --git a/miner/worker.go b/miner/worker.go index 22690f4bd19..0cfd62a7ed0 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -975,14 +975,15 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP // generateParams wraps various of settings for generating sealing task. type generateParams struct { - timestamp uint64 // The timstamp for sealing task - forceTime bool // Flag whether the given timestamp is immutable or not - parentHash common.Hash // Parent block hash, empty means the latest chain head - coinbase common.Address // The fee recipient address for including transaction - random common.Hash // The randomness generated by beacon chain, empty before the merge - noUncle bool // Flag whether the uncle block inclusion is allowed - noExtra bool // Flag whether the extra field assignment is allowed - noTxs bool // Flag whether an empty block without any transaction is expected + timestamp uint64 // The timstamp for sealing task + forceTime bool // Flag whether the given timestamp is immutable or not + parentHash common.Hash // Parent block hash, empty means the latest chain head + coinbase common.Address // The fee recipient address for including transaction + random common.Hash // The randomness generated by beacon chain, empty before the merge + withdrawals types.Withdrawals // List of withdrawals to include in block. + noUncle bool // Flag whether the uncle block inclusion is allowed + noExtra bool // Flag whether the extra field assignment is allowed + noTxs bool // Flag whether an empty block without any transaction is expected } // prepareWork constructs the sealing task according to the given parameters, @@ -1116,7 +1117,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout)) } } - block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts) + block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, params.withdrawals) if err != nil { return nil, nil, err } @@ -1201,7 +1202,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti // Create a local environment copy, avoid the data race with snapshot state. // https://github.com/ethereum/go-ethereum/issues/24299 env := env.copy() - block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts) + block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts, nil) if err != nil { return err } @@ -1232,17 +1233,18 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti // getSealingBlock generates the sealing block based on the given parameters. // The generation result will be passed back via the given channel no matter // the generation itself succeeds or not. -func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, noTxs bool) (*types.Block, *big.Int, error) { +func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, withdrawals types.Withdrawals, noTxs bool) (*types.Block, *big.Int, error) { req := &getWorkReq{ params: &generateParams{ - timestamp: timestamp, - forceTime: true, - parentHash: parent, - coinbase: coinbase, - random: random, - noUncle: true, - noExtra: true, - noTxs: noTxs, + timestamp: timestamp, + forceTime: true, + parentHash: parent, + coinbase: coinbase, + random: random, + withdrawals: withdrawals, + noUncle: true, + noExtra: true, + noTxs: noTxs, }, result: make(chan *newPayloadResult, 1), } diff --git a/miner/worker_test.go b/miner/worker_test.go index 08a337a3a94..b66dff4590f 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -678,7 +678,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co // This API should work even when the automatic sealing is not enabled for _, c := range cases { - block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, false) + block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false) if c.expectErr { if err == nil { t.Error("Expect error but get nil") @@ -694,7 +694,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co // This API should work even when the automatic sealing is enabled w.start() for _, c := range cases { - block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, false) + block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false) if c.expectErr { if err == nil { t.Error("Expect error but get nil") diff --git a/tests/init.go b/tests/init.go index ef5ea4bb9a9..5f8196512bc 100644 --- a/tests/init.go +++ b/tests/init.go @@ -230,6 +230,24 @@ var Forks = map[string]*params.ChainConfig{ MergeNetsplitBlock: big.NewInt(0), TerminalTotalDifficulty: big.NewInt(0), }, + "Shanghai": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiBlock: big.NewInt(0), + }, } // AvailableForks returns the set of defined fork names