diff --git a/pkg/beacon/api/api.go b/pkg/beacon/api/api.go index f20f956..881478e 100644 --- a/pkg/beacon/api/api.go +++ b/pkg/beacon/api/api.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -13,12 +14,18 @@ import ( "github.com/sirupsen/logrus" ) +// ErrNotFound is returned when the node responds with HTTP 404 for the +// requested resource, e.g. an execution payload envelope that has not been +// revealed or a block that does not exist. +var ErrNotFound = errors.New("not found") + // ConsensusClient is an interface for executing RPC calls to the Ethereum node. type ConsensusClient interface { NodePeer(ctx context.Context, peerID string) (types.Peer, error) NodePeers(ctx context.Context) (types.Peers, error) NodePeerCount(ctx context.Context) (types.PeerCount, error) RawBlock(ctx context.Context, stateID string, contentType string) ([]byte, error) + RawExecutionPayloadEnvelope(ctx context.Context, blockID string, contentType string) ([]byte, error) RawDebugBeaconState(ctx context.Context, stateID string, contentType string) ([]byte, error) DepositSnapshot(ctx context.Context) (*types.DepositSnapshot, error) NodeIdentity(ctx context.Context) (*types.Identity, error) @@ -152,6 +159,10 @@ func (c *consensusClient) getRaw(ctx context.Context, path string, contentType s defer rsp.Body.Close() if rsp.StatusCode != http.StatusOK { + if rsp.StatusCode == http.StatusNotFound { + return nil, fmt.Errorf("status code: %d: %w", rsp.StatusCode, ErrNotFound) + } + return nil, fmt.Errorf("status code: %d", rsp.StatusCode) } @@ -223,6 +234,17 @@ func (c *consensusClient) RawBlock(ctx context.Context, stateID string, contentT return data, nil } +// RawExecutionPayloadEnvelope returns the signed execution payload envelope +// for the given block id in the requested format (gloas onwards). +func (c *consensusClient) RawExecutionPayloadEnvelope(ctx context.Context, blockID string, contentType string) ([]byte, error) { + data, err := c.getRaw(ctx, fmt.Sprintf("/eth/v1/beacon/execution_payload_envelopes/%s", blockID), contentType) + if err != nil { + return nil, err + } + + return data, nil +} + // DepositSnapshot returns the deposit snapshot in the requested format. func (c *consensusClient) DepositSnapshot(ctx context.Context) (*types.DepositSnapshot, error) { data, err := c.get(ctx, "/eth/v1/beacon/deposit_snapshot") diff --git a/pkg/beacon/beacon.go b/pkg/beacon/beacon.go index 277f6e6..b797ce1 100644 --- a/pkg/beacon/beacon.go +++ b/pkg/beacon/beacon.go @@ -67,6 +67,9 @@ type Node interface { FetchBlock(ctx context.Context, stateID string) (*spec.VersionedSignedBeaconBlock, error) // FetchRawBlock fetches the raw, unparsed block for the given state id. FetchRawBlock(ctx context.Context, stateID string, contentType string) ([]byte, error) + // FetchRawExecutionPayloadEnvelope fetches the raw, unparsed signed execution + // payload envelope for the given block id (gloas onwards). + FetchRawExecutionPayloadEnvelope(ctx context.Context, blockID string, contentType string) ([]byte, error) // FetchBlockRoot fetches the block root for the given state id. FetchBlockRoot(ctx context.Context, stateID string) (*phase0.Root, error) // FetchBeaconState fetches the beacon state for the given state id. diff --git a/pkg/beacon/fetch.go b/pkg/beacon/fetch.go index 9595448..1dc1824 100644 --- a/pkg/beacon/fetch.go +++ b/pkg/beacon/fetch.go @@ -73,6 +73,10 @@ func (n *node) FetchRawBlock(ctx context.Context, stateID string, contentType st return n.api.RawBlock(ctx, stateID, contentType) } +func (n *node) FetchRawExecutionPayloadEnvelope(ctx context.Context, blockID string, contentType string) ([]byte, error) { + return n.api.RawExecutionPayloadEnvelope(ctx, blockID, contentType) +} + func (n *node) FetchBlockRoot(ctx context.Context, stateID string) (*phase0.Root, error) { return n.getBlockRoot(ctx, stateID) }