@@ -2,7 +2,6 @@ package blob_client
22
33import (
44 "context"
5- "crypto/sha256"
65 "encoding/json"
76 "fmt"
87 "io"
@@ -29,7 +28,7 @@ type BeaconNodeClient struct {
2928var (
3029 beaconNodeGenesisEndpoint = "/eth/v1/beacon/genesis"
3130 beaconNodeSpecEndpoint = "/eth/v1/config/spec"
32- beaconNodeBlobEndpoint = "/eth/v1/beacon/blob_sidecars "
31+ beaconNodeBlobEndpoint = "/eth/v1/beacon/blobs "
3332)
3433
3534func NewBeaconNodeClient (apiEndpoint string ) (* BeaconNodeClient , error ) {
@@ -106,15 +105,31 @@ func NewBeaconNodeClient(apiEndpoint string) (*BeaconNodeClient, error) {
106105 }, nil
107106}
108107
108+ func (c * BeaconNodeClient ) getBlobsPath (slot uint64 , versionedHash common.Hash ) (string , error ) {
109+ basePath , err := url .JoinPath (c .apiEndpoint , beaconNodeBlobEndpoint , fmt .Sprintf ("%d" , slot ))
110+ if err != nil {
111+ return "" , fmt .Errorf ("failed to join path, err: %w" , err )
112+ }
113+ u , err := url .Parse (basePath )
114+ if err != nil {
115+ return "" , fmt .Errorf ("failed to parse path, err: %w" , err )
116+ }
117+ q := u .Query ()
118+ q .Set ("versioned_hashes" , versionedHash .Hex ())
119+ u .RawQuery = q .Encode ()
120+ queryPath := u .String ()
121+ return queryPath , nil
122+ }
123+
109124func (c * BeaconNodeClient ) GetBlobByVersionedHashAndBlockTime (ctx context.Context , versionedHash common.Hash , blockTime uint64 ) (* kzg4844.Blob , error ) {
110125 slot := (blockTime - c .genesisTime ) / c .secondsPerSlot
111126
112- // get blob sidecar for slot
113- blobSidecarPath , err := url . JoinPath ( c . apiEndpoint , beaconNodeBlobEndpoint , fmt . Sprintf ( "%d" , slot ) )
127+ // get blob by slot and versioned hash
128+ getBlobsPath , err := c . getBlobsPath ( slot , versionedHash )
114129 if err != nil {
115- return nil , fmt .Errorf ("failed to join path, err: %w" , err )
130+ return nil , fmt .Errorf ("failed to create getBlobs path, err: %w" , err )
116131 }
117- req , err := http .NewRequestWithContext (ctx , "GET" , blobSidecarPath , nil )
132+ req , err := http .NewRequestWithContext (ctx , "GET" , getBlobsPath , nil )
118133 if err != nil {
119134 return nil , fmt .Errorf ("failed to create request, err: %w" , err )
120135 }
@@ -133,35 +148,27 @@ func (c *BeaconNodeClient) GetBlobByVersionedHashAndBlockTime(ctx context.Contex
133148 return nil , fmt .Errorf ("beacon node request failed, status: %s, body: %s" , resp .Status , bodyStr )
134149 }
135150
136- var blobSidecarResp BlobSidecarResp
137- err = json .NewDecoder (resp .Body ).Decode (& blobSidecarResp )
151+ var blobsResp BlobsResp
152+ err = json .NewDecoder (resp .Body ).Decode (& blobsResp )
138153 if err != nil {
139154 return nil , fmt .Errorf ("failed to decode result into struct, err: %w" , err )
140155 }
141156
142- // find blob with desired versionedHash
143- for _ , blob := range blobSidecarResp .Data {
144- // calculate blob hash from commitment and check it with desired
145- commitmentBytes := common .FromHex (blob .KzgCommitment )
146- if len (commitmentBytes ) != lenKZGCommitment {
147- return nil , fmt .Errorf ("len of kzg commitment is not correct, expected: %d, got: %d" , lenKZGCommitment , len (commitmentBytes ))
148- }
149- commitment := kzg4844 .Commitment (commitmentBytes )
150- blobVersionedHash := kzg4844 .CalcBlobHashV1 (sha256 .New (), & commitment )
151-
152- if blobVersionedHash == versionedHash {
153- // found desired blob
154- blobBytes := common .FromHex (blob .Blob )
155- if len (blobBytes ) != lenBlobBytes {
156- return nil , fmt .Errorf ("len of blob data is not correct, expected: %d, got: %d" , lenBlobBytes , len (blobBytes ))
157- }
158-
159- b := kzg4844 .Blob (blobBytes )
160- return & b , nil
161- }
157+ // sanity check response length
158+ if len (blobsResp .Data ) == 0 {
159+ return nil , fmt .Errorf ("missing blob %v in slot %d" , versionedHash , slot )
160+ }
161+ if len (blobsResp .Data ) > 1 {
162+ return nil , fmt .Errorf ("more than 1 blob returned from beacon node for slot %d, requested blob hash: %s, expected 1, got: %d" , slot , versionedHash .Hex (), len (blobsResp .Data ))
162163 }
163164
164- return nil , fmt .Errorf ("missing blob %v in slot %d" , versionedHash , slot )
165+ blobBytes := common .FromHex (blobsResp .Data [0 ])
166+ if len (blobBytes ) != lenBlobBytes {
167+ return nil , fmt .Errorf ("len of blob data is not correct, expected: %d, got: %d" , lenBlobBytes , len (blobBytes ))
168+ }
169+
170+ b := kzg4844 .Blob (blobBytes )
171+ return & b , nil
165172}
166173
167174type GenesisResp struct {
@@ -176,22 +183,6 @@ type SpecResp struct {
176183 } `json:"data"`
177184}
178185
179- type BlobSidecarResp struct {
180- Data []struct {
181- Index string `json:"index"`
182- Blob string `json:"blob"`
183- KzgCommitment string `json:"kzg_commitment"`
184- KzgProof string `json:"kzg_proof"`
185- SignedBlockHeader struct {
186- Message struct {
187- Slot string `json:"slot"`
188- ProposerIndex string `json:"proposer_index"`
189- ParentRoot string `json:"parent_root"`
190- StateRoot string `json:"state_root"`
191- BodyRoot string `json:"body_root"`
192- } `json:"message"`
193- Signature string `json:"signature"`
194- } `json:"signed_block_header"`
195- KzgCommitmentInclusionProof []string `json:"kzg_commitment_inclusion_proof"`
196- } `json:"data"`
186+ type BlobsResp struct {
187+ Data []string `json:"data"` // array of blobs as hex strings
197188}
0 commit comments