Unverified Commit 4f4622bc authored by Marius van der Wijden's avatar Marius van der Wijden Committed by GitHub

core: eth: implement Kiln-v2 spec (#24506)

* core/beacon: eth/catalyst: updated engine api to new version

* core: implement exchangeTransitionConfig

* core/beacon: prevRandao instead of Random

* eth/catalyst: Fix ExchangeTransitionConfig, add test

* eth/catalyst: stop external miners on TTD reached

* node: implement --authrpc.vhosts flag

* core: allow for config override on non-mainnet networks

* eth/catalyst: fix peters comments

* eth/catalyst: make stop remote sealer more explicit

* eth/catalyst: add log output

* cmd/utils: rename authrpc.host to authrpc.addr

* eth/catalyst: disable the disabling of the miner

* eth: core: remove notion of terminal pow block

* eth: les: more of peters nitpicks
parent 830231c1
...@@ -166,8 +166,9 @@ var ( ...@@ -166,8 +166,9 @@ var (
utils.HTTPListenAddrFlag, utils.HTTPListenAddrFlag,
utils.HTTPPortFlag, utils.HTTPPortFlag,
utils.HTTPCORSDomainFlag, utils.HTTPCORSDomainFlag,
utils.AuthHostFlag, utils.AuthListenFlag,
utils.AuthPortFlag, utils.AuthPortFlag,
utils.AuthVirtualHostsFlag,
utils.JWTSecretFlag, utils.JWTSecretFlag,
utils.HTTPVirtualHostsFlag, utils.HTTPVirtualHostsFlag,
utils.GraphQLEnabledFlag, utils.GraphQLEnabledFlag,
......
...@@ -150,8 +150,9 @@ var AppHelpFlagGroups = []flags.FlagGroup{ ...@@ -150,8 +150,9 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.WSPathPrefixFlag, utils.WSPathPrefixFlag,
utils.WSAllowedOriginsFlag, utils.WSAllowedOriginsFlag,
utils.JWTSecretFlag, utils.JWTSecretFlag,
utils.AuthHostFlag, utils.AuthListenFlag,
utils.AuthPortFlag, utils.AuthPortFlag,
utils.AuthVirtualHostsFlag,
utils.GraphQLEnabledFlag, utils.GraphQLEnabledFlag,
utils.GraphQLCORSDomainFlag, utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag, utils.GraphQLVirtualHostsFlag,
......
...@@ -527,16 +527,21 @@ var ( ...@@ -527,16 +527,21 @@ var (
Value: ethconfig.Defaults.RPCTxFeeCap, Value: ethconfig.Defaults.RPCTxFeeCap,
} }
// Authenticated RPC HTTP settings // Authenticated RPC HTTP settings
AuthHostFlag = cli.StringFlag{ AuthListenFlag = cli.StringFlag{
Name: "authrpc.host", Name: "authrpc.addr",
Usage: "Listening address for authenticated APIs", Usage: "Listening address for authenticated APIs",
Value: node.DefaultConfig.AuthHost, Value: node.DefaultConfig.AuthAddr,
} }
AuthPortFlag = cli.IntFlag{ AuthPortFlag = cli.IntFlag{
Name: "authrpc.port", Name: "authrpc.port",
Usage: "Listening port for authenticated APIs", Usage: "Listening port for authenticated APIs",
Value: node.DefaultConfig.AuthPort, Value: node.DefaultConfig.AuthPort,
} }
AuthVirtualHostsFlag = cli.StringFlag{
Name: "authrpc.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: strings.Join(node.DefaultConfig.AuthVirtualHosts, ","),
}
JWTSecretFlag = cli.StringFlag{ JWTSecretFlag = cli.StringFlag{
Name: "authrpc.jwtsecret", Name: "authrpc.jwtsecret",
Usage: "JWT secret (or path to a jwt secret) to use for authenticated RPC endpoints", Usage: "JWT secret (or path to a jwt secret) to use for authenticated RPC endpoints",
...@@ -974,13 +979,18 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { ...@@ -974,13 +979,18 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
cfg.HTTPPort = ctx.GlobalInt(HTTPPortFlag.Name) cfg.HTTPPort = ctx.GlobalInt(HTTPPortFlag.Name)
} }
if ctx.GlobalIsSet(AuthHostFlag.Name) { if ctx.GlobalIsSet(AuthListenFlag.Name) {
cfg.AuthHost = ctx.GlobalString(AuthHostFlag.Name) cfg.AuthAddr = ctx.GlobalString(AuthListenFlag.Name)
} }
if ctx.GlobalIsSet(AuthPortFlag.Name) { if ctx.GlobalIsSet(AuthPortFlag.Name) {
cfg.AuthPort = ctx.GlobalInt(AuthPortFlag.Name) cfg.AuthPort = ctx.GlobalInt(AuthPortFlag.Name)
} }
if ctx.GlobalIsSet(AuthVirtualHostsFlag.Name) {
cfg.AuthVirtualHosts = SplitAndTrim(ctx.GlobalString(AuthVirtualHostsFlag.Name))
}
if ctx.GlobalIsSet(HTTPCORSDomainFlag.Name) { if ctx.GlobalIsSet(HTTPCORSDomainFlag.Name) {
cfg.HTTPCors = SplitAndTrim(ctx.GlobalString(HTTPCORSDomainFlag.Name)) cfg.HTTPCors = SplitAndTrim(ctx.GlobalString(HTTPCORSDomainFlag.Name))
} }
......
...@@ -549,6 +549,11 @@ func NewShared() *Ethash { ...@@ -549,6 +549,11 @@ func NewShared() *Ethash {
// Close closes the exit channel to notify all backend threads exiting. // Close closes the exit channel to notify all backend threads exiting.
func (ethash *Ethash) Close() error { func (ethash *Ethash) Close() error {
return ethash.StopRemoteSealer()
}
// StopRemoteSealer stops the remote sealer
func (ethash *Ethash) StopRemoteSealer() error {
ethash.closeOnce.Do(func() { ethash.closeOnce.Do(func() {
// Short circuit if the exit channel is not allocated. // Short circuit if the exit channel is not allocated.
if ethash.remote == nil { if ethash.remote == nil {
......
...@@ -38,7 +38,13 @@ var ( ...@@ -38,7 +38,13 @@ var (
// - newPayloadV1: if the payload was accepted, but not processed (side chain) // - newPayloadV1: if the payload was accepted, but not processed (side chain)
ACCEPTED = "ACCEPTED" ACCEPTED = "ACCEPTED"
INVALIDBLOCKHASH = "INVALID_BLOCK_HASH"
INVALIDTERMINALBLOCK = "INVALID_TERMINAL_BLOCK"
GenericServerError = rpc.CustomError{Code: -32000, ValidationError: "Server error"} GenericServerError = rpc.CustomError{Code: -32000, ValidationError: "Server error"}
UnknownPayload = rpc.CustomError{Code: -32001, ValidationError: "Unknown payload"} UnknownPayload = rpc.CustomError{Code: -32001, ValidationError: "Unknown payload"}
InvalidTB = rpc.CustomError{Code: -32002, ValidationError: "Invalid terminal block"} InvalidTB = rpc.CustomError{Code: -32002, ValidationError: "Invalid terminal block"}
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
) )
...@@ -16,7 +16,7 @@ var _ = (*payloadAttributesMarshaling)(nil) ...@@ -16,7 +16,7 @@ var _ = (*payloadAttributesMarshaling)(nil)
func (p PayloadAttributesV1) MarshalJSON() ([]byte, error) { func (p PayloadAttributesV1) MarshalJSON() ([]byte, error) {
type PayloadAttributesV1 struct { type PayloadAttributesV1 struct {
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Random common.Hash `json:"random" gencodec:"required"` Random common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
} }
var enc PayloadAttributesV1 var enc PayloadAttributesV1
...@@ -30,7 +30,7 @@ func (p PayloadAttributesV1) MarshalJSON() ([]byte, error) { ...@@ -30,7 +30,7 @@ func (p PayloadAttributesV1) MarshalJSON() ([]byte, error) {
func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error { func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error {
type PayloadAttributesV1 struct { type PayloadAttributesV1 struct {
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Random *common.Hash `json:"random" gencodec:"required"` Random *common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
} }
var dec PayloadAttributesV1 var dec PayloadAttributesV1
...@@ -42,7 +42,7 @@ func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error { ...@@ -42,7 +42,7 @@ func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error {
} }
p.Timestamp = uint64(*dec.Timestamp) p.Timestamp = uint64(*dec.Timestamp)
if dec.Random == nil { if dec.Random == nil {
return errors.New("missing required field 'random' for PayloadAttributesV1") return errors.New("missing required field 'prevRandao' for PayloadAttributesV1")
} }
p.Random = *dec.Random p.Random = *dec.Random
if dec.SuggestedFeeRecipient == nil { if dec.SuggestedFeeRecipient == nil {
......
...@@ -19,9 +19,9 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) { ...@@ -19,9 +19,9 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) {
ParentHash common.Hash `json:"parentHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot common.Hash `json:"stateRoot" gencodec:"required"` StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random common.Hash `json:"random" gencodec:"required"` Random common.Hash `json:"prevRandao" gencodec:"required"`
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
...@@ -60,9 +60,9 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error { ...@@ -60,9 +60,9 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"` ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random *common.Hash `json:"random" gencodec:"required"` Random *common.Hash `json:"prevRandao" gencodec:"required"`
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
...@@ -97,7 +97,7 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error { ...@@ -97,7 +97,7 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error {
} }
e.LogsBloom = *dec.LogsBloom e.LogsBloom = *dec.LogsBloom
if dec.Random == nil { if dec.Random == nil {
return errors.New("missing required field 'random' for ExecutableDataV1") return errors.New("missing required field 'prevRandao' for ExecutableDataV1")
} }
e.Random = *dec.Random e.Random = *dec.Random
if dec.Number == nil { if dec.Number == nil {
......
...@@ -31,7 +31,7 @@ import ( ...@@ -31,7 +31,7 @@ import (
// PayloadAttributesV1 structure described at https://github.com/ethereum/execution-apis/pull/74 // PayloadAttributesV1 structure described at https://github.com/ethereum/execution-apis/pull/74
type PayloadAttributesV1 struct { type PayloadAttributesV1 struct {
Timestamp uint64 `json:"timestamp" gencodec:"required"` Timestamp uint64 `json:"timestamp" gencodec:"required"`
Random common.Hash `json:"random" gencodec:"required"` Random common.Hash `json:"prevRandao" gencodec:"required"`
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
} }
...@@ -47,9 +47,9 @@ type ExecutableDataV1 struct { ...@@ -47,9 +47,9 @@ type ExecutableDataV1 struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot common.Hash `json:"stateRoot" gencodec:"required"` StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom []byte `json:"logsBloom" gencodec:"required"` LogsBloom []byte `json:"logsBloom" gencodec:"required"`
Random common.Hash `json:"random" gencodec:"required"` Random common.Hash `json:"prevRandao" gencodec:"required"`
Number uint64 `json:"blockNumber" gencodec:"required"` Number uint64 `json:"blockNumber" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasLimit uint64 `json:"gasLimit" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"`
...@@ -72,14 +72,16 @@ type executableDataMarshaling struct { ...@@ -72,14 +72,16 @@ type executableDataMarshaling struct {
Transactions []hexutil.Bytes Transactions []hexutil.Bytes
} }
type ExecutePayloadResponse struct { type PayloadStatusV1 struct {
Status string `json:"status"` Status string `json:"status"`
LatestValidHash common.Hash `json:"latestValidHash"` LatestValidHash *common.Hash `json:"latestValidHash"`
ValidationError *string `json:"validationError"`
} }
type ConsensusValidatedParams struct { type TransitionConfigurationV1 struct {
BlockHash common.Hash `json:"blockHash"` TerminalTotalDifficulty *hexutil.Big `json:"terminalTotalDifficulty"`
Status string `json:"status"` TerminalBlockHash common.Hash `json:"terminalBlockHash"`
TerminalBlockNumber hexutil.Uint64 `json:"terminalBlockNumber"`
} }
// PayloadID is an identifier of the payload build process // PayloadID is an identifier of the payload build process
...@@ -102,8 +104,8 @@ func (b *PayloadID) UnmarshalText(input []byte) error { ...@@ -102,8 +104,8 @@ func (b *PayloadID) UnmarshalText(input []byte) error {
} }
type ForkChoiceResponse struct { type ForkChoiceResponse struct {
Status string `json:"status"` PayloadStatus PayloadStatusV1 `json:"payloadStatus"`
PayloadID *PayloadID `json:"payloadId"` PayloadID *PayloadID `json:"payloadId"`
} }
type ForkchoiceStateV1 struct { type ForkchoiceStateV1 struct {
......
...@@ -207,9 +207,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override ...@@ -207,9 +207,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
if overrideArrowGlacier != nil { if overrideArrowGlacier != nil {
newcfg.ArrowGlacierBlock = overrideArrowGlacier newcfg.ArrowGlacierBlock = overrideArrowGlacier
} }
if overrideTerminalTotalDifficulty != nil {
newcfg.TerminalTotalDifficulty = overrideTerminalTotalDifficulty
}
if err := newcfg.CheckConfigForkOrder(); err != nil { if err := newcfg.CheckConfigForkOrder(); err != nil {
return newcfg, common.Hash{}, err return newcfg, common.Hash{}, err
} }
...@@ -219,6 +216,10 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override ...@@ -219,6 +216,10 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
rawdb.WriteChainConfig(db, stored, newcfg) rawdb.WriteChainConfig(db, stored, newcfg)
return newcfg, stored, nil return newcfg, stored, nil
} }
if overrideTerminalTotalDifficulty != nil {
storedcfg.TerminalTotalDifficulty = overrideTerminalTotalDifficulty
}
// Special case: don't change the existing config of a non-mainnet chain if no new // Special case: don't change the existing config of a non-mainnet chain if no new
// config is supplied. These chains would get AllProtocolChanges (and a compat error) // config is supplied. These chains would get AllProtocolChanges (and a compat error)
// if we just continued here. // if we just continued here.
......
This diff is collapsed.
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"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/beacon"
...@@ -49,11 +50,12 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { ...@@ -49,11 +50,12 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
config := params.AllEthashProtocolChanges config := params.AllEthashProtocolChanges
genesis := &core.Genesis{ genesis := &core.Genesis{
Config: config, Config: config,
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
ExtraData: []byte("test genesis"), ExtraData: []byte("test genesis"),
Timestamp: 9000, Timestamp: 9000,
BaseFee: big.NewInt(params.InitialBaseFee), BaseFee: big.NewInt(params.InitialBaseFee),
Difficulty: big.NewInt(0),
} }
testNonce := uint64(0) testNonce := uint64(0)
generate := func(i int, g *core.BlockGen) { generate := func(i int, g *core.BlockGen) {
...@@ -130,8 +132,10 @@ func TestSetHeadBeforeTotalDifficulty(t *testing.T) { ...@@ -130,8 +132,10 @@ func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
SafeBlockHash: common.Hash{}, SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{}, FinalizedBlockHash: common.Hash{},
} }
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err == nil { if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
t.Errorf("fork choice updated before total terminal difficulty should fail") t.Errorf("fork choice updated should not error: %v", err)
} else if resp.PayloadStatus.Status != beacon.INVALIDTERMINALBLOCK {
t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
} }
} }
...@@ -277,7 +281,7 @@ func TestEth2NewBlock(t *testing.T) { ...@@ -277,7 +281,7 @@ func TestEth2NewBlock(t *testing.T) {
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)
} }
newResp, err := api.ExecutePayloadV1(*execData) newResp, err := api.NewPayloadV1(*execData)
if err != nil || newResp.Status != "VALID" { if err != nil || newResp.Status != "VALID" {
t.Fatalf("Failed to insert block: %v", err) t.Fatalf("Failed to insert block: %v", err)
} }
...@@ -317,7 +321,7 @@ func TestEth2NewBlock(t *testing.T) { ...@@ -317,7 +321,7 @@ func TestEth2NewBlock(t *testing.T) {
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)
} }
newResp, err := api.ExecutePayloadV1(*execData) newResp, err := api.NewPayloadV1(*execData)
if err != nil || newResp.Status != "VALID" { if err != nil || newResp.Status != "VALID" {
t.Fatalf("Failed to insert block: %v", err) t.Fatalf("Failed to insert block: %v", err)
} }
...@@ -414,66 +418,108 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) ...@@ -414,66 +418,108 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
} }
func TestFullAPI(t *testing.T) { func TestFullAPI(t *testing.T) {
// TODO (MariusVanDerWijden) TestFullAPI is currently broken, because it tries to reorg genesis, preMergeBlocks := generatePreMergeChain(10)
// before the totalTerminalDifficulty threshold, fixed in upcoming merge-kiln-v2 pr n, ethservice := startEthService(t, genesis, preMergeBlocks)
/* ethservice.Merger().ReachTTD()
genesis, preMergeBlocks := generatePreMergeChain(10) defer n.Close()
n, ethservice := startEthService(t, genesis, preMergeBlocks) var (
ethservice.Merger().ReachTTD() api = NewConsensusAPI(ethservice)
defer n.Close() parent = ethservice.BlockChain().CurrentBlock()
var ( // This EVM code generates a log when the contract is created.
api = NewConsensusAPI(ethservice) logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
parent = ethservice.BlockChain().CurrentBlock() )
// This EVM code generates a log when the contract is created. for i := 0; i < 10; i++ {
logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
) nonce := statedb.GetNonce(testAddr)
for i := 0; i < 10; i++ { tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) ethservice.TxPool().AddLocal(tx)
nonce := statedb.GetNonce(testAddr)
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)
params := beacon.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 := beacon.ForkchoiceStateV1{
HeadBlockHash: parent.Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
}
resp, err := api.ForkchoiceUpdatedV1(fcState, &params)
if err != nil {
t.Fatalf("error preparing payload, err=%v", err)
}
if resp.Status != beacon.VALID {
t.Fatalf("error preparing payload, invalid status: %v", resp.Status)
}
payloadID := computePayloadId(parent.Hash(), &params)
payload, err := api.GetPayloadV1(payloadID)
if err != nil {
t.Fatalf("can't get payload: %v", err)
}
execResp, err := api.ExecutePayloadV1(*payload)
if err != nil {
t.Fatalf("can't execute payload: %v", err)
}
if execResp.Status != beacon.VALID {
t.Fatalf("invalid status: %v", execResp.Status)
}
fcState = beacon.ForkchoiceStateV1{
HeadBlockHash: payload.BlockHash,
SafeBlockHash: payload.ParentHash,
FinalizedBlockHash: payload.ParentHash,
}
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
t.Fatalf("Failed to insert block: %v", err)
}
if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
t.Fatalf("Chain head should be updated")
}
parent = ethservice.BlockChain().CurrentBlock()
} }
*/
fcState := beacon.ForkchoiceStateV1{
HeadBlockHash: parent.Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
}
resp, err := api.ForkchoiceUpdatedV1(fcState, &params)
if err != nil {
t.Fatalf("error preparing payload, err=%v", err)
}
if resp.PayloadStatus.Status != beacon.VALID {
t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
}
payload, err := api.GetPayloadV1(*resp.PayloadID)
if err != nil {
t.Fatalf("can't get payload: %v", err)
}
execResp, err := api.NewPayloadV1(*payload)
if err != nil {
t.Fatalf("can't execute payload: %v", err)
}
if execResp.Status != beacon.VALID {
t.Fatalf("invalid status: %v", execResp.Status)
}
fcState = beacon.ForkchoiceStateV1{
HeadBlockHash: payload.BlockHash,
SafeBlockHash: payload.ParentHash,
FinalizedBlockHash: payload.ParentHash,
}
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
t.Fatalf("Failed to insert block: %v", err)
}
if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
t.Fatalf("Chain head should be updated")
}
parent = ethservice.BlockChain().CurrentBlock()
}
}
func TestExchangeTransitionConfig(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
var (
api = NewConsensusAPI(ethservice)
)
// invalid ttd
config := beacon.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
TerminalBlockHash: common.Hash{},
TerminalBlockNumber: 0,
}
if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
t.Fatal("expected error on invalid config, invalid ttd")
}
// invalid terminal block hash
config = beacon.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
TerminalBlockHash: common.Hash{1},
TerminalBlockNumber: 0,
}
if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
t.Fatal("expected error on invalid config, invalid hash")
}
// valid config
config = beacon.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
TerminalBlockHash: common.Hash{},
TerminalBlockNumber: 0,
}
if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
t.Fatalf("expected no error on valid config, got %v", err)
}
// valid config
config = beacon.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
TerminalBlockHash: preMergeBlocks[5].Hash(),
TerminalBlockNumber: 6,
}
if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
t.Fatalf("expected no error on valid config, got %v", err)
}
} }
...@@ -189,7 +189,7 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp { ...@@ -189,7 +189,7 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp {
// We seem to be in sync according to the legacy rules. In the merge // We seem to be in sync according to the legacy rules. In the merge
// world, it can also mean we're stuck on the merge block, waiting for // world, it can also mean we're stuck on the merge block, waiting for
// a beacon client. In the latter case, notify the user. // a beacon client. In the latter case, notify the user.
if cs.handler.chain.Config().TerminalTotalDifficulty != nil && time.Since(cs.warned) > 10*time.Second { if ttd := cs.handler.chain.Config().TerminalTotalDifficulty; ttd != nil && ourTD.Cmp(ttd) >= 0 && time.Since(cs.warned) > 10*time.Second {
log.Warn("Local chain is post-merge, waiting for beacon client sync switch-over...") log.Warn("Local chain is post-merge, waiting for beacon client sync switch-over...")
cs.warned = time.Now() cs.warned = time.Now()
} }
......
...@@ -69,30 +69,31 @@ func NewConsensusAPI(les *les.LightEthereum) *ConsensusAPI { ...@@ -69,30 +69,31 @@ func NewConsensusAPI(les *les.LightEthereum) *ConsensusAPI {
// we return an error since block creation is not supported in les mode // 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.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
if heads.HeadBlockHash == (common.Hash{}) { if heads.HeadBlockHash == (common.Hash{}) {
return beacon.ForkChoiceResponse{Status: beacon.VALID}, nil log.Warn("Forkchoice requested update to zero hash")
return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
} }
if err := api.checkTerminalTotalDifficulty(heads.HeadBlockHash); err != nil { if err := api.checkTerminalTotalDifficulty(heads.HeadBlockHash); err != nil {
if header := api.les.BlockChain().GetHeaderByHash(heads.HeadBlockHash); header == nil { if header := api.les.BlockChain().GetHeaderByHash(heads.HeadBlockHash); header == nil {
// TODO (MariusVanDerWijden) trigger sync // TODO (MariusVanDerWijden) trigger sync
return beacon.ForkChoiceResponse{Status: beacon.SYNCING}, nil return beacon.STATUS_SYNCING, nil
} }
return beacon.ForkChoiceResponse{Status: beacon.INVALID}, err return beacon.STATUS_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 header := api.les.BlockChain().GetHeaderByHash(heads.FinalizedBlockHash); header == nil { if header := api.les.BlockChain().GetHeaderByHash(heads.FinalizedBlockHash); header == nil {
// TODO (MariusVanDerWijden) trigger sync // TODO (MariusVanDerWijden) trigger sync
return beacon.ForkChoiceResponse{Status: beacon.SYNCING}, nil return beacon.STATUS_SYNCING, nil
} }
} }
// SetHead // SetHead
if err := api.setHead(heads.HeadBlockHash); err != nil { if err := api.setHead(heads.HeadBlockHash); err != nil {
return beacon.ForkChoiceResponse{Status: beacon.INVALID}, err return beacon.STATUS_INVALID, err
} }
if payloadAttributes != nil { if payloadAttributes != nil {
return beacon.ForkChoiceResponse{Status: beacon.INVALID}, errors.New("not supported") return beacon.STATUS_INVALID, errors.New("not supported")
} }
return beacon.ForkChoiceResponse{Status: beacon.VALID}, nil return api.validForkChoiceResponse(), nil
} }
// GetPayloadV1 returns a cached payload by id. It's not supported in les mode. // GetPayloadV1 returns a cached payload by id. It's not supported in les mode.
...@@ -101,7 +102,7 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.Execu ...@@ -101,7 +102,7 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.Execu
} }
// 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 beacon.ExecutableDataV1) (beacon.ExecutePayloadResponse, error) { func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) {
block, err := beacon.ExecutableDataToBlock(params) block, err := beacon.ExecutableDataToBlock(params)
if err != nil { if err != nil {
return api.invalid(), err return api.invalid(), err
...@@ -114,7 +115,7 @@ func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beaco ...@@ -114,7 +115,7 @@ func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beaco
} }
*/ */
// TODO (MariusVanDerWijden) we should return nil here not empty hash // TODO (MariusVanDerWijden) we should return nil here not empty hash
return beacon.ExecutePayloadResponse{Status: beacon.SYNCING, LatestValidHash: common.Hash{}}, nil return beacon.PayloadStatusV1{Status: beacon.SYNCING, LatestValidHash: nil}, nil
} }
parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash) parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash)
if parent == nil { if parent == nil {
...@@ -131,12 +132,21 @@ func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beaco ...@@ -131,12 +132,21 @@ func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beaco
if merger := api.les.Merger(); !merger.TDDReached() { if merger := api.les.Merger(); !merger.TDDReached() {
merger.ReachTTD() merger.ReachTTD()
} }
return beacon.ExecutePayloadResponse{Status: beacon.VALID, LatestValidHash: block.Hash()}, nil hash := block.Hash()
return beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &hash}, nil
}
func (api *ConsensusAPI) validForkChoiceResponse() beacon.ForkChoiceResponse {
currentHash := api.les.BlockChain().CurrentHeader().Hash()
return beacon.ForkChoiceResponse{
PayloadStatus: beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &currentHash},
}
} }
// invalid returns a response "INVALID" with the latest valid hash set to the current head. // invalid returns a response "INVALID" with the latest valid hash set to the current head.
func (api *ConsensusAPI) invalid() beacon.ExecutePayloadResponse { func (api *ConsensusAPI) invalid() beacon.PayloadStatusV1 {
return beacon.ExecutePayloadResponse{Status: beacon.INVALID, LatestValidHash: api.les.BlockChain().CurrentHeader().Hash()} currentHash := api.les.BlockChain().CurrentHeader().Hash()
return beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: &currentHash}
} }
func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error { func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
......
...@@ -173,7 +173,7 @@ func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error { ...@@ -173,7 +173,7 @@ func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error {
} }
switch n.typ { switch n.typ {
case eth2NormalNode, eth2MiningNode: case eth2NormalNode, eth2MiningNode:
newResp, err := n.api.ExecutePayloadV1(eb) newResp, err := n.api.NewPayloadV1(eb)
if err != nil { if err != nil {
return err return err
} else if newResp.Status != "VALID" { } else if newResp.Status != "VALID" {
......
...@@ -139,12 +139,16 @@ type Config struct { ...@@ -139,12 +139,16 @@ type Config struct {
// HTTPPathPrefix specifies a path prefix on which http-rpc is to be served. // HTTPPathPrefix specifies a path prefix on which http-rpc is to be served.
HTTPPathPrefix string `toml:",omitempty"` HTTPPathPrefix string `toml:",omitempty"`
// AuthHost is the listening address on which authenticated APIs are provided. // AuthAddr is the listening address on which authenticated APIs are provided.
AuthHost string `toml:",omitempty"` AuthAddr string `toml:",omitempty"`
// AuthPort is the port number on which authenticated APIs are provided. // AuthPort is the port number on which authenticated APIs are provided.
AuthPort int `toml:",omitempty"` AuthPort int `toml:",omitempty"`
// AuthVirtualHosts is the list of virtual hostnames which are allowed on incoming requests
// for the authenticated api. This is by default {'localhost'}.
AuthVirtualHosts []string `toml:",omitempty"`
// WSHost is the host interface on which to start the websocket RPC server. If // WSHost is the host interface on which to start the websocket RPC server. If
// this field is empty, no websocket API endpoint will be started. // this field is empty, no websocket API endpoint will be started.
WSHost string WSHost string
......
...@@ -50,8 +50,9 @@ var ( ...@@ -50,8 +50,9 @@ var (
var DefaultConfig = Config{ var DefaultConfig = Config{
DataDir: DefaultDataDir(), DataDir: DefaultDataDir(),
HTTPPort: DefaultHTTPPort, HTTPPort: DefaultHTTPPort,
AuthHost: DefaultAuthHost, AuthAddr: DefaultAuthHost,
AuthPort: DefaultAuthPort, AuthPort: DefaultAuthPort,
AuthVirtualHosts: DefaultAuthVhosts,
HTTPModules: []string{"net", "web3"}, HTTPModules: []string{"net", "web3"},
HTTPVirtualHosts: []string{"localhost"}, HTTPVirtualHosts: []string{"localhost"},
HTTPTimeouts: rpc.DefaultHTTPTimeouts, HTTPTimeouts: rpc.DefaultHTTPTimeouts,
......
...@@ -439,12 +439,12 @@ func (n *Node) startRPC() error { ...@@ -439,12 +439,12 @@ func (n *Node) startRPC() error {
initAuth := func(apis []rpc.API, port int, secret []byte) error { initAuth := func(apis []rpc.API, port int, secret []byte) error {
// Enable auth via HTTP // Enable auth via HTTP
server := n.httpAuth server := n.httpAuth
if err := server.setListenAddr(n.config.AuthHost, port); err != nil { if err := server.setListenAddr(n.config.AuthAddr, port); err != nil {
return err return err
} }
if err := server.enableRPC(apis, httpConfig{ if err := server.enableRPC(apis, httpConfig{
CorsAllowedOrigins: DefaultAuthCors, CorsAllowedOrigins: DefaultAuthCors,
Vhosts: DefaultAuthVhosts, Vhosts: n.config.AuthVirtualHosts,
Modules: DefaultAuthModules, Modules: DefaultAuthModules,
prefix: DefaultAuthPrefix, prefix: DefaultAuthPrefix,
jwtSecret: secret, jwtSecret: secret,
...@@ -454,7 +454,7 @@ func (n *Node) startRPC() error { ...@@ -454,7 +454,7 @@ func (n *Node) startRPC() error {
servers = append(servers, server) servers = append(servers, server)
// Enable auth via WS // Enable auth via WS
server = n.wsServerForPort(port, true) server = n.wsServerForPort(port, true)
if err := server.setListenAddr(n.config.AuthHost, port); err != nil { if err := server.setListenAddr(n.config.AuthAddr, port); err != nil {
return err return err
} }
if err := server.enableWS(apis, wsConfig{ if err := server.enableWS(apis, wsConfig{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment