创建你的第一个Hyperledger Fabric网络

1. 启动第一个网络

首先要下载fabric的示例代码

$ git clone https://github.com/hyperledger/fabric-samples.git

然后找到目录first-network,这里我们会用到的一个脚本就是byfn.sh

  1. 首先执行如下命令,
$ ./byfn.sh -m generate

这个命令执行如下的操作

  • 生成密码和证书,用于身份认证;
  • 创建orderer的创世纪区块,genesis.block;
  • 创建通道配置交易所需的工件,channel.tx;
  • 创建锚节点更新工件,Org1MSPanchor.tx/Org2MSPanchor.tx;
  1. 然后执行命令,
$ ./byfn.sh -m up

这个命令会启动所有所需的容器,

  • 一个ordere容器;
  • 四个peer节点容器;
  • 一个CLI容器;
    之后再CLI容器中自动运行脚本,执行如下操作
  • 创建通道;
  • 节点加入到通道;
  • 更新锚节点;
  • 安装和实例化chaincode;
  • 进行查询和更新操作;

2. 密码生成器

使用cryptogen工具来生成用于多个网络实体的密码材料,这些证书是识别身份的代表。
通过这些证书可以运行不同的网络实体之间通信过程中身份认证的登陆/验证

2.1 这是如何工作的?

cyptogen工具需要有个配置文件:crypto-config.yaml,这个文件包含网络拓扑,基于这个配置文件我们可以生成一个集合的证书和密钥;这些证书和密钥既可以用于这些组织,也可以用于属于这些组织的部件。

  • 每个组织都准备了一个独有的根证书(ca-cert),这个根证书把一些特殊的组件(peers和orderers节点)绑定到了这个组织;通过给每个组织分配一个特有的CA证书,我们就模拟出了一个典型的网络,在这个网络里所有的参与者都要使用他自己独有的证书鉴权。
  • Hyperledger Fabric里面所有的交易和通信都被一个实体(entity)的私钥(keystore)签名,然后通过公钥(signcerts)来进行验证;
  • 你会发现这个文件里面有一个计数器变量。我们用这个来指定每个组织里面的peers节点的个数;这个例子里面每个组织(Orgnization)都有两个peers节点。
  • 我们这里不会去钻研x.509证书和公钥这些基础设施的细节。
    首先来简单看一下这个yaml文件,尤其注意“Name”,“Domain”和“Specs这些参数”
OrdererOrgs:
#---------------------------------------------------------
# Orderer
# --------------------------------------------------------
- Name: Orderer
  Domain: example.com
  CA:
    Country: US
    Province: California
    Locality: San Francisco
  # OrganizationalUnit: Hyperledger Fabric
  # StreetAddress: address for org # default nil
  # PostalCode: postalCode for org # default nil
  # ------------------------------------------------------
  # "Specs" - See PeerOrgs below for complete description
  # -----------------------------------------------------
  Specs:
    - Hostname: orderer
# -------------------------------------------------------
# "PeerOrgs" - Definition of organizations managing peer nodes
# ------------------------------------------------------
PeerOrgs:
# -----------------------------------------------------
# Org1
# ----------------------------------------------------
- Name: Org1
  Domain: org1.example.com

网络实体(entity)的命名规则如下 - "{{.Hostname}}.{{.Domain}}";这里拿ordering节点为例,orderer.example.com这个名字就和Orderer的一个MSP ID关联上了。这个文件还包含了一个扩展文件,描述定义和符号规则,可以参考成员服务提供者(MSP: Membership Service Provider)文档来进一步理解MSP
运行完cyptogen工具后,生成的证书和密钥会被保存到crypto-config文件夹中。
本文例子中的crypto-config文件夹的目录结构如下

.
├── ordererOrganizations
│   └── example.com
│       ├── ca
│       ├── msp
│       ├── orderers
│       ├── tlsca
│       └── users
└── peerOrganizations
    ├── org1.example.com
    │   ├── ca
    │   ├── msp
    │   ├── peers
    │   ├── tlsca
    │   └── users
    └── org2.example.com
        ├── ca
 ...
  • 可见,生成了两个组织:orderer以及peer
  • ca目录下保存了根证书,有两个文件,一个是证书文件,一个密钥文件
    我们打开证书文件ca.example.com-cert.pem来看看到底生成了什么
-----BEGIN CERTIFICATE-----
MIICLjCCAdWgAwIBAgIQT+87OiMiSvnXF74e2QOp2jAKBggqhkjOPQQDAjBpMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1w
bGUuY29tMB4XDTE4MDMyMjEyMTY1NFoXDTI4MDMxOTEyMTY1NFowaTELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz
Y28xFDASBgNVBAoTC2V4YW1wbGUuY29tMRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFwgglk7r8kqdR69C+SA0A9EBLhL
QALXpsAnqojj9btYfTCRdXbGu1SmkE88o8Qel8q42vjN8XTQO6dmco3E3XijXzBd
MA4GA1UdDwEB/wQEAwIBpjAPBgNVHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMB
Af8wKQYDVR0OBCIEIIfdbeaMNg+kkU9Kp89R7jUulqNYoaRtSLkHM/s31MyuMAoG
CCqGSM49BAMCA0cAMEQCIGHPp0S8x/nOcrNHu6SP0muhX884sxAbZnFi+GMzLZes
AiBm/LZOkRZVgw6ud3SUIjnSqUJ4dr9tcw5IOzr4MPdRMQ==
-----END CERTIFICATE-----

再打开密钥文件看看

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8DtexDbtkQMUiZ7W
v/IVUvwSfUa2rtYBRymyXyK1X8ihRANCAARcIIJZO6/JKnUevQvkgNAPRAS4S0AC
16bAJ6qI4/W7WH0wkXV2xrtUppBPPKPEHpfKuNr4zfF00DunZnKNxN14
-----END PRIVATE KEY-----

其实看起来就是一串杂乱无章的字符串。

3. 配置交易生成器

configtxgen 工具(Channel configuration)是用来创建四个配置构建

  • orderer的创世纪块(genesis block)
  • channel的配置交易(configuration transaction)
  • 以及两个锚节点交易(anchor peer transaction) - 一个Peer组织一个
    orderer区块是ordering服务的创世纪区块,并且在channel创建的时候会把channel交易文件广播到orderer。
    顾名思义,每个锚节点交易(anchor peer transaction)就指定了每个组织在这个channel上的的锚节点(anchor peer)

3.1. 配置交易生成器如何工作

Configtxgen会读取配置文件configtx.yaml,这个配置文件包含了示例网络的的定义。这个网络里面一共有三种成员:

  • 一个Orderer组织(OrdererOrg);
  • 两个Peer组织(Org1和Org2),每个组织管理和维护两个peer节点;
    参看下面这段配置
...
################################################################################
#
#   Section: Organizations
#
#   - This section defines the different organizational identities which will
#   be referenced later in the configuration.
#
################################################################################
Organizations:

    # SampleOrg defines an MSP using the sampleconfig.  It should never be used
    # in production but may be used as a template for other definitions
    - &OrdererOrg
        # DefaultOrg defines the organization which is used in the sampleconfig
        # of the fabric.git development environment
        Name: OrdererOrg

        # ID to load the MSP definition as
        ID: OrdererMSP

        # MSPDir is the filesystem path which contains the MSP configuration
        MSPDir: crypto-config/ordererOrganizations/example.com/msp

    - &Org1
        # DefaultOrg defines the organization which is used in the sampleconfig
        # of the fabric.git development environment
        Name: Org1MSP

        # ID to load the MSP definition as
        ID: Org1MSP

        MSPDir: crypto-config/peerOrganizations/org1.example.com/msp

        AnchorPeers:
            # AnchorPeers defines the location of peers which can be used
            # for cross org gossip communication.  Note, this value is only
            # encoded in the genesis block in the Application section context
            - Host: peer0.org1.example.com
              Port: 7051

    - &Org2
        # DefaultOrg defines the organization which is used in the sampleconfig
        # of the fabric.git development environment
        Name: Org2MSP

        # ID to load the MSP definition as
        ID: Org2MSP

        MSPDir: crypto-config/peerOrganizations/org2.example.com/msp

        AnchorPeers:
            # AnchorPeers defines the location of peers which can be used
            # for cross org gossip communication.  Note, this value is only
            # encoded in the genesis block in the Application section context

...

这个文件同时还定义了一个共同体(consortium) - SampleConsortium,这个共同体包含了我们的两个Peer组织;尤其注意这个文件最上面的"Profiles"部分。你会发现有两个特殊的头:

  • 一个用于orderer的创世纪区块 - TwoOrgsOrdererGeneisis;
  • 另一个用于我们的channel - TwoOrgsChannel
    参看如下这段配置
################################################################################
#
#   Profile
...
################################################################################
Profiles:

    TwoOrgsOrdererGenesis:
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
        Consortiums:
            SampleConsortium:
                Organizations:
                    - *Org1
                    - *Org2
    TwoOrgsChannel:
        Consortium: SampleConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1
                - *Org2
....

这些头和重用,当我们创建工件(artifacts)的时候会把他们作为参数传递进去;
NOTE: 注意我们的SampleConsortium定义在系统级别的配置文件里,但是被我们的channel级别的配置文件引用。channels只存在于一个共同体(consortium)的职权范围内,在网络范围内必须定义所有的联盟(consortia)

这个文件还包含了两个额外的没有太多价值的定义

  • 第一个是定义了每个Peer Org的锚节点(peer0.org1.example.com & peer0.org2.example.com );
  • 第二个,我们指向了每个成员的MSP的文件夹路径,这样我们就可以把每个org的根证书保存到orderer的创世区块中了;这是一个关键原理,现在任何一个和ordering服务进行通信网络实体就都可以验证自己的数字签名了。

4. 运行工具

4.1. 手动生成工件(artifact)

你可以在命令行里手动运行这两个程序来生成证书和密钥,以及那些配置工件;或者你也可以试着修改一下byfn.sh脚本来实现你要的目的。
我们这里简单介绍一下怎么使用,
首先运行一下cryptogen 工具,

../bin/cryptogen generate --config=./crypto-config.yaml

你有可能会看到如下告警,可以选择忽略这些告警

[bccsp] GetDefault -> WARN 001 Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP.

接着,我们需要告诉工具configtxgen怎么找到它需要的configx.yaml配置文件,
第一步,设置环境变量,指定配置文件的搜索路径,比如说设置成了当前目录

export FABRIC_CFG_PATH=$PWD

第二步,运行configtxgen工具,创建orderer创世纪区块

../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

你可以忽略日志中关于中间证书,证书撤销列表(crls)和MSP配置的那些告警,在这个示例网络里面我们不会使用这些功能。

4.2. 创建通道配置交易

现在,我们需要创建一个通道交易工件。在这之前确保环境变量$CHANNEL_NAME已经设置好了

export CHANNEL_NAME=mychannel
# this file contains the definitions for our sample channel
../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

接下来,我们会在我们构造的通道上定义Org1的锚节点。同样的我们要保证环境变量$CHANNEL_NAME已经设置好了并且可以被如下的命令使用,

../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP

现在,我就可以在同一个通道上间里Org2的锚节点

../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

5. 启动网络

我们会使用一个docker-compose脚本来把我们的网络带起来,这个docker-compose文件使用了我们之前下载的那些docker镜像文件,并且使用我们之前创建的genesis.block来引导启动了orderer

working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
# command: /bin/bash -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT'
volumes

如果第二行不注释掉,这个脚本会在网络启动后执行所有的CLI命令,但是我们希望手动的一步一步运行一下这些命令来了解一下这完整的语法和每次调用的功能
传进去一个适当的大一点的TIMEOUT变量值(单位是秒);不然的话,CLI容器默认会在60秒后退出。
启动你的网络:

CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=<pick_a_value> docker-compose -f docker-compose-cli.yaml up -d

如果你希望看到你网络的实时log,那么就不要添加-d标志

5.1. 环境变量

为了保证后面针对peer0.org1.example.com的CLI命令可以正常工作,我们首先需要设置如下的四个环境变量。这些用于peer0.org1.example.com的环境变量都已经打包进了CLI容器中了,就不需要额外的去传递这些环境变量了;但是如果你想要往其他的节点或者是orderer发送调用,你就需要相应的提供这些参数了。检查一下docker-compose-base.yaml文件,

# Environment variables for PEER0
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

5.2. 创建并加入通道

回想一下之前我们创建了通道配置交易,你可以重复这个步骤来创建一个额外的通道配置交易
使用configtx.yaml中相同的或者不同的配置文件并传递给configtxgen工具,这样你就可以在你的网络中创建其他的通道。
首先进入CLI container

docker exec -it cli bash

如果成功了,你会看到如下信息

root@0d78bb69300d:/opt/gopath/src/github.com/hyperledger/fabric/peer#

接下来,我们要传递生成的通道配置交易工件(channel.tx)给orderer作为创建通道请求的一部分。
我们通过-c标签指定了我们的通道名字,使用-f标签指定我们的通道配置交易,在这个例子里面是channel.tx。当然你也可以挂载你自己的配置交易

export CHANNEL_NAME=mychannel
# the channel.tx file is mounted in the channel-artifacts directory within your CLI container
# as a result, we pass the full path for the file
# we also pass the path for the orderer ca-cert in order to verify the TLS handshake
# be sure to replace the $CHANNEL_NAME variable appropriately
peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME 
    -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED 
    --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

Note:注意命令行参数里面--cafile这个选项,这是orderer的根证书的文件路径,允许我们来验证TLS握手。
这个命令返回了一个创世纪区块-<channel-ID.block>,我们会使用这个区块来加入通道中。
Note:手动运行完这些命令以后你还会留在容器中,记住,当你要针对除了peer0.org1.example.com以外的其他节点创建通道,你要预先把所有的环境变量设置成相应的值
现在我们开始把节点peer0.org1.example.com加入到通道中

# By default, this joins ``peer0.org1.example.com`` only
# the <channel-ID.block> was returned by the previous command
peer channel join -b <channel-ID.block>

5.3. 安装和实例化(Instantiate)Chaincode

Note: 我们这里利用一个简单的chaincode来学习怎么样编写自己的chaincode
应用程序通过chaincode来作用于区块链账本。我们需要在每个可能执行我们交易的节点上面安装chaincode,然后在通道上实例化这些chaincode
首先在四个peer节点上的一个安装示例程序,下面的命令把我们的源代码部署到四个节点的文件系统中

peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02

Next, instantiate the chaincode on the channel. This will initialize the chaincode on the channel, set the endorsement
接下来,在通道上实例化chaincode,这一步骤会在通道上初始化chaincode;设置chaincode的背书(endorsement)策略;为目标节点启动chaincode容器。注意一下-P选项,这是我们的策略,定义当前这个chaincode在验证一个交易过程中的背书的级别(?)
在如下的命令中,你注意到我们是这么来指定我们的策略的:-P "OR('Org0MSP.member', 'Org1MSP.member')"。这就意味着我们需要属于Org1或者Org2的节点的背书,如果我们把语法改为AND,这样我们就需要两个组织节点的背书

# 确保你替换了 $CHANNEL_NAME 环境变量值
# 如果你没有把你安装的chaincode的名字定义为mycc,那么就修改成你定义的那个
peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
  -C $CHANNEL_NAME -n mycc -v 1.0 
  -c '{"Args":["init","a", "100", "b","200"]}' 
  -P "OR ('Org1MSP.member','Org2MSP.member')"

5.4. 查询

接下来我们查询一下a的值,来确保chaincode正常实例化了,并且state DB也部署了。查询的语法如下,

#  确保-C 和 -n 标志都设置准确了
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

5.5. 调用

Now let’s move 10 from a to b . This transaction will cut a new block and update the state DB. The syntax for invoke
现在我们从a转移10个到b,这个交易会切割一个新的区块,并且更新state DB。调用的语法如下,

# be sure to set the -C and -n flags appropriately
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED 
    --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
    -C $CHANNEL_NAME -n mycc 
    -c '{"Args":["invoke","a","b","10"]}'

然后再查询一下,我们要确认之前的调用正常执行了。

# be sure to set the -C and -n flags appropriately
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

正常来说我们会得到如下的查询结果:

Query Result: 90

5.6. 这些现象背后发生了些什么?

NOTE: 上述的那些步骤是在script.sh脚本中运行的,cli容器启动以后会自动调用这个脚本运行;在你自己一步步执行的时候,你需要在docker-compose-cli.yaml文件中把这一行给注销掉。

  1. 一个脚本 - script.sh
  • 这个脚本被安装到CLI容器内了。这个脚本驱动了依据提供的channel名称来运行createChannel的命令,同时使用channel.tx文件来进行通道配置;

  • createChannel的输出是一个genesis区块 - <your_channel_name>.block - 这个区块会保存到peers节点的文件系统中并且包含一个channel.tx指定的通道配置;

  • 对4个peer执行joinChannel,之前一步生成的genesis区块做为输入。这个命令指示这些节点加入通道<your_channel_name>中,并且创建一个由<your_channel_name>.block起头的区块链;

现在我们就创建了一个通道,包含4个节点,以及两个组织。这就是我们的TwoOrgsChannelprofile

  • 节点peer0.org1.example.com 和peer1.org1.example.com 属于Org1;

  • 节点peer0.org2.example.com 和peer1.org2.example.com 属于Org2;

  • 这些关系通过crypto-config.yaml配置文件来定义,MSP的路径定义在docker compose文件中;

  • 这个时候会更新Org1MSP(peer0.org1.example.com )和Org2MSP(peer0.org2.example.com ) 的锚节点。我们是通过传递Org1MSPanchors.tx和Org2MSPanchors.tx工件传递给ordering服务,同时还带上了我们通道的名字来实现更新的;

  1. 一个chaincode - chaincode_example02安装到了节点peer0.org1.example.com和peer0.org2.example.com中了
  • 这个chaincode这个时候就在peer0.org2.example.com上实例化了(instantiated)。实例化会把chaincode添加到channel上,为每个目标节点启动容器,并且初始化和chaincode相关联的密钥对;
    这个例子里面初始化值是 [”a”,”100” “b”,”200”]。这个实例化的结局是启动了一个名叫dev-peer0.org2.example.com-mycc-1.0的容器;
  • 这个实例化还会为背书策略传递一个参数进去。这个策略定义为-P "OR
    ('Org1MSP.member','Org2MSP.member')",意味着任何交易都必须被一个绑定到Org1或者Org2的peer节点准许(背书,endose)
  • 往节点peer0.org1.example.com发起一次针对变量"a"的查询。之前有chaincode安装到了节点peer0.org1.example.com,所以这个查询会为Org1 peer0启动一个名叫dev-peer0.org1.example.com-mycc-1.0的容器。同时也会返回查询结果
  • 发送一个调用给peer0.org1.example.com来从“a”转移10到"b"上;
  • 这个时候chaincode被安装到了peer1.org2.example.com上;
  • 往节点peer1.org2.example.com发起一次针对变量"a"的查询。这个操作会启动第三个chaincode容器,名字是dev-peer1.org2.example.com-mycc-1.0。 返回值是90,正确反映了之前的交易。

5.7. 这个证明了什么?

Chaincode必须安装到一个节点上,以保证可以成功实现对账本的读写。
进一步的,一个chaincode容器不会为这个节点启动,直到发生了初始化,或者针对这个chaincode执行了传统的交易 - 读写(比如说,查询"a"的值),这个交易会导致容器的启动。
当然,通道上的所有peers节点维护一个额外的账本拷贝,这个账本拷贝由区块链组成;区块链上保存了不可变更的,有序的记录,以及一个状态数据库来维护当前状态的snapshot。
这也包含那些上面没有安装chaincode的节点(比如说上述例子中的peer1.org1.example.com)。最终,chaincode在安装完成后就可以对他进行访问了(比如说上述例子中的peer1.org2.example.com ),这是因为它已经实例化了。

5.8. 怎么看到这些交易?

可以通过查看CLI容器的logs

docker logs -f cli

你会看到如下的输出

2017-05-16 17:08:01.366 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSP
2017-05-16 17:08:01.366 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining
˓→default signing identity
2017-05-16 17:08:01.366 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext:
˓→0AB1070A6708031A0C08F1E3ECC80510...6D7963631A0A0A0571756572790A0161
2017-05-16 17:08:01.367 UTC [msp/identity] Sign -> DEBU 007 Sign: digest:
˓→E61DB37F4E8B0D32C9FE10E3936BA9B8CD278FAA1F3320B08712164248285C54
Query Result: 90
2017-05-16 17:08:15.158 UTC [main] main -> INFO 008 Exiting.....
===================== Query on PEER3 on channel 'mychannel' is successful
˓→=====================
===================== All GOOD, BYFN execution completed =====================
_____ _ _ ____
| ____| | \ | | | _ \
| _| | \| | | | | |
| |___ | |\ | | |_| |
|_____| |_| \_| |____/

5.9. 怎么查看chaincode的日志

查看每个单独的chaincode容器来看看针对每个容器分别执行的交易,这里是每个容器的输出的集合

$ docker logs dev-peer0.org2.example.com-mycc-1.0
04:30:45.947 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
ex02 Init
Aval = 100, Bval = 200

$ docker logs dev-peer0.org1.example.com-mycc-1.0
04:31:10.569 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
ex02 Invoke
Query Response:{"Name":"a","Amount":"100"}
ex02 Invoke
Aval = 90, Bval = 210

$ docker logs dev-peer1.org2.example.com-mycc-1.0
04:31:30.420 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

6. 理解Docker Compose拓扑

BYFN这个例子给我提供了两个Docker compose文件的风味,这两个例子都是从docker-compose-base.yaml文件扩展而来。我们的第一种风味,docker-compose-cli.yaml,提供给我们CLI容器,以及一个orderer,四个peers。
NOTE: 这一部分的描述涉及SDK中docker-compose文件的设计,参考Node SDK部分的描述来理解这些步骤的细节

第二道风味,docker-compose-e2e.yaml,可以用于通过Node.js SDK运行一个端到端的测试

抛开SDK的功能不说,它的主要区别就是,有一个用于fabric-ca服务的容器

结果就是我们可以通过发送一个REST调用给CAs组织来进行用户注册和登记。
如果你想不通过运行byfn.sh脚本来使用docker-compose-e2e.yaml文件,那你可能需要做一些小的改动,
我们需要在yaml文件里面指定我们组织的CA's(证书)的密钥,这些密钥保存在crypto-config文件夹中。
比如说,Org1的私钥就保存在这样的目录中 - crypto-config/peerOrganizations/org1.example.com/ca/。私钥是一个以_sk为后缀的很长的哈希值。Org2的路径是 - crypto-config/peerOrganizations/org2.example.com/ca/;
在docker-compose-e2e.yaml文件中,需要为ca0和ca1更新一下FABRIC_CA_SERVER_TLS_KEYFILE变量的值。在启动ca服务的时候你还需要修改命令行参数中的路径,你要为每个CA容器提供两次私钥。

7 使用CouchDB

状态数据库可以从默认的goleveldb切换成CouchDB。同样的chaincode也可以和CoubhDB一起工作,当然,用了CouchDB之后,我们就可能执行更加丰富和复杂的查询DB操作了;这些查询可以通过JSON的格式来进行
使用CouchDB需要在之前描述的生成工件的步骤基础上做一些修改,在启动网络的时候把yaml文件替换成docker-compose-couch.yaml

CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=<pick_a_value> docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml up -d

下面chaincode_example02就可以和CouchDB一起工作了。
NOTE:如果你这里选择把fabric-couchdb容器的端口映射到主机端口上,请确保你意识到了这里可能有安全隐患。在开发环境中做这样的映射使得CouchDB的REST接口可用了,但是同时CouchDB的数据口内容也通过web接口Fauxton变得可视了。生产环境中要注意限制外部环境的CouchDB容器的访问。
如果你想要使用CouchDB的查询能力,这个时候就需要使用数据建模成JSON格式的chaincode了,比如说marbles02.
首先还是创建通道,并把节点加进通道中去。

  • 在节点peer0.org1.example.com上安装和实例化chaincode
# be sure to modify the $CHANNEL_NAME variable accordingly for the instantiate command
peer chaincode install -n marbles -v 1.0 
  -p github.com/hyperledger/fabric/examples/chaincode/go/marbles02

peer chaincode instantiate 
  -o orderer.example.com:7050 
  --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME 
  -n marbles -v 1.0 -c '{"Args":["init"]}' -P "OR ('Org0MSP.member','Org1MSP.member')"
  • 制造一些marbles,然后使用
# be sure to modify the $CHANNEL_NAME variable accordingly
peer chaincode invoke -o orderer.example.com:7050 
  --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
  -C $CHANNEL_NAME -n marbles 
  -c '{"Args":["initMarble","marble1","blue","35","tom"]}'

peer chaincode invoke -o orderer.example.com:7050 
  --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
  -C $CHANNEL_NAME -n marbles 
  -c '{"Args":["initMarble","marble2","red","50","tom"]}'

peer chaincode invoke -o orderer.example.com:7050 
  --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
  -C $CHANNEL_NAME -n marbles 
  -c '{"Args":["initMarble","marble3","blue","70","tom"]}'

peer chaincode invoke -o orderer.example.com:7050 
  --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
  -C $CHANNEL_NAME -n marbles 
  -c '{"Args":["transferMarble","marble2","jerry"]}'

peer chaincode invoke -o orderer.example.com:7050 
  --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
  -C $CHANNEL_NAME -n marbles 
  -c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'

peer chaincode invoke -o orderer.example.com:7050 
  --tls $CORE_PEER_TLS_ENABLED 
  --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 
  -C $CHANNEL_NAME -n marbles 
  -c '{"Args":["delete","marble1"]}'

• If you chose to map the CouchDB ports in docker-compose, you can now view the state database through the

  • 如果你选择在docker-compose中映射CouchDB端口,你现在就可以通过web接口查看CouchDB的数据库内容了。用浏览器打开http://localhost:5984/_utils
    你可以看到一个名叫mychannel的数据库,里面有一些文件,
    Note: For the below commands, be sure to update the $CHANNEL_NAME variable appropriately.
    You can run regular queries from the CLI (e.g. reading marble2 ):
    NOTE: 对于下面的这些命令,请确保你以及相应的更新了$CHANNEL_NAME变量。
    你可以通过CLI来进行一些常规查询,比如说读取marble2:
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["readMarble","marble2"]}'

输出结果会显示marble2的一些细节,

Query Result: {"color":"red","docType":"marble","name":"marble2","owner":"jerry","size":50}

You can retrieve the history of a specific marble - e.g. marble1 :
你可以获取一个特定marble的历史 - 比如说marble1:

peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'

结果会显示marble1的历史交易记录,

Query Result: [
  { "TxId":"1c3d3caf124c89f91a4c0f353723ac736c58155325f02890adebaa15e16e6464", 
    "Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}},
  { "TxId":"755d55c281889eaeebf405586f9e25d71d36eb3d35420af833a20a2f53a3eefd", 
    "Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"jerry"}},
  { "TxId":"819451032d813dde6247f85e56a89262555e04f14788ee33e28b232eef36d98f", 
    "Value":}
]

You can also perform rich queries on the data content, such as querying marble fields by owner jerry :
你也可以对数据内容进行富查询,比如说查询jerry这个拥有者的marble,

peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["queryMarblesByOwner","jerry"]}'

输出显示jerry拥有的两个marbles,

Query Result: [
  { "Key":"marble2", "Record":{"color":"red","docType":"marble","name":"marble2","owner":"jerry","size":50}}, 
  { "Key":"marble3", "Record":{"color":"blue","docType":"marble","name":"marble3","owner":"jerry","size":70}}
]

7.1. 为什么用CouchDB

CouchDB采用的是NoSQL的解决方案,是一种面向文档的数据库,文档的内容都以键值对的方式保存在数据库中;可以实简单的键值对,列表,或者映射表。
除了LevelDB支持的这些键值查询之外,CouchDB还支持完整数据的富查询能力,比如说针对整个区块链数据的非键值查询,这些数据内容以JSON格式存储。
CouchDB还可以增强区块链上数据保护的安全性。

推荐阅读更多精彩内容