hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)

hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)_第1张图片

此商业票据系统包含两个组织(MagnetoCorp和DigiBank),他们之间通过票据网络(PaperNet)进行商业票据的交易。

当启动了basic network之后,你将扮演 MagnetoCorp公司的一个名叫Isabella的职员的角色来issue 发行一个商业票据。然后你将扮演Balaji的角色,Balaji是Digibank的一名员工,他将购买这张商业票据,并且持有一段时间,然后与MagnetoCorp进行redeem赎回操作,进而赚取一小笔利润。

接下来您将作为开发人员、最终用户和管理员,分别在不同的组织中执行以下步骤,旨在帮助您了解作为两个独立工作的不同组织是如何协同工作的。

  • Set up machine and download samples
  • Create a network
  • Understand the structure of a smart contract
  • Work as an organization, MagnetoCorp, to install and instantiate smart contract
  • Understand the structure of a MagnetoCorp application, including its dependencies
  • Configure and use a wallet and identities
  • Run a MagnetoCorp application to issue a commercial paper
  • Understand how a second organization, Digibank, uses the smart contract in their applications
  • As Digibank, run applications that buy and redeem commercial paper

前提条件

1. Node version 8.9.0, or higher. Node is a JavaScript runtime that you can use to run applications and smart contracts

2. Docker version 18.06, or higher. Docker help developers and administrators create standard environments for building and running applications and smart contracts.

3. 非必要:A source code editor, such as Visual Studio Code version 1.28, or higher

4. 非必要:Node Version Manager. NVM helps you easily switch between different versions of node

下载samples

这个商业票据教程是fabric-samples里的一部分,因此下载过的就不用再次下载了。(我是直接下了fabric,然后通过bootstrap.sh脚本下载的fabric-samples,这样连一些附加依赖也会自动下载好。如果只下载fabric-samples,还需要下载一些二进制文件和依赖,麻烦一些。因此建议直接下载fabric。)

此后还有GOPATH环境变量之类的,假设都已经弄好了,这里不再介绍。

创建网络

 接下来的过程会打开多个终端供不同的用户和组件使用。比如:

1. Isabella and Balaji使用的终端:与其他组织进行票据交易

2. administrators from MagnetoCorp and DigiBank使用的终端:发行票据以及安装和初始化智能合约

3. 显示用的终端:显示peer、orderer和CA日志

本教程案例是在basic network网络上进行的,你可以使用fabric-samples\basic-network目录下的命令和配置文件对basic network进行管理。

现在先把这个网络启动起来:

[root@slave2 fabric-samples]# cd basic-network/
[root@slave2 basic-network]# ./start.sh 

# don't rewrite paths for Windows Git Bash users
export MSYS_NO_PATHCONV=1

docker-compose -f docker-compose.yml down
Stopping cli                    ... done
Stopping peer0.org1.example.com ... done
Stopping orderer.example.com    ... done
Stopping ca.example.com         ... done
Stopping couchdb                ... done
Removing cli                    ... done
Removing peer0.org1.example.com ... done
Removing orderer.example.com    ... done
Removing ca.example.com         ... done
Removing couchdb                ... done
Removing network net_basic

docker-compose -f docker-compose.yml up -d ca.example.com orderer.example.com peer0.org1.example.com couchdb
Creating network "net_basic" with the default driver
Creating ca.example.com      ... done
Creating couchdb             ... done
Creating orderer.example.com ... done
Creating peer0.org1.example.com ... done

# wait for Hyperledger Fabric to start
# incase of errors when running later commands, issue export FABRIC_START_TIMEOUT=
export FABRIC_START_TIMEOUT=10
#echo ${FABRIC_START_TIMEOUT}
sleep ${FABRIC_START_TIMEOUT}

# Create the channel
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/configtx/channel.tx
2019-02-19 13:59:18.287 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-02-19 13:59:18.409 UTC [cli.common] readBlock -> INFO 002 Received block: 0
# Join peer0.org1.example.com to the channel.
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org1.example.com peer channel join -b mychannel.block
2019-02-19 13:59:18.897 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-02-19 13:59:19.135 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel

可以看到,使用的是docker-compose.yml这个配置文件进行镜像拉取和启动等操作,具体可以查看这个文件。

网络启动起来后,可以查看启动了哪些容器:

[root@slave2 basic-network]# docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                            NAMES
988a305f5a61        hyperledger/fabric-peer      "peer node start"        4 minutes ago       Up 4 minutes        0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp   peer0.org1.example.com
ecf32e058040        hyperledger/fabric-orderer   "orderer"                4 minutes ago       Up 4 minutes        0.0.0.0:7050->7050/tcp                           orderer.example.com
035f9f462948        hyperledger/fabric-ca        "sh -c 'fabric-ca-se…"   4 minutes ago       Up 4 minutes        0.0.0.0:7054->7054/tcp                           ca.example.com
05e9598f98c1        hyperledger/fabric-couchdb   "tini -- /docker-ent…"   4 minutes ago       Up 4 minutes        4369/tcp, 9100/tcp, 0.0.0.0:5984->5984/tcp       couchdb

这些容器组成了一个称为net_basic的docker网络,可以通过docker network命令查看这个网络:

[root@slave2 basic-network]# docker network inspect net_basic
[
    {
        "Name": "net_basic",
        "Id": "75c9e8ad23392c10f9b815f72e0f847cd815a5f419e7c67b2c172ad23f570c43",
        "Created": "2019-02-19T21:59:01.917209341+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.25.0.0/16",
                    "Gateway": "172.25.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "035f9f462948a62b7928d83601efdff5ee3f241899d8bb87a7c605f1ef0fe4ab": {
                "Name": "ca.example.com",
                "EndpointID": "2a6121d092b1a559d33057ecdc8fc69cbde5e3a971cc5ba153592f1b7ae82540",
                "MacAddress": "02:42:ac:19:00:02",
                "IPv4Address": "172.25.0.2/16",
                "IPv6Address": ""
            },
            "05e9598f98c10322ff50ddf303258a2baa65d110d4dcff47a4d1a235329d4f0a": {
                "Name": "couchdb",
                "EndpointID": "9496f486a589777f55293d32bc805c34ce4525cb4b7189c4f0eeabf6414a5adf",
                "MacAddress": "02:42:ac:19:00:04",
                "IPv4Address": "172.25.0.4/16",
                "IPv6Address": ""
            },
            "988a305f5a613307ca1cc5b8f65333fcac3a7f8bb17e8f13382e08039ae07946": {
                "Name": "peer0.org1.example.com",
                "EndpointID": "509a41dc62afa1ccca1a801a2010c0f08490089de08f525aa2cc3ad8befed594",
                "MacAddress": "02:42:ac:19:00:05",
                "IPv4Address": "172.25.0.5/16",
                "IPv6Address": ""
            },
            "ecf32e058040f1246392ae7f60bf63480f1a969bdc30c7fce935e678622f7b60": {
                "Name": "orderer.example.com",
                "EndpointID": "0b0a4364945a96d106e76edd2c7a352af6b7e20128f858dfa3c18aacbbc498a3",
                "MacAddress": "02:42:ac:19:00:03",
                "IPv4Address": "172.25.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

可以看到,虽然这4个容器处于同一个docker网络中,但使用了不同的ip地址。

接下来,以MagnetoCorp的身份去交易商业票据!

Working as MagnetoCorp

为了监视Papernet的Magnetorp组件,管理员可以使用logpute工具查看一组Docker容器的聚合输出。它将不同的输出流收集到一个地方,这样就可以很容易地从一个窗口看到正在发生的事情(例如在安装智能合约或在调用智能合约时),这对管理员或开发人员非常有用。 

现在我们以MagnetoCorp 公司管理员的身份操作网络。

首先新打开一个终端,进入commercial-paper/organization/magnetocorp/configuration/cli/目录执行以下命令(因为我之前启动过,所显示的有所不同):

[root@slave2 fabric-samples]# cd commercial-paper/organization/magnetocorp/configuration/cli/
[root@slave2 cli]# ll
总用量 8
-rw-r--r-- 1 root root 1168 2月  18 12:58 docker-compose.yml
-rwxr-xr-x 1 root root  740 2月  18 12:58 monitordocker.sh
[root@slave2 cli]# ./monitordocker.sh net_basic
Starting monitoring on all containers on the network net_basic
a6c6fe2e5b13ad8d13699ee97ef5bc0826f2db89be0b003f732b177df667a78b
         couchdb|[info] 2019-02-19T14:14:08.325134Z nonode@nohost <0.47.0> -------- alarm_handler: {set,{system_memory_high_watermark,[]}}

这个终端是用来显示所有docker容器的输出的,因此我们现在再另外打开一个终端,以MagnetoCorp 管理员的身份与这个网络进行交互。

hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)_第2张图片

为了与PaperNet网络进行交互,MagnetoCorp 管理员需要使用peer命令。这些命令可以很方便地在hyperledger/fabric-tools镜像中预先构建 。

现在使用docker-compose命令为管理员启动一个MagnetoCorp公司特定的docker容器:

[root@slave2 fabric-samples]# cd commercial-paper/organization/magnetocorp/configuration/cli/
[root@slave2 cli]# docker-compose -f docker-compose.yml up -d cliMagnetoCorp
Creating cliMagnetoCorp ... done

接下来通过docker ps命令查看fabric-tools镜像是否已经拉取下来并加入到网络中:

[root@slave2 cli]# docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                            NAMES
52d428e6983b        hyperledger/fabric-tools     "/bin/bash"              2 minutes ago       Up 2 minutes                                                         cliMagnetoCorp
a6c6fe2e5b13        gliderlabs/logspout          "/bin/logspout"          19 minutes ago      Up 19 minutes       127.0.0.1:8000->80/tcp                           logspout
988a305f5a61        hyperledger/fabric-peer      "peer node start"        33 minutes ago      Up 33 minutes       0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp   peer0.org1.example.com
ecf32e058040        hyperledger/fabric-orderer   "orderer"                33 minutes ago      Up 33 minutes       0.0.0.0:7050->7050/tcp                           orderer.example.com
035f9f462948        hyperledger/fabric-ca        "sh -c 'fabric-ca-se…"   33 minutes ago      Up 33 minutes       0.0.0.0:7054->7054/tcp                           ca.example.com
05e9598f98c1        hyperledger/fabric-couchdb   "tini -- /docker-ent…"   33 minutes ago      Up 33 minutes       4369/tcp, 9100/tcp, 0.0.0.0:5984->5984/tcp       couchdb

可以看到fabric-tools镜像的容器ID为52d428e6983b,MagnetoCorp 公司管理员将会在这个容器中使用命令行与PaperNet进行交互。除此之外还可以看到logspout 镜像的容器ID为a6c6fe2e5b13,这个容器的作用是为monitordocker.sh命令获取所以容器的输出。

接下来我们以MagnetoCorp 管理员的身份与网络进行交互。

智能合约

PaperNet 智能合约的三个主要方法是issue、buy和redeem。接下来测试这三个方法。

首先看一下智能合约:

[root@slave2 lib]# pwd
/root/go/src/github.com/hyperledger/fabric/scripts/fabric-samples/commercial-paper/organization/magnetocorp/contract/lib
[root@slave2 lib]# ll
总用量 16
-rw-r--r-- 1 root root 5071 2月  18 12:58 papercontract.js
-rw-r--r-- 1 root root 2177 2月  18 12:58 paper.js
-rw-r--r-- 1 root root  649 2月  18 12:58 paperlist.js

papercontract.js文件即是这个商业票据的智能合约。

/*
SPDX-License-Identifier: Apache-2.0
*/

'use strict';

// Fabric smart contract classes
const { Contract, Context } = require('fabric-contract-api');

// PaperNet specifc classes
const CommercialPaper = require('./paper.js');
const PaperList = require('./paperlist.js');

/**
 * A custom context provides easy access to list of all commercial papers
 */
class CommercialPaperContext extends Context {

    constructor() {
        super();
        // All papers are held in a list of papers
        this.paperList = new PaperList(this);
    }

}

/**
 * Define commercial paper smart contract by extending Fabric Contract class
 *
 */
class CommercialPaperContract extends Contract {

    constructor() {
        // Unique namespace when multiple contracts per chaincode file
        super('org.papernet.commercialpaper');
    }

    /**
     * Define a custom context for commercial paper
    */
    createContext() {
        return new CommercialPaperContext();
    }

    /**
     * Instantiate to perform any setup of the ledger that might be required.
     * @param {Context} ctx the transaction context
     */
    async instantiate(ctx) {
        // No implementation required with this example
        // It could be where data migration is performed, if necessary
        console.log('Instantiate the contract');
    }

    /**
     * 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, faceValue);

        // Smart contract, rather than paper, moves paper into ISSUED state
        paper.setIssued();

        // Newly issued paper is owned by the issuer
        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.toBuffer();
    }

    /**
     * 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
     * @param {String} purchaseDateTime time paper was purchased (i.e. traded)
    */
    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('Paper ' + issuer + paperNumber + ' is not owned by ' + currentOwner);
        }

        // First buy moves state from ISSUED to TRADING
        if (paper.isIssued()) {
            paper.setTrading();
        }

        // Check paper is not already REDEEMED
        if (paper.isTrading()) {
            paper.setOwner(newOwner);
        } else {
            throw new Error('Paper ' + issuer + paperNumber + ' is not trading. Current state = ' +paper.getCurrentState());
        }

        // Update the paper
        await ctx.paperList.updatePaper(paper);
        return paper.toBuffer();
    }

    /**
     * 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} redeemDateTime time paper was redeemed
    */
    async redeem(ctx, issuer, paperNumber, redeemingOwner, redeemDateTime) {

        let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);

        let paper = await ctx.paperList.getPaper(paperKey);

        // Check paper is not REDEEMED
        if (paper.isRedeemed()) {
            throw new Error('Paper ' + issuer + paperNumber + ' already redeemed');
        }

        // Verify that the redeemer owns the commercial paper before redeeming it
        if (paper.getOwner() === redeemingOwner) {
            paper.setOwner(paper.getIssuer());
            paper.setRedeemed();
        } else {
            throw new Error('Redeeming owner does not own paper' + issuer + paperNumber);
        }

        await ctx.paperList.updatePaper(paper);
        return paper.toBuffer();
    }

}

module.exports = CommercialPaperContract;
  • // Fabric smart contract classes
    const { Contract, Context } = require('fabric-contract-api');

此语句将智能合约广泛使用的两个关键的fabric类(Contract和上Context)引入进来。关于这些类的详细信息请看这里。

  • /**
     * Define commercial paper smart contract by extending Fabric Contract class
     *
     */
    class CommercialPaperContract extends Contract {

此语句定义了智能合约类CommercialPaperContract (基于内置的Contract类)。issue、buy和redeem方法就在此类中定义。

  • async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {

此方法用来发行商业票据,其中的参数将会用来创建商业票据。buy和redeem方法与此类似。

  • // create an instance of the paper
    let paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime,maturityDateTime, faceValue);

此语句位于issue方法中,作用是使用CommercialPaper 类根据输入的参数创建一个商业票据 。buy和redeem方法中也用到了CommercialPaper 这个类,用法与此类似。

  • // Add the paper to the list of all similar commercial papers in the ledger world state
    await ctx.paperList.addPaper(paper);

 此语句使用ctx.paperList将创建的商业票据添加到账本中(ctx.paperList是PaperList 类的一个实例,PaperList 这个类是在智能合约环境CommercialPaperContext 被初始化时创建的)。

  • // Must return a serialized paper to caller of smart contract
    return paper.toBuffer();

此语句返回一个二进制buffer作为issue交易的响应,供智能合约调用者处理。 

请仔细阅读commercial-paper/organization/magnetocorp/contract/目录下的文件,以更全面的了解智能合约是如何工作的。

安装智能合约

papercontract 在被应用程序调用之前,必须被安装到PaperNet中正确的peer节点上。MagnetoCorp 和 DigiBank的管理员能够将 papercontract安装到他们拥有权限的peer节点上。

hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)_第3张图片

智能合约是开发应用程序的关键,是chaincode链码的一部分。一个或多个智能合约可以被定义在一个链码中,安装链码之后PaperNet中的不同组织就可以使用这些合约了。这意味着只有管理员需要考虑chaincode链码,其他人只需要考虑合约即可。

MagnetoCorp 管理员使用peer chaincode install命令将papercontract智能合约从本地计算机的文件系统复制到目标peer节点的Docker容器中的文件系统中。 一旦将智能合约安装到peer节点并在通道上初始化之后,papercontract就能够被应用程序调用,进而通过putState() 和getState()等fabric API与账本数据库交互。在ledger-api\statelist.js中可以看到这些API是如何被StateList类调用的。

现在,我们以MagnetoCorp 管理员的身份安装papercontract。在MagnetoCorp 管理员终端的cliMagnetCorp容器中使用docker exec命令运行peer chaincode install命令:

[root@slave2 lib]# docker exec cliMagnetoCorp peer chaincode install -n papercontract -v 0 -p /opt/gopath/src/github.com/contract -l node
2019-02-19 15:36:52.282 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2019-02-19 15:36:52.282 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2019-02-19 15:36:52.466 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response: 

上面的输出说明已经成功的将papercontract安装到了peer0.org1.example.com中,现在只需要再将papercontract安装到MagnetoCorp节点即可。

peer chaincode install命令通过参数-p指定智能合约路径位于cliMagnetCorp容器的文件系统的/opt/gopath/src/github.com/contract路径下,而这个路径已经通过magnetocorp/configuration/cli/docker-compose.yml映射到本地文件系统的.../organization/magnetocorp/contract路径下。查看一下docker-compose.yml即可看到具体映射实现:

You can read more about docker compose here and peer chaincode install command here.

实例化智能合约 

现在,包含CommercialPaper智能合约的papercontract 链码已经被安装到了PaperNet 网络中相关的peer节点上了,管理员接下来可以将其提供给不同的channel,以便连接到这些channel的应用程序可以调用这个链码。因为我们使用的是PaperNet的基本网络配置,所以我们只在一个网络通道MyChannel中提供PaperContract。

 hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)_第4张图片

MagnetoCorp 管理员使用peer chaincode instantiate命令将papercontract实例化到mychannel上:

[root@slave2 lib]# docker exec cliMagnetoCorp peer chaincode instantiate -n papercontract -v 0 -l node -c '{"Args":["org.papernet.commercialpaper:instantiate"]}' -C mychannel -P "AND ('Org1MSP.member')"
2019-02-19 16:04:17.572 UTC [chaincodeCmd] InitCmdFactory -> INFO 001 Retrieved channel (mychannel) orderer endpoint: orderer.example.com:7050
2019-02-19 16:04:17.578 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default escc
2019-02-19 16:04:17.578 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default vscc

 (注意:运行上面这些docker命令的时候,可以看到打开的第一个监视终端会有内容输出)

docker exec cliMagnetoCorp peer chaincode instantiate -n papercontract -v 0 -l node -c '{"Args":["org.papernet.commercialpaper:instantiate"]}' -C mychannel -P "AND ('Org1MSP.member')"这条命令最重要的参数是-p参数,它指明了papercontract链码的背书策略(背书策略是指只有当所有指定的组织都对交易进行了背书后才证明交易是有效的)。任何交易,不管是有效还是无效,都会被记录到区块链账本上,但是只有有效的交易才会更新世界状态。

In passing, see how instantiate passes the orderer address orderer.example.com:7050. This is because it additionally submits an instantiate transaction to the orderer, which will include the transaction in the next block and distribute it to all peers that have joined mychannel, enabling any peer to execute the chaincode in their own isolated chaincode container. Note that instantiate only needs to be issued once for papercontract even though typically it is installed on many peers.

上面这段话需要好好理解!

再次查看docker ps可以看到papercontract容器已经启动起来了:

[root@slave2 lib]# docker ps
CONTAINER ID        IMAGE                                                                                                         COMMAND                  CREATED             STATUS              PORTS                                            NAMES
8a7e2c1de652        dev-peer0.org1.example.com-papercontract-0-d96abb966a1ed760663cf0a061700a902284832716c55b4cb05eca53054fe011   "/bin/sh -c 'cd /usr…"   16 minutes ago      Up 15 minutes                                                        dev-peer0.org1.example.com-papercontract-0
52d428e6983b        hyperledger/fabric-tools                                                                                      "/bin/bash"              2 hours ago         Up 2 hours                                                           cliMagnetoCorp
a6c6fe2e5b13        gliderlabs/logspout                                                                                           "/bin/logspout"          2 hours ago         Up 2 hours          127.0.0.1:8000->80/tcp                           logspout
988a305f5a61        hyperledger/fabric-peer                                                                                       "peer node start"        2 hours ago         Up 2 hours          0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp   peer0.org1.example.com
ecf32e058040        hyperledger/fabric-orderer                                                                                    "orderer"                2 hours ago         Up 2 hours          0.0.0.0:7050->7050/tcp                           orderer.example.com
035f9f462948        hyperledger/fabric-ca                                                                                         "sh -c 'fabric-ca-se…"   2 hours ago         Up 2 hours          0.0.0.0:7054->7054/tcp                           ca.example.com
05e9598f98c1        hyperledger/fabric-couchdb                                                                                    "tini -- /docker-ent…"   2 hours ago         Up 2 hours          4369/tcp, 9100/tcp, 0.0.0.0:5984->5984/tcp       couchdb

 观察papercontract容器的名字:dev-peer0.org1.example.com-papercontract-0,说明此容器是peer0.org1.example.com节点启动的,且正在运行的papercontract链码版本为0。

目前为止,PaperNet网络已经启动好了,papercontract链码也已经安装和实例化了,接下来转换到MagnetoCorp Application进行票据发行。

Application structure

papercontract中包含的智能合约由MagnetoCorp的application——issue.js调用。Isabella 使用issue.js提交交易(发行商业票据00001)到账本中 。下图显示了issue应用的工作流程:

hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)_第5张图片 

因为交易是Isabella用issue应用提交的,因此首先需要从Isabella的钱包中获取她的 X.509证书(certificate )(可能存储在本地文件系统或Hardware Security Module中)。获取证书后,issue应用就能够通过gateway提交交易到通道中。Hyperledger Fabric SDK提供了一个网关抽象,这样应用程序就可以专注于应用程序逻辑的设计(而将网络交互委托给网关)。网关和钱包使得编写hyperledger fabric应用程序变得非常简单。

接下来新开一个终端给Isabella,进入/fabric-samples/commercial-paper/organization/magnetocorp/application目录:

[root@slave2 application]# pwd
/root/go/src/github.com/hyperledger/fabric/scripts/fabric-samples/commercial-paper/organization/magnetocorp/application
[root@slave2 application]# ll
总用量 12
-rw-r--r-- 1 root root 1476 2月  18 12:58 addToWallet.js
-rw-r--r-- 1 root root 2974 2月  18 12:58 issue.js
-rw-r--r-- 1 root root  397 2月  18 12:58 package.json

其中的addToWallet.js程序的作用是将Isabella的身份(identity )保存到钱包中。issue.js程序将会代表MagnetoCorp通过调用papercontract链码创建商业票据00001.

在Isabella的终端运行addToWallet.js程序:

[root@slave2 application]# node addToWallet.js
done

注意:运行addToWallet.js的时候可能会出现以下错误

hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)_第6张图片

 注意:虽然我们的例子中Isabella在钱包中只有一个身份([email protected]),但实际上可以有多个身份。

addtowallet.js是一个简单的文件复制程序(将身份信息从basic network移动到Isabella的钱包中)。现在我们主要看一个钱包结构:

[root@slave2 application]# ll ../identity/user/isabella/wallet/
总用量 0
drwxr-xr-x 2 root root 189 2月  20 02:36 [email protected]
[root@slave2 application]# ll ../identity/user/isabella/wallet/User1\@org1.example.com/
总用量 12
-rw-r--r-- 1 root root  246 2月  20 02:36 c75bd6911aca808941c3557ee7c97e90f3952e379497dc55eb903f31b50abc83-priv
-rw-r--r-- 1 root root  182 2月  20 02:36 c75bd6911aca808941c3557ee7c97e90f3952e379497dc55eb903f31b50abc83-pub
-rw-r--r-- 1 root root 1037 2月  20 02:36 [email protected]

可以看到Isabella的钱包中只有一个身份文件夹[email protected],里面包含三个文件*-priv和*-pub以及[email protected]

Learn more about certificates here. In practice, the certificate file also contains some Fabric-specific metadata such as Isabella’s organization and role – read more in the wallet topic.

发行申请 

  • -priv:Isabella的私钥,用于对交易进行签名,不向外分发,只由Isabella控制
  • -pub:Isabella的公钥,与其私钥相关联,完全包含在Isabella的X.509证书中
  • [email protected]:证书文件,包含Isabella的公钥和证书颁发机构在创建证书时添加的其他X.509属性。这个证书被分发到网络,这样不同的参与者在不同的时间可以用密码验证Isabella的私钥创建的信息。

Isabella现在可以使用issue.js来提交一个交易(发行MagnetoCorp 公司的商业票据00001):

[root@slave2 application]# node issue.js 
Connect to Fabric gateway.
Use network channel: mychannel.
Use org.papernet.commercialpaper smart contract.
Submit commercial paper issue transaction.
2019-02-19T19:11:25.892Z - info: [TransactionEventHandler]: _strategySuccess: strategy success for transaction "8d8f70bb43a2bcc8068e9f86afbb3f0a70eee1ed8d1cf2b11a2193bae0bf2d8e"
Process issue transaction response.
MagnetoCorp commercial paper : 00001 successfully issued for value 5000000
Transaction complete.
Disconnect from Fabric gateway.
Issue program complete.

 node命令初始化了一个node,js环境,然后运行issue.js程序。为了实现上述功能,应用程序调用了papercontract.js链码中的CommercialPaper智能合约中定义的issue交易。papercontract.js链码在之前已经被MagnetoCorp 管理员安装并实例化到了网络中了。

接下来我们将身份转换到DigiBank上,他将购买刚刚发行的商业票据。

Working as DigiBank

 hyperledger fabric1.4——Commercial paper tutorial(官方文档学习记录)_第7张图片

为DigiBank管理员新开一个终端,并运行下面的命令来启动一个cliDigiBank容器:

[root@slave2 cli]# docker-compose -f docker-compose.yml up -d cliDigiBank
WARNING: Found orphan containers (cliMagnetoCorp) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
Creating cliDigiBank ... done

现在Digibank 管理员就可以通过这个容器(cliDigiBank)跟此网络进行交互了。但是本教程中Digibank管理员没有多少可做的事情,因为PaperNet 网络配置太简单了。现在我们将关注点转移到Balaji身上。

Digibank applications

Balaji 使用DigiBank的buy程序来提交交易(将商业票据00001的所有权从MagnetoCorp 转移到DigiBank)到账本中。现在,为Balaji 新打开一个终端,查看../digibank/application/buy.js文件:

/*
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 { FileSystemWallet, Gateway } = require('fabric-network');
const CommercialPaper = require('../contract/lib/paper.js');

// A wallet stores a collection of identities for use
const wallet = new FileSystemWallet('../identity/user/balaji/wallet');

// Main program function
async function main() {

  // 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 = '[email protected]';
    const userName = '[email protected]';

    // Load connection profile; will be used to locate a gateway
    let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/networkConnection.yaml', 'utf8'));

    // Set connection options; identity and wallet
    let connectionOptions = {
      identity: userName,
      wallet: wallet,
      discovery: { enabled:false, 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);

});

DigiBank’s buy.js application is very similar in structure to MagnetoCorp’s issue.js with two important differences:

  • Identity: the user is a DigiBank user Balaji rather than MagnetoCorp’s Isabella

    const wallet = new FileSystemWallet('../identity/user/balaji/wallet');`
    

    See how the application uses the balaji wallet when it connects to the PaperNet network channel. buy.js selects a particular identity within balaji wallet.

  • Transaction: the invoked transaction is buy rather than issue

    `const buyResponse = await contract.submitTransaction('buy', 'MagnetoCorp', '00001'...);`
    

    buy transaction is submitted with the values MagnetoCorp00001…, that are used by the CommercialPaper smart contract class to transfer ownership of commercial paper 00001 to DigiBank.

Run as DigiBank

跟MagnetoCorp一样,Digibank 必须先用npm install命令安装应用程序require的package :

npm install

下载完依赖后,运行addToWallet程序,将Balaji的身份添加到他的钱包中:

[root@slave2 application]# node addToWallet.js 
done

Buy application

Balaji现在可以使用buy.js程序来提交交换商业票据00001所有权的交易:

[root@slave2 application]# node buy.js 
Connect to Fabric gateway.
Use network channel: mychannel.
Use org.papernet.commercialpaper smart contract.
Submit commercial paper buy transaction.
2019-02-19T20:16:42.402Z - info: [TransactionEventHandler]: _strategySuccess: strategy success for transaction "57df04553d3fdf8226e8aa4aaba03b2b072bc34ac45ca0e5afea98acfd7fd927"
Process buy transaction response.
MagnetoCorp commercial paper : 00001 successfully purchased by DigiBank
Transaction complete.
Disconnect from Fabric gateway.
Buy program complete.

 buy.js invoked the buy transaction defined in the CommercialPaper smart contract which updated commercial paper 00001 within the world state using the putState() and getState() Fabric APIs. 

Redeem application

商业票据00001生命周期的最后一个交易是redeem交易(DigiBank redeem it with MagnetoCorp)。Balaji 通过运行redeem.js程序来实现这一过程:

[root@slave2 application]# node redeem.js 
Connect to Fabric gateway.
Use network channel: mychannel.
Use org.papernet.commercialpaper smart contract.
Submit commercial paper redeem transaction.
2019-02-19T20:19:42.771Z - info: [TransactionEventHandler]: _strategySuccess: strategy success for transaction "7191a9a4656c0706a8bca804a43eaf2d83ab516440e5c73322e5ce368de03f3f"
Process redeem transaction response.
MagnetoCorp commercial paper : 00001 successfully redeemed with MagnetoCorp
Transaction complete.
Disconnect from Fabric gateway.
Redeem program complete.

至此,fabric1.4的Commercial paper tutorial运行结束!

你可能感兴趣的:(区块链,hyperleger,fabric)