From 23a502a2c348d3b61c5d5b8c1198fcc81395f902 Mon Sep 17 00:00:00 2001 From: Chen Kai <281165273grape@gmail.com> Date: Mon, 8 Apr 2024 21:51:16 +0800 Subject: [PATCH] feat:add light client types Signed-off-by: Chen Kai <281165273grape@gmail.com> --- beacon/light/api/portal_api.go | 64 +++++++++----------------- portalnetwork/beacon/beacon_network.go | 63 +++++++++++++++++++++---- portalnetwork/beacon/light_client.go | 53 +++++++++++++++++++++ portalnetwork/beacon/portal_api.go | 48 +++++++++++++++++++ 4 files changed, 179 insertions(+), 49 deletions(-) create mode 100644 portalnetwork/beacon/light_client.go create mode 100644 portalnetwork/beacon/portal_api.go diff --git a/beacon/light/api/portal_api.go b/beacon/light/api/portal_api.go index 6b40f28eba8e..cd532553c6a7 100644 --- a/beacon/light/api/portal_api.go +++ b/beacon/light/api/portal_api.go @@ -1,57 +1,39 @@ package api import ( - "errors" + "time" - "github.com/ethereum/go-ethereum/beacon/types" - "github.com/ethereum/go-ethereum/common" + "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) GetBestUpdatesAndCommittees(firstPeriod, count uint64) ([]*types.LightClientUpdate, []*types.SerializedSyncCommittee, error) { - //contentKey := &beacon.LightClientUpdateKey{ - // StartPeriod: firstPeriod, - // Count: count, - //} - - //resp, err := api.httpGetf("/eth/v1/beacon/light_client/updates?start_period=%d&count=%d", firstPeriod, count) - //if err != nil { - // return nil, nil, err - //} - // - //var data []CommitteeUpdate - //if err := json.Unmarshal(resp, &data); err != nil { - // return nil, nil, err - //} - //if len(data) != int(count) { - // return nil, nil, errors.New("invalid number of committee updates") - //} - //updates := make([]*types.LightClientUpdate, int(count)) - //committees := make([]*types.SerializedSyncCommittee, int(count)) - //for i, d := range data { - // if d.Update.AttestedHeader.Header.SyncPeriod() != firstPeriod+uint64(i) { - // return nil, nil, errors.New("wrong committee update header period") - // } - // if err := d.Update.Validate(); err != nil { - // return nil, nil, err - // } - // if d.NextSyncCommittee.Root() != d.Update.NextSyncCommitteeRoot { - // return nil, nil, errors.New("wrong sync committee root") - // } - // updates[i], committees[i] = new(types.LightClientUpdate), new(types.SerializedSyncCommittee) - // *updates[i], *committees[i] = d.Update, d.NextSyncCommittee - //} - //return updates, committees, nil - - return nil, nil, errors.New("not implemented") +func (api *PortalLightApi) GetUpdates(firstPeriod, count uint64) (beacon.LightClientUpdateRange, error) { + return api.bn.GetUpdates(firstPeriod, count) } -func (api *PortalLightApi) GetCheckpointData(checkpointHash common.Hash) (*types.BootstrapData, error) { - return nil, errors.New("not implemented") +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/beacon_network.go b/portalnetwork/beacon/beacon_network.go index 289658e892e4..e0f1f788e5c1 100644 --- a/portalnetwork/beacon/beacon_network.go +++ b/portalnetwork/beacon/beacon_network.go @@ -19,14 +19,15 @@ const ( LightClientFinalityUpdate storage.ContentType = 0x12 LightClientOptimisticUpdate storage.ContentType = 0x13 HistoricalSummaries storage.ContentType = 0x14 + BeaconGenesisTime uint64 = 1606824023 ) type BeaconNetwork struct { - portalProtocol *discover.PortalProtocol - spec *common.Spec + PortalProtocol *discover.PortalProtocol + Spec *common.Spec } -func (bn *BeaconNetwork) GetBestUpdatesAndCommittees(firstPeriod, count uint64) (LightClientUpdateRange, error) { +func (bn *BeaconNetwork) GetUpdates(firstPeriod, count uint64) (LightClientUpdateRange, error) { lightClientUpdateKey := &LightClientUpdateKey{ StartPeriod: firstPeriod, Count: count, @@ -38,7 +39,7 @@ func (bn *BeaconNetwork) GetBestUpdatesAndCommittees(firstPeriod, count uint64) } var lightClientUpdateRange LightClientUpdateRange = make([]ForkedLightClientUpdate, 0) - err = lightClientUpdateRange.Deserialize(bn.spec, codec.NewDecodingReader(bytes.NewReader(lightClientUpdateRangeContent), uint64(len(lightClientUpdateRangeContent)))) + err = lightClientUpdateRange.Deserialize(bn.Spec, codec.NewDecodingReader(bytes.NewReader(lightClientUpdateRangeContent), uint64(len(lightClientUpdateRangeContent)))) if err != nil { return nil, err } @@ -57,7 +58,7 @@ func (bn *BeaconNetwork) GetCheckpointData(checkpointHash tree.Root) (*capella.L } var forkedLightClientBootstrap ForkedLightClientBootstrap - err = forkedLightClientBootstrap.Deserialize(bn.spec, codec.NewDecodingReader(bytes.NewReader(bootstrapValue), uint64(len(bootstrapValue)))) + err = forkedLightClientBootstrap.Deserialize(bn.Spec, codec.NewDecodingReader(bytes.NewReader(bootstrapValue), uint64(len(bootstrapValue)))) if err != nil { return nil, err } @@ -69,6 +70,52 @@ func (bn *BeaconNetwork) GetCheckpointData(checkpointHash tree.Root) (*capella.L return forkedLightClientBootstrap.Bootstrap.(*capella.LightClientBootstrap), nil } +func (bn *BeaconNetwork) GetFinalityUpdate(finalizedSlot uint64) (*capella.LightClientFinalityUpdate, error) { + finalityUpdateKey := &LightClientFinalityUpdateKey{ + FinalizedSlot: finalizedSlot, + } + + finalityUpdateValue, err := bn.getContent(LightClientFinalityUpdate, finalityUpdateKey) + if err != nil { + return nil, err + } + + var forkedLightClientFinalityUpdate ForkedLightClientFinalityUpdate + err = forkedLightClientFinalityUpdate.Deserialize(bn.Spec, codec.NewDecodingReader(bytes.NewReader(finalityUpdateValue), uint64(len(finalityUpdateValue)))) + if err != nil { + return nil, err + } + + if forkedLightClientFinalityUpdate.ForkDigest != Capella { + return nil, errors.New("unknown fork digest") + } + + return forkedLightClientFinalityUpdate.LightClientFinalityUpdate.(*capella.LightClientFinalityUpdate), nil +} + +func (bn *BeaconNetwork) GetOptimisticUpdate(optimisticSlot uint64) (*capella.LightClientOptimisticUpdate, error) { + optimisticUpdateKey := &LightClientOptimisticUpdateKey{ + OptimisticSlot: optimisticSlot, + } + + optimisticUpdateValue, err := bn.getContent(LightClientOptimisticUpdate, optimisticUpdateKey) + if err != nil { + return nil, err + } + + var forkedLightClientOptimisticUpdate ForkedLightClientOptimisticUpdate + err = forkedLightClientOptimisticUpdate.Deserialize(bn.Spec, codec.NewDecodingReader(bytes.NewReader(optimisticUpdateValue), uint64(len(optimisticUpdateValue)))) + if err != nil { + return nil, err + } + + if forkedLightClientOptimisticUpdate.ForkDigest != Capella { + return nil, errors.New("unknown fork digest") + } + + return forkedLightClientOptimisticUpdate.LightClientOptimisticUpdate.(*capella.LightClientOptimisticUpdate), nil +} + func (bn *BeaconNetwork) getContent(contentType storage.ContentType, beaconContentKey ssz.Marshaler) ([]byte, error) { contentKeyBytes, err := beaconContentKey.MarshalSSZ() if err != nil { @@ -76,9 +123,9 @@ func (bn *BeaconNetwork) getContent(contentType storage.ContentType, beaconConte } contentKey := storage.NewContentKey(contentType, contentKeyBytes).Encode() - contentId := bn.portalProtocol.ToContentId(contentKey) + contentId := bn.PortalProtocol.ToContentId(contentKey) - res, err := bn.portalProtocol.Get(contentKey, contentId) + res, err := bn.PortalProtocol.Get(contentKey, contentId) // other error if err != nil && !errors.Is(err, storage.ErrContentNotFound) { return nil, err @@ -88,7 +135,7 @@ func (bn *BeaconNetwork) getContent(contentType storage.ContentType, beaconConte return res, nil } - content, _, err := bn.portalProtocol.ContentLookup(contentKey, contentId) + content, _, err := bn.PortalProtocol.ContentLookup(contentKey, contentId) if err != nil { return nil, err } diff --git a/portalnetwork/beacon/light_client.go b/portalnetwork/beacon/light_client.go new file mode 100644 index 000000000000..52bfc186918f --- /dev/null +++ b/portalnetwork/beacon/light_client.go @@ -0,0 +1,53 @@ +package beacon + +import ( + "github.com/protolambda/zrnt/eth2/beacon/capella" + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/view" +) + +type ConsensusAPI interface { + GetUpdates(firstPeriod, count uint64) (LightClientUpdateRange, error) + GetCheckpointData(checkpointHash common.Root) (*capella.LightClientBootstrap, error) + GetFinalityData() (*capella.LightClientFinalityUpdate, error) + GetOptimisticData() (*capella.LightClientOptimisticUpdate, error) + ChainID() uint64 + Name() string +} + +type LightClientStore struct { + FinalizedHeader common.BeaconBlockHeader + CurrentSyncCommittee common.SyncCommittee + NextSyncCommittee common.SyncCommittee + OptimisticHeader common.BeaconBlockHeader + PreviousMaxActiveParticipants view.Uint64View + CurrentMaxActiveParticipants view.Uint64View +} + +type ConsensusLightClient struct { + Store LightClientStore + API ConsensusAPI + InitialCheckpoint common.Root + LastCheckpoint common.Root + Config Config +} + +type Config struct { + ConsensusAPI string + Port uint64 + DefaultCheckpoint common.Root + Checkpoint common.Root + DataDir string + ChainConfig ChainConfig + Spec *common.Spec + MaxCheckpointAge uint64 + Fallback string + LoadExternalFallback bool + StrictCheckpointAge bool +} + +type ChainConfig struct { + ChainID uint64 + GenesisTime uint64 + GenesisRoot common.Root +} diff --git a/portalnetwork/beacon/portal_api.go b/portalnetwork/beacon/portal_api.go new file mode 100644 index 000000000000..a265ab00c11e --- /dev/null +++ b/portalnetwork/beacon/portal_api.go @@ -0,0 +1,48 @@ +package beacon + +import ( + "time" + + "github.com/protolambda/zrnt/eth2/beacon/capella" + zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/tree" +) + +var _ ConsensusAPI = &PortalLightApi{} + +type PortalLightApi struct { + bn *BeaconNetwork +} + +func NewPortalLightApi() *PortalLightApi { + return &PortalLightApi{} +} + +func (api *PortalLightApi) GetUpdates(firstPeriod, count uint64) (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(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(BeaconGenesisTime)) + + return api.bn.GetOptimisticUpdate(uint64(expectedCurrentSlot)) +} + +func (api *PortalLightApi) ChainID() uint64 { + return 1 +} + +func (api *PortalLightApi) Name() string { + return "portal" +}