@@ -38,11 +38,46 @@ type RestApi struct {
3838 Url string
3939}
4040
41+ // GetBestUpdateAndCommittee fetches and validates LightClientUpdate for given period and full serialized
42+ // committee for the next period (committee root hash equals update.NextSyncCommitteeRoot).
43+ // Note that the results are validated but the update signature should be verified by the caller as its
44+ // validity depends on the update chain.
4145func (api * RestApi ) GetBestUpdateAndCommittee (period uint64 ) (beacon.LightClientUpdate , []byte , error ) {
42- c , err := api .getCommitteeUpdate (period )
46+ periodStr := strconv .Itoa (int (period ))
47+ resp , err := http .Get (api .Url + "/eth/v1/beacon/light_client/updates?start_period=" + periodStr + "&count=1" )
4348 if err != nil {
4449 return beacon.LightClientUpdate {}, nil , err
4550 }
51+ body , err := ioutil .ReadAll (resp .Body )
52+ resp .Body .Close ()
53+ if err != nil {
54+ return beacon.LightClientUpdate {}, nil , err
55+ }
56+
57+ type committeeUpdate struct {
58+ Header beacon.Header `json:"attested_header"`
59+ NextSyncCommittee syncCommitteeJson `json:"next_sync_committee"`
60+ NextSyncCommitteeBranch beacon.MerkleValues `json:"next_sync_committee_branch"`
61+ FinalizedHeader beacon.Header `json:"finalized_header"`
62+ FinalityBranch beacon.MerkleValues `json:"finality_branch"`
63+ Aggregate syncAggregate `json:"sync_aggregate"`
64+ ForkVersion hexutil.Bytes `json:"fork_version"`
65+ }
66+
67+ var data struct {
68+ Data []committeeUpdate `json:"data"`
69+ }
70+ if err := json .Unmarshal (body , & data ); err != nil {
71+ return beacon.LightClientUpdate {}, nil , err
72+ }
73+ if len (data .Data ) != 1 {
74+ return beacon.LightClientUpdate {}, nil , errors .New ("invalid number of committee updates" )
75+ }
76+ c := data .Data [0 ]
77+ if len (c .NextSyncCommittee .Pubkeys ) != 512 {
78+ return beacon.LightClientUpdate {}, nil , errors .New ("invalid number of pubkeys in next_sync_committee" )
79+ }
80+
4681 committee , ok := c .NextSyncCommittee .serialize ()
4782 if ! ok {
4883 return beacon.LightClientUpdate {}, nil , errors .New ("invalid sync committee" )
@@ -57,14 +92,24 @@ func (api *RestApi) GetBestUpdateAndCommittee(period uint64) (beacon.LightClient
5792 SyncCommitteeSignature : c .Aggregate .Signature ,
5893 ForkVersion : c .ForkVersion ,
5994 }
95+ if err := update .Validate (); err != nil {
96+ return beacon.LightClientUpdate {}, nil , err
97+ }
98+ if beacon .SerializedCommitteeRoot (committee ) != update .NextSyncCommitteeRoot {
99+ return beacon.LightClientUpdate {}, nil , errors .New ("sync committee root does not match" )
100+ }
60101 return update , committee , nil
61102}
62103
104+ // syncAggregate represents an aggregated BLS signature with BitMask referring to a subset
105+ // of the corresponding sync committee
63106type syncAggregate struct {
64107 BitMask hexutil.Bytes `json:"sync_committee_bits"`
65108 Signature hexutil.Bytes `json:"sync_committee_signature"`
66109}
67110
111+ // GetHeadUpdate fetches the latest available signed header.
112+ // Note that the signature should be verified by the caller as its validity depends on the update chain.
68113func (api * RestApi ) GetHeadUpdate () (beacon.SignedHead , error ) {
69114 resp , err := http .Get (api .Url + "/eth/v1/beacon/light_client/optimistic_update/" )
70115 if err != nil {
@@ -98,11 +143,13 @@ func (api *RestApi) GetHeadUpdate() (beacon.SignedHead, error) {
98143 }, nil
99144}
100145
146+ // syncCommitteeJson is the JSON representation of a sync committee
101147type syncCommitteeJson struct {
102148 Pubkeys []hexutil.Bytes `json:"pubkeys"`
103149 Aggregate hexutil.Bytes `json:"aggregate_pubkey"`
104150}
105151
152+ // serialize returns the serialized version of the committee
106153func (s * syncCommitteeJson ) serialize () ([]byte , bool ) {
107154 if len (s .Pubkeys ) != 512 {
108155 return nil , false
@@ -121,45 +168,8 @@ func (s *syncCommitteeJson) serialize() ([]byte, bool) {
121168 return sk , true
122169}
123170
124- type committeeUpdate struct {
125- Header beacon.Header `json:"attested_header"`
126- NextSyncCommittee syncCommitteeJson `json:"next_sync_committee"`
127- NextSyncCommitteeBranch beacon.MerkleValues `json:"next_sync_committee_branch"`
128- FinalizedHeader beacon.Header `json:"finalized_header"`
129- FinalityBranch beacon.MerkleValues `json:"finality_branch"`
130- Aggregate syncAggregate `json:"sync_aggregate"`
131- ForkVersion hexutil.Bytes `json:"fork_version"`
132- }
133-
134- func (api * RestApi ) getCommitteeUpdate (period uint64 ) (committeeUpdate , error ) {
135- periodStr := strconv .Itoa (int (period ))
136- resp , err := http .Get (api .Url + "/eth/v1/beacon/light_client/updates?start_period=" + periodStr + "&count=1" )
137- if err != nil {
138- return committeeUpdate {}, err
139- }
140- body , err := ioutil .ReadAll (resp .Body )
141- resp .Body .Close ()
142- if err != nil {
143- return committeeUpdate {}, err
144- }
145-
146- var data struct {
147- Data []committeeUpdate `json:"data"`
148- }
149- if err := json .Unmarshal (body , & data ); err != nil {
150- return committeeUpdate {}, err
151- }
152- if len (data .Data ) != 1 {
153- return committeeUpdate {}, errors .New ("invalid number of committee updates" )
154- }
155- update := data .Data [0 ]
156- if len (update .NextSyncCommittee .Pubkeys ) != 512 {
157- return committeeUpdate {}, errors .New ("invalid number of pubkeys in next_sync_committee" )
158- }
159- return update , nil
160- }
161-
162- // null hash -> current head
171+ // GetHead fetches and validates the beacon header with the given blockRoot.
172+ // If blockRoot is null hash then the latest head header is fetched.
163173func (api * RestApi ) GetHeader (blockRoot common.Hash ) (beacon.Header , error ) {
164174 url := api .Url + "/eth/v1/beacon/headers/"
165175 if blockRoot == (common.Hash {}) {
@@ -200,6 +210,10 @@ func (api *RestApi) GetHeader(blockRoot common.Hash) (beacon.Header, error) {
200210 return header , nil
201211}
202212
213+ // GetStateProof fetches and validates a Merkle proof for the specified parts of the recent
214+ // beacon state referenced by stateRoot. If successful the returned multiproof has the format
215+ // specified by expFormat. The state subset specified by the list of string keys (paths) should
216+ // cover the subset specified by expFormat.
203217func (api * RestApi ) GetStateProof (stateRoot common.Hash , paths []string , expFormat beacon.ProofFormat ) (beacon.MultiProof , error ) {
204218 url := api .Url + "/eth/v1/beacon/light_client/proof/" + stateRoot .Hex () + "?paths=" + paths [0 ]
205219 for i := 1 ; i < len (paths ); i ++ {
@@ -226,6 +240,7 @@ func (api *RestApi) GetStateProof(stateRoot common.Hash, paths []string, expForm
226240 return beacon.MultiProof {Format : expFormat , Values : values }, nil
227241}
228242
243+ // GetCheckpointData fetches and validates bootstrap data belonging to the given checkpoint.
229244func (api * RestApi ) GetCheckpointData (ctx context.Context , checkpoint common.Hash ) (beacon.Header , beacon.CheckpointData , []byte , error ) {
230245 resp , err := http .Get (api .Url + "/eth/v1/beacon/light_client/bootstrap/" + checkpoint .String ())
231246 if err != nil {
@@ -267,9 +282,10 @@ func (api *RestApi) GetCheckpointData(ctx context.Context, checkpoint common.Has
267282
268283}
269284
270- // beacon block root -> exec block
271- func (api * RestApi ) GetExecutionPayload ( /*ctx context.Context, */ blockRoot , execRoot common.Hash ) (* types.Block , error ) {
272- resp , err := http .Get (api .Url + "/eth/v2/beacon/blocks/" + blockRoot .Hex ())
285+ // GetExecutionPayload fetches the execution block belonging to the beacon block specified
286+ // by beaconRoot and validates its block hash against the expected execRoot.
287+ func (api * RestApi ) GetExecutionPayload (beaconRoot , execRoot common.Hash ) (* types.Block , error ) {
288+ resp , err := http .Get (api .Url + "/eth/v2/beacon/blocks/" + beaconRoot .Hex ())
273289 if err != nil {
274290 return nil , err
275291 }
0 commit comments