From 135622abe39e1e1d767861b6c3419a8393c6cbba Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Thu, 1 Jun 2023 11:41:35 +0200 Subject: [PATCH] feat(blob): add marshaling for blob and proof (#2286) --- api/docgen/examples.go | 6 ++++ blob/blob.go | 75 ++++++++++++++++++++++++++++++++++++++++-- blob/blob_test.go | 12 +++++++ blob/service_test.go | 19 +++++++++++ 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/api/docgen/examples.go b/api/docgen/examples.go index ae65501ba6..8293396c7b 100644 --- a/api/docgen/examples.go +++ b/api/docgen/examples.go @@ -18,8 +18,10 @@ import ( "golang.org/x/text/language" "github.com/celestiaorg/go-fraud" + "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" + "github.com/celestiaorg/celestia-node/blob" "github.com/celestiaorg/celestia-node/das" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/nodebuilder/node" @@ -128,6 +130,10 @@ func init() { Addrs: []multiaddr.Multiaddr{ma}, } addToExampleValues(addrInfo) + + proof := nmt.NewInclusionProof(0, 4, [][]byte{[]byte("test")}, true) + blobProof := &blob.Proof{&proof} + addToExampleValues(blobProof) } func addToExampleValues(v interface{}) { diff --git a/blob/blob.go b/blob/blob.go index 434ab220c7..9771714cb9 100644 --- a/blob/blob.go +++ b/blob/blob.go @@ -2,12 +2,15 @@ package blob import ( "bytes" + "encoding/json" "fmt" appns "github.com/celestiaorg/celestia-app/pkg/namespace" "github.com/celestiaorg/celestia-app/x/blob/types" "github.com/celestiaorg/nmt" "github.com/celestiaorg/nmt/namespace" + + "github.com/celestiaorg/celestia-node/share/ipld" ) // Commitment is a Merkle Root of the subtree built from shares of the Blob. @@ -57,11 +60,47 @@ func (p Proof) equal(input Proof) error { return nil } +type jsonProof struct { + Start int `json:"start"` + End int `json:"end"` + Nodes [][]byte `json:"nodes"` +} + +func (p *Proof) MarshalJSON() ([]byte, error) { + proofs := make([]jsonProof, 0, p.Len()) + for _, pp := range *p { + proofs = append(proofs, jsonProof{ + Start: pp.Start(), + End: pp.End(), + Nodes: pp.Nodes(), + }) + } + + return json.Marshal(proofs) +} + +func (p *Proof) UnmarshalJSON(data []byte) error { + var proofs []jsonProof + err := json.Unmarshal(data, &proofs) + if err != nil { + return err + } + + nmtProofs := make([]*nmt.Proof, len(proofs)) + for i, jProof := range proofs { + nmtProof := nmt.NewInclusionProof(jProof.Start, jProof.End, jProof.Nodes, ipld.NMTIgnoreMaxNamespace) + nmtProofs[i] = &nmtProof + } + + *p = nmtProofs + return nil +} + // Blob represents any application-specific binary data that anyone can submit to Celestia. type Blob struct { - types.Blob + types.Blob `json:"blob"` - Commitment Commitment + Commitment Commitment `json:"commitment"` } // NewBlob constructs a new blob from the provided namespace.ID and data. @@ -91,3 +130,35 @@ func NewBlob(shareVersion uint8, namespace namespace.ID, data []byte) (*Blob, er func (b *Blob) Namespace() namespace.ID { return append([]byte{uint8(b.NamespaceVersion)}, b.NamespaceId...) } + +type jsonBlob struct { + Namespace namespace.ID `json:"namespace"` + Data []byte `json:"data"` + ShareVersion uint32 `json:"share_version"` + Commitment Commitment `json:"commitment"` +} + +func (b *Blob) MarshalJSON() ([]byte, error) { + blob := &jsonBlob{ + Namespace: b.Namespace(), + Data: b.Data, + ShareVersion: b.ShareVersion, + Commitment: b.Commitment, + } + return json.Marshal(blob) +} + +func (b *Blob) UnmarshalJSON(data []byte) error { + var blob jsonBlob + err := json.Unmarshal(data, &blob) + if err != nil { + return err + } + + b.Blob.NamespaceVersion = uint32(blob.Namespace[0]) + b.Blob.NamespaceId = blob.Namespace[1:] + b.Blob.Data = blob.Data + b.Blob.ShareVersion = blob.ShareVersion + b.Commitment = blob.Commitment + return nil +} diff --git a/blob/blob_test.go b/blob/blob_test.go index 5b3920ae9e..7f34fbfc84 100644 --- a/blob/blob_test.go +++ b/blob/blob_test.go @@ -1,6 +1,7 @@ package blob import ( + "reflect" "testing" "github.com/stretchr/testify/assert" @@ -62,6 +63,17 @@ func TestBlob(t *testing.T) { assert.Equal(t, blob[0].Commitment, b[0].Commitment) }, }, + { + name: "blob marshaling", + expectedRes: func(t *testing.T) { + data, err := blob[0].MarshalJSON() + require.NoError(t, err) + + newBlob := &Blob{} + require.NoError(t, newBlob.UnmarshalJSON(data)) + require.True(t, reflect.DeepEqual(blob[0], newBlob)) + }, + }, } for _, tt := range test { diff --git a/blob/service_test.go b/blob/service_test.go index 87554eb3d0..4160f36205 100644 --- a/blob/service_test.go +++ b/blob/service_test.go @@ -268,6 +268,25 @@ func TestBlobService_Get(t *testing.T) { }, }, + { + name: "marshal proof", + doFn: func() (interface{}, error) { + proof, err := service.GetProof(ctx, 1, blobs0[1].Namespace(), blobs0[1].Commitment) + require.NoError(t, err) + return proof.MarshalJSON() + }, + expectedResult: func(i interface{}, err error) { + require.NoError(t, err) + jsonData, ok := i.([]byte) + require.True(t, ok) + var proof Proof + require.NoError(t, proof.UnmarshalJSON(jsonData)) + + newProof, err := service.GetProof(ctx, 1, blobs0[1].Namespace(), blobs0[1].Commitment) + require.NoError(t, err) + require.NoError(t, proof.equal(*newProof)) + }, + }, } for _, tt := range test {