From b6d129ca2acc5ed8d75dbd3c7bbc349dbe562b7e Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Wed, 5 May 2021 19:51:16 +0000 Subject: [PATCH 01/16] Initial checkin of Fabric Interop Node SDK for HTLC Asset Exchanges Code and partial unit test to trigger asset locking. Signed-off-by: VRamakrishna --- .../interoperation-node-sdk/package.json | 2 +- .../src/AssetManager.ts | 96 +++++++++++++++++++ .../test/AssetManager.js | 76 +++++++++++++++ 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 sdks/fabric/interoperation-node-sdk/src/AssetManager.ts create mode 100644 sdks/fabric/interoperation-node-sdk/test/AssetManager.js diff --git a/sdks/fabric/interoperation-node-sdk/package.json b/sdks/fabric/interoperation-node-sdk/package.json index ab09385ad..23bfb52d1 100644 --- a/sdks/fabric/interoperation-node-sdk/package.json +++ b/sdks/fabric/interoperation-node-sdk/package.json @@ -13,7 +13,7 @@ "types": "./types/index.d.ts", "scripts": { "test": "nyc mocha -r ts-node/register --exclude 'test/data/**/*.js' --recursive -t 10000", - "protos": "grpc_tools_node_protoc --proto_path=protos --proto_path=fabric-protos --js_out=import_style=commonjs,binary:protos-js/ --grpc_out=grpc_js:protos-js/ --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` protos/relay/datatransfer.proto protos/networks/networks.proto protos/driver/driver.proto protos/common/ack.proto protos/common/query.proto protos/fabric/view_data.proto protos/common/state.proto protos/common/proofs.proto protos/common/verification_policy.proto fabric-protos/peer/proposal_response.proto fabric-protos/peer/proposal.proto fabric-protos/peer/chaincode.proto fabric-protos/common/policies.proto fabric-protos/msp/msp_principal.proto && protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=./protos-js -I ./protos -I ./fabric-protos protos/relay/datatransfer.proto protos/networks/networks.proto protos/driver/driver.proto protos/common/ack.proto protos/common/query.proto protos/fabric/view_data.proto protos/common/state.proto protos/common/proofs.proto protos/common/verification_policy.proto fabric-protos/peer/proposal_response.proto fabric-protos/peer/proposal.proto fabric-protos/peer/chaincode.proto fabric-protos/common/policies.proto fabric-protos/msp/msp_principal.proto", + "protos": "grpc_tools_node_protoc --proto_path=protos --proto_path=fabric-protos --js_out=import_style=commonjs,binary:protos-js/ --grpc_out=grpc_js:protos-js/ --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` protos/relay/datatransfer.proto protos/networks/networks.proto protos/driver/driver.proto protos/common/asset_locks.proto protos/common/ack.proto protos/common/query.proto protos/fabric/view_data.proto protos/common/state.proto protos/common/proofs.proto protos/common/verification_policy.proto fabric-protos/peer/proposal_response.proto fabric-protos/peer/proposal.proto fabric-protos/peer/chaincode.proto fabric-protos/common/policies.proto fabric-protos/msp/msp_principal.proto && protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=./protos-js -I ./protos -I ./fabric-protos protos/relay/datatransfer.proto protos/networks/networks.proto protos/driver/driver.proto protos/common/asset_locks.proto protos/common/ack.proto protos/common/query.proto protos/fabric/view_data.proto protos/common/state.proto protos/common/proofs.proto protos/common/verification_policy.proto fabric-protos/peer/proposal_response.proto fabric-protos/peer/proposal.proto fabric-protos/peer/chaincode.proto fabric-protos/common/policies.proto fabric-protos/msp/msp_principal.proto", "build": "tsc && cp -r protos-js build/", "prepublishOnly": "npm test", "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix", diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts new file mode 100644 index 000000000..e1ae8bf24 --- /dev/null +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -0,0 +1,96 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This file provides helper functions for interoperability operations. + **/ +/** End file docs */ + +import log4js from "log4js"; +import crypto from "crypto"; +import fabproto6 from "fabric-protos"; +import * as helpers from "./helpers"; +import assetLocksPb from "../protos-js/common/asset_locks_pb"; +import { Contract } from "fabric-network"; +const logger = log4js.getLogger("InteroperableHelper"); + + +/** + * First step of a Hashed Time Lock Contract + * - Lock a unique asset instance using a hash + **/ +const createHTLC = async ( + contract: Contract, + assetType: string, + assetID: string, + recipientID: string, + hashValue: string, + expiryTimeSecs: number, +): Promise<{ preimage: any; result: any }> => { + + let defaultExpiryTimeSecs = 0, preimage = ""; + if (hashValue && hashValue.length > 0) + { + defaultExpiryTimeSecs = 5 * 60 // 5 mins: this is the 't' used to time out the second contract of the HTLC pair + } + else + { + defaultExpiryTimeSecs = 10 * 60 // 10 mins: this is the '2t' used to time out the first contract of the HTLC pair + + // Create a random preimage of 20 bytes + for (let i = 0 ; i < 20 ; i++) + { + preimage += String.fromCharCode(Math.floor(Math.random() * 256)) + } + + // Hash the preimage + hashValue = crypto.createHash('sha256').update(preimage).digest('base64'); + } + const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds + if (expiryTimeSecs <= currTimeSecs) + { + logger.warn("Supplied HTLC expiry time is invalid or in the past: %d", expiryTimeSecs) + expiryTimeSecs = currTimeSecs + defaultExpiryTimeSecs + } + + // Create an asset exchange agreement structure + const assetExchangeAgreement = new assetLocksPb.AssetExchangeAgreement(); + assetExchangeAgreement.setType(assetType); + assetExchangeAgreement.setId(assetID); + assetExchangeAgreement.setRecipient(recipientID); + const assetExchangeAgreementStr = assetExchangeAgreement.serializeBinary().toString(); + + // Create an asset lock structure + const lockInfoHTLC = new assetLocksPb.AssetLockHTLC(); + lockInfoHTLC.setHash(hashValue); + lockInfoHTLC.setExpirytimesecs(expiryTimeSecs); + lockInfoHTLC.setTimespec(assetLocksPb.AssetLockHTLC.TimeSpec.EPOCH) + const lockInfoHTLCSerialized = lockInfoHTLC.serializeBinary(); + const lockInfo = new assetLocksPb.AssetLock(); + lockInfo.setLockmechanism(assetLocksPb.LockMechanism.HTLC); + lockInfo.setLockinfo(lockInfoHTLCSerialized); + const lockInfoStr = lockInfo.serializeBinary().toString(); + + // Normal invoke function + const [result, submitError] = await helpers.handlePromise( + contract.submitTransaction("LockAsset", assetExchangeAgreementStr, lockInfoStr), + ); + if (submitError) { + throw new Error(`LockAsset submitTransaction Error: ${submitError}`); + } + return { preimage: preimage, result: result }; +}; + +export { + createHTLC, + /*createFungibleHTLC, + isAssetLockedInHTLC, + isFungibleAssetLockedInHTLC, + claimAssetInHTLC, + claimFungibleAssetInHTLC, + reclaimAssetInHTLC, + reclaimFungibleAssetInHTLC,*/ +}; diff --git a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js new file mode 100644 index 000000000..e91a8505e --- /dev/null +++ b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js @@ -0,0 +1,76 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* eslint-disable no-unused-expressions */ + +const fs = require("fs"); +const keyutil = require("jsrsasign").KEYUTIL; +const sinon = require("sinon"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); + +chai.use(chaiAsPromised); +const { expect } = chai; +chai.should(); + +const { Wallets } = require("fabric-network"); +const { ContractImpl } = require("fabric-network/lib/contract"); +const { NetworkImpl } = require("fabric-network/lib/network"); +const assetManager = require("../src/AssetManager"); + +describe("AssetManager", () => { + const mspId = "mspId"; + const foreignNetworkId = "foreignNetworkId"; + const userName = "user_name"; + + const assetType = "bond"; + const assetID = "A001"; + const recipientID = "Bob"; + + let wallet; + let amc; + // Initialize wallet with a single user identity + async function initializeWallet() { + const privKeyFile = `${__dirname}/data/privKey.pem`; + const signCertFile = `${__dirname}/data/signCert.pem`; + const privateKeyStr = fs.readFileSync(privKeyFile).toString(); + const signCert = fs.readFileSync(signCertFile).toString(); + wallet = await Wallets.newInMemoryWallet(); + const userIdentity = { + credentials: { certificate: signCert, privateKey: privateKeyStr }, + mspId, + type: "X.509", + }; + await wallet.put(userName, userIdentity); + return userIdentity; + } + beforeEach(async () => { + await initializeWallet(); + const network = sinon.createStubInstance(NetworkImpl); + amc = new ContractImpl(network, "amc", "AssetManager"); + }); + + afterEach(() => { + sinon.restore(); + }); + + describe("create HTLC for unique asset", () => { + beforeEach(() => { + const amcStub = sinon.stub(amc, "submitTransaction").resolves(true); + //const amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + //amcStub.withArgs("LockAsset", assetAgreementStr, lockInfoStr).resolves(true); + }); + + it("submit asset lock invocation", async () => { + const assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientID, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(20); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); + }); + }); +}); From fa2190dcc780c6b6ae2bfaca1b17529301f860d4 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Thu, 6 May 2021 21:52:22 +0000 Subject: [PATCH 02/16] Added asset lock and claim functions in Fabric Interop Node SDK Also refactored code and added a new signing certificate. Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 289 ++++++++++++++++-- .../test/AssetManager.js | 195 +++++++++++- .../test/data/anotherSignCert.pem | 14 + 3 files changed, 468 insertions(+), 30 deletions(-) create mode 100644 sdks/fabric/interoperation-node-sdk/test/data/anotherSignCert.pem diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index e1ae8bf24..c39ab1812 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -18,19 +18,95 @@ import { Contract } from "fabric-network"; const logger = log4js.getLogger("InteroperableHelper"); +// Create an asset exchange agreement structure +function createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, locker) +{ + const assetExchangeAgreement = new assetLocksPb.AssetExchangeAgreement(); + assetExchangeAgreement.setType(assetType); + assetExchangeAgreement.setId(assetID); + assetExchangeAgreement.setRecipient(recipientECert); + assetExchangeAgreement.setLocker(locker); + return assetExchangeAgreement.serializeBinary().toString(); +} + +// Create a fungible asset exchange agreement structure +function createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, locker) +{ + const assetExchangeAgreement = new assetLocksPb.FungibleAssetExchangeAgreement(); + assetExchangeAgreement.setType(assetType); + assetExchangeAgreement.setNumunits(numUnits); + assetExchangeAgreement.setRecipient(recipientECert); + assetExchangeAgreement.setLocker(locker); + return assetExchangeAgreement.serializeBinary().toString(); +} + +// Create an asset lock structure +function createAssetLockInfoSerialized(hashValue, expiryTimeSecs) +{ + const lockInfoHTLC = new assetLocksPb.AssetLockHTLC(); + lockInfoHTLC.setHash(hashValue); + lockInfoHTLC.setExpirytimesecs(expiryTimeSecs); + lockInfoHTLC.setTimespec(assetLocksPb.AssetLockHTLC.TimeSpec.EPOCH) + const lockInfoHTLCSerialized = lockInfoHTLC.serializeBinary(); + const lockInfo = new assetLocksPb.AssetLock(); + lockInfo.setLockmechanism(assetLocksPb.LockMechanism.HTLC); + lockInfo.setLockinfo(lockInfoHTLCSerialized); + return lockInfo.serializeBinary().toString(); +} + +// Create an asset claim structure +function createAssetClaimInfoSerialized(hashPreimage) +{ + const claimInfoHTLC = new assetLocksPb.AssetClaimHTLC(); + claimInfoHTLC.setHashpreimage(Buffer.from(hashPreimage).toString('base64')); + const claimInfoHTLCSerialized = claimInfoHTLC.serializeBinary(); + const claimInfo = new assetLocksPb.AssetClaim(); + claimInfo.setLockmechanism(assetLocksPb.LockMechanism.HTLC); + claimInfo.setClaiminfo(claimInfoHTLCSerialized); + return claimInfo.serializeBinary().toString(); +} + +// Create a SHA-256 hash over an ASCII string +function createSHA256Hash(preimage) +{ + return crypto.createHash('sha256').update(preimage).digest('base64'); +} + + /** - * First step of a Hashed Time Lock Contract + * First/second step of a Hashed Time Lock Contract * - Lock a unique asset instance using a hash **/ const createHTLC = async ( contract: Contract, assetType: string, assetID: string, - recipientID: string, + recipientECert: string, hashValue: string, expiryTimeSecs: number, ): Promise<{ preimage: any; result: any }> => { + if (!contract) + { + logger.error("Contract handle not supplied"); + return { preimage: "", result: false }; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return { preimage: "", result: false }; + } + if (!assetID) + { + logger.error("Asset ID not supplied"); + return { preimage: "", result: false }; + } + if (!recipientECert) + { + logger.error("Recipient ECert not supplied"); + return { preimage: "", result: false }; + } + let defaultExpiryTimeSecs = 0, preimage = ""; if (hashValue && hashValue.length > 0) { @@ -47,32 +123,17 @@ const createHTLC = async ( } // Hash the preimage - hashValue = crypto.createHash('sha256').update(preimage).digest('base64'); + hashValue = createSHA256Hash(preimage); } const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds if (expiryTimeSecs <= currTimeSecs) { - logger.warn("Supplied HTLC expiry time is invalid or in the past: %d", expiryTimeSecs) - expiryTimeSecs = currTimeSecs + defaultExpiryTimeSecs + logger.warn("Supplied HTLC expiry time is invalid or in the past: %d", expiryTimeSecs); + expiryTimeSecs = currTimeSecs + defaultExpiryTimeSecs; } - // Create an asset exchange agreement structure - const assetExchangeAgreement = new assetLocksPb.AssetExchangeAgreement(); - assetExchangeAgreement.setType(assetType); - assetExchangeAgreement.setId(assetID); - assetExchangeAgreement.setRecipient(recipientID); - const assetExchangeAgreementStr = assetExchangeAgreement.serializeBinary().toString(); - - // Create an asset lock structure - const lockInfoHTLC = new assetLocksPb.AssetLockHTLC(); - lockInfoHTLC.setHash(hashValue); - lockInfoHTLC.setExpirytimesecs(expiryTimeSecs); - lockInfoHTLC.setTimespec(assetLocksPb.AssetLockHTLC.TimeSpec.EPOCH) - const lockInfoHTLCSerialized = lockInfoHTLC.serializeBinary(); - const lockInfo = new assetLocksPb.AssetLock(); - lockInfo.setLockmechanism(assetLocksPb.LockMechanism.HTLC); - lockInfo.setLockinfo(lockInfoHTLCSerialized); - const lockInfoStr = lockInfo.serializeBinary().toString(); + const assetExchangeAgreementStr = createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, ""); + const lockInfoStr = createAssetLockInfoSerialized(hashValue, expiryTimeSecs); // Normal invoke function const [result, submitError] = await helpers.handlePromise( @@ -84,13 +145,191 @@ const createHTLC = async ( return { preimage: preimage, result: result }; }; +/** + * First/second step of a Hashed Time Lock Contract + * - Lock a unique asset instance using a hash + **/ +const createFungibleHTLC = async ( + contract: Contract, + assetType: string, + numUnits: number, + recipientECert: string, + hashValue: string, + expiryTimeSecs: number, +): Promise<{ preimage: any; result: any }> => { + + if (!contract) + { + logger.error("Contract handle not supplied"); + return { preimage: "", result: false }; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return { preimage: "", result: false }; + } + if (numUnits <= 0) + { + logger.error("Asset count must be a positive integer"); + return { preimage: "", result: false }; + } + if (!recipientECert) + { + logger.error("Recipient ECert not supplied"); + return { preimage: "", result: false }; + } + + let defaultExpiryTimeSecs = 0, preimage = ""; + if (hashValue && hashValue.length > 0) + { + defaultExpiryTimeSecs = 5 * 60 // 5 mins: this is the 't' used to time out the second contract of the HTLC pair + } + else + { + defaultExpiryTimeSecs = 10 * 60 // 10 mins: this is the '2t' used to time out the first contract of the HTLC pair + + // Create a random preimage of 20 bytes + for (let i = 0 ; i < 20 ; i++) + { + preimage += String.fromCharCode(Math.floor(Math.random() * 256)) + } + + // Hash the preimage + hashValue = createSHA256Hash(preimage); + } + const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds + if (expiryTimeSecs <= currTimeSecs) + { + logger.warn("Supplied HTLC expiry time is invalid or in the past: %d", expiryTimeSecs); + expiryTimeSecs = currTimeSecs + defaultExpiryTimeSecs; + } + + const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, ""); + const lockInfoStr = createAssetLockInfoSerialized(hashValue, expiryTimeSecs); + + // Normal invoke function + const [result, submitError] = await helpers.handlePromise( + contract.submitTransaction("LockFungibleAsset", assetExchangeAgreementStr, lockInfoStr), + ); + if (submitError) { + throw new Error(`LockFungibleAsset submitTransaction Error: ${submitError}`); + } + return { preimage: preimage, result: result }; +}; + +/** + * Latter step of a Hashed Time Lock Contract + * - Claim a unique asset instance using a hash preimage + **/ +const claimAssetInHTLC = async ( + contract: Contract, + assetType: string, + assetID: string, + lockerECert: string, + hashPreimage: string, +): Promise => { + + if (!contract) + { + logger.error("Contract handle not supplied"); + return false; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return false; + } + if (!assetID) + { + logger.error("Asset ID not supplied"); + return false; + } + if (!lockerECert) + { + logger.error("Locker ECert not supplied"); + return false; + } + if (!hashPreimage) + { + logger.error("Hash Preimage not supplied"); + return false; + } + + const assetExchangeAgreementStr = createAssetExchangeAgreementSerialized(assetType, assetID, "", lockerECert); + const claimInfoStr = createAssetClaimInfoSerialized(hashPreimage); + + // Normal invoke function + const [result, submitError] = await helpers.handlePromise( + contract.submitTransaction("ClaimAsset", assetExchangeAgreementStr, claimInfoStr), + ); + if (submitError) { + throw new Error(`ClaimAsset submitTransaction Error: ${submitError}`); + } + return result; +}; + +/** + * Latter step of a Hashed Time Lock Contract + * - Claim a fungible asset instance using a hash preimage + **/ +const claimFungibleAssetInHTLC = async ( + contract: Contract, + assetType: string, + numUnits: number, + lockerECert: string, + hashPreimage: string, +): Promise => { + + if (!contract) + { + logger.error("Contract handle not supplied"); + return false; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return false; + } + if (numUnits <= 0) + { + logger.error("Asset count must be a positive integer"); + return false; + } + if (!lockerECert) + { + logger.error("Locker ECert not supplied"); + return false; + } + if (!hashPreimage) + { + logger.error("Hash Preimage not supplied"); + return false; + } + + const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, "", lockerECert); + const claimInfoStr = createAssetClaimInfoSerialized(hashPreimage); + + // Normal invoke function + const [result, submitError] = await helpers.handlePromise( + contract.submitTransaction("ClaimFungibleAsset", assetExchangeAgreementStr, claimInfoStr), + ); + if (submitError) { + throw new Error(`ClaimFungibleAsset submitTransaction Error: ${submitError}`); + } + return result; +}; + export { + createAssetExchangeAgreementSerialized, + createFungibleAssetExchangeAgreementSerialized, + createAssetLockInfoSerialized, + createAssetClaimInfoSerialized, createHTLC, - /*createFungibleHTLC, - isAssetLockedInHTLC, - isFungibleAssetLockedInHTLC, + createFungibleHTLC, claimAssetInHTLC, claimFungibleAssetInHTLC, + /*isAssetLockedInHTLC, + isFungibleAssetLockedInHTLC, reclaimAssetInHTLC, reclaimFungibleAssetInHTLC,*/ }; diff --git a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js index e91a8505e..8ff7e124e 100644 --- a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js +++ b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js @@ -20,6 +20,7 @@ const { Wallets } = require("fabric-network"); const { ContractImpl } = require("fabric-network/lib/contract"); const { NetworkImpl } = require("fabric-network/lib/network"); const assetManager = require("../src/AssetManager"); +import assetLocksPb from "../protos-js/common/asset_locks_pb"; describe("AssetManager", () => { const mspId = "mspId"; @@ -28,7 +29,10 @@ describe("AssetManager", () => { const assetType = "bond"; const assetID = "A001"; - const recipientID = "Bob"; + const fungibleAssetType = "cbdc"; + const numUnits = 1000; + const recipientECert = fs.readFileSync(`${__dirname}/data/anotherSignCert.pem`).toString(); + let lockerECert; let wallet; let amc; @@ -38,6 +42,7 @@ describe("AssetManager", () => { const signCertFile = `${__dirname}/data/signCert.pem`; const privateKeyStr = fs.readFileSync(privKeyFile).toString(); const signCert = fs.readFileSync(signCertFile).toString(); + lockerECert = signCert; wallet = await Wallets.newInMemoryWallet(); const userIdentity = { credentials: { certificate: signCert, privateKey: privateKeyStr }, @@ -47,6 +52,7 @@ describe("AssetManager", () => { await wallet.put(userName, userIdentity); return userIdentity; } + beforeEach(async () => { await initializeWallet(); const network = sinon.createStubInstance(NetworkImpl); @@ -58,14 +64,59 @@ describe("AssetManager", () => { }); describe("create HTLC for unique asset", () => { + let amcStub; + beforeEach(() => { - const amcStub = sinon.stub(amc, "submitTransaction").resolves(true); - //const amcStub = sinon.stub(amc, "submitTransaction").resolves(false); - //amcStub.withArgs("LockAsset", assetAgreementStr, lockInfoStr).resolves(true); + amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + }); + + it("asset lock fails with invalid parameters", async () => { + let assetLockInvocation = await assetManager.createHTLC(null, assetType, assetID, recipientECert, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createHTLC(amc, "", assetID, recipientECert, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, "", recipientECert, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, "", "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); }); it("submit asset lock invocation", async () => { - const assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientID, "", 0); + let assetAgreementStr = assetManager.createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, ""); + const hashValue = "abcdef123456"; + let expiryTimeSecs = Math.floor(Date.now()/1000) + 300; // Convert epoch milliseconds to seconds and add 5 minutes + let lockInfoStr = assetManager.createAssetLockInfoSerialized(hashValue, expiryTimeSecs); + amcStub.withArgs("LockAsset", assetAgreementStr, lockInfoStr).resolves(true); + let assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, hashValue, expiryTimeSecs); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); + amcStub.withArgs("LockAsset", assetAgreementStr, sinon.match.any).resolves(true); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, hashValue, 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", 0); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(20); @@ -73,4 +124,138 @@ describe("AssetManager", () => { expect(assetLockInvocation.result).to.equal(true); }); }); + + describe("create HTLC for fungible asset", () => { + let amcStub; + + beforeEach(() => { + amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + }); + + it("asset lock fails with invalid parameters", async () => { + let assetLockInvocation = await assetManager.createFungibleHTLC(null, fungibleAssetType, numUnits, recipientECert, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, "", numUnits, recipientECert, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, -1, recipientECert, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, "", "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + }); + + it("submit asset lock invocation", async () => { + let assetAgreementStr = assetManager.createFungibleAssetExchangeAgreementSerialized(fungibleAssetType, numUnits, recipientECert, ""); + const hashValue = "abcdef123456"; + let expiryTimeSecs = Math.floor(Date.now()/1000) + 300; // Convert epoch milliseconds to seconds and add 5 minutes + let lockInfoStr = assetManager.createAssetLockInfoSerialized(hashValue, expiryTimeSecs); + amcStub.withArgs("LockFungibleAsset", assetAgreementStr, lockInfoStr).resolves(true); + let assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, hashValue, expiryTimeSecs); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); + amcStub.withArgs("LockFungibleAsset", assetAgreementStr, sinon.match.any).resolves(true); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, hashValue, 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(20); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); + }); + }); + + describe("claim unique asset locked in HTLC", () => { + let amcStub; + const hashPreimage = "xyz+123-*ty%"; + + beforeEach(() => { + amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + }); + + it("asset claim fails with invalid parameters", async () => { + let assetClaimInvocation = await assetManager.claimAssetInHTLC(null, assetType, assetID, lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimAssetInHTLC(amc, "", assetID, lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimAssetInHTLC(amc, assetType, "", lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimAssetInHTLC(amc, assetType, assetID, "", hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimAssetInHTLC(amc, assetType, assetID, lockerECert, ""); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + }); + + it("submit asset claim invocation", async () => { + let assetAgreementStr = assetManager.createAssetExchangeAgreementSerialized(assetType, assetID, "", lockerECert); + let claimInfoStr = assetManager.createAssetClaimInfoSerialized(hashPreimage); + amcStub.withArgs("ClaimAsset", assetAgreementStr, claimInfoStr).resolves(true); + let assetClaimInvocation = await assetManager.claimAssetInHTLC(amc, assetType, assetID, lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(true); + }); + }); + + describe("claim fungible asset locked in HTLC", () => { + let amcStub; + const hashPreimage = "xyz+123-*ty%"; + + beforeEach(() => { + amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + }); + + it("asset claim fails with invalid parameters", async () => { + let assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(null, fungibleAssetType, numUnits, lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, "", numUnits, lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, -1, lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, "", hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, lockerECert, ""); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(false); + }); + + it("submit asset claim invocation", async () => { + let assetAgreementStr = assetManager.createFungibleAssetExchangeAgreementSerialized(fungibleAssetType, numUnits, "", lockerECert); + let claimInfoStr = assetManager.createAssetClaimInfoSerialized(hashPreimage); + amcStub.withArgs("ClaimFungibleAsset", assetAgreementStr, claimInfoStr).resolves(true); + let assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, lockerECert, hashPreimage); + expect(assetClaimInvocation).to.be.a('boolean'); + expect(assetClaimInvocation).to.equal(true); + }); + }); }); diff --git a/sdks/fabric/interoperation-node-sdk/test/data/anotherSignCert.pem b/sdks/fabric/interoperation-node-sdk/test/data/anotherSignCert.pem new file mode 100644 index 000000000..202820972 --- /dev/null +++ b/sdks/fabric/interoperation-node-sdk/test/data/anotherSignCert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICNjCCAd2gAwIBAgIRAJ7xltB2QbZ1NXbpj1g9dIcwCgYIKoZIzj0EAwIwezEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG +cmFuY2lzY28xHTAbBgNVBAoTFGNhcnJpZXJvcmcudHJhZGUuY29tMSAwHgYDVQQD +ExdjYS5jYXJyaWVyb3JnLnRyYWRlLmNvbTAeFw0yMTAyMjIxMDA4MDBaFw0zMTAy +MjAxMDA4MDBaMHAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw +FAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQLEwZjbGllbnQxIzAhBgNVBAMM +GlVzZXIxQGNhcnJpZXJvcmcudHJhZGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAENb8HAEq+6w/QkyOcuJ8dxSD5dlqzGOYIrLA9iQ8RuwvQtGOQk1IElK/T +xnSUXBTQfKSW7jszxf8ixt7MhJunJqNNMEswDgYDVR0PAQH/BAQDAgeAMAwGA1Ud +EwEB/wQCMAAwKwYDVR0jBCQwIoAg0Z9daWiEhKVlUqx3z5b7k960IvK2pKbf0Tb1 +Vw3baogwCgYIKoZIzj0EAwIDRwAwRAIgHDVXaD5fU3RMxWJY2NCdOB8mbrPhmMn7 +qVeftYVkaXwCIE+/Oy2LJVZNPkUwQ3qlVLEhdSYxJVKEHj9Aob9wxIsz +-----END CERTIFICATE----- From 7f128cee68bab443a0458b8bf61a6e2c7f310b2f Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Fri, 7 May 2021 13:58:35 +0000 Subject: [PATCH 03/16] Added reclaim and asset lock query functions to Fabric Interop Node SDK Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 206 +++++++++++++++++- .../test/AssetManager.js | 130 +++++++++++ 2 files changed, 327 insertions(+), 9 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index c39ab1812..d64992c87 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -19,24 +19,24 @@ const logger = log4js.getLogger("InteroperableHelper"); // Create an asset exchange agreement structure -function createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, locker) +function createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, lockerECert) { const assetExchangeAgreement = new assetLocksPb.AssetExchangeAgreement(); assetExchangeAgreement.setType(assetType); assetExchangeAgreement.setId(assetID); assetExchangeAgreement.setRecipient(recipientECert); - assetExchangeAgreement.setLocker(locker); + assetExchangeAgreement.setLocker(lockerECert); return assetExchangeAgreement.serializeBinary().toString(); } // Create a fungible asset exchange agreement structure -function createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, locker) +function createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, lockerECert) { const assetExchangeAgreement = new assetLocksPb.FungibleAssetExchangeAgreement(); assetExchangeAgreement.setType(assetType); assetExchangeAgreement.setNumunits(numUnits); assetExchangeAgreement.setRecipient(recipientECert); - assetExchangeAgreement.setLocker(locker); + assetExchangeAgreement.setLocker(lockerECert); return assetExchangeAgreement.serializeBinary().toString(); } @@ -147,7 +147,7 @@ const createHTLC = async ( /** * First/second step of a Hashed Time Lock Contract - * - Lock a unique asset instance using a hash + * - Lock a set of fungible assets using a hash **/ const createFungibleHTLC = async ( contract: Contract, @@ -270,7 +270,7 @@ const claimAssetInHTLC = async ( /** * Latter step of a Hashed Time Lock Contract - * - Claim a fungible asset instance using a hash preimage + * - Claim a set of fungible assets using a hash preimage **/ const claimFungibleAssetInHTLC = async ( contract: Contract, @@ -319,6 +319,194 @@ const claimFungibleAssetInHTLC = async ( return result; }; +/** + * Rollback step of a Hashed Time Lock Contract + * - Reclaim a unique asset instance + **/ +const reclaimAssetInHTLC = async ( + contract: Contract, + assetType: string, + assetID: string, + recipientECert: string, +): Promise => { + + if (!contract) + { + logger.error("Contract handle not supplied"); + return false; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return false; + } + if (!assetID) + { + logger.error("Asset ID not supplied"); + return false; + } + if (!recipientECert) + { + logger.error("Recipient ECert not supplied"); + return false; + } + + const assetExchangeAgreementStr = createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, ""); + + // Normal invoke function + const [result, submitError] = await helpers.handlePromise( + contract.submitTransaction("UnlockAsset", assetExchangeAgreementStr), + ); + if (submitError) { + throw new Error(`UnlockAsset submitTransaction Error: ${submitError}`); + } + return result; +}; + +/** + * Rollback step of a Hashed Time Lock Contract + * - Reclaim a set of fungible assets + **/ +const reclaimFungibleAssetInHTLC = async ( + contract: Contract, + assetType: string, + numUnits: number, + recipientECert: string, +): Promise => { + + if (!contract) + { + logger.error("Contract handle not supplied"); + return false; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return false; + } + if (numUnits <= 0) + { + logger.error("Asset count must be a positive integer"); + return false; + } + if (!recipientECert) + { + logger.error("Recipient ECert not supplied"); + return false; + } + + const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, ""); + + // Normal invoke function + const [result, submitError] = await helpers.handlePromise( + contract.submitTransaction("UnlockFungibleAsset", assetExchangeAgreementStr), + ); + if (submitError) { + throw new Error(`UnlockFungibleAsset submitTransaction Error: ${submitError}`); + } + return result; +}; + +/** + * Query the state of a Hashed Time Lock Contract + * - Determine if a unique asset instance is locked by a given party for another given party + **/ +const isAssetLockedInHTLC = async ( + contract: Contract, + assetType: string, + assetID: string, + recipientECert: string, + lockerECert: string, +): Promise => { + + if (!contract) + { + logger.error("Contract handle not supplied"); + return false; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return false; + } + if (!assetID) + { + logger.error("Asset ID not supplied"); + return false; + } + if (!recipientECert) + { + logger.error("Recipient ECert not supplied"); + return false; + } + if (!lockerECert) + { + logger.error("Locker ECert not supplied"); + return false; + } + + const assetExchangeAgreementStr = createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, lockerECert); + + // Normal invoke function + const [result, evaluateError] = await helpers.handlePromise( + contract.evaluateTransaction("IsAssetLocked", assetExchangeAgreementStr), + ); + if (evaluateError) { + throw new Error(`IsAssetLocked evaluateTransaction Error: ${evaluateError}`); + } + return result; +}; + +/** + * Query the state of a Hashed Time Lock Contract + * - Determine if a set of fungible assets is locked by a given party for another given party + **/ +const isFungibleAssetLockedInHTLC = async ( + contract: Contract, + assetType: string, + numUnits: number, + recipientECert: string, + lockerECert: string, +): Promise => { + + if (!contract) + { + logger.error("Contract handle not supplied"); + return false; + } + if (!assetType) + { + logger.error("Asset type not supplied"); + return false; + } + if (numUnits <= 0) + { + logger.error("Asset count must be a positive integer"); + return false; + } + if (!recipientECert) + { + logger.error("Recipient ECert not supplied"); + return false; + } + if (!lockerECert) + { + logger.error("Locker ECert not supplied"); + return false; + } + + const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, lockerECert); + + // Normal invoke function + const [result, evaluateError] = await helpers.handlePromise( + contract.evaluateTransaction("IsFungibleAssetLocked", assetExchangeAgreementStr), + ); + if (evaluateError) { + throw new Error(`IsFungibleAssetLocked evaluateTransaction Error: ${evaluateError}`); + } + return result; +}; + export { createAssetExchangeAgreementSerialized, createFungibleAssetExchangeAgreementSerialized, @@ -328,8 +516,8 @@ export { createFungibleHTLC, claimAssetInHTLC, claimFungibleAssetInHTLC, - /*isAssetLockedInHTLC, - isFungibleAssetLockedInHTLC, reclaimAssetInHTLC, - reclaimFungibleAssetInHTLC,*/ + reclaimFungibleAssetInHTLC, + isAssetLockedInHTLC, + isFungibleAssetLockedInHTLC, }; diff --git a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js index 8ff7e124e..13cd39888 100644 --- a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js +++ b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js @@ -258,4 +258,134 @@ describe("AssetManager", () => { expect(assetClaimInvocation).to.equal(true); }); }); + + describe("reclaim unique asset locked in HTLC", () => { + let amcStub; + + beforeEach(() => { + amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + }); + + it("asset reclaim fails with invalid parameters", async () => { + let assetReclaimInvocation = await assetManager.reclaimAssetInHTLC(null, assetType, assetID, recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + assetReclaimInvocation = await assetManager.reclaimAssetInHTLC(amc, "", assetID, recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + assetReclaimInvocation = await assetManager.reclaimAssetInHTLC(amc, assetType, "", recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + assetReclaimInvocation = await assetManager.reclaimAssetInHTLC(amc, assetType, assetID, ""); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + }); + + it("submit asset claim invocation", async () => { + let assetAgreementStr = assetManager.createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, ""); + amcStub.withArgs("UnlockAsset", assetAgreementStr).resolves(true); + let assetReclaimInvocation = await assetManager.reclaimAssetInHTLC(amc, assetType, assetID, recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(true); + }); + }); + + describe("reclaim fungible asset locked in HTLC", () => { + let amcStub; + + beforeEach(() => { + amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + }); + + it("asset reclaim fails with invalid parameters", async () => { + let assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(null, fungibleAssetType, numUnits, recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, "", numUnits, recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, fungibleAssetType, -1, recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, ""); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(false); + }); + + it("submit asset claim invocation", async () => { + let assetAgreementStr = assetManager.createFungibleAssetExchangeAgreementSerialized(fungibleAssetType, numUnits, recipientECert, ""); + amcStub.withArgs("UnlockFungibleAsset", assetAgreementStr).resolves(true); + let assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, recipientECert); + expect(assetReclaimInvocation).to.be.a('boolean'); + expect(assetReclaimInvocation).to.equal(true); + }); + }); + + describe("check unique asset lock status in HTLC", () => { + let amcStub; + + beforeEach(() => { + amcStub = sinon.stub(amc, "evaluateTransaction").resolves(false); + }); + + it("asset lock status check fails with invalid parameters", async () => { + let assetLockQuery = await assetManager.isAssetLockedInHTLC(null, assetType, assetID, recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isAssetLockedInHTLC(amc, "", assetID, recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isAssetLockedInHTLC(amc, assetType, "", recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isAssetLockedInHTLC(amc, assetType, assetID, "", lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isAssetLockedInHTLC(amc, assetType, assetID, recipientECert, ""); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + }); + + it("submit asset lock status query", async () => { + let assetAgreementStr = assetManager.createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, lockerECert); + amcStub.withArgs("IsAssetLocked", assetAgreementStr).resolves(true); + let assetLockQuery = await assetManager.isAssetLockedInHTLC(amc, assetType, assetID, recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(true); + }); + }); + + describe("check fungible asset lock status in HTLC", () => { + let amcStub; + + beforeEach(() => { + amcStub = sinon.stub(amc, "evaluateTransaction").resolves(false); + }); + + it("asset lock status check fails with invalid parameters", async () => { + let assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(null, fungibleAssetType, numUnits, recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, "", numUnits, recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, -1, recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, numUnits, "", lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, numUnits, recipientECert, ""); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(false); + }); + + it("submit asset lock status query", async () => { + let assetAgreementStr = assetManager.createFungibleAssetExchangeAgreementSerialized(fungibleAssetType, numUnits, recipientECert, lockerECert); + amcStub.withArgs("IsFungibleAssetLocked", assetAgreementStr).resolves(true); + let assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, numUnits, recipientECert, lockerECert); + expect(assetLockQuery).to.be.a('boolean'); + expect(assetLockQuery).to.equal(true); + }); + }); }); From 6e4c4b567a93faa490002716e0cd02d9265c6ab6 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Fri, 7 May 2021 14:10:13 +0000 Subject: [PATCH 04/16] Changed asset lock and claim field names to reflect Base64 encoding Signed-off-by: VRamakrishna --- .../interop-protos/common/asset_locks.proto | 4 +- .../interfaces/asset-mgmt/asset_locks.go | 8 +- .../interfaces/asset-mgmt/asset_locks_test.go | 84 +++++++++---------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/common/interop-protos/common/asset_locks.proto b/common/interop-protos/common/asset_locks.proto index 301121bfe..ec22bf754 100644 --- a/common/interop-protos/common/asset_locks.proto +++ b/common/interop-protos/common/asset_locks.proto @@ -18,7 +18,7 @@ message AssetClaim { } message AssetLockHTLC { - bytes hash = 1; + bytes hashBase64 = 1; uint64 expiryTimeSecs = 2; enum TimeSpec { EPOCH = 0; @@ -28,7 +28,7 @@ message AssetLockHTLC { } message AssetClaimHTLC { - bytes hashPreimage = 1; + bytes hashPreimageBase64 = 1; } message AssetExchangeAgreement { diff --git a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks.go b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks.go index bd17b12b1..5d499ad48 100644 --- a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks.go +++ b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks.go @@ -109,7 +109,7 @@ func (am *AssetManagement) LockAsset(stub shim.ChaincodeStubInterface, assetAgre log.Error(errorMsg) return false, errors.New(errorMsg) } - if len(lockInfoHTLC.Hash) == 0 { + if len(lockInfoHTLC.HashBase64) == 0 { errorMsg = "empty lock hash value" log.Error(errorMsg) return false, errors.New(errorMsg) @@ -179,7 +179,7 @@ func (am *AssetManagement) LockFungibleAsset(stub shim.ChaincodeStubInterface, a log.Error(errorMsg) return false, errors.New(errorMsg) } - if len(lockInfoHTLC.Hash) == 0 { + if len(lockInfoHTLC.HashBase64) == 0 { errorMsg = "empty lock hash value" log.Error(errorMsg) return false, errors.New(errorMsg) @@ -354,7 +354,7 @@ func (am *AssetManagement) ClaimAsset(stub shim.ChaincodeStubInterface, assetAgr log.Error(err.Error()) return false, err } - if len(claimInfoHTLC.HashPreimage) == 0 { + if len(claimInfoHTLC.HashPreimageBase64) == 0 { errorMsg = "empty lock hash preimage" log.Error(errorMsg) return false, errors.New(errorMsg) @@ -419,7 +419,7 @@ func (am *AssetManagement) ClaimFungibleAsset(stub shim.ChaincodeStubInterface, log.Error(err.Error()) return false, err } - if len(claimInfoHTLC.HashPreimage) == 0 { + if len(claimInfoHTLC.HashPreimageBase64) == 0 { errorMsg = "empty lock hash preimage" log.Error(errorMsg) return false, errors.New(errorMsg) diff --git a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_test.go b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_test.go index 16bc02c1c..59f17305e 100644 --- a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_test.go +++ b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_test.go @@ -235,9 +235,9 @@ func TestAssetLock(t *testing.T) { newAssetId := "A002" recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") lockInfoHTLC := &common.AssetLockHTLC { - Hash: nil, + HashBase64: nil, ExpiryTimeSecs: 0, } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -282,7 +282,7 @@ func TestAssetLock(t *testing.T) { require.False(t, lockSuccess) assetAgreement.Recipient = "" - lockInfoHTLC.Hash = hash + lockInfoHTLC.HashBase64 = hash lockInfoBytes, _ = proto.Marshal(lockInfoHTLC) lockInfo.LockInfo = lockInfoBytes assetAgreement.Id = assetId @@ -291,7 +291,7 @@ func TestAssetLock(t *testing.T) { require.False(t, lockSuccess) assetAgreement.Recipient = recipient - lockInfoHTLC.Hash = []byte{} + lockInfoHTLC.HashBase64 = []byte{} lockInfoBytes, _ = proto.Marshal(lockInfoHTLC) lockInfo.LockInfo = lockInfoBytes lockSuccess, err = amcc.LockAsset(amstub, assetAgreement, lockInfo) @@ -304,7 +304,7 @@ func TestAssetLock(t *testing.T) { require.False(t, lockSuccess) // Test success - lockInfoHTLC.Hash = hash + lockInfoHTLC.HashBase64 = hash lockInfoBytes, _ = proto.Marshal(lockInfoHTLC) lockInfo.LockInfo = lockInfoBytes lockSuccess, err = amcc.LockAsset(amstub, assetAgreement, lockInfo) @@ -345,9 +345,9 @@ func TestFungibleAssetLock(t *testing.T) { numUnits := uint64(1000) recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") lockInfoHTLC := &common.AssetLockHTLC { - Hash: nil, + HashBase64: nil, ExpiryTimeSecs: 0, } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -392,7 +392,7 @@ func TestFungibleAssetLock(t *testing.T) { require.False(t, lockSuccess) assetAgreement.Recipient = "" - lockInfoHTLC.Hash = hash + lockInfoHTLC.HashBase64 = hash lockInfoBytes, _ = proto.Marshal(lockInfoHTLC) lockInfo.LockInfo = lockInfoBytes assetAgreement.NumUnits = numUnits @@ -401,7 +401,7 @@ func TestFungibleAssetLock(t *testing.T) { require.False(t, lockSuccess) assetAgreement.Recipient = recipient - lockInfoHTLC.Hash = []byte{} + lockInfoHTLC.HashBase64 = []byte{} lockInfoBytes, _ = proto.Marshal(lockInfoHTLC) lockInfo.LockInfo = lockInfoBytes lockSuccess, err = amcc.LockFungibleAsset(amstub, assetAgreement, lockInfo) @@ -414,7 +414,7 @@ func TestFungibleAssetLock(t *testing.T) { require.False(t, lockSuccess) // Test failure when there is no unit balance (total not declared yet) - lockInfoHTLC.Hash = hash + lockInfoHTLC.HashBase64 = hash lockInfoBytes, _ = proto.Marshal(lockInfoHTLC) lockInfo.LockInfo = lockInfoBytes lockSuccess, err = amcc.LockFungibleAsset(amstub, assetAgreement, lockInfo) @@ -465,8 +465,8 @@ func TestIsAssetLocked(t *testing.T) { assetId := "A001" recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") - hashPreimage := []byte("asset-exchange-scenario") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") + hashPreimage := []byte("YW5jaXNjbzEeMBwGA1UE") assetAgreement := &common.AssetExchangeAgreement { Type: assetType, Id: assetId, @@ -474,7 +474,7 @@ func TestIsAssetLocked(t *testing.T) { Locker: locker, } lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: 0, } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -483,7 +483,7 @@ func TestIsAssetLocked(t *testing.T) { LockInfo: lockInfoBytes, } claimInfoHTLC := &common.AssetClaimHTLC { - HashPreimage: hashPreimage, + HashPreimageBase64: hashPreimage, } claimInfoBytes, _ := proto.Marshal(claimInfoHTLC) claimInfo := &common.AssetClaim { @@ -584,8 +584,8 @@ func TestIsFungibleAssetLocked(t *testing.T) { numUnits := uint64(1000) recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") - hashPreimage := []byte("asset-exchange-scenario") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") + hashPreimage := []byte("YW5jaXNjbzEeMBwGA1UE") assetAgreement := &common.FungibleAssetExchangeAgreement { Type: assetType, NumUnits: numUnits, @@ -593,7 +593,7 @@ func TestIsFungibleAssetLocked(t *testing.T) { Locker: locker, } lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: 0, } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -602,7 +602,7 @@ func TestIsFungibleAssetLocked(t *testing.T) { LockInfo: lockInfoBytes, } claimInfoHTLC := &common.AssetClaimHTLC { - HashPreimage: hashPreimage, + HashPreimageBase64: hashPreimage, } claimInfoBytes, _ := proto.Marshal(claimInfoHTLC) claimInfo := &common.AssetClaim { @@ -710,7 +710,7 @@ func TestAssetUnlock(t *testing.T) { assetId := "A001" recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") assetAgreement := &common.AssetExchangeAgreement { Type: assetType, Id: assetId, @@ -720,7 +720,7 @@ func TestAssetUnlock(t *testing.T) { currTime := time.Now() expiryTime := currTime.Add(time.Minute) // expires in 1 minute lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: uint64(expiryTime.Unix()), } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -803,7 +803,7 @@ func TestFungibleAssetUnlock(t *testing.T) { numUnits := uint64(1000) recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") assetAgreement := &common.FungibleAssetExchangeAgreement { Type: assetType, NumUnits: numUnits, @@ -813,7 +813,7 @@ func TestFungibleAssetUnlock(t *testing.T) { currTime := time.Now() expiryTime := currTime.Add(time.Minute) // expires in 1 minute lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: uint64(expiryTime.Unix()), } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -899,10 +899,10 @@ func TestAssetClaim(t *testing.T) { assetId := "A001" recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") - hashPreimage := []byte("asset-exchange-scenario") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") + hashPreimage := []byte("YW5jaXNjbzEeMBwGA1UE") claimInfoHTLC := &common.AssetClaimHTLC { - HashPreimage: nil, + HashPreimageBase64: nil, } claimInfoBytes, _ := proto.Marshal(claimInfoHTLC) claimInfo := &common.AssetClaim { @@ -946,7 +946,7 @@ func TestAssetClaim(t *testing.T) { require.False(t, claimSuccess) assetAgreement.Locker = "" - claimInfoHTLC.HashPreimage = hashPreimage + claimInfoHTLC.HashPreimageBase64 = hashPreimage claimInfoBytes, _ = proto.Marshal(claimInfoHTLC) claimInfo.ClaimInfo = claimInfoBytes assetAgreement.Id = assetId @@ -955,7 +955,7 @@ func TestAssetClaim(t *testing.T) { require.False(t, claimSuccess) assetAgreement.Locker = locker - claimInfoHTLC.HashPreimage = []byte{} + claimInfoHTLC.HashPreimageBase64 = []byte{} claimInfoBytes, _ = proto.Marshal(claimInfoHTLC) claimInfo.ClaimInfo = claimInfoBytes claimSuccess, err = amcc.ClaimAsset(amstub, assetAgreement, claimInfo) @@ -970,7 +970,7 @@ func TestAssetClaim(t *testing.T) { // Test success // First, lock an asset lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: 0, } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -989,7 +989,7 @@ func TestAssetClaim(t *testing.T) { // Now claim the asset assetAgreement.Locker = locker - claimInfoHTLC.HashPreimage = hashPreimage + claimInfoHTLC.HashPreimageBase64 = hashPreimage claimInfoBytes, _ = proto.Marshal(claimInfoHTLC) claimInfo.ClaimInfo = claimInfoBytes setCreator(amstub, recipient) @@ -1013,10 +1013,10 @@ func TestFungibleAssetClaim(t *testing.T) { numUnits := uint64(1000) recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") - hashPreimage := []byte("asset-exchange-scenario") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") + hashPreimage := []byte("YW5jaXNjbzEeMBwGA1UE") claimInfoHTLC := &common.AssetClaimHTLC { - HashPreimage: nil, + HashPreimageBase64: nil, } claimInfoBytes, _ := proto.Marshal(claimInfoHTLC) claimInfo := &common.AssetClaim { @@ -1060,7 +1060,7 @@ func TestFungibleAssetClaim(t *testing.T) { require.False(t, claimSuccess) assetAgreement.Locker = "" - claimInfoHTLC.HashPreimage = hashPreimage + claimInfoHTLC.HashPreimageBase64 = hashPreimage claimInfoBytes, _ = proto.Marshal(claimInfoHTLC) claimInfo.ClaimInfo = claimInfoBytes assetAgreement.NumUnits = numUnits @@ -1069,7 +1069,7 @@ func TestFungibleAssetClaim(t *testing.T) { require.False(t, claimSuccess) assetAgreement.Locker = locker - claimInfoHTLC.HashPreimage = []byte{} + claimInfoHTLC.HashPreimageBase64 = []byte{} claimInfoBytes, _ = proto.Marshal(claimInfoHTLC) claimInfo.ClaimInfo = claimInfoBytes claimSuccess, err = amcc.ClaimFungibleAsset(amstub, assetAgreement, claimInfo) @@ -1088,7 +1088,7 @@ func TestFungibleAssetClaim(t *testing.T) { require.True(t, addSuccess) lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: 0, } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -1107,7 +1107,7 @@ func TestFungibleAssetClaim(t *testing.T) { // Now claim the asset assetAgreement.Locker = locker - claimInfoHTLC.HashPreimage = hashPreimage + claimInfoHTLC.HashPreimageBase64 = hashPreimage claimInfoBytes, _ = proto.Marshal(claimInfoHTLC) claimInfo.ClaimInfo = claimInfoBytes setCreator(amstub, recipient) @@ -1130,14 +1130,14 @@ func TestFungibleAssetCountFunctions(t *testing.T) { totalUnits := uint64(10000) numUnits := uint64(1000) recipient := "Bob" - hash := []byte("j8r484r484") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") fungibleAssetExchangeAgreement := &common.FungibleAssetExchangeAgreement { Type: assetType, NumUnits: numUnits, Recipient: recipient, } lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: 0, } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -1220,7 +1220,7 @@ func TestAssetListFunctions(t *testing.T) { numUnits := uint64(1000) recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") assetAgreement := &common.AssetExchangeAgreement { Type: assetType, Id: assetId, @@ -1236,7 +1236,7 @@ func TestAssetListFunctions(t *testing.T) { currTime := time.Now() expiryTime := currTime.Add(time.Minute) // expires in 1 minute lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: uint64(expiryTime.Unix()), } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) @@ -1370,7 +1370,7 @@ func TestAssetTimeFunctions(t *testing.T) { numUnits := uint64(1000) recipient := "Bob" locker := clientId - hash := []byte("j8r484r484") + hash := []byte("MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MSQwIgYDVQQD") assetAgreement := &common.AssetExchangeAgreement { Type: assetType, Id: assetId, @@ -1386,7 +1386,7 @@ func TestAssetTimeFunctions(t *testing.T) { currTime := time.Now() expiryTime := currTime.Add(time.Minute) // expires in 1 minute lockInfoHTLC := &common.AssetLockHTLC { - Hash: hash, + HashBase64: hash, ExpiryTimeSecs: uint64(expiryTime.Unix()), } lockInfoBytes, _ := proto.Marshal(lockInfoHTLC) From 65646240c6198ef2421b8ea760e438a129651535 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Fri, 7 May 2021 14:15:58 +0000 Subject: [PATCH 05/16] Updated field and function names in Fabric Interop Node SDK based on asset lock protobuf changes Signed-off-by: VRamakrishna --- .../fabric/interoperation-node-sdk/src/AssetManager.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index d64992c87..fe55bc28b 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -44,7 +44,7 @@ function createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, rec function createAssetLockInfoSerialized(hashValue, expiryTimeSecs) { const lockInfoHTLC = new assetLocksPb.AssetLockHTLC(); - lockInfoHTLC.setHash(hashValue); + lockInfoHTLC.setHashbase64(hashValue); lockInfoHTLC.setExpirytimesecs(expiryTimeSecs); lockInfoHTLC.setTimespec(assetLocksPb.AssetLockHTLC.TimeSpec.EPOCH) const lockInfoHTLCSerialized = lockInfoHTLC.serializeBinary(); @@ -58,7 +58,7 @@ function createAssetLockInfoSerialized(hashValue, expiryTimeSecs) function createAssetClaimInfoSerialized(hashPreimage) { const claimInfoHTLC = new assetLocksPb.AssetClaimHTLC(); - claimInfoHTLC.setHashpreimage(Buffer.from(hashPreimage).toString('base64')); + claimInfoHTLC.setHashpreimagebase64(Buffer.from(hashPreimage).toString('base64')); const claimInfoHTLCSerialized = claimInfoHTLC.serializeBinary(); const claimInfo = new assetLocksPb.AssetClaim(); claimInfo.setLockmechanism(assetLocksPb.LockMechanism.HTLC); @@ -67,7 +67,7 @@ function createAssetClaimInfoSerialized(hashPreimage) } // Create a SHA-256 hash over an ASCII string -function createSHA256Hash(preimage) +function createSHA256HashBase64(preimage) { return crypto.createHash('sha256').update(preimage).digest('base64'); } @@ -123,7 +123,7 @@ const createHTLC = async ( } // Hash the preimage - hashValue = createSHA256Hash(preimage); + hashValue = createSHA256HashBase64(preimage); } const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds if (expiryTimeSecs <= currTimeSecs) @@ -195,7 +195,7 @@ const createFungibleHTLC = async ( } // Hash the preimage - hashValue = createSHA256Hash(preimage); + hashValue = createSHA256HashBase64(preimage); } const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds if (expiryTimeSecs <= currTimeSecs) From 4624feac91288feddbcc22779a9e9cde8ae66dec Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Tue, 11 May 2021 20:40:45 +0000 Subject: [PATCH 06/16] Generating secure pseudorandom hash preimage for asset locks in Fabric Interop SDK Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 41 +++++++++++-------- .../test/AssetManager.js | 26 +++++++++--- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index fe55bc28b..132f64c8b 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -67,11 +67,20 @@ function createAssetClaimInfoSerialized(hashPreimage) } // Create a SHA-256 hash over an ASCII string -function createSHA256HashBase64(preimage) +function createSHA256HashBase64(preimage: string) { return crypto.createHash('sha256').update(preimage).digest('base64'); } +// Create a secure pseudo-random preimage of a given length +function generateRandomHashPreimage(strLength: number) +{ + if (!strLength || strLength <= 0) + { + strLength = 20; // Default length + } + return crypto.randomBytes(strLength).toString(); +} /** * First/second step of a Hashed Time Lock Contract @@ -82,6 +91,7 @@ const createHTLC = async ( assetType: string, assetID: string, recipientECert: string, + hashPreimage: string, hashValue: string, expiryTimeSecs: number, ): Promise<{ preimage: any; result: any }> => { @@ -107,7 +117,7 @@ const createHTLC = async ( return { preimage: "", result: false }; } - let defaultExpiryTimeSecs = 0, preimage = ""; + let defaultExpiryTimeSecs = 0; if (hashValue && hashValue.length > 0) { defaultExpiryTimeSecs = 5 * 60 // 5 mins: this is the 't' used to time out the second contract of the HTLC pair @@ -115,15 +125,13 @@ const createHTLC = async ( else { defaultExpiryTimeSecs = 10 * 60 // 10 mins: this is the '2t' used to time out the first contract of the HTLC pair - - // Create a random preimage of 20 bytes - for (let i = 0 ; i < 20 ; i++) + if (!hashPreimage || hashPreimage.length == 0) { - preimage += String.fromCharCode(Math.floor(Math.random() * 256)) + // Generate the preimage + hashPreimage = generateRandomHashPreimage(-1); } - // Hash the preimage - hashValue = createSHA256HashBase64(preimage); + hashValue = createSHA256HashBase64(hashPreimage); } const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds if (expiryTimeSecs <= currTimeSecs) @@ -142,7 +150,7 @@ const createHTLC = async ( if (submitError) { throw new Error(`LockAsset submitTransaction Error: ${submitError}`); } - return { preimage: preimage, result: result }; + return { preimage: hashPreimage, result: result }; }; /** @@ -154,6 +162,7 @@ const createFungibleHTLC = async ( assetType: string, numUnits: number, recipientECert: string, + hashPreimage: string, hashValue: string, expiryTimeSecs: number, ): Promise<{ preimage: any; result: any }> => { @@ -179,7 +188,7 @@ const createFungibleHTLC = async ( return { preimage: "", result: false }; } - let defaultExpiryTimeSecs = 0, preimage = ""; + let defaultExpiryTimeSecs = 0; if (hashValue && hashValue.length > 0) { defaultExpiryTimeSecs = 5 * 60 // 5 mins: this is the 't' used to time out the second contract of the HTLC pair @@ -187,15 +196,13 @@ const createFungibleHTLC = async ( else { defaultExpiryTimeSecs = 10 * 60 // 10 mins: this is the '2t' used to time out the first contract of the HTLC pair - - // Create a random preimage of 20 bytes - for (let i = 0 ; i < 20 ; i++) + if (!hashPreimage || hashPreimage.length == 0) { - preimage += String.fromCharCode(Math.floor(Math.random() * 256)) + // Generate the preimage + hashPreimage = generateRandomHashPreimage(-1); } - // Hash the preimage - hashValue = createSHA256HashBase64(preimage); + hashValue = createSHA256HashBase64(hashPreimage); } const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds if (expiryTimeSecs <= currTimeSecs) @@ -214,7 +221,7 @@ const createFungibleHTLC = async ( if (submitError) { throw new Error(`LockFungibleAsset submitTransaction Error: ${submitError}`); } - return { preimage: preimage, result: result }; + return { preimage: hashPreimage, result: result }; }; /** diff --git a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js index 13cd39888..95e8d8cbe 100644 --- a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js +++ b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js @@ -103,25 +103,32 @@ describe("AssetManager", () => { let expiryTimeSecs = Math.floor(Date.now()/1000) + 300; // Convert epoch milliseconds to seconds and add 5 minutes let lockInfoStr = assetManager.createAssetLockInfoSerialized(hashValue, expiryTimeSecs); amcStub.withArgs("LockAsset", assetAgreementStr, lockInfoStr).resolves(true); - let assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, hashValue, expiryTimeSecs); + let assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", hashValue, expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); amcStub.withArgs("LockAsset", assetAgreementStr, sinon.match.any).resolves(true); - assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, hashValue, 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", hashValue, 0); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); - assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", "", 0); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(20); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); + const preimage = "something"; + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, preimage, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(preimage.length); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); }); }); @@ -165,25 +172,32 @@ describe("AssetManager", () => { let expiryTimeSecs = Math.floor(Date.now()/1000) + 300; // Convert epoch milliseconds to seconds and add 5 minutes let lockInfoStr = assetManager.createAssetLockInfoSerialized(hashValue, expiryTimeSecs); amcStub.withArgs("LockFungibleAsset", assetAgreementStr, lockInfoStr).resolves(true); - let assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, hashValue, expiryTimeSecs); + let assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", hashValue, expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); amcStub.withArgs("LockFungibleAsset", assetAgreementStr, sinon.match.any).resolves(true); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, hashValue, 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", hashValue, 0); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", "", 0); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(20); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); + const preimage = "something"; + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, preimage, "", 0); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(preimage.length); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(true); }); }); From e79cce131615aa8a650519d98bd0a8c98088fdec Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Wed, 12 May 2021 14:14:37 +0000 Subject: [PATCH 07/16] Updated error handling in asset exchange functions in Fabric Interop Node SDK Throwing an error if lock expiration time is invalid. Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 36 +++++-------- .../test/AssetManager.js | 52 ++++++++++++------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index 132f64c8b..955bad148 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -116,15 +116,15 @@ const createHTLC = async ( logger.error("Recipient ECert not supplied"); return { preimage: "", result: false }; } - - let defaultExpiryTimeSecs = 0; - if (hashValue && hashValue.length > 0) + const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds + if (expiryTimeSecs <= currTimeSecs) { - defaultExpiryTimeSecs = 5 * 60 // 5 mins: this is the 't' used to time out the second contract of the HTLC pair + logger.error("Supplied expiry time invalid or in the past: %s; current time: %s", new Date(expiryTimeSecs).toISOString(), new Date(currTimeSecs).toISOString()); + return { preimage: "", result: false }; } - else + + if (!hashValue || hashValue.length == 0) { - defaultExpiryTimeSecs = 10 * 60 // 10 mins: this is the '2t' used to time out the first contract of the HTLC pair if (!hashPreimage || hashPreimage.length == 0) { // Generate the preimage @@ -133,12 +133,6 @@ const createHTLC = async ( // Hash the preimage hashValue = createSHA256HashBase64(hashPreimage); } - const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds - if (expiryTimeSecs <= currTimeSecs) - { - logger.warn("Supplied HTLC expiry time is invalid or in the past: %d", expiryTimeSecs); - expiryTimeSecs = currTimeSecs + defaultExpiryTimeSecs; - } const assetExchangeAgreementStr = createAssetExchangeAgreementSerialized(assetType, assetID, recipientECert, ""); const lockInfoStr = createAssetLockInfoSerialized(hashValue, expiryTimeSecs); @@ -187,15 +181,15 @@ const createFungibleHTLC = async ( logger.error("Recipient ECert not supplied"); return { preimage: "", result: false }; } - - let defaultExpiryTimeSecs = 0; - if (hashValue && hashValue.length > 0) + const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds + if (expiryTimeSecs <= currTimeSecs) { - defaultExpiryTimeSecs = 5 * 60 // 5 mins: this is the 't' used to time out the second contract of the HTLC pair + logger.error("Supplied expiry time invalid or in the past: %s; current time: %s", new Date(expiryTimeSecs).toISOString(), new Date(currTimeSecs).toISOString()); + return { preimage: "", result: false }; } - else + + if (!hashValue || hashValue.length == 0) { - defaultExpiryTimeSecs = 10 * 60 // 10 mins: this is the '2t' used to time out the first contract of the HTLC pair if (!hashPreimage || hashPreimage.length == 0) { // Generate the preimage @@ -204,12 +198,6 @@ const createFungibleHTLC = async ( // Hash the preimage hashValue = createSHA256HashBase64(hashPreimage); } - const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds - if (expiryTimeSecs <= currTimeSecs) - { - logger.warn("Supplied HTLC expiry time is invalid or in the past: %d", expiryTimeSecs); - expiryTimeSecs = currTimeSecs + defaultExpiryTimeSecs; - } const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, ""); const lockInfoStr = createAssetLockInfoSerialized(hashValue, expiryTimeSecs); diff --git a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js index 95e8d8cbe..b58075f53 100644 --- a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js +++ b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js @@ -71,25 +71,32 @@ describe("AssetManager", () => { }); it("asset lock fails with invalid parameters", async () => { - let assetLockInvocation = await assetManager.createHTLC(null, assetType, assetID, recipientECert, "", 0); + let expiryTimeSecs = Math.floor(Date.now()/1000) + 300; // Convert epoch milliseconds to seconds and add 5 minutes + let assetLockInvocation = await assetManager.createHTLC(null, assetType, assetID, recipientECert, "", "", expiryTimeSecs); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createHTLC(amc, "", assetID, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(false); - assetLockInvocation = await assetManager.createHTLC(amc, "", assetID, recipientECert, "", 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, "", recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(false); - assetLockInvocation = await assetManager.createHTLC(amc, assetType, "", recipientECert, "", 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, "", "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(false); - assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, "", "", 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", "", expiryTimeSecs - 600); // Expiry time in the past expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); @@ -110,23 +117,22 @@ describe("AssetManager", () => { expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); amcStub.withArgs("LockAsset", assetAgreementStr, sinon.match.any).resolves(true); - assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", hashValue, 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", hashValue, expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); - assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", "", 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); - expect(assetLockInvocation.preimage.length).to.equal(20); + expect(assetLockInvocation.preimage.length).to.be.above(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); - const preimage = "something"; - assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, preimage, "", 0); + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "some-preimage", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); - expect(assetLockInvocation.preimage.length).to.equal(preimage.length); + expect(assetLockInvocation.preimage.length).to.be.above(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); }); @@ -140,25 +146,32 @@ describe("AssetManager", () => { }); it("asset lock fails with invalid parameters", async () => { - let assetLockInvocation = await assetManager.createFungibleHTLC(null, fungibleAssetType, numUnits, recipientECert, "", 0); + let expiryTimeSecs = Math.floor(Date.now()/1000) + 300; // Convert epoch milliseconds to seconds and add 5 minutes + let assetLockInvocation = await assetManager.createFungibleHTLC(null, fungibleAssetType, numUnits, recipientECert, "", "", expiryTimeSecs); + expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); + expect(assetLockInvocation.preimage).to.be.a("string"); + expect(assetLockInvocation.preimage.length).to.equal(0); + expect(assetLockInvocation.result).to.be.a('boolean'); + expect(assetLockInvocation.result).to.equal(false); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, "", numUnits, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(false); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, "", numUnits, recipientECert, "", 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, -1, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(false); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, -1, recipientECert, "", 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, "", "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(false); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, "", "", 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", "", expiryTimeSecs - 600); // Expiry time in the past expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); @@ -179,23 +192,22 @@ describe("AssetManager", () => { expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); amcStub.withArgs("LockFungibleAsset", assetAgreementStr, sinon.match.any).resolves(true); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", hashValue, 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", hashValue, expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", "", 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); - expect(assetLockInvocation.preimage.length).to.equal(20); + expect(assetLockInvocation.preimage.length).to.be.above(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); - const preimage = "something"; - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, preimage, "", 0); + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "some-preimage", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); - expect(assetLockInvocation.preimage.length).to.equal(preimage.length); + expect(assetLockInvocation.preimage.length).to.be.above(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); }); From 4f5eae8b98db68da2b5d8eb75528b5c4787540eb Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Sun, 16 May 2021 15:14:33 +0000 Subject: [PATCH 08/16] Fabric Interop CC asset exchange code cleanup Changed 'log.Info(fmt.Sprintf(...))' to 'log.Infof(...)'. Removed unnecessary 'uint64' casts. Added placeholder functions to refactor error reporting code (TODO: actual refactoring). Signed-off-by: VRamakrishna --- .../contracts/interop/manage_assets.go | 73 ++++++++++++------- .../contracts/interop/manage_assets_test.go | 28 +++---- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/core/network/fabric-interop-cc/contracts/interop/manage_assets.go b/core/network/fabric-interop-cc/contracts/interop/manage_assets.go index c78597860..d0b89950e 100644 --- a/core/network/fabric-interop-cc/contracts/interop/manage_assets.go +++ b/core/network/fabric-interop-cc/contracts/interop/manage_assets.go @@ -47,6 +47,25 @@ const( contractIdPrefix = "ContractId_" // prefix for the map, contractId --> asset-key ) +// helper functions to log and return errors +func logAndReturnErrorfBool(retVal bool, format string, args ...interface{}) (bool, error) { + errorMsg := fmt.Sprintf(format, args...) + log.Error(errorMsg) + return retVal, errors.New(errorMsg) +} + +func logAndReturnErrorfInt(retVal int, format string, args ...interface{}) (int, error) { + errorMsg := fmt.Sprintf(format, args...) + log.Error(errorMsg) + return retVal, errors.New(errorMsg) +} + +func logAndReturnErrorfString(retVal string, format string, args ...interface{}) (string, error) { + errorMsg := fmt.Sprintf(format, args...) + log.Error(errorMsg) + return retVal, errors.New(errorMsg) +} + // function to generate a "SHA256" hash in base64 format for a given preimage func generateSHA256HashInBase64Form(preimage string) string { hasher := sha256.New() @@ -98,7 +117,7 @@ func (s *SmartContract) LockAsset(ctx contractapi.TransactionContextInterface, a return "", err } //display the requested asset agreement - log.Info(fmt.Sprintf("assetExchangeAgreement: %+v\n", assetAgreement)) + log.Infof("assetExchangeAgreement: %+v\n", assetAgreement) lockInfoHTLC := &common.AssetLockHTLC{} err = proto.Unmarshal([]byte(lockInfoBytes), lockInfoHTLC) @@ -107,7 +126,7 @@ func (s *SmartContract) LockAsset(ctx contractapi.TransactionContextInterface, a return "", err } //display the passed lock information - log.Info(fmt.Sprintf("lockInfoHTLC: %+v\n", lockInfoHTLC)) + log.Infof("lockInfoHTLC: %+v\n", lockInfoHTLC) if lockInfoHTLC.TimeSpec != common.AssetLockHTLC_EPOCH { errorMsg := "only EPOCH time is supported at present" @@ -121,7 +140,7 @@ func (s *SmartContract) LockAsset(ctx contractapi.TransactionContextInterface, a return "", err } - assetLockVal := AssetLockValue{Locker: assetAgreement.Locker, Recipient: assetAgreement.Recipient, Hash: string(lockInfoHTLC.Hash), ExpiryTimeSecs: lockInfoHTLC.ExpiryTimeSecs} + assetLockVal := AssetLockValue{Locker: assetAgreement.Locker, Recipient: assetAgreement.Recipient, Hash: string(lockInfoHTLC.HashBase64), ExpiryTimeSecs: lockInfoHTLC.ExpiryTimeSecs} assetLockValBytes, err := ctx.GetStub().GetState(assetLockKey) if err != nil { @@ -173,7 +192,7 @@ func (s *SmartContract) UnLockAsset(ctx contractapi.TransactionContextInterface, return err } //display the requested asset agreement - log.Info(fmt.Sprintf("assetExchangeAgreement: %+v\n", assetAgreement)) + log.Infof("assetExchangeAgreement: %+v\n", assetAgreement) assetLockKey, _, err := generateAssetLockKeyAndContractId(ctx, assetAgreement) if err != nil { @@ -187,7 +206,7 @@ func (s *SmartContract) UnLockAsset(ctx contractapi.TransactionContextInterface, return err } - if assetLockValBytes == nil { + if assetLockValBytes == nil { errorMsg := fmt.Sprintf("no asset of type %s and ID %s is locked", assetAgreement.Type, assetAgreement.Id) log.Error(errorMsg) return errors.New(errorMsg) @@ -209,7 +228,7 @@ func (s *SmartContract) UnLockAsset(ctx contractapi.TransactionContextInterface, // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) < assetLockVal.ExpiryTimeSecs { + if currentTimeSecs < assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("cannot unlock asset of type %s and ID %s as the expiry time is not yet elapsed", assetAgreement.Type, assetAgreement.Id) log.Error(errorMsg) return errors.New(errorMsg) @@ -235,7 +254,7 @@ func (s *SmartContract) IsAssetLocked(ctx contractapi.TransactionContextInterfac return false, err } //display the requested asset agreement - log.Info(fmt.Sprintf("assetExchangeAgreement: %+v\n", assetAgreement)) + log.Infof("assetExchangeAgreement: %+v\n", assetAgreement) assetLockKey, _, err := generateAssetLockKeyAndContractId(ctx, assetAgreement) if err != nil { @@ -262,11 +281,11 @@ func (s *SmartContract) IsAssetLocked(ctx contractapi.TransactionContextInterfac log.Error(errorMsg) return false, errors.New(errorMsg) } - log.Info(fmt.Sprintf("assetLockVal: %+v\n", assetLockVal)) + log.Infof("assetLockVal: %+v\n", assetLockVal) // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) >= assetLockVal.ExpiryTimeSecs { + if currentTimeSecs >= assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("expiry time for asset of type %s and ID %s is already elapsed", assetAgreement.Type, assetAgreement.Id) log.Error(errorMsg) return false, errors.New(errorMsg) @@ -307,9 +326,9 @@ func checkIfCorrectPreimage(preimageBase64 string, hashBase64 string) (bool, err shaHashBase64 := generateSHA256HashInBase64Form(string(preimage)) if shaHashBase64 == hashBase64 { - log.Info(fmt.Sprintf("%s: preimage %s is passed correctly.\n", funName, preimage)) + log.Infof("%s: preimage %s is passed correctly.\n", funName, preimage) } else { - log.Info(fmt.Sprintf("%s: preimage %s is not passed correctly.\n", funName, preimage)) + log.Infof("%s: preimage %s is not passed correctly.\n", funName, preimage) return false, nil } return true, nil @@ -325,7 +344,7 @@ func (s *SmartContract) ClaimAsset(ctx contractapi.TransactionContextInterface, return err } // display the requested asset agreement - log.Info(fmt.Sprintf("assetExchangeAgreement: %+v\n", assetAgreement)) + log.Infof("assetExchangeAgreement: %+v\n", assetAgreement) claimInfo := &common.AssetClaimHTLC{} err = proto.Unmarshal([]byte(claimInfoBytes), claimInfo) @@ -336,7 +355,7 @@ func (s *SmartContract) ClaimAsset(ctx contractapi.TransactionContextInterface, } // display the claim information - log.Info(fmt.Sprintf("claimInfo: %+v\n", claimInfo)) + log.Infof("claimInfo: %+v\n", claimInfo) assetLockKey, _, err := generateAssetLockKeyAndContractId(ctx, assetAgreement) if err != nil { @@ -372,14 +391,14 @@ func (s *SmartContract) ClaimAsset(ctx contractapi.TransactionContextInterface, // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) >= assetLockVal.ExpiryTimeSecs { + if currentTimeSecs >= assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("cannot claim asset of type %s and ID %s as the expiry time is already elapsed", assetAgreement.Type, assetAgreement.Id) log.Error(errorMsg) return errors.New(errorMsg) } // compute the hash from the preimage - isCorrectPreimage, err := checkIfCorrectPreimage(string(claimInfo.HashPreimage), string(assetLockVal.Hash)) + isCorrectPreimage, err := checkIfCorrectPreimage(string(claimInfo.HashPreimageBase64), string(assetLockVal.Hash)) if err != nil { errorMsg := fmt.Sprintf("claim asset of type %s and ID %s error: %v", assetAgreement.Type, assetAgreement.Id, err) log.Error(errorMsg) @@ -452,7 +471,7 @@ func (s *SmartContract) UnLockAssetUsingContractId(ctx contractapi.TransactionCo // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) < assetLockVal.ExpiryTimeSecs { + if currentTimeSecs < assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("cannot unlock asset associated with the contractId %s as the expiry time is not yet elapsed", contractId) log.Error(errorMsg) return errors.New(errorMsg) @@ -493,18 +512,18 @@ func (s *SmartContract) ClaimAssetUsingContractId(ctx contractapi.TransactionCon } // display the claim information - log.Info(fmt.Sprintf("claimInfo: %+v\n", claimInfo)) + log.Infof("claimInfo: %+v\n", claimInfo) // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) >= assetLockVal.ExpiryTimeSecs { + if currentTimeSecs >= assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("cannot claim asset associated with contractId %s as the expiry time is already elapsed", contractId) log.Error(errorMsg) return errors.New(errorMsg) } // compute the hash from the preimage - isCorrectPreimage, err := checkIfCorrectPreimage(string(claimInfo.HashPreimage), string(assetLockVal.Hash)) + isCorrectPreimage, err := checkIfCorrectPreimage(string(claimInfo.HashPreimageBase64), string(assetLockVal.Hash)) if err != nil { errorMsg := fmt.Sprintf("claim asset associated with contractId %s failed with error: %v", contractId, err) log.Error(errorMsg) @@ -545,7 +564,7 @@ func (s *SmartContract) IsAssetLockedQueryUsingContractId(ctx contractapi.Transa // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) >= assetLockVal.ExpiryTimeSecs { + if currentTimeSecs >= assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("expiry time for asset associated with contractId %s is already elapsed", contractId) log.Error(errorMsg) return false, errors.New(errorMsg) @@ -577,7 +596,7 @@ func (s *SmartContract) LockFungibleAsset(ctx contractapi.TransactionContextInte } //display the passed lock information - log.Info(fmt.Sprintf("lockInfoHTLC: %+v\n", lockInfoHTLC)) + log.Infof("lockInfoHTLC: %+v\n", lockInfoHTLC) if lockInfoHTLC.TimeSpec != common.AssetLockHTLC_EPOCH { errorMsg := "only EPOCH time is supported at present" @@ -589,7 +608,7 @@ func (s *SmartContract) LockFungibleAsset(ctx contractapi.TransactionContextInte contractId := generateFungibleAssetLockContractId(ctx, assetAgreement) assetLockVal := FungibleAssetLockValue{Type: assetAgreement.Type, NumUnits: assetAgreement.NumUnits, Locker: assetAgreement.Locker, - Recipient: assetAgreement.Recipient, Hash: string(lockInfoHTLC.Hash), ExpiryTimeSecs: lockInfoHTLC.ExpiryTimeSecs} + Recipient: assetAgreement.Recipient, Hash: string(lockInfoHTLC.HashBase64), ExpiryTimeSecs: lockInfoHTLC.ExpiryTimeSecs} assetLockValBytes, err := ctx.GetStub().GetState(contractId) if err != nil { @@ -660,7 +679,7 @@ func (s *SmartContract) IsFungibleAssetLocked(ctx contractapi.TransactionContext // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) >= assetLockVal.ExpiryTimeSecs { + if currentTimeSecs >= assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("expiry time for fungible asset associated with contractId %s is already elapsed", contractId) log.Error(errorMsg) return false, errors.New(errorMsg) @@ -687,18 +706,18 @@ func (s *SmartContract) ClaimFungibleAsset(ctx contractapi.TransactionContextInt } // display the claim information - log.Info(fmt.Sprintf("claimInfo: %+v\n", claimInfo)) + log.Infof("claimInfo: %+v\n", claimInfo) // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) >= assetLockVal.ExpiryTimeSecs { + if currentTimeSecs >= assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("cannot claim fungible asset associated with contractId %s as the expiry time is already elapsed", contractId) log.Error(errorMsg) return errors.New(errorMsg) } // compute the hash from the preimage - isCorrectPreimage, err := checkIfCorrectPreimage(string(claimInfo.HashPreimage), string(assetLockVal.Hash)) + isCorrectPreimage, err := checkIfCorrectPreimage(string(claimInfo.HashPreimageBase64), string(assetLockVal.Hash)) if err != nil { errorMsg := fmt.Sprintf("claim fungible asset associated with contractId %s failed with error: %v", contractId, err) log.Error(errorMsg) @@ -732,7 +751,7 @@ func (s *SmartContract) UnLockFungibleAsset(ctx contractapi.TransactionContextIn // Check if expiry time is elapsed currentTimeSecs := uint64(time.Now().Unix()) - if uint64(currentTimeSecs) < assetLockVal.ExpiryTimeSecs { + if currentTimeSecs < assetLockVal.ExpiryTimeSecs { errorMsg := fmt.Sprintf("cannot unlock fungible asset associated with the contractId %s as the expiry time is not yet elapsed", contractId) log.Error(errorMsg) return errors.New(errorMsg) diff --git a/core/network/fabric-interop-cc/contracts/interop/manage_assets_test.go b/core/network/fabric-interop-cc/contracts/interop/manage_assets_test.go index 86c70ae25..58ee8146a 100644 --- a/core/network/fabric-interop-cc/contracts/interop/manage_assets_test.go +++ b/core/network/fabric-interop-cc/contracts/interop/manage_assets_test.go @@ -35,7 +35,7 @@ func TestLockAsset(t *testing.T) { currentTimeSecs := uint64(time.Now().Unix()) lockInfoHTLC := &common.AssetLockHTLC { - Hash: []byte(hashBase64), + HashBase64: []byte(hashBase64), // lock for next 5 minutes ExpiryTimeSecs: currentTimeSecs + defaultTimeLockSecs, TimeSpec: common.AssetLockHTLC_EPOCH, @@ -66,7 +66,7 @@ func TestLockAsset(t *testing.T) { // no need to set chaincodeStub.GetStateReturns below since the error is hit before GetState() ledger access in LockAsset() lockInfoHTLC = &common.AssetLockHTLC { - Hash: []byte(hashBase64), + HashBase64: []byte(hashBase64), // lock for next 5 mintues ExpiryTimeSecs: currentTimeSecs + defaultTimeLockSecs, // TimeSpec of AssetLockHTLC_DURATION is not currently supported @@ -91,7 +91,7 @@ func TestUnLockAsset(t *testing.T) { currentTimeSecs := uint64(time.Now().Unix()) lockInfoHTLC := &common.AssetLockHTLC { - Hash: []byte(hashBase64), + HashBase64: []byte(hashBase64), // lock for sometime in the past for testing UnLockAsset functionality ExpiryTimeSecs: currentTimeSecs - defaultTimeLockSecs, TimeSpec: common.AssetLockHTLC_EPOCH, @@ -158,7 +158,7 @@ func TestIsAssetLocked(t *testing.T) { currentTimeSecs := uint64(time.Now().Unix()) lockInfoHTLC := &common.AssetLockHTLC { - Hash: []byte(hashBase64), + HashBase64: []byte(hashBase64), ExpiryTimeSecs: currentTimeSecs + defaultTimeLockSecs, TimeSpec: common.AssetLockHTLC_EPOCH, } @@ -308,7 +308,7 @@ func TestClaimAsset(t *testing.T) { currentTimeSecs := uint64(time.Now().Unix()) lockInfoHTLC := &common.AssetLockHTLC { - Hash: []byte(hashBase64), + HashBase64: []byte(hashBase64), ExpiryTimeSecs: currentTimeSecs + defaultTimeLockSecs, TimeSpec: common.AssetLockHTLC_EPOCH, } @@ -323,7 +323,7 @@ func TestClaimAsset(t *testing.T) { assetAgreementBytes, _ := proto.Marshal(assetAgreement) claimInfo := &common.AssetClaimHTLC { - HashPreimage: []byte(preimageBase64), + HashPreimageBase64: []byte(preimageBase64), } claimInfoBytes, _ := proto.Marshal(claimInfo) @@ -352,7 +352,7 @@ func TestClaimAsset(t *testing.T) { wrongPreimage := "abc" wrongPreimageBase64 := base64.StdEncoding.EncodeToString([]byte(wrongPreimage)) wrongClaimInfo := &common.AssetClaimHTLC { - HashPreimage: []byte(wrongPreimageBase64), + HashPreimageBase64: []byte(wrongPreimageBase64), } wrongClaimInfoBytes, _ := proto.Marshal(wrongClaimInfo) @@ -499,7 +499,7 @@ func TestClaimAssetUsingContractId(t *testing.T) { assetLockKey, contractId, _ := generateAssetLockKeyAndContractId(ctx, assetAgreement) claimInfo := &common.AssetClaimHTLC { - HashPreimage: []byte(preimageBase64), + HashPreimageBase64: []byte(preimageBase64), } claimInfoBytes, _ := proto.Marshal(claimInfo) @@ -549,7 +549,7 @@ func TestClaimAssetUsingContractId(t *testing.T) { wrongPreimage := "abc" wrongPreimageBase64 := base64.StdEncoding.EncodeToString([]byte(wrongPreimage)) wrongClaimInfo := &common.AssetClaimHTLC { - HashPreimage: []byte(wrongPreimageBase64), + HashPreimageBase64: []byte(wrongPreimageBase64), } wrongClaimInfoBytes, _ := proto.Marshal(wrongClaimInfo) assetLockVal = AssetLockValue{Locker: locker, Recipient: recipient, Hash: hashBase64, ExpiryTimeSecs: currentTimeSecs + defaultTimeLockSecs} @@ -692,7 +692,7 @@ func TestLockFungibleAsset(t *testing.T) { // Test failure with TimeSpec that is part of lock information not being currently supported // no need to set chaincodeStub.GetStateReturns below since the error is hit before GetState() ledger access lockInfoHTLC := &common.AssetLockHTLC { - Hash: []byte(hashBase64), + HashBase64: []byte(hashBase64), // lock for next 5 mintues ExpiryTimeSecs: currentTimeSecs + defaultTimeLockSecs, // TimeSpec of AssetLockHTLC_DURATION is not currently supported @@ -707,7 +707,7 @@ func TestLockFungibleAsset(t *testing.T) { // Test failure with GetState(contractId) fail to read the world state chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve contractId %s", contractId)) lockInfoHTLC = &common.AssetLockHTLC { - Hash: []byte(hashBase64), + HashBase64: []byte(hashBase64), // lock for next 5 mintues ExpiryTimeSecs: currentTimeSecs + defaultTimeLockSecs, // TimeSpec of AssetLockHTLC_EPOCH is only supported currently @@ -721,7 +721,7 @@ func TestLockFungibleAsset(t *testing.T) { // Test failure with contractId already existing on the ledger assetLockVal := FungibleAssetLockValue{Type: assetType, NumUnits: numUnits, Locker: locker, Recipient: recipient, - Hash: string(lockInfoHTLC.Hash), ExpiryTimeSecs: lockInfoHTLC.ExpiryTimeSecs} + Hash: string(lockInfoHTLC.HashBase64), ExpiryTimeSecs: lockInfoHTLC.ExpiryTimeSecs} assetLockValBytes, _ := json.Marshal(assetLockVal) chaincodeStub.GetStateReturns(assetLockValBytes, nil) _, err = interopcc.LockFungibleAsset(ctx, string(assetAgreementBytes), string(lockInfoBytes)) @@ -824,7 +824,7 @@ func TestClaimFungibleAsset(t *testing.T) { contractId := generateFungibleAssetLockContractId(ctx, assetAgreement) claimInfo := &common.AssetClaimHTLC { - HashPreimage: []byte(preimageBase64), + HashPreimageBase64: []byte(preimageBase64), } claimInfoBytes, _ := proto.Marshal(claimInfo) @@ -856,7 +856,7 @@ func TestClaimFungibleAsset(t *testing.T) { wrongPreimage := "abc" wrongPreimageBase64 := base64.StdEncoding.EncodeToString([]byte(wrongPreimage)) wrongClaimInfo := &common.AssetClaimHTLC { - HashPreimage: []byte(wrongPreimageBase64), + HashPreimageBase64: []byte(wrongPreimageBase64), } wrongClaimInfoBytes, _ := proto.Marshal(wrongClaimInfo) assetLockVal = FungibleAssetLockValue{Type: assetType, NumUnits: numUnits, Locker: locker, Recipient: recipient, From afa400fa397c9522f97fbd6c8205c39d53f40438 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Sun, 16 May 2021 19:16:17 +0000 Subject: [PATCH 09/16] Emitting asset exchange (HTLC) events from Fabric Interop CC Also updated the common asset locking protobuf. Signed-off-by: VRamakrishna --- .../interop-protos/common/asset_locks.proto | 14 +++ .../asset-mgmt/asset_locks_contract.go | 117 +++++++++++++++++- 2 files changed, 125 insertions(+), 6 deletions(-) diff --git a/common/interop-protos/common/asset_locks.proto b/common/interop-protos/common/asset_locks.proto index ec22bf754..04a607f5c 100644 --- a/common/interop-protos/common/asset_locks.proto +++ b/common/interop-protos/common/asset_locks.proto @@ -44,3 +44,17 @@ message FungibleAssetExchangeAgreement { string locker = 3; string recipient = 4; } + +message AssetContractHTLC { + string contractId = 1; + AssetExchangeAgreement agreement = 2; + AssetLockHTLC lock = 3; + AssetClaimHTLC claim = 4; +} + +message FungibleAssetContractHTLC { + string contractId = 1; + FungibleAssetExchangeAgreement agreement = 2; + AssetLockHTLC lock = 3; + AssetClaimHTLC claim = 4; +} diff --git a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go index 3b7dd2f0d..569eb19d9 100644 --- a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go +++ b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go @@ -51,7 +51,26 @@ func (amc *AssetManagementContract) LockAsset(ctx contractapi.TransactionContext log.Error(err.Error()) return false, err } - return amc.assetManagement.LockAsset(ctx.GetStub(), assetAgreement, lockInfo) + retVal, err := amc.assetManagement.LockAsset(ctx.GetStub(), assetAgreement, lockInfo) + if retVal && err == nil { + lockInfoHTLC := &common.AssetLockHTLC{} + err = proto.Unmarshal(lockInfo.LockInfo, lockInfoHTLC) + if err == nil { + contractInfo := &common.AssetContractHTLC{ + Agreement: assetAgreement, + Lock: lockInfoHTLC, + } + contractInfoBytes, err := proto.Marshal(contractInfo) + if err == nil { + err = ctx.GetStub().SetEvent("LockAsset", contractInfoBytes) + } + } + if err != nil { + log.Warn("Unable to set 'LockAsset' event") + log.Warn(err.Error()) + } + } + return retVal, err } func (amc *AssetManagementContract) LockFungibleAsset(ctx contractapi.TransactionContextInterface, fungibleAssetExchangeAgreementSerializedProto, lockInfoSerializedProto string) (string, error) { @@ -75,7 +94,27 @@ func (amc *AssetManagementContract) LockFungibleAsset(ctx contractapi.Transactio log.Error(err.Error()) return "", err } - return amc.assetManagement.LockFungibleAsset(ctx.GetStub(), assetAgreement, lockInfo) + retVal, err := amc.assetManagement.LockFungibleAsset(ctx.GetStub(), assetAgreement, lockInfo) + if err == nil { + lockInfoHTLC := &common.AssetLockHTLC{} + err = proto.Unmarshal(lockInfo.LockInfo, lockInfoHTLC) + if err == nil { + contractInfo := &common.FungibleAssetContractHTLC{ + ContractId: retVal, + Agreement: assetAgreement, + Lock: lockInfoHTLC, + } + contractInfoBytes, err := proto.Marshal(contractInfo) + if err == nil { + err = ctx.GetStub().SetEvent("LockFungibleAsset", contractInfoBytes) + } + } + if err != nil { + log.Warn("Unable to set 'LockFungibleAsset' event") + log.Warn(err.Error()) + } + } + return retVal, err } func (amc *AssetManagementContract) IsAssetLocked(ctx contractapi.TransactionContextInterface, assetAgreementSerializedProto string) (bool, error) { @@ -121,7 +160,26 @@ func (amc *AssetManagementContract) ClaimAsset(ctx contractapi.TransactionContex log.Error(err.Error()) return false, err } - return amc.assetManagement.ClaimAsset(ctx.GetStub(), assetAgreement, claimInfo) + retVal, err := amc.assetManagement.ClaimAsset(ctx.GetStub(), assetAgreement, claimInfo) + if retVal && err == nil { + claimInfoHTLC := &common.AssetClaimHTLC{} + err = proto.Unmarshal(claimInfo.ClaimInfo, claimInfoHTLC) + if err == nil { + contractInfo := &common.AssetContractHTLC{ + Agreement: assetAgreement, + Claim: claimInfoHTLC, + } + contractInfoBytes, err := proto.Marshal(contractInfo) + if err == nil { + err = ctx.GetStub().SetEvent("ClaimAsset", contractInfoBytes) + } + } + if err != nil { + log.Warn("Unable to set 'ClaimAsset' event") + log.Warn(err.Error()) + } + } + return retVal, err } func (amc *AssetManagementContract) ClaimFungibleAsset(ctx contractapi.TransactionContextInterface, contractId, claimInfoSerializedProto string) (bool, error) { @@ -139,7 +197,26 @@ func (amc *AssetManagementContract) ClaimFungibleAsset(ctx contractapi.Transacti log.Error(err.Error()) return false, err } - return amc.assetManagement.ClaimFungibleAsset(ctx.GetStub(), contractId, claimInfo) + retVal, err := amc.assetManagement.ClaimFungibleAsset(ctx.GetStub(), contractId, claimInfo) + if retVal && err == nil { + claimInfoHTLC := &common.AssetClaimHTLC{} + err = proto.Unmarshal(claimInfo.ClaimInfo, claimInfoHTLC) + if err == nil { + contractInfo := &common.FungibleAssetContractHTLC{ + ContractId: contractId, + Claim: claimInfoHTLC, + } + contractInfoBytes, err := proto.Marshal(contractInfo) + if err == nil { + err = ctx.GetStub().SetEvent("ClaimFungibleAsset", contractInfoBytes) + } + } + if err != nil { + log.Warn("Unable to set 'ClaimFungibleAsset' event") + log.Warn(err.Error()) + } + } + return retVal, err } func (amc *AssetManagementContract) UnlockAsset(ctx contractapi.TransactionContextInterface, assetAgreementSerializedProto string) (bool, error) { @@ -153,7 +230,21 @@ func (amc *AssetManagementContract) UnlockAsset(ctx contractapi.TransactionConte log.Error(err.Error()) return false, err } - return amc.assetManagement.UnlockAsset(ctx.GetStub(), assetAgreement) + retVal, err := amc.assetManagement.UnlockAsset(ctx.GetStub(), assetAgreement) + if retVal && err == nil { + contractInfo := &common.AssetContractHTLC{ + Agreement: assetAgreement, + } + contractInfoBytes, err := proto.Marshal(contractInfo) + if err == nil { + err = ctx.GetStub().SetEvent("UnlockAsset", contractInfoBytes) + } + if err != nil { + log.Warn("Unable to set 'UnlockAsset' event") + log.Warn(err.Error()) + } + } + return retVal, err } func (amc *AssetManagementContract) UnlockFungibleAsset(ctx contractapi.TransactionContextInterface, contractId string) (bool, error) { @@ -161,7 +252,21 @@ func (amc *AssetManagementContract) UnlockFungibleAsset(ctx contractapi.Transact log.Error("empty contract id") return false, fmt.Errorf("empty contract id") } - return amc.assetManagement.UnlockFungibleAsset(ctx.GetStub(), contractId) + retVal, err := amc.assetManagement.UnlockFungibleAsset(ctx.GetStub(), contractId) + if retVal && err == nil { + contractInfo := &common.AssetContractHTLC{ + ContractId: contractId, + } + contractInfoBytes, err := proto.Marshal(contractInfo) + if err == nil { + err = ctx.GetStub().SetEvent("UnlockFungibleAsset", contractInfoBytes) + } + if err != nil { + log.Warn("Unable to set 'UnlockFungibleAsset' event") + log.Warn(err.Error()) + } + } + return retVal, err } From 04239fc9dfc8af3bb127f7ca99d23d3d7fb4de60 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Sun, 16 May 2021 22:01:20 +0000 Subject: [PATCH 10/16] Added timeout listeners to asset locking functions in Fabric Interop SDK Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 47 +++++++++-- .../test/AssetManager.js | 79 ++++++++++++------- 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index 955bad148..6eb1c4ebf 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -14,7 +14,7 @@ import crypto from "crypto"; import fabproto6 from "fabric-protos"; import * as helpers from "./helpers"; import assetLocksPb from "../protos-js/common/asset_locks_pb"; -import { Contract } from "fabric-network"; +import { Contract, ContractListener } from "fabric-network"; const logger = log4js.getLogger("InteroperableHelper"); @@ -94,6 +94,7 @@ const createHTLC = async ( hashPreimage: string, hashValue: string, expiryTimeSecs: number, + timeoutCallback: (c: Contract, t: string, i: string, r: string, p: string, v: string) => any, ): Promise<{ preimage: any; result: any }> => { if (!contract) @@ -144,6 +145,13 @@ const createHTLC = async ( if (submitError) { throw new Error(`LockAsset submitTransaction Error: ${submitError}`); } + + if (timeoutCallback) + { + // Start timer for lock expiration + setTimeout(timeoutCallback, (expiryTimeSecs * 1000) - Date.now(), contract, assetType, assetID, recipientECert, hashPreimage, hashValue); + } + return { preimage: hashPreimage, result: result }; }; @@ -159,33 +167,34 @@ const createFungibleHTLC = async ( hashPreimage: string, hashValue: string, expiryTimeSecs: number, + timeoutCallback: (c: Contract, i: string, t: string, n: number, r: string, p: string, v: string) => any, ): Promise<{ preimage: any; result: any }> => { if (!contract) { logger.error("Contract handle not supplied"); - return { preimage: "", result: false }; + return { preimage: "", result: "" }; } if (!assetType) { logger.error("Asset type not supplied"); - return { preimage: "", result: false }; + return { preimage: "", result: "" }; } if (numUnits <= 0) { logger.error("Asset count must be a positive integer"); - return { preimage: "", result: false }; + return { preimage: "", result: "" }; } if (!recipientECert) { logger.error("Recipient ECert not supplied"); - return { preimage: "", result: false }; + return { preimage: "", result: "" }; } const currTimeSecs = Math.floor(Date.now()/1000); // Convert epoch milliseconds to seconds if (expiryTimeSecs <= currTimeSecs) { logger.error("Supplied expiry time invalid or in the past: %s; current time: %s", new Date(expiryTimeSecs).toISOString(), new Date(currTimeSecs).toISOString()); - return { preimage: "", result: false }; + return { preimage: "", result: "" }; } if (!hashValue || hashValue.length == 0) @@ -203,13 +212,20 @@ const createFungibleHTLC = async ( const lockInfoStr = createAssetLockInfoSerialized(hashValue, expiryTimeSecs); // Normal invoke function - const [result, submitError] = await helpers.handlePromise( + const [contractId, submitError] = await helpers.handlePromise( contract.submitTransaction("LockFungibleAsset", assetExchangeAgreementStr, lockInfoStr), ); if (submitError) { throw new Error(`LockFungibleAsset submitTransaction Error: ${submitError}`); } - return { preimage: hashPreimage, result: result }; + + if (timeoutCallback) + { + // Start timer for lock expiration + setTimeout(timeoutCallback, (expiryTimeSecs * 1000) - Date.now(), contract, contractId, assetType, numUnits, recipientECert, hashPreimage, hashValue); + } + + return { preimage: hashPreimage, result: contractId }; }; /** @@ -502,6 +518,21 @@ const isFungibleAssetLockedInHTLC = async ( return result; }; +/*const StartHTLCAssetLockEventListener = ( + contract: Contract, + lockCallback: (c: Contract, i: string, t: string, n: number, r: string, p: string, v: string) => any, + assetType: string, + assetId: string, + recipientECert: string, + lockerECert: string, +): void => { + const listener: ContractListener = async (event) => { + if (event.eventName === 'LockAssetHTLC') { + const assetLockContractInfo = event.payload.toString('utf8'); + } + }; +}*/ + export { createAssetExchangeAgreementSerialized, createFungibleAssetExchangeAgreementSerialized, diff --git a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js index b58075f53..6e494eb0a 100644 --- a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js +++ b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js @@ -53,6 +53,10 @@ describe("AssetManager", () => { return userIdentity; } + async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + beforeEach(async () => { await initializeWallet(); const network = sinon.createStubInstance(NetworkImpl); @@ -129,12 +133,22 @@ describe("AssetManager", () => { expect(assetLockInvocation.preimage.length).to.be.above(0); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); - assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, "some-preimage", "", expiryTimeSecs); + const hashPreimage = "some-preimage"; + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, hashPreimage, "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); - expect(assetLockInvocation.preimage).to.be.a("string"); - expect(assetLockInvocation.preimage.length).to.be.above(0); + expect(assetLockInvocation.preimage).to.equal(hashPreimage); expect(assetLockInvocation.result).to.be.a('boolean'); expect(assetLockInvocation.result).to.equal(true); + const testAttr = assetType + ':' + assetID + ':' + recipientECert + ':' + hashPreimage + ':' + hashValue; + const timeoutCb = function(c, t, i, r, p, v) { + console.log('Asset lock TIMEOUT at', Date()); + c.testAttr = t + ':' + i + ':' + r + ':' + p + ':' + v; + }; + expiryTimeSecs = Math.floor(Date.now()/1000) + 3; // 3 seconds + assetLockInvocation = await assetManager.createHTLC(amc, assetType, assetID, recipientECert, hashPreimage, hashValue, expiryTimeSecs, timeoutCb); + await sleep(4000); + expect(amc).to.have.own.property('testAttr'); + expect(amc.testAttr).to.equal(testAttr); }); }); @@ -142,7 +156,7 @@ describe("AssetManager", () => { let amcStub; beforeEach(() => { - amcStub = sinon.stub(amc, "submitTransaction").resolves(false); + amcStub = sinon.stub(amc, "submitTransaction").resolves(""); }); it("asset lock fails with invalid parameters", async () => { @@ -151,32 +165,32 @@ describe("AssetManager", () => { expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(false); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(''); assetLockInvocation = await assetManager.createFungibleHTLC(amc, "", numUnits, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(false); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(''); assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, -1, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(false); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(''); assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, "", "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(false); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(''); assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", "", expiryTimeSecs - 600); // Expiry time in the past expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(false); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(''); }); it("submit asset lock invocation", async () => { @@ -184,32 +198,43 @@ describe("AssetManager", () => { const hashValue = "abcdef123456"; let expiryTimeSecs = Math.floor(Date.now()/1000) + 300; // Convert epoch milliseconds to seconds and add 5 minutes let lockInfoStr = assetManager.createAssetLockInfoSerialized(hashValue, expiryTimeSecs); - amcStub.withArgs("LockFungibleAsset", assetAgreementStr, lockInfoStr).resolves(true); + const contractId = "CONTRACT-1234"; + amcStub.withArgs("LockFungibleAsset", assetAgreementStr, lockInfoStr).resolves(contractId); let assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", hashValue, expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(true); - amcStub.withArgs("LockFungibleAsset", assetAgreementStr, sinon.match.any).resolves(true); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(contractId); + amcStub.withArgs("LockFungibleAsset", assetAgreementStr, sinon.match.any).resolves(contractId); assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", hashValue, expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.equal(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(true); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(contractId); assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "", "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); expect(assetLockInvocation.preimage).to.be.a("string"); expect(assetLockInvocation.preimage.length).to.be.above(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(true); - assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, "some-preimage", "", expiryTimeSecs); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(contractId); + const hashPreimage = "some-preimage"; + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, hashPreimage, "", expiryTimeSecs); expect(assetLockInvocation).to.be.an('object').that.has.all.keys('preimage', 'result'); - expect(assetLockInvocation.preimage).to.be.a("string"); - expect(assetLockInvocation.preimage.length).to.be.above(0); - expect(assetLockInvocation.result).to.be.a('boolean'); - expect(assetLockInvocation.result).to.equal(true); + expect(assetLockInvocation.preimage).to.equal(hashPreimage); + expect(assetLockInvocation.result).to.be.a('string'); + expect(assetLockInvocation.result).to.equal(contractId); + const testAttr = contractId + ':' + fungibleAssetType + ':' + numUnits + ':' + recipientECert + ':' + hashPreimage + ':' + hashValue; + const timeoutCb = function(c, i, t, n, r, p, v) { + console.log('Fungible asset lock TIMEOUT at', Date()); + c.testAttr = i + ':' + t + ':' + n + ':' + r + ':' + p + ':' + v; + }; + expiryTimeSecs = Math.floor(Date.now()/1000) + 3; // 3 seconds + assetLockInvocation = await assetManager.createFungibleHTLC(amc, fungibleAssetType, numUnits, recipientECert, hashPreimage, hashValue, expiryTimeSecs, timeoutCb); + await sleep(4000); + expect(amc).to.have.own.property('testAttr'); + expect(amc.testAttr).to.equal(testAttr); }); }); From 4694f61bf62bd951544978e3e22212b8a668c6c5 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Sun, 16 May 2021 22:52:30 +0000 Subject: [PATCH 11/16] HTLC asset event listener functions in Fabric Interop SDK Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 143 +++++++++++++++++- 1 file changed, 138 insertions(+), 5 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index 6eb1c4ebf..4b98d1a8e 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -518,20 +518,153 @@ const isFungibleAssetLockedInHTLC = async ( return result; }; -/*const StartHTLCAssetLockEventListener = ( +const StartHTLCAssetLockListener = ( contract: Contract, - lockCallback: (c: Contract, i: string, t: string, n: number, r: string, p: string, v: string) => any, + lockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, v: string) => any, + contractId: string, assetType: string, assetId: string, recipientECert: string, lockerECert: string, ): void => { const listener: ContractListener = async (event) => { - if (event.eventName === 'LockAssetHTLC') { - const assetLockContractInfo = event.payload.toString('utf8'); + if (event.eventName === 'LockAsset') { + const assetLockContractInfo: assetLocksPb.AssetContractHTLC = assetLocksPb.AssetContractHTLC.deserializeBinary(event.payload); + const infoContractId = assetLockContractInfo.getContractid(); + if (contractId && contractId.length > 0) { + if (infoContractId.length > 0 && infoContractId !== contractId) { + return; + } + } + const infoAssetType = assetLockContractInfo.getAgreement().getType(); + if (assetType && assetType.length > 0) { + if (infoAssetType.length > 0 && infoAssetType !== assetType) { + return; + } + } + const infoAssetId = assetLockContractInfo.getAgreement().getId(); + if (assetId && assetId.length > 0) { + if (infoAssetId.length > 0 && infoAssetId !== assetId) { + return; + } + } + const infoRecipient = assetLockContractInfo.getAgreement().getRecipient(); + if (recipientECert && recipientECert.length > 0) { + if (infoRecipient.length > 0 && infoRecipient !== recipientECert) { + return; + } + } + const infoLocker = assetLockContractInfo.getAgreement().getLocker(); + if (lockerECert && lockerECert.length > 0) { + if (infoLocker.length > 0 && infoLocker !== lockerECert) { + return; + } + } + // All filters passed + const hashBase64 = assetLockContractInfo.getLock().getHashbase64(); + const hashValue: string = Buffer.from(hashBase64.toString(), 'base64').toString('utf8'); + lockCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker, hashValue); } }; -}*/ +} + +const StartHTLCAssetClaimListener = ( + contract: Contract, + claimCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, p: string) => any, + contractId: string, + assetType: string, + assetId: string, + recipientECert: string, + lockerECert: string, +): void => { + const listener: ContractListener = async (event) => { + if (event.eventName === 'ClaimAsset') { + const assetLockContractInfo: assetLocksPb.AssetContractHTLC = assetLocksPb.AssetContractHTLC.deserializeBinary(event.payload); + const infoContractId = assetLockContractInfo.getContractid(); + if (contractId && contractId.length > 0) { + if (infoContractId.length > 0 && infoContractId !== contractId) { + return; + } + } + const infoAssetType = assetLockContractInfo.getAgreement().getType(); + if (assetType && assetType.length > 0) { + if (infoAssetType.length > 0 && infoAssetType !== assetType) { + return; + } + } + const infoAssetId = assetLockContractInfo.getAgreement().getId(); + if (assetId && assetId.length > 0) { + if (infoAssetId.length > 0 && infoAssetId !== assetId) { + return; + } + } + const infoRecipient = assetLockContractInfo.getAgreement().getRecipient(); + if (recipientECert && recipientECert.length > 0) { + if (infoRecipient.length > 0 && infoRecipient !== recipientECert) { + return; + } + } + const infoLocker = assetLockContractInfo.getAgreement().getLocker(); + if (lockerECert && lockerECert.length > 0) { + if (infoLocker.length > 0 && infoLocker !== lockerECert) { + return; + } + } + // All filters passed + const hashPreimageBase64 = assetLockContractInfo.getClaim().getHashpreimagebase64(); + const hashPreimage: string = Buffer.from(hashPreimageBase64.toString(), 'base64').toString('utf8'); + claimCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker, hashPreimage); + } + }; +} + +const StartHTLCAssetUnlockListener = ( + contract: Contract, + unlockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string) => any, + contractId: string, + assetType: string, + assetId: string, + recipientECert: string, + lockerECert: string, +): void => { + const listener: ContractListener = async (event) => { + if (event.eventName === 'UnlockAsset') { + const assetLockContractInfo: assetLocksPb.AssetContractHTLC = assetLocksPb.AssetContractHTLC.deserializeBinary(event.payload); + const infoContractId = assetLockContractInfo.getContractid(); + if (contractId && contractId.length > 0) { + if (infoContractId.length > 0 && infoContractId !== contractId) { + return; + } + } + const infoAssetType = assetLockContractInfo.getAgreement().getType(); + if (assetType && assetType.length > 0) { + if (infoAssetType.length > 0 && infoAssetType !== assetType) { + return; + } + } + const infoAssetId = assetLockContractInfo.getAgreement().getId(); + if (assetId && assetId.length > 0) { + if (infoAssetId.length > 0 && infoAssetId !== assetId) { + return; + } + } + const infoRecipient = assetLockContractInfo.getAgreement().getRecipient(); + if (recipientECert && recipientECert.length > 0) { + if (infoRecipient.length > 0 && infoRecipient !== recipientECert) { + return; + } + } + const infoLocker = assetLockContractInfo.getAgreement().getLocker(); + if (lockerECert && lockerECert.length > 0) { + if (infoLocker.length > 0 && infoLocker !== lockerECert) { + return; + } + } + // All filters passed + unlockCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker); + } + }; +} export { createAssetExchangeAgreementSerialized, From a5813d206073fc17a9eb5d73e67953e1a9d93108 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Mon, 17 May 2021 11:45:23 +0000 Subject: [PATCH 12/16] Fabric Interop SDK refactoring and adding fungible asset event listeners Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 189 ++++++++++-------- 1 file changed, 103 insertions(+), 86 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index 4b98d1a8e..a26282a84 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -518,18 +518,27 @@ const isFungibleAssetLockedInHTLC = async ( return result; }; -const StartHTLCAssetLockListener = ( +const StartHTLCEventListener = ( contract: Contract, - lockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, v: string) => any, + eventName: string, + eventCallback: Function, contractId: string, assetType: string, assetId: string, + numUnits: number, recipientECert: string, lockerECert: string, ): void => { const listener: ContractListener = async (event) => { - if (event.eventName === 'LockAsset') { - const assetLockContractInfo: assetLocksPb.AssetContractHTLC = assetLocksPb.AssetContractHTLC.deserializeBinary(event.payload); + if (event.eventName === eventName) { + let assetLockContractInfo; + if (eventName.includes('Fungible')) { + const eventInfo: assetLocksPb.FungibleAssetContractHTLC = assetLocksPb.FungibleAssetContractHTLC.deserializeBinary(event.payload); + assetLockContractInfo = eventInfo; + } else { + const eventInfo: assetLocksPb.AssetContractHTLC = assetLocksPb.AssetContractHTLC.deserializeBinary(event.payload); + assetLockContractInfo = eventInfo; + } const infoContractId = assetLockContractInfo.getContractid(); if (contractId && contractId.length > 0) { if (infoContractId.length > 0 && infoContractId !== contractId) { @@ -542,11 +551,19 @@ const StartHTLCAssetLockListener = ( return; } } - const infoAssetId = assetLockContractInfo.getAgreement().getId(); - if (assetId && assetId.length > 0) { - if (infoAssetId.length > 0 && infoAssetId !== assetId) { + let infoNumUnits: number, infoAssetId: string; + if (eventName.includes('Fungible')) { + infoNumUnits = assetLockContractInfo.getAgreement().getNumunits(); + if (infoNumUnits !== numUnits) { return; } + } else { + infoAssetId = assetLockContractInfo.getAgreement().getId(); + if (assetId && assetId.length > 0) { + if (infoAssetId.length > 0 && infoAssetId !== assetId) { + return; + } + } } const infoRecipient = assetLockContractInfo.getAgreement().getRecipient(); if (recipientECert && recipientECert.length > 0) { @@ -561,11 +578,42 @@ const StartHTLCAssetLockListener = ( } } // All filters passed - const hashBase64 = assetLockContractInfo.getLock().getHashbase64(); - const hashValue: string = Buffer.from(hashBase64.toString(), 'base64').toString('utf8'); - lockCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker, hashValue); + if (eventName === 'LockAsset' || eventName === 'LockFungibleAsset') { + const hashBase64 = assetLockContractInfo.getLock().getHashbase64(); + const hashValue: string = Buffer.from(hashBase64.toString(), 'base64').toString('utf8'); + if (eventName === 'LockAsset') { + eventCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker, hashValue); + } else { + eventCallback(contract, infoContractId, infoAssetType, infoNumUnits, infoRecipient, infoLocker, hashValue); + } + } else if (eventName === 'ClaimAsset' || eventName === 'ClaimFungibleAsset') { + const hashPreimageBase64 = assetLockContractInfo.getClaim().getHashpreimagebase64(); + const hashPreimage: string = Buffer.from(hashPreimageBase64.toString(), 'base64').toString('utf8'); + if (eventName === 'ClaimAsset') { + eventCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker, hashPreimage); + } else { + eventCallback(contract, infoContractId, infoAssetType, infoNumUnits, infoRecipient, infoLocker, hashPreimage); + } + } else if (eventName === 'UnlockAsset') { + eventCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker); + } else if (eventName === 'UnlockFungibleAsset') { + eventCallback(contract, infoContractId, infoAssetType, infoNumUnits, infoRecipient, infoLocker); + } } }; + contract.addContractListener(listener); +} + +const StartHTLCAssetLockListener = ( + contract: Contract, + lockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, v: string) => any, + contractId: string, + assetType: string, + assetId: string, + recipientECert: string, + lockerECert: string, +): void => { + StartHTLCEventListener(contract, 'LockAsset', lockCallback, contractId, assetType, assetId, -1, recipientECert, lockerECert); } const StartHTLCAssetClaimListener = ( @@ -577,45 +625,7 @@ const StartHTLCAssetClaimListener = ( recipientECert: string, lockerECert: string, ): void => { - const listener: ContractListener = async (event) => { - if (event.eventName === 'ClaimAsset') { - const assetLockContractInfo: assetLocksPb.AssetContractHTLC = assetLocksPb.AssetContractHTLC.deserializeBinary(event.payload); - const infoContractId = assetLockContractInfo.getContractid(); - if (contractId && contractId.length > 0) { - if (infoContractId.length > 0 && infoContractId !== contractId) { - return; - } - } - const infoAssetType = assetLockContractInfo.getAgreement().getType(); - if (assetType && assetType.length > 0) { - if (infoAssetType.length > 0 && infoAssetType !== assetType) { - return; - } - } - const infoAssetId = assetLockContractInfo.getAgreement().getId(); - if (assetId && assetId.length > 0) { - if (infoAssetId.length > 0 && infoAssetId !== assetId) { - return; - } - } - const infoRecipient = assetLockContractInfo.getAgreement().getRecipient(); - if (recipientECert && recipientECert.length > 0) { - if (infoRecipient.length > 0 && infoRecipient !== recipientECert) { - return; - } - } - const infoLocker = assetLockContractInfo.getAgreement().getLocker(); - if (lockerECert && lockerECert.length > 0) { - if (infoLocker.length > 0 && infoLocker !== lockerECert) { - return; - } - } - // All filters passed - const hashPreimageBase64 = assetLockContractInfo.getClaim().getHashpreimagebase64(); - const hashPreimage: string = Buffer.from(hashPreimageBase64.toString(), 'base64').toString('utf8'); - claimCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker, hashPreimage); - } - }; + StartHTLCEventListener(contract, 'ClaimAsset', claimCallback, contractId, assetType, assetId, -1, recipientECert, lockerECert); } const StartHTLCAssetUnlockListener = ( @@ -627,45 +637,46 @@ const StartHTLCAssetUnlockListener = ( recipientECert: string, lockerECert: string, ): void => { - const listener: ContractListener = async (event) => { - if (event.eventName === 'UnlockAsset') { - const assetLockContractInfo: assetLocksPb.AssetContractHTLC = assetLocksPb.AssetContractHTLC.deserializeBinary(event.payload); - const infoContractId = assetLockContractInfo.getContractid(); - if (contractId && contractId.length > 0) { - if (infoContractId.length > 0 && infoContractId !== contractId) { - return; - } - } - const infoAssetType = assetLockContractInfo.getAgreement().getType(); - if (assetType && assetType.length > 0) { - if (infoAssetType.length > 0 && infoAssetType !== assetType) { - return; - } - } - const infoAssetId = assetLockContractInfo.getAgreement().getId(); - if (assetId && assetId.length > 0) { - if (infoAssetId.length > 0 && infoAssetId !== assetId) { - return; - } - } - const infoRecipient = assetLockContractInfo.getAgreement().getRecipient(); - if (recipientECert && recipientECert.length > 0) { - if (infoRecipient.length > 0 && infoRecipient !== recipientECert) { - return; - } - } - const infoLocker = assetLockContractInfo.getAgreement().getLocker(); - if (lockerECert && lockerECert.length > 0) { - if (infoLocker.length > 0 && infoLocker !== lockerECert) { - return; - } - } - // All filters passed - unlockCallback(contract, infoContractId, infoAssetType, infoAssetId, infoRecipient, infoLocker); - } - }; + StartHTLCEventListener(contract, 'UnlockAsset', unlockCallback, contractId, assetType, assetId, -1, recipientECert, lockerECert); +} + +const StartHTLCFungibleAssetLockListener = ( + contract: Contract, + lockCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string, v: string) => any, + contractId: string, + assetType: string, + numUnits: number, + recipientECert: string, + lockerECert: string, +): void => { + StartHTLCEventListener(contract, 'LockFungibleAsset', lockCallback, contractId, assetType, "", numUnits, recipientECert, lockerECert); } +const StartHTLCFungibleAssetClaimListener = ( + contract: Contract, + claimCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string, p: string) => any, + contractId: string, + assetType: string, + numUnits: number, + recipientECert: string, + lockerECert: string, +): void => { + StartHTLCEventListener(contract, 'ClaimFungibleAsset', claimCallback, contractId, assetType, "", numUnits, recipientECert, lockerECert); +} + +const StartHTLCFungibleAssetUnlockListener = ( + contract: Contract, + unlockCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string) => any, + contractId: string, + assetType: string, + numUnits: number, + recipientECert: string, + lockerECert: string, +): void => { + StartHTLCEventListener(contract, 'UnlockFungibleAsset', unlockCallback, contractId, assetType, "", numUnits, recipientECert, lockerECert); +} + + export { createAssetExchangeAgreementSerialized, createFungibleAssetExchangeAgreementSerialized, @@ -679,4 +690,10 @@ export { reclaimFungibleAssetInHTLC, isAssetLockedInHTLC, isFungibleAssetLockedInHTLC, + StartHTLCAssetLockListener, + StartHTLCAssetClaimListener, + StartHTLCAssetUnlockListener, + StartHTLCFungibleAssetLockListener, + StartHTLCFungibleAssetClaimListener, + StartHTLCFungibleAssetUnlockListener, }; From fdc53c651d064a649c0b17ac740582899f1bd852 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Mon, 17 May 2021 14:34:31 +0000 Subject: [PATCH 13/16] Fabric Interop SDK: HTLC event promises, API updates, code cleanup Signed-off-by: VRamakrishna --- .../src/AssetManager.ts | 192 +++++++++++------- .../test/AssetManager.js | 53 ++--- 2 files changed, 136 insertions(+), 109 deletions(-) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index a26282a84..52de4a989 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -285,9 +285,7 @@ const claimAssetInHTLC = async ( **/ const claimFungibleAssetInHTLC = async ( contract: Contract, - assetType: string, - numUnits: number, - lockerECert: string, + contractId: string, hashPreimage: string, ): Promise => { @@ -296,19 +294,9 @@ const claimFungibleAssetInHTLC = async ( logger.error("Contract handle not supplied"); return false; } - if (!assetType) - { - logger.error("Asset type not supplied"); - return false; - } - if (numUnits <= 0) - { - logger.error("Asset count must be a positive integer"); - return false; - } - if (!lockerECert) + if (!contractId) { - logger.error("Locker ECert not supplied"); + logger.error("contract ID not supplied"); return false; } if (!hashPreimage) @@ -317,12 +305,11 @@ const claimFungibleAssetInHTLC = async ( return false; } - const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, "", lockerECert); const claimInfoStr = createAssetClaimInfoSerialized(hashPreimage); // Normal invoke function const [result, submitError] = await helpers.handlePromise( - contract.submitTransaction("ClaimFungibleAsset", assetExchangeAgreementStr, claimInfoStr), + contract.submitTransaction("ClaimFungibleAsset", contractId, claimInfoStr), ); if (submitError) { throw new Error(`ClaimFungibleAsset submitTransaction Error: ${submitError}`); @@ -380,9 +367,7 @@ const reclaimAssetInHTLC = async ( **/ const reclaimFungibleAssetInHTLC = async ( contract: Contract, - assetType: string, - numUnits: number, - recipientECert: string, + contractId: string, ): Promise => { if (!contract) @@ -390,27 +375,15 @@ const reclaimFungibleAssetInHTLC = async ( logger.error("Contract handle not supplied"); return false; } - if (!assetType) + if (!contractId) { - logger.error("Asset type not supplied"); + logger.error("contract ID not supplied"); return false; } - if (numUnits <= 0) - { - logger.error("Asset count must be a positive integer"); - return false; - } - if (!recipientECert) - { - logger.error("Recipient ECert not supplied"); - return false; - } - - const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, ""); // Normal invoke function const [result, submitError] = await helpers.handlePromise( - contract.submitTransaction("UnlockFungibleAsset", assetExchangeAgreementStr), + contract.submitTransaction("UnlockFungibleAsset", contractId), ); if (submitError) { throw new Error(`UnlockFungibleAsset submitTransaction Error: ${submitError}`); @@ -474,10 +447,7 @@ const isAssetLockedInHTLC = async ( **/ const isFungibleAssetLockedInHTLC = async ( contract: Contract, - assetType: string, - numUnits: number, - recipientECert: string, - lockerECert: string, + contractId: string, ): Promise => { if (!contract) @@ -485,32 +455,15 @@ const isFungibleAssetLockedInHTLC = async ( logger.error("Contract handle not supplied"); return false; } - if (!assetType) + if (!contractId) { - logger.error("Asset type not supplied"); - return false; - } - if (numUnits <= 0) - { - logger.error("Asset count must be a positive integer"); - return false; - } - if (!recipientECert) - { - logger.error("Recipient ECert not supplied"); - return false; - } - if (!lockerECert) - { - logger.error("Locker ECert not supplied"); + logger.error("contract ID not supplied"); return false; } - const assetExchangeAgreementStr = createFungibleAssetExchangeAgreementSerialized(assetType, numUnits, recipientECert, lockerECert); - // Normal invoke function const [result, evaluateError] = await helpers.handlePromise( - contract.evaluateTransaction("IsFungibleAssetLocked", assetExchangeAgreementStr), + contract.evaluateTransaction("IsFungibleAssetLocked", contractId), ); if (evaluateError) { throw new Error(`IsFungibleAssetLocked evaluateTransaction Error: ${evaluateError}`); @@ -521,13 +474,13 @@ const isFungibleAssetLockedInHTLC = async ( const StartHTLCEventListener = ( contract: Contract, eventName: string, - eventCallback: Function, contractId: string, assetType: string, assetId: string, numUnits: number, recipientECert: string, lockerECert: string, + eventCallback: Function, ): void => { const listener: ContractListener = async (event) => { if (event.eventName === eventName) { @@ -606,76 +559,171 @@ const StartHTLCEventListener = ( const StartHTLCAssetLockListener = ( contract: Contract, - lockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, v: string) => any, contractId: string, assetType: string, assetId: string, recipientECert: string, lockerECert: string, + lockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, v: string) => any, ): void => { - StartHTLCEventListener(contract, 'LockAsset', lockCallback, contractId, assetType, assetId, -1, recipientECert, lockerECert); + StartHTLCEventListener(contract, 'LockAsset', contractId, assetType, assetId, -1, recipientECert, lockerECert, lockCallback); } const StartHTLCAssetClaimListener = ( contract: Contract, - claimCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, p: string) => any, contractId: string, assetType: string, assetId: string, recipientECert: string, lockerECert: string, + claimCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string, p: string) => any, ): void => { - StartHTLCEventListener(contract, 'ClaimAsset', claimCallback, contractId, assetType, assetId, -1, recipientECert, lockerECert); + StartHTLCEventListener(contract, 'ClaimAsset', contractId, assetType, assetId, -1, recipientECert, lockerECert, claimCallback); } const StartHTLCAssetUnlockListener = ( contract: Contract, - unlockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string) => any, contractId: string, assetType: string, assetId: string, recipientECert: string, lockerECert: string, + unlockCallback: (c: Contract, d: string, t: string, i: string, r: string, l: string) => any, ): void => { - StartHTLCEventListener(contract, 'UnlockAsset', unlockCallback, contractId, assetType, assetId, -1, recipientECert, lockerECert); + StartHTLCEventListener(contract, 'UnlockAsset', contractId, assetType, assetId, -1, recipientECert, lockerECert, unlockCallback); } const StartHTLCFungibleAssetLockListener = ( contract: Contract, - lockCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string, v: string) => any, contractId: string, assetType: string, numUnits: number, recipientECert: string, lockerECert: string, + lockCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string, v: string) => any, ): void => { - StartHTLCEventListener(contract, 'LockFungibleAsset', lockCallback, contractId, assetType, "", numUnits, recipientECert, lockerECert); + StartHTLCEventListener(contract, 'LockFungibleAsset', contractId, assetType, "", numUnits, recipientECert, lockerECert, lockCallback); } const StartHTLCFungibleAssetClaimListener = ( contract: Contract, - claimCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string, p: string) => any, contractId: string, assetType: string, numUnits: number, recipientECert: string, lockerECert: string, + claimCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string, p: string) => any, ): void => { - StartHTLCEventListener(contract, 'ClaimFungibleAsset', claimCallback, contractId, assetType, "", numUnits, recipientECert, lockerECert); + StartHTLCEventListener(contract, 'ClaimFungibleAsset', contractId, assetType, "", numUnits, recipientECert, lockerECert, claimCallback); } const StartHTLCFungibleAssetUnlockListener = ( contract: Contract, - unlockCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string) => any, contractId: string, assetType: string, numUnits: number, recipientECert: string, lockerECert: string, + unlockCallback: (c: Contract, d: string, t: string, n: number, r: string, l: string) => any, ): void => { - StartHTLCEventListener(contract, 'UnlockFungibleAsset', unlockCallback, contractId, assetType, "", numUnits, recipientECert, lockerECert); + StartHTLCEventListener(contract, 'UnlockFungibleAsset', contractId, assetType, "", numUnits, recipientECert, lockerECert, unlockCallback); +} + +const HTLCAssetLocked = async ( + contract: Contract, + contractId: string, + assetType: string, + assetId: string, + recipientECert: string, + lockerECert: string, +): Promise => { + return new Promise((resolve, reject) => { + const waitForLock = (contract, contractId, assetType, assetId, recipientECert, lockerECert, hashValue) => { + resolve(hashValue); + }; + StartHTLCAssetLockListener(contract, contractId, assetType, assetId, recipientECert, lockerECert, waitForLock); + }); } +const HTLCAssetClaimed = async ( + contract: Contract, + contractId: string, + assetType: string, + assetId: string, + recipientECert: string, + lockerECert: string, +): Promise => { + return new Promise((resolve, reject) => { + const waitForClaim = (contract, contractId, assetType, assetId, recipientECert, lockerECert, hashPreimage) => { + resolve(hashPreimage); + }; + StartHTLCAssetClaimListener(contract, contractId, assetType, assetId, recipientECert, lockerECert, waitForClaim); + }); +} + +const HTLCAssetUnlocked = async ( + contract: Contract, + contractId: string, + assetType: string, + assetId: string, + recipientECert: string, + lockerECert: string, +): Promise => { + return new Promise((resolve, reject) => { + const waitForUnlock = (contract, contractId, assetType, assetId, recipientECert, lockerECert) => { + resolve(); + }; + StartHTLCAssetUnlockListener(contract, contractId, assetType, assetId, recipientECert, lockerECert, waitForUnlock); + }); +} + +const HTLCFungibleAssetLocked = async ( + contract: Contract, + contractId: string, + assetType: string, + numUnits: number, + recipientECert: string, + lockerECert: string, +): Promise => { + return new Promise((resolve, reject) => { + const waitForLock = (contract, contractId, assetType, numUnits, recipientECert, lockerECert, hashValue) => { + resolve(hashValue); + }; + StartHTLCFungibleAssetLockListener(contract, contractId, assetType, numUnits, recipientECert, lockerECert, waitForLock); + }); +} + +const HTLCFungibleAssetClaimed = async ( + contract: Contract, + contractId: string, + assetType: string, + numUnits: number, + recipientECert: string, + lockerECert: string, +): Promise => { + return new Promise((resolve, reject) => { + const waitForClaim = (contract, contractId, assetType, numUnits, recipientECert, lockerECert, hashPreimage) => { + resolve(hashPreimage); + }; + StartHTLCFungibleAssetClaimListener(contract, contractId, assetType, numUnits, recipientECert, lockerECert, waitForClaim); + }); +} + +const HTLCFungibleAssetUnlocked = async ( + contract: Contract, + contractId: string, + assetType: string, + numUnits: number, + recipientECert: string, + lockerECert: string, +): Promise => { + return new Promise((resolve, reject) => { + const waitForUnlock = (contract, contractId, assetType, numUnits, recipientECert, lockerECert) => { + resolve(); + }; + StartHTLCFungibleAssetUnlockListener(contract, contractId, assetType, numUnits, recipientECert, lockerECert, waitForUnlock); + }); +} export { createAssetExchangeAgreementSerialized, diff --git a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js index 6e494eb0a..432ec5901 100644 --- a/sdks/fabric/interoperation-node-sdk/test/AssetManager.js +++ b/sdks/fabric/interoperation-node-sdk/test/AssetManager.js @@ -277,34 +277,28 @@ describe("AssetManager", () => { describe("claim fungible asset locked in HTLC", () => { let amcStub; const hashPreimage = "xyz+123-*ty%"; + const contractId = "CONTRACT-1234"; beforeEach(() => { amcStub = sinon.stub(amc, "submitTransaction").resolves(false); }); it("asset claim fails with invalid parameters", async () => { - let assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(null, fungibleAssetType, numUnits, lockerECert, hashPreimage); + let assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(null, contractId, hashPreimage); expect(assetClaimInvocation).to.be.a('boolean'); expect(assetClaimInvocation).to.equal(false); - assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, "", numUnits, lockerECert, hashPreimage); + assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, "", hashPreimage); expect(assetClaimInvocation).to.be.a('boolean'); expect(assetClaimInvocation).to.equal(false); - assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, -1, lockerECert, hashPreimage); - expect(assetClaimInvocation).to.be.a('boolean'); - expect(assetClaimInvocation).to.equal(false); - assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, "", hashPreimage); - expect(assetClaimInvocation).to.be.a('boolean'); - expect(assetClaimInvocation).to.equal(false); - assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, lockerECert, ""); + assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, contractId, ""); expect(assetClaimInvocation).to.be.a('boolean'); expect(assetClaimInvocation).to.equal(false); }); it("submit asset claim invocation", async () => { - let assetAgreementStr = assetManager.createFungibleAssetExchangeAgreementSerialized(fungibleAssetType, numUnits, "", lockerECert); let claimInfoStr = assetManager.createAssetClaimInfoSerialized(hashPreimage); - amcStub.withArgs("ClaimFungibleAsset", assetAgreementStr, claimInfoStr).resolves(true); - let assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, lockerECert, hashPreimage); + amcStub.withArgs("ClaimFungibleAsset", contractId, claimInfoStr).resolves(true); + let assetClaimInvocation = await assetManager.claimFungibleAssetInHTLC(amc, contractId, hashPreimage); expect(assetClaimInvocation).to.be.a('boolean'); expect(assetClaimInvocation).to.equal(true); }); @@ -343,30 +337,24 @@ describe("AssetManager", () => { describe("reclaim fungible asset locked in HTLC", () => { let amcStub; + const contractId = "CONTRACT-1234"; beforeEach(() => { amcStub = sinon.stub(amc, "submitTransaction").resolves(false); }); it("asset reclaim fails with invalid parameters", async () => { - let assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(null, fungibleAssetType, numUnits, recipientECert); - expect(assetReclaimInvocation).to.be.a('boolean'); - expect(assetReclaimInvocation).to.equal(false); - assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, "", numUnits, recipientECert); - expect(assetReclaimInvocation).to.be.a('boolean'); - expect(assetReclaimInvocation).to.equal(false); - assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, fungibleAssetType, -1, recipientECert); + let assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(null, contractId); expect(assetReclaimInvocation).to.be.a('boolean'); expect(assetReclaimInvocation).to.equal(false); - assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, ""); + assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, ""); expect(assetReclaimInvocation).to.be.a('boolean'); expect(assetReclaimInvocation).to.equal(false); }); it("submit asset claim invocation", async () => { - let assetAgreementStr = assetManager.createFungibleAssetExchangeAgreementSerialized(fungibleAssetType, numUnits, recipientECert, ""); - amcStub.withArgs("UnlockFungibleAsset", assetAgreementStr).resolves(true); - let assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, fungibleAssetType, numUnits, recipientECert); + amcStub.withArgs("UnlockFungibleAsset", contractId).resolves(true); + let assetReclaimInvocation = await assetManager.reclaimFungibleAssetInHTLC(amc, contractId); expect(assetReclaimInvocation).to.be.a('boolean'); expect(assetReclaimInvocation).to.equal(true); }); @@ -408,33 +396,24 @@ describe("AssetManager", () => { describe("check fungible asset lock status in HTLC", () => { let amcStub; + const contractId = "CONTRACT-1234"; beforeEach(() => { amcStub = sinon.stub(amc, "evaluateTransaction").resolves(false); }); it("asset lock status check fails with invalid parameters", async () => { - let assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(null, fungibleAssetType, numUnits, recipientECert, lockerECert); - expect(assetLockQuery).to.be.a('boolean'); - expect(assetLockQuery).to.equal(false); - assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, "", numUnits, recipientECert, lockerECert); - expect(assetLockQuery).to.be.a('boolean'); - expect(assetLockQuery).to.equal(false); - assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, -1, recipientECert, lockerECert); - expect(assetLockQuery).to.be.a('boolean'); - expect(assetLockQuery).to.equal(false); - assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, numUnits, "", lockerECert); + let assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(null, contractId); expect(assetLockQuery).to.be.a('boolean'); expect(assetLockQuery).to.equal(false); - assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, numUnits, recipientECert, ""); + assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, ""); expect(assetLockQuery).to.be.a('boolean'); expect(assetLockQuery).to.equal(false); }); it("submit asset lock status query", async () => { - let assetAgreementStr = assetManager.createFungibleAssetExchangeAgreementSerialized(fungibleAssetType, numUnits, recipientECert, lockerECert); - amcStub.withArgs("IsFungibleAssetLocked", assetAgreementStr).resolves(true); - let assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, fungibleAssetType, numUnits, recipientECert, lockerECert); + amcStub.withArgs("IsFungibleAssetLocked", contractId).resolves(true); + let assetLockQuery = await assetManager.isFungibleAssetLockedInHTLC(amc, contractId); expect(assetLockQuery).to.be.a('boolean'); expect(assetLockQuery).to.equal(true); }); From 73d39921f65197d4fc3718c583207642155d30c7 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Mon, 17 May 2021 14:44:05 +0000 Subject: [PATCH 14/16] Exposed event-related functions in Fabric Interop SDK Signed-off-by: VRamakrishna --- sdks/fabric/interoperation-node-sdk/src/AssetManager.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index 52de4a989..6006e153f 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -744,4 +744,10 @@ export { StartHTLCFungibleAssetLockListener, StartHTLCFungibleAssetClaimListener, StartHTLCFungibleAssetUnlockListener, + HTLCAssetLocked, + HTLCAssetClaimed, + HTLCAssetUnlocked, + HTLCFungibleAssetLocked, + HTLCFungibleAssetClaimed, + HTLCFungibleAssetUnlocked, }; From 2717622d84b316d0dc73a12bee87564fd6352a3e Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Tue, 18 May 2021 11:58:01 +0000 Subject: [PATCH 15/16] Minor code refactoring and commenting Added qualifications about events exposed in Fabric Interop Node SDK. Updated code to log and return error objects in Fabric asset management CC. (TODO: refactor chaincodes to use the above pattern.) Signed-off-by: VRamakrishna --- .../contracts/interop/manage_assets.go | 36 ++++--------------- .../src/AssetManager.ts | 15 ++++++++ 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/core/network/fabric-interop-cc/contracts/interop/manage_assets.go b/core/network/fabric-interop-cc/contracts/interop/manage_assets.go index d0b89950e..d30dd4081 100644 --- a/core/network/fabric-interop-cc/contracts/interop/manage_assets.go +++ b/core/network/fabric-interop-cc/contracts/interop/manage_assets.go @@ -48,22 +48,10 @@ const( ) // helper functions to log and return errors -func logAndReturnErrorfBool(retVal bool, format string, args ...interface{}) (bool, error) { +func logThenErrorf(format string, args ...interface{}) error { errorMsg := fmt.Sprintf(format, args...) log.Error(errorMsg) - return retVal, errors.New(errorMsg) -} - -func logAndReturnErrorfInt(retVal int, format string, args ...interface{}) (int, error) { - errorMsg := fmt.Sprintf(format, args...) - log.Error(errorMsg) - return retVal, errors.New(errorMsg) -} - -func logAndReturnErrorfString(retVal string, format string, args ...interface{}) (string, error) { - errorMsg := fmt.Sprintf(format, args...) - log.Error(errorMsg) - return retVal, errors.New(errorMsg) + return errors.New(errorMsg) } // function to generate a "SHA256" hash in base64 format for a given preimage @@ -87,9 +75,7 @@ func generateContractIdMapKey(contractId string) string { func generateAssetLockKeyAndContractId(ctx contractapi.TransactionContextInterface, assetAgreement *common.AssetExchangeAgreement) (string, string, error) { assetLockKey, err := ctx.GetStub().CreateCompositeKey("AssetExchangeContract", []string{assetAgreement.Type, assetAgreement.Id}) if err != nil { - errorMsg := fmt.Sprintf("error while creating composite key: %+v", err) - log.Error(errorMsg) - return "", "", errors.New(errorMsg) + return "", "", logThenErrorf("error while creating composite key: %+v", err) } contractId := generateSHA256HashInBase64Form(assetLockKey) @@ -129,9 +115,7 @@ func (s *SmartContract) LockAsset(ctx contractapi.TransactionContextInterface, a log.Infof("lockInfoHTLC: %+v\n", lockInfoHTLC) if lockInfoHTLC.TimeSpec != common.AssetLockHTLC_EPOCH { - errorMsg := "only EPOCH time is supported at present" - log.Error(errorMsg) - return "", errors.New(errorMsg) + return "", logThenErrorf("only EPOCH time is supported at present") } assetLockKey, contractId, err := generateAssetLockKeyAndContractId(ctx, assetAgreement) @@ -149,16 +133,12 @@ func (s *SmartContract) LockAsset(ctx contractapi.TransactionContextInterface, a } if assetLockValBytes != nil { - errorMsg := fmt.Sprintf("asset of type %s and ID %s is already locked", assetAgreement.Type, assetAgreement.Id) - log.Error(errorMsg) - return "", errors.New(errorMsg) + return "", logThenErrorf("asset of type %s and ID %s is already locked", assetAgreement.Type, assetAgreement.Id) } assetLockValBytes, err = json.Marshal(assetLockVal) if err != nil { - errorMsg := fmt.Sprintf("marshal error: %s", err) - log.Error(errorMsg) - return "", errors.New(errorMsg) + return "", logThenErrorf("marshal error: %+v", err) } err = ctx.GetStub().PutState(assetLockKey, assetLockValBytes) @@ -169,9 +149,7 @@ func (s *SmartContract) LockAsset(ctx contractapi.TransactionContextInterface, a assetLockKeyBytes, err := json.Marshal(assetLockKey) if err != nil { - errorMsg := fmt.Sprintf("marshal error: %s", err) - log.Error(errorMsg) - return "", errors.New(errorMsg) + return "", logThenErrorf("marshal error: %+v", err) } err = ctx.GetStub().PutState(generateContractIdMapKey(string(contractId)), assetLockKeyBytes) diff --git a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts index 6006e153f..bf347d0a3 100644 --- a/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts +++ b/sdks/fabric/interoperation-node-sdk/src/AssetManager.ts @@ -471,6 +471,17 @@ const isFungibleAssetLockedInHTLC = async ( return result; }; + +/** + * HTLC Lifecycle Events + * - Developers should note that event emission and actions in response occur on a best effort basis. + * - Also, there is no guarantee that a particular event (lock, claim, reclaim) will ever get emitted + * - Therefore, the calling (or listening) logic should incorporate suitable fallbacks and timeouts. + **/ + +/** + * The below functions trigger callbacks passed as arguments when a matching event is received from the contract layer + **/ const StartHTLCEventListener = ( contract: Contract, eventName: string, @@ -629,6 +640,10 @@ const StartHTLCFungibleAssetUnlockListener = ( StartHTLCEventListener(contract, 'UnlockFungibleAsset', contractId, assetType, "", numUnits, recipientECert, lockerECert, unlockCallback); } +/** + * The below functions return promises for HTLC events. + * Developers can use 'await' to synchronously manage asset swapping logic. + **/ const HTLCAssetLocked = async ( contract: Contract, contractId: string, From 2ebeb32c0958e2366cf72374e10f1359d96cbfc9 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Tue, 18 May 2021 12:07:39 +0000 Subject: [PATCH 16/16] Added more comments on event setting in Fabric asset management base class Signed-off-by: VRamakrishna --- .../interfaces/asset-mgmt/asset_locks_contract.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go index 569eb19d9..1ecb2a7d3 100644 --- a/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go +++ b/core/network/fabric-interop-cc/interfaces/asset-mgmt/asset_locks_contract.go @@ -51,6 +51,8 @@ func (amc *AssetManagementContract) LockAsset(ctx contractapi.TransactionContext log.Error(err.Error()) return false, err } + + // The below 'SetEvent' should be the last in a given transaction (if this function is being called by another), otherwise it will be overridden retVal, err := amc.assetManagement.LockAsset(ctx.GetStub(), assetAgreement, lockInfo) if retVal && err == nil { lockInfoHTLC := &common.AssetLockHTLC{} @@ -94,6 +96,8 @@ func (amc *AssetManagementContract) LockFungibleAsset(ctx contractapi.Transactio log.Error(err.Error()) return "", err } + + // The below 'SetEvent' should be the last in a given transaction (if this function is being called by another), otherwise it will be overridden retVal, err := amc.assetManagement.LockFungibleAsset(ctx.GetStub(), assetAgreement, lockInfo) if err == nil { lockInfoHTLC := &common.AssetLockHTLC{} @@ -160,6 +164,8 @@ func (amc *AssetManagementContract) ClaimAsset(ctx contractapi.TransactionContex log.Error(err.Error()) return false, err } + + // The below 'SetEvent' should be the last in a given transaction (if this function is being called by another), otherwise it will be overridden retVal, err := amc.assetManagement.ClaimAsset(ctx.GetStub(), assetAgreement, claimInfo) if retVal && err == nil { claimInfoHTLC := &common.AssetClaimHTLC{} @@ -197,6 +203,8 @@ func (amc *AssetManagementContract) ClaimFungibleAsset(ctx contractapi.Transacti log.Error(err.Error()) return false, err } + + // The below 'SetEvent' should be the last in a given transaction (if this function is being called by another), otherwise it will be overridden retVal, err := amc.assetManagement.ClaimFungibleAsset(ctx.GetStub(), contractId, claimInfo) if retVal && err == nil { claimInfoHTLC := &common.AssetClaimHTLC{} @@ -230,6 +238,8 @@ func (amc *AssetManagementContract) UnlockAsset(ctx contractapi.TransactionConte log.Error(err.Error()) return false, err } + + // The below 'SetEvent' should be the last in a given transaction (if this function is being called by another), otherwise it will be overridden retVal, err := amc.assetManagement.UnlockAsset(ctx.GetStub(), assetAgreement) if retVal && err == nil { contractInfo := &common.AssetContractHTLC{ @@ -252,6 +262,8 @@ func (amc *AssetManagementContract) UnlockFungibleAsset(ctx contractapi.Transact log.Error("empty contract id") return false, fmt.Errorf("empty contract id") } + + // The below 'SetEvent' should be the last in a given transaction (if this function is being called by another), otherwise it will be overridden retVal, err := amc.assetManagement.UnlockFungibleAsset(ctx.GetStub(), contractId) if retVal && err == nil { contractInfo := &common.AssetContractHTLC{