From 9fd49b01c988131d952f3f4fd1ea2162d596efee Mon Sep 17 00:00:00 2001 From: Zsolt Felfoldi Date: Wed, 29 Nov 2023 17:27:13 +0100 Subject: [PATCH] beacon/light: unexported fixed roots, moved checkpoint init into CommitteeChain --- beacon/light/checkpoint.go | 63 ---------------------- beacon/light/committee_chain.go | 65 +++++++++++++++-------- beacon/light/committee_chain_test.go | 8 +-- beacon/light/test_helpers.go | 4 +- beacon/types/{update.go => light_sync.go} | 18 +++++++ 5 files changed, 67 insertions(+), 91 deletions(-) delete mode 100644 beacon/light/checkpoint.go rename beacon/types/{update.go => light_sync.go} (88%) diff --git a/beacon/light/checkpoint.go b/beacon/light/checkpoint.go deleted file mode 100644 index 73fe97085a..0000000000 --- a/beacon/light/checkpoint.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2023 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 light - -import ( - "errors" - - "github.com/ethereum/go-ethereum/beacon/merkle" - "github.com/ethereum/go-ethereum/beacon/params" - "github.com/ethereum/go-ethereum/beacon/types" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" -) - -// CheckpointData contains a sync committee where light sync can be started, -// together with a proof through a beacon header and corresponding state. -// Note: CheckpointData is fetched from a server based on a known checkpoint hash. -type CheckpointData struct { - Header types.Header - CommitteeRoot common.Hash - Committee *types.SerializedSyncCommittee `rlp:"-"` - CommitteeBranch merkle.Values -} - -// Validate verifies the proof included in CheckpointData. -func (c *CheckpointData) Validate() error { - if c.CommitteeRoot != c.Committee.Root() { - return errors.New("wrong committee root") - } - return merkle.VerifyProof(c.Header.StateRoot, params.StateIndexSyncCommittee, c.CommitteeBranch, merkle.Value(c.CommitteeRoot)) -} - -// InitChain initializes a CommitteeChain based on the checkpoint. -// Note that the checkpoint is expected to be already validated. -func (c *CheckpointData) InitChain(chain *CommitteeChain) { - must := func(err error) { - if err != nil { - log.Error("Error initializing committee chain with checkpoint", "error", err) - } - } - period := c.Header.SyncPeriod() - must(chain.DeleteFixedCommitteeRootsFrom(period + 2)) - if chain.AddFixedCommitteeRoot(period, c.CommitteeRoot) != nil { - chain.Reset() - must(chain.AddFixedCommitteeRoot(period, c.CommitteeRoot)) - } - must(chain.AddFixedCommitteeRoot(period+1, common.Hash(c.CommitteeBranch[0]))) - must(chain.AddCommittee(period, c.Committee)) -} diff --git a/beacon/light/committee_chain.go b/beacon/light/committee_chain.go index 5dcc4fd478..937453bff9 100644 --- a/beacon/light/committee_chain.go +++ b/beacon/light/committee_chain.go @@ -45,7 +45,7 @@ var ( // CommitteeChain is a passive data structure that can validate, hold and update // a chain of beacon light sync committees and updates. It requires at least one // externally set fixed committee root at the beginning of the chain which can -// be set either based on a CheckpointData or a trusted source (a local beacon +// be set either based on a BootstrapData or a trusted source (a local beacon // full node). This makes the structure useful for both light client and light // server setups. // @@ -209,10 +209,45 @@ func (s *CommitteeChain) Reset() { } } -// AddFixedCommitteeRoot sets a fixed committee root at the given period. +// CheckpointInit initializes a CommitteeChain based on the checkpoint. +// Note: if the chain is already initialized and the committees proven by the +// checkpoint do match the existing chain then the chain is retained and the +// new checkpoint becomes fixed. +func (s *CommitteeChain) CheckpointInit(bootstrap *types.BootstrapData) error { + s.chainmu.Lock() + defer s.chainmu.Unlock() + + if err := bootstrap.Validate(); err != nil { + return err + } + + period := bootstrap.Header.SyncPeriod() + if err := s.deleteFixedCommitteeRootsFrom(period + 2); err != nil { + s.Reset() + return err + } + if s.addFixedCommitteeRoot(period, bootstrap.CommitteeRoot) != nil { + s.Reset() + if err := s.addFixedCommitteeRoot(period, bootstrap.CommitteeRoot); err != nil { + s.Reset() + return err + } + } + if err := s.addFixedCommitteeRoot(period+1, common.Hash(bootstrap.CommitteeBranch[0])); err != nil { + s.Reset() + return err + } + if err := s.addCommittee(period, bootstrap.Committee); err != nil { + s.Reset() + return err + } + return nil +} + +// addFixedCommitteeRoot sets a fixed committee root at the given period. // Note that the period where the first committee is added has to have a fixed -// root which can either come from a CheckpointData or a trusted source. -func (s *CommitteeChain) AddFixedCommitteeRoot(period uint64, root common.Hash) error { +// root which can either come from a BootstrapData or a trusted source. +func (s *CommitteeChain) addFixedCommitteeRoot(period uint64, root common.Hash) error { s.chainmu.Lock() defer s.chainmu.Unlock() @@ -260,10 +295,10 @@ func (s *CommitteeChain) AddFixedCommitteeRoot(period uint64, root common.Hash) return nil } -// DeleteFixedCommitteeRootsFrom deletes fixed roots starting from the given period. +// deleteFixedCommitteeRootsFrom deletes fixed roots starting from the given period. // It also maintains chain consistency, meaning that it also deletes updates and // committees if they are no longer supported by a valid update chain. -func (s *CommitteeChain) DeleteFixedCommitteeRootsFrom(period uint64) error { +func (s *CommitteeChain) deleteFixedCommitteeRootsFrom(period uint64) error { s.chainmu.Lock() defer s.chainmu.Unlock() @@ -305,15 +340,8 @@ func (s *CommitteeChain) deleteCommitteesFrom(batch ethdb.Batch, period uint64) } } -// GetCommittee returns the committee at the given period. -// Note: GetCommittee can be called either with locked or unlocked chain mutex. -func (s *CommitteeChain) GetCommittee(period uint64) *types.SerializedSyncCommittee { - committee, _ := s.committees.get(period) - return committee -} - -// AddCommittee adds a committee at the given period if possible. -func (s *CommitteeChain) AddCommittee(period uint64, committee *types.SerializedSyncCommittee) error { +// addCommittee adds a committee at the given period if possible. +func (s *CommitteeChain) addCommittee(period uint64, committee *types.SerializedSyncCommittee) error { s.chainmu.Lock() defer s.chainmu.Unlock() @@ -336,13 +364,6 @@ func (s *CommitteeChain) AddCommittee(period uint64, committee *types.Serialized return nil } -// GetUpdate returns the update at the given period. -// Note: GetUpdate can be called either with locked or unlocked chain mutex. -func (s *CommitteeChain) GetUpdate(period uint64) *types.LightClientUpdate { - update, _ := s.updates.get(period) - return update -} - // InsertUpdate adds a new update if possible. func (s *CommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommittee *types.SerializedSyncCommittee) error { s.chainmu.Lock() diff --git a/beacon/light/committee_chain_test.go b/beacon/light/committee_chain_test.go index fd7c624f03..60ea2a0efd 100644 --- a/beacon/light/committee_chain_test.go +++ b/beacon/light/committee_chain_test.go @@ -259,14 +259,14 @@ func (c *committeeChainTest) setClockPeriod(period float64) { } func (c *committeeChainTest) addFixedCommitteeRoot(tc *testCommitteeChain, period uint64, expErr error) { - if err := c.chain.AddFixedCommitteeRoot(period, tc.periods[period].committee.Root()); err != expErr { - c.t.Errorf("Incorrect error output from AddFixedCommitteeRoot at period %d (expected %v, got %v)", period, expErr, err) + if err := c.chain.addFixedCommitteeRoot(period, tc.periods[period].committee.Root()); err != expErr { + c.t.Errorf("Incorrect error output from addFixedCommitteeRoot at period %d (expected %v, got %v)", period, expErr, err) } } func (c *committeeChainTest) addCommittee(tc *testCommitteeChain, period uint64, expErr error) { - if err := c.chain.AddCommittee(period, tc.periods[period].committee); err != expErr { - c.t.Errorf("Incorrect error output from AddCommittee at period %d (expected %v, got %v)", period, expErr, err) + if err := c.chain.addCommittee(period, tc.periods[period].committee); err != expErr { + c.t.Errorf("Incorrect error output from addCommittee at period %d (expected %v, got %v)", period, expErr, err) } } diff --git a/beacon/light/test_helpers.go b/beacon/light/test_helpers.go index 8f7cc2bf61..f537d963a6 100644 --- a/beacon/light/test_helpers.go +++ b/beacon/light/test_helpers.go @@ -62,9 +62,9 @@ func GenerateTestSignedHeader(header types.Header, config *types.ChainConfig, co } } -func GenerateTestCheckpoint(period uint64, committee *types.SerializedSyncCommittee) *CheckpointData { +func GenerateTestCheckpoint(period uint64, committee *types.SerializedSyncCommittee) *types.BootstrapData { header, branch := makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexSyncCommittee, merkle.Value(committee.Root())) - return &CheckpointData{ + return &types.BootstrapData{ Header: header, Committee: committee, CommitteeRoot: committee.Root(), diff --git a/beacon/types/update.go b/beacon/types/light_sync.go similarity index 88% rename from beacon/types/update.go rename to beacon/types/light_sync.go index 06c1b61792..3284081e4d 100644 --- a/beacon/types/update.go +++ b/beacon/types/light_sync.go @@ -25,6 +25,24 @@ import ( "github.com/ethereum/go-ethereum/common" ) +// BootstrapData contains a sync committee where light sync can be started, +// together with a proof through a beacon header and corresponding state. +// Note: BootstrapData is fetched from a server based on a known checkpoint hash. +type BootstrapData struct { + Header Header + CommitteeRoot common.Hash + Committee *SerializedSyncCommittee `rlp:"-"` + CommitteeBranch merkle.Values +} + +// Validate verifies the proof included in BootstrapData. +func (c *BootstrapData) Validate() error { + if c.CommitteeRoot != c.Committee.Root() { + return errors.New("wrong committee root") + } + return merkle.VerifyProof(c.Header.StateRoot, params.StateIndexSyncCommittee, c.CommitteeBranch, merkle.Value(c.CommitteeRoot)) +} + // LightClientUpdate is a proof of the next sync committee root based on a header // signed by the sync committee of the given period. Optionally, the update can // prove quasi-finality by the signed header referring to a previous, finalized