diff --git a/blob/blob.go b/blob/blob.go index 945ffdf587..9843441dd2 100644 --- a/blob/blob.go +++ b/blob/blob.go @@ -35,6 +35,33 @@ type Proof []*nmt.Proof func (p Proof) Len() int { return len(p) } +func (p Proof) MarshalJSON() ([]byte, error) { + proofs := make([]string, 0, len(p)) + for _, proof := range p { + proofBytes, err := proof.MarshalJSON() + if err != nil { + return nil, err + } + proofs = append(proofs, string(proofBytes)) + } + return json.Marshal(proofs) +} + +func (p *Proof) UnmarshalJSON(b []byte) error { + var proofs []string + if err := json.Unmarshal(b, &proofs); err != nil { + return err + } + for _, proof := range proofs { + var nmtProof nmt.Proof + if err := nmtProof.UnmarshalJSON([]byte(proof)); err != nil { + return err + } + *p = append(*p, &nmtProof) + } + return nil +} + // equal is a temporary method that compares two proofs. // should be removed in BlobService V1. func (p Proof) equal(input Proof) error { diff --git a/nodebuilder/da/da.go b/nodebuilder/da/da.go index dc6cb51cbe..0d604d769f 100644 --- a/nodebuilder/da/da.go +++ b/nodebuilder/da/da.go @@ -18,9 +18,10 @@ type API struct { MaxBlobSize func(ctx context.Context) (uint64, error) `perm:"read"` Get func(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Blob, error) `perm:"read"` GetIDs func(ctx context.Context, height uint64, ns da.Namespace) ([]da.ID, error) `perm:"read"` + GetProofs func(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Proof, error) `perm:"read"` Commit func(ctx context.Context, blobs []da.Blob, ns da.Namespace) ([]da.Commitment, error) `perm:"read"` Validate func(context.Context, []da.ID, []da.Proof, da.Namespace) ([]bool, error) `perm:"read"` - Submit func(context.Context, []da.Blob, float64, da.Namespace) ([]da.ID, []da.Proof, error) `perm:"write"` + Submit func(context.Context, []da.Blob, float64, da.Namespace) ([]da.ID, error) `perm:"write"` } } @@ -36,6 +37,10 @@ func (api *API) GetIDs(ctx context.Context, height uint64, ns da.Namespace) ([]d return api.Internal.GetIDs(ctx, height, ns) } +func (api *API) GetProofs(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Proof, error) { + return api.Internal.GetProofs(ctx, ids, ns) +} + func (api *API) Commit(ctx context.Context, blobs []da.Blob, ns da.Namespace) ([]da.Commitment, error) { return api.Internal.Commit(ctx, blobs, ns) } @@ -44,6 +49,6 @@ func (api *API) Validate(ctx context.Context, ids []da.ID, proofs []da.Proof, ns return api.Internal.Validate(ctx, ids, proofs, ns) } -func (api *API) Submit(ctx context.Context, blobs []da.Blob, gasPrice float64, ns da.Namespace) ([]da.ID, []da.Proof, error) { +func (api *API) Submit(ctx context.Context, blobs []da.Blob, gasPrice float64, ns da.Namespace) ([]da.ID, error) { return api.Internal.Submit(ctx, blobs, gasPrice, ns) } diff --git a/nodebuilder/da/mocks/api.go b/nodebuilder/da/mocks/api.go index 6e65f97c5c..5895240906 100644 --- a/nodebuilder/da/mocks/api.go +++ b/nodebuilder/da/mocks/api.go @@ -79,6 +79,21 @@ func (mr *MockModuleMockRecorder) GetIDs(arg0, arg1, arg2 interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIDs", reflect.TypeOf((*MockModule)(nil).GetIDs), arg0, arg1, arg2) } +// GetProofs mocks base method. +func (m *MockModule) GetProofs(arg0 context.Context, arg1 [][]byte, arg2 []byte) ([][]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetProofs", arg0, arg1, arg2) + ret0, _ := ret[0].([][]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetProofs indicates an expected call of GetProofs. +func (mr *MockModuleMockRecorder) GetProofs(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProofs", reflect.TypeOf((*MockModule)(nil).GetProofs), arg0, arg1, arg2) +} + // MaxBlobSize mocks base method. func (m *MockModule) MaxBlobSize(arg0 context.Context) (uint64, error) { m.ctrl.T.Helper() @@ -95,13 +110,12 @@ func (mr *MockModuleMockRecorder) MaxBlobSize(arg0 interface{}) *gomock.Call { } // Submit mocks base method. -func (m *MockModule) Submit(arg0 context.Context, arg1 [][]byte, arg2 float64, arg3 []byte) ([][]byte, [][]byte, error) { +func (m *MockModule) Submit(arg0 context.Context, arg1 [][]byte, arg2 float64, arg3 []byte) ([][]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Submit", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([][]byte) - ret1, _ := ret[1].([][]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret1, _ := ret[1].(error) + return ret0, ret1 } // Submit indicates an expected call of Submit. diff --git a/nodebuilder/da/service.go b/nodebuilder/da/service.go index 56a59e8829..b775e10396 100644 --- a/nodebuilder/da/service.go +++ b/nodebuilder/da/service.go @@ -3,14 +3,13 @@ package da import ( "context" "encoding/binary" + "fmt" "strings" logging "github.com/ipfs/go-log/v2" "github.com/rollkit/go-da" "github.com/celestiaorg/celestia-app/pkg/appconsts" - "github.com/celestiaorg/celestia-app/x/blob/types" - "github.com/celestiaorg/nmt" "github.com/celestiaorg/celestia-node/blob" nodeblob "github.com/celestiaorg/celestia-node/nodebuilder/blob" @@ -75,6 +74,23 @@ func (s *Service) GetIDs(ctx context.Context, height uint64, namespace da.Namesp return ids, nil } +// GetProofs returns inclusion Proofs for all Blobs located in DA at given height. +func (s *Service) GetProofs(ctx context.Context, ids []da.ID, namespace da.Namespace) ([]da.Proof, error) { + proofs := make([]da.Proof, len(ids)) + for i, id := range ids { + height, commitment := SplitID(id) + proof, err := s.blobServ.GetProof(ctx, height, namespace, commitment) + if err != nil { + return nil, err + } + proofs[i], err = proof.MarshalJSON() + if err != nil { + return nil, err + } + } + return proofs, nil +} + // Commit creates a Commitment for each given Blob. func (s *Service) Commit(_ context.Context, daBlobs []da.Blob, namespace da.Namespace) ([]da.Commitment, error) { _, commitments, err := s.blobsAndCommitments(daBlobs, namespace) @@ -87,33 +103,23 @@ func (s *Service) Submit( daBlobs []da.Blob, gasPrice float64, namespace da.Namespace, -) ([]da.ID, []da.Proof, error) { - blobs, commitments, err := s.blobsAndCommitments(daBlobs, namespace) +) ([]da.ID, error) { + blobs, _, err := s.blobsAndCommitments(daBlobs, namespace) if err != nil { - return nil, nil, err + return nil, err } height, err := s.blobServ.Submit(ctx, blobs, blob.GasPrice(gasPrice)) if err != nil { log.Error("failed to submit blobs", "height", height, "gas price", gasPrice) - return nil, nil, err + return nil, err } log.Info("successfully submitted blobs", "height", height, "gas price", gasPrice) - ids := make([]da.ID, len(daBlobs)) - proofs := make([]da.Proof, len(daBlobs)) - for i, commitment := range commitments { - ids[i] = MakeID(height, commitment) - proof, err := s.blobServ.GetProof(ctx, height, namespace, commitment) - if err != nil { - return nil, nil, err - } - // TODO(tzdybal): does always len(*proof) == 1? - proofs[i], err = (*proof)[0].MarshalJSON() - if err != nil { - return nil, nil, err - } + ids := make([]da.ID, len(blobs)) + for i, blob := range blobs { + ids[i] = MakeID(height, blob.Commitment) } - return ids, proofs, nil + return ids, nil } // blobsAndCommitments converts []da.Blob to []*blob.Blob and generates corresponding @@ -130,11 +136,7 @@ func (s *Service) blobsAndCommitments( } blobs = append(blobs, b) - commitment, err := types.CreateCommitment(&b.Blob) - if err != nil { - return nil, nil, err - } - commitments = append(commitments, commitment) + commitments = append(commitments, b.Commitment) } return blobs, commitments, nil } @@ -149,13 +151,13 @@ func (s *Service) Validate( ) ([]bool, error) { included := make([]bool, len(ids)) proofs := make([]*blob.Proof, len(ids)) - for _, daProof := range daProofs { - nmtProof := &nmt.Proof{} - if err := nmtProof.UnmarshalJSON(daProof); err != nil { + for i, daProof := range daProofs { + blobProof := &blob.Proof{} + err := blobProof.UnmarshalJSON(daProof) + if err != nil { return nil, err } - proof := &blob.Proof{nmtProof} - proofs = append(proofs, proof) + proofs[i] = blobProof } for i, id := range ids { height, commitment := SplitID(id) @@ -163,6 +165,7 @@ func (s *Service) Validate( // invalid proof") but analysis of the code in celestia-node implies this should never happen - // maybe it's caused by openrpc? there is no way of gently handling errors here, but returned // value is fine for us + fmt.Println("proof", proofs[i] == nil, "commitment", commitment == nil) isIncluded, _ := s.blobServ.Included(ctx, height, namespace, proofs[i], commitment) included = append(included, isIncluded) } diff --git a/nodebuilder/tests/da_test.go b/nodebuilder/tests/da_test.go index 6ecfb1299e..024fc62b91 100644 --- a/nodebuilder/tests/da_test.go +++ b/nodebuilder/tests/da_test.go @@ -1,4 +1,4 @@ -//go:build da || integration +//go:build blob || integration package tests @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-node/blob" "github.com/celestiaorg/celestia-node/blob/blobtest" "github.com/celestiaorg/celestia-node/nodebuilder/da" @@ -24,7 +25,7 @@ import ( func TestDaModule(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second) t.Cleanup(cancel) - sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second*1)) + sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second)) namespace, err := share.NewBlobNamespaceV0([]byte("namespace")) require.NoError(t, err) @@ -37,7 +38,7 @@ func TestDaModule(t *testing.T) { daBlobs := make([][]byte, 0, len(appBlobs0)+len(appBlobs1)) for _, b := range append(appBlobs0, appBlobs1...) { - blob, err := blob.NewBlob(b.ShareVersion, append([]byte{b.NamespaceVersion}, namespace...), b.Data) + blob, err := blob.NewBlob(b.ShareVersion, namespace, b.Data) require.NoError(t, err) blobs = append(blobs, blob) daBlobs = append(daBlobs, blob.Data) @@ -66,7 +67,7 @@ func TestDaModule(t *testing.T) { fullClient := getAdminClient(ctx, fullNode, t) lightClient := getAdminClient(ctx, lightNode, t) - ids, proofs, err := fullClient.DA.Submit(ctx, daBlobs, -1, namespace) + ids, err := fullClient.DA.Submit(ctx, daBlobs, -1, namespace) require.NoError(t, err) var test = []struct { @@ -82,8 +83,13 @@ func TestDaModule(t *testing.T) { }, }, { - name: "Validate", + name: "GetProofs + Validate", doFn: func(t *testing.T) { + h, _ := da.SplitID(ids[0]) + lightClient.Header.WaitForHeight(ctx, h) + proofs, err := lightClient.DA.GetProofs(ctx, ids, namespace) + require.NoError(t, err) + require.NotEmpty(t, proofs) valid, err := fullClient.DA.Validate(ctx, ids, proofs, namespace) require.NoError(t, err) for _, v := range valid { @@ -103,6 +109,8 @@ func TestDaModule(t *testing.T) { { name: "Get", doFn: func(t *testing.T) { + h, _ := da.SplitID(ids[0]) + lightClient.Header.WaitForHeight(ctx, h) fetched, err := lightClient.DA.Get(ctx, ids, namespace) require.NoError(t, err) require.Len(t, fetched, len(ids))