本文演示Hyperledger Fabric的网络创建过程。本地环境采用Ubuntu14.04(由Vagrant创建 ),Hyperledger Fabric采用1.1.0版本。
本示例包括2个部分:
1、创建第一个Fabric网络
2、创建过程分解
3、创建过程解析
1、创建第一个Fabric网络
当前述的各个步骤都准备无误之后,就可以演示创建你的第一个Fabric网络了。
从0开始创建Fabric网络,是一个非常麻烦的事情。好在官方已经提供了创建的脚本,可以让我们快速搭建这个网络环境。官方提供的脚本名称叫byfn.sh( Build your first network的首字母缩写), 在fabric-sample/firsh-network 目录中,直接执行./byfn.sh 可以查看命令的执行提示。
(1)、切换工作目录
运行下面命令,切换工作目录:
cd fabric-samples/first-network |
这一步骤经常会被忘记,而造成后续执行错误。因为脚本中命令是使用的相对路径。
(2)、生成网络神器
在创建之前,我们使用byfn.sh脚本生成网络必须的证书和初始区块(Genesis Block)。
./byfn.sh -m generate |
输入 y 以继续。
(3)、启动网络
接下来,就可以启动网络了。当再次提示时,使用 y 并继续。
# 这里使用 -l 指定区块链的语言为nodejs,如果不指定,则默认使用go语言。 ./byfn.sh -m up -l node |
查看命令的提示,当出现All GOOD,BYTN execution completed 的提示时,说明执行成功了。
Query Result: 90 2018-04-06 18:23:59.646 UTC [main] main -> INFO 003 Exiting..... ===================== Query on peer1.org2 on channel 'mychannel' is successful ===================== ========= All GOOD, BYFN execution completed =========== _____ _ _ ____ | ____| | \ | | | _ \ | _| | \| | | | | | | |___ | |\ | | |_| | |_____| |_| \_| |____/ |
(4)关闭网络
运行关闭网络后,将会删除步骤 (3)中所创建的所有证书和所有Docker容器。这样,我们可以中后续命令过程中再次执行验证。
./byfn.sh -m down |
同样,需要手动键入 y 以继续执行。
2、创建过程分解
在下面的过程中,我们将把1中的步骤拆解,一步一步地执行,并解释这样执行的原因。
[注:非常重要,下面步骤是理解Fabric的关键,一定要完全弄懂;
(1)、设置cli容器日志输出级别。
下面的步骤需要将cli容器的日志输出级别设置为DEBUG,这个日志级别是在docker-compose-cli.yaml文件中设置的,在这个文件中,默认注释掉了DEBUG级别,而使用了INFO级别:
cli: container_name: cli image: hyperledger/fabric-tools:$IMAGE_TAG tty: true stdin_open: true environment: - GOPATH=/opt/gopath - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock #- CORE_LOGGING_LEVEL=DEBUG - CORE_LOGGING_LEVEL=INFO |
运行下述脚本来将日记级别从 INFO修改为 DEBUG:(语句有问题)
sed -i "s/- CORE_LOGGING_LEVEL=INFO/#- CORE_LOGGING_LEVEL=INFO/g" docker-compose-cli.yaml sed -i "s/#- CORE_LOGGING_LEVEL=DEBUG/#- CORE_LOGGING_LEVEL=INFO/g" docker-compose-cli.yaml |
(2)、加密生成器
在运行加密生成器和配置交易生成器之前,先补充些基础知识。这里是加密生成器的知识介绍。
我们将使用 cryptogen 工具生成各个网络实体所需要的密码材料(主要是x509证书和签名),这些材料用来标识身份,并在实体间进行通讯和交易时进行签名/验证身份认证。
它是如何工作的?
cryptogen 工具使用 crypto-config.yaml 配置文件进行配置,这个配置文件中包含了网络结构,并允许我们为组织(Org)和从属于组织的各个组件生成一组证书和密钥。每一个组织都会分配一个唯一的根证书(ca-cert),并绑定特定的组件(peers和orderer)到这个组织上面。通过对每个组织分配一个唯一的CA证书,我们正在模仿一个经典的网络,这个网络中的成员将使用自己的证书颁发机构。Hyperledger Fabric中的交易和通信就是通过存储在keystore中的实体的私钥进行签名,然后通过公钥手段来进行验证(signcerts)的。
注意在这个文件里有一个count变量,它指定每个组织中peer的数量;在我们的例子中,一共有1个排序服务节点(OrdererOrg)和2个一般组织(PeerOrg),每个Peer组织有两个peer。
在运行该工具之前,让我们快速浏览一下这段代码crypto-config.yaml。特别注意在OrdererOrgs头下的Name,Domain和Specs参数:
OrdererOrgs: #--------------------------------------------------------- # Orderer # -------------------------------------------------------- - Name: Orderer Domain: example.com # ------------------------------------------------------ # "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 |
网络实体的命名约定如下:”{{.Hostname}}.{{.Domain}}”。所以使用我们的排序节点作为参考点,它与Order的MSP ID相关联。该文件包含了有关定义和语法的大量文档。你还可以参考Membership Service Providers(MSP),以便更深入地了解MSP。
[注意PeerOrg中Domain和OrdererOrg中Domain的对应关系。]
当运行cryptogen工具时,生成的证书和密钥将被保存到名为crypto-config的文件夹中。
(3)、配置交易生成器
在运行加密生成器和配置交易生成器之前,先补充些基础知识。这里是配置交易生成器的知识介绍。
configtxgen tool 用来生成4个配置项:
orderer的创世区块(genesis block)
chanel的channel configuration transaction
2个anchor peer transactions ——一个Peer组织一个
有关此工具的完整说明,请参阅Channel Configuration(configtxgen)。
order block是一个ordering service的创世区块,channel transaction文件在Channel创建的时侯广播给order。anchor peer transactions,正如名称所示,指定了每个组织在此channel上的Anchor peer。
它是如何工作的?
Configtxgen使用一个包含示例网络的configtx.yaml文件。有3个成员-一个排序服务组织OrdererOrg以及两个节点组织(Org1&Org2),每个组织管理和持有2个peer节点。该文件还指定了一个SampleConsortium的联盟,由上述2个节点组织构成。 请特别注意此文件顶部的”Profiles”部分。你会注意到我们有两个独特的标题。一个是orderer的创世区块-TwoOrgsOrdererGenesis-另一个是针对管道的TwoOrgsChannel。
这些标题很重要,因为在我们创建我们的工作的时侯她们将作为传递的参数。
注意
请注意SampleConsortium在系统界别的配置文件中定义,然后由管道级别配置文件引用。管道存在于联盟的范围内,所有的联盟必须定义在整个网络范围内。
此文件还包含两个值得注意的附加规格。首先,我们为每个组织指定了锚点节点(peer0.org1.example.com和peer0.org2.example.com)。其次,我们为每个成员指定MSP文件夹,用来存储每个组织在orderer genesis block中指定的根证书。这是一个关键的概念。现在任意和ordering service通信的网络实体都可以对其数字签名进行验证。
(4)、运行工具
在通过(2)、(3)的解释之后,我们来运行这些工具。
可以用configtxgen和cryptogen命令来手动生成证书/密钥和各种配置文件。当然,也可以尝试使用byfn.sh脚本来完成。
手动生成配置文件
如果使用byfn.sh脚本,可以使用其中的generateCerts 函数生成定义在crypto-config.yaml文中的网络配置证书。我们这里来演示直接调用cryptogen工具完成。
首先,我们来运行cryptogen 工具。由于二进制文件都在fabric-example/bin目录下,而我们的工作目录是first-network,所有我们需要使用相对路径来执行命令。(这也是为什么我们在篇首调用byfn.sh时,必须要切换到first-network目录下面的原因)。
../bin/cryptogen generate --config=./crypto-config.yaml |
命令行中将会输出以下信息:
org1.example.com org2.example.com |
如果执行报错:org1.example.com
panic: runtime error: invalid memory address or nil pointer dereference
,则很可能是用户权限的问题,请尝试使用root用户执行。[注:为了方便其间,建议全程使用root用户执行所有操作]
命令执行成功后,会在当前目录(first-network)下面生成一个 crypto-config 目录,所有的证书和密钥都在这个目录中。可以使用tree命令查看这个目录的结构。
接下来,我们要运行configtxgen工具来配置创世区块。在运行这个命令之前,我们要设置一个环境变量FABRIC_CFG_PATHl来告诉configtx.yaml配置文件的位置(现在就是中当前目录first-network中):
export FABRIC_CFG_PATH=$PWD |
现在,可以执行configtxgen命令了:
../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block |
其中 profile参数指定了TwoOrgsOrdererGenesis 这个profile( 这个里面指定了orderer服务), outputBlock则指定了创世区块的输出位置。
执行成功后,将会输入以下的内容:
2018-04-07 15:50:11.249 UTC [common/tools/configtxgen] main -> INFO 001 Loading configuration 2018-04-07 15:50:11.259 UTC [msp] getMspConfig -> INFO 002 Loading NodeOUs 2018-04-07 15:50:11.259 UTC [msp] getMspConfig -> INFO 003 Loading NodeOUs 2018-04-07 15:50:11.260 UTC [common/tools/configtxgen] doOutputBlock -> INFO 004 Generating genesis block 2018-04-07 15:50:11.260 UTC [common/tools/configtxgen] doOutputBlock -> INFO 005 Writing genesis block |
同时,在./channel-artifacts/目录中,将会生成一个genesis.block文件,这个就是我们生成的创世区块。
创建通道配置交易
(Create a Channel Configuration Transaction)
接下来,需要创建通道交易所需的配置。
首先,我们使用环境变量的方式指定通道名称,这样,执行后续的命令时就不需要修改名称了:
export CHANNEL_NAME=mychannel |
创建通道:
../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME |
[注:这里再次使用到了configtxgen 命令。还记得configtx.yaml中的Profiles配置吗?刚才,我们使用到了TwoOrgsOrdererGenesis这个profile,现在则用使用TwoOrgsChannel这个profile了。]
执行成功后,控制台中将会输出如下信息:
2018-04-07 16:15:28.565 UTC [common/tools/configtxgen] main -> INFO 001 Loading configuration 2018-04-07 16:15:28.575 UTC [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx 2018-04-07 16:15:28.576 UTC [msp] getMspConfig -> INFO 003 Loading NodeOUs 2018-04-07 16:15:28.576 UTC [msp] getMspConfig -> INFO 004 Loading NodeOUs 2018-04-07 16:15:28.597 UTC [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 005 Writing new channel tx |
现在,我们在mychannel这个通道上创建组织Org1的锚节点(anchor peer)。
../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文件利用我们之前下载的镜像和以前生成的genesis.block来引导orderer。
首先,让我们启动网络:
docker-compose -f docker-compose-cli.yaml up -d |
-d参数表示程序将在后台运行,如果你希望看到实时的日志信息,请不要使用-d参数。这样的话,需要再启动一个命令行来执行CLI请求。
CLI容器将等待1000秒后退出。当你需要它的时候,你可以使用一个简单的命令来重启启动它:
docker start cli |
环境变量:
为了后续针对 peer0.org1.example.com 的CLI命令正常执行,我们需要设置下述4个环境变量。当然,这4个环境变量已经在CLI容器的启动脚本中写入了,因此不需要我们在这里指令。然而,如果你需要发送指令到其他的peer或orderer,你就要指定它们了。从 docker-compose-base.yaml文件中,我们可以看到这4个环境变量指定的路径:
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 |
创建&加入信道
Create & Join Channel
现在CLI容器已经运行正确。我们使用docker命令进入cli容器:
docker exec -it cli bash |
上述docker exec命令表示在cli容器中执行bash命令,切使用交互式的命令执行 (-it参数),在执行成功后,将会出现cli容器内部的命令提示界面:
root@ec60a39c493b:/opt/gopath/src/github.com/hyperledger/fabric/peer# |
执行下ls命令可以看到目录下面有3个子目录:
root@ec60a39c493b:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts crypto scripts |
是不是很熟悉?如果你进一步查看,就会发现这三个目录里面的内容,和first-network中channel-artifacts、crypto-config、scripts三个目录中的文件内容都是一样的。[channel-artifacts、crypto-config目录中分别放置了我们刚才生成的证书和密钥文件。]
如果了解docker的话,马上就会反应出来,first-network中的三个目录是通过volume方式挂载到了cli容器里面了。确实是这样,再次检查一下 docker-compose-cli.yaml 文件,就会发现其中的玄机:
volumes: - /var/run/:/host/var/run/ - ./../chaincode/:/opt/gopath/src/github.com/chaincode - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts |
好了,现在我们来使用peer命令来创建通道(channel), 而使用的配置就是我们前面创建的配置。[注意:这个及后续的命令都是在容器中执行的]
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 export or replace the $CHANNEL_NAME variable appropriately peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem |
[在上面命令中,channel.tx文件使用了相对路径,而tlsca.example.com-cert.pem文件使用了绝对路径。其实都可以使用相对路径。]
这个命令执行完毕后,将会在当前目录中创建一个-
现在,我们来将 peer0.org1.example.com 加入到通道中。
# By default, this joins ``peer0.org1.example.com`` only # the # if you have not modified the channel name, you will join with mychannel.block # if you have created a different channel name, then pass in the appropriately named block peer channel join -b mychannel.block |
这个命令中又没有指定 peer0 参数,为什么是把 peer0.org1.example.com 加入通道中呢? 还记得[环境变量]小节中的4个环境变量吗?是它们
指定了peer0,通过compose文件将这些环境变量作为cli容器的初始化环境变量创建了,我们在cli容器中运行env命令,就可以看到这些环境变量的值。
了解了这些,我们就可以很容易地将 peer0.org2.example.com 加入到通道里面了:只需要改变这几个变量的值即可:
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel join -b mychannel.block |
(注意一条语句中指定这些环境变量,只会针对本条语句有效,环境变量的值并不会被修改)
更新锚节点
Update the anchor peers
下面的命令将更新通道,并传播通道的定义。实际上,我们是增加了额外的配置信息到通道的创世区块之上。主要我们并没有修改创世区块,只是简单地增加了定义锚节点的信息。
更新channel的定义,将 peer0.org1.example.com 指定为Org1的锚节点:(注意目前的环境变量仍然指定的是peer0.org1.example.com)
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem |
同理,我们在一条语句中覆盖环境变量,将 peer0.org2.example.com 指定为Org2的锚节点:
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem |
安装和实例化链码
在下面的演示中,我们使用了一个简单的已有链码示例。如果想学习如何自己写链码,请参考:Chaincode for Developers 。
应用通过链码(chaincode)与区块链账本交互。因此,我们需要中每个节点上面安装链码,这些节点将执行我们的交易,并对这些交易背书。同时,在通道上实例化链码。
首先,安装样例中的Go或Node.js链码到4个节点中的一个。这些命令将会把指定的链码源代码放到节点的文件系统上面。
[注意,一个链码的名称和版本中,只能对于一个版本的源代码。这些源代码在节点的文件系统的目录中,目录使用链码名称和版本进行命名;它与开发语言无关。同理,实例化链码的容器将反映出安装在节点中的语言(安装chaincode时通过-l参数指定开发语言,如果不指定,默认是使用Go语言)。
[下面两种开发语言2选1]
GO语言:
# this installs the Go chaincode peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/ |
Node.js:[注意,同一个链码mycc和版本 1.0下,只能使用一个版本的源代码。如果你执行了上面Go的版本,就没有必要执行node.js版本了,否则将会报“背书失败,链码已存在"的错误 ]
# this installs the Node.js chaincode # make note of the -l flag; we use this to specify the language peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/ |
下面来实例化链码。这个操作将在通道上面实例化链码,并为链码设定背书策略,并为目标节点启动一个链码容器。注意-P参数,这是我们需要指定的当这个chaincode的交易需要被验证的时侯的背书策略。
在下面的这个命令中,我们指定的背书策略是:-P "OR ('Org0MSP.peer','Org1MSP.peer')" 。 这个表示我们需要Org0或 Org1中的一个节点为我们背书(即只需要一个背书就认为交易验证通过),如果我们修改为 AND,则表示我们需要2个背书者。
Golang
export CHANNEL_NAME=mychannel # be sure to replace the $CHANNEL_NAME environment variable if you have not exported it # if you did not install your chaincode with a name of mycc, then modify that argument as well peer chaincode instantiate -o orderer.example.com:7050 --tls --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.peer','Org2MSP.peer')" |
Nodejs (如果前面选择使用nodejs,则此处继续。再次说明,只能选择go或nodejs之一)
# be sure to replace the $CHANNEL_NAME environment variable if you have not exported it # if you did not install your chaincode with a name of mycc, then modify that argument as well # notice that we must pass the -l flag after the chaincode name to identify the language peer chaincode instantiate -o orderer.example.com:7050 --tls --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 -l node -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')" |
让我们查询一下a的值,以确保链码被正确实例化,state DB被填充。查询的语法如下:
# be sure to set the -C and -n flags appropriately peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' |
调用
现在让我们从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"]}' |
查询
让我们确认下我们之前的调用被正确地执行了。我们初始化了a的值为100,在上一次调用的时侯转移了10给b。因此,查询a应该展示90。查询的语法如下:
# 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
随时重新开始并操作键值对和随后的调用。
3、创建过程解析
通过1和2的操作过程,我们详细说明一下,在执行./byfn.sh up时,到底后台发生了什么?
[注; 再次执行 ./byfn.sh up 前,可以通过./byfn.sh down清理掉运行环境]
script.sh 脚本被事先拷贝到CLI容器里面。这个脚本执行根据给定的通道名称,执行createChannel 命令,并使用channell.tx作为通道配置;
createChannel的输出是一个创世区块—— <通道名称 >.block —— 它存储在节点的文件系统中,包含了channel.tx特定的配置信息;
joinChannel 命令被所有4个节点执行,它作为前面创建的创世通道的输入。这个命令将peer加入到通道中,并基于 <通道名称 >.block创建了一个链;
现在我们有了由4个peer节点以及2个组织构成的信道。这是我们的TwoOrgsChannel配置文件。
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)的anchor peers将在后续被更新。我们通过携带channel的名字传递Org1MSPanchors.tx和Org2MSPanchors.tx配置到排序服务来实现anchor peer的更新。
一个链码-chaincode_example02被安装在peer0.org1.example.com和peer0.org2.example.com
这个链码在peer0.org2.example.com被实例化。实例化过程将链码添加到信道上,并启动peer节点对应的容器,并且初始化和链码服务有关的键值对。示例的初始化的值是[”a“,”100“,”b“,”200“]。实例化的结果是一个名为dev-peer0.org2.example.com-mycc-1.0的容器启动了。
实例化过程同样为背书策略传递相关参数。策略被定义为-P "OR ('Org1MSP.member','Org2MSP.member')",意思是任何交易必须被Org1或者Org2背书。
一个针对a的查询发往peer0.org1.example.com。链码服务已经被安装在了peer0.org1.example.com,因此这次查询将启动一个名为dev-peer0.org1.example.com-mycc-1.0的容器。查询的结果也将被返回。没有写操作出现,因此查询的结果的值将为100。
一次invoke被发往peer0.org1.example.com,从a转移10到b。
然后链码服务被安装到peer1.org2.example.com
一个query请求被发往peer1.org2.example.com用于查询a的值。这将启动第三个链码服务名为dev-peer1.org2.example.com-mycc-1.0。返回a的值为90,正确地反映了之前的交易,a的值被转移了10。
===================================
以下内容直接摘录 https://hyperledgercn.github.io/hyperledgerDocs/build_network_zh/#17=============================
.9.8. 这指明了什么?
为了能够正确地在账本上进行读写操作,链码服务必须被安装在peer节点上。此外,每个peer节点的链码服务的容器除了init或者传统的交易-读/写-针对该链码服务执行(例如查询a的值),在其他情况下不会启动。交易导致容器的启动。当然,所有信道中的节点都持有以块的形式顺序存储的不可变的账本精确的备份,以及状态数据库来保存前状态的快照。这包括了没有在其上安装链码服务的peer节点(peer1.org2.example.com如上所示)。最后,链码在被安装后将是可达状态,因为它已经被实例化了。
1.9.9. 我如何查询这些交易?
检查CLI容器的日志。
docker logs -f cli
你应该看到以下输出:
2017-05-16 17:08:01.366 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSP2017-05-16 17:08:01.366 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining default signing identity2017-05-16 17:08:01.366 UTC [msp/identity] Sign -> DEBU 006Sign: plaintext: 0AB1070A6708031A0C08F1E3ECC80510...6D7963631A0A0A0571756572790A01612017-05-16 17:08:01.367 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: E61DB37F4E8B0D32C9FE10E3936BA9B8CD278FAA1F3320B08712164248285C54 Query Result: 902017-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 ===================== _____ _ _ ____ | ____| | \ | | | _ \ | _| | \| | | | | | | |___ | |\ | | |_| | |_____| |_| \_| |____/
你可以滚动这些日志来查看各种交易。
1.9.10. 我如何查看链码日志?
检查每个独立的链码服务容器来查看每个容器内的分隔的交易。下面是每个链码服务容器的日志的组合:
$ docker logs dev-peer0.org2.example.com-mycc-1.004:30:45.947 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW] ex02 Init Aval =100, Bval = 200$ docker logs dev-peer0.org1.example.com-mycc-1.004: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.004:31:30.420 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW] ex02 Invoke Query Response:{"Name":"a","Amount":"90"}
1.10. 了解 Docker Compose 技术
BYFN示例给我们提供了两种风格的Docker Compose文件,它们都继承自docker-compose-base.yaml(base目录下)。我们的第一种类型,docker-compose-cli.yaml给我们提供了一个CLI容器,以及一个orderer容器,四个peer容器。我们用此文件来展开这个页面上的所有说明。
注意
本节的剩余部分涵盖了为SDK设计的docker-compose文件。有关运行这些测试的详细信息,请参阅Node SDK仓库。
第二种风格是docker-compose-e2e.yaml,被构造为使用Node.js SDK来运行端到端测试。除了SDK的功能之外,它主要的区别在于它有运行fabric-ca服务的容器。因此,我们能够向组织的CA节点发送REST的请求用于注册和登记。
如果你在没有运行byfn.sh脚本的情况下,想使用docker-compose-e2e.yaml,我们需要进行4个轻微的修改。我们需要指出本组织CA的私钥。你可以在crypto-config文件夹中找到这些值。举个例子,为了定位Org1的私钥,我们将使用crypto-config/peerOrganizations/org1.example.com/ca/。Org2的路径为crypto-config/peerOrganizations/org2.example.com/ca/。
在docker-compose-e2e.yaml里为ca0和ca1更新FABRIC_CA_SERVER_TLS_KEYFILE变量。你同样需要编辑command中去启动ca server的路径。你为每个CA容器提供了2次同样的私钥。
1.11. 使用CouchDB
状态数据库可以从默认的goleveldb切换到CouchDB。链码功能同样能使用CouchDB。但是,CouchDB提供了额外的能力来根据JSON形式的链码服务数据提供更加丰富以及复杂的查询。
使用CouchDB代替默认的数据库(goleveldb),除了在启动网络的时侯传递docker-compose-couch.yaml之外,请遵循前面提到的生成配置文件的过程:
CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=
chaincode_example02现在应该使用下面的CouchDB。
注意
如果你选择将fabric-couchdb容器端口映射到主机端口,请确保你意识到了安全性的影响。在开发环境中映射端口可以使CouchDB REST API可用,并允许通过CouchDB Web界面(Fauxton)对数据库进行可视化。生产环境将避免端口映射,以限制对CouchDB容器的外部访问。
你可以使用上面列出的步骤使用CouchDB来执行chaincode_example02,然而为了执行执行CouchDB的查询能力,你将需要使用被格式化为JSON的数据(例如marbles02)。你可以在fabric/examples/chaincode/go目录中找到marbles02链码服务。
我们将按照上述创建和加入频道部分所述的相同过程创建和加入信道。一旦你将peer节点加入到了信道,请使用以下步骤与marbles02链码交互:
在peer0.org1.example.com上安装和实例化链码:
# be sure to modify the $CHANNEL_NAME variable accordingly for the instantiate commandpeer 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 accordinglypeer 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"]}'
如果你选择在docker-compose文件中映射你的CouchDB的端口,那么你现在就可以通过CouchDB Web界面(Fauxton)通过打开浏览器导航下列URL:http://localhost:5984/_utils
你应该可以看到一个名为mychannel(或者你的唯一的信道名字)的数据库以及它的文档在里面:
Note
For the below commands, be sure to update the $CHANNEL_NAME variable appropriately.
注意
对于下面的命令,请确定$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}
你可以检索特定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":}]
你还可以对数据内容执行丰富的查询,例如通过拥有者jerry查询marble:
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["queryMarblesByOwner","jerry"]}'
输出应该显示2个属于jerry的marble:
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}}]
1.12. 关于数据持久化的提示
如果需要在peer容器或者CouchDB容器进行数据持久化,一种选择是将docker容器内相应的目录挂载到容器所在的宿主机的一个目录中。例如,你可以添加下列的两行到docker-compose-base.yaml文件中peer的约定中:
volumes: - /var/hyperledger/peer0:/var/hyperledger/production
对于CouchDB容器,你可以在CouchDB的约定中添加两行:
volumes: - /var/hyperledger/couchdb0:/opt/couchdb/data
1.13. 故障排除
始终保持你的网络是全新的。使用以下命令来移除之前生成的artifacts,crypto,containers以及chaincode images:
./byfn.sh -m down
你将会看到错误信息,如果你不移除容器和镜像
如果你看到相关的Docker错误信息,请检查你的版本(应为1.12或更高版本),然后重启你的Docker进程。Docker的问题通常不会被立即识别。例如,你可能看到由于容器内加密材料导致的错误。
如果她们坚持删除您的镜像,并从头开始:
docker rm -f $(docker ps -aq) docker rmi -f $(docker images -q)
如果你发现你的创建、实例化,调用或者查询命令,请确保你已经更新了信道和链码的名字。提供的示例命令中有占位符。
如果你看到如下错误:
Error: Error endorsing chaincode: rpc error: code = 2 desc = Error installing chaincode code mycc:1.0(chaincode /var/hyperledger/production/chaincodes/mycc.1.0 exits)
你可能由以前运行的链码服务(例如dev-peer1.org2.example.com-mycc-1.0或dev-peer0.org1.example.com-mycc-1.0)。删除它们,然后重试。
docker rmi -f $(docker images | grep peer[0-9]-peer[0-9] | awk '{print $3}')
如果你看到类似以下内容的错误信息:
Error connecting: rpc error: code = 14 desc = grpc: RPC failed fast due to transport failure Error: rpc error: code = 14 desc = grpc: RPC failed fast due to transport failure
请确保你的fabric网络运行在被标记为latest的1.0.0-rc1镜像上。
如果你看到了类似以下错误的内容:
[configtx/tool/localconfig] Load -> CRIT 002 Error reading configuration: Unsupported Config Type ""panic: Error reading configuration: Unsupported Config Type ""
那么你没有正确设置FABRIC_CFG_PATH环境变量。configtxgen工具需要这个变量才能找到configtx.yaml。返回并执行export FABRIC_CFG_PATH=$PWD,然后重新创建channel配置。
要清理网络,请使用down选项:
./byfn.sh -m down
如果你看到一条指示你依然有“active endpoints”,然后清理你的Docker网络。这将会清除你之前的网络并且给你一个全新的环境:
docker network prune
你将看到以下消息:
WARNING! This will remove all networks not used by at least one container. Are you sure you want to continue? [y/N]
选择y。
如果你仍旧看到了错误,请在Hyperledger Rocket Chat的# fabric-questions频道上分享你的日志。
==更多原创内容分享,请扫码关注公众号: 超级账本开发 ==