Hyperledger Tutorial のコマーシャルペーパー をやってみた
前回やった Hyperledger のチェインコードデプロイのチュートリアルに引き続ぎコマーシャルペーパーのチュートリアルもやってみました。
以下のfabric-samples
リポジトリの commercial-paper
配下を動かしていきます。
ネットワーク起動
まずは docker コンテナを立ち上げます。
[root@localhost fabric-samples]# cd commercial-paper/ [root@localhost commercial-paper]# ./network-starter.sh set -o pipefail # Where am I? DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export FABRIC_CFG_PATH="${DIR}/../config"
network-starter.sh
の中身を見ればわかるように前回のチェインコードデプロイで使用したdocker-compose.yml
を使ってコンテナを立ち上げています。
cd "${DIR}/../test-network/" docker kill cliDigiBank cliMagnetoCorp logspout || true ./network.sh down ./network.sh up createChannel -ca -s couchdb
次にlogspoutを起動して docker ネットワーク上のログをモニタリングします。
[root@localhost commercial-paper]# ./organization/magnetocorp/configuration/cli/monitordocker.sh fabric_test Starting monitoring on all containers on the network fabric_test a41c2cc213df75e4c208663a2e816da1742cee3fde0eb3624c9214d307c594da
チェインコードデプロイ
次にチェインコードをデプロイします。前回と同じようにチェインコードを圧縮してインストール後、組織ごとで承認し、コミットします。
まず magnetocorp の組織でチェインコードをインストール、承認します。
[root@localhost commercial-paper]# cd organization/magnetocorp/ [root@localhost magnetocorp]# # 環境変数設定 [root@localhost magnetocorp]# source magnetocorp.sh Using organization 2 Using organization 2 ~省略~ [root@localhost magnetocorp]# # デプロイ対象のパッケージ化 [root@localhost magnetocorp]# peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0 [root@localhost magnetocorp]# # インストール [root@localhost magnetocorp]# peer lifecycle chaincode install cp.tar.gz 2021-03-07 17:19:14.512 JST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nEcp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a\022\004cp_0" > 2021-03-07 17:19:14.512 JST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: cp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a [root@localhost magnetocorp]# # パッケージIDを確認してインストール [root@localhost magnetocorp]# peer lifecycle chaincode queryinstalled Installed chaincodes on peer: Package ID: cp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a, Label: cp_0 [root@localhost magnetocorp]# export PACKAGE_ID=cp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a [root@localhost magnetocorp]# peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name papercontract -v 0 --package-id $PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA 2021-03-07 17:21:35.576 JST [chaincodeCmd] ClientWait -> INFO 001 txid [b54ba81a3bc71cd44a786132924ae8fef32f41eff9a99a4f7d385da99fd91c4c] committed with status (VALID) at localhost:9051 [root@localhost magnetocorp]#
次に digibank の組織でもチェインコードをインストール、承認します。
[root@localhost magnetocorp]# cd ../digibank/ [root@localhost digibank]# # 環境変数設定 [root@localhost digibank]# source digibank.sh Using organization 1 Using organization 1 ~省略~ [root@localhost digibank]# # デプロイ対象のパッケージ化 [root@localhost digibank]# peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0 [root@localhost digibank]# peer lifecycle chaincode install cp.tar.gz 2021-03-07 17:25:19.159 JST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nEcp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a\022\004cp_0" > 2021-03-07 17:25:19.159 JST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: cp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a [root@localhost digibank]# # パッケージIDを確認してインストール [root@localhost digibank]# peer lifecycle chaincode queryinstalled Installed chaincodes on peer: Package ID: cp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a, Label: cp_0 [root@localhost digibank]# export PACKAGE_ID=cp_0:ddca913c004eb34f36dfb0b4c0bcc6d4afc1fa823520bb5966a3bfcf1808f40a [root@localhost digibank]# peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name papercontract -v 0 --package-id $PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA 2021-03-07 17:26:38.400 JST [chaincodeCmd] ClientWait -> INFO 001 txid [e89816190ff60f4fd134cd9830560fd40e1a5b1fb565cc17aa32fcc8cd1f5c03] committed with status (VALID) at localhost:7051
この時 PackageID が一致していますが、実際パッケージ対象の diff がなく一致していることが確認できます。
[root@localhost digibank]# diff -r ./contract ../magnetocorp/contract [root@localhost digibank]#
それからネットワークに参加した組織の半数以上で承認されたのでpapercontract
をコミットします。
[root@localhost digibank]# peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} --channelID mychannel --name papercontract -v 0 --sequence 1 --tls --cafile $ORDERER_CA --waitForEvent 2021-03-07 17:31:11.620 JST [chaincodeCmd] ClientWait -> INFO 001 txid [a11f23a0d40fccb93501d87a968efec0f72dcb268da922b8a54b701c304e21b9] committed with status (VALID) at localhost:7051 2021-03-07 17:31:11.625 JST [chaincodeCmd] ClientWait -> INFO 002 txid [a11f23a0d40fccb93501d87a968efec0f72dcb268da922b8a54b701c304e21b9] committed with status (VALID) at localhost:9051 [root@localhost digibank]#
これで papercontract で定義された関数が動くようになります。
コマーシャルペーパーの発行
チェインコードが動くようになったので magnetocorp でコマーシャルペーパーを発行します。 まず wallet を初期化します。
[root@localhost organization]# cd magnetocorp/application [root@localhost application]# npm install npm WARN nodejs@1.0.0 No description npm WARN nodejs@1.0.0 No repository field. ~省略~ [root@localhost application]# node enrollUser.js Wallet path: /home/arimura/work/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/identity/user/isabella/wallet Successfully enrolled client user "isabella" and imported it into the wallet
ここで実行しているenrollUser.js
を見てみると fabric の Node SDK の Wallets を使用していることが確認できます。元のコードは ts になっているので定義の確認などしやすくなっています。
/* * Copyright IBM Corp. All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ 'use strict'; const FabricCAServices = require('fabric-ca-client'); const { Wallets } = require('fabric-network'); const fs = require('fs'); const yaml = require('js-yaml'); const path = require('path'); async function main() { try { // load the network configuration let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org2.yaml', 'utf8')); // Create a new CA client for interacting with the CA. const caInfo = connectionProfile.certificateAuthorities['ca.org2.example.com']; const caTLSCACerts = caInfo.tlsCACerts.pem; const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); // Create a new file system based wallet for managing identities. const walletPath = path.join(process.cwd(), '../identity/user/isabella/wallet'); const wallet = await Wallets.newFileSystemWallet(walletPath); console.log(`Wallet path: ${walletPath}`); // Check to see if we've already enrolled the admin user. const userExists = await wallet.get('isabella'); if (userExists) { console.log('An identity for the client user "user1" already exists in the wallet'); return; } // Enroll the admin user, and import the new identity into the wallet. const enrollment = await ca.enroll({ enrollmentID: 'user1', enrollmentSecret: 'user1pw' }); const x509Identity = { credentials: { certificate: enrollment.certificate, privateKey: enrollment.key.toBytes(), }, mspId: 'Org2MSP', type: 'X.509', }; await wallet.put('isabella', x509Identity); console.log('Successfully enrolled client user "isabella" and imported it into the wallet'); } catch (error) { console.error(`Failed to enroll client user "isabella": ${error}`); process.exit(1); } } main();
以下のように wallet はファイルシステム上に保存するようで
const walletPath = path.join(process.cwd(), '../identity/user/isabella/wallet'); const wallet = await Wallets.newFileSystemWallet(walletPath);
isabella の wallet として初期化しているようです。
await wallet.put('isabella', x509Identity);
以下の初期化した wallet が作られているのを確認できます。
[root@localhost application]# ls ../identity/user/isabella/wallet/ isabella.id
次に発行します。
[root@localhost application]# node issue.js Connect to Fabric gateway. Use network channel: mychannel. Use org.papernet.commercialpaper smart contract. Submit commercial paper issue transaction. Process issue transaction response.{"class":"org.papernet.commercialpaper","currentState":1,"issuer":"MagnetoCorp","paperNumber":"00001","issueDateTime":"2020-05-31","maturityDateTime":"2020-11-30","faceValue":5000000,"mspid":"Org2MSP","owner":"MagnetoCorp"} MagnetoCorp commercial paper : 00001 successfully issued for value 5000000 Transaction complete. Disconnect from Fabric gateway. Issue program complete. [root@localhost application]#
実行しているissue.js
の内容を確認してみると以下のようにsubmitTransaction
を実行していてデプロイしたチェインコードのissue
を実行しているようです。
const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');
issue
の中身は以下のようになっていて issuer=MagnetoCorp,paperNumber=00001,issueDateTime=2020-05-31,maturityDateTime=2020-11-30,faceValue=5000000
で呼び出していることが確認できます。コマーシャルペーパーを生成し組織判別のための msp とオーナーをセットし台帳に書き込んでいることが確認できます。
/** * Issue commercial paper * * @param {Context} ctx the transaction context * @param {String} issuer commercial paper issuer * @param {Integer} paperNumber paper number for this issuer * @param {String} issueDateTime paper issue date * @param {String} maturityDateTime paper maturity date * @param {Integer} faceValue face value of paper */ async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) { // create an instance of the paper let paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, parseInt(faceValue)); // Smart contract, rather than paper, moves paper into ISSUED state paper.setIssued(); // save the owner's MSP let mspid = ctx.clientIdentity.getMSPID(); paper.setOwnerMSP(mspid); // Newly issued paper is owned by the issuer to begin with (recorded for reporting purposes) paper.setOwner(issuer); // Add the paper to the list of all similar commercial papers in the ledger world state await ctx.paperList.addPaper(paper); // Must return a serialized paper to caller of smart contract return paper; }
以下のqueryHistory
を実行してみます。
'use strict'; // Bring key classes into scope, most importantly Fabric SDK network class const fs = require('fs'); const yaml = require('js-yaml'); const { Wallets, Gateway } = require('fabric-network'); // Main program function async function main() { // A wallet stores a collection of identities for use const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet'); // A gateway defines the peers used to access Fabric networks const gateway = new Gateway(); // Main try/catch block try { // Specify userName for network access const userName = 'isabella'; // Load connection profile; will be used to locate a gateway let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org2.yaml', 'utf8')); // Set connection options; identity and wallet let connectionOptions = { identity: userName, wallet: wallet, discovery: { enabled: true, asLocalhost: true } }; // Connect to gateway using application specified parameters console.log('Connect to Fabric gateway.'); await gateway.connect(connectionProfile, connectionOptions); // Access PaperNet network console.log('Use network channel: mychannel.'); const network = await gateway.getNetwork('mychannel'); // Get addressability to commercial paper contract console.log('Use org.papernet.commercialpaper smart contract.'); const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper'); // queries - commercial paper console.log('-----------------------------------------------------------------------------------------'); console.log('****** Submitting commercial paper queries ****** \n\n '); // 1 asset history console.log('1. Query Commercial Paper History....'); console.log('-----------------------------------------------------------------------------------------\n'); let queryResponse = await contract.evaluateTransaction('queryHistory', 'MagnetoCorp', '00001'); let json = JSON.parse(queryResponse.toString()); console.log(json); console.log('\n\n'); console.log('\n History query complete.'); console.log('-----------------------------------------------------------------------------------------\n\n'); } catch (error) { console.log(`Error processing transaction. ${error}`); console.log(error.stack); } finally { // Disconnect from the gateway console.log('Disconnect from Fabric gateway.'); gateway.disconnect(); } } main().then(() => { console.log('Queryapp program complete.'); }).catch((e) => { console.log('Queryapp program exception.'); console.log(e); console.log(e.stack); process.exit(-1); });
そしたら実際に台帳へ書き込まれているのが確認できます。
[root@localhost application]# node queryapp.js Connect to Fabric gateway. Use network channel: mychannel. Use org.papernet.commercialpaper smart contract. ----------------------------------------------------------------------------------------- ****** Submitting commercial paper queries ****** 1. Query Commercial Paper History.... ----------------------------------------------------------------------------------------- [ { TxId: '12b7350859ba3c5417bc4e655556b7543ef899561ccae19c864d521050f51840', Timestamp: '2021-03-07T08:53:10.737Z', Value: { class: 'org.papernet.commercialpaper', currentState: 'ISSUED', issuer: 'MagnetoCorp', paperNumber: '00001', issueDateTime: '2020-05-31', maturityDateTime: '2020-11-30', faceValue: 5000000, mspid: 'Org2MSP', owner: 'MagnetoCorp' } } ] History query complete. ----------------------------------------------------------------------------------------- Disconnect from Fabric gateway. Queryapp program complete.
この時実行しているのは以下のチェインコードになります。
/** * Query history of a commercial paper * @param {Context} ctx the transaction context * @param {String} issuer commercial paper issuer * @param {Integer} paperNumber paper number for this issuer */ async queryHistory(ctx, issuer, paperNumber) { // Get a key to be used for History query let query = new QueryUtils(ctx, 'org.papernet.paper'); let results = await query.getAssetHistory(issuer, paperNumber); // (cpKey); return results; }
コマーシャルペーパーの購入、償還
magnetocorp という組織でコマーシャルペーパーを発行したので、次は digibank という組織でコマーシャルペーパーを購入、償還します。
まずは magnetocorp でやった時と同様に wallet を初期化します。
[root@localhost commercial-paper]# cd organization/digibank/application [root@localhost application]# node enrollUser.js
次にコマーシャルペーパーを購入します。
[root@localhost application]# node buy.js Connect to Fabric gateway. Use network channel: mychannel. Use org.papernet.commercialpaper smart contract. Submit commercial paper buy transaction. Process buy transaction response. MagnetoCorp commercial paper : 00001 successfully purchased by DigiBank Transaction complete. Disconnect from Fabric gateway. Buy program complete.
実行しているbuy.js
は以下のようになっています。
/* * Copyright IBM Corp. All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /* * This application has 6 basic steps: * 1. Select an identity from a wallet * 2. Connect to network gateway * 3. Access PaperNet network * 4. Construct request to buy commercial paper * 5. Submit transaction * 6. Process response */ 'use strict'; // Bring key classes into scope, most importantly Fabric SDK network class const fs = require('fs'); const yaml = require('js-yaml'); const { Wallets, Gateway } = require('fabric-network'); const CommercialPaper = require('../../magnetocorp/contract/lib/paper.js'); // Main program function async function main () { // A wallet stores a collection of identities for use const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet'); // A gateway defines the peers used to access Fabric networks const gateway = new Gateway(); // Main try/catch block try { // Specify userName for network access const userName = 'balaji'; // Load connection profile; will be used to locate a gateway let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org1.yaml', 'utf8')); // Set connection options; identity and wallet let connectionOptions = { identity: userName, wallet: wallet, discovery: { enabled: true, asLocalhost: true } }; // Connect to gateway using application specified parameters console.log('Connect to Fabric gateway.'); await gateway.connect(connectionProfile, connectionOptions); // Access PaperNet network console.log('Use network channel: mychannel.'); const network = await gateway.getNetwork('mychannel'); // Get addressability to commercial paper contract console.log('Use org.papernet.commercialpaper smart contract.'); const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper'); // buy commercial paper console.log('Submit commercial paper buy transaction.'); const buyResponse = await contract.submitTransaction('buy', 'MagnetoCorp', '00001', 'MagnetoCorp', 'DigiBank', '4900000', '2020-05-31'); // process response console.log('Process buy transaction response.'); let paper = CommercialPaper.fromBuffer(buyResponse); console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully purchased by ${paper.owner}`); console.log('Transaction complete.'); } catch (error) { console.log(`Error processing transaction. ${error}`); console.log(error.stack); } finally { // Disconnect from the gateway console.log('Disconnect from Fabric gateway.'); gateway.disconnect(); } } main().then(() => { console.log('Buy program complete.'); }).catch((e) => { console.log('Buy program exception.'); console.log(e); console.log(e.stack); process.exit(-1); });
buy.js
内で実行しているconst buyResponse = await contract.submitTransaction('buy', 'MagnetoCorp', '00001', 'MagnetoCorp', 'DigiBank', '4900000', '2020-05-31');
より、実行しているチェインコードは以下のようになっていることが確認できます。この時issuer=MagnetoCorp,paperNumber=00001,currentOwner=MagnetoCorp,newOwner=DigiBank,price=4900000,purchaseDateTime=2020-05-31
で先ほど発行したコマーシャルペーパーが指定されていることが確認できます。
mspid や owner を更新しているのが確認できます。
/** * Buy commercial paper * * @param {Context} ctx the transaction context * @param {String} issuer commercial paper issuer * @param {Integer} paperNumber paper number for this issuer * @param {String} currentOwner current owner of paper * @param {String} newOwner new owner of paper * @param {Integer} price price paid for this paper // transaction input - not written to asset * @param {String} purchaseDateTime time paper was purchased (i.e. traded) // transaction input - not written to asset */ async buy(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) { // Retrieve the current paper using key fields provided let paperKey = CommercialPaper.makeKey([issuer, paperNumber]); let paper = await ctx.paperList.getPaper(paperKey); // Validate current owner if (paper.getOwner() !== currentOwner) { throw new Error('\nPaper ' + issuer + paperNumber + ' is not owned by ' + currentOwner); } // First buy moves state from ISSUED to TRADING (when running ) if (paper.isIssued()) { paper.setTrading(); } // Check paper is not already REDEEMED if (paper.isTrading()) { paper.setOwner(newOwner); // save the owner's MSP let mspid = ctx.clientIdentity.getMSPID(); paper.setOwnerMSP(mspid); } else { throw new Error('\nPaper ' + issuer + paperNumber + ' is not trading. Current state = ' + paper.getCurrentState()); } // Update the paper await ctx.paperList.updatePaper(paper); return paper; }
queryapp.js
を実行して台帳の履歴を確認すると実際反映されていることが確認できます。まだ償還していないので4. Named Query: ... All papers in org.papernet.papers that are in current state of redeemed
の結果が空になっています。
[root@localhost application]# node queryapp.js Connect to Fabric gateway. Use network channel: mychannel. Use org.papernet.commercialpaper smart contract. ----------------------------------------------------------------------------------------- ****** Submitting commercial paper queries ****** 1. Query Commercial Paper History.... ----------------------------------------------------------------------------------------- [ { TxId: '5d3157011e7fd9cc6782b1fc8b85aa2df3d8eed449f786b980b9def35b3d89ec', Timestamp: '2021-03-07T09:56:38.922Z', Value: { class: 'org.papernet.commercialpaper', currentState: 'TRADING', faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org1MSP', owner: 'DigiBank', paperNumber: '00001' } }, { TxId: '12b7350859ba3c5417bc4e655556b7543ef899561ccae19c864d521050f51840', Timestamp: '2021-03-07T08:53:10.737Z', Value: { class: 'org.papernet.commercialpaper', currentState: 'ISSUED', issuer: 'MagnetoCorp', paperNumber: '00001', issueDateTime: '2020-05-31', maturityDateTime: '2020-11-30', faceValue: 5000000, mspid: 'Org2MSP', owner: 'MagnetoCorp' } } ] History query complete. ----------------------------------------------------------------------------------------- 2. Query Commercial Paper Ownership.... Papers owned by MagnetoCorp ----------------------------------------------------------------------------------------- [] Paper Ownership query complete. ----------------------------------------------------------------------------------------- 3. Query Commercial Paper Partial Key.... Papers in org.papernet.papers namespace and prefixed MagnetoCorp ----------------------------------------------------------------------------------------- [ { Key: '\u0000org.papernet.paper\u0000MagnetoCorp\u000000001\u0000', Record: { class: 'org.papernet.commercialpaper', currentState: 3, faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org1MSP', owner: 'DigiBank', paperNumber: '00001' } } ] Partial Key query complete. ----------------------------------------------------------------------------------------- 4. Named Query: ... All papers in org.papernet.papers that are in current state of redeemed ----------------------------------------------------------------------------------------- [] Named query "redeemed" complete. ----------------------------------------------------------------------------------------- 5. Named Query:.... All papers in org.papernet.papers with faceValue > 4000000 ----------------------------------------------------------------------------------------- [ { Key: '\u0000org.papernet.paper\u0000MagnetoCorp\u000000001\u0000', Record: { class: 'org.papernet.commercialpaper', currentState: 3, faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org1MSP', owner: 'DigiBank', paperNumber: '00001' } } ] Named query by "value" complete. ----------------------------------------------------------------------------------------- Disconnect from Fabric gateway. Queryapp program complete. [root@localhost application]#
それから償還します。
[root@localhost application]# node redeem.js Connect to Fabric gateway. Use network channel: mychannel. Use org.papernet.commercialpaper smart contract. Submit commercial paper redeem transaction. Process redeem transaction response. MagnetoCorp commercial paper : 00001 successfully redeemed with MagnetoCorp Transaction complete. Disconnect from Fabric gateway. Redeem program complete. [root@localhost application]#
実行しているredeem.js
は以下のようになっています。
/* * Copyright IBM Corp. All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /* * This application has 6 basic steps: * 1. Select an identity from a wallet * 2. Connect to network gateway * 3. Access PaperNet network * 4. Construct request to issue commercial paper * 5. Submit transaction * 6. Process response */ 'use strict'; // Bring key classes into scope, most importantly Fabric SDK network class const fs = require('fs'); const yaml = require('js-yaml'); const { Wallets, Gateway } = require('fabric-network'); const CommercialPaper = require('../contract/lib/paper.js'); // Main program function async function main() { // A wallet stores a collection of identities for use const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet'); // A gateway defines the peers used to access Fabric networks const gateway = new Gateway(); // Main try/catch block try { // Specify userName for network access // Specify userName for network access const userName = 'balaji'; // Load connection profile; will be used to locate a gateway let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org1.yaml', 'utf8')); // Set connection options; identity and wallet let connectionOptions = { identity: userName, wallet: wallet, discovery: { enabled:true, asLocalhost: true } }; // Connect to gateway using application specified parameters console.log('Connect to Fabric gateway.'); await gateway.connect(connectionProfile, connectionOptions); // Access PaperNet network console.log('Use network channel: mychannel.'); const network = await gateway.getNetwork('mychannel'); // Get addressability to commercial paper contract console.log('Use org.papernet.commercialpaper smart contract.'); const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper'); // redeem commercial paper console.log('Submit commercial paper redeem transaction.'); const redeemResponse = await contract.submitTransaction('redeem', 'MagnetoCorp', '00001', 'DigiBank', 'Org2MSP', '2020-11-30'); // process response console.log('Process redeem transaction response.'); let paper = CommercialPaper.fromBuffer(redeemResponse); console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully redeemed with ${paper.owner}`); console.log('Transaction complete.'); } catch (error) { console.log(`Error processing transaction. ${error}`); console.log(error.stack); } finally { // Disconnect from the gateway console.log('Disconnect from Fabric gateway.') gateway.disconnect(); } } main().then(() => { console.log('Redeem program complete.'); }).catch((e) => { console.log('Redeem program exception.'); console.log(e); console.log(e.stack); process.exit(-1); });
const redeemResponse = await contract.submitTransaction('redeem', 'MagnetoCorp', '00001', 'DigiBank', 'Org2MSP', '2020-11-30');
より実行しているチェインコードはredeem
になっています。issuer=MagnetoCorp,paperNumber=00001,redeemingOwner=DigiBank,issuingOwnerMSP=Org2MSP,redeemDateTime=2020-11-30
を指定しています。
実行ユーザーがオーナーであれば償還しているのが確認できます。
/** * Redeem commercial paper * * @param {Context} ctx the transaction context * @param {String} issuer commercial paper issuer * @param {Integer} paperNumber paper number for this issuer * @param {String} redeemingOwner redeeming owner of paper * @param {String} issuingOwnerMSP the MSP of the org that the paper will be redeemed with. * @param {String} redeemDateTime time paper was redeemed */ async redeem(ctx, issuer, paperNumber, redeemingOwner, issuingOwnerMSP, redeemDateTime) { let paperKey = CommercialPaper.makeKey([issuer, paperNumber]); let paper = await ctx.paperList.getPaper(paperKey); // Check paper is not alread in a state of REDEEMED if (paper.isRedeemed()) { throw new Error('\nPaper ' + issuer + paperNumber + ' has already been redeemed'); } // Validate current redeemer's MSP matches the invoking redeemer's MSP id - can only redeem if you are the owning org. if (paper.getOwnerMSP() !== ctx.clientIdentity.getMSPID()) { throw new Error('\nPaper ' + issuer + paperNumber + ' cannot be redeemed by ' + ctx.clientIdentity.getMSPID() + ', as it is not the authorised owning Organisation'); } // As this is just a sample, can show additional verification check: that the redeemer provided matches that on record, before redeeming it if (paper.getOwner() === redeemingOwner) { paper.setOwner(paper.getIssuer()); paper.setOwnerMSP(issuingOwnerMSP); paper.setRedeemed(); paper.redeemDateTime = redeemDateTime; // record redemption date against the asset (the complement to 'issue date') } else { throw new Error('\nRedeeming owner: ' + redeemingOwner + ' organisation does not currently own paper: ' + issuer + paperNumber); } await ctx.paperList.updatePaper(paper); return paper; }
queryapp.js
実行で4. Named Query: ... All papers in org.papernet.papers that are in current state of redeemed
に償還済コマーシャルペーパーとして表示されるのが確認できます。
[root@localhost application]# node queryapp.js Connect to Fabric gateway. Use network channel: mychannel. Use org.papernet.commercialpaper smart contract. ----------------------------------------------------------------------------------------- ****** Submitting commercial paper queries ****** 1. Query Commercial Paper History.... ----------------------------------------------------------------------------------------- [ { TxId: 'dc9c212c50c31ef798ecedb26ddde8bf1ac4e8c3910ea157cdcc5df838a178f9', Timestamp: '2021-03-07T10:08:52.416Z', Value: { class: 'org.papernet.commercialpaper', currentState: 'REDEEMED', faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org2MSP', owner: 'MagnetoCorp', paperNumber: '00001', redeemDateTime: '2020-11-30' } }, { TxId: '5d3157011e7fd9cc6782b1fc8b85aa2df3d8eed449f786b980b9def35b3d89ec', Timestamp: '2021-03-07T09:56:38.922Z', Value: { class: 'org.papernet.commercialpaper', currentState: 'TRADING', faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org1MSP', owner: 'DigiBank', paperNumber: '00001' } }, { TxId: '12b7350859ba3c5417bc4e655556b7543ef899561ccae19c864d521050f51840', Timestamp: '2021-03-07T08:53:10.737Z', Value: { class: 'org.papernet.commercialpaper', currentState: 'ISSUED', issuer: 'MagnetoCorp', paperNumber: '00001', issueDateTime: '2020-05-31', maturityDateTime: '2020-11-30', faceValue: 5000000, mspid: 'Org2MSP', owner: 'MagnetoCorp' } } ] History query complete. ----------------------------------------------------------------------------------------- 2. Query Commercial Paper Ownership.... Papers owned by MagnetoCorp ----------------------------------------------------------------------------------------- [ { Key: '\u0000org.papernet.paper\u0000MagnetoCorp\u000000001\u0000', Record: { class: 'org.papernet.commercialpaper', currentState: 4, faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org2MSP', owner: 'MagnetoCorp', paperNumber: '00001', redeemDateTime: '2020-11-30' } } ] Paper Ownership query complete. ----------------------------------------------------------------------------------------- 3. Query Commercial Paper Partial Key.... Papers in org.papernet.papers namespace and prefixed MagnetoCorp ----------------------------------------------------------------------------------------- [ { Key: '\u0000org.papernet.paper\u0000MagnetoCorp\u000000001\u0000', Record: { class: 'org.papernet.commercialpaper', currentState: 4, faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org2MSP', owner: 'MagnetoCorp', paperNumber: '00001', redeemDateTime: '2020-11-30' } } ] Partial Key query complete. ----------------------------------------------------------------------------------------- 4. Named Query: ... All papers in org.papernet.papers that are in current state of redeemed ----------------------------------------------------------------------------------------- [ { Key: '\u0000org.papernet.paper\u0000MagnetoCorp\u000000001\u0000', Record: { class: 'org.papernet.commercialpaper', currentState: 4, faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org2MSP', owner: 'MagnetoCorp', paperNumber: '00001', redeemDateTime: '2020-11-30' } } ] Named query "redeemed" complete. ----------------------------------------------------------------------------------------- 5. Named Query:.... All papers in org.papernet.papers with faceValue > 4000000 ----------------------------------------------------------------------------------------- [ { Key: '\u0000org.papernet.paper\u0000MagnetoCorp\u000000001\u0000', Record: { class: 'org.papernet.commercialpaper', currentState: 4, faceValue: 5000000, issueDateTime: '2020-05-31', issuer: 'MagnetoCorp', maturityDateTime: '2020-11-30', mspid: 'Org2MSP', owner: 'MagnetoCorp', paperNumber: '00001', redeemDateTime: '2020-11-30' } } ] Named query by "value" complete. ----------------------------------------------------------------------------------------- Disconnect from Fabric gateway. Queryapp program complete. [root@localhost application]#
終了
コマーシャルペーパーの動作が確認できたので docker コンテナを停止して終了します。
[root@localhost commercial-paper]# ./network-clean.sh set -o pipefail ~省略~