From cbd5efca253009cc83e733065376916eb2a06945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 8 Oct 2025 14:20:39 +0200 Subject: [PATCH 1/3] feat: featch blobs via /eth/v1/beacon/blobs from beacon node --- params/version.go | 2 +- .../blob_client/beacon_node_client.go | 63 ++++++------------- 2 files changed, 20 insertions(+), 45 deletions(-) diff --git a/params/version.go b/params/version.go index 25e472d7298..8a779174ef3 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 9 // Minor version component of the current release - VersionPatch = 5 // Patch version component of the current release + VersionPatch = 6 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/da_syncer/blob_client/beacon_node_client.go b/rollup/da_syncer/blob_client/beacon_node_client.go index 98e922aa553..e8e5bf9f7b7 100644 --- a/rollup/da_syncer/blob_client/beacon_node_client.go +++ b/rollup/da_syncer/blob_client/beacon_node_client.go @@ -2,7 +2,6 @@ package blob_client import ( "context" - "crypto/sha256" "encoding/json" "fmt" "io" @@ -29,7 +28,7 @@ type BeaconNodeClient struct { var ( beaconNodeGenesisEndpoint = "/eth/v1/beacon/genesis" beaconNodeSpecEndpoint = "/eth/v1/config/spec" - beaconNodeBlobEndpoint = "/eth/v1/beacon/blob_sidecars" + beaconNodeBlobEndpoint = "/eth/v1/beacon/blobs" ) func NewBeaconNodeClient(apiEndpoint string) (*BeaconNodeClient, error) { @@ -110,7 +109,7 @@ func (c *BeaconNodeClient) GetBlobByVersionedHashAndBlockTime(ctx context.Contex slot := (blockTime - c.genesisTime) / c.secondsPerSlot // get blob sidecar for slot - blobSidecarPath, err := url.JoinPath(c.apiEndpoint, beaconNodeBlobEndpoint, fmt.Sprintf("%d", slot)) + blobSidecarPath, err := url.JoinPath(c.apiEndpoint, beaconNodeBlobEndpoint, fmt.Sprintf("%d?versioned_hashes=[%s]", slot, versionedHash)) if err != nil { return nil, fmt.Errorf("failed to join path, err: %w", err) } @@ -133,35 +132,27 @@ func (c *BeaconNodeClient) GetBlobByVersionedHashAndBlockTime(ctx context.Contex return nil, fmt.Errorf("beacon node request failed, status: %s, body: %s", resp.Status, bodyStr) } - var blobSidecarResp BlobSidecarResp - err = json.NewDecoder(resp.Body).Decode(&blobSidecarResp) + var blobsResp BlobsResp + err = json.NewDecoder(resp.Body).Decode(&blobsResp) if err != nil { return nil, fmt.Errorf("failed to decode result into struct, err: %w", err) } - // find blob with desired versionedHash - for _, blob := range blobSidecarResp.Data { - // calculate blob hash from commitment and check it with desired - commitmentBytes := common.FromHex(blob.KzgCommitment) - if len(commitmentBytes) != lenKZGCommitment { - return nil, fmt.Errorf("len of kzg commitment is not correct, expected: %d, got: %d", lenKZGCommitment, len(commitmentBytes)) - } - commitment := kzg4844.Commitment(commitmentBytes) - blobVersionedHash := kzg4844.CalcBlobHashV1(sha256.New(), &commitment) - - if blobVersionedHash == versionedHash { - // found desired blob - blobBytes := common.FromHex(blob.Blob) - if len(blobBytes) != lenBlobBytes { - return nil, fmt.Errorf("len of blob data is not correct, expected: %d, got: %d", lenBlobBytes, len(blobBytes)) - } - - b := kzg4844.Blob(blobBytes) - return &b, nil - } + // sanity check response length + if len(blobsResp.Data) == 0 { + return nil, fmt.Errorf("missing blob %v in slot %d", versionedHash, slot) + } + if len(blobsResp.Data) > 1 { + 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)) } - return nil, fmt.Errorf("missing blob %v in slot %d", versionedHash, slot) + blobBytes := common.FromHex(blobsResp.Data[0]) + if len(blobBytes) != lenBlobBytes { + return nil, fmt.Errorf("len of blob data is not correct, expected: %d, got: %d", lenBlobBytes, len(blobBytes)) + } + + b := kzg4844.Blob(blobBytes) + return &b, nil } type GenesisResp struct { @@ -176,22 +167,6 @@ type SpecResp struct { } `json:"data"` } -type BlobSidecarResp struct { - Data []struct { - Index string `json:"index"` - Blob string `json:"blob"` - KzgCommitment string `json:"kzg_commitment"` - KzgProof string `json:"kzg_proof"` - SignedBlockHeader struct { - Message struct { - Slot string `json:"slot"` - ProposerIndex string `json:"proposer_index"` - ParentRoot string `json:"parent_root"` - StateRoot string `json:"state_root"` - BodyRoot string `json:"body_root"` - } `json:"message"` - Signature string `json:"signature"` - } `json:"signed_block_header"` - KzgCommitmentInclusionProof []string `json:"kzg_commitment_inclusion_proof"` - } `json:"data"` +type BlobsResp struct { + Data []string `json:"data"` // array of blobs as hex strings } From e3b2c4cdab9b462972f4160125272d60aca5e80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 8 Oct 2025 14:35:01 +0200 Subject: [PATCH 2/3] fix --- .../blob_client/beacon_node_client.go | 24 +++++++++++++++---- rollup/da_syncer/blob_client/blob_client.go | 3 +-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/rollup/da_syncer/blob_client/beacon_node_client.go b/rollup/da_syncer/blob_client/beacon_node_client.go index e8e5bf9f7b7..7e9b830ca51 100644 --- a/rollup/da_syncer/blob_client/beacon_node_client.go +++ b/rollup/da_syncer/blob_client/beacon_node_client.go @@ -105,15 +105,31 @@ func NewBeaconNodeClient(apiEndpoint string) (*BeaconNodeClient, error) { }, nil } +func (c *BeaconNodeClient) getBlobsPath(slot uint64, versionedHash common.Hash) (string, error) { + basePath, err := url.JoinPath(c.apiEndpoint, beaconNodeBlobEndpoint, fmt.Sprintf("%d", slot)) + if err != nil { + return "", fmt.Errorf("failed to join path, err: %w", err) + } + u, err := url.Parse(basePath) + if err != nil { + return "", fmt.Errorf("failed to parse path, err: %w", err) + } + q := u.Query() + q.Set("versioned_hashes", fmt.Sprintf("[%s]", versionedHash.Hex())) + u.RawQuery = q.Encode() + queryPath := u.String() + return queryPath, nil +} + func (c *BeaconNodeClient) GetBlobByVersionedHashAndBlockTime(ctx context.Context, versionedHash common.Hash, blockTime uint64) (*kzg4844.Blob, error) { slot := (blockTime - c.genesisTime) / c.secondsPerSlot - // get blob sidecar for slot - blobSidecarPath, err := url.JoinPath(c.apiEndpoint, beaconNodeBlobEndpoint, fmt.Sprintf("%d?versioned_hashes=[%s]", slot, versionedHash)) + // get blob by slot and versioned hash + getBlobsPath, err := c.getBlobsPath(slot, versionedHash) if err != nil { - return nil, fmt.Errorf("failed to join path, err: %w", err) + return nil, fmt.Errorf("failed to create getBlobs path, err: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", blobSidecarPath, nil) + req, err := http.NewRequestWithContext(ctx, "GET", getBlobsPath, nil) if err != nil { return nil, fmt.Errorf("failed to create request, err: %w", err) } diff --git a/rollup/da_syncer/blob_client/blob_client.go b/rollup/da_syncer/blob_client/blob_client.go index 70635311559..b56a45e2d9e 100644 --- a/rollup/da_syncer/blob_client/blob_client.go +++ b/rollup/da_syncer/blob_client/blob_client.go @@ -12,8 +12,7 @@ import ( ) const ( - lenBlobBytes int = 131072 - lenKZGCommitment int = 48 + lenBlobBytes int = 131072 ) type BlobClient interface { From d8ebd7db2bc6cee7b40c4adc94f006aa6ea90e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 8 Oct 2025 14:44:18 +0200 Subject: [PATCH 3/3] fix query parameter syntax --- rollup/da_syncer/blob_client/beacon_node_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup/da_syncer/blob_client/beacon_node_client.go b/rollup/da_syncer/blob_client/beacon_node_client.go index 7e9b830ca51..91449e13069 100644 --- a/rollup/da_syncer/blob_client/beacon_node_client.go +++ b/rollup/da_syncer/blob_client/beacon_node_client.go @@ -115,7 +115,7 @@ func (c *BeaconNodeClient) getBlobsPath(slot uint64, versionedHash common.Hash) return "", fmt.Errorf("failed to parse path, err: %w", err) } q := u.Query() - q.Set("versioned_hashes", fmt.Sprintf("[%s]", versionedHash.Hex())) + q.Set("versioned_hashes", versionedHash.Hex()) u.RawQuery = q.Encode() queryPath := u.String() return queryPath, nil