Hyperledger Tutorial のコマーシャルペーパー をやってみた

前回やった Hyperledger のチェインコードデプロイのチュートリアルに引き続ぎコマーシャルペーパーチュートリアルもやってみました。 以下のfabric-samplesリポジトリcommercial-paper 配下を動かしていきます。

github.com

ネットワーク起動

まずは 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 で定義された関数が動くようになります。

github.com

コマーシャルペーパーの発行

チェインコードが動くようになったので 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 になっているので定義の確認などしやすくなっています。

github.com

/*
 * 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を実行しているようです。

github.com

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
~省略~