From 6e9c0b572d5ff5112e7955c439ba292b3ff87733 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Sat, 16 Nov 2024 08:01:50 +0100 Subject: [PATCH] move htlc and pledge validator to common Signed-off-by: Angelo De Caro --- token/core/common/validator_htlc.go | 87 ++++++++++++ .../validator => common}/validator_pledge.go | 29 ++-- token/core/fabtoken/actions.go | 4 + token/core/fabtoken/validator.go | 6 +- token/core/fabtoken/validator_pledge.go | 129 ------------------ token/core/fabtoken/validator_transfer.go | 84 ------------ .../zkatdlog/crypto/validator/validator.go | 6 +- .../crypto/validator/validator_transfer.go | 70 ---------- token/driver/action.go | 2 + token/services/identity/deserializer/typed.go | 15 +- token/token/token.go | 20 ++- 11 files changed, 140 insertions(+), 312 deletions(-) create mode 100644 token/core/common/validator_htlc.go rename token/core/{zkatdlog/crypto/validator => common}/validator_pledge.go (80%) delete mode 100644 token/core/fabtoken/validator_pledge.go diff --git a/token/core/common/validator_htlc.go b/token/core/common/validator_htlc.go new file mode 100644 index 000000000..5626964fb --- /dev/null +++ b/token/core/common/validator_htlc.go @@ -0,0 +1,87 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package common + +import ( + "encoding/json" + "time" + + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity" + "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/htlc" + "github.com/pkg/errors" +) + +// TransferHTLCValidate checks the validity of the HTLC scripts, if any +func TransferHTLCValidate[P driver.PublicParameters, T driver.Output, TA driver.TransferAction, IA driver.IssueAction](ctx *Context[P, T, TA, IA]) error { + now := time.Now() + + for i, in := range ctx.InputTokens { + owner, err := identity.UnmarshalTypedIdentity(in.GetOwner()) + if err != nil { + return errors.Wrap(err, "failed to unmarshal owner of input token") + } + // is it owned by an htlc script? + if owner.Type == htlc.ScriptType { + // Then, the first output must be compatible with this input. + if len(ctx.TransferAction.GetOutputs()) != 1 { + return errors.New("invalid transfer action: an htlc script only transfers the ownership of a token") + } + + // check it is not a redeem + output := ctx.TransferAction.GetOutputs()[0] + if output.IsRedeem() { + return errors.New("invalid transfer action: the output corresponding to an htlc spending should not be a redeem") + } + + // check that owner field in output is correct + script, op, err := htlc.VerifyOwner(ctx.InputTokens[0].GetOwner(), output.GetOwner(), now) + if err != nil { + return errors.Wrap(err, "failed to verify transfer from htlc script") + } + + // check metadata + sigma := ctx.Signatures[i] + metadataKey, err := htlc.MetadataClaimKeyCheck(ctx.TransferAction, script, op, sigma) + if err != nil { + return errors.WithMessagef(err, "failed to check htlc metadata") + } + if op != htlc.Reclaim { + ctx.CountMetadataKey(metadataKey) + } + } + } + + for _, out := range ctx.TransferAction.GetOutputs() { + if out.IsRedeem() { + continue + } + + // if it is an htlc script then the deadline must still be valid + owner, err := identity.UnmarshalTypedIdentity(out.GetOwner()) + if err != nil { + return err + } + if owner.Type == htlc.ScriptType { + script := &htlc.Script{} + err = json.Unmarshal(owner.Identity, script) + if err != nil { + return err + } + if err := script.Validate(now); err != nil { + return errors.WithMessagef(err, "htlc script invalid") + } + metadataKey, err := htlc.MetadataLockKeyCheck(ctx.TransferAction, script) + if err != nil { + return errors.WithMessagef(err, "failed to check htlc metadata") + } + ctx.CountMetadataKey(metadataKey) + continue + } + } + return nil +} diff --git a/token/core/zkatdlog/crypto/validator/validator_pledge.go b/token/core/common/validator_pledge.go similarity index 80% rename from token/core/zkatdlog/crypto/validator/validator_pledge.go rename to token/core/common/validator_pledge.go index 39738b959..4ec6143fa 100644 --- a/token/core/zkatdlog/crypto/validator/validator_pledge.go +++ b/token/core/common/validator_pledge.go @@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package validator +package common import ( "bytes" @@ -12,23 +12,22 @@ import ( "fmt" "time" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/token" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/transfer" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/pledge" "github.com/pkg/errors" ) -func IssuePledgeValidate(ctx *Context) error { - for k := range ctx.IssueAction.Metadata { +func IssuePledgeValidate[P driver.PublicParameters, T driver.Output, TA driver.TransferAction, IA driver.IssueAction](ctx *Context[P, T, TA, IA]) error { + for k := range ctx.IssueAction.GetMetadata() { ctx.CountMetadataKey(k) } return nil } -func TransferPledgeValidate(ctx *Context) error { +func TransferPledgeValidate[P driver.PublicParameters, T driver.Output, TA driver.TransferAction, IA driver.IssueAction](ctx *Context[P, T, TA, IA]) error { for _, in := range ctx.InputTokens { - id, err := identity.UnmarshalTypedIdentity(in.Owner) + id, err := identity.UnmarshalTypedIdentity(in.GetOwner()) if err != nil { return errors.Wrap(err, "failed to unmarshal owner of input token") } @@ -36,8 +35,8 @@ func TransferPledgeValidate(ctx *Context) error { if len(ctx.InputTokens) != 1 || len(ctx.TransferAction.GetOutputs()) != 1 { return errors.Errorf("invalid transfer action: a pledge script only transfers the ownership of a token") } - out := ctx.TransferAction.GetOutputs()[0].(*token.Token) - sender, err := identity.UnmarshalTypedIdentity(ctx.InputTokens[0].Owner) + out := ctx.TransferAction.GetOutputs()[0] + sender, err := identity.UnmarshalTypedIdentity(ctx.InputTokens[0].GetOwner()) if err != nil { return err } @@ -67,7 +66,7 @@ func TransferPledgeValidate(ctx *Context) error { ctx.CountMetadataKey(redeemKey) continue } - if !script.Sender.Equal(out.Owner) { + if !script.Sender.Equal(out.GetOwner()) { return errors.New("recipient of token does not correspond to sender of reclaim request") } @@ -83,15 +82,11 @@ func TransferPledgeValidate(ctx *Context) error { } } - for _, o := range ctx.TransferAction.GetOutputs() { - out, ok := o.(*token.Token) - if !ok { - return errors.Errorf("invalid output") - } + for _, out := range ctx.TransferAction.GetOutputs() { if out.IsRedeem() { continue } - owner, err := identity.UnmarshalTypedIdentity(out.Owner) + owner, err := identity.UnmarshalTypedIdentity(out.GetOwner()) if err != nil { return err } @@ -117,7 +112,7 @@ func TransferPledgeValidate(ctx *Context) error { return nil } -func constructMetadataKey(action *transfer.Action) (string, error) { +func constructMetadataKey(action driver.TransferAction) (string, error) { inputs, err := action.GetInputs() if err != nil { return "", errors.Wrap(err, "failed to retrieve input IDs from action") diff --git a/token/core/fabtoken/actions.go b/token/core/fabtoken/actions.go index 08fb98e43..81926abf7 100644 --- a/token/core/fabtoken/actions.go +++ b/token/core/fabtoken/actions.go @@ -46,6 +46,10 @@ func (t *Output) IsRedeem() bool { return len(t.Output.Owner.Raw) == 0 } +func (t *Output) GetOwner() []byte { + return t.Output.Owner.Raw +} + // IssueAction encodes a fabtoken Issue type IssueAction struct { // issuer's public key diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index 4e39b412d..cc4e60210 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -49,14 +49,14 @@ func NewValidator(logger logging.Logger, pp *PublicParams, deserializer driver.D transferValidators := []ValidateTransferFunc{ TransferSignatureValidate, TransferBalanceValidate, - TransferHTLCValidate, - TransferPledgeValidate, + common.TransferHTLCValidate[*PublicParams, *token.Token, *TransferAction, *IssueAction], + common.TransferPledgeValidate[*PublicParams, *token.Token, *TransferAction, *IssueAction], } transferValidators = append(transferValidators, extraValidators...) issueValidators := []ValidateIssueFunc{ IssueValidate, - IssuePledgeValidate, + common.IssuePledgeValidate[*PublicParams, *token.Token, *TransferAction, *IssueAction], } return common.NewValidator[*PublicParams, *token.Token, *TransferAction, *IssueAction]( diff --git a/token/core/fabtoken/validator_pledge.go b/token/core/fabtoken/validator_pledge.go deleted file mode 100644 index 73a333083..000000000 --- a/token/core/fabtoken/validator_pledge.go +++ /dev/null @@ -1,129 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package fabtoken - -import ( - "bytes" - "encoding/json" - "fmt" - "time" - - "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/pledge" - "github.com/pkg/errors" -) - -func IssuePledgeValidate(ctx *Context) error { - for k := range ctx.IssueAction.Metadata { - ctx.CountMetadataKey(k) - } - return nil -} - -func TransferPledgeValidate(ctx *Context) error { - ctx.Logger.Debug("pledge validation starts") - for _, in := range ctx.InputTokens { - id, err := identity.UnmarshalTypedIdentity(in.Owner.Raw) - if err != nil { - return errors.Wrap(err, "failed to unmarshal owner of input token") - } - if id.Type == pledge.ScriptType { - if len(ctx.InputTokens) != 1 || len(ctx.TransferAction.GetOutputs()) != 1 { - return errors.Errorf("invalid transfer action: a pledge script only transfers the ownership of a token") - } - out := ctx.TransferAction.GetOutputs()[0].(*Output) - tok := out.Output - sender, err := identity.UnmarshalTypedIdentity(ctx.InputTokens[0].Owner.Raw) - if err != nil { - return err - } - script := &pledge.Script{} - err = json.Unmarshal(sender.Identity, script) - if err != nil { - return errors.Wrap(err, "failed to unmarshal pledge script") - } - if time.Now().Before(script.Deadline) { - return errors.New("cannot reclaim pledge yet: wait for timeout to elapse.") - } - - key, err := constructMetadataKey(ctx.TransferAction) - if err != nil { - return errors.Wrap(err, "failed constructing metadata key") - } - - if out.IsRedeem() { - redeemKey := pledge.RedeemPledgeKey + key - v, ok := ctx.TransferAction.GetMetadata()[redeemKey] - if !ok { - return errors.Errorf("empty metadata of redeem for pledge script with identifier %s", redeemKey) - } - if v == nil { - return errors.Errorf("invalid metadatata of redeem for pledge script with identifier %s, metadata should contain a proof", redeemKey) - } - ctx.CountMetadataKey(redeemKey) - continue - } - if !script.Sender.Equal(tok.Owner.Raw) { - return errors.Errorf("recipient of token does not correspond to the sender of reclaim request") - } - - reclaimKey := pledge.MetadataReclaimKey + key - v, ok := ctx.TransferAction.GetMetadata()[reclaimKey] - if !ok { - return errors.Errorf("empty metadata of reclaim with identifier %s", reclaimKey) - } - if v == nil { - return errors.Errorf("invalid metadatata of reclaim with identifier %s, metadata should contain a proof", reclaimKey) - } - ctx.CountMetadataKey(reclaimKey) - } - } - - for _, o := range ctx.TransferAction.GetOutputs() { - out, ok := o.(*Output) - if !ok { - return errors.Errorf("invalid output") - } - if out.IsRedeem() { - continue - } - owner, err := identity.UnmarshalTypedIdentity(out.Output.Owner.Raw) - if err != nil { - return err - } - if owner.Type == pledge.ScriptType { - script := &pledge.Script{} - err = json.Unmarshal(owner.Identity, script) - if err != nil { - return err - } - if script.Deadline.Before(time.Now()) { - return errors.Errorf("pledge script is invalid: expiration date has already passed") - } - v, ok := ctx.TransferAction.GetMetadata()[pledge.MetadataKey+script.ID] - if !ok { - return errors.Errorf("empty metadata for pledge script with identifier %s", script.ID) - } - if !bytes.Equal(v, []byte("1")) { - return errors.Errorf("invalid metadatata for pledge script with identifier %s", script.ID) - } - ctx.CountMetadataKey(pledge.MetadataKey + script.ID) - } - } - return nil -} - -func constructMetadataKey(action *TransferAction) (string, error) { - inputs, err := action.GetInputs() - if err != nil { - return "", errors.Wrap(err, "failed to retrieve input IDs from action") - } - if len(inputs) != 1 { - return "", errors.New("invalid transfer action, does not carry a single input") - } - return fmt.Sprintf(".%d.%s", inputs[0].Index, inputs[0].TxId), nil -} diff --git a/token/core/fabtoken/validator_transfer.go b/token/core/fabtoken/validator_transfer.go index d333a9786..4e11f3a48 100644 --- a/token/core/fabtoken/validator_transfer.go +++ b/token/core/fabtoken/validator_transfer.go @@ -8,11 +8,8 @@ package fabtoken import ( "encoding/json" - "time" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" - "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/pkg/errors" ) @@ -89,87 +86,6 @@ func TransferBalanceValidate(ctx *Context) error { return nil } -// TransferHTLCValidate checks the validity of the HTLC scripts, if any -func TransferHTLCValidate(ctx *Context) error { - now := time.Now() - - for i, in := range ctx.InputTokens { - owner, err := identity.UnmarshalTypedIdentity(in.Owner.Raw) - if err != nil { - return errors.Wrap(err, "failed to unmarshal owner of input token") - } - // is it owned by an htlc script? - if owner.Type == htlc.ScriptType { - // Then, the first output must be compatible with this input. - if len(ctx.TransferAction.GetOutputs()) != 1 { - return errors.New("invalid transfer action: an htlc script only transfers the ownership of a token") - } - - // check type and quantity - output := ctx.TransferAction.GetOutputs()[0].(*Output) - tok := output.Output - if ctx.InputTokens[0].Type != tok.Type { - return errors.New("invalid transfer action: type of input does not match type of output") - } - if ctx.InputTokens[0].Quantity != tok.Quantity { - return errors.New("invalid transfer action: quantity of input does not match quantity of output") - } - if output.IsRedeem() { - return errors.New("invalid transfer action: the output corresponding to an htlc spending should not be a redeem") - } - - // check owner field - script, op, err := htlc.VerifyOwner(ctx.InputTokens[0].Owner.Raw, tok.Owner.Raw, now) - if err != nil { - return errors.Wrap(err, "failed to verify transfer from htlc script") - } - - // check metadata - sigma := ctx.Signatures[i] - metadataKey, err := htlc.MetadataClaimKeyCheck(ctx.TransferAction, script, op, sigma) - if err != nil { - return errors.WithMessagef(err, "failed to check htlc metadata") - } - if op != htlc.Reclaim { - ctx.CountMetadataKey(metadataKey) - } - } - } - - for _, o := range ctx.TransferAction.GetOutputs() { - out, ok := o.(*Output) - if !ok { - return errors.New("invalid output") - } - if out.IsRedeem() { - continue - } - - // if it is an htlc script then the deadline must still be valid - owner, err := identity.UnmarshalTypedIdentity(out.Output.Owner.Raw) - if err != nil { - return err - } - if owner.Type == htlc.ScriptType { - script := &htlc.Script{} - err = json.Unmarshal(owner.Identity, script) - if err != nil { - return err - } - if err := script.Validate(now); err != nil { - return errors.WithMessagef(err, "htlc script invalid") - } - metadataKey, err := htlc.MetadataLockKeyCheck(ctx.TransferAction, script) - if err != nil { - return errors.WithMessagef(err, "failed to check htlc metadata") - } - ctx.CountMetadataKey(metadataKey) - continue - } - } - return nil -} - // RetrieveInputsFromTransferAction retrieves from the passed ledger the inputs identified in TransferAction func RetrieveInputsFromTransferAction(t *TransferAction, ledger driver.Ledger) ([]*token.Token, error) { var inputTokens []*token.Token diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index 4027c28e1..c9b38e616 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -52,14 +52,14 @@ func New(logger logging.Logger, pp *crypto.PublicParams, deserializer driver.Des transferValidators := []ValidateTransferFunc{ TransferSignatureValidate, TransferZKProofValidate, - TransferHTLCValidate, - TransferPledgeValidate, + common.TransferHTLCValidate[*crypto.PublicParams, *token.Token, *transfer.Action, *issue.IssueAction], + common.TransferPledgeValidate[*crypto.PublicParams, *token.Token, *transfer.Action, *issue.IssueAction], } transferValidators = append(transferValidators, extraValidators...) issueValidators := []ValidateIssueFunc{ IssueValidate, - IssuePledgeValidate, + common.IssuePledgeValidate[*crypto.PublicParams, *token.Token, *transfer.Action, *issue.IssueAction], } return common.NewValidator[*crypto.PublicParams, *token.Token, *transfer.Action, *issue.IssueAction]( diff --git a/token/core/zkatdlog/crypto/validator/validator_transfer.go b/token/core/zkatdlog/crypto/validator/validator_transfer.go index a69a0ed71..be52afe5a 100644 --- a/token/core/zkatdlog/crypto/validator/validator_transfer.go +++ b/token/core/zkatdlog/crypto/validator/validator_transfer.go @@ -7,15 +7,10 @@ SPDX-License-Identifier: Apache-2.0 package validator import ( - "encoding/json" - "time" - math "github.com/IBM/mathlib" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/token" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/transfer" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" - "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/htlc" "github.com/pkg/errors" ) @@ -76,68 +71,3 @@ func TransferZKProofValidate(ctx *Context) error { return nil } - -func TransferHTLCValidate(ctx *Context) error { - now := time.Now() - - for i, in := range ctx.InputTokens { - owner, err := identity.UnmarshalTypedIdentity(in.Owner) - if err != nil { - return errors.Wrap(err, "failed to unmarshal owner of input token") - } - if owner.Type == htlc.ScriptType { - if len(ctx.InputTokens) != 1 || len(ctx.TransferAction.GetOutputs()) != 1 { - return errors.Errorf("invalid transfer action: an htlc script only transfers the ownership of a token") - } - - out := ctx.TransferAction.GetOutputs()[0].(*token.Token) - - // check that owner field in output is correct - script, op, err := htlc.VerifyOwner(ctx.InputTokens[0].Owner, out.Owner, now) - if err != nil { - return errors.Wrap(err, "failed to verify transfer from htlc script") - } - - // check metadata - sigma := ctx.Signatures[i] - metadataKey, err := htlc.MetadataClaimKeyCheck(ctx.TransferAction, script, op, sigma) - if err != nil { - return errors.WithMessagef(err, "failed to check htlc metadata") - } - if op != htlc.Reclaim { - ctx.CountMetadataKey(metadataKey) - } - } - } - - for _, o := range ctx.TransferAction.GetOutputs() { - out, ok := o.(*token.Token) - if !ok { - return errors.Errorf("invalid output") - } - if out.IsRedeem() { - continue - } - owner, err := identity.UnmarshalTypedIdentity(out.Owner) - if err != nil { - return err - } - if owner.Type == htlc.ScriptType { - script := &htlc.Script{} - err = json.Unmarshal(owner.Identity, script) - if err != nil { - return err - } - if err := script.Validate(now); err != nil { - return errors.WithMessagef(err, "htlc script invalid") - } - metadataKey, err := htlc.MetadataLockKeyCheck(ctx.TransferAction, script) - if err != nil { - return errors.WithMessagef(err, "failed to check htlc metadata") - } - ctx.CountMetadataKey(metadataKey) - continue - } - } - return nil -} diff --git a/token/driver/action.go b/token/driver/action.go index 45d41b672..e05c8ae8d 100644 --- a/token/driver/action.go +++ b/token/driver/action.go @@ -39,6 +39,8 @@ type Output interface { Serialize() ([]byte, error) // IsRedeem returns true if the output is a redeem output IsRedeem() bool + // GetOwner returns the owner of this token + GetOwner() []byte } //go:generate counterfeiter -o mock/ta.go -fake-name TransferAction . TransferAction diff --git a/token/services/identity/deserializer/typed.go b/token/services/identity/deserializer/typed.go index 889d79eac..0eed2acc5 100644 --- a/token/services/identity/deserializer/typed.go +++ b/token/services/identity/deserializer/typed.go @@ -9,7 +9,6 @@ package deserializer import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-token-sdk/token" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/services/logging" @@ -19,6 +18,12 @@ import ( var logger = logging.MustGetLogger("token-sdk.services.identity.deserializer") +// VerifierDeserializer is the interface for verifiers' deserializer. +// A verifier checks the validity of a signature against the identity associated with the verifier +type VerifierDeserializer interface { + DeserializeVerifier(id driver.Identity) (driver.Verifier, error) +} + type AuditInfoProvider = driver.AuditInfoProvider type TypedVerifierDeserializer interface { @@ -86,9 +91,9 @@ func (v *TypedVerifierDeserializerMultiplex) MatchOwnerIdentity(id driver.Identi if err != nil { return errors.Wrapf(err, "failed to unmarshal identity [%s]", id) } - //if recipient.Type != v.identityType { + // if recipient.Type != v.identityType { // return errors.Errorf("expected serialized identity type, got [%s]", recipient.Type) - //} + // } matcher, err := v.GetOwnerMatcher(ai) if err != nil { @@ -118,10 +123,10 @@ func (v *TypedVerifierDeserializerMultiplex) GetOwnerAuditInfo(id driver.Identit } type TypedIdentityVerifierDeserializer struct { - common.VerifierDeserializer + VerifierDeserializer } -func NewTypedIdentityVerifierDeserializer(verifierDeserializer common.VerifierDeserializer) *TypedIdentityVerifierDeserializer { +func NewTypedIdentityVerifierDeserializer(verifierDeserializer VerifierDeserializer) *TypedIdentityVerifierDeserializer { return &TypedIdentityVerifierDeserializer{VerifierDeserializer: verifierDeserializer} } diff --git a/token/token/token.go b/token/token/token.go index 94cff6a8b..678fffccd 100644 --- a/token/token/token.go +++ b/token/token/token.go @@ -6,7 +6,10 @@ SPDX-License-Identifier: Apache-2.0 package token -import "fmt" +import ( + "encoding/json" + "fmt" +) // ID identifies a token as a function of the identifier of the transaction (issue, transfer) // that created it and its index in that transaction @@ -42,6 +45,21 @@ type Token struct { Quantity string `protobuf:"bytes,3,opt,name=quantity,proto3" json:"quantity,omitempty"` } +func (t *Token) Serialize() ([]byte, error) { + return json.Marshal(t) +} + +func (t *Token) IsRedeem() bool { + return t.Owner == nil || len(t.Owner.Raw) == 0 +} + +func (t *Token) GetOwner() []byte { + if t.Owner != nil { + return t.Owner.Raw + } + return nil +} + type IssuedToken struct { // Id is used to uniquely identify the token in the ledger Id *ID `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`