all: separate catalyst package (#24280)
* all: seperate catalyst package * eth/catalyst: moved some methods, added docs * eth/catalyst, les/catalyst: add method docs * core, eth, les, miner: move common function to beacon package * eth/catalyst: goimported * cmd/utils, miner/stress/beacon: naming nitpicks Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Péter Szilágyi <peterke@gmail.com>
This commit is contained in:
parent
a5c0cfb451
commit
9da25c5db7
|
@ -45,7 +45,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
ethcatalyst "github.com/ethereum/go-ethereum/eth/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
|
@ -56,6 +56,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
|
lescatalyst "github.com/ethereum/go-ethereum/les/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/metrics/exp"
|
"github.com/ethereum/go-ethereum/metrics/exp"
|
||||||
|
@ -1724,7 +1725,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config, isCatalyst bool
|
||||||
}
|
}
|
||||||
stack.RegisterAPIs(tracers.APIs(backend.ApiBackend))
|
stack.RegisterAPIs(tracers.APIs(backend.ApiBackend))
|
||||||
if isCatalyst {
|
if isCatalyst {
|
||||||
if err := catalyst.RegisterLight(stack, backend); err != nil {
|
if err := lescatalyst.Register(stack, backend); err != nil {
|
||||||
Fatalf("Failed to register the catalyst service: %v", err)
|
Fatalf("Failed to register the catalyst service: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1741,7 +1742,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config, isCatalyst bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isCatalyst {
|
if isCatalyst {
|
||||||
if err := catalyst.Register(stack, backend); err != nil {
|
if err := ethcatalyst.Register(stack, backend); err != nil {
|
||||||
Fatalf("Failed to register the catalyst service: %v", err)
|
Fatalf("Failed to register the catalyst service: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2022 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 beacon
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
|
var (
|
||||||
|
VALID = GenericStringResponse{"VALID"}
|
||||||
|
SUCCESS = GenericStringResponse{"SUCCESS"}
|
||||||
|
INVALID = ForkChoiceResponse{Status: "INVALID", PayloadID: nil}
|
||||||
|
SYNCING = ForkChoiceResponse{Status: "SYNCING", PayloadID: nil}
|
||||||
|
GenericServerError = rpc.CustomError{Code: -32000, ValidationError: "Server error"}
|
||||||
|
UnknownPayload = rpc.CustomError{Code: -32001, ValidationError: "Unknown payload"}
|
||||||
|
InvalidTB = rpc.CustomError{Code: -32002, ValidationError: "Invalid terminal block"}
|
||||||
|
)
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
package catalyst
|
package beacon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
package catalyst
|
package beacon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The go-ethereum Authors
|
// Copyright 2022 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package catalyst
|
package beacon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -22,6 +22,8 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"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 PayloadAttributesV1 -field-override payloadAttributesMarshaling -out gen_blockparams.go
|
||||||
|
@ -121,3 +123,82 @@ type ForkchoiceStateV1 struct {
|
||||||
SafeBlockHash common.Hash `json:"safeBlockHash"`
|
SafeBlockHash common.Hash `json:"safeBlockHash"`
|
||||||
FinalizedBlockHash common.Hash `json:"finalizedBlockHash"`
|
FinalizedBlockHash common.Hash `json:"finalizedBlockHash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeTransactions(txs []*types.Transaction) [][]byte {
|
||||||
|
var enc = make([][]byte, len(txs))
|
||||||
|
for i, tx := range txs {
|
||||||
|
enc[i], _ = tx.MarshalBinary()
|
||||||
|
}
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||||
|
var txs = make([]*types.Transaction, len(enc))
|
||||||
|
for i, encTx := range enc {
|
||||||
|
var tx types.Transaction
|
||||||
|
if err := tx.UnmarshalBinary(encTx); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
|
||||||
|
}
|
||||||
|
txs[i] = &tx
|
||||||
|
}
|
||||||
|
return txs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutableDataToBlock constructs a block from executable data.
|
||||||
|
// It verifies that the following fields:
|
||||||
|
// len(extraData) <= 32
|
||||||
|
// uncleHash = emptyUncleHash
|
||||||
|
// difficulty = 0
|
||||||
|
// and that the blockhash of the constructed block matches the parameters.
|
||||||
|
func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
|
||||||
|
txs, err := decodeTransactions(params.Transactions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(params.ExtraData) > 32 {
|
||||||
|
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
|
||||||
|
}
|
||||||
|
header := &types.Header{
|
||||||
|
ParentHash: params.ParentHash,
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
Coinbase: params.FeeRecipient,
|
||||||
|
Root: params.StateRoot,
|
||||||
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
|
ReceiptHash: params.ReceiptsRoot,
|
||||||
|
Bloom: types.BytesToBloom(params.LogsBloom),
|
||||||
|
Difficulty: common.Big0,
|
||||||
|
Number: new(big.Int).SetUint64(params.Number),
|
||||||
|
GasLimit: params.GasLimit,
|
||||||
|
GasUsed: params.GasUsed,
|
||||||
|
Time: params.Timestamp,
|
||||||
|
BaseFee: params.BaseFeePerGas,
|
||||||
|
Extra: params.ExtraData,
|
||||||
|
MixDigest: params.Random,
|
||||||
|
}
|
||||||
|
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
|
||||||
|
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
|
||||||
|
// fields from the given block. It assumes the given block is post-merge block.
|
||||||
|
func BlockToExecutableData(block *types.Block) *ExecutableDataV1 {
|
||||||
|
return &ExecutableDataV1{
|
||||||
|
BlockHash: block.Hash(),
|
||||||
|
ParentHash: block.ParentHash(),
|
||||||
|
FeeRecipient: block.Coinbase(),
|
||||||
|
StateRoot: block.Root(),
|
||||||
|
Number: block.NumberU64(),
|
||||||
|
GasLimit: block.GasLimit(),
|
||||||
|
GasUsed: block.GasUsed(),
|
||||||
|
BaseFeePerGas: block.BaseFee(),
|
||||||
|
Timestamp: block.Time(),
|
||||||
|
ReceiptsRoot: block.ReceiptHash(),
|
||||||
|
LogsBloom: block.Bloom().Bytes(),
|
||||||
|
Transactions: encodeTransactions(block.Transactions()),
|
||||||
|
Random: block.MixDigest(),
|
||||||
|
ExtraData: block.Extra(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,29 +20,15 @@ package catalyst
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/core/beacon"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
VALID = GenericStringResponse{"VALID"}
|
|
||||||
SUCCESS = GenericStringResponse{"SUCCESS"}
|
|
||||||
INVALID = ForkChoiceResponse{Status: "INVALID", PayloadID: nil}
|
|
||||||
SYNCING = ForkChoiceResponse{Status: "SYNCING", PayloadID: nil}
|
|
||||||
GenericServerError = rpc.CustomError{Code: -32000, ValidationError: "Server error"}
|
|
||||||
UnknownPayload = rpc.CustomError{Code: -32001, ValidationError: "Unknown payload"}
|
|
||||||
InvalidTB = rpc.CustomError{Code: -32002, ValidationError: "Invalid terminal block"}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register adds catalyst APIs to the full node.
|
// Register adds catalyst APIs to the full node.
|
||||||
|
@ -52,21 +38,7 @@ func Register(stack *node.Node, backend *eth.Ethereum) error {
|
||||||
{
|
{
|
||||||
Namespace: "engine",
|
Namespace: "engine",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: NewConsensusAPI(backend, nil),
|
Service: NewConsensusAPI(backend),
|
||||||
Public: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterLight adds catalyst APIs to the light client.
|
|
||||||
func RegisterLight(stack *node.Node, backend *les.LightEthereum) error {
|
|
||||||
log.Warn("Catalyst mode enabled", "protocol", "les")
|
|
||||||
stack.RegisterAPIs([]rpc.API{
|
|
||||||
{
|
|
||||||
Namespace: "engine",
|
|
||||||
Version: "1.0",
|
|
||||||
Service: NewConsensusAPI(nil, backend),
|
|
||||||
Public: true,
|
Public: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -74,143 +46,86 @@ func RegisterLight(stack *node.Node, backend *les.LightEthereum) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConsensusAPI struct {
|
type ConsensusAPI struct {
|
||||||
light bool
|
|
||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
les *les.LightEthereum
|
|
||||||
preparedBlocks *payloadQueue // preparedBlocks caches payloads (*ExecutableDataV1) by payload ID (PayloadID)
|
preparedBlocks *payloadQueue // preparedBlocks caches payloads (*ExecutableDataV1) by payload ID (PayloadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI {
|
// NewConsensusAPI creates a new consensus api for the given backend.
|
||||||
if eth == nil {
|
// The underlying blockchain needs to have a valid terminal total difficulty set.
|
||||||
if les.BlockChain().Config().TerminalTotalDifficulty == nil {
|
func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
|
||||||
panic("Catalyst started without valid total difficulty")
|
if eth.BlockChain().Config().TerminalTotalDifficulty == nil {
|
||||||
}
|
panic("Catalyst started without valid total difficulty")
|
||||||
} else {
|
|
||||||
if eth.BlockChain().Config().TerminalTotalDifficulty == nil {
|
|
||||||
panic("Catalyst started without valid total difficulty")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ConsensusAPI{
|
return &ConsensusAPI{
|
||||||
light: eth == nil,
|
|
||||||
eth: eth,
|
eth: eth,
|
||||||
les: les,
|
|
||||||
preparedBlocks: newPayloadQueue(),
|
preparedBlocks: newPayloadQueue(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ConsensusAPI) GetPayloadV1(payloadID PayloadID) (*ExecutableDataV1, error) {
|
// ForkchoiceUpdatedV1 has several responsibilities:
|
||||||
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
// If the method is called with an empty head block:
|
||||||
data := api.preparedBlocks.get(payloadID)
|
// we return success, which can be used to check if the catalyst mode is enabled
|
||||||
if data == nil {
|
// If the total difficulty was not reached:
|
||||||
return nil, &UnknownPayload
|
// we return INVALID
|
||||||
}
|
// If the finalizedBlockHash is set:
|
||||||
return data, nil
|
// we check if we have the finalizedBlockHash in our db, if not we start a sync
|
||||||
}
|
// We try to set our blockchain to the headBlock
|
||||||
|
// If there are payloadAttributes:
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads ForkchoiceStateV1, payloadAttributes *PayloadAttributesV1) (ForkChoiceResponse, error) {
|
// we try to assemble a block with the payloadAttributes and return its payloadID
|
||||||
|
func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
|
||||||
log.Trace("Engine API request received", "method", "ForkChoiceUpdated", "head", heads.HeadBlockHash, "finalized", heads.FinalizedBlockHash, "safe", heads.SafeBlockHash)
|
log.Trace("Engine API request received", "method", "ForkChoiceUpdated", "head", heads.HeadBlockHash, "finalized", heads.FinalizedBlockHash, "safe", heads.SafeBlockHash)
|
||||||
if heads.HeadBlockHash == (common.Hash{}) {
|
if heads.HeadBlockHash == (common.Hash{}) {
|
||||||
return ForkChoiceResponse{Status: SUCCESS.Status, PayloadID: nil}, nil
|
return beacon.ForkChoiceResponse{Status: beacon.SUCCESS.Status, PayloadID: nil}, nil
|
||||||
}
|
}
|
||||||
if err := api.checkTerminalTotalDifficulty(heads.HeadBlockHash); err != nil {
|
if err := api.checkTerminalTotalDifficulty(heads.HeadBlockHash); err != nil {
|
||||||
if api.light {
|
if block := api.eth.BlockChain().GetBlockByHash(heads.HeadBlockHash); block == nil {
|
||||||
if header := api.les.BlockChain().GetHeaderByHash(heads.HeadBlockHash); header == nil {
|
// TODO (MariusVanDerWijden) trigger sync
|
||||||
// TODO (MariusVanDerWijden) trigger sync
|
return beacon.SYNCING, nil
|
||||||
return SYNCING, nil
|
|
||||||
}
|
|
||||||
return INVALID, err
|
|
||||||
} else {
|
|
||||||
if block := api.eth.BlockChain().GetBlockByHash(heads.HeadBlockHash); block == nil {
|
|
||||||
// TODO (MariusVanDerWijden) trigger sync
|
|
||||||
return SYNCING, nil
|
|
||||||
}
|
|
||||||
return INVALID, err
|
|
||||||
}
|
}
|
||||||
|
return beacon.INVALID, err
|
||||||
}
|
}
|
||||||
// If the finalized block is set, check if it is in our blockchain
|
// If the finalized block is set, check if it is in our blockchain
|
||||||
if heads.FinalizedBlockHash != (common.Hash{}) {
|
if heads.FinalizedBlockHash != (common.Hash{}) {
|
||||||
if api.light {
|
if block := api.eth.BlockChain().GetBlockByHash(heads.FinalizedBlockHash); block == nil {
|
||||||
if header := api.les.BlockChain().GetHeaderByHash(heads.FinalizedBlockHash); header == nil {
|
// TODO (MariusVanDerWijden) trigger sync
|
||||||
// TODO (MariusVanDerWijden) trigger sync
|
return beacon.SYNCING, nil
|
||||||
return SYNCING, nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if block := api.eth.BlockChain().GetBlockByHash(heads.FinalizedBlockHash); block == nil {
|
|
||||||
// TODO (MariusVanDerWijden) trigger sync
|
|
||||||
return SYNCING, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SetHead
|
// SetHead
|
||||||
if err := api.setHead(heads.HeadBlockHash); err != nil {
|
if err := api.setHead(heads.HeadBlockHash); err != nil {
|
||||||
return INVALID, err
|
return beacon.INVALID, err
|
||||||
}
|
}
|
||||||
// Assemble block (if needed). It only works for full node.
|
// Assemble block (if needed). It only works for full node.
|
||||||
if !api.light && payloadAttributes != nil {
|
if payloadAttributes != nil {
|
||||||
data, err := api.assembleBlock(heads.HeadBlockHash, payloadAttributes)
|
data, err := api.assembleBlock(heads.HeadBlockHash, payloadAttributes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return INVALID, err
|
return beacon.INVALID, err
|
||||||
}
|
}
|
||||||
id := computePayloadId(heads.HeadBlockHash, payloadAttributes)
|
id := computePayloadId(heads.HeadBlockHash, payloadAttributes)
|
||||||
api.preparedBlocks.put(id, data)
|
api.preparedBlocks.put(id, data)
|
||||||
log.Info("Created payload", "payloadID", id)
|
log.Info("Created payload", "payloadID", id)
|
||||||
return ForkChoiceResponse{Status: SUCCESS.Status, PayloadID: &id}, nil
|
return beacon.ForkChoiceResponse{Status: beacon.SUCCESS.Status, PayloadID: &id}, nil
|
||||||
}
|
}
|
||||||
return ForkChoiceResponse{Status: SUCCESS.Status, PayloadID: nil}, nil
|
return beacon.ForkChoiceResponse{Status: beacon.SUCCESS.Status, PayloadID: nil}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func computePayloadId(headBlockHash common.Hash, params *PayloadAttributesV1) PayloadID {
|
// GetPayloadV1 returns a cached payload by id.
|
||||||
// Hash
|
func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) {
|
||||||
hasher := sha256.New()
|
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
||||||
hasher.Write(headBlockHash[:])
|
data := api.preparedBlocks.get(payloadID)
|
||||||
binary.Write(hasher, binary.BigEndian, params.Timestamp)
|
if data == nil {
|
||||||
hasher.Write(params.Random[:])
|
return nil, &beacon.UnknownPayload
|
||||||
hasher.Write(params.SuggestedFeeRecipient[:])
|
|
||||||
var out PayloadID
|
|
||||||
copy(out[:], hasher.Sum(nil)[:8])
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ConsensusAPI) invalid() ExecutePayloadResponse {
|
|
||||||
if api.light {
|
|
||||||
return ExecutePayloadResponse{Status: INVALID.Status, LatestValidHash: api.les.BlockChain().CurrentHeader().Hash()}
|
|
||||||
}
|
}
|
||||||
return ExecutePayloadResponse{Status: INVALID.Status, LatestValidHash: api.eth.BlockChain().CurrentHeader().Hash()}
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutePayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// ExecutePayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) ExecutePayloadV1(params ExecutableDataV1) (ExecutePayloadResponse, error) {
|
func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beacon.ExecutePayloadResponse, error) {
|
||||||
log.Trace("Engine API request received", "method", "ExecutePayload", params.BlockHash, "number", params.Number)
|
log.Trace("Engine API request received", "method", "ExecutePayload", params.BlockHash, "number", params.Number)
|
||||||
block, err := ExecutableDataToBlock(params)
|
block, err := beacon.ExecutableDataToBlock(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.invalid(), err
|
return api.invalid(), err
|
||||||
}
|
}
|
||||||
if api.light {
|
|
||||||
if !api.les.BlockChain().HasHeader(block.ParentHash(), block.NumberU64()-1) {
|
|
||||||
/*
|
|
||||||
TODO (MariusVanDerWijden) reenable once sync is merged
|
|
||||||
if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), block.Header()); err != nil {
|
|
||||||
return SYNCING, err
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// TODO (MariusVanDerWijden) we should return nil here not empty hash
|
|
||||||
return ExecutePayloadResponse{Status: SYNCING.Status, LatestValidHash: common.Hash{}}, nil
|
|
||||||
}
|
|
||||||
parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash)
|
|
||||||
td := api.les.BlockChain().GetTd(parent.Hash(), block.NumberU64()-1)
|
|
||||||
ttd := api.les.BlockChain().Config().TerminalTotalDifficulty
|
|
||||||
if td.Cmp(ttd) < 0 {
|
|
||||||
return api.invalid(), fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd)
|
|
||||||
}
|
|
||||||
if err = api.les.BlockChain().InsertHeader(block.Header()); err != nil {
|
|
||||||
return api.invalid(), err
|
|
||||||
}
|
|
||||||
if merger := api.merger(); !merger.TDDReached() {
|
|
||||||
merger.ReachTTD()
|
|
||||||
}
|
|
||||||
return ExecutePayloadResponse{Status: VALID.Status, LatestValidHash: block.Hash()}, nil
|
|
||||||
}
|
|
||||||
if !api.eth.BlockChain().HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
if !api.eth.BlockChain().HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
||||||
/*
|
/*
|
||||||
TODO (MariusVanDerWijden) reenable once sync is merged
|
TODO (MariusVanDerWijden) reenable once sync is merged
|
||||||
|
@ -219,7 +134,7 @@ func (api *ConsensusAPI) ExecutePayloadV1(params ExecutableDataV1) (ExecutePaylo
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// TODO (MariusVanDerWijden) we should return nil here not empty hash
|
// TODO (MariusVanDerWijden) we should return nil here not empty hash
|
||||||
return ExecutePayloadResponse{Status: SYNCING.Status, LatestValidHash: common.Hash{}}, nil
|
return beacon.ExecutePayloadResponse{Status: beacon.SYNCING.Status, LatestValidHash: common.Hash{}}, nil
|
||||||
}
|
}
|
||||||
parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash)
|
parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash)
|
||||||
td := api.eth.BlockChain().GetTd(parent.Hash(), block.NumberU64()-1)
|
td := api.eth.BlockChain().GetTd(parent.Hash(), block.NumberU64()-1)
|
||||||
|
@ -232,97 +147,39 @@ func (api *ConsensusAPI) ExecutePayloadV1(params ExecutableDataV1) (ExecutePaylo
|
||||||
return api.invalid(), err
|
return api.invalid(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if merger := api.merger(); !merger.TDDReached() {
|
if merger := api.eth.Merger(); !merger.TDDReached() {
|
||||||
merger.ReachTTD()
|
merger.ReachTTD()
|
||||||
}
|
}
|
||||||
return ExecutePayloadResponse{Status: VALID.Status, LatestValidHash: block.Hash()}, nil
|
return beacon.ExecutePayloadResponse{Status: beacon.VALID.Status, LatestValidHash: block.Hash()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssembleBlock creates a new block, inserts it into the chain, and returns the "execution
|
// computePayloadId computes a pseudo-random payloadid, based on the parameters.
|
||||||
// data" required for eth2 clients to process the new block.
|
func computePayloadId(headBlockHash common.Hash, params *beacon.PayloadAttributesV1) beacon.PayloadID {
|
||||||
func (api *ConsensusAPI) assembleBlock(parentHash common.Hash, params *PayloadAttributesV1) (*ExecutableDataV1, error) {
|
// Hash
|
||||||
if api.light {
|
hasher := sha256.New()
|
||||||
return nil, errors.New("not supported")
|
hasher.Write(headBlockHash[:])
|
||||||
}
|
binary.Write(hasher, binary.BigEndian, params.Timestamp)
|
||||||
|
hasher.Write(params.Random[:])
|
||||||
|
hasher.Write(params.SuggestedFeeRecipient[:])
|
||||||
|
var out beacon.PayloadID
|
||||||
|
copy(out[:], hasher.Sum(nil)[:8])
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid returns a response "INVALID" with the latest valid hash set to the current head.
|
||||||
|
func (api *ConsensusAPI) invalid() beacon.ExecutePayloadResponse {
|
||||||
|
return beacon.ExecutePayloadResponse{Status: beacon.INVALID.Status, LatestValidHash: api.eth.BlockChain().CurrentHeader().Hash()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assembleBlock creates a new block and returns the "execution
|
||||||
|
// data" required for beacon clients to process the new block.
|
||||||
|
func (api *ConsensusAPI) assembleBlock(parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) {
|
||||||
log.Info("Producing block", "parentHash", parentHash)
|
log.Info("Producing block", "parentHash", parentHash)
|
||||||
block, err := api.eth.Miner().GetSealingBlock(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random)
|
block, err := api.eth.Miner().GetSealingBlock(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return BlockToExecutableData(block), nil
|
return beacon.BlockToExecutableData(block), nil
|
||||||
}
|
|
||||||
|
|
||||||
func encodeTransactions(txs []*types.Transaction) [][]byte {
|
|
||||||
var enc = make([][]byte, len(txs))
|
|
||||||
for i, tx := range txs {
|
|
||||||
enc[i], _ = tx.MarshalBinary()
|
|
||||||
}
|
|
||||||
return enc
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
|
||||||
var txs = make([]*types.Transaction, len(enc))
|
|
||||||
for i, encTx := range enc {
|
|
||||||
var tx types.Transaction
|
|
||||||
if err := tx.UnmarshalBinary(encTx); err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
|
|
||||||
}
|
|
||||||
txs[i] = &tx
|
|
||||||
}
|
|
||||||
return txs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
|
|
||||||
txs, err := decodeTransactions(params.Transactions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(params.ExtraData) > 32 {
|
|
||||||
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
|
|
||||||
}
|
|
||||||
header := &types.Header{
|
|
||||||
ParentHash: params.ParentHash,
|
|
||||||
UncleHash: types.EmptyUncleHash,
|
|
||||||
Coinbase: params.FeeRecipient,
|
|
||||||
Root: params.StateRoot,
|
|
||||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
|
||||||
ReceiptHash: params.ReceiptsRoot,
|
|
||||||
Bloom: types.BytesToBloom(params.LogsBloom),
|
|
||||||
Difficulty: common.Big0,
|
|
||||||
Number: new(big.Int).SetUint64(params.Number),
|
|
||||||
GasLimit: params.GasLimit,
|
|
||||||
GasUsed: params.GasUsed,
|
|
||||||
Time: params.Timestamp,
|
|
||||||
BaseFee: params.BaseFeePerGas,
|
|
||||||
Extra: params.ExtraData,
|
|
||||||
MixDigest: params.Random,
|
|
||||||
}
|
|
||||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
|
|
||||||
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
|
|
||||||
// fields from the given block. It assumes the given block is post-merge block.
|
|
||||||
func BlockToExecutableData(block *types.Block) *ExecutableDataV1 {
|
|
||||||
return &ExecutableDataV1{
|
|
||||||
BlockHash: block.Hash(),
|
|
||||||
ParentHash: block.ParentHash(),
|
|
||||||
FeeRecipient: block.Coinbase(),
|
|
||||||
StateRoot: block.Root(),
|
|
||||||
Number: block.NumberU64(),
|
|
||||||
GasLimit: block.GasLimit(),
|
|
||||||
GasUsed: block.GasUsed(),
|
|
||||||
BaseFeePerGas: block.BaseFee(),
|
|
||||||
Timestamp: block.Time(),
|
|
||||||
ReceiptsRoot: block.ReceiptHash(),
|
|
||||||
LogsBloom: block.Bloom().Bytes(),
|
|
||||||
Transactions: encodeTransactions(block.Transactions()),
|
|
||||||
Random: block.MixDigest(),
|
|
||||||
ExtraData: block.Extra(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used in tests to add a the list of transactions from a block to the tx pool.
|
// Used in tests to add a the list of transactions from a block to the tx pool.
|
||||||
|
@ -335,29 +192,17 @@ func (api *ConsensusAPI) insertTransactions(txs types.Transactions) error {
|
||||||
|
|
||||||
func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
|
func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
|
||||||
// shortcut if we entered PoS already
|
// shortcut if we entered PoS already
|
||||||
if api.merger().PoSFinalized() {
|
if api.eth.Merger().PoSFinalized() {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if api.light {
|
|
||||||
// make sure the parent has enough terminal total difficulty
|
|
||||||
header := api.les.BlockChain().GetHeaderByHash(head)
|
|
||||||
if header == nil {
|
|
||||||
return &GenericServerError
|
|
||||||
}
|
|
||||||
td := api.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
|
|
||||||
if td != nil && td.Cmp(api.les.BlockChain().Config().TerminalTotalDifficulty) < 0 {
|
|
||||||
return &InvalidTB
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// make sure the parent has enough terminal total difficulty
|
// make sure the parent has enough terminal total difficulty
|
||||||
newHeadBlock := api.eth.BlockChain().GetBlockByHash(head)
|
newHeadBlock := api.eth.BlockChain().GetBlockByHash(head)
|
||||||
if newHeadBlock == nil {
|
if newHeadBlock == nil {
|
||||||
return &GenericServerError
|
return &beacon.GenericServerError
|
||||||
}
|
}
|
||||||
td := api.eth.BlockChain().GetTd(newHeadBlock.Hash(), newHeadBlock.NumberU64())
|
td := api.eth.BlockChain().GetTd(newHeadBlock.Hash(), newHeadBlock.NumberU64())
|
||||||
if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) < 0 {
|
if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) < 0 {
|
||||||
return &InvalidTB
|
return &beacon.InvalidTB
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -365,48 +210,22 @@ func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
|
||||||
// setHead is called to perform a force choice.
|
// setHead is called to perform a force choice.
|
||||||
func (api *ConsensusAPI) setHead(newHead common.Hash) error {
|
func (api *ConsensusAPI) setHead(newHead common.Hash) error {
|
||||||
log.Info("Setting head", "head", newHead)
|
log.Info("Setting head", "head", newHead)
|
||||||
if api.light {
|
|
||||||
headHeader := api.les.BlockChain().CurrentHeader()
|
|
||||||
if headHeader.Hash() == newHead {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
newHeadHeader := api.les.BlockChain().GetHeaderByHash(newHead)
|
|
||||||
if newHeadHeader == nil {
|
|
||||||
return &GenericServerError
|
|
||||||
}
|
|
||||||
if err := api.les.BlockChain().SetChainHead(newHeadHeader); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Trigger the transition if it's the first `NewHead` event.
|
|
||||||
if merger := api.merger(); !merger.PoSFinalized() {
|
|
||||||
merger.FinalizePoS()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
headBlock := api.eth.BlockChain().CurrentBlock()
|
headBlock := api.eth.BlockChain().CurrentBlock()
|
||||||
if headBlock.Hash() == newHead {
|
if headBlock.Hash() == newHead {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
newHeadBlock := api.eth.BlockChain().GetBlockByHash(newHead)
|
newHeadBlock := api.eth.BlockChain().GetBlockByHash(newHead)
|
||||||
if newHeadBlock == nil {
|
if newHeadBlock == nil {
|
||||||
return &GenericServerError
|
return &beacon.GenericServerError
|
||||||
}
|
}
|
||||||
if err := api.eth.BlockChain().SetChainHead(newHeadBlock); err != nil {
|
if err := api.eth.BlockChain().SetChainHead(newHeadBlock); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Trigger the transition if it's the first `NewHead` event.
|
// Trigger the transition if it's the first `NewHead` event.
|
||||||
if merger := api.merger(); !merger.PoSFinalized() {
|
if merger := api.eth.Merger(); !merger.PoSFinalized() {
|
||||||
merger.FinalizePoS()
|
merger.FinalizePoS()
|
||||||
}
|
}
|
||||||
// TODO (MariusVanDerWijden) are we really synced now?
|
// TODO (MariusVanDerWijden) are we really synced now?
|
||||||
api.eth.SetSynced()
|
api.eth.SetSynced()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function, return the merger instance.
|
|
||||||
func (api *ConsensusAPI) merger() *consensus.Merger {
|
|
||||||
if api.light {
|
|
||||||
return api.les.Merger()
|
|
||||||
}
|
|
||||||
return api.eth.Merger()
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/beacon"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
@ -78,14 +79,14 @@ func TestEth2AssembleBlock(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice, nil)
|
api := NewConsensusAPI(ethservice)
|
||||||
signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
|
signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
|
||||||
tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error signing transaction, err=%v", err)
|
t.Fatalf("error signing transaction, err=%v", err)
|
||||||
}
|
}
|
||||||
ethservice.TxPool().AddLocal(tx)
|
ethservice.TxPool().AddLocal(tx)
|
||||||
blockParams := PayloadAttributesV1{
|
blockParams := beacon.PayloadAttributesV1{
|
||||||
Timestamp: blocks[9].Time() + 5,
|
Timestamp: blocks[9].Time() + 5,
|
||||||
}
|
}
|
||||||
execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams)
|
execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams)
|
||||||
|
@ -102,11 +103,11 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice, nil)
|
api := NewConsensusAPI(ethservice)
|
||||||
|
|
||||||
// Put the 10th block's tx in the pool and produce a new block
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
api.insertTransactions(blocks[9].Transactions())
|
api.insertTransactions(blocks[9].Transactions())
|
||||||
blockParams := PayloadAttributesV1{
|
blockParams := beacon.PayloadAttributesV1{
|
||||||
Timestamp: blocks[8].Time() + 5,
|
Timestamp: blocks[8].Time() + 5,
|
||||||
}
|
}
|
||||||
execData, err := api.assembleBlock(blocks[8].Hash(), &blockParams)
|
execData, err := api.assembleBlock(blocks[8].Hash(), &blockParams)
|
||||||
|
@ -123,8 +124,8 @@ func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice, nil)
|
api := NewConsensusAPI(ethservice)
|
||||||
fcState := ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: blocks[5].Hash(),
|
HeadBlockHash: blocks[5].Hash(),
|
||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: common.Hash{},
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
@ -141,14 +142,14 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice, nil)
|
api := NewConsensusAPI(ethservice)
|
||||||
|
|
||||||
// Put the 10th block's tx in the pool and produce a new block
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
api.insertTransactions(blocks[9].Transactions())
|
api.insertTransactions(blocks[9].Transactions())
|
||||||
blockParams := PayloadAttributesV1{
|
blockParams := beacon.PayloadAttributesV1{
|
||||||
Timestamp: blocks[8].Time() + 5,
|
Timestamp: blocks[8].Time() + 5,
|
||||||
}
|
}
|
||||||
fcState := ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: blocks[8].Hash(),
|
HeadBlockHash: blocks[8].Hash(),
|
||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: common.Hash{},
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
@ -166,7 +167,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
||||||
t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
|
t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
|
||||||
}
|
}
|
||||||
// Test invalid payloadID
|
// Test invalid payloadID
|
||||||
var invPayload PayloadID
|
var invPayload beacon.PayloadID
|
||||||
copy(invPayload[:], payloadID[:])
|
copy(invPayload[:], payloadID[:])
|
||||||
invPayload[0] = ^invPayload[0]
|
invPayload[0] = ^invPayload[0]
|
||||||
_, err = api.GetPayloadV1(invPayload)
|
_, err = api.GetPayloadV1(invPayload)
|
||||||
|
@ -199,7 +200,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
||||||
ethservice.Merger().ReachTTD()
|
ethservice.Merger().ReachTTD()
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice, nil)
|
api = NewConsensusAPI(ethservice)
|
||||||
parent = ethservice.BlockChain().CurrentBlock()
|
parent = ethservice.BlockChain().CurrentBlock()
|
||||||
)
|
)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -215,12 +216,12 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
|
||||||
params := PayloadAttributesV1{
|
params := beacon.PayloadAttributesV1{
|
||||||
Timestamp: test.time,
|
Timestamp: test.time,
|
||||||
Random: crypto.Keccak256Hash([]byte{byte(123)}),
|
Random: crypto.Keccak256Hash([]byte{byte(123)}),
|
||||||
SuggestedFeeRecipient: parent.Coinbase(),
|
SuggestedFeeRecipient: parent.Coinbase(),
|
||||||
}
|
}
|
||||||
fcState := ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: parent.Hash(),
|
HeadBlockHash: parent.Hash(),
|
||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: common.Hash{},
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
@ -242,7 +243,7 @@ func TestEth2NewBlock(t *testing.T) {
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice, nil)
|
api = NewConsensusAPI(ethservice)
|
||||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||||
|
|
||||||
// This EVM code generates a log when the contract is created.
|
// This EVM code generates a log when the contract is created.
|
||||||
|
@ -260,13 +261,13 @@ 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)
|
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)
|
ethservice.TxPool().AddLocal(tx)
|
||||||
|
|
||||||
execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{
|
execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{
|
||||||
Timestamp: parent.Time() + 5,
|
Timestamp: parent.Time() + 5,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create the executable data %v", err)
|
t.Fatalf("Failed to create the executable data %v", err)
|
||||||
}
|
}
|
||||||
block, err := ExecutableDataToBlock(*execData)
|
block, err := beacon.ExecutableDataToBlock(*execData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||||
}
|
}
|
||||||
|
@ -278,7 +279,7 @@ func TestEth2NewBlock(t *testing.T) {
|
||||||
t.Fatalf("Chain head shouldn't be updated")
|
t.Fatalf("Chain head shouldn't be updated")
|
||||||
}
|
}
|
||||||
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
||||||
fcState := ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: block.Hash(),
|
HeadBlockHash: block.Hash(),
|
||||||
SafeBlockHash: block.Hash(),
|
SafeBlockHash: block.Hash(),
|
||||||
FinalizedBlockHash: block.Hash(),
|
FinalizedBlockHash: block.Hash(),
|
||||||
|
@ -300,13 +301,13 @@ func TestEth2NewBlock(t *testing.T) {
|
||||||
)
|
)
|
||||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{
|
execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{
|
||||||
Timestamp: parent.Time() + 6,
|
Timestamp: parent.Time() + 6,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create the executable data %v", err)
|
t.Fatalf("Failed to create the executable data %v", err)
|
||||||
}
|
}
|
||||||
block, err := ExecutableDataToBlock(*execData)
|
block, err := beacon.ExecutableDataToBlock(*execData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||||
}
|
}
|
||||||
|
@ -318,7 +319,7 @@ func TestEth2NewBlock(t *testing.T) {
|
||||||
t.Fatalf("Chain head shouldn't be updated")
|
t.Fatalf("Chain head shouldn't be updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
fcState := ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: block.Hash(),
|
HeadBlockHash: block.Hash(),
|
||||||
SafeBlockHash: block.Hash(),
|
SafeBlockHash: block.Hash(),
|
||||||
FinalizedBlockHash: block.Hash(),
|
FinalizedBlockHash: block.Hash(),
|
||||||
|
@ -412,7 +413,7 @@ func TestFullAPI(t *testing.T) {
|
||||||
ethservice.Merger().ReachTTD()
|
ethservice.Merger().ReachTTD()
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice, nil)
|
api = NewConsensusAPI(ethservice)
|
||||||
parent = ethservice.BlockChain().CurrentBlock()
|
parent = ethservice.BlockChain().CurrentBlock()
|
||||||
// This EVM code generates a log when the contract is created.
|
// This EVM code generates a log when the contract is created.
|
||||||
logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||||
|
@ -423,12 +424,12 @@ func TestFullAPI(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)
|
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)
|
ethservice.TxPool().AddLocal(tx)
|
||||||
|
|
||||||
params := PayloadAttributesV1{
|
params := beacon.PayloadAttributesV1{
|
||||||
Timestamp: parent.Time() + 1,
|
Timestamp: parent.Time() + 1,
|
||||||
Random: crypto.Keccak256Hash([]byte{byte(i)}),
|
Random: crypto.Keccak256Hash([]byte{byte(i)}),
|
||||||
SuggestedFeeRecipient: parent.Coinbase(),
|
SuggestedFeeRecipient: parent.Coinbase(),
|
||||||
}
|
}
|
||||||
fcState := ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: parent.Hash(),
|
HeadBlockHash: parent.Hash(),
|
||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: common.Hash{},
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
@ -437,7 +438,7 @@ func TestFullAPI(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error preparing payload, err=%v", err)
|
t.Fatalf("error preparing payload, err=%v", err)
|
||||||
}
|
}
|
||||||
if resp.Status != SUCCESS.Status {
|
if resp.Status != beacon.SUCCESS.Status {
|
||||||
t.Fatalf("error preparing payload, invalid status: %v", resp.Status)
|
t.Fatalf("error preparing payload, invalid status: %v", resp.Status)
|
||||||
}
|
}
|
||||||
payloadID := computePayloadId(parent.Hash(), ¶ms)
|
payloadID := computePayloadId(parent.Hash(), ¶ms)
|
||||||
|
@ -449,10 +450,10 @@ func TestFullAPI(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("can't execute payload: %v", err)
|
t.Fatalf("can't execute payload: %v", err)
|
||||||
}
|
}
|
||||||
if execResp.Status != VALID.Status {
|
if execResp.Status != beacon.VALID.Status {
|
||||||
t.Fatalf("invalid status: %v", execResp.Status)
|
t.Fatalf("invalid status: %v", execResp.Status)
|
||||||
}
|
}
|
||||||
fcState = ForkchoiceStateV1{
|
fcState = beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: payload.BlockHash,
|
HeadBlockHash: payload.BlockHash,
|
||||||
SafeBlockHash: payload.ParentHash,
|
SafeBlockHash: payload.ParentHash,
|
||||||
FinalizedBlockHash: payload.ParentHash,
|
FinalizedBlockHash: payload.ParentHash,
|
||||||
|
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package catalyst
|
package catalyst
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/beacon"
|
||||||
|
)
|
||||||
|
|
||||||
// maxTrackedPayloads is the maximum number of prepared payloads the execution
|
// maxTrackedPayloads is the maximum number of prepared payloads the execution
|
||||||
// engine tracks before evicting old ones. Ideally we should only ever track the
|
// engine tracks before evicting old ones. Ideally we should only ever track the
|
||||||
|
@ -26,8 +30,8 @@ const maxTrackedPayloads = 10
|
||||||
// payloadQueueItem represents an id->payload tuple to store until it's retrieved
|
// payloadQueueItem represents an id->payload tuple to store until it's retrieved
|
||||||
// or evicted.
|
// or evicted.
|
||||||
type payloadQueueItem struct {
|
type payloadQueueItem struct {
|
||||||
id PayloadID
|
id beacon.PayloadID
|
||||||
payload *ExecutableDataV1
|
payload *beacon.ExecutableDataV1
|
||||||
}
|
}
|
||||||
|
|
||||||
// payloadQueue tracks the latest handful of constructed payloads to be retrieved
|
// payloadQueue tracks the latest handful of constructed payloads to be retrieved
|
||||||
|
@ -46,7 +50,7 @@ func newPayloadQueue() *payloadQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
// put inserts a new payload into the queue at the given id.
|
// put inserts a new payload into the queue at the given id.
|
||||||
func (q *payloadQueue) put(id PayloadID, data *ExecutableDataV1) {
|
func (q *payloadQueue) put(id beacon.PayloadID, data *beacon.ExecutableDataV1) {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
|
@ -58,7 +62,7 @@ func (q *payloadQueue) put(id PayloadID, data *ExecutableDataV1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get retrieves a previously stored payload item or nil if it does not exist.
|
// get retrieves a previously stored payload item or nil if it does not exist.
|
||||||
func (q *payloadQueue) get(id PayloadID) *ExecutableDataV1 {
|
func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 {
|
||||||
q.lock.RLock()
|
q.lock.RLock()
|
||||||
defer q.lock.RUnlock()
|
defer q.lock.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
// Copyright 2022 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 catalyst implements the temporary eth1/eth2 RPC integration.
|
||||||
|
package catalyst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/beacon"
|
||||||
|
"github.com/ethereum/go-ethereum/les"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register adds catalyst APIs to the light client.
|
||||||
|
func Register(stack *node.Node, backend *les.LightEthereum) error {
|
||||||
|
log.Warn("Catalyst mode enabled", "protocol", "les")
|
||||||
|
stack.RegisterAPIs([]rpc.API{
|
||||||
|
{
|
||||||
|
Namespace: "engine",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: NewConsensusAPI(backend),
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConsensusAPI struct {
|
||||||
|
les *les.LightEthereum
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConsensusAPI creates a new consensus api for the given backend.
|
||||||
|
// The underlying blockchain needs to have a valid terminal total difficulty set.
|
||||||
|
func NewConsensusAPI(les *les.LightEthereum) *ConsensusAPI {
|
||||||
|
if les.BlockChain().Config().TerminalTotalDifficulty == nil {
|
||||||
|
panic("Catalyst started without valid total difficulty")
|
||||||
|
}
|
||||||
|
return &ConsensusAPI{les: les}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForkchoiceUpdatedV1 has several responsibilities:
|
||||||
|
// If the method is called with an empty head block:
|
||||||
|
// we return success, which can be used to check if the catalyst mode is enabled
|
||||||
|
// If the total difficulty was not reached:
|
||||||
|
// we return INVALID
|
||||||
|
// If the finalizedBlockHash is set:
|
||||||
|
// we check if we have the finalizedBlockHash in our db, if not we start a sync
|
||||||
|
// We try to set our blockchain to the headBlock
|
||||||
|
// 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) {
|
||||||
|
if heads.HeadBlockHash == (common.Hash{}) {
|
||||||
|
return beacon.ForkChoiceResponse{Status: beacon.SUCCESS.Status, PayloadID: nil}, nil
|
||||||
|
}
|
||||||
|
if err := api.checkTerminalTotalDifficulty(heads.HeadBlockHash); err != nil {
|
||||||
|
if header := api.les.BlockChain().GetHeaderByHash(heads.HeadBlockHash); header == nil {
|
||||||
|
// TODO (MariusVanDerWijden) trigger sync
|
||||||
|
return beacon.SYNCING, nil
|
||||||
|
}
|
||||||
|
return beacon.INVALID, err
|
||||||
|
}
|
||||||
|
// If the finalized block is set, check if it is in our blockchain
|
||||||
|
if heads.FinalizedBlockHash != (common.Hash{}) {
|
||||||
|
if header := api.les.BlockChain().GetHeaderByHash(heads.FinalizedBlockHash); header == nil {
|
||||||
|
// TODO (MariusVanDerWijden) trigger sync
|
||||||
|
return beacon.SYNCING, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SetHead
|
||||||
|
if err := api.setHead(heads.HeadBlockHash); err != nil {
|
||||||
|
return beacon.INVALID, err
|
||||||
|
}
|
||||||
|
if payloadAttributes != nil {
|
||||||
|
return beacon.INVALID, errors.New("not supported")
|
||||||
|
}
|
||||||
|
return beacon.ForkChoiceResponse{Status: beacon.SUCCESS.Status, PayloadID: nil}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayloadV1 returns a cached payload by id. It's not supported in les mode.
|
||||||
|
func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) {
|
||||||
|
return nil, &beacon.GenericServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.ExecutePayloadResponse, error) {
|
||||||
|
block, err := beacon.ExecutableDataToBlock(params)
|
||||||
|
if err != nil {
|
||||||
|
return api.invalid(), err
|
||||||
|
}
|
||||||
|
if !api.les.BlockChain().HasHeader(block.ParentHash(), block.NumberU64()-1) {
|
||||||
|
/*
|
||||||
|
TODO (MariusVanDerWijden) reenable once sync is merged
|
||||||
|
if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), block.Header()); err != nil {
|
||||||
|
return SYNCING, err
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// TODO (MariusVanDerWijden) we should return nil here not empty hash
|
||||||
|
return beacon.ExecutePayloadResponse{Status: beacon.SYNCING.Status, LatestValidHash: common.Hash{}}, nil
|
||||||
|
}
|
||||||
|
parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash)
|
||||||
|
if parent == nil {
|
||||||
|
return api.invalid(), fmt.Errorf("could not find parent %x", params.ParentHash)
|
||||||
|
}
|
||||||
|
td := api.les.BlockChain().GetTd(parent.Hash(), block.NumberU64()-1)
|
||||||
|
ttd := api.les.BlockChain().Config().TerminalTotalDifficulty
|
||||||
|
if td.Cmp(ttd) < 0 {
|
||||||
|
return api.invalid(), fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd)
|
||||||
|
}
|
||||||
|
if err = api.les.BlockChain().InsertHeader(block.Header()); err != nil {
|
||||||
|
return api.invalid(), err
|
||||||
|
}
|
||||||
|
if merger := api.les.Merger(); !merger.TDDReached() {
|
||||||
|
merger.ReachTTD()
|
||||||
|
}
|
||||||
|
return beacon.ExecutePayloadResponse{Status: beacon.VALID.Status, LatestValidHash: block.Hash()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid returns a response "INVALID" with the latest valid hash set to the current head.
|
||||||
|
func (api *ConsensusAPI) invalid() beacon.ExecutePayloadResponse {
|
||||||
|
return beacon.ExecutePayloadResponse{Status: beacon.INVALID.Status, LatestValidHash: api.les.BlockChain().CurrentHeader().Hash()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
|
||||||
|
// shortcut if we entered PoS already
|
||||||
|
if api.les.Merger().PoSFinalized() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// make sure the parent has enough terminal total difficulty
|
||||||
|
header := api.les.BlockChain().GetHeaderByHash(head)
|
||||||
|
if header == nil {
|
||||||
|
return &beacon.GenericServerError
|
||||||
|
}
|
||||||
|
td := api.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
|
||||||
|
if td != nil && td.Cmp(api.les.BlockChain().Config().TerminalTotalDifficulty) < 0 {
|
||||||
|
return &beacon.InvalidTB
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setHead is called to perform a force choice.
|
||||||
|
func (api *ConsensusAPI) setHead(newHead common.Hash) error {
|
||||||
|
log.Info("Setting head", "head", newHead)
|
||||||
|
|
||||||
|
headHeader := api.les.BlockChain().CurrentHeader()
|
||||||
|
if headHeader.Hash() == newHead {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newHeadHeader := api.les.BlockChain().GetHeaderByHash(newHead)
|
||||||
|
if newHeadHeader == nil {
|
||||||
|
return &beacon.GenericServerError
|
||||||
|
}
|
||||||
|
if err := api.les.BlockChain().SetChainHead(newHeadHeader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Trigger the transition if it's the first `NewHead` event.
|
||||||
|
if merger := api.les.Merger(); !merger.PoSFinalized() {
|
||||||
|
merger.FinalizePoS()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
// Copyright 2020 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 catalyst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/beacon"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
|
"github.com/ethereum/go-ethereum/les"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// testKey is a private key to use for funding a tester account.
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
||||||
|
// testAddr is the Ethereum address of the tester account.
|
||||||
|
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
|
testBalance = big.NewInt(2e18)
|
||||||
|
)
|
||||||
|
|
||||||
|
func generatePreMergeChain(n int) (*core.Genesis, []*types.Header, []*types.Block) {
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
config := params.AllEthashProtocolChanges
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: config,
|
||||||
|
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
|
||||||
|
ExtraData: []byte("test genesis"),
|
||||||
|
Timestamp: 9000,
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
}
|
||||||
|
gblock := genesis.ToBlock(db)
|
||||||
|
engine := ethash.NewFaker()
|
||||||
|
blocks, _ := core.GenerateChain(config, gblock, engine, db, n, nil)
|
||||||
|
totalDifficulty := big.NewInt(0)
|
||||||
|
|
||||||
|
var headers []*types.Header
|
||||||
|
for _, b := range blocks {
|
||||||
|
totalDifficulty.Add(totalDifficulty, b.Difficulty())
|
||||||
|
headers = append(headers, b.Header())
|
||||||
|
}
|
||||||
|
config.TerminalTotalDifficulty = totalDifficulty
|
||||||
|
|
||||||
|
return genesis, headers, blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
|
||||||
|
genesis, headers, blocks := generatePreMergeChain(10)
|
||||||
|
n, lesService := startLesService(t, genesis, headers)
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
api := NewConsensusAPI(lesService)
|
||||||
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
|
HeadBlockHash: blocks[5].Hash(),
|
||||||
|
SafeBlockHash: common.Hash{},
|
||||||
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
}
|
||||||
|
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err == nil {
|
||||||
|
t.Errorf("fork choice updated before total terminal difficulty should fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecutePayloadV1(t *testing.T) {
|
||||||
|
genesis, headers, blocks := generatePreMergeChain(10)
|
||||||
|
n, lesService := startLesService(t, genesis, headers[:9])
|
||||||
|
lesService.Merger().ReachTTD()
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
api := NewConsensusAPI(lesService)
|
||||||
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
|
HeadBlockHash: blocks[8].Hash(),
|
||||||
|
SafeBlockHash: common.Hash{},
|
||||||
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
}
|
||||||
|
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||||
|
t.Errorf("Failed to update head %v", err)
|
||||||
|
}
|
||||||
|
block := blocks[9]
|
||||||
|
|
||||||
|
fakeBlock := types.NewBlock(&types.Header{
|
||||||
|
ParentHash: block.ParentHash(),
|
||||||
|
UncleHash: crypto.Keccak256Hash(nil),
|
||||||
|
Coinbase: block.Coinbase(),
|
||||||
|
Root: block.Root(),
|
||||||
|
TxHash: crypto.Keccak256Hash(nil),
|
||||||
|
ReceiptHash: crypto.Keccak256Hash(nil),
|
||||||
|
Bloom: block.Bloom(),
|
||||||
|
Difficulty: big.NewInt(0),
|
||||||
|
Number: block.Number(),
|
||||||
|
GasLimit: block.GasLimit(),
|
||||||
|
GasUsed: block.GasUsed(),
|
||||||
|
Time: block.Time(),
|
||||||
|
Extra: block.Extra(),
|
||||||
|
MixDigest: block.MixDigest(),
|
||||||
|
Nonce: types.BlockNonce{},
|
||||||
|
BaseFee: block.BaseFee(),
|
||||||
|
}, nil, nil, nil, trie.NewStackTrie(nil))
|
||||||
|
|
||||||
|
_, err := api.ExecutePayloadV1(beacon.ExecutableDataV1{
|
||||||
|
ParentHash: fakeBlock.ParentHash(),
|
||||||
|
FeeRecipient: fakeBlock.Coinbase(),
|
||||||
|
StateRoot: fakeBlock.Root(),
|
||||||
|
ReceiptsRoot: fakeBlock.ReceiptHash(),
|
||||||
|
LogsBloom: fakeBlock.Bloom().Bytes(),
|
||||||
|
Random: fakeBlock.MixDigest(),
|
||||||
|
Number: fakeBlock.NumberU64(),
|
||||||
|
GasLimit: fakeBlock.GasLimit(),
|
||||||
|
GasUsed: fakeBlock.GasUsed(),
|
||||||
|
Timestamp: fakeBlock.Time(),
|
||||||
|
ExtraData: fakeBlock.Extra(),
|
||||||
|
BaseFeePerGas: fakeBlock.BaseFee(),
|
||||||
|
BlockHash: fakeBlock.Hash(),
|
||||||
|
Transactions: encodeTransactions(fakeBlock.Transactions()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to execute payload %v", err)
|
||||||
|
}
|
||||||
|
headHeader := api.les.BlockChain().CurrentHeader()
|
||||||
|
if headHeader.Number.Uint64() != fakeBlock.NumberU64()-1 {
|
||||||
|
t.Fatal("Unexpected chain head update")
|
||||||
|
}
|
||||||
|
fcState = beacon.ForkchoiceStateV1{
|
||||||
|
HeadBlockHash: fakeBlock.Hash(),
|
||||||
|
SafeBlockHash: common.Hash{},
|
||||||
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
}
|
||||||
|
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||||
|
t.Fatal("Failed to update head")
|
||||||
|
}
|
||||||
|
headHeader = api.les.BlockChain().CurrentHeader()
|
||||||
|
if headHeader.Number.Uint64() != fakeBlock.NumberU64() {
|
||||||
|
t.Fatal("Failed to update chain head")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEth2DeepReorg(t *testing.T) {
|
||||||
|
// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
|
||||||
|
// before the totalTerminalDifficulty threshold
|
||||||
|
/*
|
||||||
|
genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
|
||||||
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
api = NewConsensusAPI(ethservice, nil)
|
||||||
|
parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
|
||||||
|
head = ethservice.BlockChain().CurrentBlock().NumberU64()
|
||||||
|
)
|
||||||
|
if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
|
||||||
|
t.Errorf("Block %d not pruned", parent.NumberU64())
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
execData, err := api.assembleBlock(AssembleBlockParams{
|
||||||
|
ParentHash: parent.Hash(),
|
||||||
|
Timestamp: parent.Time() + 5,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create the executable data %v", err)
|
||||||
|
}
|
||||||
|
block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||||
|
}
|
||||||
|
newResp, err := api.ExecutePayload(*execData)
|
||||||
|
if err != nil || newResp.Status != "VALID" {
|
||||||
|
t.Fatalf("Failed to insert block: %v", err)
|
||||||
|
}
|
||||||
|
if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
|
||||||
|
t.Fatalf("Chain head shouldn't be updated")
|
||||||
|
}
|
||||||
|
if err := api.setHead(block.Hash()); err != nil {
|
||||||
|
t.Fatalf("Failed to set head: %v", err)
|
||||||
|
}
|
||||||
|
if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
|
||||||
|
t.Fatalf("Chain head should be updated")
|
||||||
|
}
|
||||||
|
parent, head = block, block.NumberU64()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// startEthService creates a full node instance for testing.
|
||||||
|
func startLesService(t *testing.T, genesis *core.Genesis, headers []*types.Header) (*node.Node, *les.LightEthereum) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
n, err := node.New(&node.Config{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("can't create node:", err)
|
||||||
|
}
|
||||||
|
ethcfg := ðconfig.Config{
|
||||||
|
Genesis: genesis,
|
||||||
|
Ethash: ethash.Config{PowMode: ethash.ModeFake},
|
||||||
|
SyncMode: downloader.LightSync,
|
||||||
|
TrieDirtyCache: 256,
|
||||||
|
TrieCleanCache: 256,
|
||||||
|
LightPeers: 10,
|
||||||
|
}
|
||||||
|
lesService, err := les.New(n, ethcfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("can't create eth service:", err)
|
||||||
|
}
|
||||||
|
if err := n.Start(); err != nil {
|
||||||
|
t.Fatal("can't start node:", err)
|
||||||
|
}
|
||||||
|
if _, err := lesService.BlockChain().InsertHeaderChain(headers, 0); err != nil {
|
||||||
|
n.Close()
|
||||||
|
t.Fatal("can't import test headers:", err)
|
||||||
|
}
|
||||||
|
return n, lesService
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeTransactions(txs []*types.Transaction) [][]byte {
|
||||||
|
var enc = make([][]byte, len(txs))
|
||||||
|
for i, tx := range txs {
|
||||||
|
enc[i], _ = tx.MarshalBinary()
|
||||||
|
}
|
||||||
|
return enc
|
||||||
|
}
|
|
@ -32,13 +32,15 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/beacon"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
ethcatalyst "github.com/ethereum/go-ethereum/eth/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
|
lescatalyst "github.com/ethereum/go-ethereum/les/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
@ -88,24 +90,26 @@ var (
|
||||||
|
|
||||||
type ethNode struct {
|
type ethNode struct {
|
||||||
typ nodetype
|
typ nodetype
|
||||||
api *catalyst.ConsensusAPI
|
|
||||||
ethBackend *eth.Ethereum
|
|
||||||
lesBackend *les.LightEthereum
|
|
||||||
stack *node.Node
|
stack *node.Node
|
||||||
enode *enode.Node
|
enode *enode.Node
|
||||||
|
api *ethcatalyst.ConsensusAPI
|
||||||
|
ethBackend *eth.Ethereum
|
||||||
|
lapi *lescatalyst.ConsensusAPI
|
||||||
|
lesBackend *les.LightEthereum
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode {
|
func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
api *catalyst.ConsensusAPI
|
api *ethcatalyst.ConsensusAPI
|
||||||
|
lapi *lescatalyst.ConsensusAPI
|
||||||
stack *node.Node
|
stack *node.Node
|
||||||
ethBackend *eth.Ethereum
|
ethBackend *eth.Ethereum
|
||||||
lesBackend *les.LightEthereum
|
lesBackend *les.LightEthereum
|
||||||
)
|
)
|
||||||
// Start the node and wait until it's up
|
// Start the node and wait until it's up
|
||||||
if typ == eth2LightClient {
|
if typ == eth2LightClient {
|
||||||
stack, lesBackend, api, err = makeLightNode(genesis)
|
stack, lesBackend, lapi, err = makeLightNode(genesis)
|
||||||
} else {
|
} else {
|
||||||
stack, ethBackend, api, err = makeFullNode(genesis)
|
stack, ethBackend, api, err = makeFullNode(genesis)
|
||||||
}
|
}
|
||||||
|
@ -131,13 +135,14 @@ func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode
|
||||||
typ: typ,
|
typ: typ,
|
||||||
api: api,
|
api: api,
|
||||||
ethBackend: ethBackend,
|
ethBackend: ethBackend,
|
||||||
|
lapi: lapi,
|
||||||
lesBackend: lesBackend,
|
lesBackend: lesBackend,
|
||||||
stack: stack,
|
stack: stack,
|
||||||
enode: enode,
|
enode: enode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*catalyst.ExecutableDataV1, error) {
|
func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*beacon.ExecutableDataV1, error) {
|
||||||
if n.typ != eth2MiningNode {
|
if n.typ != eth2MiningNode {
|
||||||
return nil, errors.New("invalid node type")
|
return nil, errors.New("invalid node type")
|
||||||
}
|
}
|
||||||
|
@ -145,12 +150,12 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
|
||||||
if timestamp <= parentTimestamp {
|
if timestamp <= parentTimestamp {
|
||||||
timestamp = parentTimestamp + 1
|
timestamp = parentTimestamp + 1
|
||||||
}
|
}
|
||||||
payloadAttribute := catalyst.PayloadAttributesV1{
|
payloadAttribute := beacon.PayloadAttributesV1{
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
Random: common.Hash{},
|
Random: common.Hash{},
|
||||||
SuggestedFeeRecipient: common.HexToAddress("0xdeadbeef"),
|
SuggestedFeeRecipient: common.HexToAddress("0xdeadbeef"),
|
||||||
}
|
}
|
||||||
fcState := catalyst.ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: parentHash,
|
HeadBlockHash: parentHash,
|
||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: common.Hash{},
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
@ -162,39 +167,62 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
|
||||||
return n.api.GetPayloadV1(*payload.PayloadID)
|
return n.api.GetPayloadV1(*payload.PayloadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ethNode) insertBlock(eb catalyst.ExecutableDataV1) error {
|
func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error {
|
||||||
if !eth2types(n.typ) {
|
if !eth2types(n.typ) {
|
||||||
return errors.New("invalid node type")
|
return errors.New("invalid node type")
|
||||||
}
|
}
|
||||||
newResp, err := n.api.ExecutePayloadV1(eb)
|
switch n.typ {
|
||||||
if err != nil {
|
case eth2NormalNode, eth2MiningNode:
|
||||||
return err
|
newResp, err := n.api.ExecutePayloadV1(eb)
|
||||||
} else if newResp.Status != "VALID" {
|
if err != nil {
|
||||||
return errors.New("failed to insert block")
|
return err
|
||||||
|
} else if newResp.Status != "VALID" {
|
||||||
|
return errors.New("failed to insert block")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case eth2LightClient:
|
||||||
|
newResp, err := n.lapi.ExecutePayloadV1(eb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if newResp.Status != "VALID" {
|
||||||
|
return errors.New("failed to insert block")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.New("undefined node")
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed catalyst.ExecutableDataV1) error {
|
func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed beacon.ExecutableDataV1) error {
|
||||||
if !eth2types(n.typ) {
|
if !eth2types(n.typ) {
|
||||||
return errors.New("invalid node type")
|
return errors.New("invalid node type")
|
||||||
}
|
}
|
||||||
if err := n.insertBlock(ed); err != nil {
|
if err := n.insertBlock(ed); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
block, err := catalyst.ExecutableDataToBlock(ed)
|
block, err := beacon.ExecutableDataToBlock(ed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fcState := catalyst.ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: block.ParentHash(),
|
HeadBlockHash: block.ParentHash(),
|
||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: common.Hash{},
|
FinalizedBlockHash: common.Hash{},
|
||||||
}
|
}
|
||||||
if _, err := n.api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
switch n.typ {
|
||||||
return err
|
case eth2NormalNode, eth2MiningNode:
|
||||||
|
if _, err := n.api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case eth2LightClient:
|
||||||
|
if _, err := n.lapi.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.New("undefined node")
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type nodeManager struct {
|
type nodeManager struct {
|
||||||
|
@ -290,7 +318,7 @@ func (mgr *nodeManager) run() {
|
||||||
nodes = append(nodes, mgr.getNodes(eth2NormalNode)...)
|
nodes = append(nodes, mgr.getNodes(eth2NormalNode)...)
|
||||||
nodes = append(nodes, mgr.getNodes(eth2LightClient)...)
|
nodes = append(nodes, mgr.getNodes(eth2LightClient)...)
|
||||||
for _, node := range append(nodes) {
|
for _, node := range append(nodes) {
|
||||||
fcState := catalyst.ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
HeadBlockHash: oldest.Hash(),
|
HeadBlockHash: oldest.Hash(),
|
||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: oldest.Hash(),
|
FinalizedBlockHash: oldest.Hash(),
|
||||||
|
@ -336,20 +364,16 @@ func (mgr *nodeManager) run() {
|
||||||
log.Error("Failed to assemble the block", "err", err)
|
log.Error("Failed to assemble the block", "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
block, _ := catalyst.ExecutableDataToBlock(*ed)
|
block, _ := beacon.ExecutableDataToBlock(*ed)
|
||||||
|
|
||||||
nodes := mgr.getNodes(eth2MiningNode)
|
nodes := mgr.getNodes(eth2MiningNode)
|
||||||
nodes = append(nodes, mgr.getNodes(eth2NormalNode)...)
|
nodes = append(nodes, mgr.getNodes(eth2NormalNode)...)
|
||||||
|
nodes = append(nodes, mgr.getNodes(eth2LightClient)...)
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
if err := node.insertBlockAndSetHead(parentBlock.Header(), *ed); err != nil {
|
if err := node.insertBlockAndSetHead(parentBlock.Header(), *ed); err != nil {
|
||||||
log.Error("Failed to insert block", "type", node.typ, "err", err)
|
log.Error("Failed to insert block", "type", node.typ, "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, node := range mgr.getNodes(eth2LightClient) {
|
|
||||||
if err := node.insertBlock(*ed); err != nil {
|
|
||||||
log.Error("Failed to insert block", "type", node.typ, "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info("Create and insert eth2 block", "number", ed.Number)
|
log.Info("Create and insert eth2 block", "number", ed.Number)
|
||||||
parentBlock = block
|
parentBlock = block
|
||||||
waitFinalise = append(waitFinalise, block)
|
waitFinalise = append(waitFinalise, block)
|
||||||
|
@ -435,7 +459,7 @@ func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis {
|
||||||
return genesis
|
return genesis
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeFullNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *catalyst.ConsensusAPI, error) {
|
func makeFullNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *ethcatalyst.ConsensusAPI, error) {
|
||||||
// Define the basic configurations for the Ethereum node
|
// Define the basic configurations for the Ethereum node
|
||||||
datadir, _ := ioutil.TempDir("", "")
|
datadir, _ := ioutil.TempDir("", "")
|
||||||
|
|
||||||
|
@ -483,10 +507,10 @@ func makeFullNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *catalyst.C
|
||||||
log.Crit("Failed to create the LES server", "err", err)
|
log.Crit("Failed to create the LES server", "err", err)
|
||||||
}
|
}
|
||||||
err = stack.Start()
|
err = stack.Start()
|
||||||
return stack, ethBackend, catalyst.NewConsensusAPI(ethBackend, nil), err
|
return stack, ethBackend, ethcatalyst.NewConsensusAPI(ethBackend), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeLightNode(genesis *core.Genesis) (*node.Node, *les.LightEthereum, *catalyst.ConsensusAPI, error) {
|
func makeLightNode(genesis *core.Genesis) (*node.Node, *les.LightEthereum, *lescatalyst.ConsensusAPI, error) {
|
||||||
// Define the basic configurations for the Ethereum node
|
// Define the basic configurations for the Ethereum node
|
||||||
datadir, _ := ioutil.TempDir("", "")
|
datadir, _ := ioutil.TempDir("", "")
|
||||||
|
|
||||||
|
@ -521,7 +545,7 @@ func makeLightNode(genesis *core.Genesis) (*node.Node, *les.LightEthereum, *cata
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
err = stack.Start()
|
err = stack.Start()
|
||||||
return stack, lesBackend, catalyst.NewConsensusAPI(nil, lesBackend), err
|
return stack, lesBackend, lescatalyst.NewConsensusAPI(lesBackend), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func eth2types(typ nodetype) bool {
|
func eth2types(typ nodetype) bool {
|
||||||
|
|
Loading…
Reference in New Issue