Skip to content

Commit

Permalink
Merge pull request #146 from fearlessfe/light
Browse files Browse the repository at this point in the history
light cleint and history summaries validation
  • Loading branch information
fearlessfe authored Sep 7, 2024
2 parents 3b626a8 + 50d7b0d commit 17308df
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 40 deletions.
35 changes: 33 additions & 2 deletions portalnetwork/beacon/beacon_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
ssz "github.com/ferranbt/fastssz"
"github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/zrnt/eth2/configs"
"github.com/protolambda/zrnt/eth2/util/merkle"
"github.com/protolambda/ztyp/codec"
"github.com/protolambda/ztyp/tree"
)
Expand All @@ -32,6 +33,7 @@ type BeaconNetwork struct {
log log.Logger
closeCtx context.Context
closeFunc context.CancelFunc
lightClient *ConsensusLightClient
}

func NewBeaconNetwork(portalProtocol *discover.PortalProtocol) *BeaconNetwork {
Expand Down Expand Up @@ -251,8 +253,29 @@ func (bn *BeaconNetwork) validateContent(contentKey []byte, content []byte) erro
return nil
// TODO: VERIFY
case HistoricalSummaries:
var historicalSummaries HistoricalSummariesProof
return historicalSummaries.Deserialize(codec.NewDecodingReader(bytes.NewReader(content), uint64(len(content))))
key := &HistoricalSummariesWithProofKey{}
err := key.Deserialize(codec.NewDecodingReader(bytes.NewReader(contentKey[1:]), uint64(len(contentKey[1:]))))
if err != nil {
return err
}
forkedHistoricalSummariesWithProof := &ForkedHistoricalSummariesWithProof{}
err = forkedHistoricalSummariesWithProof.Deserialize(bn.spec, codec.NewDecodingReader(bytes.NewReader(content), uint64(len(content))))
if err != nil {
return err
}
if forkedHistoricalSummariesWithProof.HistoricalSummariesWithProof.EPOCH != common.Epoch(key.Epoch) {
return fmt.Errorf("historical summaries with proof epoch does not match the content key epoch: %d != %d", forkedHistoricalSummariesWithProof.HistoricalSummariesWithProof.EPOCH, key.Epoch)
}

// TODO get root from light client
header := bn.lightClient.GetFinalityHeader()
latestFinalizedRoot := header.StateRoot

valid := bn.stateSummariesValidation(*forkedHistoricalSummariesWithProof, latestFinalizedRoot)
if !valid {
return errors.New("merkle proof validation failed for HistoricalSummariesProof")
}
return nil
default:
return fmt.Errorf("unknown content type %v", contentKey[0])
}
Expand Down Expand Up @@ -305,3 +328,11 @@ func (bn *BeaconNetwork) processContentLoop(ctx context.Context) {
}
}
}

func (bn *BeaconNetwork) stateSummariesValidation(f ForkedHistoricalSummariesWithProof, latestFinalizedRoot common.Root) bool {
proof := f.HistoricalSummariesWithProof.Proof
summariesRoot := f.HistoricalSummariesWithProof.HistoricalSummaries.HashTreeRoot(bn.spec, tree.GetHashFn())

gIndex := 59
return merkle.VerifyMerkleBranch(summariesRoot, proof.Proof[:], 5, uint64(gIndex), latestFinalizedRoot)
}
16 changes: 8 additions & 8 deletions portalnetwork/beacon/light_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ var (
)

type ConsensusAPI interface {
GetBootstrap(blockRoot common.Root) (common.SpecObj, error)
GetUpdates(firstPeriod, count uint64) ([]common.SpecObj, error)
GetCheckpointData(checkpointHash common.Root) (common.SpecObj, error)
GetFinalityData() (common.SpecObj, error)
GetOptimisticData() (common.SpecObj, error)
GetFinalityUpdate() (common.SpecObj, error)
GetOptimisticUpdate() (common.SpecObj, error)
ChainID() uint64
Name() string
}
Expand Down Expand Up @@ -174,7 +174,7 @@ func (c *ConsensusLightClient) Sync() error {
c.ApplyUpdate(update)
}

finalityUpdate, err := c.API.GetFinalityData()
finalityUpdate, err := c.API.GetFinalityUpdate()
if err != nil {
return err
}
Expand All @@ -184,7 +184,7 @@ func (c *ConsensusLightClient) Sync() error {
}
c.ApplyFinalityUpdate(finalityUpdate)

optimisticUpdate, err := c.API.GetOptimisticData()
optimisticUpdate, err := c.API.GetOptimisticUpdate()
if err != nil {
return err
}
Expand All @@ -199,7 +199,7 @@ func (c *ConsensusLightClient) Sync() error {
}

func (c *ConsensusLightClient) Advance() error {
finalityUpdate, err := c.API.GetFinalityData()
finalityUpdate, err := c.API.GetFinalityUpdate()
if err != nil {
return err
}
Expand All @@ -209,7 +209,7 @@ func (c *ConsensusLightClient) Advance() error {
}
c.ApplyFinalityUpdate(finalityUpdate)

optimisticUpdate, err := c.API.GetOptimisticData()
optimisticUpdate, err := c.API.GetOptimisticUpdate()
if err != nil {
return err
}
Expand Down Expand Up @@ -240,7 +240,7 @@ func (c *ConsensusLightClient) Advance() error {
}

func (c *ConsensusLightClient) bootstrap() error {
forkedBootstrap, err := c.API.GetCheckpointData(c.InitialCheckpoint)
forkedBootstrap, err := c.API.GetBootstrap(c.InitialCheckpoint)
if err != nil {
return err
}
Expand Down
12 changes: 6 additions & 6 deletions portalnetwork/beacon/light_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (m MockConsensusAPI) GetUpdates(_, _ uint64) ([]common.SpecObj, error) {
return res, nil
}

func (m MockConsensusAPI) GetCheckpointData(_ common.Root) (common.SpecObj, error) {
func (m MockConsensusAPI) GetBootstrap(_ common.Root) (common.SpecObj, error) {
jsonStr, _ := os.ReadFile(m.testdataDir + "/bootstrap.json")

bootstrap := &capella.LightClientBootstrap{}
Expand All @@ -47,7 +47,7 @@ func (m MockConsensusAPI) GetCheckpointData(_ common.Root) (common.SpecObj, erro
return bootstrap, nil
}

func (m MockConsensusAPI) GetFinalityData() (common.SpecObj, error) {
func (m MockConsensusAPI) GetFinalityUpdate() (common.SpecObj, error) {
jsonStr, _ := os.ReadFile(m.testdataDir + "/finality.json")

finality := &capella.LightClientFinalityUpdate{}
Expand All @@ -56,7 +56,7 @@ func (m MockConsensusAPI) GetFinalityData() (common.SpecObj, error) {
return finality, nil
}

func (m MockConsensusAPI) GetOptimisticData() (common.SpecObj, error) {
func (m MockConsensusAPI) GetOptimisticUpdate() (common.SpecObj, error) {
jsonStr, _ := os.ReadFile(m.testdataDir + "/optimistic.json")

optimistic := &capella.LightClientOptimisticUpdate{}
Expand Down Expand Up @@ -140,7 +140,7 @@ func TestVerifyFinalityUpdate(t *testing.T) {
client, err := getClient(false, t)
require.NoError(t, err)

update, err := client.API.GetFinalityData()
update, err := client.API.GetFinalityUpdate()
require.NoError(t, err)

// normal
Expand All @@ -154,7 +154,7 @@ func TestVerifyFinalityUpdate(t *testing.T) {
err = client.VerifyGenericUpdate(genericUpdate)
require.Equal(t, ErrInvalidFinalityProof, err)
// ErrInvalidSignature
update, err = client.API.GetFinalityData()
update, err = client.API.GetFinalityUpdate()
require.NoError(t, err)

genericUpdate, err = FromLightClientFinalityUpdate(update)
Expand All @@ -168,7 +168,7 @@ func TestVerifyOptimisticUpdate(t *testing.T) {
client, err := getClient(false, t)
require.NoError(t, err)

update, err := client.API.GetOptimisticData()
update, err := client.API.GetOptimisticUpdate()
require.NoError(t, err)

// normal
Expand Down
145 changes: 126 additions & 19 deletions portalnetwork/beacon/portal_api.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package beacon

import (
"bytes"
"errors"
"time"

zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/portalnetwork/storage"
"github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/ztyp/codec"
"github.com/protolambda/ztyp/tree"
)

Expand All @@ -12,40 +17,142 @@ const BeaconGenesisTime uint64 = 1606824023
var _ ConsensusAPI = &PortalLightApi{}

type PortalLightApi struct {
bn *BeaconNetwork
portalProtocol *discover.PortalProtocol
spec *common.Spec
}

func NewPortalLightApi() *PortalLightApi {
return &PortalLightApi{}
}

func (api *PortalLightApi) GetUpdates(firstPeriod, count uint64) ([]zrntcommon.SpecObj, error) {
return api.bn.GetUpdates(firstPeriod, count)
// ChainID implements ConsensusAPI.
func (p *PortalLightApi) ChainID() uint64 {
return 1
}

func (api *PortalLightApi) GetCheckpointData(checkpointHash tree.Root) (zrntcommon.SpecObj, error) {
return api.bn.GetCheckpointData(checkpointHash)
// 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
}

func (api *PortalLightApi) GetFinalityData() (zrntcommon.SpecObj, 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))
// 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
}

func (api *PortalLightApi) GetOptimisticData() (zrntcommon.SpecObj, error) {
expectedCurrentSlot := api.bn.spec.TimeToSlot(zrntcommon.Timestamp(time.Now().Unix()), zrntcommon.Timestamp(BeaconGenesisTime))
return api.bn.GetOptimisticUpdate(uint64(expectedCurrentSlot))
// 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
}

func (api *PortalLightApi) ChainID() uint64 {
return 1
// 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
}

func (api *PortalLightApi) Name() string {
// Name implements ConsensusAPI.
func (p *PortalLightApi) Name() string {
return "portal"
}

// func CurrentSlot() uint64 {

// }
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
}
Loading

0 comments on commit 17308df

Please sign in to comment.