Skip to content

Commit

Permalink
ssz encode request to publish block
Browse files Browse the repository at this point in the history
  • Loading branch information
avalonche committed Oct 24, 2023
1 parent 36b0183 commit 716165c
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 54 deletions.
1 change: 1 addition & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ linters-settings:

gomoddirectives:
replace-allow-list:
- github.com/attestantio/go-builder-client
- github.com/attestantio/go-eth2-client

maintidx:
Expand Down
32 changes: 17 additions & 15 deletions beaconclient/prod_beacon_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ type ValidatorResponseValidatorData struct {
func (c *ProdBeaconInstance) GetStateValidators(stateID string) (*GetStateValidatorsResponse, error) {
uri := fmt.Sprintf("%s/eth/v1/beacon/states/%s/validators?status=active,pending", c.beaconURI, stateID)
vd := new(GetStateValidatorsResponse)
_, err := fetchBeacon(http.MethodGet, uri, nil, vd, nil, http.Header{})
_, err := fetchBeacon(http.MethodGet, uri, nil, vd, nil, http.Header{}, false)
return vd, err
}

Expand All @@ -172,7 +172,7 @@ func (c *ProdBeaconInstance) SyncStatus() (*SyncStatusPayloadData, error) {
uri := c.beaconURI + "/eth/v1/node/syncing"
timeout := 5 * time.Second
resp := new(SyncStatusPayload)
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, &timeout, http.Header{})
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, &timeout, http.Header{}, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -202,7 +202,7 @@ type ProposerDutiesResponseData struct {
func (c *ProdBeaconInstance) GetProposerDuties(epoch uint64) (*ProposerDutiesResponse, error) {
uri := fmt.Sprintf("%s/eth/v1/validator/duties/proposer/%d", c.beaconURI, epoch)
resp := new(ProposerDutiesResponse)
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

Expand All @@ -225,15 +225,15 @@ type GetHeaderResponseMessage struct {
func (c *ProdBeaconInstance) GetHeader() (*GetHeaderResponse, error) {
uri := fmt.Sprintf("%s/eth/v1/beacon/headers/head", c.beaconURI)
resp := new(GetHeaderResponse)
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

// GetHeaderForSlot returns the header for a given slot - https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader
func (c *ProdBeaconInstance) GetHeaderForSlot(slot uint64) (*GetHeaderResponse, error) {
uri := fmt.Sprintf("%s/eth/v1/beacon/headers/%d", c.beaconURI, slot)
resp := new(GetHeaderResponse)
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

Expand All @@ -253,15 +253,15 @@ type GetBlockResponse struct {
func (c *ProdBeaconInstance) GetBlock(blockID string) (block *GetBlockResponse, err error) {
uri := fmt.Sprintf("%s/eth/v2/beacon/blocks/%s", c.beaconURI, blockID)
resp := new(GetBlockResponse)
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

// GetBlockForSlot returns the block for a given slot - https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockV2
func (c *ProdBeaconInstance) GetBlockForSlot(slot uint64) (*GetBlockResponse, error) {
uri := fmt.Sprintf("%s/eth/v2/beacon/blocks/%d", c.beaconURI, slot)
resp := new(GetBlockResponse)
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

Expand All @@ -271,14 +271,16 @@ func (c *ProdBeaconInstance) GetURI() string {

func (c *ProdBeaconInstance) PublishBlock(block *common.VersionedSignedBlockRequest, broadcastMode BroadcastMode) (code int, err error) {
var uri string
headers := http.Header{}
headers.Add("Eth-Consensus-Version", block.Version.String()) // optional in v1, required in v2

if c.ffUseV2PublishBlockEndpoint {
uri = fmt.Sprintf("%s/eth/v2/beacon/blocks?broadcast_validation=%s", c.beaconURI, broadcastMode.String())
return fetchBeacon(http.MethodPost, uri, block, nil, nil, headers, true)
} else {
uri = fmt.Sprintf("%s/eth/v1/beacon/blocks", c.beaconURI)
return fetchBeacon(http.MethodPost, uri, block, nil, nil, headers, false)
}
headers := http.Header{}
headers.Add("Eth-Consensus-Version", block.Version.String()) // optional in v1, required in v2
return fetchBeacon(http.MethodPost, uri, block, nil, nil, headers)
}

type GetGenesisResponse struct {
Expand All @@ -295,7 +297,7 @@ type GetGenesisResponseData struct {
func (c *ProdBeaconInstance) GetGenesis() (*GetGenesisResponse, error) {
uri := fmt.Sprintf("%s/eth/v1/beacon/genesis", c.beaconURI)
resp := new(GetGenesisResponse)
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err := fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

Expand All @@ -312,7 +314,7 @@ type GetSpecResponse struct {
func (c *ProdBeaconInstance) GetSpec() (spec *GetSpecResponse, err error) {
uri := fmt.Sprintf("%s/eth/v1/config/spec", c.beaconURI)
resp := new(GetSpecResponse)
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

Expand All @@ -328,7 +330,7 @@ type GetForkScheduleResponse struct {
func (c *ProdBeaconInstance) GetForkSchedule() (spec *GetForkScheduleResponse, err error) {
uri := fmt.Sprintf("%s/eth/v1/config/fork_schedule", c.beaconURI)
resp := new(GetForkScheduleResponse)
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

Expand All @@ -342,7 +344,7 @@ type GetRandaoResponse struct {
func (c *ProdBeaconInstance) GetRandao(slot uint64) (randaoResp *GetRandaoResponse, err error) {
uri := fmt.Sprintf("%s/eth/v1/beacon/states/%d/randao", c.beaconURI, slot)
resp := new(GetRandaoResponse)
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}

Expand All @@ -356,6 +358,6 @@ type GetWithdrawalsResponse struct {
func (c *ProdBeaconInstance) GetWithdrawals(slot uint64) (withdrawalsResp *GetWithdrawalsResponse, err error) {
uri := fmt.Sprintf("%s/eth/v1/beacon/states/%d/withdrawals", c.beaconURI, slot)
resp := new(GetWithdrawalsResponse)
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{})
_, err = fetchBeacon(http.MethodGet, uri, nil, resp, nil, http.Header{}, false)
return resp, err
}
50 changes: 38 additions & 12 deletions beaconclient/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
)

var (
ErrHTTPErrorResponse = errors.New("got an HTTP error response")
ErrHTTPErrorResponse = errors.New("got an HTTP error response")
ErrInvalidRequestPayload = errors.New("invalid request payload")

StateIDHead = "head"
StateIDGenesis = "genesis"
Expand All @@ -30,28 +31,53 @@ func parseBroadcastModeString(s string) (BroadcastMode, bool) {
return b, ok
}

func fetchBeacon(method, url string, payload, dst any, timeout *time.Duration, headers http.Header) (code int, err error) {
func makeJSONRequest(method, url string, payload any) (*http.Request, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("could not marshal request: %w", err)
}
req, err := http.NewRequest(method, url, bytes.NewReader(payloadBytes))
if err != nil {
return nil, fmt.Errorf("invalid request for %s: %w", url, err)
}
// Set content-type
req.Header.Add("Content-Type", "application/json")
return req, nil
}

func makeSSZRequest(method, url string, payload any) (*http.Request, error) {
if payloadBytes, ok := payload.([]byte); ok {
req, err := http.NewRequest(method, url, bytes.NewReader(payloadBytes))
if err != nil {
return nil, fmt.Errorf("invalid request for %s: %w", url, err)
}
// Set content-type
req.Header.Add("Content-Type", "application/octet-stream")
return req, nil
}
return nil, fmt.Errorf("invalid payload type for SSZ request: %w", ErrInvalidRequestPayload)
}

func fetchBeacon(method, url string, payload, dst any, timeout *time.Duration, headers http.Header, ssz bool) (code int, err error) {
var req *http.Request

if payload == nil {
req, err = http.NewRequest(method, url, nil)
} else {
payloadBytes, err2 := json.Marshal(payload)
if err2 != nil {
return 0, fmt.Errorf("could not marshal request: %w", err2)
}
req, err = http.NewRequest(method, url, bytes.NewReader(payloadBytes))

// Set content-type
req.Header.Add("Content-Type", "application/json")
for k, v := range headers {
req.Header.Add(k, v[0])
if ssz {
req, err = makeSSZRequest(method, url, payload)
} else {
req, err = makeJSONRequest(method, url, payload)
}
}

if err != nil {
return 0, fmt.Errorf("invalid request for %s: %w", url, err)
}

for k, v := range headers {
req.Header.Add(k, v[0])
}
req.Header.Set("accept", "application/json")

client := http.DefaultClient
Expand Down
2 changes: 1 addition & 1 deletion common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func NewEthNetworkDetails(networkName string) (ret *EthNetworkDetails, err error
DomainBeaconProposerBellatrix: domainBeaconProposerBellatrix,
DomainBeaconProposerCapella: domainBeaconProposerCapella,
DomainBeaconProposerDeneb: domainBeaconProposerDeneb,
DomainBlobSidecarDeneb: domainBlobSidecarDeneb ,
DomainBlobSidecarDeneb: domainBlobSidecarDeneb,
}, nil
}

Expand Down
50 changes: 41 additions & 9 deletions common/types_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb"
builderSpec "github.com/attestantio/go-builder-client/spec"
eth2Api "github.com/attestantio/go-eth2-client/api"
eth2builderApiV1Capella "github.com/attestantio/go-eth2-client/api/v1/capella"
eth2builderApiV1Deneb "github.com/attestantio/go-eth2-client/api/v1/deneb"
eth2ApiV1Capella "github.com/attestantio/go-eth2-client/api/v1/capella"
eth2ApiV1Deneb "github.com/attestantio/go-eth2-client/api/v1/deneb"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
Expand Down Expand Up @@ -189,7 +189,7 @@ func SignedBlindedBeaconBlockToBeaconBlock(signedBlindedBeaconBlock *VersionedSi
if len(denebBlindedBlobs) != len(blockPayload.Deneb.BlobsBundle.Blobs) {
return nil, errors.New("number of blinded blobs does not match blobs bundle length")
}

denebBlindedBlock := signedBlindedBeaconBlock.Deneb.SignedBlindedBlock
blockRoot, err := denebBlindedBlock.Message.HashTreeRoot()
if err != nil {
Expand All @@ -203,7 +203,7 @@ func SignedBlindedBeaconBlockToBeaconBlock(signedBlindedBeaconBlock *VersionedSi
return &signedBeaconBlock, nil
}

func CapellaUnblindSignedBlock(blindedBlock *eth2builderApiV1Capella.SignedBlindedBeaconBlock, executionPayload *capella.ExecutionPayload) *capella.SignedBeaconBlock {
func CapellaUnblindSignedBlock(blindedBlock *eth2ApiV1Capella.SignedBlindedBeaconBlock, executionPayload *capella.ExecutionPayload) *capella.SignedBeaconBlock {
return &capella.SignedBeaconBlock{
Signature: blindedBlock.Signature,
Message: &capella.BeaconBlock{
Expand All @@ -228,7 +228,7 @@ func CapellaUnblindSignedBlock(blindedBlock *eth2builderApiV1Capella.SignedBlind
}
}

func DenebUnblindSignedBlock(blindedBlock *eth2builderApiV1Deneb.SignedBlindedBeaconBlock, blindedBlobs []*eth2builderApiV1Deneb.SignedBlindedBlobSidecar, blockPayload *builderApiDeneb.ExecutionPayloadAndBlobsBundle, blockRoot phase0.Root) *eth2builderApiV1Deneb.SignedBlockContents {
func DenebUnblindSignedBlock(blindedBlock *eth2ApiV1Deneb.SignedBlindedBeaconBlock, blindedBlobs []*eth2ApiV1Deneb.SignedBlindedBlobSidecar, blockPayload *builderApiDeneb.ExecutionPayloadAndBlobsBundle, blockRoot phase0.Root) *eth2ApiV1Deneb.SignedBlockContents {
denebBlobSidecars := make([]*deneb.SignedBlobSidecar, len(blockPayload.BlobsBundle.Blobs))

for i := range denebBlobSidecars {
Expand All @@ -246,7 +246,7 @@ func DenebUnblindSignedBlock(blindedBlock *eth2builderApiV1Deneb.SignedBlindedBe
Signature: blindedBlobs[i].Signature,
}
}
return &eth2builderApiV1Deneb.SignedBlockContents{
return &eth2ApiV1Deneb.SignedBlockContents{
SignedBlock: &deneb.SignedBeaconBlock{
Message: &deneb.BeaconBlock{
Slot: blindedBlock.Message.Slot,
Expand Down Expand Up @@ -372,6 +372,38 @@ type VersionedSignedBlockRequest struct {
eth2Api.VersionedBlockRequest
}

func (r *VersionedSignedBlockRequest) MarshalSSZ() ([]byte, error) {
switch r.Version {
case spec.DataVersionCapella:
return r.Capella.MarshalSSZ()
case spec.DataVersionDeneb:
return r.Deneb.MarshalSSZ()
case spec.DataVersionUnknown, spec.DataVersionPhase0, spec.DataVersionAltair, spec.DataVersionBellatrix:
fallthrough
default:
return nil, errors.Wrap(ErrInvalidVersion, fmt.Sprintf("%d is not supported", r.Version))
}
}

func (r *VersionedSignedBlockRequest) UnmarshalSSZ(input []byte) error {
var err error

denebRequest := new(eth2ApiV1Deneb.SignedBlockContents)
if err = denebRequest.UnmarshalSSZ(input); err == nil {
r.Version = spec.DataVersionDeneb
r.Deneb = denebRequest
return nil
}

capellaRequest := new(capella.SignedBeaconBlock)
if err = capellaRequest.UnmarshalSSZ(input); err == nil {
r.Version = spec.DataVersionCapella
r.Capella = capellaRequest
return nil
}
return errors.Wrap(err, "failed to unmarshal SubmitBlockRequest SSZ")
}

func (r *VersionedSignedBlockRequest) MarshalJSON() ([]byte, error) {
switch r.Version {
case spec.DataVersionCapella:
Expand All @@ -388,7 +420,7 @@ func (r *VersionedSignedBlockRequest) MarshalJSON() ([]byte, error) {
func (r *VersionedSignedBlockRequest) UnmarshalJSON(input []byte) error {
var err error

denebContents := new(eth2builderApiV1Deneb.SignedBlockContents)
denebContents := new(eth2ApiV1Deneb.SignedBlockContents)
if err = json.Unmarshal(input, denebContents); err == nil {
r.Version = spec.DataVersionDeneb
r.Deneb = denebContents
Expand Down Expand Up @@ -424,14 +456,14 @@ func (r *VersionedSignedBlindedBlockRequest) MarshalJSON() ([]byte, error) {
func (r *VersionedSignedBlindedBlockRequest) UnmarshalJSON(input []byte) error {
var err error

denebContents := new(eth2builderApiV1Deneb.SignedBlindedBlockContents)
denebContents := new(eth2ApiV1Deneb.SignedBlindedBlockContents)
if err = json.Unmarshal(input, denebContents); err == nil {
r.Version = spec.DataVersionDeneb
r.Deneb = denebContents
return nil
}

capellaBlock := new(eth2builderApiV1Capella.SignedBlindedBeaconBlock)
capellaBlock := new(eth2ApiV1Capella.SignedBlindedBeaconBlock)
if err = json.Unmarshal(input, capellaBlock); err == nil {
r.Version = spec.DataVersionCapella
r.Capella = capellaBlock
Expand Down
Loading

0 comments on commit 716165c

Please sign in to comment.