Skip to content

Commit

Permalink
Merge pull request #79 from Savid/blob_sidecar
Browse files Browse the repository at this point in the history
add blob_sidecar event
  • Loading branch information
mcdee authored Oct 4, 2023
2 parents 001628d + 6e09554 commit 7368130
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 0 deletions.
96 changes: 96 additions & 0 deletions api/v1/blobsidecarevent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package v1

import (
"encoding/json"
"fmt"

"github.com/attestantio/go-eth2-client/spec/deneb"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
)

// BlobSidecarEvent is the data for the blob sidecar event.
type BlobSidecarEvent struct {
BlockRoot phase0.Root
Slot phase0.Slot
Index deneb.BlobIndex
KzgCommitment deneb.KzgCommitment
VersionedHash deneb.VersionedHash
}

// blobSidecarEventJSON is the spec representation of the struct.
type blobSidecarEventJSON struct {
BlockRoot string `json:"block_root"`
Slot string `json:"slot"`
Index string `json:"index"`
KzgCommitment string `json:"kzg_commitment"`
VersionedHash string `json:"versioned_hash"`
}

// MarshalJSON implements json.Marshaler.
func (e *BlobSidecarEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(&blobSidecarEventJSON{
BlockRoot: fmt.Sprintf("%#x", e.BlockRoot),
Slot: fmt.Sprintf("%d", e.Slot),
Index: fmt.Sprintf("%d", e.Index),
KzgCommitment: fmt.Sprintf("%#x", e.KzgCommitment),
VersionedHash: fmt.Sprintf("%#x", e.VersionedHash),
})
}

// UnmarshalJSON implements json.Unmarshaler.
func (e *BlobSidecarEvent) UnmarshalJSON(input []byte) error {
var err error

var blobSidecarEventJSON blobSidecarEventJSON
if err = json.Unmarshal(input, &blobSidecarEventJSON); err != nil {
return errors.Wrap(err, "invalid JSON")
}

if blobSidecarEventJSON.BlockRoot == "" {
return errors.New("block_root missing")
}
err = e.BlockRoot.UnmarshalJSON([]byte(fmt.Sprintf(`"%s"`, blobSidecarEventJSON.BlockRoot)))
if err != nil {
return errors.Wrap(err, "invalid value for block_root")
}
if blobSidecarEventJSON.Slot == "" {
return errors.New("slot missing")
}
err = e.Slot.UnmarshalJSON([]byte(fmt.Sprintf(`"%s"`, blobSidecarEventJSON.Slot)))
if err != nil {
return errors.Wrap(err, "invalid value for slot")
}
if blobSidecarEventJSON.Index == "" {
return errors.New("index missing")
}
err = e.Index.UnmarshalJSON([]byte(fmt.Sprintf(`"%s"`, blobSidecarEventJSON.Index)))
if err != nil {
return errors.Wrap(err, "invalid value for index")
}
if blobSidecarEventJSON.KzgCommitment == "" {
return errors.New("kzg_commitment missing")
}
err = e.KzgCommitment.UnmarshalJSON([]byte(fmt.Sprintf(`"%s"`, blobSidecarEventJSON.KzgCommitment)))
if err != nil {
return errors.Wrap(err, "invalid value for kzg_commitment")
}
if blobSidecarEventJSON.VersionedHash == "" {
return errors.New("versioned_hash missing")
}
err = e.VersionedHash.UnmarshalJSON([]byte(fmt.Sprintf(`"%s"`, blobSidecarEventJSON.VersionedHash)))
if err != nil {
return errors.Wrap(err, "invalid value for versioned_hash")
}

return nil
}

// String returns a string version of the structure.
func (e *BlobSidecarEvent) String() string {
data, err := json.Marshal(e)
if err != nil {
return fmt.Sprintf("ERR: %v", err)
}
return string(data)
}
153 changes: 153 additions & 0 deletions api/v1/blobsidecarevent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package v1_test

import (
"encoding/json"
"testing"

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

func TestBlobSidecarEventJSON(t *testing.T) {
tests := []struct {
name string
input []byte
err string
}{
{
name: "Empty",
err: "unexpected end of JSON input",
},
{
name: "JSONBad",
input: []byte("[]"),
err: "invalid JSON: json: cannot unmarshal array into Go value of type v1.blobSidecarEventJSON",
},
{
name: "BlockRootMissing",
input: []byte(`{"slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "block_root missing",
},
{
name: "BlockRootWrongType",
input: []byte(`{"block_root": true, "slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field blobSidecarEventJSON.block_root of type string",
},
{
name: "BlockRootInvalid",
input: []byte(`{"block_root":"0xinvalide9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for block_root: invalid value invalide9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2: encoding/hex: invalid byte: U+0069 'i'",
},
{
name: "BlockRootShort",
input: []byte(`{"block_root":"0x","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for block_root: incorrect length",
},
{
name: "BlockRootLong",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2a","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for block_root: incorrect length",
},
{
name: "SlotMissing",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "slot missing",
},
{
name: "SlotWrongType",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":true,"index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field blobSidecarEventJSON.slot of type string",
},
{
name: "SlotInvalid",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"-1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for slot: invalid value -1: strconv.ParseUint: parsing \"-1\": invalid syntax",
},
{
name: "IndexMissing",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "index missing",
},
{
name: "IndexWrongType",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":true,"kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field blobSidecarEventJSON.index of type string",
},
{
name: "IndexInvalid",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"-1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for index: invalid value -1: strconv.ParseUint: parsing \"-1\": invalid syntax",
},
{
name: "KZGCommitmentMissing",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "kzg_commitment missing",
},
{
name: "KZGCommitmentWrongType",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":true,"versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field blobSidecarEventJSON.kzg_commitment of type string",
},
{
name: "KZGCommitmentInvalid",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0xinvalidfb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for kzg_commitment: invalid value invalidfb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2: encoding/hex: invalid byte: U+0069 'i'",
},
{
name: "KZGCommitmentShort",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for kzg_commitment: incorrect length",
},
{
name: "KZGCommitmentLong",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2a","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for kzg_commitment: incorrect length",
},
{
name: "VersionedHashMissing",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2"}`),
err: "versioned_hash missing",
},
{
name: "VersionedHashWrongType",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":true}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field blobSidecarEventJSON.versioned_hash of type string",
},
{
name: "VersionedHashInvalid",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xinvalide9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
err: "invalid value for versioned_hash: invalid value invalide9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2: encoding/hex: invalid byte: U+0069 'i'",
},
{
name: "VersionedHashShort",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0x"}`),
err: "invalid value for versioned_hash: incorrect length",
},
{
name: "VersionedHashLong",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2a"}`),
err: "invalid value for versioned_hash: incorrect length",
},
{
name: "Good",
input: []byte(`{"block_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","slot":"1","index":"1","kzg_commitment":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2","versioned_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}`),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var res api.BlobSidecarEvent
err := json.Unmarshal(test.input, &res)
if test.err != "" {
require.EqualError(t, err, test.err)
} else {
require.NoError(t, err)
rt, err := json.Marshal(&res)
require.NoError(t, err)
assert.Equal(t, string(test.input), string(rt))
assert.Equal(t, string(rt), res.String())
}
})
}
}
3 changes: 3 additions & 0 deletions api/v1/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var SupportedEventTopics = map[string]bool{
"voluntary_exit": true,
"contribution_and_proof": true,
"payload_attributes": true,
"blob_sidecar": true,
}

// eventJSON is the spec representation of the struct.
Expand Down Expand Up @@ -99,6 +100,8 @@ func (e *Event) UnmarshalJSON(input []byte) error {
e.Data = &altair.SignedContributionAndProof{}
case "payload_attributes":
e.Data = &PayloadAttributesEvent{}
case "blob_sidecar":
e.Data = &BlobSidecarEvent{}
default:
return fmt.Errorf("unsupported event topic %s", eventJSON.Topic)
}
Expand Down

0 comments on commit 7368130

Please sign in to comment.