From 3cc84c0b1f94b36c04a4f3acc787eca0c11e8624 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Oct 2025 13:57:24 +0100 Subject: [PATCH 1/3] addional validation Signed-off-by: Angelo De Caro --- token/core/fabtoken/v1/issue.go | 2 +- token/core/zkatdlog/nogh/v1/issue.go | 55 +++++++++++++++++----- token/core/zkatdlog/nogh/v1/token/token.go | 22 ++++++++- token/core/zkatdlog/nogh/v1/transfer.go | 36 ++++++++------ token/driver/issue.go | 2 +- token/request.go | 2 +- 6 files changed, 90 insertions(+), 29 deletions(-) diff --git a/token/core/fabtoken/v1/issue.go b/token/core/fabtoken/v1/issue.go index 9a8cb43ab2..68512fd534 100644 --- a/token/core/fabtoken/v1/issue.go +++ b/token/core/fabtoken/v1/issue.go @@ -108,7 +108,7 @@ func (s *IssueService) Issue(ctx context.Context, issuerIdentity driver.Identity } // VerifyIssue checks if the outputs of an IssueAction match the passed tokenInfos -func (s *IssueService) VerifyIssue(tr driver.IssueAction, metadata []*driver.IssueOutputMetadata) error { +func (s *IssueService) VerifyIssue(ctx context.Context, ia driver.IssueAction, metadata []*driver.IssueOutputMetadata) error { // TODO: return nil } diff --git a/token/core/zkatdlog/nogh/v1/issue.go b/token/core/zkatdlog/nogh/v1/issue.go index 7855d21562..c83030bb07 100644 --- a/token/core/zkatdlog/nogh/v1/issue.go +++ b/token/core/zkatdlog/nogh/v1/issue.go @@ -10,6 +10,7 @@ import ( "context" "time" + math "github.com/IBM/mathlib" "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors" common2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/common/meta" @@ -195,23 +196,55 @@ func (s *IssueService) Issue(ctx context.Context, issuerIdentity driver.Identity } // VerifyIssue checks if the outputs of an IssueAction match the passed metadata -func (s *IssueService) VerifyIssue(ia driver.IssueAction, metadata []*driver.IssueOutputMetadata) error { +func (s *IssueService) VerifyIssue(ctx context.Context, ia driver.IssueAction, outputMetadata []*driver.IssueOutputMetadata) error { + // prepare if ia == nil { - return errors.New("failed to verify issue: nil issue action") + return errors.Errorf("nil action") } action, ok := ia.(*issue.Action) if !ok { - return errors.New("failed to verify issue: expected *zkatdlog.IssueAction") + return errors.Errorf("expected *zkatdlog.IssueAction") } - pp := s.PublicParametersManager.PublicParameters() - coms, err := action.GetCommitments() - if err != nil { - return errors.New("failed to verify issue") + if err := action.Validate(); err != nil { + return errors.Wrap(err, "invalid action") + } + if len(action.Outputs) != len(outputMetadata) { + return errors.Errorf("number of outputs [%d] does not match number of metadata entries [%d]", len(action.Outputs), len(outputMetadata)) } - // todo check tokenInfo - return issue.NewVerifier( - coms, - pp.(*setup.PublicParams)).Verify(action.GetProof()) + + // check the metadata and extract the commitment + pp := s.PublicParametersManager.PublicParams() + coms := make([]*math.G1, len(action.Outputs)) + for i := range len(action.Outputs) { + coms[i] = action.Outputs[i].Data + + if outputMetadata[i] == nil || len(outputMetadata[i].OutputMetadata) == 0 { + return errors.Errorf("missing output metadata for output index [%d]", i) + } + // token information in cleartext + metadata := &token2.Metadata{} + if err := metadata.Deserialize(outputMetadata[i].OutputMetadata); err != nil { + return errors.Wrap(err, "failed unmarshalling metadata") + } + if err := metadata.Validate(); err != nil { + return errors.Wrap(err, "invalid metadata") + } + + // check that token info matches output. + // If so, return token in cleartext. Else return an error. + tok, err := action.Outputs[i].ToClear(metadata, pp) + if err != nil { + return errors.Wrap(err, "failed getting token in the clear") + } + s.Logger.DebugfContext(ctx, "transfer output [%s,%s,%s]", tok.Type, tok.Quantity, driver.Identity(tok.Owner)) + } + + // check the proof + if err := issue.NewVerifier(coms, pp).Verify(action.GetProof()); err != nil { + return errors.Wrap(err, "failed to verify issue proof") + } + + return nil } // DeserializeIssueAction un-marshals raw bytes into a zkatdlog IssueAction diff --git a/token/core/zkatdlog/nogh/v1/token/token.go b/token/core/zkatdlog/nogh/v1/token/token.go index 168ddc8d6e..c72ab787af 100644 --- a/token/core/zkatdlog/nogh/v1/token/token.go +++ b/token/core/zkatdlog/nogh/v1/token/token.go @@ -67,7 +67,11 @@ func (t *Token) Deserialize(bytes []byte) error { // ToClear returns Token in the clear func (t *Token) ToClear(meta *Metadata, pp *noghv1.PublicParams) (*token.Token, error) { - com, err := commit([]*math.Zr{math.Curves[pp.Curve].HashToZr([]byte(meta.Type)), meta.Value, meta.BlindingFactor}, pp.PedersenGenerators, math.Curves[pp.Curve]) + com, err := commit([]*math.Zr{ + math.Curves[pp.Curve].HashToZr([]byte(meta.Type)), + meta.Value, + meta.BlindingFactor, + }, pp.PedersenGenerators, math.Curves[pp.Curve]) if err != nil { return nil, errors.Wrap(err, "cannot retrieve token in the clear: failed to check token data") } @@ -198,6 +202,22 @@ func (m *Metadata) Clone() *Metadata { } } +func (m *Metadata) Validate() error { + if len(m.Type) == 0 { + return errors.New("missing Type") + } + if m.Value == nil { + return errors.New("missing Value") + } + if m.BlindingFactor == nil { + return errors.New("missing BlindingFactor") + } + if len(m.Issuer) == 0 { + return errors.New("missing Issuer") + } + return nil +} + func commit(vector []*math.Zr, generators []*math.G1, c *math.Curve) (*math.G1, error) { com := c.NewG1() for i := range vector { diff --git a/token/core/zkatdlog/nogh/v1/transfer.go b/token/core/zkatdlog/nogh/v1/transfer.go index f042b1d979..9bdc51dd90 100644 --- a/token/core/zkatdlog/nogh/v1/transfer.go +++ b/token/core/zkatdlog/nogh/v1/transfer.go @@ -264,40 +264,48 @@ func (s *TransferService) Transfer(ctx context.Context, anchor driver.TokenReque } // VerifyTransfer checks the outputs in the TransferActionMetadata against the passed metadata -func (s *TransferService) VerifyTransfer(ctx context.Context, action driver.TransferAction, outputMetadata []*driver.TransferOutputMetadata) error { - if action == nil { - return errors.New("failed to verify transfer: nil transfer action") +func (s *TransferService) VerifyTransfer(ctx context.Context, transferAction driver.TransferAction, outputMetadata []*driver.TransferOutputMetadata) error { + if transferAction == nil { + return errors.New("nil action") } - tr, ok := action.(*transfer.Action) + action, ok := transferAction.(*transfer.Action) if !ok { - return errors.New("failed to verify transfer: expected *zkatdlog.TransferActionMetadata") + return errors.New("expected *zkatdlog.TransferActionMetadata") + } + if err := action.Validate(); err != nil { + return errors.Wrap(err, "invalid action") + } + if len(action.Outputs) != len(outputMetadata) { + return errors.Errorf("number of outputs [%d] does not match number of metadata entries [%d]", len(action.Outputs), len(outputMetadata)) } // get commitments from outputs pp := s.PublicParametersManager.PublicParams() - com := make([]*math.G1, len(tr.Outputs)) - for i := range len(tr.Outputs) { - com[i] = tr.Outputs[i].Data + com := make([]*math.G1, len(action.Outputs)) + for i := range len(action.Outputs) { + com[i] = action.Outputs[i].Data if outputMetadata[i] == nil || len(outputMetadata[i].OutputMetadata) == 0 { continue } - // TODO: complete this check... - // token information in cleartext metadata := &token.Metadata{} if err := metadata.Deserialize(outputMetadata[i].OutputMetadata); err != nil { - return errors.Wrap(err, "failed unmarshalling token information") + return errors.Wrap(err, "failed unmarshalling metadata") + } + if err := metadata.Validate(); err != nil { + return errors.Wrap(err, "invalid metadata") } - // check that token info matches output. If so, return token in cleartext. Else return an error. - tok, err := tr.Outputs[i].ToClear(metadata, pp) + // check that token info matches output. + // If so, return token in cleartext. Else return an error. + tok, err := action.Outputs[i].ToClear(metadata, pp) if err != nil { return errors.Wrap(err, "failed getting token in the clear") } s.Logger.DebugfContext(ctx, "transfer output [%s,%s,%s]", tok.Type, tok.Quantity, driver.Identity(tok.Owner)) } - return transfer.NewVerifier(getTokenData(tr.InputTokens()), com, pp).Verify(tr.Proof) + return transfer.NewVerifier(getTokenData(action.InputTokens()), com, pp).Verify(action.Proof) } // DeserializeTransferAction un-marshals a TransferActionMetadata from the passed array of bytes. diff --git a/token/driver/issue.go b/token/driver/issue.go index d837377a23..ca508cadbb 100644 --- a/token/driver/issue.go +++ b/token/driver/issue.go @@ -43,7 +43,7 @@ type IssueService interface { Issue(ctx context.Context, issuerIdentity Identity, tokenType token.Type, values []uint64, owners [][]byte, opts *IssueOptions) (IssueAction, *IssueMetadata, error) // VerifyIssue checks the well-formedness of the passed IssuerAction with the respect to the passed metadata - VerifyIssue(ia IssueAction, metadata []*IssueOutputMetadata) error + VerifyIssue(ctx context.Context, ia IssueAction, metadata []*IssueOutputMetadata) error // DeserializeIssueAction deserializes the passed bytes into an IssuerAction DeserializeIssueAction(raw []byte) (IssueAction, error) diff --git a/token/request.go b/token/request.go index aa56006ad5..db4f01f33b 100644 --- a/token/request.go +++ b/token/request.go @@ -920,7 +920,7 @@ func (r *Request) inputsAndOutputs(ctx context.Context, failOnMissing, verifyAct } if verifyActions { - if err := issueService.VerifyIssue(issueAction, issueMeta.Outputs); err != nil { + if err := issueService.VerifyIssue(ctx, issueAction, issueMeta.Outputs); err != nil { return nil, nil, nil, errors.WithMessagef(err, "failed verifying issue action") } } From cad3a6b4a498ee0f3705151eb2d616aa5455ba0c Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Oct 2025 15:36:31 +0100 Subject: [PATCH 2/3] checks with unit-tests (to be completed Signed-off-by: Angelo De Caro --- token/core/zkatdlog/nogh/v1/issue.go | 6 +- token/core/zkatdlog/nogh/v1/issue_test.go | 247 ++++++++++++++ token/core/zkatdlog/nogh/v1/mock/ppm.go | 309 ++++++++++++++++++ token/core/zkatdlog/nogh/v1/ppm.go | 16 + token/core/zkatdlog/nogh/v1/token/token.go | 2 +- .../core/zkatdlog/nogh/v1/token/token_test.go | 122 ++++--- token/core/zkatdlog/nogh/v1/transfer.go | 5 +- token/core/zkatdlog/nogh/v1/transfer_test.go | 42 +++ 8 files changed, 689 insertions(+), 60 deletions(-) create mode 100644 token/core/zkatdlog/nogh/v1/issue_test.go create mode 100644 token/core/zkatdlog/nogh/v1/mock/ppm.go create mode 100644 token/core/zkatdlog/nogh/v1/ppm.go create mode 100644 token/core/zkatdlog/nogh/v1/transfer_test.go diff --git a/token/core/zkatdlog/nogh/v1/issue.go b/token/core/zkatdlog/nogh/v1/issue.go index c83030bb07..6665e6cdd6 100644 --- a/token/core/zkatdlog/nogh/v1/issue.go +++ b/token/core/zkatdlog/nogh/v1/issue.go @@ -12,12 +12,10 @@ import ( math "github.com/IBM/mathlib" "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors" - common2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/common/meta" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/crypto/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/crypto/upgrade" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/issue" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/setup" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/token" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" "github.com/hyperledger-labs/fabric-token-sdk/token/services/logging" @@ -26,7 +24,7 @@ import ( type IssueService struct { Logger logging.Logger - PublicParametersManager common2.PublicParametersManager[*setup.PublicParams] + PublicParametersManager PublicParametersManager WalletService driver.WalletService Deserializer driver.Deserializer Metrics *Metrics @@ -36,7 +34,7 @@ type IssueService struct { func NewIssueService( logger logging.Logger, - publicParametersManager common2.PublicParametersManager[*setup.PublicParams], + publicParametersManager PublicParametersManager, walletService driver.WalletService, deserializer driver.Deserializer, metrics *Metrics, diff --git a/token/core/zkatdlog/nogh/v1/issue_test.go b/token/core/zkatdlog/nogh/v1/issue_test.go new file mode 100644 index 0000000000..78c9f7ec5b --- /dev/null +++ b/token/core/zkatdlog/nogh/v1/issue_test.go @@ -0,0 +1,247 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1_test + +import ( + "testing" + + math "github.com/IBM/mathlib" + v1 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/issue" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/mock" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/setup" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/token" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + "github.com/hyperledger-labs/fabric-token-sdk/token/services/logging" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIssueService_VerifyIssue(t *testing.T) { + tests := []struct { + name string + TestCase func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) + wantErr string + }{ + { + name: "nil action", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + service := &v1.IssueService{} + return service, nil, nil + }, + wantErr: "nil action", + }, + { + name: "invalid action", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + service := &v1.IssueService{} + return service, &issue.Action{}, nil + }, + wantErr: "invalid action", + }, + { + name: "outputs metadata mismatch", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + testSetup := setupTest(t) + action, metadata := testSetup.createValidAction(t, []byte("an_issuer")) + + // Return only one metadata entry for two outputs + metaRaw0, err := metadata[0].Serialize() + require.NoError(t, err) + return testSetup.service, action, []*driver.IssueOutputMetadata{ + { + OutputMetadata: metaRaw0, + }, + } + }, + wantErr: "number of outputs [2] does not match number of metadata entries [1]", + }, + { + name: "missing output metadata", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + testSetup := setupTest(t) + action, _ := testSetup.createValidAction(t, []byte("an_issuer")) + return testSetup.service, action, []*driver.IssueOutputMetadata{nil, nil} + }, + wantErr: "missing output metadata for output index [0]", + }, + { + name: "invalid metadata", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + testSetup := setupTest(t) + action, _ := testSetup.createValidAction(t, []byte("an_issuer")) + // Return invalid metadata bytes + return testSetup.service, action, []*driver.IssueOutputMetadata{ + { + OutputMetadata: []byte("invalid"), + }, + { + OutputMetadata: []byte("invalid"), + }, + } + }, + wantErr: "failed unmarshalling metadata", + }, + { + name: "failed unmarshalling metadata", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + testSetup := setupTest(t) + action, meta := testSetup.createValidAction(t, []byte("an_issuer")) + meta[0].Issuer = nil + metaRaw0, err := meta[0].Serialize() + require.NoError(t, err) + metaRaw1, err := meta[1].Serialize() + require.NoError(t, err) + return testSetup.service, action, []*driver.IssueOutputMetadata{ + { + OutputMetadata: metaRaw0, + Receivers: nil, + }, + { + OutputMetadata: metaRaw1, + Receivers: nil, + }, + } + }, + wantErr: "invalid metadata: missing Issuer", + }, + { + name: "failed getting token in the clear", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + testSetup := setupTest(t) + action, meta := testSetup.createValidAction(t, []byte("an_issuer")) + meta[0].Type = "fake type" + metaRaw0, err := meta[0].Serialize() + require.NoError(t, err) + metaRaw1, err := meta[1].Serialize() + require.NoError(t, err) + return testSetup.service, action, []*driver.IssueOutputMetadata{ + { + OutputMetadata: metaRaw0, + Receivers: nil, + }, + { + OutputMetadata: metaRaw1, + Receivers: nil, + }, + } + }, + wantErr: "failed getting token in the clear", + }, + { + name: "success", + TestCase: func() (*v1.IssueService, driver.IssueAction, []*driver.IssueOutputMetadata) { + testSetup := setupTest(t) + action, meta := testSetup.createValidAction(t, []byte("an_issuer")) + metaRaw0, err := meta[0].Serialize() + require.NoError(t, err) + metaRaw1, err := meta[1].Serialize() + require.NoError(t, err) + + return testSetup.service, action, []*driver.IssueOutputMetadata{ + { + OutputMetadata: metaRaw0, + Receivers: nil, + }, + { + OutputMetadata: metaRaw1, + Receivers: nil, + }, + } + }, + wantErr: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + service, action, meta := tt.TestCase() + err := service.VerifyIssue(t.Context(), action, meta) + if tt.wantErr == "" { + assert.NoError(t, err) + } else { + assert.ErrorContains(t, err, tt.wantErr) + } + }) + } +} + +type testSetup struct { + service *v1.IssueService + pp *setup.PublicParams +} + +func setupTest(t *testing.T) *testSetup { + t.Helper() + pp, err := setup.Setup(32, nil, math.BN254) + assert.NoError(t, err) + ppm := &mock.PublicParametersManager{} + ppm.PublicParamsReturns(pp) + service := &v1.IssueService{ + Logger: logging.MustGetLogger(), + PublicParametersManager: ppm, + } + return &testSetup{ + service: service, + pp: pp, + } +} + +func (ts *testSetup) createValidAction(t *testing.T, issuer []byte) (driver.IssueAction, []*token.Metadata) { + t.Helper() + meta, tokens := prepareInputsForZKIssue(ts.pp) + prover, err := issue.NewProver(meta, tokens, ts.pp) + assert.NoError(t, err) + proof, err := prover.Prove() + assert.NoError(t, err) + + action := &issue.Action{ + Issuer: issuer, + Outputs: []*token.Token{ + { + Owner: []byte("an_owner"), + Data: tokens[0], + }, + { + Owner: []byte("an_owner"), + Data: tokens[1], + }, + }, + Proof: proof, + } + + // Prepare metadata + meta[0].Issuer = issuer + meta[1].Issuer = issuer + + return action, meta +} + +func prepareInputsForZKIssue(pp *setup.PublicParams) ([]*token.Metadata, []*math.G1) { + values := make([]uint64, 2) + values[0] = 120 + values[1] = 190 + curve := math.Curves[pp.Curve] + rand, _ := curve.Rand() + bf := make([]*math.Zr, len(values)) + for i := range values { + bf[i] = math.Curves[pp.Curve].NewRandomZr(rand) + } + + tokens := make([]*math.G1, len(values)) + for i := range values { + tokens[i] = newToken(curve.NewZrFromInt(int64(values[i])), bf[i], "ABC", pp.PedersenGenerators, curve) + } + return token.NewMetadata(pp.Curve, "ABC", values, bf), tokens +} + +func newToken(value *math.Zr, rand *math.Zr, tokenType string, pp []*math.G1, curve *math.Curve) *math.G1 { + tok := curve.NewG1() + tok.Add(pp[0].Mul(curve.HashToZr([]byte(tokenType)))) + tok.Add(pp[1].Mul(value)) + tok.Add(pp[2].Mul(rand)) + return tok +} diff --git a/token/core/zkatdlog/nogh/v1/mock/ppm.go b/token/core/zkatdlog/nogh/v1/mock/ppm.go new file mode 100644 index 0000000000..66b143bff2 --- /dev/null +++ b/token/core/zkatdlog/nogh/v1/mock/ppm.go @@ -0,0 +1,309 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package mock + +import ( + "sync" + + v1 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/setup" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" +) + +type PublicParametersManager struct { + NewCertifierKeyPairStub func() ([]byte, []byte, error) + newCertifierKeyPairMutex sync.RWMutex + newCertifierKeyPairArgsForCall []struct { + } + newCertifierKeyPairReturns struct { + result1 []byte + result2 []byte + result3 error + } + newCertifierKeyPairReturnsOnCall map[int]struct { + result1 []byte + result2 []byte + result3 error + } + PublicParametersStub func() driver.PublicParameters + publicParametersMutex sync.RWMutex + publicParametersArgsForCall []struct { + } + publicParametersReturns struct { + result1 driver.PublicParameters + } + publicParametersReturnsOnCall map[int]struct { + result1 driver.PublicParameters + } + PublicParamsStub func() *setup.PublicParams + publicParamsMutex sync.RWMutex + publicParamsArgsForCall []struct { + } + publicParamsReturns struct { + result1 *setup.PublicParams + } + publicParamsReturnsOnCall map[int]struct { + result1 *setup.PublicParams + } + PublicParamsHashStub func() driver.PPHash + publicParamsHashMutex sync.RWMutex + publicParamsHashArgsForCall []struct { + } + publicParamsHashReturns struct { + result1 driver.PPHash + } + publicParamsHashReturnsOnCall map[int]struct { + result1 driver.PPHash + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *PublicParametersManager) NewCertifierKeyPair() ([]byte, []byte, error) { + fake.newCertifierKeyPairMutex.Lock() + ret, specificReturn := fake.newCertifierKeyPairReturnsOnCall[len(fake.newCertifierKeyPairArgsForCall)] + fake.newCertifierKeyPairArgsForCall = append(fake.newCertifierKeyPairArgsForCall, struct { + }{}) + stub := fake.NewCertifierKeyPairStub + fakeReturns := fake.newCertifierKeyPairReturns + fake.recordInvocation("NewCertifierKeyPair", []interface{}{}) + fake.newCertifierKeyPairMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *PublicParametersManager) NewCertifierKeyPairCallCount() int { + fake.newCertifierKeyPairMutex.RLock() + defer fake.newCertifierKeyPairMutex.RUnlock() + return len(fake.newCertifierKeyPairArgsForCall) +} + +func (fake *PublicParametersManager) NewCertifierKeyPairCalls(stub func() ([]byte, []byte, error)) { + fake.newCertifierKeyPairMutex.Lock() + defer fake.newCertifierKeyPairMutex.Unlock() + fake.NewCertifierKeyPairStub = stub +} + +func (fake *PublicParametersManager) NewCertifierKeyPairReturns(result1 []byte, result2 []byte, result3 error) { + fake.newCertifierKeyPairMutex.Lock() + defer fake.newCertifierKeyPairMutex.Unlock() + fake.NewCertifierKeyPairStub = nil + fake.newCertifierKeyPairReturns = struct { + result1 []byte + result2 []byte + result3 error + }{result1, result2, result3} +} + +func (fake *PublicParametersManager) NewCertifierKeyPairReturnsOnCall(i int, result1 []byte, result2 []byte, result3 error) { + fake.newCertifierKeyPairMutex.Lock() + defer fake.newCertifierKeyPairMutex.Unlock() + fake.NewCertifierKeyPairStub = nil + if fake.newCertifierKeyPairReturnsOnCall == nil { + fake.newCertifierKeyPairReturnsOnCall = make(map[int]struct { + result1 []byte + result2 []byte + result3 error + }) + } + fake.newCertifierKeyPairReturnsOnCall[i] = struct { + result1 []byte + result2 []byte + result3 error + }{result1, result2, result3} +} + +func (fake *PublicParametersManager) PublicParameters() driver.PublicParameters { + fake.publicParametersMutex.Lock() + ret, specificReturn := fake.publicParametersReturnsOnCall[len(fake.publicParametersArgsForCall)] + fake.publicParametersArgsForCall = append(fake.publicParametersArgsForCall, struct { + }{}) + stub := fake.PublicParametersStub + fakeReturns := fake.publicParametersReturns + fake.recordInvocation("PublicParameters", []interface{}{}) + fake.publicParametersMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *PublicParametersManager) PublicParametersCallCount() int { + fake.publicParametersMutex.RLock() + defer fake.publicParametersMutex.RUnlock() + return len(fake.publicParametersArgsForCall) +} + +func (fake *PublicParametersManager) PublicParametersCalls(stub func() driver.PublicParameters) { + fake.publicParametersMutex.Lock() + defer fake.publicParametersMutex.Unlock() + fake.PublicParametersStub = stub +} + +func (fake *PublicParametersManager) PublicParametersReturns(result1 driver.PublicParameters) { + fake.publicParametersMutex.Lock() + defer fake.publicParametersMutex.Unlock() + fake.PublicParametersStub = nil + fake.publicParametersReturns = struct { + result1 driver.PublicParameters + }{result1} +} + +func (fake *PublicParametersManager) PublicParametersReturnsOnCall(i int, result1 driver.PublicParameters) { + fake.publicParametersMutex.Lock() + defer fake.publicParametersMutex.Unlock() + fake.PublicParametersStub = nil + if fake.publicParametersReturnsOnCall == nil { + fake.publicParametersReturnsOnCall = make(map[int]struct { + result1 driver.PublicParameters + }) + } + fake.publicParametersReturnsOnCall[i] = struct { + result1 driver.PublicParameters + }{result1} +} + +func (fake *PublicParametersManager) PublicParams() *setup.PublicParams { + fake.publicParamsMutex.Lock() + ret, specificReturn := fake.publicParamsReturnsOnCall[len(fake.publicParamsArgsForCall)] + fake.publicParamsArgsForCall = append(fake.publicParamsArgsForCall, struct { + }{}) + stub := fake.PublicParamsStub + fakeReturns := fake.publicParamsReturns + fake.recordInvocation("PublicParams", []interface{}{}) + fake.publicParamsMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *PublicParametersManager) PublicParamsCallCount() int { + fake.publicParamsMutex.RLock() + defer fake.publicParamsMutex.RUnlock() + return len(fake.publicParamsArgsForCall) +} + +func (fake *PublicParametersManager) PublicParamsCalls(stub func() *setup.PublicParams) { + fake.publicParamsMutex.Lock() + defer fake.publicParamsMutex.Unlock() + fake.PublicParamsStub = stub +} + +func (fake *PublicParametersManager) PublicParamsReturns(result1 *setup.PublicParams) { + fake.publicParamsMutex.Lock() + defer fake.publicParamsMutex.Unlock() + fake.PublicParamsStub = nil + fake.publicParamsReturns = struct { + result1 *setup.PublicParams + }{result1} +} + +func (fake *PublicParametersManager) PublicParamsReturnsOnCall(i int, result1 *setup.PublicParams) { + fake.publicParamsMutex.Lock() + defer fake.publicParamsMutex.Unlock() + fake.PublicParamsStub = nil + if fake.publicParamsReturnsOnCall == nil { + fake.publicParamsReturnsOnCall = make(map[int]struct { + result1 *setup.PublicParams + }) + } + fake.publicParamsReturnsOnCall[i] = struct { + result1 *setup.PublicParams + }{result1} +} + +func (fake *PublicParametersManager) PublicParamsHash() driver.PPHash { + fake.publicParamsHashMutex.Lock() + ret, specificReturn := fake.publicParamsHashReturnsOnCall[len(fake.publicParamsHashArgsForCall)] + fake.publicParamsHashArgsForCall = append(fake.publicParamsHashArgsForCall, struct { + }{}) + stub := fake.PublicParamsHashStub + fakeReturns := fake.publicParamsHashReturns + fake.recordInvocation("PublicParamsHash", []interface{}{}) + fake.publicParamsHashMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *PublicParametersManager) PublicParamsHashCallCount() int { + fake.publicParamsHashMutex.RLock() + defer fake.publicParamsHashMutex.RUnlock() + return len(fake.publicParamsHashArgsForCall) +} + +func (fake *PublicParametersManager) PublicParamsHashCalls(stub func() driver.PPHash) { + fake.publicParamsHashMutex.Lock() + defer fake.publicParamsHashMutex.Unlock() + fake.PublicParamsHashStub = stub +} + +func (fake *PublicParametersManager) PublicParamsHashReturns(result1 driver.PPHash) { + fake.publicParamsHashMutex.Lock() + defer fake.publicParamsHashMutex.Unlock() + fake.PublicParamsHashStub = nil + fake.publicParamsHashReturns = struct { + result1 driver.PPHash + }{result1} +} + +func (fake *PublicParametersManager) PublicParamsHashReturnsOnCall(i int, result1 driver.PPHash) { + fake.publicParamsHashMutex.Lock() + defer fake.publicParamsHashMutex.Unlock() + fake.PublicParamsHashStub = nil + if fake.publicParamsHashReturnsOnCall == nil { + fake.publicParamsHashReturnsOnCall = make(map[int]struct { + result1 driver.PPHash + }) + } + fake.publicParamsHashReturnsOnCall[i] = struct { + result1 driver.PPHash + }{result1} +} + +func (fake *PublicParametersManager) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.newCertifierKeyPairMutex.RLock() + defer fake.newCertifierKeyPairMutex.RUnlock() + fake.publicParametersMutex.RLock() + defer fake.publicParametersMutex.RUnlock() + fake.publicParamsMutex.RLock() + defer fake.publicParamsMutex.RUnlock() + fake.publicParamsHashMutex.RLock() + defer fake.publicParamsHashMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *PublicParametersManager) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ v1.PublicParametersManager = new(PublicParametersManager) diff --git a/token/core/zkatdlog/nogh/v1/ppm.go b/token/core/zkatdlog/nogh/v1/ppm.go new file mode 100644 index 0000000000..99cf192390 --- /dev/null +++ b/token/core/zkatdlog/nogh/v1/ppm.go @@ -0,0 +1,16 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1 + +import ( + common2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/setup" +) + +//go:generate counterfeiter -o mock/ppm.go -fake-name PublicParametersManager . PublicParametersManager + +type PublicParametersManager = common2.PublicParametersManager[*setup.PublicParams] diff --git a/token/core/zkatdlog/nogh/v1/token/token.go b/token/core/zkatdlog/nogh/v1/token/token.go index c72ab787af..14b81c5af1 100644 --- a/token/core/zkatdlog/nogh/v1/token/token.go +++ b/token/core/zkatdlog/nogh/v1/token/token.go @@ -141,8 +141,8 @@ func NewMetadata(curve math.CurveID, tokenType token.Type, values []uint64, bfs witness := make([]*Metadata, len(values)) for i, v := range values { witness[i] = &Metadata{Value: math.Curves[curve].NewZrFromUint64(v), BlindingFactor: bfs[i]} + witness[i].Type = tokenType } - witness[0].Type = tokenType return witness } diff --git a/token/core/zkatdlog/nogh/v1/token/token_test.go b/token/core/zkatdlog/nogh/v1/token/token_test.go index b63b3e2d5d..c17621f963 100644 --- a/token/core/zkatdlog/nogh/v1/token/token_test.go +++ b/token/core/zkatdlog/nogh/v1/token/token_test.go @@ -4,71 +4,89 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package token_test +package token import ( "testing" math "github.com/IBM/mathlib" - v1 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/setup" - token2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/token" - token3 "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/stretchr/testify/assert" ) -func TestToClear(t *testing.T) { - var ( - inf *token2.Metadata - token *token2.Token - pp *v1.PublicParams - err error - ) - pp, err = v1.Setup(64, nil, math.BN254) - assert.NoError(t, err) - c := math.Curves[pp.Curve] +func TestMetadata_Validate(t *testing.T) { + // Setup valid metadata for reference + curve := math.BN254 + c := math.Curves[curve] rand, err := c.Rand() assert.NoError(t, err) - inf = &token2.Metadata{ - Value: c.NewZrFromInt(50), - Type: "ABC", + + validMetadata := &Metadata{ + Type: "COIN", + Value: c.NewRandomZr(rand), BlindingFactor: c.NewRandomZr(rand), + Issuer: []byte("issuer1"), } - token = &token2.Token{} - token.Data = c.NewG1() - token.Data.Add(pp.PedersenGenerators[1].Mul(inf.Value)) - token.Data.Add(pp.PedersenGenerators[2].Mul(inf.BlindingFactor)) - token.Data.Add(pp.PedersenGenerators[0].Mul(c.HashToZr([]byte("ABC")))) - tok, err := token.ToClear(inf, pp) - assert.NoError(t, err) - assert.Equal(t, token3.Type("ABC"), tok.Type) - assert.Equal(t, "0x"+inf.Value.String(), tok.Quantity) -} -func FuzzSerialization(f *testing.F) { - testcases := [][]any{ - {[]byte("Alice"), false}, - {[]byte("Charlie"), true}, - } - for _, tc := range testcases { - f.Add(tc[0].([]byte), tc[1].(bool)) + tests := []struct { + name string + meta *Metadata + wantErr string + }{ + { + name: "valid metadata", + meta: validMetadata, + wantErr: "", + }, + { + name: "missing type", + meta: &Metadata{ + Type: "", + Value: validMetadata.Value, + BlindingFactor: validMetadata.BlindingFactor, + Issuer: validMetadata.Issuer, + }, + wantErr: "missing Type", + }, + { + name: "missing value", + meta: &Metadata{ + Type: validMetadata.Type, + Value: nil, + BlindingFactor: validMetadata.BlindingFactor, + Issuer: validMetadata.Issuer, + }, + wantErr: "missing Value", + }, + { + name: "missing blinding factor", + meta: &Metadata{ + Type: validMetadata.Type, + Value: validMetadata.Value, + BlindingFactor: nil, + Issuer: validMetadata.Issuer, + }, + wantErr: "missing BlindingFactor", + }, + { + name: "missing issuer", + meta: &Metadata{ + Type: validMetadata.Type, + Value: validMetadata.Value, + BlindingFactor: validMetadata.BlindingFactor, + Issuer: nil, + }, + wantErr: "missing Issuer", + }, } - f.Fuzz(func(t *testing.T, owner []byte, putData bool) { - token := &token2.Token{ - Owner: owner, - } - if putData { - token.Data = math.Curves[math.BN254].NewG1() - } - raw, err := token.Serialize() - assert.NoError(f, err) - assert.NotNil(t, raw) - token2 := &token2.Token{} - err = token2.Deserialize(raw) - if err != nil { - t.Errorf("failed to deserialize token [owner: %s, putData: %v]: [%v]", owner, putData, err) - } - assert.Len(t, token2.Owner, len(token.Owner), "owner mismatch [owner: %s, putData: %v]", owner, putData) - assert.Equal(t, token.Data, token2.Data) - }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.meta.Validate() + if tt.wantErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.wantErr) + } + }) + } } diff --git a/token/core/zkatdlog/nogh/v1/transfer.go b/token/core/zkatdlog/nogh/v1/transfer.go index 9bdc51dd90..c5f5d0308c 100644 --- a/token/core/zkatdlog/nogh/v1/transfer.go +++ b/token/core/zkatdlog/nogh/v1/transfer.go @@ -15,7 +15,6 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/tracing" "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/common/meta" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/setup" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/token" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/nogh/v1/transfer" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" @@ -69,7 +68,7 @@ type TokenDeserializer interface { type TransferService struct { Logger logging.Logger - PublicParametersManager common.PublicParametersManager[*setup.PublicParams] + PublicParametersManager PublicParametersManager WalletService driver.WalletService TokenLoader TokenLoader IdentityDeserializer driver.Deserializer @@ -80,7 +79,7 @@ type TransferService struct { func NewTransferService( logger logging.Logger, - publicParametersManager common.PublicParametersManager[*setup.PublicParams], + publicParametersManager PublicParametersManager, walletService driver.WalletService, tokenLoader TokenLoader, identityDeserializer driver.Deserializer, diff --git a/token/core/zkatdlog/nogh/v1/transfer_test.go b/token/core/zkatdlog/nogh/v1/transfer_test.go new file mode 100644 index 0000000000..ffbe6caf94 --- /dev/null +++ b/token/core/zkatdlog/nogh/v1/transfer_test.go @@ -0,0 +1,42 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1 + +import ( + "testing" + + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + "github.com/stretchr/testify/assert" +) + +func TestTransferService_VerifyTransfer(t *testing.T) { + tests := []struct { + name string + TestCase func() (*TransferService, driver.TransferAction, []*driver.TransferOutputMetadata) + wantErr string + }{ + { + name: "nil action", + TestCase: func() (*TransferService, driver.TransferAction, []*driver.TransferOutputMetadata) { + service := &TransferService{} + return service, nil, nil + }, + wantErr: "nil action", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + service, action, meta := tt.TestCase() + err := service.VerifyTransfer(t.Context(), action, meta) + if tt.wantErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.wantErr) + } + }) + } +} From 89c6f49b42509de88f633ae1cb24bb453b1854b7 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Oct 2025 15:57:42 +0100 Subject: [PATCH 3/3] not validate the issuer for transfers Signed-off-by: Angelo De Caro --- token/core/zkatdlog/nogh/v1/issue.go | 2 +- token/core/zkatdlog/nogh/v1/token/errors.go | 40 +++++++++++++++++++ token/core/zkatdlog/nogh/v1/token/token.go | 36 ++++++++++------- .../core/zkatdlog/nogh/v1/token/token_test.go | 39 +++++++++++++----- token/core/zkatdlog/nogh/v1/transfer.go | 2 +- 5 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 token/core/zkatdlog/nogh/v1/token/errors.go diff --git a/token/core/zkatdlog/nogh/v1/issue.go b/token/core/zkatdlog/nogh/v1/issue.go index 6665e6cdd6..5bde803aee 100644 --- a/token/core/zkatdlog/nogh/v1/issue.go +++ b/token/core/zkatdlog/nogh/v1/issue.go @@ -224,7 +224,7 @@ func (s *IssueService) VerifyIssue(ctx context.Context, ia driver.IssueAction, o if err := metadata.Deserialize(outputMetadata[i].OutputMetadata); err != nil { return errors.Wrap(err, "failed unmarshalling metadata") } - if err := metadata.Validate(); err != nil { + if err := metadata.Validate(true); err != nil { return errors.Wrap(err, "invalid metadata") } diff --git a/token/core/zkatdlog/nogh/v1/token/errors.go b/token/core/zkatdlog/nogh/v1/token/errors.go new file mode 100644 index 0000000000..e383f08cb4 --- /dev/null +++ b/token/core/zkatdlog/nogh/v1/token/errors.go @@ -0,0 +1,40 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package token + +import "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors" + +var ( + // ErrEmptyType is returned when a token type is empty + ErrEmptyType = errors.New("missing Type") + // ErrEmptyValue is returned when a token value is nil + ErrEmptyValue = errors.New("missing Value") + // ErrEmptyBlindingFactor is returned when a token blinding factor is nil + ErrEmptyBlindingFactor = errors.New("missing BlindingFactor") + // ErrMissingIssuer is returned when an issuer is required but missing + ErrMissingIssuer = errors.New("missing Issuer") + // ErrUnexpectedIssuer is returned when an issuer is present but should not be + ErrUnexpectedIssuer = errors.New("issuer should not be there") + // ErrEmptyOwner is returned when a token owner is empty + ErrEmptyOwner = errors.New("token owner cannot be empty") + // ErrEmptyTokenData is returned when token data is nil + ErrEmptyTokenData = errors.New("token data cannot be empty") + // ErrNilCommitElement is returned when trying to commit a nil element + ErrNilCommitElement = errors.New("cannot commit a nil element") + // ErrTokenMismatch is returned when a token does not match its metadata + ErrTokenMismatch = errors.New("cannot retrieve token in the clear: output does not match provided opening") + // ErrMissingFabToken is returned when FabToken is nil in UpgradeWitness + ErrMissingFabToken = errors.New("missing FabToken") + // ErrMissingFabTokenOwner is returned when FabToken owner is empty + ErrMissingFabTokenOwner = errors.New("missing FabToken.Owner") + // ErrMissingFabTokenType is returned when FabToken type is empty + ErrMissingFabTokenType = errors.New("missing FabToken.Type") + // ErrMissingFabTokenQuantity is returned when FabToken quantity is empty + ErrMissingFabTokenQuantity = errors.New("missing FabToken.Quantity") + // ErrMissingUpgradeBlindingFactor is returned when upgrade blinding factor is nil + ErrMissingUpgradeBlindingFactor = errors.New("missing BlindingFactor") +) diff --git a/token/core/zkatdlog/nogh/v1/token/token.go b/token/core/zkatdlog/nogh/v1/token/token.go index 14b81c5af1..259b32c00b 100644 --- a/token/core/zkatdlog/nogh/v1/token/token.go +++ b/token/core/zkatdlog/nogh/v1/token/token.go @@ -77,7 +77,7 @@ func (t *Token) ToClear(meta *Metadata, pp *noghv1.PublicParams) (*token.Token, } // check that token matches meta if !com.Equals(t.Data) { - return nil, errors.New("cannot retrieve token in the clear: output does not match provided opening") + return nil, ErrTokenMismatch } return &token.Token{ Type: meta.Type, @@ -88,10 +88,10 @@ func (t *Token) ToClear(meta *Metadata, pp *noghv1.PublicParams) (*token.Token, func (t *Token) Validate(checkOwner bool) error { if checkOwner && len(t.Owner) == 0 { - return errors.Errorf("token owner cannot be empty") + return ErrEmptyOwner } if t.Data == nil { - return errors.Errorf("token data cannot be empty") + return ErrEmptyTokenData } return nil } @@ -202,18 +202,24 @@ func (m *Metadata) Clone() *Metadata { } } -func (m *Metadata) Validate() error { +// Validate checks that Metadata is well-formed. +// If checkIssuer is true, it checks that the Issuer field is set. +// If checkIssuer is false, it checks that the Issuer field is not set. +func (m *Metadata) Validate(checkIssuer bool) error { if len(m.Type) == 0 { - return errors.New("missing Type") + return ErrEmptyType } if m.Value == nil { - return errors.New("missing Value") + return ErrEmptyValue } if m.BlindingFactor == nil { - return errors.New("missing BlindingFactor") + return ErrEmptyBlindingFactor } - if len(m.Issuer) == 0 { - return errors.New("missing Issuer") + if checkIssuer && len(m.Issuer) == 0 { + return ErrMissingIssuer + } + if !checkIssuer && len(m.Issuer) != 0 { + return ErrUnexpectedIssuer } return nil } @@ -222,7 +228,7 @@ func commit(vector []*math.Zr, generators []*math.G1, c *math.Curve) (*math.G1, com := c.NewG1() for i := range vector { if vector[i] == nil { - return nil, errors.New("cannot commit a nil element") + return nil, ErrNilCommitElement } com.Add(generators[i].Mul(vector[i])) } @@ -237,19 +243,19 @@ type UpgradeWitness struct { func (u *UpgradeWitness) Validate() error { if u.FabToken == nil { - return errors.New("missing FabToken") + return ErrMissingFabToken } if len(u.FabToken.Owner) == 0 { - return errors.New("missing FabToken.Owner") + return ErrMissingFabTokenOwner } if len(u.FabToken.Type) == 0 { - return errors.New("missing FabToken.Type") + return ErrMissingFabTokenType } if len(u.FabToken.Quantity) == 0 { - return errors.New("missing FabToken.Quantity") + return ErrMissingFabTokenQuantity } if u.BlindingFactor == nil { - return errors.New("missing BlindingFactor") + return ErrMissingUpgradeBlindingFactor } return nil } diff --git a/token/core/zkatdlog/nogh/v1/token/token_test.go b/token/core/zkatdlog/nogh/v1/token/token_test.go index c17621f963..a73d29f3ac 100644 --- a/token/core/zkatdlog/nogh/v1/token/token_test.go +++ b/token/core/zkatdlog/nogh/v1/token/token_test.go @@ -28,14 +28,16 @@ func TestMetadata_Validate(t *testing.T) { } tests := []struct { - name string - meta *Metadata - wantErr string + name string + meta *Metadata + checkIssuer bool + wantErr string }{ { - name: "valid metadata", - meta: validMetadata, - wantErr: "", + name: "valid metadata", + meta: validMetadata, + checkIssuer: true, + wantErr: "", }, { name: "missing type", @@ -45,7 +47,8 @@ func TestMetadata_Validate(t *testing.T) { BlindingFactor: validMetadata.BlindingFactor, Issuer: validMetadata.Issuer, }, - wantErr: "missing Type", + checkIssuer: true, + wantErr: ErrEmptyType.Error(), }, { name: "missing value", @@ -55,7 +58,8 @@ func TestMetadata_Validate(t *testing.T) { BlindingFactor: validMetadata.BlindingFactor, Issuer: validMetadata.Issuer, }, - wantErr: "missing Value", + checkIssuer: true, + wantErr: ErrEmptyValue.Error(), }, { name: "missing blinding factor", @@ -65,7 +69,8 @@ func TestMetadata_Validate(t *testing.T) { BlindingFactor: nil, Issuer: validMetadata.Issuer, }, - wantErr: "missing BlindingFactor", + checkIssuer: true, + wantErr: ErrEmptyBlindingFactor.Error(), }, { name: "missing issuer", @@ -75,13 +80,25 @@ func TestMetadata_Validate(t *testing.T) { BlindingFactor: validMetadata.BlindingFactor, Issuer: nil, }, - wantErr: "missing Issuer", + checkIssuer: true, + wantErr: ErrMissingIssuer.Error(), + }, + { + name: "should not have the issuer", + meta: &Metadata{ + Type: validMetadata.Type, + Value: validMetadata.Value, + BlindingFactor: validMetadata.BlindingFactor, + Issuer: validMetadata.Issuer, + }, + checkIssuer: false, + wantErr: ErrUnexpectedIssuer.Error(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := tt.meta.Validate() + err := tt.meta.Validate(tt.checkIssuer) if tt.wantErr == "" { assert.NoError(t, err) } else { diff --git a/token/core/zkatdlog/nogh/v1/transfer.go b/token/core/zkatdlog/nogh/v1/transfer.go index c5f5d0308c..eafd591c4f 100644 --- a/token/core/zkatdlog/nogh/v1/transfer.go +++ b/token/core/zkatdlog/nogh/v1/transfer.go @@ -291,7 +291,7 @@ func (s *TransferService) VerifyTransfer(ctx context.Context, transferAction dri if err := metadata.Deserialize(outputMetadata[i].OutputMetadata); err != nil { return errors.Wrap(err, "failed unmarshalling metadata") } - if err := metadata.Validate(); err != nil { + if err := metadata.Validate(false); err != nil { return errors.Wrap(err, "invalid metadata") }