From 6edb2a29b3c0a6913525075ba5298394ea374d57 Mon Sep 17 00:00:00 2001 From: Chen Kai <281165273grape@gmail.com> Date: Sat, 6 Apr 2024 21:28:15 +0800 Subject: [PATCH] feat:add lc bootstrap and update range Signed-off-by: Chen Kai <281165273grape@gmail.com> --- beacon/light/api/portal_api.go | 5 ++ go.mod | 2 +- go.sum | 4 +- p2p/discover/portal_protocol.go | 3 +- portalnetwork/beacon/beacon_network.go | 42 +++++++-- portalnetwork/beacon/types.go | 117 +++++++++++++++++++++++-- portalnetwork/beacon/types_encoding.go | 2 +- portalnetwork/beacon/types_test.go | 30 +++++++ 8 files changed, 186 insertions(+), 19 deletions(-) diff --git a/beacon/light/api/portal_api.go b/beacon/light/api/portal_api.go index 4c96bf486e..6b40f28eba 100644 --- a/beacon/light/api/portal_api.go +++ b/beacon/light/api/portal_api.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/ethereum/go-ethereum/beacon/types" + "github.com/ethereum/go-ethereum/common" ) type PortalLightApi struct { @@ -50,3 +51,7 @@ func (api *PortalLightApi) GetBestUpdatesAndCommittees(firstPeriod, count uint64 return nil, nil, errors.New("not implemented") } + +func (api *PortalLightApi) GetCheckpointData(checkpointHash common.Hash) (*types.BootstrapData, error) { + return nil, errors.New("not implemented") +} diff --git a/go.mod b/go.mod index e0f4d188c0..3c632fc6e8 100644 --- a/go.mod +++ b/go.mod @@ -153,4 +153,4 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/protolambda/zrnt v0.32.2 => github.com/optimism-java/zrnt v0.32.4-0.20240402113914-188558bfad88 +replace github.com/protolambda/zrnt v0.32.2 => github.com/optimism-java/zrnt v0.32.4-0.20240403132616-04d1d446b0da diff --git a/go.sum b/go.sum index e64a0368e1..ca81c25493 100644 --- a/go.sum +++ b/go.sum @@ -431,8 +431,8 @@ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsq github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/optimism-java/utp-go v0.0.0-20240309041853-b6b3a0dea581 h1:ZxgrtI0xIw+clB32iDDDWaiTcCizTeN7rNyzH9YorPI= github.com/optimism-java/utp-go v0.0.0-20240309041853-b6b3a0dea581/go.mod h1:DZ0jYzLzt4ZsCmhI/iqYgGFoNx45OfpEoKzXB8HVALQ= -github.com/optimism-java/zrnt v0.32.4-0.20240402113914-188558bfad88 h1:hYCtTSAgXOtapscLmr6OgCQJulRjF3K7wZlvh9Zzrwk= -github.com/optimism-java/zrnt v0.32.4-0.20240402113914-188558bfad88/go.mod h1:A0fezkp9Tt3GBLATSPIbuY4ywYESyAuc/FFmPKg8Lqs= +github.com/optimism-java/zrnt v0.32.4-0.20240403132616-04d1d446b0da h1:s8V7G1gRW5EeyHYCDVW3O01gEm2BVAvV1cLPZ+WIB1E= +github.com/optimism-java/zrnt v0.32.4-0.20240403132616-04d1d446b0da/go.mod h1:A0fezkp9Tt3GBLATSPIbuY4ywYESyAuc/FFmPKg8Lqs= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= diff --git a/p2p/discover/portal_protocol.go b/p2p/discover/portal_protocol.go index f95636dbc4..cf53b1cf6d 100644 --- a/p2p/discover/portal_protocol.go +++ b/p2p/discover/portal_protocol.go @@ -13,6 +13,7 @@ import ( "math/big" "math/rand" "net" + "slices" "sort" "sync" "time" @@ -1223,7 +1224,7 @@ func (p *PortalProtocol) verifyResponseNode(sender *enode.Node, r *enr.Record, d } if distances != nil { nd := enode.LogDist(sender.ID(), n.ID()) - if !containsUint(uint(nd), distances) { + if !slices.Contains(distances, uint(nd)) { return nil, errors.New("does not match any requested distance") } } diff --git a/portalnetwork/beacon/beacon_network.go b/portalnetwork/beacon/beacon_network.go index 8158525d80..4b78767a10 100644 --- a/portalnetwork/beacon/beacon_network.go +++ b/portalnetwork/beacon/beacon_network.go @@ -1,12 +1,15 @@ package beacon import ( + "bytes" "errors" - "github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/portalnetwork/storage" ssz "github.com/ferranbt/fastssz" + "github.com/protolambda/zrnt/eth2/beacon/capella" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" ) const ( @@ -21,18 +24,47 @@ type BeaconNetwork struct { portalProtocol *discover.PortalProtocol } -func (bn *BeaconNetwork) GetBestUpdatesAndCommittees(firstPeriod, count uint64) ([]*types.LightClientUpdate, []*types.SerializedSyncCommittee, error) { +func (bn *BeaconNetwork) GetBestUpdatesAndCommittees(firstPeriod, count uint64) (LightClientUpdateRange, error) { lightClientUpdateKey := &LightClientUpdateKey{ StartPeriod: firstPeriod, Count: count, } - _, err := bn.getContent(LightClientUpdate, lightClientUpdateKey) + lightClientUpdateRangeContent, err := bn.getContent(LightClientUpdate, lightClientUpdateKey) if err != nil { - return nil, nil, err + return nil, err } - return nil, nil, err + var lightClientUpdateRange LightClientUpdateRange = make([]ForkedLightClientUpdate, 0) + err = lightClientUpdateRange.Deserialize(codec.NewDecodingReader(bytes.NewReader(lightClientUpdateRangeContent), uint64(len(lightClientUpdateRangeContent)))) + if err != nil { + return nil, err + } + + return lightClientUpdateRange, nil +} + +func (bn *BeaconNetwork) GetCheckpointData(checkpointHash tree.Root) (*capella.LightClientBootstrap, error) { + bootstrapKey := &LightClientBootstrapKey{ + BlockHash: checkpointHash[:], + } + + bootstrapValue, err := bn.getContent(LightClientBootstrap, bootstrapKey) + if err != nil { + return nil, err + } + + var forkedLightClientBootstrap ForkedLightClientBootstrap + err = forkedLightClientBootstrap.Deserialize(codec.NewDecodingReader(bytes.NewReader(bootstrapValue), uint64(len(bootstrapValue)))) + if err != nil { + return nil, err + } + + if forkedLightClientBootstrap.ForkDigest != Capella { + return nil, errors.New("unknown fork digest") + } + + return forkedLightClientBootstrap.Bootstrap.(*capella.LightClientBootstrap), nil } func (bn *BeaconNetwork) getContent(contentType storage.ContentType, beaconContentKey ssz.Marshaler) ([]byte, error) { diff --git a/portalnetwork/beacon/types.go b/portalnetwork/beacon/types.go index c1c877e765..850fcd5c98 100644 --- a/portalnetwork/beacon/types.go +++ b/portalnetwork/beacon/types.go @@ -8,6 +8,7 @@ import ( "github.com/protolambda/zrnt/eth2/beacon/common" "github.com/protolambda/zrnt/eth2/configs" "github.com/protolambda/ztyp/codec" + tree "github.com/protolambda/ztyp/tree" ) var ( @@ -15,7 +16,7 @@ var ( Capella common.ForkDigest = [4]byte{0xbb, 0xa4, 0xda, 0x96} ) -//go:generate sszgen --path types.go +//go:generate sszgen --path types.go --exclude-objs ForkedLightClientBootstrap,ForkedLightClientUpdate,LightClientUpdateRange type LightClientUpdateKey struct { StartPeriod uint64 @@ -39,21 +40,21 @@ type ForkedLightClientBootstrap struct { Bootstrap common.SpecObj } -func (flb *ForkedLightClientBootstrap) Deserialize(dr *codec.DecodingReader) error { - _, err := dr.Read(flb.ForkDigest[:]) +func (flcb *ForkedLightClientBootstrap) Deserialize(dr *codec.DecodingReader) error { + _, err := dr.Read(flcb.ForkDigest[:]) if err != nil { return err } - if flb.ForkDigest == Bellatrix { - flb.Bootstrap = &altair.LightClientBootstrap{} - } else if flb.ForkDigest == Capella { - flb.Bootstrap = &capella.LightClientBootstrap{} + if flcb.ForkDigest == Bellatrix { + flcb.Bootstrap = &altair.LightClientBootstrap{} + } else if flcb.ForkDigest == Capella { + flcb.Bootstrap = &capella.LightClientBootstrap{} } else { return errors.New("unknown fork digest") } - err = flb.Bootstrap.Deserialize(configs.Mainnet, dr) + err = flcb.Bootstrap.Deserialize(configs.Mainnet, dr) if err != nil { return err } @@ -61,6 +62,104 @@ func (flb *ForkedLightClientBootstrap) Deserialize(dr *codec.DecodingReader) err return nil } -func (flb *ForkedLightClientBootstrap) FixedLength() uint64 { +func (flcb *ForkedLightClientBootstrap) Serialize(w *codec.EncodingWriter) error { + if err := w.Write(flcb.ForkDigest[:]); err != nil { + return err + } + return flcb.Bootstrap.Serialize(configs.Mainnet, w) +} + +func (flcb *ForkedLightClientBootstrap) FixedLength() uint64 { return 0 } + +func (flcb *ForkedLightClientBootstrap) ByteLength() uint64 { + return 4 + flcb.Bootstrap.ByteLength(configs.Mainnet) +} + +func (flcb *ForkedLightClientBootstrap) HashTreeRoot(h tree.HashFn) common.Root { + return h.HashTreeRoot(flcb.ForkDigest, configs.Mainnet.Wrap(flcb.Bootstrap)) +} + +type ForkedLightClientUpdate struct { + ForkDigest common.ForkDigest + LightClientUpdate common.SpecObj +} + +func (flcu *ForkedLightClientUpdate) Deserialize(dr *codec.DecodingReader) error { + _, err := dr.Read(flcu.ForkDigest[:]) + if err != nil { + return err + } + + if flcu.ForkDigest == Bellatrix { + flcu.LightClientUpdate = &altair.LightClientUpdate{} + } else if flcu.ForkDigest == Capella { + flcu.LightClientUpdate = &capella.LightClientUpdate{} + } else { + return errors.New("unknown fork digest") + } + + err = flcu.LightClientUpdate.Deserialize(configs.Mainnet, dr) + if err != nil { + return err + } + + return nil +} + +func (flcu *ForkedLightClientUpdate) Serialize(w *codec.EncodingWriter) error { + if err := w.Write(flcu.ForkDigest[:]); err != nil { + return err + } + return flcu.LightClientUpdate.Serialize(configs.Mainnet, w) +} + +func (flcu *ForkedLightClientUpdate) FixedLength() uint64 { + return 0 +} + +func (flcu *ForkedLightClientUpdate) ByteLength() uint64 { + return 4 + flcu.LightClientUpdate.ByteLength(configs.Mainnet) +} + +func (flcu *ForkedLightClientUpdate) HashTreeRoot(h tree.HashFn) common.Root { + return h.HashTreeRoot(flcu.ForkDigest, configs.Mainnet.Wrap(flcu.LightClientUpdate)) +} + +type LightClientUpdateRange []ForkedLightClientUpdate + +func (r *LightClientUpdateRange) Deserialize(dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*r) + *r = append(*r, ForkedLightClientUpdate{}) + return &((*r)[i]) + }, 0, 128) +} + +func (r LightClientUpdateRange) Serialize(w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return &r[i] + }, 0, uint64(len(r))) +} + +func (r LightClientUpdateRange) ByteLength() (out uint64) { + for _, v := range r { + out += v.ByteLength() + codec.OFFSET_SIZE + } + return +} + +func (r *LightClientUpdateRange) FixedLength() uint64 { + return 0 +} + +func (r LightClientUpdateRange) HashTreeRoot(hFn tree.HashFn) common.Root { + length := uint64(len(r)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return &r[i] + } + return nil + }, length, 128) +} diff --git a/portalnetwork/beacon/types_encoding.go b/portalnetwork/beacon/types_encoding.go index 398a7bff90..95eb623d9c 100644 --- a/portalnetwork/beacon/types_encoding.go +++ b/portalnetwork/beacon/types_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 7fed4f33c894dec224a58ca39c80acbd272f7fae47aa077a94e75a98a24804c5 +// Hash: 7b06aa2a0612821c21a42cced443e791afc5c4f842d09312d0f29e40102138a3 // Version: 0.1.3 package beacon diff --git a/portalnetwork/beacon/types_test.go b/portalnetwork/beacon/types_test.go index 8b5d3efdc7..8019c71942 100644 --- a/portalnetwork/beacon/types_test.go +++ b/portalnetwork/beacon/types_test.go @@ -29,5 +29,35 @@ func TestForkedLightClientBootstrap(t *testing.T) { err := f.Deserialize(dec) assert.NoError(t, err) assert.Equal(t, k, f.Bootstrap.(*capella.LightClientBootstrap).Header.Beacon.Slot.String()) + + var buf bytes.Buffer + err = f.Serialize(codec.NewEncodingWriter(&buf)) + assert.NoError(t, err) + assert.Equal(t, b, buf.Bytes()) + } +} + +func TestLightClientUpdateRange(t *testing.T) { + filePath := "testdata/light_client_updates_by_range.json" + + f, _ := os.Open(filePath) + jsonStr, _ := io.ReadAll(f) + + var result map[string]interface{} + _ = json.Unmarshal(jsonStr, &result) + + for k, v := range result { + b, _ := hexutil.Decode(v.(map[string]interface{})["content_value"].(string)) + dec := codec.NewDecodingReader(bytes.NewReader(b), uint64(len(b))) + var f LightClientUpdateRange = make([]ForkedLightClientUpdate, 0) + err := f.Deserialize(dec) + assert.NoError(t, err) + assert.Equal(t, k, f[0].LightClientUpdate.(*capella.LightClientUpdate).AttestedHeader.Beacon.Slot.String()) + assert.Equal(t, 4, len(f)) + + var buf bytes.Buffer + err = f.Serialize(codec.NewEncodingWriter(&buf)) + assert.NoError(t, err) + assert.Equal(t, b, buf.Bytes()) } }