mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 12:46:44 +00:00
feat: fetch blobs via /eth/v1/beacon/blobs from beacon node (#1244)
* feat: featch blobs via /eth/v1/beacon/blobs from beacon node * fix * fix query parameter syntax * bump version
This commit is contained in:
parent
9cfdf865ca
commit
ff72fff220
3 changed files with 40 additions and 50 deletions
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 9 // Minor version component of the current release
|
VersionMinor = 9 // Minor version component of the current release
|
||||||
VersionPatch = 9 // Patch version component of the current release
|
VersionPatch = 10 // Patch version component of the current release
|
||||||
VersionMeta = "mainnet" // Version metadata to append to the version string
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package blob_client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -29,7 +28,7 @@ type BeaconNodeClient struct {
|
||||||
var (
|
var (
|
||||||
beaconNodeGenesisEndpoint = "/eth/v1/beacon/genesis"
|
beaconNodeGenesisEndpoint = "/eth/v1/beacon/genesis"
|
||||||
beaconNodeSpecEndpoint = "/eth/v1/config/spec"
|
beaconNodeSpecEndpoint = "/eth/v1/config/spec"
|
||||||
beaconNodeBlobEndpoint = "/eth/v1/beacon/blob_sidecars"
|
beaconNodeBlobEndpoint = "/eth/v1/beacon/blobs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewBeaconNodeClient(apiEndpoint string) (*BeaconNodeClient, error) {
|
func NewBeaconNodeClient(apiEndpoint string) (*BeaconNodeClient, error) {
|
||||||
|
|
@ -106,15 +105,31 @@ func NewBeaconNodeClient(apiEndpoint string) (*BeaconNodeClient, error) {
|
||||||
}, nil
|
}, 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", 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) {
|
func (c *BeaconNodeClient) GetBlobByVersionedHashAndBlockTime(ctx context.Context, versionedHash common.Hash, blockTime uint64) (*kzg4844.Blob, error) {
|
||||||
slot := (blockTime - c.genesisTime) / c.secondsPerSlot
|
slot := (blockTime - c.genesisTime) / c.secondsPerSlot
|
||||||
|
|
||||||
// get blob sidecar for slot
|
// get blob by slot and versioned hash
|
||||||
blobSidecarPath, err := url.JoinPath(c.apiEndpoint, beaconNodeBlobEndpoint, fmt.Sprintf("%d", slot))
|
getBlobsPath, err := c.getBlobsPath(slot, versionedHash)
|
||||||
if err != nil {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create request, err: %w", err)
|
return nil, fmt.Errorf("failed to create request, err: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -133,35 +148,27 @@ func (c *BeaconNodeClient) GetBlobByVersionedHashAndBlockTime(ctx context.Contex
|
||||||
return nil, fmt.Errorf("beacon node request failed, status: %s, body: %s", resp.Status, bodyStr)
|
return nil, fmt.Errorf("beacon node request failed, status: %s, body: %s", resp.Status, bodyStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var blobSidecarResp BlobSidecarResp
|
var blobsResp BlobsResp
|
||||||
err = json.NewDecoder(resp.Body).Decode(&blobSidecarResp)
|
err = json.NewDecoder(resp.Body).Decode(&blobsResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to decode result into struct, err: %w", err)
|
return nil, fmt.Errorf("failed to decode result into struct, err: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// find blob with desired versionedHash
|
// sanity check response length
|
||||||
for _, blob := range blobSidecarResp.Data {
|
if len(blobsResp.Data) == 0 {
|
||||||
// calculate blob hash from commitment and check it with desired
|
return nil, fmt.Errorf("missing blob %v in slot %d", versionedHash, slot)
|
||||||
commitmentBytes := common.FromHex(blob.KzgCommitment)
|
}
|
||||||
if len(commitmentBytes) != lenKZGCommitment {
|
if len(blobsResp.Data) > 1 {
|
||||||
return nil, fmt.Errorf("len of kzg commitment is not correct, expected: %d, got: %d", lenKZGCommitment, len(commitmentBytes))
|
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))
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
type GenesisResp struct {
|
||||||
|
|
@ -176,22 +183,6 @@ type SpecResp struct {
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlobSidecarResp struct {
|
type BlobsResp struct {
|
||||||
Data []struct {
|
Data []string `json:"data"` // array of blobs as hex strings
|
||||||
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"`
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
lenBlobBytes int = 131072
|
lenBlobBytes int = 131072
|
||||||
lenKZGCommitment int = 48
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlobClient interface {
|
type BlobClient interface {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue