From 1cb914693729e37421c246756bf87c603a714ca4 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Mon, 10 Jul 2023 17:06:34 +0200 Subject: [PATCH 1/3] This PR does the following: - It identifies an attack that allows the attacker to spend the same token in multiple actions via an update to the existing integration test - It fixes the bug Signed-off-by: Angelo De Caro --- integration/token/fungible/dlog/dlog_test.go | 3 +-- integration/token/fungible/support.go | 3 ++- integration/token/fungible/tests.go | 7 +++++-- integration/token/fungible/views/transfer.go | 1 + token/request.go | 13 ++++++++++++ token/services/vault/translator/translator.go | 21 ++++++++++++++++--- 6 files changed, 40 insertions(+), 8 deletions(-) diff --git a/integration/token/fungible/dlog/dlog_test.go b/integration/token/fungible/dlog/dlog_test.go index e04bb9fbe..c86cba0f7 100644 --- a/integration/token/fungible/dlog/dlog_test.go +++ b/integration/token/fungible/dlog/dlog_test.go @@ -9,9 +9,8 @@ package dlog import ( "os" - "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" - "github.com/hyperledger-labs/fabric-smart-client/integration" + "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash" "github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token" "github.com/hyperledger-labs/fabric-token-sdk/integration/nwo/token/topology" "github.com/hyperledger-labs/fabric-token-sdk/integration/token/fungible" diff --git a/integration/token/fungible/support.go b/integration/token/fungible/support.go index 158ba3182..e122ffd57 100644 --- a/integration/token/fungible/support.go +++ b/integration/token/fungible/support.go @@ -247,7 +247,7 @@ func TransferCash(network *integration.Infrastructure, id string, wallet string, return "" } -func TransferCashMultiActions(network *integration.Infrastructure, id string, wallet string, typ string, amounts []uint64, receivers []string, auditor string, expectedErrorMsgs ...string) string { +func TransferCashMultiActions(network *integration.Infrastructure, id string, wallet string, typ string, amounts []uint64, receivers []string, auditor string, tokenID *token.ID, expectedErrorMsgs ...string) string { Expect(len(amounts) > 1).To(BeTrue()) Expect(len(receivers)).To(BeEquivalentTo(len(amounts))) transfer := &views.Transfer{ @@ -257,6 +257,7 @@ func TransferCashMultiActions(network *integration.Infrastructure, id string, wa Amount: amounts[0], Recipient: network.Identity(receivers[0]), RecipientEID: receivers[0], + TokenIDs: []*token.ID{tokenID}, } for i := 1; i < len(amounts); i++ { transfer.TransferAction = append(transfer.TransferAction, views.TransferAction{ diff --git a/integration/token/fungible/tests.go b/integration/token/fungible/tests.go index 8f6bc06f4..65dda2923 100644 --- a/integration/token/fungible/tests.go +++ b/integration/token/fungible/tests.go @@ -382,11 +382,14 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta // test multi action transfer... t13 := time.Now() - IssueCash(network, "", "LIRA", 3, "alice", auditor, true, "issuer") + txIssuedLira1 := IssueCash(network, "", "LIRA", 3, "alice", auditor, true, "issuer") IssueCash(network, "", "LIRA", 3, "alice", auditor, true, "issuer") t14 := time.Now() CheckAuditedTransactions(network, auditor, AuditedTransactions[10:12], &t13, &t14) - txLiraTransfer := TransferCashMultiActions(network, "alice", "", "LIRA", []uint64{2, 3}, []string{"bob", "charlie"}, auditor) + // use the same token for both actions, this must fail + TransferCashMultiActions(network, "alice", "", "LIRA", []uint64{2, 3}, []string{"bob", "charlie"}, auditor, &token2.ID{TxId: txIssuedLira1}, "failed to append spent id", txIssuedLira1) + // perform the normal transaction + txLiraTransfer := TransferCashMultiActions(network, "alice", "", "LIRA", []uint64{2, 3}, []string{"bob", "charlie"}, auditor, nil) t16 := time.Now() AuditedTransactions[12].TxID = txLiraTransfer AuditedTransactions[13].TxID = txLiraTransfer diff --git a/integration/token/fungible/views/transfer.go b/integration/token/fungible/views/transfer.go index dad043424..259ab4ee4 100644 --- a/integration/token/fungible/views/transfer.go +++ b/integration/token/fungible/views/transfer.go @@ -122,6 +122,7 @@ func (t *TransferView) Call(context view.Context) (interface{}, error) { t.Type, []uint64{action.Amount}, []view.Identity{additionalRecipients[i]}, + token2.WithTokenIDs(t.TokenIDs...), ) assert.NoError(err, "failed adding transfer action [%d:%d]", action.Amount, action.Recipient) } diff --git a/token/request.go b/token/request.go index 2eef99315..14d9816f2 100644 --- a/token/request.go +++ b/token/request.go @@ -1093,6 +1093,9 @@ func (r *Request) prepareTransfer(redeem bool, wallet *OwnerWallet, tokenType st var tokenIDs []*token.ID var inputSum token.Quantity var err error + + transferOpts.TokenIDs = r.cleanupInputIDs(transferOpts.TokenIDs) + // if inputs have been passed, parse and certify them, if needed if len(transferOpts.TokenIDs) != 0 { tokenIDs, inputSum, tokenType, err = r.parseInputIDs(transferOpts.TokenIDs) @@ -1193,6 +1196,16 @@ func (r *Request) genOutputs(values []uint64, owners []view.Identity, tokenType return outputTokens, outputSum, nil } +func (r *Request) cleanupInputIDs(ds []*token.ID) []*token.ID { + newSlice := make([]*token.ID, 0, len(ds)) + for _, item := range ds { + if item != nil { + newSlice = append(newSlice, item) + } + } + return newSlice +} + type requestSer struct { TxID string Actions []byte diff --git a/token/services/vault/translator/translator.go b/token/services/vault/translator/translator.go index 2c081c889..1769e26f7 100644 --- a/token/services/vault/translator/translator.go +++ b/token/services/vault/translator/translator.go @@ -24,7 +24,7 @@ type Translator struct { RWSet RWSet TxID string // SpentIDs the spent IDs added so far - SpentIDs [][]byte + SpentIDs []string counter uint64 namespace string @@ -385,7 +385,9 @@ func (w *Translator) spendTokens(ids []string, graphHiding bool) error { if err != nil { return errors.Wrapf(err, "failed to delete output %s", id) } - w.SpentIDs = append(w.SpentIDs, []byte(id)) + if err := w.appendSpentID(id); err != nil { + return errors.Wrapf(err, "failed to append spent id [%s]", id) + } } } else { for _, id := range ids { @@ -394,7 +396,9 @@ func (w *Translator) spendTokens(ids []string, graphHiding bool) error { if err != nil { return errors.Wrapf(err, "failed to add serial number %s", id) } - w.SpentIDs = append(w.SpentIDs, []byte(id)) + if err := w.appendSpentID(id); err != nil { + return errors.Wrapf(err, "failed to append spent id [%s]", id) + } } } @@ -425,3 +429,14 @@ func (w *Translator) areTokensSpent(ids []string, graphHiding bool) ([]bool, err return res, nil } + +func (w *Translator) appendSpentID(id string) error { + // check first it is already in the list + for _, d := range w.SpentIDs { + if d == id { + return errors.Errorf("[%s] already spent", id) + } + } + w.SpentIDs = append(w.SpentIDs, id) + return nil +} From d14a6e54b3bc267714e7ddc5e9e279fada15893b Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Tue, 11 Jul 2023 09:58:43 +0200 Subject: [PATCH 2/3] minor fix Signed-off-by: Angelo De Caro --- integration/token/fungible/support.go | 2 +- integration/token/fungible/tests.go | 24 +++++++++++++++++--- integration/token/fungible/views/transfer.go | 10 ++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/integration/token/fungible/support.go b/integration/token/fungible/support.go index e122ffd57..fdfba7840 100644 --- a/integration/token/fungible/support.go +++ b/integration/token/fungible/support.go @@ -296,7 +296,7 @@ func TransferCashMultiActions(network *integration.Infrastructure, id string, wa Expect(err.Error()).To(ContainSubstring(msg), "err [%s] should contain [%s]", err.Error(), msg) } time.Sleep(5 * time.Second) - return "" + return common.JSONUnmarshalString(txidBoxed) } func PrepareTransferCash(network *integration.Infrastructure, id string, wallet string, typ string, amount uint64, receiver string, auditor string, tokenID *token.ID, expectedErrorMsgs ...string) (string, []byte) { diff --git a/integration/token/fungible/tests.go b/integration/token/fungible/tests.go index 65dda2923..56c66dc85 100644 --- a/integration/token/fungible/tests.go +++ b/integration/token/fungible/tests.go @@ -382,12 +382,10 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta // test multi action transfer... t13 := time.Now() - txIssuedLira1 := IssueCash(network, "", "LIRA", 3, "alice", auditor, true, "issuer") + IssueCash(network, "", "LIRA", 3, "alice", auditor, true, "issuer") IssueCash(network, "", "LIRA", 3, "alice", auditor, true, "issuer") t14 := time.Now() CheckAuditedTransactions(network, auditor, AuditedTransactions[10:12], &t13, &t14) - // use the same token for both actions, this must fail - TransferCashMultiActions(network, "alice", "", "LIRA", []uint64{2, 3}, []string{"bob", "charlie"}, auditor, &token2.ID{TxId: txIssuedLira1}, "failed to append spent id", txIssuedLira1) // perform the normal transaction txLiraTransfer := TransferCashMultiActions(network, "alice", "", "LIRA", []uint64{2, 3}, []string{"bob", "charlie"}, auditor, nil) t16 := time.Now() @@ -399,6 +397,26 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta CheckBalanceAndHolding(network, "charlie", "", "LIRA", 3, auditor) CheckAuditedTransactions(network, auditor, AuditedTransactions[:], &t0, &t16) + // Check double spending by multiple action in the same transaction + + // use the same token for both actions, this must fail + txIssuedPineapples1 := IssueCash(network, "", "Pineapples", 3, "alice", auditor, true, "issuer") + IssueCash(network, "", "Pineapples", 3, "alice", auditor, true, "issuer") + failedTransferTxID := TransferCashMultiActions(network, "alice", "", "Pineapples", []uint64{2, 3}, []string{"bob", "charlie"}, auditor, &token2.ID{TxId: txIssuedPineapples1}, "failed to append spent id", txIssuedPineapples1) + // the above transfer must fail at execution phase, therefore the auditor should be explicitly informed about this transaction + CheckBalance(network, "alice", "", "Pineapples", 6) + CheckHolding(network, "alice", "", "Pineapples", 1, auditor) + CheckBalance(network, "bob", "", "Pineapples", 0) + CheckHolding(network, "bob", "", "Pineapples", 2, auditor) + CheckBalance(network, "charlie", "", "Pineapples", 0) + CheckHolding(network, "charlie", "", "Pineapples", 3, auditor) + SetTransactionAuditStatus(network, auditor, failedTransferTxID, ttx.Deleted) + CheckBalance(network, "alice", "", "Pineapples", 6) + CheckHolding(network, "alice", "", "Pineapples", 0, auditor) + CheckBalanceAndHolding(network, "bob", "", "Pineapples", 0, auditor) + CheckBalanceAndHolding(network, "charlie", "", "Pineapples", 0, auditor) + + // continue with the rest of the test IssueCash(network, "", "USD", 1, "alice", auditor, true, "issuer") testTwoGeneratedOwnerWalletsSameNode(network, auditor) diff --git a/integration/token/fungible/views/transfer.go b/integration/token/fungible/views/transfer.go index 259ab4ee4..2f42f5dd3 100644 --- a/integration/token/fungible/views/transfer.go +++ b/integration/token/fungible/views/transfer.go @@ -59,7 +59,13 @@ type TransferView struct { *Transfer } -func (t *TransferView) Call(context view.Context) (interface{}, error) { +func (t *TransferView) Call(context view.Context) (txID interface{}, err error) { + var tx *ttx.Transaction + defer func() { + if txID == "" && tx != nil { + txID = tx.ID() + } + }() // As a first step operation, the sender contacts the recipient's FSC node // to ask for the identity to use to assign ownership of the freshly created token. // Notice that, this step would not be required if the sender knew already which @@ -87,7 +93,7 @@ func (t *TransferView) Call(context view.Context) (interface{}, error) { // At this point, the sender is ready to prepare the token transaction. // The sender creates an anonymous transaction (this means that the resulting Fabric transaction will be signed using idemix, for example), // and specify the auditor that must be contacted to approve the operation. - tx, err := ttx.NewAnonymousTransaction( + tx, err = ttx.NewAnonymousTransaction( context, ttx.WithAuditor(view2.GetIdentityProvider(context).Identity(t.Auditor)), ) From 78d3bb476c508a9076b56d60dd2537c7722c5c87 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Tue, 11 Jul 2023 15:46:25 +0200 Subject: [PATCH 3/3] fixup! minor fix Signed-off-by: Angelo De Caro --- integration/token/fungible/support.go | 20 ++++-- integration/token/fungible/tests.go | 75 ++++++++++++-------- integration/token/fungible/views/transfer.go | 10 +-- 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/integration/token/fungible/support.go b/integration/token/fungible/support.go index fdfba7840..d22989260 100644 --- a/integration/token/fungible/support.go +++ b/integration/token/fungible/support.go @@ -296,7 +296,11 @@ func TransferCashMultiActions(network *integration.Infrastructure, id string, wa Expect(err.Error()).To(ContainSubstring(msg), "err [%s] should contain [%s]", err.Error(), msg) } time.Sleep(5 * time.Second) - return common.JSONUnmarshalString(txidBoxed) + // extract txID from err + strErr := err.Error() + s := strings.LastIndex(strErr, "[<<<") + e := strings.LastIndex(strErr, ">>>]") + return strErr[s+4 : e] } func PrepareTransferCash(network *integration.Infrastructure, id string, wallet string, typ string, amount uint64, receiver string, auditor string, tokenID *token.ID, expectedErrorMsgs ...string) (string, []byte) { @@ -529,15 +533,21 @@ func CheckOwnerDB(network *integration.Infrastructure, expectedErrors []string, } } -func CheckAuditorDB(network *integration.Infrastructure, auditorID string, walletID string) { +func CheckAuditorDB(network *integration.Infrastructure, auditorID string, walletID string, errorCheck func([]string) error) { errorMessagesBoxed, err := network.Client(auditorID).CallView("CheckTTXDB", common.JSONMarshall(&views.CheckTTXDB{ Auditor: true, AuditorWalletID: walletID, })) Expect(err).NotTo(HaveOccurred()) - var errorMessages []string - common.JSONUnmarshal(errorMessagesBoxed.([]byte), &errorMessages) - Expect(len(errorMessages)).To(Equal(0), "expected 0 error messages, got [% v]", errorMessages) + if errorCheck != nil { + var errorMessages []string + common.JSONUnmarshal(errorMessagesBoxed.([]byte), &errorMessages) + Expect(errorCheck(errorMessages)).NotTo(HaveOccurred(), "failed to check errors") + } else { + var errorMessages []string + common.JSONUnmarshal(errorMessagesBoxed.([]byte), &errorMessages) + Expect(len(errorMessages)).To(Equal(0), "expected 0 error messages, got [% v]", errorMessages) + } } func PruneInvalidUnspentTokens(network *integration.Infrastructure, ids ...string) { diff --git a/integration/token/fungible/tests.go b/integration/token/fungible/tests.go index 56c66dc85..ac4757dd8 100644 --- a/integration/token/fungible/tests.go +++ b/integration/token/fungible/tests.go @@ -8,6 +8,7 @@ package fungible import ( "crypto/rand" + "fmt" "math/big" "strings" "time" @@ -22,6 +23,7 @@ import ( "github.com/hyperledger-labs/fabric-token-sdk/token/services/ttxdb" token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token" . "github.com/onsi/gomega" + "github.com/pkg/errors" ) var AuditedTransactions = []*ttxdb.TransactionRecord{ @@ -397,26 +399,6 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta CheckBalanceAndHolding(network, "charlie", "", "LIRA", 3, auditor) CheckAuditedTransactions(network, auditor, AuditedTransactions[:], &t0, &t16) - // Check double spending by multiple action in the same transaction - - // use the same token for both actions, this must fail - txIssuedPineapples1 := IssueCash(network, "", "Pineapples", 3, "alice", auditor, true, "issuer") - IssueCash(network, "", "Pineapples", 3, "alice", auditor, true, "issuer") - failedTransferTxID := TransferCashMultiActions(network, "alice", "", "Pineapples", []uint64{2, 3}, []string{"bob", "charlie"}, auditor, &token2.ID{TxId: txIssuedPineapples1}, "failed to append spent id", txIssuedPineapples1) - // the above transfer must fail at execution phase, therefore the auditor should be explicitly informed about this transaction - CheckBalance(network, "alice", "", "Pineapples", 6) - CheckHolding(network, "alice", "", "Pineapples", 1, auditor) - CheckBalance(network, "bob", "", "Pineapples", 0) - CheckHolding(network, "bob", "", "Pineapples", 2, auditor) - CheckBalance(network, "charlie", "", "Pineapples", 0) - CheckHolding(network, "charlie", "", "Pineapples", 3, auditor) - SetTransactionAuditStatus(network, auditor, failedTransferTxID, ttx.Deleted) - CheckBalance(network, "alice", "", "Pineapples", 6) - CheckHolding(network, "alice", "", "Pineapples", 0, auditor) - CheckBalanceAndHolding(network, "bob", "", "Pineapples", 0, auditor) - CheckBalanceAndHolding(network, "charlie", "", "Pineapples", 0, auditor) - - // continue with the rest of the test IssueCash(network, "", "USD", 1, "alice", auditor, true, "issuer") testTwoGeneratedOwnerWalletsSameNode(network, auditor) @@ -468,7 +450,7 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta CheckBalanceAndHolding(network, "issuer", "issuer.owner", "EUR", 10, auditor) CheckOwnerDB(network, nil, "issuer", "alice", "bob", "charlie", "manager") - CheckAuditorDB(network, auditor, "") + CheckAuditorDB(network, auditor, "", nil) // Check double spending txIDPine := IssueCash(network, "", "PINE", 55, "alice", auditor, true, "issuer") @@ -509,7 +491,7 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta CheckBalanceAndHolding(network, "alice", "", "PINE", 0, auditor) CheckBalanceAndHolding(network, "bob", "", "PINE", 55, auditor) CheckOwnerDB(network, nil, "issuer", "alice", "bob", "charlie", "manager") - CheckAuditorDB(network, auditor, "") + CheckAuditorDB(network, auditor, "", nil) // Test Auditor ability to override transaction state txID3, tx3 := PrepareTransferCash(network, "bob", "", "PINE", 10, "alice", auditor, nil) @@ -527,11 +509,11 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta // Restart CheckOwnerDB(network, nil, "bob", "alice") CheckOwnerDB(network, nil, "issuer", "charlie", "manager") - CheckAuditorDB(network, auditor, "") + CheckAuditorDB(network, auditor, "", nil) Restart(network, false, "alice", "bob", "charlie", "manager") CheckOwnerDB(network, nil, "bob", "alice") CheckOwnerDB(network, nil, "issuer", "charlie", "manager") - CheckAuditorDB(network, auditor, "") + CheckAuditorDB(network, auditor, "", nil) // Addition transfers TransferCash(network, "issuer", "", "USD", 50, "issuer", auditor) @@ -699,16 +681,16 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta }() } // collect the errors, and check that they are all nil, and one of them is the error we expect. - var errors []error + var errs []error for _, transfer := range transferErrors { - errors = append(errors, <-transfer) + errs = append(errs, <-transfer) } - Expect((errors[0] == nil && errors[1] != nil) || (errors[0] != nil && errors[1] == nil)).To(BeTrue()) + Expect((errs[0] == nil && errs[1] != nil) || (errs[0] != nil && errs[1] == nil)).To(BeTrue()) var errStr string - if errors[0] == nil { - errStr = errors[1].Error() + if errs[0] == nil { + errStr = errs[1].Error() } else { - errStr = errors[0].Error() + errStr = errs[0].Error() } v := strings.Contains(errStr, "pineapple") || strings.Contains(errStr, "lemonade") Expect(v).To(BeEquivalentTo(true)) @@ -740,7 +722,7 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta // Check consistency CheckPublicParams(network, "issuer", auditor, "alice", "bob", "charlie", "manager") CheckOwnerDB(network, nil, "bob", "alice", "issuer", "charlie", "manager") - CheckAuditorDB(network, auditor, "") + CheckAuditorDB(network, auditor, "", nil) PruneInvalidUnspentTokens(network, "issuer", auditor, "alice", "bob", "charlie", "manager") for _, name := range []string{"alice", "bob", "charlie", "manager"} { @@ -748,6 +730,37 @@ func TestAll(network *integration.Infrastructure, auditor string, onAuditorResta CheckIfExistsInVault(network, auditor, IDs) } + // Check double spending by multiple action in the same transaction + + // use the same token for both actions, this must fail + txIssuedPineapples1 := IssueCash(network, "", "Pineapples", 3, "alice", auditor, true, "issuer") + IssueCash(network, "", "Pineapples", 3, "alice", auditor, true, "issuer") + failedTransferTxID := TransferCashMultiActions(network, "alice", "", "Pineapples", []uint64{2, 3}, []string{"bob", "charlie"}, auditor, &token2.ID{TxId: txIssuedPineapples1}, "failed to append spent id", txIssuedPineapples1) + // the above transfer must fail at execution phase, therefore the auditor should be explicitly informed about this transaction + CheckBalance(network, "alice", "", "Pineapples", 6) + CheckHolding(network, "alice", "", "Pineapples", 1, auditor) + CheckBalance(network, "bob", "", "Pineapples", 0) + CheckHolding(network, "bob", "", "Pineapples", 2, auditor) + CheckBalance(network, "charlie", "", "Pineapples", 0) + CheckHolding(network, "charlie", "", "Pineapples", 3, auditor) + fmt.Printf("failed transaction [%s]\n", failedTransferTxID) + SetTransactionAuditStatus(network, auditor, failedTransferTxID, ttx.Deleted) + CheckBalanceAndHolding(network, "alice", "", "Pineapples", 6, auditor) + CheckBalanceAndHolding(network, "bob", "", "Pineapples", 0, auditor) + CheckBalanceAndHolding(network, "charlie", "", "Pineapples", 0, auditor) + CheckAuditorDB(network, auditor, "", func(errs []string) error { + // We should expect 6 errors, 3 records (Alice->Bob, Alice->Charlie, Alice-Alice (the rest) * 2 (envelope non found, no match in vault) + // each error should contain failedTransferTxID + if len(errs) != 6 { + return errors.Errorf("expected only 1 error, got [%d]", len(errs)) + } + for _, err := range errs { + if !strings.Contains(err, failedTransferTxID) { + return errors.Errorf("expected error to contain [%s], got [%s]", failedTransferTxID, err) + } + } + return nil + }) } func TestPublicParamsUpdate(network *integration.Infrastructure, auditor string, ppBytes []byte, tms *topology.TMS, issuerAsAuditor bool) { diff --git a/integration/token/fungible/views/transfer.go b/integration/token/fungible/views/transfer.go index 2f42f5dd3..fe988bd0a 100644 --- a/integration/token/fungible/views/transfer.go +++ b/integration/token/fungible/views/transfer.go @@ -60,12 +60,6 @@ type TransferView struct { } func (t *TransferView) Call(context view.Context) (txID interface{}, err error) { - var tx *ttx.Transaction - defer func() { - if txID == "" && tx != nil { - txID = tx.ID() - } - }() // As a first step operation, the sender contacts the recipient's FSC node // to ask for the identity to use to assign ownership of the freshly created token. // Notice that, this step would not be required if the sender knew already which @@ -93,7 +87,7 @@ func (t *TransferView) Call(context view.Context) (txID interface{}, err error) // At this point, the sender is ready to prepare the token transaction. // The sender creates an anonymous transaction (this means that the resulting Fabric transaction will be signed using idemix, for example), // and specify the auditor that must be contacted to approve the operation. - tx, err = ttx.NewAnonymousTransaction( + tx, err := ttx.NewAnonymousTransaction( context, ttx.WithAuditor(view2.GetIdentityProvider(context).Identity(t.Auditor)), ) @@ -147,7 +141,7 @@ func (t *TransferView) Call(context view.Context) (txID interface{}, err error) // Depending on the token driver implementation, the recipient's signature might or might not be needed to make // the token transaction valid. _, err = context.RunView(ttx.NewCollectEndorsementsView(tx)) - assert.NoError(err, "failed to sign transaction") + assert.NoError(err, "failed to sign transaction [<<<%s>>>]", tx.ID()) // Sanity checks: // - the transaction is in busy state in the vault