diff --git a/beacon/blsync/client.go b/beacon/blsync/client.go index 39a1c6ea76..3c93754d3d 100644 --- a/beacon/blsync/client.go +++ b/beacon/blsync/client.go @@ -17,25 +17,22 @@ package blsync import ( - "strings" - "github.com/ethereum/go-ethereum/beacon/light" "github.com/ethereum/go-ethereum/beacon/light/api" "github.com/ethereum/go-ethereum/beacon/light/request" "github.com/ethereum/go-ethereum/beacon/light/sync" + "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/types" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "github.com/urfave/cli/v2" ) type Client struct { urls []string customHeader map[string]string - chainConfig *lightClientConfig + config *params.ClientConfig scheduler *request.Scheduler blockSync *beaconBlockSync engineRPC *rpc.Client @@ -44,34 +41,18 @@ type Client struct { engineClient *engineClient } -func NewClient(ctx *cli.Context) *Client { - if !ctx.IsSet(utils.BeaconApiFlag.Name) { - utils.Fatalf("Beacon node light client API URL not specified") - } - var ( - chainConfig = makeChainConfig(ctx) - customHeader = make(map[string]string) - ) - for _, s := range ctx.StringSlice(utils.BeaconApiHeaderFlag.Name) { - kv := strings.Split(s, ":") - if len(kv) != 2 { - utils.Fatalf("Invalid custom API header entry: %s", s) - } - customHeader[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1]) - } - +func NewClient(config params.ClientConfig) *Client { // create data structures var ( db = memorydb.New() - threshold = ctx.Int(utils.BeaconThresholdFlag.Name) - committeeChain = light.NewCommitteeChain(db, chainConfig.ChainConfig, threshold, !ctx.Bool(utils.BeaconNoFilterFlag.Name)) - headTracker = light.NewHeadTracker(committeeChain, threshold) + committeeChain = light.NewCommitteeChain(db, &config.ChainConfig, config.Threshold, !config.NoFilter) + headTracker = light.NewHeadTracker(committeeChain, config.Threshold) ) headSync := sync.NewHeadSync(headTracker, committeeChain) // set up scheduler and sync modules scheduler := request.NewScheduler() - checkpointInit := sync.NewCheckpointInit(committeeChain, chainConfig.Checkpoint) + checkpointInit := sync.NewCheckpointInit(committeeChain, config.Checkpoint) forwardSync := sync.NewForwardUpdateSync(committeeChain) beaconBlockSync := newBeaconBlockSync(headTracker) scheduler.RegisterTarget(headTracker) @@ -83,9 +64,9 @@ func NewClient(ctx *cli.Context) *Client { return &Client{ scheduler: scheduler, - urls: ctx.StringSlice(utils.BeaconApiFlag.Name), - customHeader: customHeader, - chainConfig: &chainConfig, + urls: config.Apis, + customHeader: config.CustomHeader, + config: &config, blockSync: beaconBlockSync, } } @@ -97,7 +78,7 @@ func (c *Client) SetEngineRPC(engine *rpc.Client) { func (c *Client) Start() error { headCh := make(chan types.ChainHeadEvent, 16) c.chainHeadSub = c.blockSync.SubscribeChainHead(headCh) - c.engineClient = startEngineClient(c.chainConfig, c.engineRPC, headCh) + c.engineClient = startEngineClient(c.config, c.engineRPC, headCh) c.scheduler.Start() for _, url := range c.urls { diff --git a/beacon/blsync/config.go b/beacon/blsync/config.go deleted file mode 100644 index 828c14f898..0000000000 --- a/beacon/blsync/config.go +++ /dev/null @@ -1,129 +0,0 @@ -// 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 . - -package blsync - -import ( - "github.com/ethereum/go-ethereum/beacon/types" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/urfave/cli/v2" -) - -// lightClientConfig contains beacon light client configuration -type lightClientConfig struct { - *types.ChainConfig - Checkpoint common.Hash -} - -var ( - MainnetConfig = lightClientConfig{ - ChainConfig: (&types.ChainConfig{ - GenesisValidatorsRoot: common.HexToHash("0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"), - GenesisTime: 1606824023, - }). - AddFork("GENESIS", 0, []byte{0, 0, 0, 0}). - AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}). - AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}). - AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}). - AddFork("DENEB", 269568, []byte{4, 0, 0, 0}), - Checkpoint: common.HexToHash("0x6509b691f4de4f7b083f2784938fd52f0e131675432b3fd85ea549af9aebd3d0"), - } - - SepoliaConfig = lightClientConfig{ - ChainConfig: (&types.ChainConfig{ - GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"), - GenesisTime: 1655733600, - }). - AddFork("GENESIS", 0, []byte{144, 0, 0, 105}). - AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}). - AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}). - AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}). - AddFork("DENEB", 132608, []byte{144, 0, 0, 115}), - Checkpoint: common.HexToHash("0x456e85f5608afab3465a0580bff8572255f6d97af0c5f939e3f7536b5edb2d3f"), - } - - HoleskyConfig = lightClientConfig{ - ChainConfig: (&types.ChainConfig{ - GenesisValidatorsRoot: common.HexToHash("0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1"), - GenesisTime: 1695902400, - }). - AddFork("GENESIS", 0, []byte{1, 1, 112, 0}). - AddFork("ALTAIR", 0, []byte{2, 1, 112, 0}). - AddFork("BELLATRIX", 0, []byte{3, 1, 112, 0}). - AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}). - AddFork("DENEB", 29696, []byte{5, 1, 112, 0}), - Checkpoint: common.HexToHash("0x6456a1317f54d4b4f2cb5bc9d153b5af0988fe767ef0609f0236cf29030bcff7"), - } -) - -func makeChainConfig(ctx *cli.Context) lightClientConfig { - var config lightClientConfig - customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name) - utils.CheckExclusive(ctx, utils.MainnetFlag, utils.SepoliaFlag, utils.HoleskyFlag, utils.BeaconConfigFlag) - switch { - case ctx.Bool(utils.MainnetFlag.Name): - config = MainnetConfig - case ctx.Bool(utils.SepoliaFlag.Name): - config = SepoliaConfig - case ctx.Bool(utils.HoleskyFlag.Name): - config = HoleskyConfig - default: - if !customConfig { - config = MainnetConfig - } - } - // Genesis root and time should always be specified together with custom chain config - if customConfig { - if !ctx.IsSet(utils.BeaconGenesisRootFlag.Name) { - utils.Fatalf("Custom beacon chain config is specified but genesis root is missing") - } - if !ctx.IsSet(utils.BeaconGenesisTimeFlag.Name) { - utils.Fatalf("Custom beacon chain config is specified but genesis time is missing") - } - if !ctx.IsSet(utils.BeaconCheckpointFlag.Name) { - utils.Fatalf("Custom beacon chain config is specified but checkpoint is missing") - } - config.ChainConfig = &types.ChainConfig{ - GenesisTime: ctx.Uint64(utils.BeaconGenesisTimeFlag.Name), - } - if c, err := hexutil.Decode(ctx.String(utils.BeaconGenesisRootFlag.Name)); err == nil && len(c) <= 32 { - copy(config.GenesisValidatorsRoot[:len(c)], c) - } else { - utils.Fatalf("Invalid hex string", "beacon.genesis.gvroot", ctx.String(utils.BeaconGenesisRootFlag.Name), "error", err) - } - if err := config.ChainConfig.LoadForks(ctx.String(utils.BeaconConfigFlag.Name)); err != nil { - utils.Fatalf("Could not load beacon chain config file", "file name", ctx.String(utils.BeaconConfigFlag.Name), "error", err) - } - } else { - if ctx.IsSet(utils.BeaconGenesisRootFlag.Name) { - utils.Fatalf("Genesis root is specified but custom beacon chain config is missing") - } - if ctx.IsSet(utils.BeaconGenesisTimeFlag.Name) { - utils.Fatalf("Genesis time is specified but custom beacon chain config is missing") - } - } - // Checkpoint is required with custom chain config and is optional with pre-defined config - if ctx.IsSet(utils.BeaconCheckpointFlag.Name) { - if c, err := hexutil.Decode(ctx.String(utils.BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 { - copy(config.Checkpoint[:len(c)], c) - } else { - utils.Fatalf("Invalid hex string", "beacon.checkpoint", ctx.String(utils.BeaconCheckpointFlag.Name), "error", err) - } - } - return config -} diff --git a/beacon/blsync/engineclient.go b/beacon/blsync/engineclient.go index fb8f77f32b..c6569fdbde 100644 --- a/beacon/blsync/engineclient.go +++ b/beacon/blsync/engineclient.go @@ -23,6 +23,7 @@ import ( "time" "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/common" ctypes "github.com/ethereum/go-ethereum/core/types" @@ -31,14 +32,14 @@ import ( ) type engineClient struct { - config *lightClientConfig + config *params.ClientConfig rpc *rpc.Client rootCtx context.Context cancelRoot context.CancelFunc wg sync.WaitGroup } -func startEngineClient(config *lightClientConfig, rpc *rpc.Client, headCh <-chan types.ChainHeadEvent) *engineClient { +func startEngineClient(config *params.ClientConfig, rpc *rpc.Client, headCh <-chan types.ChainHeadEvent) *engineClient { ctx, cancel := context.WithCancel(context.Background()) ec := &engineClient{ config: config, diff --git a/beacon/light/committee_chain.go b/beacon/light/committee_chain.go index 778cd3028f..4fa87785c0 100644 --- a/beacon/light/committee_chain.go +++ b/beacon/light/committee_chain.go @@ -76,24 +76,24 @@ type CommitteeChain struct { unixNano func() int64 // system clock (simulated clock in tests) sigVerifier committeeSigVerifier // BLS sig verifier (dummy verifier in tests) - config *types.ChainConfig + config *params.ChainConfig minimumUpdateScore types.UpdateScore enforceTime bool // enforceTime specifies whether the age of a signed header should be checked } // NewCommitteeChain creates a new CommitteeChain. -func NewCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool) *CommitteeChain { +func NewCommitteeChain(db ethdb.KeyValueStore, config *params.ChainConfig, signerThreshold int, enforceTime bool) *CommitteeChain { return newCommitteeChain(db, config, signerThreshold, enforceTime, blsVerifier{}, &mclock.System{}, func() int64 { return time.Now().UnixNano() }) } // NewTestCommitteeChain creates a new CommitteeChain for testing. -func NewTestCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool, clock *mclock.Simulated) *CommitteeChain { +func NewTestCommitteeChain(db ethdb.KeyValueStore, config *params.ChainConfig, signerThreshold int, enforceTime bool, clock *mclock.Simulated) *CommitteeChain { return newCommitteeChain(db, config, signerThreshold, enforceTime, dummyVerifier{}, clock, func() int64 { return int64(clock.Now()) }) } // newCommitteeChain creates a new CommitteeChain with the option of replacing the // clock source and signature verification for testing purposes. -func newCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool, sigVerifier committeeSigVerifier, clock mclock.Clock, unixNano func() int64) *CommitteeChain { +func newCommitteeChain(db ethdb.KeyValueStore, config *params.ChainConfig, signerThreshold int, enforceTime bool, sigVerifier committeeSigVerifier, clock mclock.Clock, unixNano func() int64) *CommitteeChain { s := &CommitteeChain{ committeeCache: lru.NewCache[uint64, syncCommittee](10), db: db, @@ -505,7 +505,7 @@ func (s *CommitteeChain) verifySignedHeader(head types.SignedHeader) (bool, time if committee == nil { return false, age, nil } - if signingRoot, err := s.config.Forks.SigningRoot(head.Header); err == nil { + if signingRoot, err := s.config.Forks.SigningRoot(head.Header.Epoch(), head.Header.Hash()); err == nil { return s.sigVerifier.verifySignature(committee, signingRoot, &head.Signature), age, nil } return false, age, nil diff --git a/beacon/light/committee_chain_test.go b/beacon/light/committee_chain_test.go index 57b6d7175c..17ba135905 100644 --- a/beacon/light/committee_chain_test.go +++ b/beacon/light/committee_chain_test.go @@ -31,15 +31,15 @@ var ( testGenesis = newTestGenesis() testGenesis2 = newTestGenesis() - tfBase = newTestForks(testGenesis, types.Forks{ - &types.Fork{Epoch: 0, Version: []byte{0}}, + tfBase = newTestForks(testGenesis, params.Forks{ + ¶ms.Fork{Epoch: 0, Version: []byte{0}}, }) - tfAlternative = newTestForks(testGenesis, types.Forks{ - &types.Fork{Epoch: 0, Version: []byte{0}}, - &types.Fork{Epoch: 0x700, Version: []byte{1}}, + tfAlternative = newTestForks(testGenesis, params.Forks{ + ¶ms.Fork{Epoch: 0, Version: []byte{0}}, + ¶ms.Fork{Epoch: 0x700, Version: []byte{1}}, }) - tfAnotherGenesis = newTestForks(testGenesis2, types.Forks{ - &types.Fork{Epoch: 0, Version: []byte{0}}, + tfAnotherGenesis = newTestForks(testGenesis2, params.Forks{ + ¶ms.Fork{Epoch: 0, Version: []byte{0}}, }) tcBase = newTestCommitteeChain(nil, tfBase, true, 0, 10, 400, false) @@ -226,13 +226,13 @@ type committeeChainTest struct { t *testing.T db *memorydb.Database clock *mclock.Simulated - config types.ChainConfig + config params.ChainConfig signerThreshold int enforceTime bool chain *CommitteeChain } -func newCommitteeChainTest(t *testing.T, config types.ChainConfig, signerThreshold int, enforceTime bool) *committeeChainTest { +func newCommitteeChainTest(t *testing.T, config params.ChainConfig, signerThreshold int, enforceTime bool) *committeeChainTest { c := &committeeChainTest{ t: t, db: memorydb.New(), @@ -298,20 +298,20 @@ func (c *committeeChainTest) verifyRange(tc *testCommitteeChain, begin, end uint c.verifySignedHeader(tc, float64(end)+1.5, false) } -func newTestGenesis() types.ChainConfig { - var config types.ChainConfig +func newTestGenesis() params.ChainConfig { + var config params.ChainConfig rand.Read(config.GenesisValidatorsRoot[:]) return config } -func newTestForks(config types.ChainConfig, forks types.Forks) types.ChainConfig { +func newTestForks(config params.ChainConfig, forks params.Forks) params.ChainConfig { for _, fork := range forks { config.AddFork(fork.Name, fork.Epoch, fork.Version) } return config } -func newTestCommitteeChain(parent *testCommitteeChain, config types.ChainConfig, newCommittees bool, begin, end int, signerCount int, finalizedHeader bool) *testCommitteeChain { +func newTestCommitteeChain(parent *testCommitteeChain, config params.ChainConfig, newCommittees bool, begin, end int, signerCount int, finalizedHeader bool) *testCommitteeChain { tc := &testCommitteeChain{ config: config, } @@ -337,7 +337,7 @@ type testPeriod struct { type testCommitteeChain struct { periods []testPeriod - config types.ChainConfig + config params.ChainConfig } func (tc *testCommitteeChain) fillCommittees(begin, end int) { diff --git a/beacon/light/test_helpers.go b/beacon/light/test_helpers.go index f537d963a6..7bd19ca0ad 100644 --- a/beacon/light/test_helpers.go +++ b/beacon/light/test_helpers.go @@ -33,7 +33,7 @@ func GenerateTestCommittee() *types.SerializedSyncCommittee { return s } -func GenerateTestUpdate(config *types.ChainConfig, period uint64, committee, nextCommittee *types.SerializedSyncCommittee, signerCount int, finalizedHeader bool) *types.LightClientUpdate { +func GenerateTestUpdate(config *params.ChainConfig, period uint64, committee, nextCommittee *types.SerializedSyncCommittee, signerCount int, finalizedHeader bool) *types.LightClientUpdate { update := new(types.LightClientUpdate) update.NextSyncCommitteeRoot = nextCommittee.Root() var attestedHeader types.Header @@ -48,9 +48,9 @@ func GenerateTestUpdate(config *types.ChainConfig, period uint64, committee, nex return update } -func GenerateTestSignedHeader(header types.Header, config *types.ChainConfig, committee *types.SerializedSyncCommittee, signatureSlot uint64, signerCount int) types.SignedHeader { +func GenerateTestSignedHeader(header types.Header, config *params.ChainConfig, committee *types.SerializedSyncCommittee, signatureSlot uint64, signerCount int) types.SignedHeader { bitmask := makeBitmask(signerCount) - signingRoot, _ := config.Forks.SigningRoot(header) + signingRoot, _ := config.Forks.SigningRoot(header.Epoch(), header.Hash()) c, _ := dummyVerifier{}.deserializeSyncCommittee(committee) return types.SignedHeader{ Header: header, diff --git a/beacon/types/config.go b/beacon/params/config.go similarity index 94% rename from beacon/types/config.go rename to beacon/params/config.go index 7706e85f6c..be2a40f171 100644 --- a/beacon/types/config.go +++ b/beacon/params/config.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package types +package params import ( "crypto/sha256" @@ -39,81 +39,13 @@ const syncCommitteeDomain = 7 var knownForks = []string{"GENESIS", "ALTAIR", "BELLATRIX", "CAPELLA", "DENEB"} -// Fork describes a single beacon chain fork and also stores the calculated -// signature domain used after this fork. -type Fork struct { - // Name of the fork in the chain config (config.yaml) file - Name string - - // Epoch when given fork version is activated - Epoch uint64 - - // Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types - Version []byte - - // index in list of known forks or MaxInt if unknown - knownIndex int - - // calculated by computeDomain, based on fork version and genesis validators root - domain merkle.Value -} - -// computeDomain returns the signature domain based on the given fork version -// and genesis validator set root. -func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) { - var ( - hasher = sha256.New() - forkVersion32 merkle.Value - forkDataRoot merkle.Value - ) - copy(forkVersion32[:], f.Version) - hasher.Write(forkVersion32[:]) - hasher.Write(genesisValidatorsRoot[:]) - hasher.Sum(forkDataRoot[:0]) - - f.domain[0] = syncCommitteeDomain - copy(f.domain[4:], forkDataRoot[:28]) -} - -// Forks is the list of all beacon chain forks in the chain configuration. -type Forks []*Fork - -// domain returns the signature domain for the given epoch (assumes that domains -// have already been calculated). -func (f Forks) domain(epoch uint64) (merkle.Value, error) { - for i := len(f) - 1; i >= 0; i-- { - if epoch >= f[i].Epoch { - return f[i].domain, nil - } - } - return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch) -} - -// SigningRoot calculates the signing root of the given header. -func (f Forks) SigningRoot(header Header) (common.Hash, error) { - domain, err := f.domain(header.Epoch()) - if err != nil { - return common.Hash{}, err - } - var ( - signingRoot common.Hash - headerHash = header.Hash() - hasher = sha256.New() - ) - hasher.Write(headerHash[:]) - hasher.Write(domain[:]) - hasher.Sum(signingRoot[:0]) - - return signingRoot, nil -} - -func (f Forks) Len() int { return len(f) } -func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f Forks) Less(i, j int) bool { - if f[i].Epoch != f[j].Epoch { - return f[i].Epoch < f[j].Epoch - } - return f[i].knownIndex < f[j].knownIndex +// ClientConfig contains beacon light client configuration. +type ClientConfig struct { + ChainConfig + Apis []string + CustomHeader map[string]string + Threshold int + NoFilter bool } // ChainConfig contains the beacon chain configuration. @@ -121,6 +53,7 @@ type ChainConfig struct { GenesisTime uint64 // Unix timestamp of slot 0 GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation Forks Forks + Checkpoint common.Hash } // ForkAtEpoch returns the latest active fork at the given epoch. @@ -202,3 +135,79 @@ func (c *ChainConfig) LoadForks(path string) error { } return nil } + +// Fork describes a single beacon chain fork and also stores the calculated +// signature domain used after this fork. +type Fork struct { + // Name of the fork in the chain config (config.yaml) file + Name string + + // Epoch when given fork version is activated + Epoch uint64 + + // Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types + Version []byte + + // index in list of known forks or MaxInt if unknown + knownIndex int + + // calculated by computeDomain, based on fork version and genesis validators root + domain merkle.Value +} + +// computeDomain returns the signature domain based on the given fork version +// and genesis validator set root. +func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) { + var ( + hasher = sha256.New() + forkVersion32 merkle.Value + forkDataRoot merkle.Value + ) + copy(forkVersion32[:], f.Version) + hasher.Write(forkVersion32[:]) + hasher.Write(genesisValidatorsRoot[:]) + hasher.Sum(forkDataRoot[:0]) + + f.domain[0] = syncCommitteeDomain + copy(f.domain[4:], forkDataRoot[:28]) +} + +// Forks is the list of all beacon chain forks in the chain configuration. +type Forks []*Fork + +// domain returns the signature domain for the given epoch (assumes that domains +// have already been calculated). +func (f Forks) domain(epoch uint64) (merkle.Value, error) { + for i := len(f) - 1; i >= 0; i-- { + if epoch >= f[i].Epoch { + return f[i].domain, nil + } + } + return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch) +} + +// SigningRoot calculates the signing root of the given header. +func (f Forks) SigningRoot(epoch uint64, root common.Hash) (common.Hash, error) { + domain, err := f.domain(epoch) + if err != nil { + return common.Hash{}, err + } + var ( + signingRoot common.Hash + hasher = sha256.New() + ) + hasher.Write(root[:]) + hasher.Write(domain[:]) + hasher.Sum(signingRoot[:0]) + + return signingRoot, nil +} + +func (f Forks) Len() int { return len(f) } +func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f Forks) Less(i, j int) bool { + if f[i].Epoch != f[j].Epoch { + return f[i].Epoch < f[j].Epoch + } + return f[i].knownIndex < f[j].knownIndex +} diff --git a/beacon/params/networks.go b/beacon/params/networks.go new file mode 100644 index 0000000000..5b00b27953 --- /dev/null +++ b/beacon/params/networks.go @@ -0,0 +1,56 @@ +// Copyright 2016 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 . + +package params + +import ( + "github.com/ethereum/go-ethereum/common" +) + +var ( + MainnetLightConfig = (&ChainConfig{ + GenesisValidatorsRoot: common.HexToHash("0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"), + GenesisTime: 1606824023, + Checkpoint: common.HexToHash("0x6509b691f4de4f7b083f2784938fd52f0e131675432b3fd85ea549af9aebd3d0"), + }). + AddFork("GENESIS", 0, []byte{0, 0, 0, 0}). + AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}). + AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}). + AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}). + AddFork("DENEB", 269568, []byte{4, 0, 0, 0}) + + SepoliaLightConfig = (&ChainConfig{ + GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"), + GenesisTime: 1655733600, + Checkpoint: common.HexToHash("0x456e85f5608afab3465a0580bff8572255f6d97af0c5f939e3f7536b5edb2d3f"), + }). + AddFork("GENESIS", 0, []byte{144, 0, 0, 105}). + AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}). + AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}). + AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}). + AddFork("DENEB", 132608, []byte{144, 0, 0, 115}) + + HoleskyLightConfig = (&ChainConfig{ + GenesisValidatorsRoot: common.HexToHash("0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1"), + GenesisTime: 1695902400, + Checkpoint: common.HexToHash("0x6456a1317f54d4b4f2cb5bc9d153b5af0988fe767ef0609f0236cf29030bcff7"), + }). + AddFork("GENESIS", 0, []byte{1, 1, 112, 0}). + AddFork("ALTAIR", 0, []byte{2, 1, 112, 0}). + AddFork("BELLATRIX", 0, []byte{3, 1, 112, 0}). + AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}). + AddFork("DENEB", 29696, []byte{5, 1, 112, 0}) +) diff --git a/cmd/blsync/main.go b/cmd/blsync/main.go index 6a35e9d16a..d74e1496cd 100644 --- a/cmd/blsync/main.go +++ b/cmd/blsync/main.go @@ -70,7 +70,7 @@ func main() { func sync(ctx *cli.Context) error { // set up blsync - client := blsync.NewClient(ctx) + client := blsync.NewClient(utils.MakeBeaconLightConfig(ctx)) client.SetEngineRPC(makeRPCClient(ctx)) client.Start() diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 842c1c2347..17ed9fb606 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -240,7 +240,7 @@ func makeFullNode(ctx *cli.Context) *node.Node { // Start blsync mode. srv := rpc.NewServer() srv.RegisterName("engine", catalyst.NewConsensusAPI(eth)) - blsyncer := blsync.NewClient(ctx) + blsyncer := blsync.NewClient(utils.MakeBeaconLightConfig(ctx)) blsyncer.SetEngineRPC(rpc.DialInProc(srv)) stack.RegisterLifecycle(blsyncer) } else { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c788b85089..4eef66ebc6 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -40,6 +40,7 @@ import ( bparams "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool/blobpool" @@ -1889,6 +1890,81 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } } +// MakeBeaconLightConfig constructs a beacon light client config based on the +// related command line flags. +func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig { + var config bparams.ClientConfig + customConfig := ctx.IsSet(BeaconConfigFlag.Name) + CheckExclusive(ctx, MainnetFlag, SepoliaFlag, HoleskyFlag, BeaconConfigFlag) + switch { + case ctx.Bool(MainnetFlag.Name): + config.ChainConfig = *bparams.MainnetLightConfig + case ctx.Bool(SepoliaFlag.Name): + config.ChainConfig = *bparams.SepoliaLightConfig + case ctx.Bool(HoleskyFlag.Name): + config.ChainConfig = *bparams.HoleskyLightConfig + default: + if !customConfig { + config.ChainConfig = *bparams.MainnetLightConfig + } + } + // Genesis root and time should always be specified together with custom chain config + if customConfig { + if !ctx.IsSet(BeaconGenesisRootFlag.Name) { + Fatalf("Custom beacon chain config is specified but genesis root is missing") + } + if !ctx.IsSet(BeaconGenesisTimeFlag.Name) { + Fatalf("Custom beacon chain config is specified but genesis time is missing") + } + if !ctx.IsSet(BeaconCheckpointFlag.Name) { + Fatalf("Custom beacon chain config is specified but checkpoint is missing") + } + config.ChainConfig = bparams.ChainConfig{ + GenesisTime: ctx.Uint64(BeaconGenesisTimeFlag.Name), + } + if c, err := hexutil.Decode(ctx.String(BeaconGenesisRootFlag.Name)); err == nil && len(c) <= 32 { + copy(config.GenesisValidatorsRoot[:len(c)], c) + } else { + Fatalf("Invalid hex string", "beacon.genesis.gvroot", ctx.String(BeaconGenesisRootFlag.Name), "error", err) + } + configFile := ctx.String(BeaconConfigFlag.Name) + if err := config.ChainConfig.LoadForks(configFile); err != nil { + Fatalf("Could not load beacon chain config", "file", configFile, "error", err) + } + log.Info("Using custom beacon chain config", "file", configFile) + } else { + if ctx.IsSet(BeaconGenesisRootFlag.Name) { + Fatalf("Genesis root is specified but custom beacon chain config is missing") + } + if ctx.IsSet(BeaconGenesisTimeFlag.Name) { + Fatalf("Genesis time is specified but custom beacon chain config is missing") + } + } + // Checkpoint is required with custom chain config and is optional with pre-defined config + if ctx.IsSet(BeaconCheckpointFlag.Name) { + if c, err := hexutil.Decode(ctx.String(BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 { + copy(config.Checkpoint[:len(c)], c) + } else { + Fatalf("Invalid hex string", "beacon.checkpoint", ctx.String(BeaconCheckpointFlag.Name), "error", err) + } + } + config.Apis = ctx.StringSlice(BeaconApiFlag.Name) + if config.Apis == nil { + Fatalf("Beacon node light client API URL not specified") + } + config.CustomHeader = make(map[string]string) + for _, s := range ctx.StringSlice(BeaconApiHeaderFlag.Name) { + kv := strings.Split(s, ":") + if len(kv) != 2 { + Fatalf("Invalid custom API header entry: %s", s) + } + config.CustomHeader[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1]) + } + config.Threshold = ctx.Int(BeaconThresholdFlag.Name) + config.NoFilter = ctx.Bool(BeaconNoFilterFlag.Name) + return config +} + // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if // no URLs are set. func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) {