Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spendable tokens #779

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
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 go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.6
require (
github.com/IBM/idemix v0.0.2-0.20240816143710-3dce4618d760
github.com/IBM/idemix/bccsp/types v0.0.0-20240816143710-3dce4618d760
github.com/IBM/mathlib v0.0.3-0.20231011094432-44ee0eb539da
github.com/IBM/mathlib v0.0.3-0.20241119052826-7fdf2555b381
github.com/gin-gonic/gin v1.10.0
github.com/hashicorp/go-uuid v1.0.3
github.com/hyperledger-labs/fabric-smart-client v0.3.1-0.20241114072958-8bdbea854812
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -634,8 +634,8 @@ github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20240612072411-114d281b442d h
github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20240612072411-114d281b442d/go.mod h1:FC0vVgNI6bv8GH0VTwjup+arwJ8Tau1iEhroWZ1oPwU=
github.com/IBM/idemix/bccsp/types v0.0.0-20240816143710-3dce4618d760 h1:4tcdWj0MONt4JtiqNESWMuWSb5rDInIFzlUSbncHlCM=
github.com/IBM/idemix/bccsp/types v0.0.0-20240816143710-3dce4618d760/go.mod h1:4bYvi+a50aXxmHf1vwuvR+Wd8YXZ6AhT+0p5oK4xdOA=
github.com/IBM/mathlib v0.0.3-0.20231011094432-44ee0eb539da h1:qqGozq4tF6EOVnWoTgBoJGudRKKZXSAYnEtDggzTnsw=
github.com/IBM/mathlib v0.0.3-0.20231011094432-44ee0eb539da/go.mod h1:Tco9QzE3fQzjMS7nPbHDeFfydAzctStf1Pa8hsh6Hjs=
github.com/IBM/mathlib v0.0.3-0.20241119052826-7fdf2555b381 h1:FHobdbIHiODoLuYhuUlJvGbEKsrmolJBMxZ/18L+Z3s=
github.com/IBM/mathlib v0.0.3-0.20241119052826-7fdf2555b381/go.mod h1:Tco9QzE3fQzjMS7nPbHDeFfydAzctStf1Pa8hsh6Hjs=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
Expand Down
3 changes: 2 additions & 1 deletion integration/token/fungible/mixed/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ func Topology(opts common.Opts) []api.Topology {
RegisterViewFactory("ListVaultUnspentTokens", &views.ListVaultUnspentTokensViewFactory{}).
RegisterViewFactory("TxFinality", &views2.TxFinalityViewFactory{}).
RegisterViewFactory("MaliciousTransfer", &views.MaliciousTransferViewFactory{}).
RegisterViewFactory("TxStatus", &views.TxStatusViewFactory{})
RegisterViewFactory("TxStatus", &views.TxStatusViewFactory{}).
RegisterViewFactory("SetSpendableFlag", &views.SetSpendableFlagViewFactory{})

bob := fscTopology.AddNodeByName("bob").AddOptions(
fabric.WithOrganization("Org2"),
Expand Down
9 changes: 9 additions & 0 deletions integration/token/fungible/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,15 @@ func SetKVSEntry(network *integration.Infrastructure, user *token3.NodeReference
Expect(err).NotTo(HaveOccurred())
}

func SetSpendableFlag(network *integration.Infrastructure, user *token3.NodeReference, tokenID token.ID, value bool) {
_, err := network.Client(user.ReplicaName()).CallView("SetSpendableFlag", common.JSONMarshall(&views.SetSpendableFlag{
TMSID: token2.TMSID{},
TokenID: tokenID,
Spendable: value,
}))
Expect(err).NotTo(HaveOccurred())
}

func Withdraw(network *integration.Infrastructure, wpm *WalletManagerProvider, user *token3.NodeReference, wallet string, typ string, amount uint64, auditor *token3.NodeReference, issuer *token3.NodeReference, expectedErrorMsgs ...string) string {
var recipientData *token2.RecipientData
if wpm != nil {
Expand Down
10 changes: 10 additions & 0 deletions integration/token/fungible/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,16 @@ func TestAll(network *integration.Infrastructure, auditorId string, onRestart On
CheckBalanceAndHolding(network, bob, "", "Pineapples", 0, auditor)
CheckBalanceAndHolding(network, charlie, "", "Pineapples", 0, auditor)
CheckAuditorDB(network, auditor, "", nil)

// test spendable token
txIssueSpendableToken := IssueCash(network, "", "Spendable", 3, alice, auditor, true, issuer)
SetSpendableFlag(network, alice, token2.ID{TxId: txIssueSpendableToken, Index: 0}, false)
TransferCash(network, alice, "", "Spendable", 2, bob, auditor, "failed selecting tokens")
SetSpendableFlag(network, alice, token2.ID{TxId: txIssueSpendableToken, Index: 0}, true)
TransferCash(network, alice, "", "Spendable", 2, bob, auditor)
CheckBalanceAndHolding(network, alice, "", "Spendable", 1, auditor)
CheckBalanceAndHolding(network, bob, "", "Spendable", 2, auditor)
CheckAuditorDB(network, auditor, "", nil)
}

func TestSelector(network *integration.Infrastructure, auditorId string, sel *token3.ReplicaSelector) {
Expand Down
3 changes: 2 additions & 1 deletion integration/token/fungible/topology/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ func Topology(opts common.Opts) []api.Topology {
alice.RegisterViewFactory("TxFinality", &views2.TxFinalityViewFactory{})
alice.RegisterViewFactory("MaliciousTransfer", &views.MaliciousTransferViewFactory{})
alice.RegisterViewFactory("TxStatus", &views.TxStatusViewFactory{})
alice.RegisterViewFactory("SetSpendableFlag", &views.SetSpendableFlagViewFactory{})

bob := fscTopology.AddNodeByName("bob").AddOptions(
fabric.WithOrganization("Org2"),
Expand Down Expand Up @@ -361,7 +362,7 @@ func Topology(opts common.Opts) []api.Topology {
}
if opts.Monitoring {
monitoringTopology := monitoring.NewTopology()
//monitoringTopology.EnableHyperledgerExplorer()
// monitoringTopology.EnableHyperledgerExplorer()
monitoringTopology.EnablePrometheusGrafana()
return []api.Topology{
backendNetwork, tokenTopology, fscTopology,
Expand Down
76 changes: 57 additions & 19 deletions integration/token/fungible/views/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (

view2 "github.com/hyperledger-labs/fabric-smart-client/platform/view"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/tokendb"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/tokens"
token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token"

"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/assert"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/kvs"
Expand Down Expand Up @@ -42,6 +44,31 @@ func AssertTokens(sp token.ServiceProvider, tx *ttx.Transaction, outputs *token.
}
}

// ServiceOpts creates a new array of token.ServiceOption containing token.WithTMSID and any additional token.ServiceOption passed to this function
func ServiceOpts(tmsId *token.TMSID, opts ...token.ServiceOption) []token.ServiceOption {
var serviceOpts []token.ServiceOption
if tmsId != nil {
serviceOpts = append(serviceOpts, token.WithTMSID(*tmsId))
}
return append(serviceOpts, opts...)
}

func TxOpts(tmsId *token.TMSID) []ttx.TxOption {
var txOpts []ttx.TxOption
if tmsId != nil {
txOpts = append(txOpts, ttx.WithTMSID(*tmsId))
}
return txOpts
}

func GetKVS(sp view2.ServiceProvider) *kvs.KVS {
kvss, err := sp.GetService(&kvs.KVS{})
if err != nil {
panic(err)
}
return kvss.(*kvs.KVS)
}

type KVSEntry struct {
Key string
Value string
Expand All @@ -66,27 +93,38 @@ func (p *SetKVSEntryViewFactory) NewView(in []byte) (view.View, error) {
return f, nil
}

// ServiceOpts creates a new array of token.ServiceOption containing token.WithTMSID and any additional token.ServiceOption passed to this function
func ServiceOpts(tmsId *token.TMSID, opts ...token.ServiceOption) []token.ServiceOption {
var serviceOpts []token.ServiceOption
if tmsId != nil {
serviceOpts = append(serviceOpts, token.WithTMSID(*tmsId))
}
return append(serviceOpts, opts...)
type SetSpendableFlag struct {
TMSID token.TMSID
TokenID token2.ID
Spendable bool
}

func TxOpts(tmsId *token.TMSID) []ttx.TxOption {
var txOpts []ttx.TxOption
if tmsId != nil {
txOpts = append(txOpts, ttx.WithTMSID(*tmsId))
}
return txOpts
type SetSpendableFlagView struct {
*SetSpendableFlag
}

func GetKVS(sp view2.ServiceProvider) *kvs.KVS {
kvss, err := sp.GetService(&kvs.KVS{})
if err != nil {
panic(err)
}
return kvss.(*kvs.KVS)
func (s *SetSpendableFlagView) Call(context view.Context) (interface{}, error) {
tms := token.GetManagementService(context, token.WithTMSID(s.TMSID))
assert.NotNil(tms, "failed getting token management service [%s]", s.TMSID)

tokensProvider, err := tokens.GetProvider(context)
assert.NoError(err, "failed getting tokens provider")

tokens, err := tokensProvider.Tokens(tms.ID())
assert.NoError(err, "failed getting tokens")

err = tokens.SetSpendableFlag(s.Spendable, &s.TokenID)
assert.NoError(err, "failed setting spendable flag")

return nil, nil
}

type SetSpendableFlagViewFactory struct{}

func (p *SetSpendableFlagViewFactory) NewView(in []byte) (view.View, error) {
f := &SetSpendableFlagView{SetSpendableFlag: &SetSpendableFlag{}}
err := json.Unmarshal(in, f.SetSpendableFlag)
assert.NoError(err, "failed unmarshalling input")

return f, nil
}
4 changes: 4 additions & 0 deletions token/core/fabtoken/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ func (s *TokensService) DeserializeToken(outputRaw []byte, tokenInfoRaw []byte)

return tok, tokInfo.Issuer, nil
}

func (s *TokensService) IsSpendable(output []byte, outputMetadata []byte) error {
return nil
}
58 changes: 58 additions & 0 deletions token/core/zkatdlog/crypto/math/math.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package math

import (
"reflect"

math "github.com/IBM/mathlib"
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
)

type Element interface {
IsInfinity() bool
CurveID() math.CurveID
}

func CheckElements[E Element](elements []E, curveID math.CurveID, length int) error {
if len(elements) != length {
return errors.Errorf("length of elements does not match length of curveID")
}
for _, g1 := range elements {
if err := CheckElement[E](g1, curveID); err != nil {
return err
}
}
return nil
}

func CheckElement[E Element](element E, curveID math.CurveID) (err error) {
defer func() {
if e := recover(); e != nil {
err = errors.Errorf("caught panic while checking element, err [%s]", e)
}
}()

if isNilInterface(element) {
return errors.Errorf("elememt is nil")
}
if element.CurveID() != curveID {
return errors.Errorf("element curve must equal curve ID")
}
if element.IsInfinity() {
return errors.New("element is infinity")
}
return nil
}

func isNilInterface(i interface{}) bool {
if i == nil {
return true
}
rv := reflect.ValueOf(i)
return rv.Kind() == reflect.Ptr && rv.IsNil()
}
22 changes: 22 additions & 0 deletions token/core/zkatdlog/crypto/math/math_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package math

import (
"testing"

math "github.com/IBM/mathlib"
"github.com/stretchr/testify/assert"
)

func TestCheckElement(t *testing.T) {
var g1 *math.G1
assert.Error(t, CheckElement(g1, 0))

g1 = &math.G1{}
assert.Error(t, CheckElement(g1, 0))
}
40 changes: 15 additions & 25 deletions token/core/zkatdlog/crypto/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"strconv"

mathlib "github.com/IBM/mathlib"
math2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/math"
"github.com/hyperledger-labs/fabric-token-sdk/token/driver"
"github.com/pkg/errors"
)
Expand All @@ -32,7 +33,7 @@ type RangeProofParams struct {
NumberOfRounds int
}

func (rpp *RangeProofParams) Validate() error {
func (rpp *RangeProofParams) Validate(curveID mathlib.CurveID) error {
if rpp.BitLength == 0 {
return errors.New("invalid range proof parameters: bit length is zero")
}
Expand All @@ -45,23 +46,17 @@ func (rpp *RangeProofParams) Validate() error {
if len(rpp.LeftGenerators) != len(rpp.RightGenerators) {
return errors.Errorf("invalid range proof parameters: the size of the left generators does not match the size of the right generators [%d vs, %d]", len(rpp.LeftGenerators), len(rpp.RightGenerators))
}
if len(rpp.LeftGenerators) != rpp.BitLength {
return errors.Errorf("invalid range proof parameters: the size of the generators does not match the provided bit length [%d vs %d]", len(rpp.LeftGenerators), rpp.BitLength)
if err := math2.CheckElement(rpp.Q, curveID); err != nil {
return errors.Wrapf(err, "invalid range proof parameters: generator Q is invalid")
}
if rpp.Q == nil {
return errors.New("invalid range proof parameters: generator Q is nil")
if err := math2.CheckElement(rpp.P, curveID); err != nil {
return errors.Wrapf(err, "invalid range proof parameters: generator P is invalid")
}
if rpp.P == nil {
return errors.New("invalid range proof parameters: generator P is nil")
if err := math2.CheckElements(rpp.LeftGenerators, curveID, rpp.BitLength); err != nil {
return errors.Wrap(err, "invalid range proof parameters, left generators is invalid")
}

for i := 0; i < len(rpp.LeftGenerators); i++ {
if rpp.LeftGenerators[i] == nil {
return errors.Errorf("invalid range proof parameters: left generator at index %d is nil", i)
}
if rpp.RightGenerators[i] == nil {
return errors.Errorf("invalid range proof parameters: right generator at index %d is nil", i)
}
if err := math2.CheckElements(rpp.RightGenerators, curveID, rpp.BitLength); err != nil {
return errors.Wrap(err, "invalid range proof parameters, right generators is invalid")
}

return nil
Expand Down Expand Up @@ -268,18 +263,13 @@ func (pp *PublicParams) Validate() error {
if int(pp.IdemixCurveID) > len(mathlib.Curves)-1 {
return errors.Errorf("invalid public parameters: invalid idemix curveID [%d > %d]", int(pp.Curve), len(mathlib.Curves)-1)
}
if len(pp.PedersenGenerators) != 3 {
return errors.Errorf("invalid public parameters: length mismatch in Pedersen parameters [%d vs. 3]", len(pp.PedersenGenerators))
}
for i := 0; i < len(pp.PedersenGenerators); i++ {
if pp.PedersenGenerators[i] == nil {
return errors.Errorf("invalid public parameters: nil Pedersen parameter at index %d", i)
}
if err := math2.CheckElements(pp.PedersenGenerators, pp.Curve, 3); err != nil {
return errors.Wrapf(err, "invalid pedersen generators")
}
if pp.RangeProofParams == nil {
return errors.New("invalid public parameters: nil range proof parameters")
}
err := pp.RangeProofParams.Validate()
err := pp.RangeProofParams.Validate(pp.Curve)
if err != nil {
return errors.Wrap(err, "invalid public parameters")
}
Expand All @@ -293,8 +283,8 @@ func (pp *PublicParams) Validate() error {
if maxToken != pp.MaxToken {
return errors.Errorf("invalid maxt token, [%d]!=[%d]", maxToken, pp.MaxToken)
}
//if len(pp.Issuers) == 0 {
// if len(pp.Issuers) == 0 {
// return errors.New("invalid public parameters: empty list of issuers")
//}
// }
return nil
}
Loading
Loading