mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
158 lines
4.8 KiB
Go
158 lines
4.8 KiB
Go
package beacon
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/portalnetwork/portalwire"
|
|
"github.com/ethereum/go-ethereum/portalnetwork/storage"
|
|
"github.com/protolambda/zrnt/eth2/beacon/common"
|
|
"github.com/protolambda/ztyp/codec"
|
|
"github.com/protolambda/ztyp/tree"
|
|
)
|
|
|
|
const BeaconGenesisTime uint64 = 1606824023
|
|
|
|
var _ ConsensusAPI = &PortalLightApi{}
|
|
|
|
type PortalLightApi struct {
|
|
portalProtocol *portalwire.PortalProtocol
|
|
spec *common.Spec
|
|
}
|
|
|
|
func NewPortalLightApi() *PortalLightApi {
|
|
return &PortalLightApi{}
|
|
}
|
|
|
|
// ChainID implements ConsensusAPI.
|
|
func (p *PortalLightApi) ChainID() uint64 {
|
|
return 1
|
|
}
|
|
|
|
// GetCheckpointData implements ConsensusAPI.
|
|
func (p *PortalLightApi) GetBootstrap(blockRoot tree.Root) (common.SpecObj, error) {
|
|
bootstrapKey := &LightClientBootstrapKey{
|
|
BlockHash: blockRoot[:],
|
|
}
|
|
contentKeyBytes, err := bootstrapKey.MarshalSSZ()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
contentKey := storage.NewContentKey(LightClientBootstrap, contentKeyBytes).Encode()
|
|
// Get from local
|
|
contentId := p.portalProtocol.ToContentId(contentKey)
|
|
res, err := p.getContent(contentKey, contentId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
forkedLightClientBootstrap := &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 *PortalLightApi) 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 := &LightClientFinalityUpdateKey{
|
|
FinalizedSlot: 0,
|
|
}
|
|
contentKeyBytes, err := finUpdateKey.MarshalSSZ()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
contentKey := storage.NewContentKey(LightClientFinalityUpdate, contentKeyBytes).Encode()
|
|
// Get from local
|
|
contentId := p.portalProtocol.ToContentId(contentKey)
|
|
res, err := p.getContent(contentKey, contentId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
finalityUpdate := &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 *PortalLightApi) GetOptimisticUpdate() (common.SpecObj, error) {
|
|
currentSlot := p.spec.TimeToSlot(common.Timestamp(time.Now().Unix()), common.Timestamp(BeaconGenesisTime))
|
|
optimisticUpdateKey := &LightClientOptimisticUpdateKey{
|
|
OptimisticSlot: uint64(currentSlot),
|
|
}
|
|
contentKeyBytes, err := optimisticUpdateKey.MarshalSSZ()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
contentKey := storage.NewContentKey(LightClientOptimisticUpdate, contentKeyBytes).Encode()
|
|
// Get from local
|
|
contentId := p.portalProtocol.ToContentId(contentKey)
|
|
res, err := p.getContent(contentKey, contentId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
optimisticUpdate := &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 *PortalLightApi) GetUpdates(firstPeriod uint64, count uint64) ([]common.SpecObj, error) {
|
|
lightClientUpdateKey := &LightClientUpdateKey{
|
|
StartPeriod: firstPeriod,
|
|
Count: count,
|
|
}
|
|
contentKeyBytes, err := lightClientUpdateKey.MarshalSSZ()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
contentKey := storage.NewContentKey(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 LightClientUpdateRange = make([]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 *PortalLightApi) Name() string {
|
|
return "portal"
|
|
}
|
|
|
|
func (p *PortalLightApi) 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
|
|
}
|