From 7bb520f5eb7a03e960fc75c6df79563962ba38a5 Mon Sep 17 00:00:00 2001 From: Hagar Meir Date: Mon, 15 Aug 2022 14:15:22 +0300 Subject: [PATCH 01/17] Remove signature metadata action Signed-off-by: Hagar Meir --- token/core/fabtoken/actions.go | 16 +---- token/core/fabtoken/validator.go | 71 ++++++++++++++++--- token/services/network/fabric/processor.go | 5 ++ token/services/vault/keys/keys.go | 1 + token/services/vault/translator/action.go | 5 +- token/services/vault/translator/translator.go | 18 ++--- 6 files changed, 77 insertions(+), 39 deletions(-) diff --git a/token/core/fabtoken/actions.go b/token/core/fabtoken/actions.go index 573509646..e4dc94937 100644 --- a/token/core/fabtoken/actions.go +++ b/token/core/fabtoken/actions.go @@ -15,17 +15,7 @@ import ( "github.com/pkg/errors" ) -// Signature contains metadata -type Signature struct { - metadata map[string][]byte // metadata may include for example the preimage of an htlc script -} - -// Metadata returns the contained metadata -func (s *Signature) Metadata() map[string][]byte { - return s.metadata -} - -// OutputMetadata contains a serialization of the issuer of the token. +// TokenInformation contains a serialization of the issuer of the token. // type, value and owner of token can be derived from the token itself. type OutputMetadata struct { Issuer []byte @@ -196,7 +186,7 @@ func (t *TransferAction) Deserialize(raw []byte) error { return json.Unmarshal(raw, t) } -// GetMetadata returns nil, indicating that fabtoken TransferAction carries no metadata +// GetMetadata returns the claim pre-image func (t *TransferAction) GetMetadata() []byte { - return nil + return t.ClaimPreImage } diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index 34440ff39..c15c242ac 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -150,24 +150,77 @@ func (v *Validator) VerifyTokenRequest(ledger driver.Ledger, signatureProvider d actions = append(actions, action) } for _, action := range ta { - actions = append(actions, action) + act, err := AddMetadataToTransferAction(action, ledger, signatureProvider) + if err != nil { + return nil, errors.Wrap(err, "failed adding metadata to transfer action") + } + actions = append(actions, act) } + + // actions are returned and will be used later to update the ledger + return actions, nil +} + +func AddMetadataToTransferAction(action *TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) (*TransferAction, error) { for _, sig := range signatureProvider.Signatures() { claim := &htlc.ClaimSignature{} - if err = json.Unmarshal(sig, claim); err != nil { + if err := json.Unmarshal(sig, claim); err != nil { continue } if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { return nil, errors.New("expected a valid claim preImage and recipient signature") } - actions = append(actions, &Signature{ - metadata: map[string][]byte{ - "claimPreimage": claim.Preimage, - }, - }) + if IsItAnExchangeClaimTransferAction(action, ledger) { + action.ClaimPreImage = claim.Preimage + } } - // actions are returned and will be used later to update the ledger - return actions, nil + return action, nil +} + +func IsItAnExchangeClaimTransferAction(action *TransferAction, ledger driver.Ledger) bool { + if action.NumOutputs() != 1 { + logger.Debugf("Number of outputs is %d", action.NumOutputs()) + return false + } + out, ok := action.GetOutputs()[0].(*TransferOutput) + if !ok { + logger.Debugf("invalid transfer action output") + return false + } + if out.IsRedeem() { + logger.Debugf("this is a redeem") + return false + } + inputs, err := RetrieveInputsFromTransferAction(action, ledger) + if err != nil { + logger.Debugf("error while retrieving inputs from transfer action: %v", err) + return false + } + if len(inputs) != 1 { + logger.Debugf("Number of inputs is %d", len(inputs)) + return false + } + inOwner, err := identity.UnmarshallRawOwner(inputs[0].Owner.Raw) + if err != nil { + logger.Debugf("error while unmarshalling input raw owner: %v", err) + return false + } + if inOwner.Type != exchange.ScriptTypeExchange { + logger.Debugf("script recipient does not match the output owner") + return false + } + script := &exchange.Script{} + err = json.Unmarshal(inOwner.Identity, script) + if err != nil { + logger.Debugf("error while unmarshalling into script: %v", err) + return false + } + if !script.Recipient.Equal(out.Output.Owner.Raw) { + logger.Debugf("script recipient does not match the output owner") + return false + } + logger.Debugf("this is an exchange claim") + return true } // VerifyTokenRequestFromRaw validates the raw token request diff --git a/token/services/network/fabric/processor.go b/token/services/network/fabric/processor.go index a236887ab..46adee735 100644 --- a/token/services/network/fabric/processor.go +++ b/token/services/network/fabric/processor.go @@ -198,6 +198,11 @@ func (r *RWSetProcessor) tokenRequest(req fabric.Request, tx fabric.ProcessTrans logger.Debugf("expected key without the issue action metadata, skipping") } continue + case keys.TransferActionMetadata: + if logger.IsEnabledFor(zapcore.DebugLevel) { + logger.Debugf("expected key without the issue action metadata, skipping") + } + continue case keys.SignaturePrefix: if logger.IsEnabledFor(zapcore.DebugLevel) { logger.Debugf("expected key without the sig metadata, skipping") diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index 299b3380e..16aaed262 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -3,6 +3,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ + package keys import ( diff --git a/token/services/vault/translator/action.go b/token/services/vault/translator/action.go index dad6445eb..58593d743 100644 --- a/token/services/vault/translator/action.go +++ b/token/services/vault/translator/action.go @@ -3,6 +3,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ + package translator type SetupAction interface { @@ -32,7 +33,3 @@ type TransferAction interface { IsGraphHiding() bool GetMetadata() []byte } - -type Signature interface { - Metadata() map[string][]byte -} diff --git a/token/services/vault/translator/translator.go b/token/services/vault/translator/translator.go index 4769ca338..644aa3e63 100644 --- a/token/services/vault/translator/translator.go +++ b/token/services/vault/translator/translator.go @@ -168,8 +168,6 @@ func (w *Translator) checkAction(tokenAction interface{}) error { return w.checkTransfer(action) case SetupAction: return nil - case Signature: - return nil default: return errors.Errorf("unknown token action: %T", action) } @@ -262,8 +260,6 @@ func (w *Translator) commitAction(tokenAction interface{}) (err error) { err = w.commitTransferAction(action) case SetupAction: err = w.commitSetupAction(action) - case Signature: - err = w.commitSignature(action) } return } @@ -366,23 +362,19 @@ func (w *Translator) commitTransferAction(transferAction TransferAction) error { if err := w.RWSet.SetState(w.namespace, key, metadata); err != nil { return err } - } - w.counter = w.counter + uint64(transferAction.NumOutputs()) - return nil -} -func (w *Translator) commitSignature(sig Signature) error { - for k, value := range sig.Metadata() { - key, err := keys.CreateSigMetadataKey(w.TxID, w.sigCounter, k) + // also keep the claim pre-image + key, err = keys.CreateSigMetadataKey(w.TxID, w.sigCounter, "claimPreimage") if err != nil { return errors.Errorf("error creating output ID: %s", err) } - err = w.RWSet.SetState(w.namespace, key, value) + err = w.RWSet.SetState(w.namespace, key, metadata) if err != nil { return errors.Wrapf(err, "error setting state for key [%s]", key) } + w.sigCounter++ } - w.sigCounter++ + w.counter = w.counter + uint64(transferAction.NumOutputs()) return nil } From f25dc203248f0b9ead23eafd03a111910d571704 Mon Sep 17 00:00:00 2001 From: Hagar Meir Date: Mon, 15 Aug 2022 16:06:36 +0300 Subject: [PATCH 02/17] also remove from dlog Signed-off-by: Hagar Meir --- token/core/zkatdlog/crypto/transfer/sender.go | 6 +- .../zkatdlog/crypto/validator/validator.go | 94 +++++++++++++++---- 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/token/core/zkatdlog/crypto/transfer/sender.go b/token/core/zkatdlog/crypto/transfer/sender.go index 65b34dacf..b6dd7d10a 100644 --- a/token/core/zkatdlog/crypto/transfer/sender.go +++ b/token/core/zkatdlog/crypto/transfer/sender.go @@ -110,6 +110,8 @@ type TransferAction struct { OutputTokens []*token.Token // ZK Proof that shows that the transfer is correct Proof []byte + // a transfer action may carry a ClaimPreImage + ClaimPreImage []byte } // NewTransfer returns the TransferAction that matches the passed arguments @@ -204,9 +206,9 @@ func (t *TransferAction) IsGraphHiding() bool { } // GetMetadata returns metadata of the TransferAction -// zkatdlog TransferAction does not carry any metadata +// in zkatdlog it returns the claim pre-image func (t *TransferAction) GetMetadata() []byte { - return nil + return t.ClaimPreImage } func getTokenData(tokens []*token.Token) []*math.G1 { diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index f79dd0162..e1d848455 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -140,14 +140,6 @@ func (v *Validator) VerifyTokenRequestFromRaw(getState driver.GetStateFnc, bindi return v.VerifyTokenRequest(backend, backend, binding, tr) } -type Signature struct { - metadata map[string][]byte // metadata may include for example the preimage of an htlc script -} - -func (s *Signature) Metadata() map[string][]byte { - return s.metadata -} - func (v *Validator) VerifyTokenRequest(ledger driver.Ledger, signatureProvider driver.SignatureProvider, binding string, tr *driver.TokenRequest) ([]interface{}, error) { if err := v.verifyAuditorSignature(signatureProvider); err != nil { return nil, errors.Wrapf(err, "failed to verifier auditor's signature [%s]", binding) @@ -174,25 +166,95 @@ func (v *Validator) VerifyTokenRequest(ledger driver.Ledger, signatureProvider d actions = append(actions, action) } for _, action := range ta { - actions = append(actions, action) + transferAction := action.(*transfer.TransferAction) + act, err := AddMetadataToTransferAction(transferAction, ledger, signatureProvider) + if err != nil { + return nil, errors.Wrap(err, "failed adding metadata to transfer action") + } + actions = append(actions, act) } + return actions, nil +} + +func AddMetadataToTransferAction(action *transfer.TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) (*transfer.TransferAction, error) { for _, sig := range signatureProvider.Signatures() { claim := &htlc.ClaimSignature{} - if err = json.Unmarshal(sig, claim); err != nil { + if err := json.Unmarshal(sig, claim); err != nil { continue } if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { return nil, errors.New("expected a valid claim preImage and recipient signature") } - actions = append(actions, &Signature{ - metadata: map[string][]byte{ - "claimPreimage": claim.Preimage, - }, - }) + b, err := IsItAnExchangeClaimTransferAction(action, ledger) + if err != nil { + return nil, err + } + if b { + action.ClaimPreImage = claim.Preimage + } } + return action, nil +} - return actions, nil +func IsItAnExchangeClaimTransferAction(action *transfer.TransferAction, ledger driver.Ledger) (bool, error) { + if action.NumOutputs() != 1 { + logger.Debugf("Number of outputs is %d", action.NumOutputs()) + return false, nil + } + out, ok := action.GetOutputs()[0].(*token.Token) + if !ok { + return false, errors.New("invalid transfer action output") + } + if out.IsRedeem() { + logger.Debugf("this is a redeem") + return false, nil + } + + var inputTokens []*token.Token + inputs, err := action.GetInputs() + if err != nil { + return false, errors.Wrapf(err, "failed to retrieve inputs to spend") + } + for _, in := range inputs { + bytes, err := ledger.GetState(in) + if err != nil { + return false, errors.Wrapf(err, "failed to retrieve input to spend [%s]", in) + } + if len(bytes) == 0 { + return false, errors.Errorf("input to spend [%s] does not exists", in) + } + tok := &token.Token{} + err = tok.Deserialize(bytes) + if err != nil { + return false, errors.Wrapf(err, "failed to deserialize input to spend [%s]", in) + } + inputTokens = append(inputTokens, tok) + } + + if len(inputTokens) != 1 { + logger.Debugf("Number of inputs is %d", len(inputs)) + return false, nil + } + inOwner, err := identity.UnmarshallRawOwner(inputTokens[0].Owner) + if err != nil { + return false, errors.Wrap(err, "failed to unmarshall input raw owner") + } + if inOwner.Type != exchange.ScriptTypeExchange { + logger.Debugf("script recipient does not match the output owner") + return false, nil + } + script := &exchange.Script{} + err = json.Unmarshal(inOwner.Identity, script) + if err != nil { + return false, errors.Wrap(err, "failed to unmarshall into script") + } + if !script.Recipient.Equal(out.Owner) { + logger.Debugf("script recipient does not match the output owner") + return false, nil + } + logger.Debugf("this is an exchange claim") + return true, nil } func (v *Validator) unmarshalTransferActions(raw [][]byte) ([]driver.TransferAction, error) { From 285105c33d551ffb98ef5d445cc35ae46e6e0d4f Mon Sep 17 00:00:00 2001 From: Hagar Meir Date: Mon, 15 Aug 2022 17:16:54 +0300 Subject: [PATCH 03/17] change name of key Signed-off-by: Hagar Meir --- token/services/interop/htlc/scanner.go | 2 +- token/services/network/fabric/processor.go | 4 +-- token/services/network/orion/processor.go | 4 +-- token/services/vault/keys/keys.go | 10 +++---- token/services/vault/translator/translator.go | 30 +++++++++---------- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/token/services/interop/htlc/scanner.go b/token/services/interop/htlc/scanner.go index 0c99466bf..4a6d3f8be 100644 --- a/token/services/interop/htlc/scanner.go +++ b/token/services/interop/htlc/scanner.go @@ -66,7 +66,7 @@ func ScanForPreImage(ctx view.Context, image []byte, hashFunc crypto.Hash, hashE if err != nil { return false, err } - if f, err := w.IsSigMetadataKey(k); err == nil && f { + if f, err := w.IsClaimPreImageKey(k); err == nil && f { // hash + encoding hash := hashFunc.New() if _, err = hash.Write(v); err != nil { diff --git a/token/services/network/fabric/processor.go b/token/services/network/fabric/processor.go index 46adee735..6f51c0ec3 100644 --- a/token/services/network/fabric/processor.go +++ b/token/services/network/fabric/processor.go @@ -203,9 +203,9 @@ func (r *RWSetProcessor) tokenRequest(req fabric.Request, tx fabric.ProcessTrans logger.Debugf("expected key without the issue action metadata, skipping") } continue - case keys.SignaturePrefix: + case keys.ClaimPreImage: if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("expected key without the sig metadata, skipping") + logger.Debugf("expected key without the claim pre-image, skipping") } continue } diff --git a/token/services/network/orion/processor.go b/token/services/network/orion/processor.go index a0fbc6b7c..d087431a7 100644 --- a/token/services/network/orion/processor.go +++ b/token/services/network/orion/processor.go @@ -165,9 +165,9 @@ func (r *RWSetProcessor) tokenRequest(req orion.Request, tx orion.ProcessTransac logger.Debugf("expected key without the issue action metadata, skipping") } continue - case keys.SignaturePrefix: + case keys.ClaimPreImage: if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("expected key without the sig metadata, skipping") + logger.Debugf("expected key without the claim pre-image, skipping") } continue } diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index 16aaed262..77de66205 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -21,7 +21,6 @@ const ( MaxUnicodeRuneValue = utf8.MaxRune // U+10FFFF - maximum (and unallocated) code point CompositeKeyNamespace = "\x00" TokenKeyPrefix = "ztoken" - SignaturePrefix = "sig" FabTokenKeyPrefix = "token" FabTokenExtendedKeyPrefix = "etoken" AuditTokenKeyPrefix = "audittoken" @@ -42,6 +41,7 @@ const ( SerialNumber = "sn" IssueActionMetadata = "iam" TransferActionMetadata = "tam" + ClaimPreImage = "cpi" ) func GetTokenIdFromKey(key string) (*token2.ID, error) { @@ -107,10 +107,6 @@ func CreateTokenKey(txID string, index uint64) (string, error) { return CreateCompositeKey(TokenKeyPrefix, []string{txID, strconv.FormatUint(index, 10)}) } -func CreateSigMetadataKey(txID string, index uint64, subKey string) (string, error) { - return CreateCompositeKey(SignaturePrefix, []string{txID, strconv.FormatUint(index, 10), subKey}) -} - func CreateSNKey(sn string) (string, error) { return CreateCompositeKey(TokenKeyPrefix, []string{SerialNumber, sn}) } @@ -151,6 +147,10 @@ func CreateTransferActionMetadataKey(hash string) (string, error) { return CreateCompositeKey(TokenKeyPrefix, []string{TransferActionMetadata, hash}) } +func CreateClaimPreImageKey() (string, error) { + return CreateCompositeKey(TokenKeyPrefix, []string{ClaimPreImage, "claimPreImage"}) +} + // CreateCompositeKey and its related functions and consts copied from core/chaincode/shim/chaincode.go func CreateCompositeKey(objectType string, attributes []string) (string, error) { if err := ValidateCompositeKeyAttribute(objectType); err != nil { diff --git a/token/services/vault/translator/translator.go b/token/services/vault/translator/translator.go index 644aa3e63..4b78453f0 100644 --- a/token/services/vault/translator/translator.go +++ b/token/services/vault/translator/translator.go @@ -22,20 +22,18 @@ var logger = flogging.MustGetLogger("token-sdk.vault.translator") // Translator validates token requests and generates the corresponding RWSets type Translator struct { - RWSet RWSet - TxID string - counter uint64 - sigCounter uint64 - namespace string + RWSet RWSet + TxID string + counter uint64 + namespace string } func New(txID string, rwSet RWSet, namespace string) *Translator { w := &Translator{ - RWSet: rwSet, - TxID: txID, - counter: 0, - sigCounter: 0, - namespace: namespace, + RWSet: rwSet, + TxID: txID, + counter: 0, + namespace: namespace, } return w @@ -145,12 +143,15 @@ func (w *Translator) QueryTokens(ids []*token2.ID) ([][]byte, error) { return res, nil } -func (w *Translator) IsSigMetadataKey(k string) (bool, error) { - prefix, _, err := keys.SplitCompositeKey(k) +func (w *Translator) IsClaimPreImageKey(k string) (bool, error) { + prefix, components, err := keys.SplitCompositeKey(k) if err != nil { return false, errors.Wrapf(err, "failed to split composite key [%s]", k) } - return prefix == keys.SignaturePrefix, nil + if prefix != keys.TokenKeyPrefix { + return false, nil + } + return components[0] == keys.ClaimPreImage, nil } func (w *Translator) checkProcess(action interface{}) error { @@ -364,7 +365,7 @@ func (w *Translator) commitTransferAction(transferAction TransferAction) error { } // also keep the claim pre-image - key, err = keys.CreateSigMetadataKey(w.TxID, w.sigCounter, "claimPreimage") + key, err = keys.CreateClaimPreImageKey() if err != nil { return errors.Errorf("error creating output ID: %s", err) } @@ -372,7 +373,6 @@ func (w *Translator) commitTransferAction(transferAction TransferAction) error { if err != nil { return errors.Wrapf(err, "error setting state for key [%s]", key) } - w.sigCounter++ } w.counter = w.counter + uint64(transferAction.NumOutputs()) return nil From c657189fa2659d5599de1ee86fb5bf73c897b270 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Tue, 23 Aug 2022 13:34:05 +0200 Subject: [PATCH 04/17] reuse transfer metadata to set the cliam pre-image Signed-off-by: Angelo De Caro --- token/core/common/metadata.go | 28 +++++++++ token/core/fabtoken/actions.go | 6 +- token/core/fabtoken/sender.go | 9 ++- token/core/fabtoken/validator.go | 11 +++- token/core/zkatdlog/crypto/transfer/sender.go | 13 +++-- .../zkatdlog/crypto/validator/validator.go | 11 +++- token/core/zkatdlog/nogh/sender.go | 4 ++ token/driver/action.go | 4 +- token/request.go | 22 ++++--- token/services/interop/htlc/scanner.go | 2 +- token/services/interop/htlc/transaction.go | 10 +++- token/services/network/fabric/processor.go | 5 -- token/services/network/orion/processor.go | 4 +- token/services/vault/keys/keys.go | 21 +++++-- token/services/vault/translator/action.go | 2 +- .../vault/translator/mock/transfer_action.go | 20 +++---- token/services/vault/translator/translator.go | 58 ++++++++----------- 17 files changed, 148 insertions(+), 82 deletions(-) create mode 100644 token/core/common/metadata.go diff --git a/token/core/common/metadata.go b/token/core/common/metadata.go new file mode 100644 index 000000000..ba2d85267 --- /dev/null +++ b/token/core/common/metadata.go @@ -0,0 +1,28 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package common + +import ( + "strings" + + token2 "github.com/hyperledger-labs/fabric-token-sdk/token" +) + +// SetTransferActionMetadata extracts the transfer metadata from the passed attributes and +// sets them to the passed metadata +func SetTransferActionMetadata(attrs map[interface{}]interface{}, metadata map[string][]byte) { + for key, value := range attrs { + k, ok1 := key.(string) + v, ok2 := value.([]byte) + if ok1 && ok2 { + if strings.HasPrefix(k, token2.TransferMetadataPrefix) { + mKey := strings.TrimPrefix(k, token2.TransferMetadataPrefix) + metadata[mKey] = v + } + } + } +} diff --git a/token/core/fabtoken/actions.go b/token/core/fabtoken/actions.go index e4dc94937..9be1f29c0 100644 --- a/token/core/fabtoken/actions.go +++ b/token/core/fabtoken/actions.go @@ -117,6 +117,8 @@ type TransferAction struct { Inputs []string // outputs to be created as a result of the transfer Outputs []*Output + // Metadata contains the action's metadata + Metadata map[string][]byte } // Serialize marshals TransferAction @@ -187,6 +189,6 @@ func (t *TransferAction) Deserialize(raw []byte) error { } // GetMetadata returns the claim pre-image -func (t *TransferAction) GetMetadata() []byte { - return t.ClaimPreImage +func (t *TransferAction) GetMetadata() map[string][]byte { + return t.Metadata } diff --git a/token/core/fabtoken/sender.go b/token/core/fabtoken/sender.go index 97e3d8360..aa9991c2b 100644 --- a/token/core/fabtoken/sender.go +++ b/token/core/fabtoken/sender.go @@ -8,6 +8,7 @@ package fabtoken import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" @@ -46,10 +47,14 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token2 // assemble transfer action transfer := &TransferAction{ - Inputs: inputIDs, - Outputs: outs, + Inputs: inputIDs, + Outputs: outs, + Metadata: map[string][]byte{}, } + // add transfer action's metadata + common.SetTransferActionMetadata(opts.Attributes, transfer.Metadata) + // assemble transfer metadata var receivers []view.Identity for i, output := range outs { diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index c15c242ac..35f70bc9b 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -171,7 +171,16 @@ func AddMetadataToTransferAction(action *TransferAction, ledger driver.Ledger, s return nil, errors.New("expected a valid claim preImage and recipient signature") } if IsItAnExchangeClaimTransferAction(action, ledger) { - action.ClaimPreImage = claim.Preimage + if len(action.Metadata) == 0 { + return nil, errors.Errorf("cannot find htlc pre-image, no metadata") + } + value, ok := action.Metadata[exchange.ClaimPreImage] + if !ok { + return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") + } + if !bytes.Equal(value, claim.Preimage) { + return nil, errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) + } } } return action, nil diff --git a/token/core/zkatdlog/crypto/transfer/sender.go b/token/core/zkatdlog/crypto/transfer/sender.go index b6dd7d10a..ca807825c 100644 --- a/token/core/zkatdlog/crypto/transfer/sender.go +++ b/token/core/zkatdlog/crypto/transfer/sender.go @@ -3,6 +3,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ + package transfer import ( @@ -110,8 +111,8 @@ type TransferAction struct { OutputTokens []*token.Token // ZK Proof that shows that the transfer is correct Proof []byte - // a transfer action may carry a ClaimPreImage - ClaimPreImage []byte + // Metadata contains the action's metadata + Metadata map[string][]byte } // NewTransfer returns the TransferAction that matches the passed arguments @@ -130,7 +131,9 @@ func NewTransfer(inputs []string, inputCommitments []*math.G1, outputs []*math.G Inputs: inputs, InputCommitments: inputCommitments, OutputTokens: tokens, - Proof: proof}, nil + Proof: proof, + Metadata: map[string][]byte{}, + }, nil } // GetInputs returns the inputs in the TransferAction @@ -207,8 +210,8 @@ func (t *TransferAction) IsGraphHiding() bool { // GetMetadata returns metadata of the TransferAction // in zkatdlog it returns the claim pre-image -func (t *TransferAction) GetMetadata() []byte { - return t.ClaimPreImage +func (t *TransferAction) GetMetadata() map[string][]byte { + return t.Metadata } func getTokenData(tokens []*token.Token) []*math.G1 { diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index e1d848455..09264b529 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -191,7 +191,16 @@ func AddMetadataToTransferAction(action *transfer.TransferAction, ledger driver. return nil, err } if b { - action.ClaimPreImage = claim.Preimage + if len(action.Metadata) == 0 { + return nil, errors.Errorf("cannot find htlc pre-image, no metadata") + } + value, ok := action.Metadata[exchange.ClaimPreImage] + if !ok { + return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") + } + if !bytes.Equal(value, claim.Preimage) { + return nil, errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) + } } } return action, nil diff --git a/token/core/zkatdlog/nogh/sender.go b/token/core/zkatdlog/nogh/sender.go index d8cbc081f..2386e6b57 100644 --- a/token/core/zkatdlog/nogh/sender.go +++ b/token/core/zkatdlog/nogh/sender.go @@ -9,6 +9,7 @@ package nogh import ( math "github.com/IBM/mathlib" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/token" @@ -82,6 +83,9 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token3 return nil, nil, errors.Wrapf(err, "failed to generate zkatdlog transfer action for txid [%s]", txID) } + // add transfer action's metadata + common.SetTransferActionMetadata(opts.Attributes, transfer.Metadata) + // prepare metadata var outputMetadataRaw [][]byte for _, information := range outputMetadata { diff --git a/token/driver/action.go b/token/driver/action.go index 2c8f34b15..8bcdf3760 100644 --- a/token/driver/action.go +++ b/token/driver/action.go @@ -56,6 +56,6 @@ type TransferAction interface { // IsGraphHiding returns true if the action is graph hiding // TODO: Deprecated. This should be checked using the public parameters IsGraphHiding() bool - // GetMetadata returns the metadata of the action - GetMetadata() []byte + // GetMetadata returns the action's metadata + GetMetadata() map[string][]byte } diff --git a/token/request.go b/token/request.go index 67f00f9cd..340211d02 100644 --- a/token/request.go +++ b/token/request.go @@ -8,7 +8,6 @@ package token import ( "encoding/asn1" - "fmt" view2 "github.com/hyperledger-labs/fabric-smart-client/platform/view" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" @@ -17,6 +16,10 @@ import ( "github.com/pkg/errors" ) +const ( + TransferMetadataPrefix = "TransferMetadataPrefix" +) + // IssueOptions models the options that can be passed to the issue command type IssueOptions struct { // Attributes is a container of generic options that might be driver specific @@ -78,17 +81,9 @@ func WithTokenSelector(selector Selector) TransferOption { } } -// WithOutputMetadata sets outputs metadata -func WithOutputMetadata(metadata [][]byte) TransferOption { - return func(o *TransferOptions) error { - if o.Attributes == nil { - o.Attributes = make(map[interface{}]interface{}) - } - for i, bytes := range metadata { - o.Attributes[fmt.Sprintf("output.metadata.%d", i)] = bytes - } - return nil - } +// WithTransferMetadata sets transfer action metadata +func WithTransferMetadata(key string, value []byte) TransferOption { + return WithTransferAttribute(TransferMetadataPrefix+key, value) } // WithTokenIDs sets the tokens ids to transfer @@ -102,6 +97,9 @@ func WithTokenIDs(ids ...*token.ID) TransferOption { // WithTransferAttribute sets an attribute to be used to customize the transfer command func WithTransferAttribute(attr, value interface{}) TransferOption { return func(o *TransferOptions) error { + if o.Attributes == nil { + o.Attributes = make(map[interface{}]interface{}) + } o.Attributes[attr] = value return nil } diff --git a/token/services/interop/htlc/scanner.go b/token/services/interop/htlc/scanner.go index 4a6d3f8be..50f653f38 100644 --- a/token/services/interop/htlc/scanner.go +++ b/token/services/interop/htlc/scanner.go @@ -66,7 +66,7 @@ func ScanForPreImage(ctx view.Context, image []byte, hashFunc crypto.Hash, hashE if err != nil { return false, err } - if f, err := w.IsClaimPreImageKey(k); err == nil && f { + if f, err := w.IsTransferMetadataKeyWithSubKey(k, ClaimPreImage); err == nil && f { // hash + encoding hash := hashFunc.New() if _, err = hash.Write(v); err != nil { diff --git a/token/services/interop/htlc/transaction.go b/token/services/interop/htlc/transaction.go index 01853a872..2e0cbff85 100644 --- a/token/services/interop/htlc/transaction.go +++ b/token/services/interop/htlc/transaction.go @@ -26,6 +26,7 @@ import ( const ( ScriptType = "htlc" // htlc script defaultDeadlineOffset = time.Hour + ClaimPreImage = "cpi" ) // WithHash sets a hash attribute to be used to customize the transfer command @@ -273,7 +274,14 @@ func (t *Transaction) Claim(wallet *token.OwnerWallet, tok *token2.UnspentToken, return err } - return t.Transfer(wallet, tok.Type, []uint64{q.ToBigInt().Uint64()}, []view.Identity{script.Recipient}, token.WithTokenIDs(tok.Id)) + return t.Transfer( + wallet, + tok.Type, + []uint64{q.ToBigInt().Uint64()}, + []view.Identity{script.Recipient}, + token.WithTokenIDs(tok.Id), + token.WithTransferMetadata(ClaimPreImage, preImage), + ) } func (t *Transaction) recipientAsScript(sender, recipient view.Identity, deadline time.Duration, h []byte, hashFunc crypto.Hash, hashEncoding encoding.Encoding) (view.Identity, []byte, error) { diff --git a/token/services/network/fabric/processor.go b/token/services/network/fabric/processor.go index 6f51c0ec3..a63526a8a 100644 --- a/token/services/network/fabric/processor.go +++ b/token/services/network/fabric/processor.go @@ -203,11 +203,6 @@ func (r *RWSetProcessor) tokenRequest(req fabric.Request, tx fabric.ProcessTrans logger.Debugf("expected key without the issue action metadata, skipping") } continue - case keys.ClaimPreImage: - if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("expected key without the claim pre-image, skipping") - } - continue } index, err := strconv.ParseUint(components[1], 10, 64) diff --git a/token/services/network/orion/processor.go b/token/services/network/orion/processor.go index d087431a7..c3d09e7ce 100644 --- a/token/services/network/orion/processor.go +++ b/token/services/network/orion/processor.go @@ -165,9 +165,9 @@ func (r *RWSetProcessor) tokenRequest(req orion.Request, tx orion.ProcessTransac logger.Debugf("expected key without the issue action metadata, skipping") } continue - case keys.ClaimPreImage: + case keys.TransferActionMetadata: if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("expected key without the claim pre-image, skipping") + logger.Debugf("expected key without the issue action metadata, skipping") } continue } diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index 77de66205..6fa94a238 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -143,12 +143,25 @@ func CreateIssueActionMetadataKey(hash string) (string, error) { return CreateCompositeKey(TokenKeyPrefix, []string{IssueActionMetadata, hash}) } -func CreateTransferActionMetadataKey(hash string) (string, error) { - return CreateCompositeKey(TokenKeyPrefix, []string{TransferActionMetadata, hash}) +func CreateTransferActionMetadataKey(txID string, index uint64, subKey string) (string, error) { + return CreateCompositeKey(TokenKeyPrefix, []string{TransferActionMetadata, txID, strconv.FormatUint(index, 10), subKey}) } -func CreateClaimPreImageKey() (string, error) { - return CreateCompositeKey(TokenKeyPrefix, []string{ClaimPreImage, "claimPreImage"}) +func IsTransferMetadataKeyWithSubKey(k string, subKey string) (bool, error) { + prefix, components, err := SplitCompositeKey(k) + if err != nil { + return false, errors.Wrapf(err, "failed to split composite key [%s]", k) + } + if prefix != TokenKeyPrefix { + return false, nil + } + if components[0] != TransferActionMetadata { + return false, nil + } + if len(components) != 4 { + return false, nil + } + return components[3] == subKey, nil } // CreateCompositeKey and its related functions and consts copied from core/chaincode/shim/chaincode.go diff --git a/token/services/vault/translator/action.go b/token/services/vault/translator/action.go index 58593d743..0e332ee9a 100644 --- a/token/services/vault/translator/action.go +++ b/token/services/vault/translator/action.go @@ -31,5 +31,5 @@ type TransferAction interface { SerializeOutputAt(index int) ([]byte, error) GetInputs() ([]string, error) IsGraphHiding() bool - GetMetadata() []byte + GetMetadata() map[string][]byte } diff --git a/token/services/vault/translator/mock/transfer_action.go b/token/services/vault/translator/mock/transfer_action.go index 563485a38..eb632b450 100644 --- a/token/services/vault/translator/mock/transfer_action.go +++ b/token/services/vault/translator/mock/transfer_action.go @@ -20,15 +20,15 @@ type TransferAction struct { result1 []string result2 error } - GetMetadataStub func() []byte + GetMetadataStub func() map[string][]byte getMetadataMutex sync.RWMutex getMetadataArgsForCall []struct { } getMetadataReturns struct { - result1 []byte + result1 map[string][]byte } getMetadataReturnsOnCall map[int]struct { - result1 []byte + result1 map[string][]byte } GetSerializedOutputsStub func() ([][]byte, error) getSerializedOutputsMutex sync.RWMutex @@ -158,7 +158,7 @@ func (fake *TransferAction) GetInputsReturnsOnCall(i int, result1 []string, resu }{result1, result2} } -func (fake *TransferAction) GetMetadata() []byte { +func (fake *TransferAction) GetMetadata() map[string][]byte { fake.getMetadataMutex.Lock() ret, specificReturn := fake.getMetadataReturnsOnCall[len(fake.getMetadataArgsForCall)] fake.getMetadataArgsForCall = append(fake.getMetadataArgsForCall, struct { @@ -182,32 +182,32 @@ func (fake *TransferAction) GetMetadataCallCount() int { return len(fake.getMetadataArgsForCall) } -func (fake *TransferAction) GetMetadataCalls(stub func() []byte) { +func (fake *TransferAction) GetMetadataCalls(stub func() map[string][]byte) { fake.getMetadataMutex.Lock() defer fake.getMetadataMutex.Unlock() fake.GetMetadataStub = stub } -func (fake *TransferAction) GetMetadataReturns(result1 []byte) { +func (fake *TransferAction) GetMetadataReturns(result1 map[string][]byte) { fake.getMetadataMutex.Lock() defer fake.getMetadataMutex.Unlock() fake.GetMetadataStub = nil fake.getMetadataReturns = struct { - result1 []byte + result1 map[string][]byte }{result1} } -func (fake *TransferAction) GetMetadataReturnsOnCall(i int, result1 []byte) { +func (fake *TransferAction) GetMetadataReturnsOnCall(i int, result1 map[string][]byte) { fake.getMetadataMutex.Lock() defer fake.getMetadataMutex.Unlock() fake.GetMetadataStub = nil if fake.getMetadataReturnsOnCall == nil { fake.getMetadataReturnsOnCall = make(map[int]struct { - result1 []byte + result1 map[string][]byte }) } fake.getMetadataReturnsOnCall[i] = struct { - result1 []byte + result1 map[string][]byte }{result1} } diff --git a/token/services/vault/translator/translator.go b/token/services/vault/translator/translator.go index 4b78453f0..187d350f9 100644 --- a/token/services/vault/translator/translator.go +++ b/token/services/vault/translator/translator.go @@ -12,28 +12,29 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" - "github.com/pkg/errors" - "github.com/hyperledger-labs/fabric-token-sdk/token/services/vault/keys" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" + "github.com/pkg/errors" ) var logger = flogging.MustGetLogger("token-sdk.vault.translator") // Translator validates token requests and generates the corresponding RWSets type Translator struct { - RWSet RWSet - TxID string - counter uint64 - namespace string + RWSet RWSet + TxID string + counter uint64 + metadataCounter uint64 + namespace string } func New(txID string, rwSet RWSet, namespace string) *Translator { w := &Translator{ - RWSet: rwSet, - TxID: txID, - counter: 0, - namespace: namespace, + RWSet: rwSet, + TxID: txID, + counter: 0, + metadataCounter: 0, + namespace: namespace, } return w @@ -143,15 +144,8 @@ func (w *Translator) QueryTokens(ids []*token2.ID) ([][]byte, error) { return res, nil } -func (w *Translator) IsClaimPreImageKey(k string) (bool, error) { - prefix, components, err := keys.SplitCompositeKey(k) - if err != nil { - return false, errors.Wrapf(err, "failed to split composite key [%s]", k) - } - if prefix != keys.TokenKeyPrefix { - return false, nil - } - return components[0] == keys.ClaimPreImage, nil +func (w *Translator) IsTransferMetadataKeyWithSubKey(k string, subKey string) (bool, error) { + return keys.IsTransferMetadataKeyWithSubKey(k, subKey) } func (w *Translator) checkProcess(action interface{}) error { @@ -322,6 +316,8 @@ func (w *Translator) commitIssueAction(issueAction IssueAction) error { // Check the owner of each output to determine how to generate the key func (w *Translator) commitTransferAction(transferAction TransferAction) error { base := w.counter + + // store outputs for i := 0; i < transferAction.NumOutputs(); i++ { if !transferAction.IsRedeemAt(i) { outputID, err := keys.CreateTokenKey(w.TxID, base+uint64(i)) @@ -339,6 +335,8 @@ func (w *Translator) commitTransferAction(transferAction TransferAction) error { } } } + + // store inputs ids, err := transferAction.GetInputs() if err != nil { return err @@ -347,33 +345,27 @@ func (w *Translator) commitTransferAction(transferAction TransferAction) error { if err != nil { return err } + + // store metadata metadata := transferAction.GetMetadata() - if len(metadata) != 0 { - key, err := keys.CreateTransferActionMetadataKey(hash.Hashable(metadata).String()) + for key, value := range metadata { + k, err := keys.CreateTransferActionMetadataKey(w.TxID, w.metadataCounter, key) if err != nil { return errors.Wrapf(err, "failed constructing metadata key") } - raw, err := w.RWSet.GetState(w.namespace, key) + raw, err := w.RWSet.GetState(w.namespace, k) if err != nil { return err } if len(raw) != 0 { return errors.Errorf("entry with transfer metadata key [%s] is already occupied by [%s]", key, string(raw)) } - if err := w.RWSet.SetState(w.namespace, key, metadata); err != nil { + if err := w.RWSet.SetState(w.namespace, k, value); err != nil { return err } - - // also keep the claim pre-image - key, err = keys.CreateClaimPreImageKey() - if err != nil { - return errors.Errorf("error creating output ID: %s", err) - } - err = w.RWSet.SetState(w.namespace, key, metadata) - if err != nil { - return errors.Wrapf(err, "error setting state for key [%s]", key) - } + w.metadataCounter++ } + w.counter = w.counter + uint64(transferAction.NumOutputs()) return nil } From dd51b2a85229fc977a63b122917321ae9a324136 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Tue, 23 Aug 2022 15:02:45 +0200 Subject: [PATCH 05/17] fix package imports Signed-off-by: Angelo De Caro --- token/core/fabtoken/validator.go | 8 ++++---- token/core/zkatdlog/crypto/validator/validator.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index 35f70bc9b..601257da9 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -174,7 +174,7 @@ func AddMetadataToTransferAction(action *TransferAction, ledger driver.Ledger, s if len(action.Metadata) == 0 { return nil, errors.Errorf("cannot find htlc pre-image, no metadata") } - value, ok := action.Metadata[exchange.ClaimPreImage] + value, ok := action.Metadata[htlc.ClaimPreImage] if !ok { return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") } @@ -191,7 +191,7 @@ func IsItAnExchangeClaimTransferAction(action *TransferAction, ledger driver.Led logger.Debugf("Number of outputs is %d", action.NumOutputs()) return false } - out, ok := action.GetOutputs()[0].(*TransferOutput) + out, ok := action.GetOutputs()[0].(*Output) if !ok { logger.Debugf("invalid transfer action output") return false @@ -214,11 +214,11 @@ func IsItAnExchangeClaimTransferAction(action *TransferAction, ledger driver.Led logger.Debugf("error while unmarshalling input raw owner: %v", err) return false } - if inOwner.Type != exchange.ScriptTypeExchange { + if inOwner.Type != htlc.ScriptType { logger.Debugf("script recipient does not match the output owner") return false } - script := &exchange.Script{} + script := &htlc.Script{} err = json.Unmarshal(inOwner.Identity, script) if err != nil { logger.Debugf("error while unmarshalling into script: %v", err) diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index 09264b529..5a0c1e544 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -194,7 +194,7 @@ func AddMetadataToTransferAction(action *transfer.TransferAction, ledger driver. if len(action.Metadata) == 0 { return nil, errors.Errorf("cannot find htlc pre-image, no metadata") } - value, ok := action.Metadata[exchange.ClaimPreImage] + value, ok := action.Metadata[htlc.ClaimPreImage] if !ok { return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") } @@ -249,11 +249,11 @@ func IsItAnExchangeClaimTransferAction(action *transfer.TransferAction, ledger d if err != nil { return false, errors.Wrap(err, "failed to unmarshall input raw owner") } - if inOwner.Type != exchange.ScriptTypeExchange { + if inOwner.Type != htlc.ScriptType { logger.Debugf("script recipient does not match the output owner") return false, nil } - script := &exchange.Script{} + script := &htlc.Script{} err = json.Unmarshal(inOwner.Identity, script) if err != nil { return false, errors.Wrap(err, "failed to unmarshall into script") From 68c0f0b30a8cd478d82eed06f61a2125e5a8b707 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Tue, 23 Aug 2022 17:06:58 +0200 Subject: [PATCH 06/17] validation restuctoring (phase 1) Signed-off-by: Angelo De Caro --- token/core/fabtoken/actions.go | 43 +- token/core/fabtoken/deserializer.go | 6 +- token/core/fabtoken/sender.go | 8 +- token/core/fabtoken/validator.go | 403 ++++++------------ token/core/fabtoken/validator_transfer.go | 159 +++++++ token/core/interop/{ => htlc}/deserializer.go | 2 +- token/core/interop/{ => htlc}/info.go | 2 +- token/core/interop/{ => htlc}/validator.go | 6 +- token/core/zkatdlog/crypto/audit/auditor.go | 6 +- token/core/zkatdlog/crypto/transfer/sender.go | 1 - .../zkatdlog/crypto/validator/validator.go | 275 ++++++------ token/core/zkatdlog/nogh/deserializer.go | 7 +- token/core/zkatdlog/nogh/sender.go | 8 +- token/driver/action.go | 1 - token/driver/validator.go | 4 +- token/services/vault/keys/keys.go | 11 +- token/services/vault/translator/action.go | 9 + token/services/vault/translator/translator.go | 4 +- 18 files changed, 504 insertions(+), 451 deletions(-) create mode 100644 token/core/fabtoken/validator_transfer.go rename token/core/interop/{ => htlc}/deserializer.go (99%) rename token/core/interop/{ => htlc}/info.go (99%) rename token/core/interop/{ => htlc}/validator.go (82%) diff --git a/token/core/fabtoken/actions.go b/token/core/fabtoken/actions.go index 9be1f29c0..82a040e8f 100644 --- a/token/core/fabtoken/actions.go +++ b/token/core/fabtoken/actions.go @@ -15,7 +15,7 @@ import ( "github.com/pkg/errors" ) -// TokenInformation contains a serialization of the issuer of the token. +// OutputMetadata contains a serialization of the issuer of the token. // type, value and owner of token can be derived from the token itself. type OutputMetadata struct { Issuer []byte @@ -188,7 +188,46 @@ func (t *TransferAction) Deserialize(raw []byte) error { return json.Unmarshal(raw, t) } -// GetMetadata returns the claim pre-image +// GetMetadata returns the action's metadata func (t *TransferAction) GetMetadata() map[string][]byte { return t.Metadata } + +// UnmarshalIssueTransferActions returns the deserialized issue and transfer actions contained in the passed TokenRequest +func UnmarshalIssueTransferActions(tr *driver.TokenRequest, binding string) ([]*IssueAction, []*TransferAction, error) { + ia, err := UnmarshalIssueActions(tr.Issues) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to retrieve issue actions [%s]", binding) + } + ta, err := UnmarshalTransferActions(tr.Transfers) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to retrieve transfer actions [%s]", binding) + } + return ia, ta, nil +} + +// UnmarshalTransferActions returns an array of deserialized TransferAction from raw bytes +func UnmarshalTransferActions(raw [][]byte) ([]*TransferAction, error) { + res := make([]*TransferAction, len(raw)) + for i := 0; i < len(raw); i++ { + ta := &TransferAction{} + if err := ta.Deserialize(raw[i]); err != nil { + return nil, err + } + res[i] = ta + } + return res, nil +} + +// UnmarshalIssueActions returns an array of deserialized IssueAction from raw bytes +func UnmarshalIssueActions(raw [][]byte) ([]*IssueAction, error) { + res := make([]*IssueAction, len(raw)) + for i := 0; i < len(raw); i++ { + ia := &IssueAction{} + if err := ia.Deserialize(raw[i]); err != nil { + return nil, err + } + res[i] = ia + } + return res, nil +} diff --git a/token/core/fabtoken/deserializer.go b/token/core/fabtoken/deserializer.go index fb3c9a862..ebb3040ba 100644 --- a/token/core/fabtoken/deserializer.go +++ b/token/core/fabtoken/deserializer.go @@ -12,7 +12,7 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity/msp/x509" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" ) @@ -41,7 +41,7 @@ func NewDeserializer() *deserializer { // GetOwnerVerifier deserializes the verifier for the passed owner identity func (d *deserializer) GetOwnerVerifier(id view.Identity) (driver.Verifier, error) { - return interop.NewDeserializer(d.ownerDeserializer).GetOwnerVerifier(id) + return htlc.NewDeserializer(d.ownerDeserializer).GetOwnerVerifier(id) } // GetIssuerVerifier deserializes the verifier for the passed issuer identity @@ -74,7 +74,7 @@ func (e *enrollmentService) GetEnrollmentID(auditInfo []byte) (string, error) { } // Try to unmarshal it as ScriptInfo - si := &interop.ScriptInfo{} + si := &htlc.ScriptInfo{} err := json.Unmarshal(auditInfo, si) if err == nil && (len(si.Sender) != 0 || len(si.Recipient) != 0) { if len(si.Recipient) != 0 { diff --git a/token/core/fabtoken/sender.go b/token/core/fabtoken/sender.go index aa9991c2b..4b0ef9c62 100644 --- a/token/core/fabtoken/sender.go +++ b/token/core/fabtoken/sender.go @@ -10,7 +10,7 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/pkg/errors" @@ -73,7 +73,7 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token2 receivers = append(receivers, output.Output.Owner.Raw) continue } - _, recipient, err := interop.GetScriptSenderAndRecipient(owner) + _, recipient, err := htlc.GetScriptSenderAndRecipient(owner) if err != nil { return nil, nil, errors.Wrap(err, "failed getting script sender and recipient") } @@ -82,7 +82,7 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token2 var senderAuditInfos [][]byte for _, t := range inputTokens { - auditInfo, err := interop.GetOwnerAuditInfo(t.Owner.Raw, s.SP) + auditInfo, err := htlc.GetOwnerAuditInfo(t.Owner.Raw, s.SP) if err != nil { return nil, nil, errors.Wrapf(err, "failed getting audit info for sender identity [%s]", view.Identity(t.Owner.Raw).String()) } @@ -91,7 +91,7 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token2 var receiverAuditInfos [][]byte for _, output := range outs { - auditInfo, err := interop.GetOwnerAuditInfo(output.Output.Owner.Raw, s.SP) + auditInfo, err := htlc.GetOwnerAuditInfo(output.Output.Owner.Raw, s.SP) if err != nil { return nil, nil, errors.Wrapf(err, "failed getting audit info for recipient identity [%s]", view.Identity(output.Output.Owner.Raw).String()) } diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index 601257da9..45e67308e 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -9,12 +9,10 @@ package fabtoken import ( "bytes" "encoding/json" - "time" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/htlc" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" @@ -22,96 +20,41 @@ import ( "go.uber.org/zap/zapcore" ) -var defaultValidators = []ValidateTransfer{SerializedIdentityTypeExtraValidator, ScriptTypeHTLCExtraValidator} - -type ValidateTransfer func(inputTokens []*token2.Token, tr driver.TransferAction) error - -func SerializedIdentityTypeExtraValidator(inputTokens []*token2.Token, tr driver.TransferAction) error { - // noting else to validate - return nil -} - -func ScriptTypeHTLCExtraValidator(inputTokens []*token2.Token, tr driver.TransferAction) error { - for _, in := range inputTokens { - owner, err := identity.UnmarshallRawOwner(in.Owner.Raw) - if err != nil { - return errors.Wrap(err, "failed to unmarshal owner of input token") - } - if owner.Type == htlc.ScriptType { - if len(inputTokens) != 1 || len(tr.GetOutputs()) != 1 { - return errors.Errorf("invalid transfer action: an htlc script only transfers the ownership of a token") - } - - out := tr.GetOutputs()[0].(*Output).Output - if inputTokens[0].Type != out.Type { - return errors.Errorf("invalid transfer action: type of input does not match type of output") - } - if inputTokens[0].Quantity != out.Quantity { - return errors.Errorf("invalid transfer action: quantity of input does not match quantity of output") - } - - // check that owner field in output is correct - if err := interop.VerifyTransferFromHTLCScript(inputTokens[0].Owner.Raw, out.Owner.Raw); err != nil { - return errors.Wrap(err, "failed to verify transfer from htlc script") - } - } - } - - for _, o := range tr.GetOutputs() { - out, ok := o.(*Output) - if !ok { - return errors.Errorf("invalid output") - } - if out.IsRedeem() { - continue - } - owner, err := identity.UnmarshallRawOwner(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 script.Deadline.Before(time.Now()) { - return errors.Errorf("htlc script invalid: expiration date has already passed") - } - continue - } - } - return nil -} - // Validator checks the validity of fabtoken TokenRequest type Validator struct { // fabtoken public parameters pp *PublicParams - // deserializer for identities used in fabtoken + // deserializer for identities deserializer driver.Deserializer - // extraValidators for performing additional validation - extraValidators []ValidateTransfer + // transferValidators for performing transfer action validation + transferValidators []ValidateTransferFunc } // NewValidator initializes a Validator with the passed parameters -func NewValidator(pp *PublicParams, deserializer driver.Deserializer, extraValidators ...ValidateTransfer) (*Validator, error) { +func NewValidator(pp *PublicParams, deserializer driver.Deserializer, extraValidators ...ValidateTransferFunc) (*Validator, error) { if pp == nil { return nil, errors.New("please provide a non-nil public parameters") } if deserializer == nil { return nil, errors.New("please provide a non-nil deserializer") } - defaultValidators = append(defaultValidators, extraValidators...) - return &Validator{ - pp: pp, - deserializer: deserializer, - extraValidators: defaultValidators, - }, nil + validators := []ValidateTransferFunc{ + (&TransferSignature{Deserializer: deserializer}).Validate, + (&TransferBalance{PP: pp}).Validate, + TransferHTLC, + } + validators = append(validators, extraValidators...) + v := &Validator{ + pp: pp, + deserializer: deserializer, + transferValidators: validators, + } + return v, nil } // VerifyTokenRequest validates the passed token request against data in the ledger, the signature provided and the binding func (v *Validator) VerifyTokenRequest(ledger driver.Ledger, signatureProvider driver.SignatureProvider, binding string, tr *driver.TokenRequest) ([]interface{}, error) { + // validate arguments if ledger == nil { return nil, errors.New("please provide a non-nil ledger") } @@ -150,88 +93,13 @@ func (v *Validator) VerifyTokenRequest(ledger driver.Ledger, signatureProvider d actions = append(actions, action) } for _, action := range ta { - act, err := AddMetadataToTransferAction(action, ledger, signatureProvider) - if err != nil { - return nil, errors.Wrap(err, "failed adding metadata to transfer action") - } - actions = append(actions, act) + actions = append(actions, action) } // actions are returned and will be used later to update the ledger return actions, nil } -func AddMetadataToTransferAction(action *TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) (*TransferAction, error) { - for _, sig := range signatureProvider.Signatures() { - claim := &htlc.ClaimSignature{} - if err := json.Unmarshal(sig, claim); err != nil { - continue - } - if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { - return nil, errors.New("expected a valid claim preImage and recipient signature") - } - if IsItAnExchangeClaimTransferAction(action, ledger) { - if len(action.Metadata) == 0 { - return nil, errors.Errorf("cannot find htlc pre-image, no metadata") - } - value, ok := action.Metadata[htlc.ClaimPreImage] - if !ok { - return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") - } - if !bytes.Equal(value, claim.Preimage) { - return nil, errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) - } - } - } - return action, nil -} - -func IsItAnExchangeClaimTransferAction(action *TransferAction, ledger driver.Ledger) bool { - if action.NumOutputs() != 1 { - logger.Debugf("Number of outputs is %d", action.NumOutputs()) - return false - } - out, ok := action.GetOutputs()[0].(*Output) - if !ok { - logger.Debugf("invalid transfer action output") - return false - } - if out.IsRedeem() { - logger.Debugf("this is a redeem") - return false - } - inputs, err := RetrieveInputsFromTransferAction(action, ledger) - if err != nil { - logger.Debugf("error while retrieving inputs from transfer action: %v", err) - return false - } - if len(inputs) != 1 { - logger.Debugf("Number of inputs is %d", len(inputs)) - return false - } - inOwner, err := identity.UnmarshallRawOwner(inputs[0].Owner.Raw) - if err != nil { - logger.Debugf("error while unmarshalling input raw owner: %v", err) - return false - } - if inOwner.Type != htlc.ScriptType { - logger.Debugf("script recipient does not match the output owner") - return false - } - script := &htlc.Script{} - err = json.Unmarshal(inOwner.Identity, script) - if err != nil { - logger.Debugf("error while unmarshalling into script: %v", err) - return false - } - if !script.Recipient.Equal(out.Output.Owner.Raw) { - logger.Debugf("script recipient does not match the output owner") - return false - } - logger.Debugf("this is an exchange claim") - return true -} - // VerifyTokenRequestFromRaw validates the raw token request func (v *Validator) VerifyTokenRequestFromRaw(getState driver.GetStateFnc, binding string, raw []byte) ([]interface{}, error) { if getState == nil { @@ -290,7 +158,8 @@ func (v *Validator) VerifyAuditorSignature(signatureProvider driver.SignaturePro return errors.New("failed to deserialize auditor's public key") } - return signatureProvider.HasBeenSignedBy(v.pp.AuditorIdentity(), verifier) + _, err = signatureProvider.HasBeenSignedBy(v.pp.AuditorIdentity(), verifier) + return err } return nil } @@ -300,7 +169,7 @@ func (v *Validator) VerifyAuditorSignature(signatureProvider driver.SignaturePro func (v *Validator) VerifyIssues(issues []*IssueAction, signatureProvider driver.SignatureProvider) error { for _, issue := range issues { // verify that issue is valid - if err := v.verifyIssue(issue); err != nil { + if err := v.VerifyIssue(issue); err != nil { return errors.Wrap(err, "failed to verify issue action") } @@ -325,13 +194,32 @@ func (v *Validator) VerifyIssues(issues []*IssueAction, signatureProvider driver return errors.Wrapf(err, "failed getting verifier for [%s]", issue.Issuer.String()) } // verify if the token request concatenated with the binding was signed by the issuer - if err := signatureProvider.HasBeenSignedBy(issue.Issuer, verifier); err != nil { + if _, err := signatureProvider.HasBeenSignedBy(issue.Issuer, verifier); err != nil { return errors.Wrapf(err, "failed verifying signature") } } return nil } +// VerifyIssue checks if all outputs in IssueAction are valid (no zero-value outputs) +func (v *Validator) VerifyIssue(issue driver.IssueAction) error { + if issue.NumOutputs() == 0 { + return errors.Errorf("there is no output") + } + for _, output := range issue.GetOutputs() { + out := output.(*Output).Output + q, err := token2.ToQuantity(out.Quantity, v.pp.QuantityPrecision) + if err != nil { + return errors.Wrapf(err, "failed parsing quantity [%s]", out.Quantity) + } + zero := token2.NewZeroQuantity(v.pp.QuantityPrecision) + if q.Cmp(zero) == 0 { + return errors.Errorf("quantity is zero") + } + } + return nil +} + // VerifyTransfers checks if the created output tokens are valid and if the content of the token request concatenated // with the binding was signed by the owners of the input tokens func (v *Validator) VerifyTransfers(ledger driver.Ledger, transferActions []*TransferAction, signatureProvider driver.SignatureProvider) error { @@ -343,36 +231,15 @@ func (v *Validator) VerifyTransfers(ledger driver.Ledger, transferActions []*Tra if err != nil { return errors.Wrapf(err, "failed to retrieve input from transfer action at index %d", i) } - // verify if the token request concatenated with the binding was signed by the owners of the inputs - // in the current transfer action - err = v.CheckSendersSignatures(inputTokens, i, signatureProvider) - if err != nil { - return err - } // verify if input tokens and output tokens in the current transfer action have the same type // verify if sum of input tokens in the current transfer action equals the sum of output tokens // in the current transfer action - if err := v.VerifyTransfer(inputTokens, t); err != nil { + if err := v.VerifyTransfer(inputTokens, t, signatureProvider); err != nil { return errors.Wrapf(err, "failed to verify transfer action at index %d", i) } - } - return nil -} - -// verifyIssue checks if all outputs in IssueAction are valid (no zero-value outputs) -func (v *Validator) verifyIssue(issue driver.IssueAction) error { - if issue.NumOutputs() == 0 { - return errors.Errorf("there is no output") - } - for _, output := range issue.GetOutputs() { - out := output.(*Output).Output - q, err := token2.ToQuantity(out.Quantity, v.pp.QuantityPrecision) - if err != nil { - return errors.Wrapf(err, "failed parsing quantity [%s]", out.Quantity) - } - zero := token2.NewZeroQuantity(v.pp.QuantityPrecision) - if q.Cmp(zero) == 0 { - return errors.Errorf("quantity is zero") + // check metadata, if required + if err := CheckTransferActionMetadata(t, ledger, signatureProvider); err != nil { + return errors.Wrap(err, "failed adding metadata to transfer action") } } return nil @@ -380,51 +247,9 @@ func (v *Validator) verifyIssue(issue driver.IssueAction) error { // VerifyTransfer checks that sum of inputTokens in TransferAction equals sum of outputs in TransferAction // It also checks that all outputs and inputs have the same type -func (v *Validator) VerifyTransfer(inputTokens []*token2.Token, tr driver.TransferAction) error { - if tr.NumOutputs() == 0 { - return errors.Errorf("there is no output") - } - if len(inputTokens) == 0 { - return errors.Errorf("there is no input") - } - if inputTokens[0] == nil { - return errors.Errorf("first input is nil") - } - typ := inputTokens[0].Type - inputSum := token2.NewZeroQuantity(v.pp.QuantityPrecision) - outputSum := token2.NewZeroQuantity(v.pp.QuantityPrecision) - for i, input := range inputTokens { - if input == nil { - return errors.Errorf("input %d is nil", i) - } - q, err := token2.ToQuantity(input.Quantity, v.pp.QuantityPrecision) - if err != nil { - return errors.Wrapf(err, "failed parsing quantity [%s]", input.Quantity) - } - inputSum.Add(q) - // check that all inputs have the same type - if input.Type != typ { - return errors.Errorf("input type %s does not match type %s", input.Type, typ) - } - } - for _, output := range tr.GetOutputs() { - out := output.(*Output).Output - q, err := token2.ToQuantity(out.Quantity, v.pp.QuantityPrecision) - if err != nil { - return errors.Wrapf(err, "failed parsing quantity [%s]", out.Quantity) - } - outputSum.Add(q) - // check that all outputs have the same type and it is the same type as inputs - if out.Type != typ { - return errors.Errorf("output type %s does not match type %s", out.Type, typ) - } - } - // check equality of sum of inputs and outputs - if inputSum.Cmp(outputSum) != 0 { - return errors.Errorf("input sum %v does not match output sum %v", inputSum, outputSum) - } - for _, v := range v.extraValidators { - if err := v(inputTokens, tr); err != nil { +func (v *Validator) VerifyTransfer(inputTokens []*token2.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { + for _, validator := range v.transferValidators { + if err := validator(inputTokens, tr, signatureProvider); err != nil { return err } } @@ -443,14 +268,14 @@ type backend struct { // HasBeenSignedBy checks if a given message has been signed by the signing identity matching // the passed verifier // todo shall we remove id from the parameters -func (b *backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) error { +func (b *backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) ([]byte, error) { if b.index >= len(b.signatures) { - return errors.Errorf("invalid state, insufficient number of signatures") + return nil, errors.Errorf("invalid state, insufficient number of signatures") } sigma := b.signatures[b.index] b.index++ - return verifier.Verify(b.message, sigma) + return sigma, verifier.Verify(b.message, sigma) } func (b *backend) GetState(key string) ([]byte, error) { @@ -461,61 +286,6 @@ func (b *backend) Signatures() [][]byte { return b.signatures } -// UnmarshalIssueTransferActions returns the deserialized issue and transfer actions contained in the passed TokenRequest -func UnmarshalIssueTransferActions(tr *driver.TokenRequest, binding string) ([]*IssueAction, []*TransferAction, error) { - ia, err := unmarshalIssueActions(tr.Issues) - if err != nil { - return nil, nil, errors.Wrapf(err, "failed to retrieve issue actions [%s]", binding) - } - ta, err := unmarshalTransferActions(tr.Transfers) - if err != nil { - return nil, nil, errors.Wrapf(err, "failed to retrieve transfer actions [%s]", binding) - } - return ia, ta, nil -} - -// unmarshalTransferActions returns an array of deserialized TransferAction from raw bytes -func unmarshalTransferActions(raw [][]byte) ([]*TransferAction, error) { - res := make([]*TransferAction, len(raw)) - for i := 0; i < len(raw); i++ { - ta := &TransferAction{} - if err := ta.Deserialize(raw[i]); err != nil { - return nil, err - } - res[i] = ta - } - return res, nil -} - -// unmarshalIssueActions returns an array of deserialized IssueAction from raw bytes -func unmarshalIssueActions(raw [][]byte) ([]*IssueAction, error) { - res := make([]*IssueAction, len(raw)) - for i := 0; i < len(raw); i++ { - ia := &IssueAction{} - if err := ia.Deserialize(raw[i]); err != nil { - return nil, err - } - res[i] = ia - } - return res, nil -} - -// CheckSendersSignatures verifies if a TokenRequest was signed by the owners of the inputs in the TokenRequest -func (v *Validator) CheckSendersSignatures(inputTokens []*token2.Token, actionIndex int, signatureProvider driver.SignatureProvider) error { - for _, tok := range inputTokens { - logger.Debugf("check sender [%d][%s]", actionIndex, view.Identity(tok.Owner.Raw).UniqueID()) - verifier, err := v.deserializer.GetOwnerVerifier(tok.Owner.Raw) - if err != nil { - return errors.Wrapf(err, "failed deserializing owner [%d][%v][%s]", actionIndex, tok, view.Identity(tok.Owner.Raw).UniqueID()) - } - logger.Debugf("signature verification [%d][%v][%s]", actionIndex, tok, view.Identity(tok.Owner.Raw).UniqueID()) - if err := signatureProvider.HasBeenSignedBy(tok.Owner.Raw, verifier); err != nil { - return errors.Wrapf(err, "failed signature verification [%d][%v][%s]", actionIndex, tok, view.Identity(tok.Owner.Raw).UniqueID()) - } - } - return nil -} - // RetrieveInputsFromTransferAction retrieves from the passed ledger the inputs identified in TransferAction func RetrieveInputsFromTransferAction(t *TransferAction, ledger driver.Ledger) ([]*token2.Token, error) { var inputTokens []*token2.Token @@ -540,3 +310,74 @@ func RetrieveInputsFromTransferAction(t *TransferAction, ledger driver.Ledger) ( } return inputTokens, nil } + +func CheckTransferActionMetadata(action *TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) error { + for _, sig := range signatureProvider.Signatures() { + claim := &htlc.ClaimSignature{} + if err := json.Unmarshal(sig, claim); err != nil { + continue + } + if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { + return errors.New("expected a valid claim preImage and recipient signature") + } + if IsItAnExchangeClaimTransferAction(action, ledger) { + if len(action.Metadata) == 0 { + return errors.Errorf("cannot find htlc pre-image, no metadata") + } + value, ok := action.Metadata[htlc.ClaimPreImage] + if !ok { + return errors.Errorf("cannot find htlc pre-image, missing metadata entry") + } + if !bytes.Equal(value, claim.Preimage) { + return errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) + } + } + } + return nil +} + +func IsItAnExchangeClaimTransferAction(action *TransferAction, ledger driver.Ledger) bool { + if action.NumOutputs() != 1 { + logger.Debugf("Number of outputs is %d", action.NumOutputs()) + return false + } + out, ok := action.GetOutputs()[0].(*Output) + if !ok { + logger.Debugf("invalid transfer action output") + return false + } + if out.IsRedeem() { + logger.Debugf("this is a redeem") + return false + } + inputs, err := RetrieveInputsFromTransferAction(action, ledger) + if err != nil { + logger.Debugf("error while retrieving inputs from transfer action: %v", err) + return false + } + if len(inputs) != 1 { + logger.Debugf("Number of inputs is %d", len(inputs)) + return false + } + inOwner, err := identity.UnmarshallRawOwner(inputs[0].Owner.Raw) + if err != nil { + logger.Debugf("error while unmarshalling input raw owner: %v", err) + return false + } + if inOwner.Type != htlc.ScriptType { + logger.Debugf("script recipient does not match the output owner") + return false + } + script := &htlc.Script{} + err = json.Unmarshal(inOwner.Identity, script) + if err != nil { + logger.Debugf("error while unmarshalling into script: %v", err) + return false + } + if !script.Recipient.Equal(out.Output.Owner.Raw) { + logger.Debugf("script recipient does not match the output owner") + return false + } + logger.Debugf("this is an exchange claim") + return true +} diff --git a/token/core/fabtoken/validator_transfer.go b/token/core/fabtoken/validator_transfer.go new file mode 100644 index 000000000..fcc3bdb19 --- /dev/null +++ b/token/core/fabtoken/validator_transfer.go @@ -0,0 +1,159 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package fabtoken + +import ( + "encoding/json" + "time" + + "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" + htlc2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/htlc" + "github.com/hyperledger-labs/fabric-token-sdk/token/token" + "github.com/pkg/errors" +) + +// ValidateTransferFunc is the prototype of a validation function for a transfer action +type ValidateTransferFunc func(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error + +// TransferSignature validates the signatures for the inputs spent by an action +type TransferSignature struct { + Deserializer driver.Deserializer +} + +// Validate validates the signatures for the inputs spent by an action +func (v *TransferSignature) Validate(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { + for _, tok := range inputTokens { + logger.Debugf("check sender [%s]", view.Identity(tok.Owner.Raw).UniqueID()) + verifier, err := v.Deserializer.GetOwnerVerifier(tok.Owner.Raw) + if err != nil { + return errors.Wrapf(err, "failed deserializing owner [%v][%s]", tok, view.Identity(tok.Owner.Raw).UniqueID()) + } + logger.Debugf("signature verification [%v][%s]", tok, view.Identity(tok.Owner.Raw).UniqueID()) + _, err = signatureProvider.HasBeenSignedBy(tok.Owner.Raw, verifier) + if err != nil { + return errors.Wrapf(err, "failed signature verification [%v][%s]", tok, view.Identity(tok.Owner.Raw).UniqueID()) + } + } + return nil +} + +// TransferBalance checks that the sum of the inputs is equal to the sum of the ouputs +type TransferBalance struct { + PP *PublicParams +} + +// Validate checks that the sum of the inputs is equal to the sum of the ouputs +func (v *TransferBalance) Validate(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { + if tr.NumOutputs() == 0 { + return errors.Errorf("there is no output") + } + if len(inputTokens) == 0 { + return errors.Errorf("there is no input") + } + if inputTokens[0] == nil { + return errors.Errorf("first input is nil") + } + typ := inputTokens[0].Type + inputSum := token.NewZeroQuantity(v.PP.QuantityPrecision) + outputSum := token.NewZeroQuantity(v.PP.QuantityPrecision) + for i, input := range inputTokens { + if input == nil { + return errors.Errorf("input %d is nil", i) + } + q, err := token.ToQuantity(input.Quantity, v.PP.QuantityPrecision) + if err != nil { + return errors.Wrapf(err, "failed parsing quantity [%s]", input.Quantity) + } + inputSum.Add(q) + // check that all inputs have the same type + if input.Type != typ { + return errors.Errorf("input type %s does not match type %s", input.Type, typ) + } + } + for _, output := range tr.GetOutputs() { + out := output.(*Output).Output + q, err := token.ToQuantity(out.Quantity, v.PP.QuantityPrecision) + if err != nil { + return errors.Wrapf(err, "failed parsing quantity [%s]", out.Quantity) + } + outputSum.Add(q) + // check that all outputs have the same type and it is the same type as inputs + if out.Type != typ { + return errors.Errorf("output type %s does not match type %s", out.Type, typ) + } + } + // check equality of sum of inputs and outputs + if inputSum.Cmp(outputSum) != 0 { + return errors.Errorf("input sum %v does not match output sum %v", inputSum, outputSum) + } + + return nil +} + +// TransferHTLC checks the validity of the TransferHTLC scripts, if any +func TransferHTLC(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { + now := time.Now() + + for _, in := range inputTokens { + owner, err := identity.UnmarshallRawOwner(in.Owner.Raw) + if err != nil { + return errors.Wrap(err, "failed to unmarshal owner of input token") + } + // is it owner by an htlc script? + if owner.Type == htlc.ScriptType { + // Then, the first output must be compatible with this input. + if len(tr.GetOutputs()) != 1 { + return errors.Errorf("invalid transfer action: an htlc script only transfers the ownership of a token") + } + + // check type and quantity + out := tr.GetOutputs()[0].(*Output).Output + if inputTokens[0].Type != out.Type { + return errors.Errorf("invalid transfer action: type of input does not match type of output") + } + if inputTokens[0].Quantity != out.Quantity { + return errors.Errorf("invalid transfer action: quantity of input does not match quantity of output") + } + + // check owner field + if err := htlc2.VerifyOwner(inputTokens[0].Owner.Raw, out.Owner.Raw); err != nil { + return errors.Wrap(err, "failed to verify transfer from htlc script") + } + } + } + + for _, o := range tr.GetOutputs() { + out, ok := o.(*Output) + if !ok { + return errors.Errorf("invalid output") + } + if out.IsRedeem() { + continue + } + + // if it is an htlc script than the deadline must be still valid + owner, err := identity.UnmarshallRawOwner(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 script.Deadline.Before(now) { + return errors.Errorf("htlc script invalid: expiration date has already passed") + } + continue + } + } + return nil +} diff --git a/token/core/interop/deserializer.go b/token/core/interop/htlc/deserializer.go similarity index 99% rename from token/core/interop/deserializer.go rename to token/core/interop/htlc/deserializer.go index a3c6df5c0..48139c108 100644 --- a/token/core/interop/deserializer.go +++ b/token/core/interop/htlc/deserializer.go @@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package interop +package htlc import ( "encoding/json" diff --git a/token/core/interop/info.go b/token/core/interop/htlc/info.go similarity index 99% rename from token/core/interop/info.go rename to token/core/interop/htlc/info.go index 7307bba1e..535453630 100644 --- a/token/core/interop/info.go +++ b/token/core/interop/htlc/info.go @@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package interop +package htlc import ( "encoding/json" diff --git a/token/core/interop/validator.go b/token/core/interop/htlc/validator.go similarity index 82% rename from token/core/interop/validator.go rename to token/core/interop/htlc/validator.go index fdcc605f1..8f6ad17cd 100644 --- a/token/core/interop/validator.go +++ b/token/core/interop/htlc/validator.go @@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package interop +package htlc import ( "encoding/json" @@ -15,8 +15,8 @@ import ( "github.com/pkg/errors" ) -// VerifyTransferFromHTLCScript validates the owners of the transfer in the htlc script -func VerifyTransferFromHTLCScript(senderRawOwner []byte, outRawOwner []byte) error { +// VerifyOwner validates the owners of the transfer in the htlc script +func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) error { sender, err := identity.UnmarshallRawOwner(senderRawOwner) if err != nil { return err diff --git a/token/core/zkatdlog/crypto/audit/auditor.go b/token/core/zkatdlog/crypto/audit/auditor.go index 98d876ed1..d3faa2de8 100644 --- a/token/core/zkatdlog/crypto/audit/auditor.go +++ b/token/core/zkatdlog/crypto/audit/auditor.go @@ -14,7 +14,7 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/common" issue2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/issue" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/token" @@ -278,11 +278,11 @@ func inspectTokenOwnerOfScript(des Deserializer, token *AuditableToken, index in if err != nil { return errors.Errorf("input owner at index [%d] cannot be unmarshalled", index) } - scriptInf := &interop.ScriptInfo{} + scriptInf := &htlc.ScriptInfo{} if err := json.Unmarshal(token.Owner.OwnerInfo, scriptInf); err != nil { return errors.Wrapf(err, "failed to unmarshal script info") } - scriptSender, scriptRecipient, err := interop.GetScriptSenderAndRecipient(owner) + scriptSender, scriptRecipient, err := htlc.GetScriptSenderAndRecipient(owner) if err != nil { return errors.Wrap(err, "failed getting script sender and recipient") } diff --git a/token/core/zkatdlog/crypto/transfer/sender.go b/token/core/zkatdlog/crypto/transfer/sender.go index ca807825c..8c1ca9890 100644 --- a/token/core/zkatdlog/crypto/transfer/sender.go +++ b/token/core/zkatdlog/crypto/transfer/sender.go @@ -209,7 +209,6 @@ func (t *TransferAction) IsGraphHiding() bool { } // GetMetadata returns metadata of the TransferAction -// in zkatdlog it returns the claim pre-image func (t *TransferAction) GetMetadata() map[string][]byte { return t.Metadata } diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index 5a0c1e544..0a0aa424c 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -16,7 +16,7 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" + htlc2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto" issue2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/issue" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/token" @@ -28,16 +28,29 @@ import ( var logger = flogging.MustGetLogger("token-sdk.zkatdlog") -var defaultValidators = []ValidateTransfer{SerializedIdentityTypeExtraValidator, ScriptTypeHTLCExtraValidator} +type ValidateTransferFunc func(tokens []*token.Token, tr *transfer.TransferAction) error -type ValidateTransfer func(tokens []*token.Token, tr *transfer.TransferAction) error +type ZKVerifier struct { + pp *crypto.PublicParams +} + +func (v *ZKVerifier) Validate(tokens []*token.Token, tr *transfer.TransferAction) error { + in := make([]*math.G1, len(tokens)) + for i := range tokens { + in[i] = tokens[i].GetCommitment() + } + + if err := transfer.NewVerifier( + in, + tr.GetOutputCommitments(), + v.pp).Verify(tr.GetProof()); err != nil { + return err + } -func SerializedIdentityTypeExtraValidator(tokens []*token.Token, tr *transfer.TransferAction) error { - // noting else to validate return nil } -func ScriptTypeHTLCExtraValidator(tokens []*token.Token, tr *transfer.TransferAction) error { +func HTLCValidator(tokens []*token.Token, tr *transfer.TransferAction) error { for _, in := range tokens { owner, err := identity.UnmarshallRawOwner(in.Owner) if err != nil { @@ -54,7 +67,7 @@ func ScriptTypeHTLCExtraValidator(tokens []*token.Token, tr *transfer.TransferAc } // check that owner field in output is correct - if err := interop.VerifyTransferFromHTLCScript(tokens[0].Owner, out.Owner); err != nil { + if err := htlc2.VerifyOwner(tokens[0].Owner, out.Owner); err != nil { return errors.Wrap(err, "failed to verify transfer from htlc script") } } @@ -88,17 +101,21 @@ func ScriptTypeHTLCExtraValidator(tokens []*token.Token, tr *transfer.TransferAc } type Validator struct { - pp *crypto.PublicParams - deserializer driver.Deserializer - extraValidators []ValidateTransfer + pp *crypto.PublicParams + deserializer driver.Deserializer + validators []ValidateTransferFunc } -func New(pp *crypto.PublicParams, deserializer driver.Deserializer, extraValidators ...ValidateTransfer) *Validator { - defaultValidators = append(defaultValidators, extraValidators...) +func New(pp *crypto.PublicParams, deserializer driver.Deserializer, extraValidators ...ValidateTransferFunc) *Validator { + validators := []ValidateTransferFunc{ + (&ZKVerifier{pp: pp}).Validate, + HTLCValidator, + } + validators = append(validators, extraValidators...) return &Validator{ - pp: pp, - deserializer: deserializer, - extraValidators: defaultValidators, + pp: pp, + deserializer: deserializer, + validators: validators, } } @@ -177,95 +194,6 @@ func (v *Validator) VerifyTokenRequest(ledger driver.Ledger, signatureProvider d return actions, nil } -func AddMetadataToTransferAction(action *transfer.TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) (*transfer.TransferAction, error) { - for _, sig := range signatureProvider.Signatures() { - claim := &htlc.ClaimSignature{} - if err := json.Unmarshal(sig, claim); err != nil { - continue - } - if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { - return nil, errors.New("expected a valid claim preImage and recipient signature") - } - b, err := IsItAnExchangeClaimTransferAction(action, ledger) - if err != nil { - return nil, err - } - if b { - if len(action.Metadata) == 0 { - return nil, errors.Errorf("cannot find htlc pre-image, no metadata") - } - value, ok := action.Metadata[htlc.ClaimPreImage] - if !ok { - return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") - } - if !bytes.Equal(value, claim.Preimage) { - return nil, errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) - } - } - } - return action, nil -} - -func IsItAnExchangeClaimTransferAction(action *transfer.TransferAction, ledger driver.Ledger) (bool, error) { - if action.NumOutputs() != 1 { - logger.Debugf("Number of outputs is %d", action.NumOutputs()) - return false, nil - } - out, ok := action.GetOutputs()[0].(*token.Token) - if !ok { - return false, errors.New("invalid transfer action output") - } - if out.IsRedeem() { - logger.Debugf("this is a redeem") - return false, nil - } - - var inputTokens []*token.Token - inputs, err := action.GetInputs() - if err != nil { - return false, errors.Wrapf(err, "failed to retrieve inputs to spend") - } - for _, in := range inputs { - bytes, err := ledger.GetState(in) - if err != nil { - return false, errors.Wrapf(err, "failed to retrieve input to spend [%s]", in) - } - if len(bytes) == 0 { - return false, errors.Errorf("input to spend [%s] does not exists", in) - } - tok := &token.Token{} - err = tok.Deserialize(bytes) - if err != nil { - return false, errors.Wrapf(err, "failed to deserialize input to spend [%s]", in) - } - inputTokens = append(inputTokens, tok) - } - - if len(inputTokens) != 1 { - logger.Debugf("Number of inputs is %d", len(inputs)) - return false, nil - } - inOwner, err := identity.UnmarshallRawOwner(inputTokens[0].Owner) - if err != nil { - return false, errors.Wrap(err, "failed to unmarshall input raw owner") - } - if inOwner.Type != htlc.ScriptType { - logger.Debugf("script recipient does not match the output owner") - return false, nil - } - script := &htlc.Script{} - err = json.Unmarshal(inOwner.Identity, script) - if err != nil { - return false, errors.Wrap(err, "failed to unmarshall into script") - } - if !script.Recipient.Equal(out.Owner) { - logger.Debugf("script recipient does not match the output owner") - return false, nil - } - logger.Debugf("this is an exchange claim") - return true, nil -} - func (v *Validator) unmarshalTransferActions(raw [][]byte) ([]driver.TransferAction, error) { res := make([]driver.TransferAction, len(raw)) for i := 0; i < len(raw); i++ { @@ -297,7 +225,8 @@ func (v *Validator) verifyAuditorSignature(signatureProvider driver.SignaturePro return errors.Errorf("failed to deserialize auditor's public key") } - return signatureProvider.HasBeenSignedBy(v.pp.Auditor, verifier) + _, err = signatureProvider.HasBeenSignedBy(v.pp.Auditor, verifier) + return err } return nil } @@ -329,13 +258,25 @@ func (v *Validator) verifyIssues(issues []driver.IssueAction, signatureProvider if err != nil { return errors.Wrapf(err, "failed getting verifier for [%s]", view.Identity(a.Issuer).String()) } - if err := signatureProvider.HasBeenSignedBy(a.Issuer, verifier); err != nil { + if _, err := signatureProvider.HasBeenSignedBy(a.Issuer, verifier); err != nil { return errors.Wrapf(err, "failed verifying signature") } } return nil } +func (v *Validator) verifyIssue(issue driver.IssueAction) error { + action := issue.(*issue2.IssueAction) + coms, err := action.GetCommitments() + if err != nil { + return errors.New("failed to verify issue") + } + return issue2.NewVerifier( + coms, + action.IsAnonymous(), + v.pp).Verify(action.GetProof()) +} + func (v *Validator) verifyTransfers(ledger driver.Ledger, transferActions []driver.TransferAction, signatureProvider driver.SignatureProvider) error { logger.Debugf("check sender start...") defer logger.Debugf("check sender finished.") @@ -366,7 +307,7 @@ func (v *Validator) verifyTransfers(ledger driver.Ledger, transferActions []driv return errors.Wrapf(err, "failed deserializing owner [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) } logger.Debugf("signature verification [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) - if err := signatureProvider.HasBeenSignedBy(tok.Owner, verifier); err != nil { + if _, err := signatureProvider.HasBeenSignedBy(tok.Owner, verifier); err != nil { return errors.Wrapf(err, "failed signature verification [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) } } @@ -377,43 +318,20 @@ func (v *Validator) verifyTransfers(ledger driver.Ledger, transferActions []driv return nil } -func (v *Validator) verifyIssue(issue driver.IssueAction) error { - action := issue.(*issue2.IssueAction) - coms, err := action.GetCommitments() - if err != nil { - return errors.New("failed to verify issue") - } - return issue2.NewVerifier( - coms, - action.IsAnonymous(), - v.pp).Verify(action.GetProof()) -} - func (v *Validator) verifyTransfer(inputTokens [][]byte, tr driver.TransferAction) error { action := tr.(*transfer.TransferAction) tokens := make([]*token.Token, len(inputTokens)) - in := make([]*math.G1, len(inputTokens)) for i, raw := range inputTokens { tokens[i] = &token.Token{} if err := tokens[i].Deserialize(raw); err != nil { return errors.Wrapf(err, "invalid transfer: failed to deserialize input [%d]", i) } - in[i] = tokens[i].GetCommitment() } - - if err := transfer.NewVerifier( - in, - action.GetOutputCommitments(), - v.pp).Verify(action.GetProof()); err != nil { - return err - } - - for _, v := range v.extraValidators { + for _, v := range v.validators { if err := v(tokens, action); err != nil { return err } } - return nil } @@ -424,14 +342,14 @@ type backend struct { signatures [][]byte } -func (b *backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) error { +func (b *backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) ([]byte, error) { if b.index >= len(b.signatures) { - return errors.Errorf("invalid state, insufficient number of signatures") + return nil, errors.Errorf("invalid state, insufficient number of signatures") } sigma := b.signatures[b.index] b.index++ - return verifier.Verify(b.message, sigma) + return sigma, verifier.Verify(b.message, sigma) } func (b *backend) GetState(key string) ([]byte, error) { @@ -441,3 +359,92 @@ func (b *backend) GetState(key string) ([]byte, error) { func (b *backend) Signatures() [][]byte { return b.signatures } + +func AddMetadataToTransferAction(action *transfer.TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) (*transfer.TransferAction, error) { + for _, sig := range signatureProvider.Signatures() { + claim := &htlc.ClaimSignature{} + if err := json.Unmarshal(sig, claim); err != nil { + continue + } + if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { + return nil, errors.New("expected a valid claim preImage and recipient signature") + } + b, err := IsItAnExchangeClaimTransferAction(action, ledger) + if err != nil { + return nil, err + } + if b { + if len(action.Metadata) == 0 { + return nil, errors.Errorf("cannot find htlc pre-image, no metadata") + } + value, ok := action.Metadata[htlc.ClaimPreImage] + if !ok { + return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") + } + if !bytes.Equal(value, claim.Preimage) { + return nil, errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) + } + } + } + return action, nil +} + +func IsItAnExchangeClaimTransferAction(action *transfer.TransferAction, ledger driver.Ledger) (bool, error) { + if action.NumOutputs() != 1 { + logger.Debugf("Number of outputs is %d", action.NumOutputs()) + return false, nil + } + out, ok := action.GetOutputs()[0].(*token.Token) + if !ok { + return false, errors.New("invalid transfer action output") + } + if out.IsRedeem() { + logger.Debugf("this is a redeem") + return false, nil + } + + var inputTokens []*token.Token + inputs, err := action.GetInputs() + if err != nil { + return false, errors.Wrapf(err, "failed to retrieve inputs to spend") + } + for _, in := range inputs { + bytes, err := ledger.GetState(in) + if err != nil { + return false, errors.Wrapf(err, "failed to retrieve input to spend [%s]", in) + } + if len(bytes) == 0 { + return false, errors.Errorf("input to spend [%s] does not exists", in) + } + tok := &token.Token{} + err = tok.Deserialize(bytes) + if err != nil { + return false, errors.Wrapf(err, "failed to deserialize input to spend [%s]", in) + } + inputTokens = append(inputTokens, tok) + } + + if len(inputTokens) != 1 { + logger.Debugf("Number of inputs is %d", len(inputs)) + return false, nil + } + inOwner, err := identity.UnmarshallRawOwner(inputTokens[0].Owner) + if err != nil { + return false, errors.Wrap(err, "failed to unmarshall input raw owner") + } + if inOwner.Type != htlc.ScriptType { + logger.Debugf("script recipient does not match the output owner") + return false, nil + } + script := &htlc.Script{} + err = json.Unmarshal(inOwner.Identity, script) + if err != nil { + return false, errors.Wrap(err, "failed to unmarshall into script") + } + if !script.Recipient.Equal(out.Owner) { + logger.Debugf("script recipient does not match the output owner") + return false, nil + } + logger.Debugf("this is an exchange claim") + return true, nil +} diff --git a/token/core/zkatdlog/nogh/deserializer.go b/token/core/zkatdlog/nogh/deserializer.go index e3847a5bb..af83b6be8 100644 --- a/token/core/zkatdlog/nogh/deserializer.go +++ b/token/core/zkatdlog/nogh/deserializer.go @@ -11,12 +11,13 @@ import ( "encoding/json" "sync" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" + idemix2 "github.com/hyperledger-labs/fabric-smart-client/platform/fabric/core/generic/msp/idemix" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity/msp/idemix" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity/msp/x509" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" "github.com/pkg/errors" @@ -62,7 +63,7 @@ func NewDeserializer(pp *crypto.PublicParams) (*deserializer, error) { // GetOwnerVerifier deserializes the verifier for the passed owner identity func (d *deserializer) GetOwnerVerifier(id view.Identity) (driver.Verifier, error) { - return interop.NewDeserializer(d.ownerDeserializer).GetOwnerVerifier(id) + return htlc.NewDeserializer(d.ownerDeserializer).GetOwnerVerifier(id) } // GetIssuerVerifier deserializes the verifier for the passed issuer identity @@ -127,7 +128,7 @@ func (e *enrollmentService) GetEnrollmentID(auditInfo []byte) (string, error) { } // Try to unmarshal it as ScriptInfo - si := &interop.ScriptInfo{} + si := &htlc.ScriptInfo{} err := json.Unmarshal(auditInfo, si) if err == nil && (len(si.Sender) != 0 || len(si.Recipient) != 0) { if len(si.Recipient) != 0 { diff --git a/token/core/zkatdlog/nogh/sender.go b/token/core/zkatdlog/nogh/sender.go index 2386e6b57..80088ccf7 100644 --- a/token/core/zkatdlog/nogh/sender.go +++ b/token/core/zkatdlog/nogh/sender.go @@ -11,7 +11,7 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "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" @@ -70,7 +70,7 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token3 ownerIdentities = append(ownerIdentities, output.Owner.Raw) continue } - _, recipient, err := interop.GetScriptSenderAndRecipient(owner) + _, recipient, err := htlc.GetScriptSenderAndRecipient(owner) if err != nil { return nil, nil, errors.Wrap(err, "failed getting script sender and recipient") } @@ -98,7 +98,7 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token3 // audit info for receivers var receiverAuditInfos [][]byte for _, output := range outputTokens { - auditInfo, err := interop.GetOwnerAuditInfo(output.Owner.Raw, s.SP) + auditInfo, err := htlc.GetOwnerAuditInfo(output.Owner.Raw, s.SP) if err != nil { return nil, nil, errors.Wrapf(err, "failed getting audit info for recipient identity [%s]", view.Identity(output.Owner.Raw).String()) } @@ -108,7 +108,7 @@ func (s *Service) Transfer(txID string, wallet driver.OwnerWallet, ids []*token3 // audit info for senders var senderAuditInfos [][]byte for _, t := range tokens { - auditInfo, err := interop.GetOwnerAuditInfo(t.Owner, s.SP) + auditInfo, err := htlc.GetOwnerAuditInfo(t.Owner, s.SP) if err != nil { return nil, nil, errors.Wrapf(err, "failed getting audit info for sender identity [%s]", view.Identity(t.Owner).String()) } diff --git a/token/driver/action.go b/token/driver/action.go index 8bcdf3760..d11ff3108 100644 --- a/token/driver/action.go +++ b/token/driver/action.go @@ -54,7 +54,6 @@ type TransferAction interface { // GetInputs returns the inputs's identifiers of the action GetInputs() ([]string, error) // IsGraphHiding returns true if the action is graph hiding - // TODO: Deprecated. This should be checked using the public parameters IsGraphHiding() bool // GetMetadata returns the action's metadata GetMetadata() map[string][]byte diff --git a/token/driver/validator.go b/token/driver/validator.go index 0cb7cb09b..ae53c097c 100644 --- a/token/driver/validator.go +++ b/token/driver/validator.go @@ -18,8 +18,8 @@ type Ledger interface { } type SignatureProvider interface { - // HasBeenSignedBy returns true if the provider contains a valid signature for the passed identity and verifier - HasBeenSignedBy(id view.Identity, verifier Verifier) error + // HasBeenSignedBy returns true and signature verified if the provider contains a valid signature for the passed identity and verifier + HasBeenSignedBy(id view.Identity, verifier Verifier) ([]byte, error) // Signatures returns the signatures inside this provider Signatures() [][]byte } diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index 6fa94a238..a72a38369 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -11,8 +11,7 @@ import ( "strconv" "unicode/utf8" - token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" - + "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/pkg/errors" ) @@ -44,7 +43,7 @@ const ( ClaimPreImage = "cpi" ) -func GetTokenIdFromKey(key string) (*token2.ID, error) { +func GetTokenIdFromKey(key string) (*token.ID, error) { _, components, err := SplitCompositeKey(key) if err != nil { return nil, errors.New(fmt.Sprintf("error splitting input composite key: '%s'", err)) @@ -61,10 +60,10 @@ func GetTokenIdFromKey(key string) (*token2.ID, error) { if err != nil { return nil, errors.New(fmt.Sprintf("error parsing output index '%s': '%s'", components[numComponentsInKey-1], err)) } - return &token2.ID{TxId: txID, Index: index}, nil + return &token.ID{TxId: txID, Index: index}, nil } -func GetTokenIdFromExtendedKey(key string) (*token2.ID, error) { +func GetTokenIdFromExtendedKey(key string) (*token.ID, error) { _, components, err := SplitCompositeKey(key) if err != nil { return nil, errors.New(fmt.Sprintf("error splitting input composite key: '%s'", err)) @@ -81,7 +80,7 @@ func GetTokenIdFromExtendedKey(key string) (*token2.ID, error) { if err != nil { return nil, errors.New(fmt.Sprintf("error parsing output index '%s': '%s'", components[numComponentsInExtendedKey-1], err)) } - return &token2.ID{TxId: txID, Index: index}, nil + return &token.ID{TxId: txID, Index: index}, nil } func SplitCompositeKey(compositeKey string) (string, []string, error) { diff --git a/token/services/vault/translator/action.go b/token/services/vault/translator/action.go index 0e332ee9a..73607f73d 100644 --- a/token/services/vault/translator/action.go +++ b/token/services/vault/translator/action.go @@ -23,13 +23,22 @@ type IssueAction interface { //go:generate counterfeiter -o mock/transfer_action.go -fake-name TransferAction . TransferAction +// TransferAction is the action used to transfer tokens type TransferAction interface { + // Serialize returns the serialized version of the action Serialize() ([]byte, error) + // NumOutputs returns the number of outputs of the action NumOutputs() int + // GetSerializedOutputs returns the serialized outputs of the action GetSerializedOutputs() ([][]byte, error) + // IsRedeemAt returns true if the output is a redeem output at the passed index IsRedeemAt(index int) bool + // SerializeOutputAt returns the serialized output at the passed index SerializeOutputAt(index int) ([]byte, error) + // GetInputs returns the inputs's identifiers of the action GetInputs() ([]string, error) + // IsGraphHiding returns true if the action is graph hiding IsGraphHiding() bool + // GetMetadata returns the action's metadata GetMetadata() map[string][]byte } diff --git a/token/services/vault/translator/translator.go b/token/services/vault/translator/translator.go index 187d350f9..c34d0871b 100644 --- a/token/services/vault/translator/translator.go +++ b/token/services/vault/translator/translator.go @@ -13,7 +13,7 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-token-sdk/token/services/vault/keys" - token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" + "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/pkg/errors" ) @@ -114,7 +114,7 @@ func (w *Translator) ReadSetupParameters() ([]byte, error) { return raw, nil } -func (w *Translator) QueryTokens(ids []*token2.ID) ([][]byte, error) { +func (w *Translator) QueryTokens(ids []*token.ID) ([][]byte, error) { var res [][]byte var errs []error for _, id := range ids { From 80a9832bcffc75ae7e9ab07f3845c523c465486d Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 24 Aug 2022 10:53:37 +0200 Subject: [PATCH 07/17] fabtoken validator cleanup Signed-off-by: Angelo De Caro --- integration/token/dvp/views/auditor.go | 2 +- integration/token/fungible/views/auditor.go | 2 +- integration/token/nft/views/auditor.go | 2 +- samples/fungible/views/auditor.go | 2 +- samples/nft/views/auditor.go | 2 +- token/core/fabtoken/validator.go | 97 ++----------- token/core/fabtoken/validator_transfer.go | 127 ++++++++++++------ token/core/interop/htlc/validator.go | 12 +- .../services/certifier/interactive/service.go | 2 +- 9 files changed, 113 insertions(+), 135 deletions(-) diff --git a/integration/token/dvp/views/auditor.go b/integration/token/dvp/views/auditor.go index a0585268b..894ff018a 100644 --- a/integration/token/dvp/views/auditor.go +++ b/integration/token/dvp/views/auditor.go @@ -22,7 +22,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // Validate + // TransferBalanceValidate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/integration/token/fungible/views/auditor.go b/integration/token/fungible/views/auditor.go index 281eae6bd..33302a3d4 100644 --- a/integration/token/fungible/views/auditor.go +++ b/integration/token/fungible/views/auditor.go @@ -27,7 +27,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // Validate + // TransferBalanceValidate logger.Debugf("AuditView: get auditor [%s]", tx.ID()) auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/integration/token/nft/views/auditor.go b/integration/token/nft/views/auditor.go index a0585268b..894ff018a 100644 --- a/integration/token/nft/views/auditor.go +++ b/integration/token/nft/views/auditor.go @@ -22,7 +22,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // Validate + // TransferBalanceValidate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/samples/fungible/views/auditor.go b/samples/fungible/views/auditor.go index bbf4a576a..9b747904d 100644 --- a/samples/fungible/views/auditor.go +++ b/samples/fungible/views/auditor.go @@ -24,7 +24,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // Validate + // TransferBalanceValidate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/samples/nft/views/auditor.go b/samples/nft/views/auditor.go index e0c7de12f..d094f7607 100644 --- a/samples/nft/views/auditor.go +++ b/samples/nft/views/auditor.go @@ -21,7 +21,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // Validate + // TransferBalanceValidate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index 45e67308e..3dc4913f8 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -12,9 +12,7 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" - "github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/htlc" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/pkg/errors" "go.uber.org/zap/zapcore" @@ -39,9 +37,9 @@ func NewValidator(pp *PublicParams, deserializer driver.Deserializer, extraValid return nil, errors.New("please provide a non-nil deserializer") } validators := []ValidateTransferFunc{ - (&TransferSignature{Deserializer: deserializer}).Validate, - (&TransferBalance{PP: pp}).Validate, - TransferHTLC, + TransferSignatureValidate, + TransferBalanceValidate, + TransferHTLCValidate, } validators = append(validators, extraValidators...) v := &Validator{ @@ -234,22 +232,26 @@ func (v *Validator) VerifyTransfers(ledger driver.Ledger, transferActions []*Tra // verify if input tokens and output tokens in the current transfer action have the same type // verify if sum of input tokens in the current transfer action equals the sum of output tokens // in the current transfer action - if err := v.VerifyTransfer(inputTokens, t, signatureProvider); err != nil { + if err := v.VerifyTransfer(ledger, inputTokens, t, signatureProvider); err != nil { return errors.Wrapf(err, "failed to verify transfer action at index %d", i) } - // check metadata, if required - if err := CheckTransferActionMetadata(t, ledger, signatureProvider); err != nil { - return errors.Wrap(err, "failed adding metadata to transfer action") - } } return nil } // VerifyTransfer checks that sum of inputTokens in TransferAction equals sum of outputs in TransferAction // It also checks that all outputs and inputs have the same type -func (v *Validator) VerifyTransfer(inputTokens []*token2.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { +func (v *Validator) VerifyTransfer(ledger driver.Ledger, inputTokens []*token2.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { + ctx := &Context{ + PP: v.pp, + Deserializer: v.deserializer, + SignatureProvider: signatureProvider, + InputTokens: inputTokens, + Action: tr.(*TransferAction), + Ledger: ledger, + } for _, validator := range v.transferValidators { - if err := validator(inputTokens, tr, signatureProvider); err != nil { + if err := validator(ctx); err != nil { return err } } @@ -310,74 +312,3 @@ func RetrieveInputsFromTransferAction(t *TransferAction, ledger driver.Ledger) ( } return inputTokens, nil } - -func CheckTransferActionMetadata(action *TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) error { - for _, sig := range signatureProvider.Signatures() { - claim := &htlc.ClaimSignature{} - if err := json.Unmarshal(sig, claim); err != nil { - continue - } - if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { - return errors.New("expected a valid claim preImage and recipient signature") - } - if IsItAnExchangeClaimTransferAction(action, ledger) { - if len(action.Metadata) == 0 { - return errors.Errorf("cannot find htlc pre-image, no metadata") - } - value, ok := action.Metadata[htlc.ClaimPreImage] - if !ok { - return errors.Errorf("cannot find htlc pre-image, missing metadata entry") - } - if !bytes.Equal(value, claim.Preimage) { - return errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) - } - } - } - return nil -} - -func IsItAnExchangeClaimTransferAction(action *TransferAction, ledger driver.Ledger) bool { - if action.NumOutputs() != 1 { - logger.Debugf("Number of outputs is %d", action.NumOutputs()) - return false - } - out, ok := action.GetOutputs()[0].(*Output) - if !ok { - logger.Debugf("invalid transfer action output") - return false - } - if out.IsRedeem() { - logger.Debugf("this is a redeem") - return false - } - inputs, err := RetrieveInputsFromTransferAction(action, ledger) - if err != nil { - logger.Debugf("error while retrieving inputs from transfer action: %v", err) - return false - } - if len(inputs) != 1 { - logger.Debugf("Number of inputs is %d", len(inputs)) - return false - } - inOwner, err := identity.UnmarshallRawOwner(inputs[0].Owner.Raw) - if err != nil { - logger.Debugf("error while unmarshalling input raw owner: %v", err) - return false - } - if inOwner.Type != htlc.ScriptType { - logger.Debugf("script recipient does not match the output owner") - return false - } - script := &htlc.Script{} - err = json.Unmarshal(inOwner.Identity, script) - if err != nil { - logger.Debugf("error while unmarshalling into script: %v", err) - return false - } - if !script.Recipient.Equal(out.Output.Owner.Raw) { - logger.Debugf("script recipient does not match the output owner") - return false - } - logger.Debugf("this is an exchange claim") - return true -} diff --git a/token/core/fabtoken/validator_transfer.go b/token/core/fabtoken/validator_transfer.go index fcc3bdb19..c58bca2d7 100644 --- a/token/core/fabtoken/validator_transfer.go +++ b/token/core/fabtoken/validator_transfer.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0 package fabtoken import ( + "bytes" "encoding/json" "time" @@ -19,55 +20,56 @@ import ( "github.com/pkg/errors" ) -// ValidateTransferFunc is the prototype of a validation function for a transfer action -type ValidateTransferFunc func(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error - -// TransferSignature validates the signatures for the inputs spent by an action -type TransferSignature struct { - Deserializer driver.Deserializer +type Context struct { + PP *PublicParams + Deserializer driver.Deserializer + SignatureProvider driver.SignatureProvider + Signatures [][]byte + InputTokens []*token.Token + Action *TransferAction + Ledger driver.Ledger } -// Validate validates the signatures for the inputs spent by an action -func (v *TransferSignature) Validate(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { - for _, tok := range inputTokens { +// ValidateTransferFunc is the prototype of a validation function for a transfer action +type ValidateTransferFunc func(ctx *Context) error + +// TransferSignatureValidate validates the signatures for the inputs spent by an action +func TransferSignatureValidate(ctx *Context) error { + for _, tok := range ctx.InputTokens { logger.Debugf("check sender [%s]", view.Identity(tok.Owner.Raw).UniqueID()) - verifier, err := v.Deserializer.GetOwnerVerifier(tok.Owner.Raw) + verifier, err := ctx.Deserializer.GetOwnerVerifier(tok.Owner.Raw) if err != nil { return errors.Wrapf(err, "failed deserializing owner [%v][%s]", tok, view.Identity(tok.Owner.Raw).UniqueID()) } logger.Debugf("signature verification [%v][%s]", tok, view.Identity(tok.Owner.Raw).UniqueID()) - _, err = signatureProvider.HasBeenSignedBy(tok.Owner.Raw, verifier) + sigma, err := ctx.SignatureProvider.HasBeenSignedBy(tok.Owner.Raw, verifier) if err != nil { return errors.Wrapf(err, "failed signature verification [%v][%s]", tok, view.Identity(tok.Owner.Raw).UniqueID()) } + ctx.Signatures = append(ctx.Signatures, sigma) } return nil } -// TransferBalance checks that the sum of the inputs is equal to the sum of the ouputs -type TransferBalance struct { - PP *PublicParams -} - -// Validate checks that the sum of the inputs is equal to the sum of the ouputs -func (v *TransferBalance) Validate(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { - if tr.NumOutputs() == 0 { +// TransferBalanceValidate checks that the sum of the inputs is equal to the sum of the outputs +func TransferBalanceValidate(ctx *Context) error { + if ctx.Action.NumOutputs() == 0 { return errors.Errorf("there is no output") } - if len(inputTokens) == 0 { + if len(ctx.InputTokens) == 0 { return errors.Errorf("there is no input") } - if inputTokens[0] == nil { + if ctx.InputTokens[0] == nil { return errors.Errorf("first input is nil") } - typ := inputTokens[0].Type - inputSum := token.NewZeroQuantity(v.PP.QuantityPrecision) - outputSum := token.NewZeroQuantity(v.PP.QuantityPrecision) - for i, input := range inputTokens { + typ := ctx.InputTokens[0].Type + inputSum := token.NewZeroQuantity(ctx.PP.QuantityPrecision) + outputSum := token.NewZeroQuantity(ctx.PP.QuantityPrecision) + for i, input := range ctx.InputTokens { if input == nil { return errors.Errorf("input %d is nil", i) } - q, err := token.ToQuantity(input.Quantity, v.PP.QuantityPrecision) + q, err := token.ToQuantity(input.Quantity, ctx.PP.QuantityPrecision) if err != nil { return errors.Wrapf(err, "failed parsing quantity [%s]", input.Quantity) } @@ -77,14 +79,14 @@ func (v *TransferBalance) Validate(inputTokens []*token.Token, tr driver.Transfe return errors.Errorf("input type %s does not match type %s", input.Type, typ) } } - for _, output := range tr.GetOutputs() { + for _, output := range ctx.Action.GetOutputs() { out := output.(*Output).Output - q, err := token.ToQuantity(out.Quantity, v.PP.QuantityPrecision) + q, err := token.ToQuantity(out.Quantity, ctx.PP.QuantityPrecision) if err != nil { return errors.Wrapf(err, "failed parsing quantity [%s]", out.Quantity) } outputSum.Add(q) - // check that all outputs have the same type and it is the same type as inputs + // check that all outputs have the same type, and it is the same type as inputs if out.Type != typ { return errors.Errorf("output type %s does not match type %s", out.Type, typ) } @@ -97,39 +99,46 @@ func (v *TransferBalance) Validate(inputTokens []*token.Token, tr driver.Transfe return nil } -// TransferHTLC checks the validity of the TransferHTLC scripts, if any -func TransferHTLC(inputTokens []*token.Token, tr driver.TransferAction, signatureProvider driver.SignatureProvider) error { +// TransferHTLCValidate checks the validity of the TransferHTLCValidate scripts, if any +func TransferHTLCValidate(ctx *Context) error { now := time.Now() - for _, in := range inputTokens { + for i, in := range ctx.InputTokens { owner, err := identity.UnmarshallRawOwner(in.Owner.Raw) if err != nil { return errors.Wrap(err, "failed to unmarshal owner of input token") } - // is it owner by an htlc script? + // is it owned by a htlc script? if owner.Type == htlc.ScriptType { // Then, the first output must be compatible with this input. - if len(tr.GetOutputs()) != 1 { + if len(ctx.Action.GetOutputs()) != 1 { return errors.Errorf("invalid transfer action: an htlc script only transfers the ownership of a token") } // check type and quantity - out := tr.GetOutputs()[0].(*Output).Output - if inputTokens[0].Type != out.Type { + out := ctx.Action.GetOutputs()[0].(*Output).Output + if ctx.InputTokens[0].Type != out.Type { return errors.Errorf("invalid transfer action: type of input does not match type of output") } - if inputTokens[0].Quantity != out.Quantity { + if ctx.InputTokens[0].Quantity != out.Quantity { return errors.Errorf("invalid transfer action: quantity of input does not match quantity of output") } // check owner field - if err := htlc2.VerifyOwner(inputTokens[0].Owner.Raw, out.Owner.Raw); err != nil { + script, err := htlc2.VerifyOwner(ctx.InputTokens[0].Owner.Raw, out.Owner.Raw) + if err != nil { return errors.Wrap(err, "failed to verify transfer from htlc script") } + + // check metadata + sigma := ctx.Signatures[i] + if err := HTLCMetadataCheck(ctx, script, sigma); err != nil { + return errors.WithMessagef(err, "failed to check htlc metadata") + } } } - for _, o := range tr.GetOutputs() { + for _, o := range ctx.Action.GetOutputs() { out, ok := o.(*Output) if !ok { return errors.Errorf("invalid output") @@ -138,7 +147,7 @@ func TransferHTLC(inputTokens []*token.Token, tr driver.TransferAction, signatur continue } - // if it is an htlc script than the deadline must be still valid + // if it is a htlc script than the deadline must be still valid owner, err := identity.UnmarshallRawOwner(out.Output.Owner.Raw) if err != nil { return err @@ -157,3 +166,41 @@ func TransferHTLC(inputTokens []*token.Token, tr driver.TransferAction, signatur } return nil } + +// HTLCMetadataCheck checks that the HTLC metadata is in place +func HTLCMetadataCheck(ctx *Context, script *htlc.Script, sig []byte) error { + claim := &htlc.ClaimSignature{} + if err := json.Unmarshal(sig, claim); err != nil { + return errors.Wrapf(err, "failed unmarshalling cliam signature") + } + if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { + return errors.New("expected a valid claim preImage and recipient signature") + } + + if ctx.Action.NumOutputs() != 1 { + return errors.Errorf("Number of outputs is %d", ctx.Action.NumOutputs()) + } + out, ok := ctx.Action.GetOutputs()[0].(*Output) + if !ok { + return errors.Errorf("invalid transfer action output") + } + if out.IsRedeem() { + return errors.Errorf("this is a redeem") + } + if !script.Recipient.Equal(out.Output.Owner.Raw) { + return errors.Errorf("script recipient does not match the output owner") + } + + if len(ctx.Action.Metadata) == 0 { + return errors.Errorf("cannot find htlc pre-image, no metadata") + } + value, ok := ctx.Action.Metadata[htlc.ClaimPreImage] + if !ok { + return errors.Errorf("cannot find htlc pre-image, missing metadata entry") + } + if !bytes.Equal(value, claim.Preimage) { + return errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) + } + + return nil +} diff --git a/token/core/interop/htlc/validator.go b/token/core/interop/htlc/validator.go index 8f6ad17cd..3c4693234 100644 --- a/token/core/interop/htlc/validator.go +++ b/token/core/interop/htlc/validator.go @@ -16,28 +16,28 @@ import ( ) // VerifyOwner validates the owners of the transfer in the htlc script -func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) error { +func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) (*htlc.Script, error) { sender, err := identity.UnmarshallRawOwner(senderRawOwner) if err != nil { - return err + return nil, err } script := &htlc.Script{} err = json.Unmarshal(sender.Identity, script) if err != nil { - return err + return nil, err } if time.Now().Before(script.Deadline) { // this should be a claim if !script.Recipient.Equal(outRawOwner) { - return errors.Errorf("owner of output token does not correspond to recipient in htlc request") + return nil, errors.Errorf("owner of output token does not correspond to recipient in htlc request") } } else { // this should be a reclaim if !script.Sender.Equal(outRawOwner) { - return errors.Errorf("owner of output token does not correspond to sender in htlc request") + return nil, errors.Errorf("owner of output token does not correspond to sender in htlc request") } } - return nil + return script, nil } diff --git a/token/services/certifier/interactive/service.go b/token/services/certifier/interactive/service.go index 38d90b8be..6e24fc5e3 100644 --- a/token/services/certifier/interactive/service.go +++ b/token/services/certifier/interactive/service.go @@ -169,7 +169,7 @@ func (i *CertificationRequestView) Call(context view.Context) (interface{}, erro return nil, errors.WithMessagef(err, "failed receiving certifications [%v] from [%s]", i.ids, i.certifier) } - // 4. Validate response + // 4. TransferBalanceValidate response logger.Debugf("validate certification request response for [%v]", i.ids) if err := cm.VerifyCertifications(i.ids, certifications); err != nil { logger.Errorf("failed verifying certifications of [%v] from [%s] with err [%s]", i.ids, i.certifier, err) From b96d7f8e7f0b27429b8cfa3985b13f9e0ba70358 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 24 Aug 2022 11:19:01 +0200 Subject: [PATCH 08/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- token/core/zkatdlog/crypto/validator/validator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index 0a0aa424c..38b92b25a 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -67,7 +67,7 @@ func HTLCValidator(tokens []*token.Token, tr *transfer.TransferAction) error { } // check that owner field in output is correct - if err := htlc2.VerifyOwner(tokens[0].Owner, out.Owner); err != nil { + if _, err := htlc2.VerifyOwner(tokens[0].Owner, out.Owner); err != nil { return errors.Wrap(err, "failed to verify transfer from htlc script") } } From 286b4bb125f1b8144236396fff111d87348226e6 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 24 Aug 2022 15:47:41 +0200 Subject: [PATCH 09/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- token/core/fabtoken/validator_transfer.go | 40 +++++++++---------- token/core/interop/htlc/validator.go | 22 ++++++---- .../zkatdlog/crypto/validator/validator.go | 2 +- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/token/core/fabtoken/validator_transfer.go b/token/core/fabtoken/validator_transfer.go index c58bca2d7..65d98d967 100644 --- a/token/core/fabtoken/validator_transfer.go +++ b/token/core/fabtoken/validator_transfer.go @@ -116,23 +116,27 @@ func TransferHTLCValidate(ctx *Context) error { } // check type and quantity - out := ctx.Action.GetOutputs()[0].(*Output).Output - if ctx.InputTokens[0].Type != out.Type { + output := ctx.Action.GetOutputs()[0].(*Output) + tok := output.Output + if ctx.InputTokens[0].Type != tok.Type { return errors.Errorf("invalid transfer action: type of input does not match type of output") } - if ctx.InputTokens[0].Quantity != out.Quantity { + if ctx.InputTokens[0].Quantity != tok.Quantity { return errors.Errorf("invalid transfer action: quantity of input does not match quantity of output") } + if output.IsRedeem() { + return errors.Errorf("invalid transfer action: the output for corresponding to an htlc spending should not be a redeem") + } // check owner field - script, err := htlc2.VerifyOwner(ctx.InputTokens[0].Owner.Raw, out.Owner.Raw) + _, op, err := htlc2.VerifyOwner(ctx.InputTokens[0].Owner.Raw, tok.Owner.Raw) if err != nil { return errors.Wrap(err, "failed to verify transfer from htlc script") } // check metadata sigma := ctx.Signatures[i] - if err := HTLCMetadataCheck(ctx, script, sigma); err != nil { + if err := HTLCMetadataCheck(ctx, op, sigma); err != nil { return errors.WithMessagef(err, "failed to check htlc metadata") } } @@ -168,29 +172,23 @@ func TransferHTLCValidate(ctx *Context) error { } // HTLCMetadataCheck checks that the HTLC metadata is in place -func HTLCMetadataCheck(ctx *Context, script *htlc.Script, sig []byte) error { +func HTLCMetadataCheck(ctx *Context, op htlc2.OperationType, sig []byte) error { + if op == htlc2.Reclaim { + // No metadata in this case + return nil + } + + // Unmarshal signature to ClaimSignature claim := &htlc.ClaimSignature{} if err := json.Unmarshal(sig, claim); err != nil { - return errors.Wrapf(err, "failed unmarshalling cliam signature") + return errors.Wrapf(err, "failed unmarshalling cliam signature [%s]", string(sig)) } + // Check that it is well-formed if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { return errors.New("expected a valid claim preImage and recipient signature") } - if ctx.Action.NumOutputs() != 1 { - return errors.Errorf("Number of outputs is %d", ctx.Action.NumOutputs()) - } - out, ok := ctx.Action.GetOutputs()[0].(*Output) - if !ok { - return errors.Errorf("invalid transfer action output") - } - if out.IsRedeem() { - return errors.Errorf("this is a redeem") - } - if !script.Recipient.Equal(out.Output.Owner.Raw) { - return errors.Errorf("script recipient does not match the output owner") - } - + // Check the pre-image is in the action's metadata if len(ctx.Action.Metadata) == 0 { return errors.Errorf("cannot find htlc pre-image, no metadata") } diff --git a/token/core/interop/htlc/validator.go b/token/core/interop/htlc/validator.go index 3c4693234..f2847f525 100644 --- a/token/core/interop/htlc/validator.go +++ b/token/core/interop/htlc/validator.go @@ -15,29 +15,37 @@ import ( "github.com/pkg/errors" ) +type OperationType int + +const ( + None OperationType = iota + Claim + Reclaim +) + // VerifyOwner validates the owners of the transfer in the htlc script -func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) (*htlc.Script, error) { +func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) (*htlc.Script, OperationType, error) { sender, err := identity.UnmarshallRawOwner(senderRawOwner) if err != nil { - return nil, err + return nil, None, err } script := &htlc.Script{} err = json.Unmarshal(sender.Identity, script) if err != nil { - return nil, err + return nil, None, err } if time.Now().Before(script.Deadline) { // this should be a claim if !script.Recipient.Equal(outRawOwner) { - return nil, errors.Errorf("owner of output token does not correspond to recipient in htlc request") + return nil, None, errors.Errorf("owner of output token does not correspond to recipient in htlc request") } + return script, Claim, nil } else { // this should be a reclaim if !script.Sender.Equal(outRawOwner) { - return nil, errors.Errorf("owner of output token does not correspond to sender in htlc request") + return nil, None, errors.Errorf("owner of output token does not correspond to sender in htlc request") } + return script, Reclaim, nil } - - return script, nil } diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index 38b92b25a..1965ff406 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -67,7 +67,7 @@ func HTLCValidator(tokens []*token.Token, tr *transfer.TransferAction) error { } // check that owner field in output is correct - if _, err := htlc2.VerifyOwner(tokens[0].Owner, out.Owner); err != nil { + if _, _, err := htlc2.VerifyOwner(tokens[0].Owner, out.Owner); err != nil { return errors.Wrap(err, "failed to verify transfer from htlc script") } } From ef9e8f04eb31727cc8c7a320e6634c9f53f23cb8 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Thu, 25 Aug 2022 08:49:06 +0200 Subject: [PATCH 10/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- token/core/common/backend.go | 48 ++++ token/core/fabtoken/deserializer.go | 4 +- token/core/fabtoken/validator.go | 41 +-- token/core/fabtoken/validator_transfer.go | 2 +- token/core/interop/htlc/deserializer.go | 2 +- token/core/interop/htlc/validator.go | 4 +- .../zkatdlog/crypto/validator/validator.go | 264 ++---------------- .../crypto/validator/validator_transfer.go | 187 +++++++++++++ token/core/zkatdlog/nogh/deserializer.go | 4 +- 9 files changed, 264 insertions(+), 292 deletions(-) create mode 100644 token/core/common/backend.go create mode 100644 token/core/zkatdlog/crypto/validator/validator_transfer.go diff --git a/token/core/common/backend.go b/token/core/common/backend.go new file mode 100644 index 000000000..9d7fd3e7d --- /dev/null +++ b/token/core/common/backend.go @@ -0,0 +1,48 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package common + +import ( + "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + "github.com/pkg/errors" +) + +type Backend struct { + // Ledger to access the ledget state + Ledger driver.GetStateFnc + // signed Message + Message []byte + // Cursor is used to iterate over the signatures + Cursor int + // signatures on Message + Sigs [][]byte +} + +func NewBackend(ledger driver.GetStateFnc, message []byte, sigs [][]byte) *Backend { + return &Backend{Ledger: ledger, Message: message, Sigs: sigs} +} + +// HasBeenSignedBy checks if a given Message has been signed by the signing identity matching +// the passed verifier +func (b *Backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) ([]byte, error) { + if b.Cursor >= len(b.Sigs) { + return nil, errors.Errorf("invalid state, insufficient number of signatures") + } + sigma := b.Sigs[b.Cursor] + b.Cursor++ + + return sigma, verifier.Verify(b.Message, sigma) +} + +func (b *Backend) GetState(key string) ([]byte, error) { + return b.Ledger(key) +} + +func (b *Backend) Signatures() [][]byte { + return b.Sigs +} diff --git a/token/core/fabtoken/deserializer.go b/token/core/fabtoken/deserializer.go index ebb3040ba..34a3ee4f2 100644 --- a/token/core/fabtoken/deserializer.go +++ b/token/core/fabtoken/deserializer.go @@ -35,13 +35,13 @@ func NewDeserializer() *deserializer { return &deserializer{ auditorDeserializer: &x509.MSPIdentityDeserializer{}, issuerDeserializer: &x509.MSPIdentityDeserializer{}, - ownerDeserializer: identity.NewRawOwnerIdentityDeserializer(&x509.MSPIdentityDeserializer{}), + ownerDeserializer: htlc.NewDeserializer(identity.NewRawOwnerIdentityDeserializer(&x509.MSPIdentityDeserializer{})), } } // GetOwnerVerifier deserializes the verifier for the passed owner identity func (d *deserializer) GetOwnerVerifier(id view.Identity) (driver.Verifier, error) { - return htlc.NewDeserializer(d.ownerDeserializer).GetOwnerVerifier(id) + return d.ownerDeserializer.DeserializeVerifier(id) } // GetIssuerVerifier deserializes the verifier for the passed issuer identity diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index 3dc4913f8..1c57cc36a 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -10,8 +10,9 @@ import ( "bytes" "encoding/json" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" + "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" - "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/pkg/errors" @@ -116,7 +117,7 @@ func (v *Validator) VerifyTokenRequestFromRaw(getState driver.GetStateFnc, bindi return nil, errors.Wrap(err, "failed to unmarshal token request") } - // Prepare message expected to be signed + // Prepare Message expected to be signed // TODO: encapsulate this somewhere req := &driver.TokenRequest{} req.Transfers = tr.Transfers @@ -139,11 +140,7 @@ func (v *Validator) VerifyTokenRequestFromRaw(getState driver.GetStateFnc, bindi signatures = tr.Signatures } - backend := &backend{ - getState: getState, - message: signed, - signatures: signatures, - } + backend := common.NewBackend(getState, signed, signatures) return v.VerifyTokenRequest(backend, backend, binding, tr) } @@ -258,36 +255,6 @@ func (v *Validator) VerifyTransfer(ledger driver.Ledger, inputTokens []*token2.T return nil } -type backend struct { - getState driver.GetStateFnc - // signed message - message []byte - index int - // signatures on message - signatures [][]byte -} - -// HasBeenSignedBy checks if a given message has been signed by the signing identity matching -// the passed verifier -// todo shall we remove id from the parameters -func (b *backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) ([]byte, error) { - if b.index >= len(b.signatures) { - return nil, errors.Errorf("invalid state, insufficient number of signatures") - } - sigma := b.signatures[b.index] - b.index++ - - return sigma, verifier.Verify(b.message, sigma) -} - -func (b *backend) GetState(key string) ([]byte, error) { - return b.getState(key) -} - -func (b *backend) Signatures() [][]byte { - return b.signatures -} - // RetrieveInputsFromTransferAction retrieves from the passed ledger the inputs identified in TransferAction func RetrieveInputsFromTransferAction(t *TransferAction, ledger driver.Ledger) ([]*token2.Token, error) { var inputTokens []*token2.Token diff --git a/token/core/fabtoken/validator_transfer.go b/token/core/fabtoken/validator_transfer.go index 65d98d967..f127bac5b 100644 --- a/token/core/fabtoken/validator_transfer.go +++ b/token/core/fabtoken/validator_transfer.go @@ -129,7 +129,7 @@ func TransferHTLCValidate(ctx *Context) error { } // check owner field - _, op, err := htlc2.VerifyOwner(ctx.InputTokens[0].Owner.Raw, tok.Owner.Raw) + _, op, err := htlc2.VerifyOwner(ctx.InputTokens[0].Owner.Raw, tok.Owner.Raw, now) if err != nil { return errors.Wrap(err, "failed to verify transfer from htlc script") } diff --git a/token/core/interop/htlc/deserializer.go b/token/core/interop/htlc/deserializer.go index 48139c108..51c6a24d1 100644 --- a/token/core/interop/htlc/deserializer.go +++ b/token/core/interop/htlc/deserializer.go @@ -28,7 +28,7 @@ func NewDeserializer(ownerDeserializer VerifierDES) *Deserializer { return &Deserializer{OwnerDeserializer: ownerDeserializer} } -func (d *Deserializer) GetOwnerVerifier(id view.Identity) (driver.Verifier, error) { +func (d *Deserializer) DeserializeVerifier(id view.Identity) (driver.Verifier, error) { si, err := identity.UnmarshallRawOwner(id) if err != nil { return nil, errors.Wrap(err, "failed to unmarshal RawOwner") diff --git a/token/core/interop/htlc/validator.go b/token/core/interop/htlc/validator.go index f2847f525..80e695510 100644 --- a/token/core/interop/htlc/validator.go +++ b/token/core/interop/htlc/validator.go @@ -24,7 +24,7 @@ const ( ) // VerifyOwner validates the owners of the transfer in the htlc script -func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) (*htlc.Script, OperationType, error) { +func VerifyOwner(senderRawOwner []byte, outRawOwner []byte, now time.Time) (*htlc.Script, OperationType, error) { sender, err := identity.UnmarshallRawOwner(senderRawOwner) if err != nil { return nil, None, err @@ -35,7 +35,7 @@ func VerifyOwner(senderRawOwner []byte, outRawOwner []byte) (*htlc.Script, Opera return nil, None, err } - if time.Now().Before(script.Deadline) { + if now.Before(script.Deadline) { // this should be a claim if !script.Recipient.Equal(outRawOwner) { return nil, None, errors.Errorf("owner of output token does not correspond to recipient in htlc request") diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index 1965ff406..b4d40f122 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -8,98 +8,21 @@ package validator import ( "bytes" - "encoding/json" - "time" - math "github.com/IBM/mathlib" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" + "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" - htlc2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto" issue2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/issue" - "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/interop/htlc" "github.com/pkg/errors" ) var logger = flogging.MustGetLogger("token-sdk.zkatdlog") -type ValidateTransferFunc func(tokens []*token.Token, tr *transfer.TransferAction) error - -type ZKVerifier struct { - pp *crypto.PublicParams -} - -func (v *ZKVerifier) Validate(tokens []*token.Token, tr *transfer.TransferAction) error { - in := make([]*math.G1, len(tokens)) - for i := range tokens { - in[i] = tokens[i].GetCommitment() - } - - if err := transfer.NewVerifier( - in, - tr.GetOutputCommitments(), - v.pp).Verify(tr.GetProof()); err != nil { - return err - } - - return nil -} - -func HTLCValidator(tokens []*token.Token, tr *transfer.TransferAction) error { - for _, in := range tokens { - owner, err := identity.UnmarshallRawOwner(in.Owner) - if err != nil { - return errors.Wrap(err, "failed to unmarshal owner of input token") - } - if owner.Type == htlc.ScriptType { - if len(tokens) != 1 || len(tr.GetOutputs()) != 1 { - return errors.Errorf("invalid transfer action: an htlc script only transfers the ownership of a token") - } - - out := tr.GetOutputs()[0].(*token.Token) - if tokens[0].Data.Equals(out.Data) { - return errors.Errorf("invalid transfer action: content of input does not match content of output") - } - - // check that owner field in output is correct - if _, _, err := htlc2.VerifyOwner(tokens[0].Owner, out.Owner); err != nil { - return errors.Wrap(err, "failed to verify transfer from htlc script") - } - } - } - - for _, o := range tr.GetOutputs() { - out, ok := o.(*token.Token) - if !ok { - return errors.Errorf("invalid output") - } - if out.IsRedeem() { - continue - } - owner, err := identity.UnmarshallRawOwner(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 script.Deadline.Before(time.Now()) { - return errors.Errorf("htlc script invalid: expiration date has already passed") - } - continue - } - } - return nil -} - type Validator struct { pp *crypto.PublicParams deserializer driver.Deserializer @@ -108,8 +31,9 @@ type Validator struct { func New(pp *crypto.PublicParams, deserializer driver.Deserializer, extraValidators ...ValidateTransferFunc) *Validator { validators := []ValidateTransferFunc{ - (&ZKVerifier{pp: pp}).Validate, - HTLCValidator, + TransferSignatureValidate, + TransferZKProofValidate, + TransferHTLCValidate, } validators = append(validators, extraValidators...) return &Validator{ @@ -149,11 +73,7 @@ func (v *Validator) VerifyTokenRequestFromRaw(getState driver.GetStateFnc, bindi signatures = tr.Signatures } - backend := &backend{ - getState: getState, - message: signed, - signatures: signatures, - } + backend := common.NewBackend(getState, signed, signatures) return v.VerifyTokenRequest(backend, backend, binding, tr) } @@ -183,14 +103,8 @@ func (v *Validator) VerifyTokenRequest(ledger driver.Ledger, signatureProvider d actions = append(actions, action) } for _, action := range ta { - transferAction := action.(*transfer.TransferAction) - act, err := AddMetadataToTransferAction(transferAction, ledger, signatureProvider) - if err != nil { - return nil, errors.Wrap(err, "failed adding metadata to transfer action") - } - actions = append(actions, act) + actions = append(actions, action) } - return actions, nil } @@ -280,171 +194,27 @@ func (v *Validator) verifyIssue(issue driver.IssueAction) error { func (v *Validator) verifyTransfers(ledger driver.Ledger, transferActions []driver.TransferAction, signatureProvider driver.SignatureProvider) error { logger.Debugf("check sender start...") defer logger.Debugf("check sender finished.") - for i, t := range transferActions { - var inputTokens [][]byte - inputs, err := t.GetInputs() - if err != nil { - return errors.Wrapf(err, "failed to retrieve inputs to spend") - } - for _, in := range inputs { - logger.Debugf("load token [%d][%s]", i, in) - bytes, err := ledger.GetState(in) - if err != nil { - return errors.Wrapf(err, "failed to retrieve input to spend [%s]", in) - } - if len(bytes) == 0 { - return errors.Errorf("input to spend [%s] does not exists", in) - } - inputTokens = append(inputTokens, bytes) - tok := &token.Token{} - err = tok.Deserialize(bytes) - if err != nil { - return errors.Wrapf(err, "failed to deserialize input to spend [%s]", in) - } - logger.Debugf("check sender [%d][%s]", i, view.Identity(tok.Owner).UniqueID()) - verifier, err := v.deserializer.GetOwnerVerifier(tok.Owner) - if err != nil { - return errors.Wrapf(err, "failed deserializing owner [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) - } - logger.Debugf("signature verification [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) - if _, err := signatureProvider.HasBeenSignedBy(tok.Owner, verifier); err != nil { - return errors.Wrapf(err, "failed signature verification [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) - } - } - if err := v.verifyTransfer(inputTokens, t); err != nil { + for _, t := range transferActions { + if err := v.verifyTransfer(t, ledger, signatureProvider); err != nil { return errors.Wrapf(err, "failed to verify transfer action") } } return nil } -func (v *Validator) verifyTransfer(inputTokens [][]byte, tr driver.TransferAction) error { +func (v *Validator) verifyTransfer(tr driver.TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) error { action := tr.(*transfer.TransferAction) - tokens := make([]*token.Token, len(inputTokens)) - for i, raw := range inputTokens { - tokens[i] = &token.Token{} - if err := tokens[i].Deserialize(raw); err != nil { - return errors.Wrapf(err, "invalid transfer: failed to deserialize input [%d]", i) - } + context := &Context{ + PP: v.pp, + Deserializer: v.deserializer, + Action: action, + Ledger: ledger, + SignatureProvider: signatureProvider, } for _, v := range v.validators { - if err := v(tokens, action); err != nil { + if err := v(context); err != nil { return err } } return nil } - -type backend struct { - getState driver.GetStateFnc - message []byte - index int - signatures [][]byte -} - -func (b *backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) ([]byte, error) { - if b.index >= len(b.signatures) { - return nil, errors.Errorf("invalid state, insufficient number of signatures") - } - sigma := b.signatures[b.index] - b.index++ - - return sigma, verifier.Verify(b.message, sigma) -} - -func (b *backend) GetState(key string) ([]byte, error) { - return b.getState(key) -} - -func (b *backend) Signatures() [][]byte { - return b.signatures -} - -func AddMetadataToTransferAction(action *transfer.TransferAction, ledger driver.Ledger, signatureProvider driver.SignatureProvider) (*transfer.TransferAction, error) { - for _, sig := range signatureProvider.Signatures() { - claim := &htlc.ClaimSignature{} - if err := json.Unmarshal(sig, claim); err != nil { - continue - } - if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { - return nil, errors.New("expected a valid claim preImage and recipient signature") - } - b, err := IsItAnExchangeClaimTransferAction(action, ledger) - if err != nil { - return nil, err - } - if b { - if len(action.Metadata) == 0 { - return nil, errors.Errorf("cannot find htlc pre-image, no metadata") - } - value, ok := action.Metadata[htlc.ClaimPreImage] - if !ok { - return nil, errors.Errorf("cannot find htlc pre-image, missing metadata entry") - } - if !bytes.Equal(value, claim.Preimage) { - return nil, errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) - } - } - } - return action, nil -} - -func IsItAnExchangeClaimTransferAction(action *transfer.TransferAction, ledger driver.Ledger) (bool, error) { - if action.NumOutputs() != 1 { - logger.Debugf("Number of outputs is %d", action.NumOutputs()) - return false, nil - } - out, ok := action.GetOutputs()[0].(*token.Token) - if !ok { - return false, errors.New("invalid transfer action output") - } - if out.IsRedeem() { - logger.Debugf("this is a redeem") - return false, nil - } - - var inputTokens []*token.Token - inputs, err := action.GetInputs() - if err != nil { - return false, errors.Wrapf(err, "failed to retrieve inputs to spend") - } - for _, in := range inputs { - bytes, err := ledger.GetState(in) - if err != nil { - return false, errors.Wrapf(err, "failed to retrieve input to spend [%s]", in) - } - if len(bytes) == 0 { - return false, errors.Errorf("input to spend [%s] does not exists", in) - } - tok := &token.Token{} - err = tok.Deserialize(bytes) - if err != nil { - return false, errors.Wrapf(err, "failed to deserialize input to spend [%s]", in) - } - inputTokens = append(inputTokens, tok) - } - - if len(inputTokens) != 1 { - logger.Debugf("Number of inputs is %d", len(inputs)) - return false, nil - } - inOwner, err := identity.UnmarshallRawOwner(inputTokens[0].Owner) - if err != nil { - return false, errors.Wrap(err, "failed to unmarshall input raw owner") - } - if inOwner.Type != htlc.ScriptType { - logger.Debugf("script recipient does not match the output owner") - return false, nil - } - script := &htlc.Script{} - err = json.Unmarshal(inOwner.Identity, script) - if err != nil { - return false, errors.Wrap(err, "failed to unmarshall into script") - } - if !script.Recipient.Equal(out.Owner) { - logger.Debugf("script recipient does not match the output owner") - return false, nil - } - logger.Debugf("this is an exchange claim") - return true, nil -} diff --git a/token/core/zkatdlog/crypto/validator/validator_transfer.go b/token/core/zkatdlog/crypto/validator/validator_transfer.go new file mode 100644 index 000000000..06fcd76a4 --- /dev/null +++ b/token/core/zkatdlog/crypto/validator/validator_transfer.go @@ -0,0 +1,187 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package validator + +import ( + "bytes" + "encoding/json" + "time" + + "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" + + "github.com/hyperledger-labs/fabric-token-sdk/token/driver" + + math "github.com/IBM/mathlib" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" + htlc2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto" + "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/services/interop/htlc" + "github.com/pkg/errors" +) + +type Context struct { + PP *crypto.PublicParams + Deserializer driver.Deserializer + SignatureProvider driver.SignatureProvider + Signatures [][]byte + InputTokens []*token.Token + Action *transfer.TransferAction + Ledger driver.Ledger +} + +type ValidateTransferFunc func(ctx *Context) error + +func TransferSignatureValidate(ctx *Context) error { + var tokens []*token.Token + var signatures [][]byte + + inputs, err := ctx.Action.GetInputs() + if err != nil { + return errors.Wrapf(err, "failed to retrieve inputs to spend") + } + for i, in := range inputs { + logger.Debugf("load token [%d][%s]", i, in) + bytes, err := ctx.Ledger.GetState(in) + if err != nil { + return errors.Wrapf(err, "failed to retrieve input to spend [%s]", in) + } + if len(bytes) == 0 { + return errors.Errorf("input to spend [%s] does not exists", in) + } + + tok := &token.Token{} + if err := tok.Deserialize(bytes); err != nil { + return errors.Wrapf(err, "failed to deserialize input to spend [%s]", in) + } + tokens = append(tokens, tok) + logger.Debugf("check sender [%d][%s]", i, view.Identity(tok.Owner).UniqueID()) + verifier, err := ctx.Deserializer.GetOwnerVerifier(tok.Owner) + if err != nil { + return errors.Wrapf(err, "failed deserializing owner [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) + } + logger.Debugf("signature verification [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) + sigma, err := ctx.SignatureProvider.HasBeenSignedBy(tok.Owner, verifier) + if err != nil { + return errors.Wrapf(err, "failed signature verification [%d][%s][%s]", i, in, view.Identity(tok.Owner).UniqueID()) + } + signatures = append(signatures, sigma) + } + + ctx.InputTokens = tokens + ctx.Signatures = signatures + + return nil +} + +func TransferZKProofValidate(ctx *Context) error { + in := make([]*math.G1, len(ctx.InputTokens)) + for i, tok := range ctx.InputTokens { + in[i] = tok.GetCommitment() + } + + if err := transfer.NewVerifier( + in, + ctx.Action.GetOutputCommitments(), + ctx.PP).Verify(ctx.Action.GetProof()); err != nil { + return err + } + + return nil +} + +func TransferHTLCValidate(ctx *Context) error { + now := time.Now() + + for i, in := range ctx.InputTokens { + owner, err := identity.UnmarshallRawOwner(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.Action.GetOutputs()) != 1 { + return errors.Errorf("invalid transfer action: an htlc script only transfers the ownership of a token") + } + + out := ctx.Action.GetOutputs()[0].(*token.Token) + if ctx.InputTokens[0].Data.Equals(out.Data) { + return errors.Errorf("invalid transfer action: content of input does not match content of output") + } + + // check that owner field in output is correct + _, op, err := htlc2.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] + if err := HTLCMetadataCheck(ctx, op, sigma); err != nil { + return errors.WithMessagef(err, "failed to check htlc metadata") + } + } + } + + for _, o := range ctx.Action.GetOutputs() { + out, ok := o.(*token.Token) + if !ok { + return errors.Errorf("invalid output") + } + if out.IsRedeem() { + continue + } + owner, err := identity.UnmarshallRawOwner(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 script.Deadline.Before(now) { + return errors.Errorf("htlc script invalid: expiration date has already passed") + } + continue + } + } + return nil +} + +// HTLCMetadataCheck checks that the HTLC metadata is in place +func HTLCMetadataCheck(ctx *Context, op htlc2.OperationType, sig []byte) error { + if op == htlc2.Reclaim { + // No metadata in this case + return nil + } + + // Unmarshal signature to ClaimSignature + claim := &htlc.ClaimSignature{} + if err := json.Unmarshal(sig, claim); err != nil { + return errors.Wrapf(err, "failed unmarshalling cliam signature [%s]", string(sig)) + } + // Check that it is well-formed + if len(claim.Preimage) == 0 || len(claim.RecipientSignature) == 0 { + return errors.New("expected a valid claim preImage and recipient signature") + } + + // Check the pre-image is in the action's metadata + if len(ctx.Action.Metadata) == 0 { + return errors.Errorf("cannot find htlc pre-image, no metadata") + } + value, ok := ctx.Action.Metadata[htlc.ClaimPreImage] + if !ok { + return errors.Errorf("cannot find htlc pre-image, missing metadata entry") + } + if !bytes.Equal(value, claim.Preimage) { + return errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) + } + + return nil +} diff --git a/token/core/zkatdlog/nogh/deserializer.go b/token/core/zkatdlog/nogh/deserializer.go index af83b6be8..3ffb18a67 100644 --- a/token/core/zkatdlog/nogh/deserializer.go +++ b/token/core/zkatdlog/nogh/deserializer.go @@ -56,14 +56,14 @@ func NewDeserializer(pp *crypto.PublicParams) (*deserializer, error) { return &deserializer{ auditorDeserializer: &x509.MSPIdentityDeserializer{}, issuerDeserializer: &x509.MSPIdentityDeserializer{}, - ownerDeserializer: identity.NewRawOwnerIdentityDeserializer(idemixDes), + ownerDeserializer: htlc.NewDeserializer(identity.NewRawOwnerIdentityDeserializer(idemixDes)), auditDeserializer: idemixDes, }, nil } // GetOwnerVerifier deserializes the verifier for the passed owner identity func (d *deserializer) GetOwnerVerifier(id view.Identity) (driver.Verifier, error) { - return htlc.NewDeserializer(d.ownerDeserializer).GetOwnerVerifier(id) + return d.ownerDeserializer.DeserializeVerifier(id) } // GetIssuerVerifier deserializes the verifier for the passed issuer identity From 5ae9fd0d657f3769252782d77db5483244c4a4a6 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Thu, 25 Aug 2022 08:59:52 +0200 Subject: [PATCH 11/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- token/services/network/fabric/processor.go | 2 +- token/services/network/orion/processor.go | 2 +- token/services/vault/keys/keys.go | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/token/services/network/fabric/processor.go b/token/services/network/fabric/processor.go index a63526a8a..fca836b72 100644 --- a/token/services/network/fabric/processor.go +++ b/token/services/network/fabric/processor.go @@ -200,7 +200,7 @@ func (r *RWSetProcessor) tokenRequest(req fabric.Request, tx fabric.ProcessTrans continue case keys.TransferActionMetadata: if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("expected key without the issue action metadata, skipping") + logger.Debugf("expected key without the transfer action metadata, skipping") } continue } diff --git a/token/services/network/orion/processor.go b/token/services/network/orion/processor.go index c3d09e7ce..9910b0f24 100644 --- a/token/services/network/orion/processor.go +++ b/token/services/network/orion/processor.go @@ -167,7 +167,7 @@ func (r *RWSetProcessor) tokenRequest(req orion.Request, tx orion.ProcessTransac continue case keys.TransferActionMetadata: if logger.IsEnabledFor(zapcore.DebugLevel) { - logger.Debugf("expected key without the issue action metadata, skipping") + logger.Debugf("expected key without the transfer action metadata, skipping") } continue } diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index a72a38369..ef910781f 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -26,21 +26,15 @@ const ( TokenMineKeyPrefix = "mine" TokenSetupKeyPrefix = "setup" IssuedHistoryTokenKeyPrefix = "issued" - TokenAuditorKeyPrefix = "auditor" TokenNameSpace = "zkat" numComponentsInKey = 2 // 2 components: txid, index, excluding TokenKeyPrefix numComponentsInExtendedKey = 4 // 2 components: id, type, txid, index, excluding TokenKeyPrefix - Action = "action" - ActionIssue = "issue" - ActionTransfer = "transfer" Info = "info" IDs = "ids" TokenRequestKeyPrefix = "token_request" - OwnerSeparator = "/" SerialNumber = "sn" IssueActionMetadata = "iam" TransferActionMetadata = "tam" - ClaimPreImage = "cpi" ) func GetTokenIdFromKey(key string) (*token.ID, error) { From 65f05af7296a19761486612c22f8b83eaafd11eb Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Thu, 25 Aug 2022 09:49:00 +0200 Subject: [PATCH 12/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- integration/token/dvp/views/auditor.go | 2 +- integration/token/fungible/views/auditor.go | 2 +- integration/token/nft/views/auditor.go | 2 +- samples/fungible/views/auditor.go | 2 +- samples/nft/views/auditor.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/integration/token/dvp/views/auditor.go b/integration/token/dvp/views/auditor.go index 894ff018a..a0585268b 100644 --- a/integration/token/dvp/views/auditor.go +++ b/integration/token/dvp/views/auditor.go @@ -22,7 +22,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // TransferBalanceValidate + // Validate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/integration/token/fungible/views/auditor.go b/integration/token/fungible/views/auditor.go index 33302a3d4..281eae6bd 100644 --- a/integration/token/fungible/views/auditor.go +++ b/integration/token/fungible/views/auditor.go @@ -27,7 +27,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // TransferBalanceValidate + // Validate logger.Debugf("AuditView: get auditor [%s]", tx.ID()) auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/integration/token/nft/views/auditor.go b/integration/token/nft/views/auditor.go index 894ff018a..a0585268b 100644 --- a/integration/token/nft/views/auditor.go +++ b/integration/token/nft/views/auditor.go @@ -22,7 +22,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // TransferBalanceValidate + // Validate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/samples/fungible/views/auditor.go b/samples/fungible/views/auditor.go index 9b747904d..bbf4a576a 100644 --- a/samples/fungible/views/auditor.go +++ b/samples/fungible/views/auditor.go @@ -24,7 +24,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // TransferBalanceValidate + // Validate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") diff --git a/samples/nft/views/auditor.go b/samples/nft/views/auditor.go index d094f7607..e0c7de12f 100644 --- a/samples/nft/views/auditor.go +++ b/samples/nft/views/auditor.go @@ -21,7 +21,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) { w := ttx.MyAuditorWallet(context) assert.NotNil(w, "failed getting default auditor wallet") - // TransferBalanceValidate + // Validate auditor := ttx.NewAuditor(context, w) assert.NoError(auditor.Validate(tx), "failed auditing verification") From 876ec27b16c7aa883149cfb9eb8e7c8c979e2207 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Thu, 25 Aug 2022 13:39:47 +0200 Subject: [PATCH 13/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- token/services/vault/keys/keys.go | 4 ++-- token/services/vault/translator/translator.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index ef910781f..e1351fb5b 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -136,8 +136,8 @@ func CreateIssueActionMetadataKey(hash string) (string, error) { return CreateCompositeKey(TokenKeyPrefix, []string{IssueActionMetadata, hash}) } -func CreateTransferActionMetadataKey(txID string, index uint64, subKey string) (string, error) { - return CreateCompositeKey(TokenKeyPrefix, []string{TransferActionMetadata, txID, strconv.FormatUint(index, 10), subKey}) +func CreateTransferActionMetadataKey(txID string, subKey string, index uint64) (string, error) { + return CreateCompositeKey(TokenKeyPrefix, []string{TransferActionMetadata, txID, subKey, strconv.FormatUint(index, 10)}) } func IsTransferMetadataKeyWithSubKey(k string, subKey string) (bool, error) { diff --git a/token/services/vault/translator/translator.go b/token/services/vault/translator/translator.go index c34d0871b..aa2d6e040 100644 --- a/token/services/vault/translator/translator.go +++ b/token/services/vault/translator/translator.go @@ -349,7 +349,7 @@ func (w *Translator) commitTransferAction(transferAction TransferAction) error { // store metadata metadata := transferAction.GetMetadata() for key, value := range metadata { - k, err := keys.CreateTransferActionMetadataKey(w.TxID, w.metadataCounter, key) + k, err := keys.CreateTransferActionMetadataKey(w.TxID, key, w.metadataCounter) if err != nil { return errors.Wrapf(err, "failed constructing metadata key") } From 984db015d46c32ea6490680e5088e59f3613d542 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Thu, 25 Aug 2022 14:03:48 +0200 Subject: [PATCH 14/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- token/services/vault/keys/keys.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index e1351fb5b..12beb5bfd 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -154,7 +154,7 @@ func IsTransferMetadataKeyWithSubKey(k string, subKey string) (bool, error) { if len(components) != 4 { return false, nil } - return components[3] == subKey, nil + return components[2] == subKey, nil } // CreateCompositeKey and its related functions and consts copied from core/chaincode/shim/chaincode.go From 6e72d945d55ae341542a44381494208815c75c76 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Sun, 28 Aug 2022 09:22:21 +0200 Subject: [PATCH 15/17] fixup! fabtoken validator cleanup Signed-off-by: Angelo De Caro --- token/core/fabtoken/actions.go | 2 +- token/core/fabtoken/validator_transfer.go | 4 ++-- token/core/zkatdlog/crypto/transfer/sender.go | 2 +- token/driver/action.go | 2 +- token/services/vault/keys/keys.go | 3 +++ token/services/vault/translator/action.go | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/token/core/fabtoken/actions.go b/token/core/fabtoken/actions.go index 82a040e8f..21ecc2d61 100644 --- a/token/core/fabtoken/actions.go +++ b/token/core/fabtoken/actions.go @@ -117,7 +117,7 @@ type TransferAction struct { Inputs []string // outputs to be created as a result of the transfer Outputs []*Output - // Metadata contains the action's metadata + // Metadata contains the transfer action's metadata Metadata map[string][]byte } diff --git a/token/core/fabtoken/validator_transfer.go b/token/core/fabtoken/validator_transfer.go index f127bac5b..dea1b940e 100644 --- a/token/core/fabtoken/validator_transfer.go +++ b/token/core/fabtoken/validator_transfer.go @@ -108,7 +108,7 @@ func TransferHTLCValidate(ctx *Context) error { if err != nil { return errors.Wrap(err, "failed to unmarshal owner of input token") } - // is it owned by a htlc script? + // 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.Action.GetOutputs()) != 1 { @@ -125,7 +125,7 @@ func TransferHTLCValidate(ctx *Context) error { return errors.Errorf("invalid transfer action: quantity of input does not match quantity of output") } if output.IsRedeem() { - return errors.Errorf("invalid transfer action: the output for corresponding to an htlc spending should not be a redeem") + return errors.Errorf("invalid transfer action: the output corresponding to an htlc spending should not be a redeem") } // check owner field diff --git a/token/core/zkatdlog/crypto/transfer/sender.go b/token/core/zkatdlog/crypto/transfer/sender.go index 8c1ca9890..b02e9b11f 100644 --- a/token/core/zkatdlog/crypto/transfer/sender.go +++ b/token/core/zkatdlog/crypto/transfer/sender.go @@ -111,7 +111,7 @@ type TransferAction struct { OutputTokens []*token.Token // ZK Proof that shows that the transfer is correct Proof []byte - // Metadata contains the action's metadata + // Metadata contains the transfer action's metadata Metadata map[string][]byte } diff --git a/token/driver/action.go b/token/driver/action.go index d11ff3108..3d437d390 100644 --- a/token/driver/action.go +++ b/token/driver/action.go @@ -51,7 +51,7 @@ type TransferAction interface { IsRedeemAt(index int) bool // SerializeOutputAt returns the serialized output at the passed index SerializeOutputAt(index int) ([]byte, error) - // GetInputs returns the inputs's identifiers of the action + // GetInputs returns the identifiers of the inputs in the action. GetInputs() ([]string, error) // IsGraphHiding returns true if the action is graph hiding IsGraphHiding() bool diff --git a/token/services/vault/keys/keys.go b/token/services/vault/keys/keys.go index 12beb5bfd..d5318523e 100644 --- a/token/services/vault/keys/keys.go +++ b/token/services/vault/keys/keys.go @@ -136,6 +136,9 @@ func CreateIssueActionMetadataKey(hash string) (string, error) { return CreateCompositeKey(TokenKeyPrefix, []string{IssueActionMetadata, hash}) } +// CreateTransferActionMetadataKey returns the transfer action metadata key built from the passed +// transaction id, subkey, and index. Index is used to make sure the key is unique with the respect to the +// token request this key appears. func CreateTransferActionMetadataKey(txID string, subKey string, index uint64) (string, error) { return CreateCompositeKey(TokenKeyPrefix, []string{TransferActionMetadata, txID, subKey, strconv.FormatUint(index, 10)}) } diff --git a/token/services/vault/translator/action.go b/token/services/vault/translator/action.go index 73607f73d..e11b0ef15 100644 --- a/token/services/vault/translator/action.go +++ b/token/services/vault/translator/action.go @@ -35,7 +35,7 @@ type TransferAction interface { IsRedeemAt(index int) bool // SerializeOutputAt returns the serialized output at the passed index SerializeOutputAt(index int) ([]byte, error) - // GetInputs returns the inputs's identifiers of the action + // GetInputs returns the identifiers of the inputs in the action. GetInputs() ([]string, error) // IsGraphHiding returns true if the action is graph hiding IsGraphHiding() bool From a5f3a0d411daec8abfc1e9ee2eae7e4906d2d260 Mon Sep 17 00:00:00 2001 From: Hagar Meir Date: Tue, 30 Aug 2022 11:12:05 +0300 Subject: [PATCH 16/17] fixup! fabtoken validator cleanup Signed-off-by: Hagar Meir --- token/core/common/backend.go | 4 +-- token/core/common/metadata.go | 6 ++-- token/core/fabtoken/actions.go | 2 +- token/core/fabtoken/validator.go | 5 ++-- token/core/fabtoken/validator_transfer.go | 30 +++++++++---------- token/core/interop/htlc/validator.go | 4 +-- .../zkatdlog/crypto/validator/validator.go | 3 +- .../crypto/validator/validator_transfer.go | 13 +++----- token/core/zkatdlog/nogh/deserializer.go | 3 +- token/driver/validator.go | 2 +- 10 files changed, 32 insertions(+), 40 deletions(-) diff --git a/token/core/common/backend.go b/token/core/common/backend.go index 9d7fd3e7d..36de2cd1d 100644 --- a/token/core/common/backend.go +++ b/token/core/common/backend.go @@ -13,7 +13,7 @@ import ( ) type Backend struct { - // Ledger to access the ledget state + // Ledger to access the ledger state Ledger driver.GetStateFnc // signed Message Message []byte @@ -31,7 +31,7 @@ func NewBackend(ledger driver.GetStateFnc, message []byte, sigs [][]byte) *Backe // the passed verifier func (b *Backend) HasBeenSignedBy(id view.Identity, verifier driver.Verifier) ([]byte, error) { if b.Cursor >= len(b.Sigs) { - return nil, errors.Errorf("invalid state, insufficient number of signatures") + return nil, errors.New("invalid state, insufficient number of signatures") } sigma := b.Sigs[b.Cursor] b.Cursor++ diff --git a/token/core/common/metadata.go b/token/core/common/metadata.go index ba2d85267..1f449ca09 100644 --- a/token/core/common/metadata.go +++ b/token/core/common/metadata.go @@ -9,7 +9,7 @@ package common import ( "strings" - token2 "github.com/hyperledger-labs/fabric-token-sdk/token" + "github.com/hyperledger-labs/fabric-token-sdk/token" ) // SetTransferActionMetadata extracts the transfer metadata from the passed attributes and @@ -19,8 +19,8 @@ func SetTransferActionMetadata(attrs map[interface{}]interface{}, metadata map[s k, ok1 := key.(string) v, ok2 := value.([]byte) if ok1 && ok2 { - if strings.HasPrefix(k, token2.TransferMetadataPrefix) { - mKey := strings.TrimPrefix(k, token2.TransferMetadataPrefix) + if strings.HasPrefix(k, token.TransferMetadataPrefix) { + mKey := strings.TrimPrefix(k, token.TransferMetadataPrefix) metadata[mKey] = v } } diff --git a/token/core/fabtoken/actions.go b/token/core/fabtoken/actions.go index 21ecc2d61..5313a8f31 100644 --- a/token/core/fabtoken/actions.go +++ b/token/core/fabtoken/actions.go @@ -188,7 +188,7 @@ func (t *TransferAction) Deserialize(raw []byte) error { return json.Unmarshal(raw, t) } -// GetMetadata returns the action's metadata +// GetMetadata returns the transfer action's metadata func (t *TransferAction) GetMetadata() map[string][]byte { return t.Metadata } diff --git a/token/core/fabtoken/validator.go b/token/core/fabtoken/validator.go index 1c57cc36a..cd7776650 100644 --- a/token/core/fabtoken/validator.go +++ b/token/core/fabtoken/validator.go @@ -10,9 +10,8 @@ import ( "bytes" "encoding/json" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" - "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" "github.com/pkg/errors" @@ -227,7 +226,7 @@ func (v *Validator) VerifyTransfers(ledger driver.Ledger, transferActions []*Tra return errors.Wrapf(err, "failed to retrieve input from transfer action at index %d", i) } // verify if input tokens and output tokens in the current transfer action have the same type - // verify if sum of input tokens in the current transfer action equals the sum of output tokens + // verify if sum of input tokens in the current transfer action equals the sum of output tokens // in the current transfer action if err := v.VerifyTransfer(ledger, inputTokens, t, signatureProvider); err != nil { return errors.Wrapf(err, "failed to verify transfer action at index %d", i) diff --git a/token/core/fabtoken/validator_transfer.go b/token/core/fabtoken/validator_transfer.go index dea1b940e..1ae524e78 100644 --- a/token/core/fabtoken/validator_transfer.go +++ b/token/core/fabtoken/validator_transfer.go @@ -54,13 +54,13 @@ func TransferSignatureValidate(ctx *Context) error { // TransferBalanceValidate checks that the sum of the inputs is equal to the sum of the outputs func TransferBalanceValidate(ctx *Context) error { if ctx.Action.NumOutputs() == 0 { - return errors.Errorf("there is no output") + return errors.New("there is no output") } if len(ctx.InputTokens) == 0 { - return errors.Errorf("there is no input") + return errors.New("there is no input") } if ctx.InputTokens[0] == nil { - return errors.Errorf("first input is nil") + return errors.New("first input is nil") } typ := ctx.InputTokens[0].Type inputSum := token.NewZeroQuantity(ctx.PP.QuantityPrecision) @@ -99,7 +99,7 @@ func TransferBalanceValidate(ctx *Context) error { return nil } -// TransferHTLCValidate checks the validity of the TransferHTLCValidate scripts, if any +// TransferHTLCValidate checks the validity of the HTLC scripts, if any func TransferHTLCValidate(ctx *Context) error { now := time.Now() @@ -112,20 +112,20 @@ func TransferHTLCValidate(ctx *Context) error { if owner.Type == htlc.ScriptType { // Then, the first output must be compatible with this input. if len(ctx.Action.GetOutputs()) != 1 { - return errors.Errorf("invalid transfer action: an htlc script only transfers the ownership of a token") + return errors.New("invalid transfer action: an htlc script only transfers the ownership of a token") } // check type and quantity output := ctx.Action.GetOutputs()[0].(*Output) tok := output.Output if ctx.InputTokens[0].Type != tok.Type { - return errors.Errorf("invalid transfer action: type of input does not match type of output") + return errors.New("invalid transfer action: type of input does not match type of output") } if ctx.InputTokens[0].Quantity != tok.Quantity { - return errors.Errorf("invalid transfer action: quantity of input does not match quantity of output") + return errors.New("invalid transfer action: quantity of input does not match quantity of output") } if output.IsRedeem() { - return errors.Errorf("invalid transfer action: the output corresponding to an htlc spending should not be a redeem") + return errors.New("invalid transfer action: the output corresponding to an htlc spending should not be a redeem") } // check owner field @@ -137,7 +137,7 @@ func TransferHTLCValidate(ctx *Context) error { // check metadata sigma := ctx.Signatures[i] if err := HTLCMetadataCheck(ctx, op, sigma); err != nil { - return errors.WithMessagef(err, "failed to check htlc metadata") + return errors.Wrapf(err, "failed to check htlc metadata") } } } @@ -145,13 +145,13 @@ func TransferHTLCValidate(ctx *Context) error { for _, o := range ctx.Action.GetOutputs() { out, ok := o.(*Output) if !ok { - return errors.Errorf("invalid output") + return errors.New("invalid output") } if out.IsRedeem() { continue } - // if it is a htlc script than the deadline must be still valid + // if it is an htlc script then the deadline must still be valid owner, err := identity.UnmarshallRawOwner(out.Output.Owner.Raw) if err != nil { return err @@ -163,7 +163,7 @@ func TransferHTLCValidate(ctx *Context) error { return err } if script.Deadline.Before(now) { - return errors.Errorf("htlc script invalid: expiration date has already passed") + return errors.New("htlc script invalid: expiration date has already passed") } continue } @@ -188,13 +188,13 @@ func HTLCMetadataCheck(ctx *Context, op htlc2.OperationType, sig []byte) error { return errors.New("expected a valid claim preImage and recipient signature") } - // Check the pre-image is in the action's metadata + // Check that the pre-image is in the action's metadata if len(ctx.Action.Metadata) == 0 { - return errors.Errorf("cannot find htlc pre-image, no metadata") + return errors.New("cannot find htlc pre-image, no metadata") } value, ok := ctx.Action.Metadata[htlc.ClaimPreImage] if !ok { - return errors.Errorf("cannot find htlc pre-image, missing metadata entry") + return errors.New("cannot find htlc pre-image, missing metadata entry") } if !bytes.Equal(value, claim.Preimage) { return errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) diff --git a/token/core/interop/htlc/validator.go b/token/core/interop/htlc/validator.go index 80e695510..bcbd34e12 100644 --- a/token/core/interop/htlc/validator.go +++ b/token/core/interop/htlc/validator.go @@ -38,13 +38,13 @@ func VerifyOwner(senderRawOwner []byte, outRawOwner []byte, now time.Time) (*htl if now.Before(script.Deadline) { // this should be a claim if !script.Recipient.Equal(outRawOwner) { - return nil, None, errors.Errorf("owner of output token does not correspond to recipient in htlc request") + return nil, None, errors.New("owner of output token does not correspond to recipient in htlc request") } return script, Claim, nil } else { // this should be a reclaim if !script.Sender.Equal(outRawOwner) { - return nil, None, errors.Errorf("owner of output token does not correspond to sender in htlc request") + return nil, None, errors.New("owner of output token does not correspond to sender in htlc request") } return script, Reclaim, nil } diff --git a/token/core/zkatdlog/crypto/validator/validator.go b/token/core/zkatdlog/crypto/validator/validator.go index b4d40f122..f6f25e383 100644 --- a/token/core/zkatdlog/crypto/validator/validator.go +++ b/token/core/zkatdlog/crypto/validator/validator.go @@ -9,11 +9,10 @@ package validator import ( "bytes" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" - "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto" issue2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/issue" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto/transfer" diff --git a/token/core/zkatdlog/crypto/validator/validator_transfer.go b/token/core/zkatdlog/crypto/validator/validator_transfer.go index 06fcd76a4..4d01ebf57 100644 --- a/token/core/zkatdlog/crypto/validator/validator_transfer.go +++ b/token/core/zkatdlog/crypto/validator/validator_transfer.go @@ -11,16 +11,14 @@ import ( "encoding/json" "time" - "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" - - "github.com/hyperledger-labs/fabric-token-sdk/token/driver" - math "github.com/IBM/mathlib" + "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" htlc2 "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto" "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/interop/htlc" "github.com/pkg/errors" ) @@ -109,9 +107,6 @@ func TransferHTLCValidate(ctx *Context) error { } out := ctx.Action.GetOutputs()[0].(*token.Token) - if ctx.InputTokens[0].Data.Equals(out.Data) { - return errors.Errorf("invalid transfer action: content of input does not match content of output") - } // check that owner field in output is correct _, op, err := htlc2.VerifyOwner(ctx.InputTokens[0].Owner, out.Owner, now) @@ -173,11 +168,11 @@ func HTLCMetadataCheck(ctx *Context, op htlc2.OperationType, sig []byte) error { // Check the pre-image is in the action's metadata if len(ctx.Action.Metadata) == 0 { - return errors.Errorf("cannot find htlc pre-image, no metadata") + return errors.New("cannot find htlc pre-image, no metadata") } value, ok := ctx.Action.Metadata[htlc.ClaimPreImage] if !ok { - return errors.Errorf("cannot find htlc pre-image, missing metadata entry") + return errors.New("cannot find htlc pre-image, missing metadata entry") } if !bytes.Equal(value, claim.Preimage) { return errors.Errorf("invalid action, cannot match htlc pre-image with metadata [%x]!=[%x]", value, claim.Preimage) diff --git a/token/core/zkatdlog/nogh/deserializer.go b/token/core/zkatdlog/nogh/deserializer.go index 3ffb18a67..8e4694a35 100644 --- a/token/core/zkatdlog/nogh/deserializer.go +++ b/token/core/zkatdlog/nogh/deserializer.go @@ -11,13 +11,12 @@ import ( "encoding/json" "sync" - "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" - idemix2 "github.com/hyperledger-labs/fabric-smart-client/platform/fabric/core/generic/msp/idemix" "github.com/hyperledger-labs/fabric-smart-client/platform/view/view" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity/msp/idemix" "github.com/hyperledger-labs/fabric-token-sdk/token/core/identity/msp/x509" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/interop/htlc" "github.com/hyperledger-labs/fabric-token-sdk/token/core/zkatdlog/crypto" "github.com/hyperledger-labs/fabric-token-sdk/token/driver" "github.com/pkg/errors" diff --git a/token/driver/validator.go b/token/driver/validator.go index ae53c097c..50932407f 100644 --- a/token/driver/validator.go +++ b/token/driver/validator.go @@ -18,7 +18,7 @@ type Ledger interface { } type SignatureProvider interface { - // HasBeenSignedBy returns true and signature verified if the provider contains a valid signature for the passed identity and verifier + // HasBeenSignedBy returns true and the verified signature if the provider contains a valid signature for the passed identity and verifier HasBeenSignedBy(id view.Identity, verifier Verifier) ([]byte, error) // Signatures returns the signatures inside this provider Signatures() [][]byte From e03842a9bc8e6a0a8697d522832aca49cc9124c0 Mon Sep 17 00:00:00 2001 From: Hagar Meir Date: Tue, 30 Aug 2022 11:18:57 +0300 Subject: [PATCH 17/17] fixup! fabtoken validator cleanup Signed-off-by: Hagar Meir --- token/services/certifier/interactive/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/token/services/certifier/interactive/service.go b/token/services/certifier/interactive/service.go index 6e24fc5e3..38d90b8be 100644 --- a/token/services/certifier/interactive/service.go +++ b/token/services/certifier/interactive/service.go @@ -169,7 +169,7 @@ func (i *CertificationRequestView) Call(context view.Context) (interface{}, erro return nil, errors.WithMessagef(err, "failed receiving certifications [%v] from [%s]", i.ids, i.certifier) } - // 4. TransferBalanceValidate response + // 4. Validate response logger.Debugf("validate certification request response for [%v]", i.ids) if err := cm.VerifyCertifications(i.ids, certifications); err != nil { logger.Errorf("failed verifying certifications of [%v] from [%s] with err [%s]", i.ids, i.certifier, err)