Skip to content

Commit

Permalink
Merge pull request #73 from avalonche/update-payload-attributes
Browse files Browse the repository at this point in the history
Update payload attributes v3
  • Loading branch information
mcdee authored Sep 14, 2023
2 parents 194deb2 + c9aefd0 commit 3aa9db4
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 1 deletion.
109 changes: 109 additions & 0 deletions api/v1/payloadattributesevent.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type PayloadAttributesData struct {
V1 *PayloadAttributesV1
// V2 is the v2 payload attributes.
V2 *PayloadAttributesV2
// V3 is the v3 payload attributes.
V3 *PayloadAttributesV3
}

// PayloadAttributesV1 represents the payload attributes.
Expand All @@ -62,6 +64,20 @@ type PayloadAttributesV2 struct {
Withdrawals []*capella.Withdrawal
}

// PayloadAttributesV3 represents the payload attributes v3.
type PayloadAttributesV3 struct {
// Timestamp is the timestamp of the payload.
Timestamp uint64
// PrevRandao is the previous randao.
PrevRandao [32]byte
// SuggestedFeeRecipient is the suggested fee recipient.
SuggestedFeeRecipient bellatrix.ExecutionAddress
// Withdrawals is the list of withdrawals.
Withdrawals []*capella.Withdrawal
// ParentBeaconBlockRoot is the parent beacon block root.
ParentBeaconBlockRoot phase0.Root
}

// payloadAttributesEventJSON is the spec representation of the event.
type payloadAttributesEventJSON struct {
Version spec.DataVersion `json:"version"`
Expand Down Expand Up @@ -93,6 +109,15 @@ type payloadAttributesV2JSON struct {
Withdrawals []*capella.Withdrawal `json:"withdrawals"`
}

// payloadAttributesV3JSON is the spec representation of the payload attributes v3.
type payloadAttributesV3JSON struct {
Timestamp string `json:"timestamp"`
PrevRandao string `json:"prev_randao"`
SuggestedFeeRecipient string `json:"suggested_fee_recipient"`
Withdrawals []*capella.Withdrawal `json:"withdrawals"`
ParentBeaconBlockRoot string `json:"parent_beacon_block_root"`
}

// UnmarshalJSON implements json.Unmarshaler.
func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error {
var payloadAttributes payloadAttributesV1JSON
Expand Down Expand Up @@ -192,6 +217,69 @@ func (p *PayloadAttributesV2) unpack(data *payloadAttributesV2JSON) error {
return nil
}

func (p *PayloadAttributesV3) UnmarshalJSON(input []byte) error {
var payloadAttributes payloadAttributesV3JSON
if err := json.Unmarshal(input, &payloadAttributes); err != nil {
return errors.Wrap(err, "invalid JSON")
}
return p.unpack(&payloadAttributes)
}

func (p *PayloadAttributesV3) unpack(data *payloadAttributesV3JSON) error {
var err error

if data.Timestamp == "" {
return errors.New("payload attributes timestamp missing")
}
p.Timestamp, err = strconv.ParseUint(data.Timestamp, 10, 64)
if err != nil {
return errors.Wrap(err, "invalid value for payload attributes timestamp")
}

if data.PrevRandao == "" {
return errors.New("payload attributes prev randao missing")
}
prevRandao, err := hex.DecodeString(strings.TrimPrefix(data.PrevRandao, "0x"))
if err != nil {
return errors.Wrap(err, "invalid value for payload attributes prev randao")
}
if len(prevRandao) != 32 {
return errors.New("incorrect length for payload attributes prev randao")
}
copy(p.PrevRandao[:], prevRandao)

if data.SuggestedFeeRecipient == "" {
return errors.New("payload attributes suggested fee recipient missing")
}
feeRecipient, err := hex.DecodeString(strings.TrimPrefix(data.SuggestedFeeRecipient, "0x"))
if err != nil {
return errors.Wrap(err, "invalid value for payload attributes suggested fee recipient")
}
if len(feeRecipient) != bellatrix.FeeRecipientLength {
return errors.New("incorrect length for payload attributes suggested fee recipient")
}
copy(p.SuggestedFeeRecipient[:], feeRecipient)

if data.Withdrawals == nil {
return errors.New("payload attributes withdrawals missing")
}
p.Withdrawals = data.Withdrawals

if data.ParentBeaconBlockRoot == "" {
return errors.New("payload attributes parent beacon block root missing")
}
parentBeaconBlockRoot, err := hex.DecodeString(strings.TrimPrefix(data.ParentBeaconBlockRoot, "0x"))
if err != nil {
return errors.Wrap(err, "invalid value for payload attributes parent beacon block root")
}
if len(parentBeaconBlockRoot) != phase0.RootLength {
return errors.New("incorrect length for payload attributes parent beacon block root")
}
copy(p.ParentBeaconBlockRoot[:], parentBeaconBlockRoot)

return nil
}

// MarshalJSON implements json.Marshaler.
func (e *PayloadAttributesEvent) MarshalJSON() ([]byte, error) {
var payloadAttributes []byte
Expand Down Expand Up @@ -223,6 +311,20 @@ func (e *PayloadAttributesEvent) MarshalJSON() ([]byte, error) {
if err != nil {
return nil, errors.Wrap(err, "failed to marshal payload attributes v2")
}
case spec.DataVersionDeneb:
if e.Data.V3 == nil {
return nil, errors.New("no payload attributes v3 data")
}
payloadAttributes, err = json.Marshal(&payloadAttributesV3JSON{
Timestamp: fmt.Sprintf("%d", e.Data.V3.Timestamp),
PrevRandao: fmt.Sprintf("%#x", e.Data.V3.PrevRandao),
SuggestedFeeRecipient: e.Data.V3.SuggestedFeeRecipient.String(),
Withdrawals: e.Data.V3.Withdrawals,
ParentBeaconBlockRoot: fmt.Sprintf("%#x", e.Data.V3.ParentBeaconBlockRoot),
})
if err != nil {
return nil, errors.Wrap(err, "failed to marshal payload attributes v3")
}
default:
return nil, fmt.Errorf("unsupported payload attributes version: %s", e.Version)
}
Expand Down Expand Up @@ -329,6 +431,13 @@ func (e *PayloadAttributesEvent) unpack(data *payloadAttributesEventJSON) error
return err
}
e.Data.V2 = &payloadAttributes
case spec.DataVersionDeneb:
var payloadAttributes PayloadAttributesV3
err = json.Unmarshal(data.Data.PayloadAttributes, &payloadAttributes)
if err != nil {
return err
}
e.Data.V3 = &payloadAttributes
default:
return errors.New("unsupported data version")
}
Expand Down
21 changes: 20 additions & 1 deletion api/v1/payloadattributesevent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"testing"

api "github.com/attestantio/go-eth2-client/api/v1"
require "github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
require "github.com/stretchr/testify/require"
)

func TestPayloadAttributesEventJSON(t *testing.T) {
Expand Down Expand Up @@ -119,6 +119,11 @@ func TestPayloadAttributesEventJSON(t *testing.T) {
input: []byte(`{"version":"capella","data":true}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field payloadAttributesEventJSON.data of type v1.payloadAttributesDataJSON",
},
{
name: "BadPayloadAttributesV3Data",
input: []byte(`{"version":"deneb","data":true}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field payloadAttributesEventJSON.data of type v1.payloadAttributesDataJSON",
},
{
name: "BadPayloadAttributesV1",
input: []byte(`{"version":"bellatrix","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":true}}`),
Expand All @@ -129,6 +134,11 @@ func TestPayloadAttributesEventJSON(t *testing.T) {
input: []byte(`{"version":"capella","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":true}}`),
err: "invalid JSON: json: cannot unmarshal bool into Go value of type v1.payloadAttributesV2JSON",
},
{
name: "BadPayloadAttributesV3",
input: []byte(`{"version":"deneb","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":true}}`),
err: "invalid JSON: json: cannot unmarshal bool into Go value of type v1.payloadAttributesV3JSON",
},
{
name: "BadPayloadAttributesV1",
input: []byte(`{"version":"bellatrix","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":{}}}`),
Expand All @@ -139,6 +149,11 @@ func TestPayloadAttributesEventJSON(t *testing.T) {
input: []byte(`{"version":"capella","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":{}}}`),
err: "payload attributes timestamp missing",
},
{
name: "BadPayloadAttributesV3",
input: []byte(`{"version":"deneb","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":{}}}`),
err: "payload attributes timestamp missing",
},
{
name: "MissingPayloadAttributes",
input: []byte(`{"version":"capella","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf"}}`),
Expand All @@ -157,6 +172,10 @@ func TestPayloadAttributesEventJSON(t *testing.T) {
name: "GoodPayloadAttributesV2",
input: []byte(`{"version":"capella","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":{"timestamp":"123456","prev_randao":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","suggested_fee_recipient":"0x0000000000000000000000000000000000000000","withdrawals":[{"index":"5","validator_index":"10","address":"0x0000000000000000000000000000000000000000","amount":"15640"}]}}}`),
},
{
name: "GoodPayloadAttributesV3",
input: []byte(`{"version":"deneb","data":{"proposer_index":"123","proposal_slot":"10","parent_block_number":"9","parent_block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","parent_block_hash":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf","payload_attributes":{"timestamp":"123456","prev_randao":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","suggested_fee_recipient":"0x0000000000000000000000000000000000000000","withdrawals":[{"index":"5","validator_index":"10","address":"0x0000000000000000000000000000000000000000","amount":"15640"}],"parent_beacon_block_root":"0xba4d784293df28bab771a14df58cdbed9d8d64afd0ddf1c52dff3e25fcdd51df"}}}`),
},
}

for _, test := range tests {
Expand Down

0 comments on commit 3aa9db4

Please sign in to comment.