Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom json marshalling for versioned structs #493

Merged
merged 1 commit into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions beaconclient/mock_beacon_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"sync"
"time"

"github.com/attestantio/go-eth2-client/spec"
"github.com/flashbots/mev-boost-relay/common"
)

Expand Down Expand Up @@ -107,7 +106,7 @@ func (c *MockBeaconInstance) addDelay() {
}
}

func (c *MockBeaconInstance) PublishBlock(block *spec.VersionedSignedBeaconBlock, broadcastValidation BroadcastValidation) (code int, err error) {
func (c *MockBeaconInstance) PublishBlock(block *common.VersionedSignedBlockRequest, broadcastValidation BroadcastValidation) (code int, err error) {
return 0, nil
}

Expand Down
4 changes: 2 additions & 2 deletions beaconclient/mock_multi_beacon_client.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package beaconclient

import (
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/flashbots/mev-boost-relay/common"
)

type MockMultiBeaconClient struct{}
Expand All @@ -28,7 +28,7 @@ func (*MockMultiBeaconClient) GetProposerDuties(epoch uint64) (*ProposerDutiesRe
return nil, nil
}

func (*MockMultiBeaconClient) PublishBlock(block *spec.VersionedSignedBeaconBlock) (code int, err error) {
func (*MockMultiBeaconClient) PublishBlock(block *common.VersionedSignedBlockRequest) (code int, err error) {
return 0, nil
}

Expand Down
14 changes: 9 additions & 5 deletions beaconclient/multi_beacon_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"strings"
"sync"

"github.com/attestantio/go-eth2-client/spec"
"github.com/flashbots/mev-boost-relay/common"
"github.com/sirupsen/logrus"
uberatomic "go.uber.org/atomic"
)
Expand All @@ -29,7 +29,11 @@ const (
)

func (b BroadcastValidation) String() string {
return [...]string{"gossip", "consensus", "consensus_and_equivocation"}[b]
broadcastValidationStrings := [...]string{"gossip", "consensus", "consensus_and_equivocation"}
if int(b) >= len(broadcastValidationStrings) {
return "invalid broadcast validation value"
}
return broadcastValidationStrings[b]
}

// IMultiBeaconClient is the interface for the MultiBeaconClient, which can manage several beacon client instances under the hood
Expand All @@ -42,7 +46,7 @@ type IMultiBeaconClient interface {
// GetStateValidators returns all active and pending validators from the beacon node
GetStateValidators(stateID string) (*GetStateValidatorsResponse, error)
GetProposerDuties(epoch uint64) (*ProposerDutiesResponse, error)
PublishBlock(block *spec.VersionedSignedBeaconBlock) (code int, err error)
PublishBlock(block *common.VersionedSignedBlockRequest) (code int, err error)
GetGenesis() (*GetGenesisResponse, error)
GetSpec() (spec *GetSpecResponse, err error)
GetForkSchedule() (spec *GetForkScheduleResponse, err error)
Expand All @@ -60,7 +64,7 @@ type IBeaconInstance interface {
GetStateValidators(stateID string) (*GetStateValidatorsResponse, error)
GetProposerDuties(epoch uint64) (*ProposerDutiesResponse, error)
GetURI() string
PublishBlock(block *spec.VersionedSignedBeaconBlock, broadcastValidation BroadcastValidation) (code int, err error)
PublishBlock(block *common.VersionedSignedBlockRequest, broadcastValidation BroadcastValidation) (code int, err error)
GetGenesis() (*GetGenesisResponse, error)
GetSpec() (spec *GetSpecResponse, err error)
GetForkSchedule() (spec *GetForkScheduleResponse, err error)
Expand Down Expand Up @@ -254,7 +258,7 @@ type publishResp struct {
}

// PublishBlock publishes the signed beacon block via https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/publishBlock
func (c *MultiBeaconClient) PublishBlock(block *spec.VersionedSignedBeaconBlock) (code int, err error) {
func (c *MultiBeaconClient) PublishBlock(block *common.VersionedSignedBlockRequest) (code int, err error) {
slot, err := block.Slot()
if err != nil {
c.log.WithError(err).Warn("failed to publish block as block slot is missing")
Expand Down
7 changes: 3 additions & 4 deletions beaconclient/prod_beacon_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"net/http"
"time"

"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/flashbots/mev-boost-relay/common"
"github.com/r3labs/sse/v2"
Expand Down Expand Up @@ -256,11 +255,11 @@ func (c *ProdBeaconInstance) GetURI() string {
return c.beaconURI
}

func (c *ProdBeaconInstance) PublishBlock(block *spec.VersionedSignedBeaconBlock, broadcastValidation BroadcastValidation) (code int, err error) {
func (c *ProdBeaconInstance) PublishBlock(block *common.VersionedSignedBlockRequest, broadcastValidation BroadcastValidation) (code int, err error) {
uri := fmt.Sprintf("%s/eth/v2/beacon/blocks?broadcast_validation=%s", c.beaconURI, broadcastValidation.String())
headers := http.Header{}
headers.Add("Eth-Consensus-Version", common.ForkVersionStringCapella)
return fetchBeacon(http.MethodPost, uri, block.Capella, nil, nil, headers)
headers.Add("Eth-Consensus-Version", block.Version.String())
return fetchBeacon(http.MethodPost, uri, block, nil, nil, headers)
}

type GetGenesisResponse struct {
Expand Down
29 changes: 22 additions & 7 deletions common/ssz_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"bytes"
"encoding/json"
"os"
"testing"
Expand All @@ -12,21 +13,35 @@ import (
)

func TestSSZBuilderSubmission(t *testing.T) {
byteValue := LoadGzippedBytes(t, "../testdata/submitBlockPayloadCapella_Goerli.json.gz")
// json matches marshalled SSZ
jsonBytes := LoadGzippedBytes(t, "../testdata/submitBlockPayloadCapella_Goerli.json.gz")

depositData := new(capella.SubmitBlockRequest)
err := json.Unmarshal(byteValue, &depositData)
submitBlockData := new(VersionedSubmitBlockRequest)
err := json.Unmarshal(jsonBytes, &submitBlockData)
require.NoError(t, err)

ssz, err := depositData.MarshalSSZ()
require.NotNil(t, submitBlockData.Capella)
marshalledSszBytes, err := submitBlockData.Capella.MarshalSSZ()
require.NoError(t, err)

sszExpectedBytes := LoadGzippedBytes(t, "../testdata/submitBlockPayloadCapella_Goerli.ssz.gz")
require.Equal(t, sszExpectedBytes, ssz)
sszBytes := LoadGzippedBytes(t, "../testdata/submitBlockPayloadCapella_Goerli.ssz.gz")
require.Equal(t, sszBytes, marshalledSszBytes)

htr, err := depositData.HashTreeRoot()
htr, err := submitBlockData.Capella.HashTreeRoot()
require.NoError(t, err)
require.Equal(t, "0x014c218ba41c2ed5388e7f0ed055e109b83692c772de5c2800140a95a4b66d13", hexutil.Encode(htr[:]))

// marshalled json matches ssz
submitBlockSSZ := new(VersionedSubmitBlockRequest)
err = submitBlockSSZ.UnmarshalSSZ(sszBytes)
require.NoError(t, err)
marshalledJSONBytes, err := json.Marshal(submitBlockSSZ)
require.NoError(t, err)
// trim white space from expected json
buffer := new(bytes.Buffer)
err = json.Compact(buffer, jsonBytes)
require.NoError(t, err)
require.Equal(t, buffer.Bytes(), bytes.ToLower(marshalledJSONBytes))
}

func TestSSZGetHeaderResponse(t *testing.T) {
Expand Down
24 changes: 13 additions & 11 deletions common/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,21 @@ var ValidPayloadRegisterValidator = apiv1.SignedValidatorRegistration{
"0xaf12df007a0c78abb5575067e5f8b089cfcc6227e4a91db7dd8cf517fe86fb944ead859f0781277d9b78c672e4a18c5d06368b603374673cf2007966cece9540f3a1b3f6f9e1bf421d779c4e8010368e6aac134649c7a009210780d401a778a5"),
}

func TestBuilderSubmitBlockRequest(sk *bls.SecretKey, bid *BidTraceV2) spec.VersionedSubmitBlockRequest {
func TestBuilderSubmitBlockRequest(sk *bls.SecretKey, bid *BidTraceV2) VersionedSubmitBlockRequest {
signature, err := ssz.SignMessage(bid, ssz.DomainBuilder, sk)
check(err, " SignMessage: ", bid, sk)
return spec.VersionedSubmitBlockRequest{ //nolint:exhaustruct
Version: consensusspec.DataVersionCapella,
Capella: &capella.SubmitBlockRequest{
Message: &bid.BidTrace,
Signature: [96]byte(signature),
ExecutionPayload: &consensuscapella.ExecutionPayload{ //nolint:exhaustruct
Transactions: []bellatrix.Transaction{[]byte{0x03}},
Timestamp: bid.Slot * 12, // 12 seconds per slot.
PrevRandao: _HexToHash("0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
Withdrawals: []*consensuscapella.Withdrawal{},
return VersionedSubmitBlockRequest{
VersionedSubmitBlockRequest: spec.VersionedSubmitBlockRequest{ //nolint:exhaustruct
Version: consensusspec.DataVersionCapella,
Capella: &capella.SubmitBlockRequest{
Message: &bid.BidTrace,
Signature: [96]byte(signature),
ExecutionPayload: &consensuscapella.ExecutionPayload{ //nolint:exhaustruct
Transactions: []bellatrix.Transaction{[]byte{0x03}},
Timestamp: bid.Slot * 12, // 12 seconds per slot.
PrevRandao: _HexToHash("0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),
Withdrawals: []*consensuscapella.Withdrawal{},
},
},
},
}
Expand Down
151 changes: 136 additions & 15 deletions common/types_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import (

"github.com/attestantio/go-builder-client/api"
"github.com/attestantio/go-builder-client/api/capella"
"github.com/attestantio/go-builder-client/api/deneb"
"github.com/attestantio/go-builder-client/spec"
consensusapi "github.com/attestantio/go-eth2-client/api"
apiv1capella "github.com/attestantio/go-eth2-client/api/v1/capella"
apiv1deneb "github.com/attestantio/go-eth2-client/api/v1/deneb"
consensusspec "github.com/attestantio/go-eth2-client/spec"
consensuscapella "github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/phase0"
Expand Down Expand Up @@ -36,7 +39,7 @@ var NilResponse = struct{}{}

var ZeroU256 = boostTypes.IntToU256(0)

func BuildGetHeaderResponse(payload *spec.VersionedSubmitBlockRequest, sk *bls.SecretKey, pubkey *phase0.BLSPubKey, domain phase0.Domain) (*spec.VersionedSignedBuilderBid, error) {
func BuildGetHeaderResponse(payload *VersionedSubmitBlockRequest, sk *bls.SecretKey, pubkey *phase0.BLSPubKey, domain phase0.Domain) (*spec.VersionedSignedBuilderBid, error) {
if payload == nil {
return nil, ErrMissingRequest
}
Expand All @@ -59,7 +62,7 @@ func BuildGetHeaderResponse(payload *spec.VersionedSubmitBlockRequest, sk *bls.S
return nil, ErrEmptyPayload
}

func BuildGetPayloadResponse(payload *spec.VersionedSubmitBlockRequest) (*api.VersionedExecutionPayload, error) {
func BuildGetPayloadResponse(payload *VersionedSubmitBlockRequest) (*api.VersionedExecutionPayload, error) {
if payload.Capella != nil {
return &api.VersionedExecutionPayload{
Version: consensusspec.DataVersionCapella,
Expand All @@ -70,7 +73,7 @@ func BuildGetPayloadResponse(payload *spec.VersionedSubmitBlockRequest) (*api.Ve
return nil, ErrEmptyPayload
}

func BuilderSubmitBlockRequestToSignedBuilderBid(req *spec.VersionedSubmitBlockRequest, sk *bls.SecretKey, pubkey *phase0.BLSPubKey, domain phase0.Domain) (*spec.VersionedSignedBuilderBid, error) {
func BuilderSubmitBlockRequestToSignedBuilderBid(req *VersionedSubmitBlockRequest, sk *bls.SecretKey, pubkey *phase0.BLSPubKey, domain phase0.Domain) (*spec.VersionedSignedBuilderBid, error) {
value, err := req.Value()
if err != nil {
return nil, err
Expand Down Expand Up @@ -167,8 +170,8 @@ func CapellaPayloadToPayloadHeader(p *consensuscapella.ExecutionPayload) (*conse
}, nil
}

func SignedBlindedBeaconBlockToBeaconBlock(signedBlindedBeaconBlock *consensusapi.VersionedSignedBlindedBeaconBlock, executionPayload *api.VersionedExecutionPayload) *consensusspec.VersionedSignedBeaconBlock {
var signedBeaconBlock consensusspec.VersionedSignedBeaconBlock
func SignedBlindedBeaconBlockToBeaconBlock(signedBlindedBeaconBlock *VersionedSignedBlindedBlockRequest, executionPayload *api.VersionedExecutionPayload) *VersionedSignedBlockRequest {
var signedBeaconBlock VersionedSignedBlockRequest
capellaBlindedBlock := signedBlindedBeaconBlock.Capella
if capellaBlindedBlock != nil {
signedBeaconBlock.Capella = &consensuscapella.SignedBeaconBlock{
Expand Down Expand Up @@ -198,20 +201,12 @@ func SignedBlindedBeaconBlockToBeaconBlock(signedBlindedBeaconBlock *consensusap
}

type BuilderBlockValidationRequest struct {
spec.VersionedSubmitBlockRequest
VersionedSubmitBlockRequest
RegisteredGasLimit uint64 `json:"registered_gas_limit,string"`
}

func (r *BuilderBlockValidationRequest) MarshalJSON() ([]byte, error) {
var blockRequest []byte
var err error

switch r.VersionedSubmitBlockRequest.Version { //nolint:exhaustive
case consensusspec.DataVersionCapella:
blockRequest, err = r.VersionedSubmitBlockRequest.Capella.MarshalJSON()
default:
return nil, errors.Wrap(ErrInvalidVersion, fmt.Sprintf("%d is not supported", r.VersionedSubmitBlockRequest.Version))
}
blockRequest, err := json.Marshal(r.VersionedSubmitBlockRequest)
if err != nil {
return nil, err
}
Expand All @@ -226,3 +221,129 @@ func (r *BuilderBlockValidationRequest) MarshalJSON() ([]byte, error) {
gasLimit[0] = ','
return append(blockRequest[:len(blockRequest)-1], gasLimit...), nil
}

type VersionedSubmitBlockRequest struct {
spec.VersionedSubmitBlockRequest
}

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

deneb := new(deneb.SubmitBlockRequest)
if err = deneb.UnmarshalSSZ(input); err == nil {
r.Version = consensusspec.DataVersionDeneb
r.Deneb = deneb
return nil
}

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

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

func (r *VersionedSubmitBlockRequest) UnmarshalJSON(input []byte) error {
var err error

deneb := new(deneb.SubmitBlockRequest)
if err = json.Unmarshal(input, deneb); err == nil {
r.Version = consensusspec.DataVersionDeneb
r.Deneb = deneb
return nil
}
capella := new(capella.SubmitBlockRequest)
Comment on lines +269 to +270
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: for consistency with the other funcs, please add a blank line between these two blocks.

Or delete the blank lines between these blocks of code in the other functions.

if err = json.Unmarshal(input, capella); err == nil {
r.Version = consensusspec.DataVersionCapella
r.Capella = capella
return nil
}
return errors.Wrap(err, "failed to unmarshal SubmitBlockRequest")
}

type VersionedSignedBlockRequest struct {
consensusapi.VersionedBlockRequest
}

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

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

deneb := new(apiv1deneb.SignedBlockContents)
if err = json.Unmarshal(input, deneb); err == nil {
r.Version = consensusspec.DataVersionDeneb
r.Deneb = deneb
return nil
}

capella := new(consensuscapella.SignedBeaconBlock)
if err = json.Unmarshal(input, capella); err == nil {
r.Version = consensusspec.DataVersionCapella
r.Capella = capella
return nil
}
return errors.Wrap(err, "failed to unmarshal SignedBeaconBlockRequest")
}

type VersionedSignedBlindedBlockRequest struct {
consensusapi.VersionedBlindedBlockRequest
}

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

func (r *VersionedSignedBlindedBlockRequest) UnmarshalJSON(input []byte) error {
var err error

deneb := new(apiv1deneb.SignedBlindedBlockContents)
if err = json.Unmarshal(input, deneb); err == nil {
r.Version = consensusspec.DataVersionDeneb
r.Deneb = deneb
return nil
}

capella := new(apiv1capella.SignedBlindedBeaconBlock)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: these variables (deneb and capella) collide with package names.

I think we should fix this by renaming the packages (in a later PR).

if err = json.Unmarshal(input, capella); err == nil {
r.Version = consensusspec.DataVersionCapella
r.Capella = capella
return nil
}
return errors.Wrap(err, "failed to unmarshal SignedBlindedBeaconBlock")
}
Loading
Loading