Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion token/core/fabtoken/v1/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
61 changes: 46 additions & 15 deletions token/core/zkatdlog/nogh/v1/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ 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"
"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"
Expand All @@ -25,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
Expand All @@ -35,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,
Expand Down Expand Up @@ -195,23 +194,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(true); 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
Expand Down
247 changes: 247 additions & 0 deletions token/core/zkatdlog/nogh/v1/issue_test.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading