From 4fec1b226a2e7f1a635dafcd84566fad0de64784 Mon Sep 17 00:00:00 2001 From: fearlessfe <505380967@qq.com> Date: Fri, 6 Sep 2024 08:11:22 +0800 Subject: [PATCH] refactor: extract ConsensusAPI from beacon network --- portalnetwork/beacon/lightclient/interface.go | 12 ++ portalnetwork/beacon/lightclient/mock_rpc.go | 42 +++++ .../beacon/lightclient/portal_rpc.go | 153 ++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 portalnetwork/beacon/lightclient/interface.go create mode 100644 portalnetwork/beacon/lightclient/mock_rpc.go create mode 100644 portalnetwork/beacon/lightclient/portal_rpc.go diff --git a/portalnetwork/beacon/lightclient/interface.go b/portalnetwork/beacon/lightclient/interface.go new file mode 100644 index 0000000000..cd2f801757 --- /dev/null +++ b/portalnetwork/beacon/lightclient/interface.go @@ -0,0 +1,12 @@ +package lightclient + +import "github.com/protolambda/zrnt/eth2/beacon/common" + +type ConsensusAPI interface { + GetBootstrap(blockRoot common.Root) (common.SpecObj, error) + GetUpdates(firstPeriod, count uint64) ([]common.SpecObj, error) + GetFinalityUpdate() (common.SpecObj, error) + GetOptimisticUpdate() (common.SpecObj, error) + ChainID() uint64 + Name() string +} \ No newline at end of file diff --git a/portalnetwork/beacon/lightclient/mock_rpc.go b/portalnetwork/beacon/lightclient/mock_rpc.go new file mode 100644 index 0000000000..452feb2035 --- /dev/null +++ b/portalnetwork/beacon/lightclient/mock_rpc.go @@ -0,0 +1,42 @@ +package lightclient + +import ( + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/tree" +) + +var _ ConsensusAPI = (*MockRpc)(nil) + +type MockRpc struct { + testdataDir string +} + +// ChainID implements ConsensusAPI. +func (m *MockRpc) ChainID() uint64 { + panic("unimplemented") +} + +// GetBootstrap implements ConsensusAPI. +func (m *MockRpc) GetBootstrap(blockRoot tree.Root) (common.SpecObj, error) { + panic("unimplemented") +} + +// GetFinalityUpdate implements ConsensusAPI. +func (m *MockRpc) GetFinalityUpdate() (common.SpecObj, error) { + panic("unimplemented") +} + +// GetOptimisticUpdate implements ConsensusAPI. +func (m *MockRpc) GetOptimisticUpdate() (common.SpecObj, error) { + panic("unimplemented") +} + +// GetUpdates implements ConsensusAPI. +func (m *MockRpc) GetUpdates(firstPeriod uint64, count uint64) ([]common.SpecObj, error) { + panic("unimplemented") +} + +// Name implements ConsensusAPI. +func (m *MockRpc) Name() string { + panic("unimplemented") +} diff --git a/portalnetwork/beacon/lightclient/portal_rpc.go b/portalnetwork/beacon/lightclient/portal_rpc.go new file mode 100644 index 0000000000..c6c0a0d963 --- /dev/null +++ b/portalnetwork/beacon/lightclient/portal_rpc.go @@ -0,0 +1,153 @@ +package lightclient + +import ( + "bytes" + "errors" + "time" + + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/portalnetwork/beacon" + "github.com/ethereum/go-ethereum/portalnetwork/storage" + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" +) + +var _ ConsensusAPI = &PortalRpc{} + +type PortalRpc struct { + portalProtocol *discover.PortalProtocol + spec *common.Spec +} + +// ChainID implements ConsensusAPI. +func (p *PortalRpc) ChainID() uint64 { + return 1 +} + +// GetCheckpointData implements ConsensusAPI. +func (p *PortalRpc) GetBootstrap(blockRoot tree.Root) (common.SpecObj, error) { + bootstrapKey := &beacon.LightClientBootstrapKey{ + BlockHash: blockRoot[:], + } + contentKeyBytes, err := bootstrapKey.MarshalSSZ() + if err != nil { + return nil, err + } + contentKey := storage.NewContentKey(beacon.LightClientBootstrap, contentKeyBytes).Encode() + // Get from local + contentId := p.portalProtocol.ToContentId(contentKey) + res, err := p.getContent(contentKey, contentId) + if err != nil { + return nil, err + } + forkedLightClientBootstrap := &beacon.ForkedLightClientBootstrap{} + err = forkedLightClientBootstrap.Deserialize(p.spec, codec.NewDecodingReader(bytes.NewReader(res), uint64(len(res)))) + if err != nil { + return nil, err + } + return forkedLightClientBootstrap.Bootstrap, nil +} + +// GetFinalityData implements ConsensusAPI. +func (p *PortalRpc) GetFinalityUpdate() (common.SpecObj, error) { + // Get the finality update for the most recent finalized epoch. We use 0 as the finalized + // slot because the finalized slot is not known at this point and the protocol is + // designed to return the most recent which is > 0 + finUpdateKey := &beacon.LightClientFinalityUpdateKey{ + FinalizedSlot: 0, + } + contentKeyBytes, err := finUpdateKey.MarshalSSZ() + if err != nil { + return nil, err + } + contentKey := storage.NewContentKey(beacon.LightClientFinalityUpdate, contentKeyBytes).Encode() + // Get from local + contentId := p.portalProtocol.ToContentId(contentKey) + res, err := p.getContent(contentKey, contentId) + if err != nil { + return nil, err + } + finalityUpdate := &beacon.ForkedLightClientFinalityUpdate{} + err = finalityUpdate.Deserialize(p.spec, codec.NewDecodingReader(bytes.NewReader(res), uint64(len(res)))) + if err != nil { + return nil, err + } + return finalityUpdate.LightClientFinalityUpdate, nil +} + +// GetOptimisticData implements ConsensusAPI. +func (p *PortalRpc) GetOptimisticUpdate() (common.SpecObj, error) { + currentSlot := p.spec.TimeToSlot(common.Timestamp(time.Now().Unix()), common.Timestamp(beacon.BeaconGenesisTime)) + optimisticUpdateKey := &beacon.LightClientOptimisticUpdateKey{ + OptimisticSlot: uint64(currentSlot), + } + contentKeyBytes, err := optimisticUpdateKey.MarshalSSZ() + if err != nil { + return nil, err + } + contentKey := storage.NewContentKey(beacon.LightClientOptimisticUpdate, contentKeyBytes).Encode() + // Get from local + contentId := p.portalProtocol.ToContentId(contentKey) + res, err := p.getContent(contentKey, contentId) + if err != nil { + return nil, err + } + optimisticUpdate := &beacon.ForkedLightClientOptimisticUpdate{} + err = optimisticUpdate.Deserialize(p.spec, codec.NewDecodingReader(bytes.NewReader(res), uint64(len(res)))) + if err != nil { + return nil, err + } + return optimisticUpdate.LightClientOptimisticUpdate, nil +} + +// GetUpdates implements ConsensusAPI. +func (p *PortalRpc) GetUpdates(firstPeriod uint64, count uint64) ([]common.SpecObj, error) { + lightClientUpdateKey := &beacon.LightClientUpdateKey{ + StartPeriod: firstPeriod, + Count: count, + } + contentKeyBytes, err := lightClientUpdateKey.MarshalSSZ() + if err != nil { + return nil, err + } + contentKey := storage.NewContentKey(beacon.LightClientUpdate, contentKeyBytes).Encode() + // Get from local + contentId := p.portalProtocol.ToContentId(contentKey) + data, err := p.getContent(contentKey, contentId) + if err != nil { + return nil, err + } + var lightClientUpdateRange beacon.LightClientUpdateRange = make([]beacon.ForkedLightClientUpdate, 0) + err = lightClientUpdateRange.Deserialize(p.spec, codec.NewDecodingReader(bytes.NewReader(data), uint64(len(data)))) + if err != nil { + return nil, err + } + res := make([]common.SpecObj, len(lightClientUpdateRange)) + + for i, item := range lightClientUpdateRange { + res[i] = item.LightClientUpdate + } + return res, nil +} + +// Name implements ConsensusAPI. +func (p *PortalRpc) Name() string { + return "portal" +} + +func (p *PortalRpc) getContent(contentKey, contentId []byte) ([]byte, error) { + res, err := p.portalProtocol.Get(contentKey, contentId) + // other error + if err != nil && !errors.Is(err, storage.ErrContentNotFound) { + return nil, err + } + if res == nil { + // Get from remote + res, _, err = p.portalProtocol.ContentLookup(contentKey, contentId) + if err != nil { + return nil, err + } + } + return res, nil +}