diff --git a/beacon/light/api/portal_api.go b/beacon/light/api/portal_api.go deleted file mode 100644 index cd532553c6a7..000000000000 --- a/beacon/light/api/portal_api.go +++ /dev/null @@ -1,39 +0,0 @@ -package api - -import ( - "time" - - "github.com/ethereum/go-ethereum/portalnetwork/beacon" - "github.com/protolambda/zrnt/eth2/beacon/capella" - zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common" - "github.com/protolambda/ztyp/tree" -) - -type PortalLightApi struct { - bn *beacon.BeaconNetwork -} - -func NewPortalLightApi() *PortalLightApi { - return &PortalLightApi{} -} - -func (api *PortalLightApi) GetUpdates(firstPeriod, count uint64) (beacon.LightClientUpdateRange, error) { - return api.bn.GetUpdates(firstPeriod, count) -} - -func (api *PortalLightApi) GetCheckpointData(checkpointHash tree.Root) (*capella.LightClientBootstrap, error) { - return api.bn.GetCheckpointData(checkpointHash) -} - -func (api *PortalLightApi) GetFinalityData() (*capella.LightClientFinalityUpdate, error) { - expectedCurrentSlot := api.bn.Spec.TimeToSlot(zrntcommon.Timestamp(time.Now().Unix()), zrntcommon.Timestamp(beacon.BeaconGenesisTime)) - recentEpochStart := expectedCurrentSlot - (expectedCurrentSlot % api.bn.Spec.SLOTS_PER_EPOCH) + 1 - - return api.bn.GetFinalityUpdate(uint64(recentEpochStart)) -} - -func (api *PortalLightApi) GetOptimisticData() (*capella.LightClientOptimisticUpdate, error) { - expectedCurrentSlot := api.bn.Spec.TimeToSlot(zrntcommon.Timestamp(time.Now().Unix()), zrntcommon.Timestamp(beacon.BeaconGenesisTime)) - - return api.bn.GetOptimisticUpdate(uint64(expectedCurrentSlot)) -} diff --git a/portalnetwork/beacon/light_client.go b/portalnetwork/beacon/light_client.go index 52bfc186918f..4e154f493cbc 100644 --- a/portalnetwork/beacon/light_client.go +++ b/portalnetwork/beacon/light_client.go @@ -1,8 +1,16 @@ package beacon import ( + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/protolambda/zrnt/eth2/beacon/altair" "github.com/protolambda/zrnt/eth2/beacon/capella" "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/zrnt/eth2/util/merkle" + "github.com/protolambda/ztyp/tree" "github.com/protolambda/ztyp/view" ) @@ -30,6 +38,7 @@ type ConsensusLightClient struct { InitialCheckpoint common.Root LastCheckpoint common.Root Config Config + Logger log.Logger } type Config struct { @@ -51,3 +60,78 @@ type ChainConfig struct { GenesisTime uint64 GenesisRoot common.Root } + +//lint:ignore U1000 placeholder function +func (c *ConsensusLightClient) bootstrap() error { + bootstrap, err := c.API.GetCheckpointData(c.InitialCheckpoint) + if err != nil { + return err + } + + isValid := c.isValidCheckpoint(bootstrap.Header.Beacon.Slot) + if !isValid { + if c.Config.StrictCheckpointAge { + return errors.New("checkpoint is too old") + } else { + c.Logger.Warn("checkpoint is too old") + } + } + + committeeValid := c.isCurrentCommitteeProofValid(bootstrap.Header.Beacon, bootstrap.CurrentSyncCommittee, bootstrap.CurrentSyncCommitteeBranch) + + headerHash := bootstrap.Header.Beacon.HashTreeRoot(tree.GetHashFn()).String() + expectedHash := c.InitialCheckpoint.String() + + headerValid := headerHash == expectedHash + + if !headerValid { + return fmt.Errorf("header hash %s does not match expected hash %s", headerHash, expectedHash) + } + + if !committeeValid { + return errors.New("committee proof is invalid") + } + + c.Store = LightClientStore{ + FinalizedHeader: bootstrap.Header.Beacon, + CurrentSyncCommittee: bootstrap.CurrentSyncCommittee, + OptimisticHeader: bootstrap.Header.Beacon, + PreviousMaxActiveParticipants: view.Uint64View(0), + CurrentMaxActiveParticipants: view.Uint64View(0), + } + + return nil +} + +func (c *ConsensusLightClient) isValidCheckpoint(blockHashSlot common.Slot) bool { + currentSlot := c.expectedCurrentSlot() + currentSlotTimestamp, err := c.slotTimestamp(currentSlot) + if err != nil { + return false + } + blockHashSlotTimestamp, err := c.slotTimestamp(blockHashSlot) + if err != nil { + return false + } + + slotAge := currentSlotTimestamp - blockHashSlotTimestamp + + return uint64(slotAge) < c.Config.MaxCheckpointAge +} + +func (c *ConsensusLightClient) expectedCurrentSlot() common.Slot { + return c.Config.Spec.TimeToSlot(common.Timestamp(time.Now().Unix()), common.Timestamp(c.Config.ChainConfig.GenesisTime)) +} + +func (c *ConsensusLightClient) slotTimestamp(slot common.Slot) (common.Timestamp, error) { + atSlot, err := c.Config.Spec.TimeAtSlot(slot, common.Timestamp(c.Config.ChainConfig.GenesisTime)) + if err != nil { + return 0, err + } + + return atSlot, nil +} + +func (c *ConsensusLightClient) isCurrentCommitteeProofValid(attestedHeader common.BeaconBlockHeader, currentCommittee common.SyncCommittee, currentCommitteeBranch altair.SyncCommitteeProofBranch) bool { + return merkle.VerifyMerkleBranch(currentCommittee.HashTreeRoot(c.Config.Spec, tree.GetHashFn()), currentCommitteeBranch[:], 5, 22, attestedHeader.StateRoot) +}