Hyperledger Fabric(二) 官方教程:构建第一个
前言
官方英文:Building Your First Network
感谢中文翻译!不然这么长的英文以及各种名词解释实在头疼。有一些和官方英文不一样的地方我已经按官方英文进行了补充和修正,并且尽量简化了语言,因为有些翻译过来有点拗口。如果希望看完整及原汁原味的文档,推荐看官方英文文档。
本文基于Mac OS
以及Hyperledger Fabric 1.1.0
。
Building Your First Network
构建你的第一个网络(BYFN)场景提供了由两个组织组成的示例Hyperledger Fabric网络,每个组织持有2个peer节点,以及一个“solo”排序服务。
1.1. 安装预置环境
在我们开始之前,如果你还没有这样做,你可能需要检查一下在你开发区块链应用程序或者Hyperledger Fabric的平台上是否已经安装了预置环境。
你还需要下载并安装Hyperledger Fabric Samples。你会注意到fabric-samples
文件夹中包含了许多示例。我们将使用first-network
这个例子。现在让我们打开这个子目录。
cd fabric-samples/first-network
注意:本文档中提供的命令必须运行在
fabric-samples
的子目录fabric-network
中。如果你选择从其他位置运行命令,提供的一些脚本将无法找到对应的二进制。
1.2. 想要现在运行吗?
我们提供一个完全注释的脚本byfn.sh
,利用这些Docker镜像可以快速引导一个由4个代表2个不同组织的peer节点以及一个排序服务节点的Hyperledger fabric网络。它还将启动一个容器来运行一个将peer节点加入channel、部署实例化链码服务以及驱动已经部署的链码执行交易的脚本。
以下是该byfn.sh
脚本的帮助文档:
./byfn.sh --help
Usage:
byfn.sh up|down|restart|generate [-c <channel name>] [-t <timeout>] [-d <delay>] [-f <docker-compose-file>] [-s <dbtype>]
byfn.sh -h|--help (print this message)
-m <mode> - one of 'up', 'down', 'restart' or 'generate'
- 'up' - bring up the network with docker-compose up
- 'down' - clear the network with docker-compose down
- 'restart' - restart the network
- 'generate' - generate required certificates and genesis block
-c <channel name> - channel name to use (defaults to "mychannel")
-t <timeout> - CLI timeout duration in seconds (defaults to 10)
-d <delay> - delay duration in seconds (defaults to 3)
-f <docker-compose-file> - specify which docker-compose file use (defaults to docker-compose-cli.yaml)
-s <dbtype> - the database backend to use: goleveldb (default) or couchdb
-l <language> - the chaincode language: golang (default) or node
-a - don't ask for confirmation before proceeding
Typically, one would first generate the required certificates and
genesis block, then bring up the network. e.g.:
byfn.sh -m generate -c mychannel
byfn.sh -m up -c mychannel -s couchdb
如果你选择不提供channel名称,则脚本将使用默认名称mychannel
。CLI超时参数(用-t标志指定)是一个可选值;如果你选择不设置它,那么CLI容器将会在脚本执行完之后退出。
1.3. 生成网络
准备好了吗?好吧!执行以下命令。
yuyangdeMacBook-Pro:~ yuyang$ cd /Users/yuyang/fabric-sample/fabric-samples/first-network
yuyangdeMacBook-Pro:first-network yuyang$ ./byfn.sh -m generate
命令行会提示将会发生什么。当提示yes/no时,输入y或回车键来执行描述的动作。
Generating certs and genesis block for with channel 'mychannel' and CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y
proceeding ...
/Users/yuyang/fabric-sample/fabric-samples/first-network/../bin/cryptogen
##########################################################
##### Generate certificates using cryptogen tool #########
##########################################################
+ cryptogen generate --config=./crypto-config.yaml
org1.example.com
org2.example.com
+ set +x
/Users/yuyang/fabric-sample/fabric-samples/first-network/../bin/configtxgen
##########################################################
######### Generating Orderer Genesis block ##############
##########################################################
+ configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
2018-03-17 21:56:10.295 CST [common/tools/configtxgen] main -> INFO 001 Loading configuration
2018-03-17 21:56:10.307 CST [msp] getMspConfig -> INFO 002 Loading NodeOUs
2018-03-17 21:56:10.307 CST [msp] getMspConfig -> INFO 003 Loading NodeOUs
2018-03-17 21:56:10.307 CST [common/tools/configtxgen] doOutputBlock -> INFO 004 Generating genesis block
2018-03-17 21:56:10.308 CST [common/tools/configtxgen] doOutputBlock -> INFO 005 Writing genesis block
+ set +x
#################################################################
### Generating channel configuration transaction 'channel.tx' ###
#################################################################
+ configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
2018-03-17 21:56:10.328 CST [common/tools/configtxgen] main -> INFO 001 Loading configuration
2018-03-17 21:56:10.339 CST [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2018-03-17 21:56:10.340 CST [msp] getMspConfig -> INFO 003 Loading NodeOUs
2018-03-17 21:56:10.341 CST [msp] getMspConfig -> INFO 004 Loading NodeOUs
2018-03-17 21:56:10.366 CST [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 005 Writing new channel tx
+ set +x
#################################################################
####### Generating anchor peer update for Org1MSP ##########
#################################################################
+ configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
2018-03-17 21:56:10.385 CST [common/tools/configtxgen] main -> INFO 001 Loading configuration
2018-03-17 21:56:10.393 CST [common/tools/configtxgen] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2018-03-17 21:56:10.393 CST [common/tools/configtxgen] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update
+ set +x
#################################################################
####### Generating anchor peer update for Org2MSP ##########
#################################################################
+ configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
2018-03-17 21:56:10.413 CST [common/tools/configtxgen] main -> INFO 001 Loading configuration
2018-03-17 21:56:10.421 CST [common/tools/configtxgen] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2018-03-17 21:56:10.421 CST [common/tools/configtxgen] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update
+ set +x
第一步生成我们各种网络实体的所有证书和密钥,genesis block
用于引导排序服务,以及配置Channel
所需要的一组交易配置集合。
1.4. 启动网络
接下来,你可以使用以下命令来启动整个网络。
./byfn.sh -m up
上面的指令会编译Golang智能合约镜像并且启动对应的容器。Go语言是默认的链码语言,当然这里也支持 Node.js智能合约。如果你希望以node形式的智能合约完成教程,可以将刚才的指令替换为:
# 使用 -l 标志指定智能合约语言
# 不指定默认使用Golang
./byfn.sh -m up -l node
点击 Hyperledger Fabric Shim来查看更多关于使用node.js 编写智能合约的API文档。
再次提示是否继续,输入y或者键入回车:
yuyangdeMacBook-Pro:first-network yuyang$ ./byfn.sh -m up
Starting with channel 'mychannel' and CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y
proceeding ...
2018-03-17 14:10:17.541 UTC [main] main -> INFO 001 Exiting.....
LOCAL_VERSION=1.1.0-rc1
DOCKER_IMAGE_VERSION=1.1.0-rc1
Creating network "net_byfn" with the default driver
Creating volume "net_peer0.org2.example.com" with default driver
Creating volume "net_peer1.org2.example.com" with default driver
Creating volume "net_peer1.org1.example.com" with default driver
Creating volume "net_peer0.org1.example.com" with default driver
Creating peer1.org2.example.com ... done
Creating cli ... done
Creating peer0.org1.example.com ...
Creating orderer.example.com ...
Creating peer0.org2.example.com ...
Creating peer1.org2.example.com ...
Creating cli ...
____ _____ _ ____ _____
/ ___| |_ _| / \ | _ \ |_ _|
\___ \ | | / _ \ | |_) | | |
___) | | | / ___ \ | _ < | |
|____/ |_| /_/ \_\ |_| \_\ |_|
Build your first network (BYFN) end-to-end test
Channel name : mychannel
Creating channel...
日志将从这里继续。然后启动所有容器,驱动一个端到端的应用场景。成功以后,在终端窗口中会报告以下内容:
Query Result: 90
2018-03-17 14:11:33.653 UTC [main] main -> INFO 003 Exiting.....
===================== Query on peer1.org2 on channel 'mychannel' is successful =====================
========= All GOOD, BYFN execution completed ===========
_____ _ _ ____
| ____| | \ | | | _ \
| _| | \| | | | | |
| |___ | |\ | | |_| |
|_____| |_| \_| |____/
你可以滚动这些日志去查看各种交易。如果你没有获得这个结果,请移步疑难解答部分Troubleshooting,看看我们是否可以帮助你发现问题。
1.5. 关闭网络
最后,让我们把它全部停下来,这样我们可以一步一步地探索网络设置。以下操作将关闭你的容器,移除加密材料和4个配置信息,并且从Docker仓库删除智能合约镜像。你将再一次被提示是否继续,回答y或者键入回车:
yuyangdeMacBook-Pro:first-network yuyang$ ./byfn.sh -m down
如果你想了解关于底层工具和引导材料相关的更多信息,请继续阅读。在接下来的章节中,我们将浏览构建功能齐全的Hyperledger fabric网络的各种要求和步骤。
在上面的步骤中,默认的
cli
容器日志输出等级CORE_LOGGING_LEVEL
是INFO
。你可以在first-network
目录中的docker-compose-cli.yaml
文件中进行修改。
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
1.6. 加密原理
我们将使用cryptogen
工具为我们生成各种网络实体的加密材料(x509证书)。这些证书是身份的代表,它们允许在我们的网络实体进行交流和交易时进行签名/验证身份验证。
Fabric中有两种类型的公私钥和证书,一种是给节点之前通讯安全而准备的TLS证书,另一种是用户登录和权限控制的用户证书。这些证书本来应该是由CA来颁发,但是我们这里是测试环境,并没有启用CA节点,所以Fabric帮我们提供了一个工具:cryptogen。
1.6.1. 它是如何工作的?
Cryptogen
使用crypto-config.yaml
,并允许我们为组织和属于这些组织的组件生成一组证书和密钥。每个组织都配置了唯一的根证书(ca-cert)
,它将特定组件(peers和orders)绑定到该组织。通过为每一个组织分配唯一的CA证书,我们正在模仿一个典型的网络,这个网络中的成员将使用自己的证书颁发机构。Hyperledger Fabric中的交易和通信是通过存储在keystore
中的实体的私钥签名,然后通过公钥手段进行验证(signcerts
)。
你将注意到在这个文件里有一个count
变量。我们将使用它来指定每个组织中peer
的数量;在我们的例子中,每个组织有两个peer。我们现在不会深入研究x.509证书和公钥基础设施的细节。如果你有兴趣,你可以在自己的时间细读这些主题。
在运行该工具之前,让我们快速浏览一下这段代码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
EnableNodeOUs: true
网络实体的命名约定如下:”{{.Hostname}}.{{.Domain}}”。所以使用我们的排序节点作为参考点,它与Order
的MSP ID相关联。该文件包含了有关定义和语法的大量文档。你还可以参考Membership Service Providers(MSP),以便更深入地了解MSP。
我们运行cryptogen
工具,生成的证书和密钥将被保存到名为crypto-config
的文件夹中。
1.7. 配置工作说明
configtxgen tool
用于创建4个配置工作:
-
order的
genesis block
创世区块 -
channel的
channel configuration transaction
-
以及两个
anchor peer transactions
一个对应一个Peer组织
有关此工具的完整说明,请参阅Channel Configuration(configtxgen)。
order block
是一个ordering service的创世区块,channel transaction
配置文件在Channel
创建的时侯广播给order。anchor peer transactions
,正如名称所示,指定了每个组织在此channel上的锚节点。
1.7.1. 它是如何工作的?
Configtxgen使用一个包含示例网络的configtx.yaml
文件。有3个成员-一个排序服务组织OrdererOrg
以及两个节点组织(Org1
&Org2
),每个组织管理和持有2个peer节点。该文件还指定了一个SampleConsortium
的联盟,由上述2个节点组织构成。 请特别注意此文件顶部的”Profiles”部分。你会注意到我们有两个独特的标题。一个是orderer的创世区块-TwoOrgsOrdererGenesis
-另一个是针对channel的TwoOrgsChannel
。
这些标题很重要,因为在我们创建我们的工作的时侯它们将作为传递的参数。
注意:请注意我们的
SampleConsortium
在系统级别的配置文件中定义,然后由channel级别配置文件引用。
此文件还包含两个值得注意的附加规格。首先,我们为每个组织指定了锚点节点(peer0.org1.example.com
和peer0.org2.example.com
)。其次,我们为每个成员指定MSP文件夹,用来存储每个组织在orderer genesis block
中指定的根证书。这是一个关键的概念。现在任意和ordering service通信的网络实体都可以对其数字签名进行验证。
这个文件里面配置了由2个Org参与的Orderer共识配置TwoOrgsOrdererGenesis,以及由2个Org参与的Channel配置:TwoOrgsChannel。Orderer可以设置共识的算法是Solo还是Kafka,以及共识时区块大小,超时时间等,我们使用默认值即可,不用更改。而Peer节点的配置包含了MSP的配置,锚节点的配置。如果我们有更多的Org,或者有更多的Channel,那么就可以根据模板进行对应的修改。
1.8. 生成公私钥证书和创世区块
你可以用configtxgen
和cryptogen
命令来手动生成证书/密钥和各种配置文件。或者,你可以尝试使用byfn.sh
脚本来完成你的目标。
1.8.1. 手动生成
必要的话,你可以参考byfn.sh脚本中的generateCerts
函数去生成相关定义在crypto-config.yaml
文件中用于你的网络配置的相关证书。然而,为了方便起见,我们也将在此提供参考。
首先,我们来运行cryptogen
这个工具。我们的二进制文件在bin
目录中,所以我们需要提供工具所在的相对路径。
yuyangdeMacBook-Pro:first-network yuyang$ ../bin/cryptogen generate --config=./crypto-config.yaml
你会看到以下内容出现在命令行中:
org1.example.com
org2.example.com
证书和秘钥(也就是MSP材料)会生成在位于first-network
文件夹下的crypto-config
文件夹中。
接下来,我们需要告诉configtxgen
工具去哪里获取configtx.yaml
文件。我们将告诉它在我们当前的工作目录获取。
我们需要设置一个环境变量来告诉configtxgen
哪里去寻找configtx.yaml
。
yuyangdeMacBook-Pro:first-network yuyang$ export FABRIC_CFG_PATH=$PWD
然后,我们将调用configtxgen
工具去创建orderer genesis block
:
yuyangdeMacBook-Pro:first-network yuyang$ ../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
你会看到以下内容出现在命令行中:
2018-03-18 12:31:44.111 CST [common/tools/configtxgen] main -> INFO 001 Loading configuration
2018-03-18 12:31:44.123 CST [msp] getMspConfig -> INFO 002 Loading NodeOUs
2018-03-18 12:31:44.123 CST [msp] getMspConfig -> INFO 003 Loading NodeOUs
2018-03-18 12:31:44.123 CST [common/tools/configtxgen] doOutputBlock -> INFO 004 Generating genesis block
2018-03-18 12:31:44.124 CST [common/tools/configtxgen] doOutputBlock -> INFO 005 Writing genesis block
注意:刚才的指令会将生成的文件
genesis.block
放置在当前目录下的channel-artifacts
文件夹中。
1.8.2. 创建channel transaction配置
接下来,我们需要创建channel transaction
配置。请确保替换$CHANNEL_NAME
或者将CHANNEL_NAME
设置为整个说明中可以使用的环境变量:
yuyangdeMacBook-Pro:first-network yuyang$ export CHANNEL_NAME=mychannel && ../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
你会看到以下内容出现在命令行中:
2018-03-18 12:42:33.661 CST [common/tools/configtxgen] main -> INFO 001 Loading configuration
2018-03-18 12:42:33.669 CST [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2018-03-18 12:42:33.669 CST [msp] getMspConfig -> INFO 003 Loading NodeOUs
2018-03-18 12:42:33.670 CST [msp] getMspConfig -> INFO 004 Loading NodeOUs
2018-03-18 12:42:33.702 CST [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 005 Writing new channel tx
注意:刚才的指令会将生成的文件
channel.tx
放置在当前目录下的channel-artifacts
文件夹中。
接下来,我们将在正在构建的通道上定义Org1
的anchor peer
。请再次确认$CHANNEL_NAME已被替换或者为以下命令设置了环境变量:
yuyangdeMacBook-Pro:first-network yuyang$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
现在,我们将在同一个通道定义Org2
的anchor peer
:
yuyangdeMacBook-Pro:first-network yuyang$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
注意:刚才的指令会将生成的文件
Org1MSPanchors.tx
和Org2MSPanchors.tx
放置在当前目录下的channel-artifacts
文件夹中。
1.9. 启动网络
我们将利用docker-compose
脚本来启动我们的区块链网络。docker-compose
文件会利用我们之前下载的镜像,并用以前生成的genesis.block
来引导orderer
。
我们想手动执行命令,以便说明每个调用的语法和功能。
首先启动你的网络:
yuyangdeMacBook-Pro:first-network yuyang$ docker-compose -f docker-compose-cli.yaml up -d
Creating network "net_byfn" with the default driver
Creating volume "net_peer0.org2.example.com" with default driver
Creating volume "net_peer1.org2.example.com" with default driver
Creating volume "net_peer1.org1.example.com" with default driver
Creating volume "net_peer0.org1.example.com" with default driver
Creating peer0.org2.example.com ... done
Creating cli ... done
Creating peer0.org1.example.com ...
Creating peer0.org2.example.com ...
Creating orderer.example.com ...
Creating peer1.org2.example.com ...
Creating cli ...
如果要实时查看你的区块链网络的日志,请不要提供-d
标志。如果你需要日志流,你需要打开第二个终端来执行CLI命令。
CLI容器处于空闲状态时只会会停留1000秒。你可以使用以下命令再次启动它:
docker start cli
1.9.1. 环境变量
下面是peer0.org1.example.com
的环境变量。每当在CLI命令中需要执行节点操作时,都要确认当前处于哪个节点下。如果要切换至peer0.org1
节点,就需要将其环境变量复制到命令行中执行。同理,如果切换到其他节点,需要对应的修改环境变量。后面会进行实际操作。
# 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
1.9.2. 创建&加入信道
回忆在之前的1.8.2. 创建channel transaction配置,我们使用configtxgen
工具创建channel transaction配置 。你可以重复使用相同或不同的configtx.yaml
文件,在你的网络创建其他的信道。
我们将使用docker exec
命令进入CLI容器:
yuyangdeMacBook-Pro:first-network yuyang$ docker exec -it cli bash
如果成功,你将看到下列信息:
root@cfbda07a993d:/opt/gopath/src/github.com/hyperledger/fabric/peer#
我们使用-c
标志指定channel的名字,-f
标志指定channel配置文件。在这个例子中它是channel.tx
,当然你也可以使用不同的名称,挂载你自己的channel配置。再一次,我们将在CLI容器里设置了CHANNEL_NAME
环境变量,所以不需要明确传递这个参数。
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
注意:
-- cafile
会作为命令的一部分。这是orderer的root cert的本地路径,允许我们去验证TLS握手。
此命令返回一个创世区块-<channel-ID.block>
-我们将使用它加入信道。它包含了channel.tx中的配置信息。如果你没有修改过channel.tx
文件中默认的通道名称,上面的命令会返回mychannel.block
。
2018-03-19 05:15:15.385 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2018-03-19 05:15:15.419 UTC [channelCmd] InitCmdFactory -> INFO 002 Endorser and orderer connections initialized
2018-03-19 05:15:15.624 UTC [main] main -> INFO 003 Exiting.....
注意:这里我的返回信息并没有区块
mychannel.block
,而是INFO 003 Exiting.....
,是直接退出了。因为我看的官方文档和网上其他人写的示例都是返回了区块信息,所以我以为我这里出问题了,然后想了很多办法查了很多资料,重来了很多次都不行。接着我发现之前官方写的脚本byfn.sh
运行是没有问题的,所以我试着再次运行,发现它在创建channel的时候也是INFO 003 Exiting.....
,应该是成功了的。如果有朋友有更好的解释,麻烦留言告知,谢谢!
另外这里有一个方法可以查看容器启动后docker-compose -f docker-compose-cli.yaml up -d
的状态:
yuyangdeMacBook-Pro:first-network yuyang$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
45b3f41ae42f hyperledger/fabric-tools:latest "/bin/bash" 20 seconds ago Up 21 seconds cli
26dadcb90749 hyperledger/fabric-peer:latest "peer node start" 22 seconds ago Up 22 seconds 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.example.com
053b317fc840 hyperledger/fabric-peer:latest "peer node start" 22 seconds ago Up 22 seconds 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.example.com
0622660b60cd hyperledger/fabric-peer:latest "peer node start" 22 seconds ago Up 23 seconds 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.example.com
5b6d636ab4ed hyperledger/fabric-peer:latest "peer node start" 22 seconds ago Up 23 seconds 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.example.com
9c4da795db8c hyperledger/fabric-orderer:latest "orderer" 22 seconds ago Up 23 seconds 0.0.0.0:7050->7050/tcp orderer.example.com
可以看到1Orderer+4Peer+1CLI都启动了。
现在让我们将peer0.org1
加入频道。
# By default, this joins ``peer0.org1.example.com`` only
# the <channel-ID.block> was returned by the previous command
# 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
root@45b3f41ae42f:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel join -b mychannel.block
2018-03-19 08:19:55.355 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2018-03-19 08:19:55.477 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
2018-03-19 08:19:55.477 UTC [main] main -> INFO 003 Exiting.....
我们可以通过修改上面提到的四个环境变量来将其他的节点加入信道。
加入peer1.org1
:
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=peer1.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/peer1.org1.example.com/tls/ca.crt
peer channel join -b mychannel.block
加入peer0.org2
:
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
加入peer1.org2
:
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=peer1.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/peer1.org2.example.com/tls/ca.crt
peer channel join -b mychannel.block
1.9.3. 更新锚节点
由于我们在刚才的步骤中最后加入的节点是peer1.org2
,所以执行完成后我们的命令行现在处于peer1.org2
下。而现在我们需要为Org1设置锚节点peer0.org1
,所以命令行需要先切回到peer0.org1
:
设置当前操作节点为peer0.org1
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
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
然后更新Org1锚节点为peer0.org1
:
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
2018-03-19 09:00:21.829 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2018-03-19 09:00:21.846 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
2018-03-19 09:00:21.846 UTC [main] main -> INFO 003 Exiting.....
接下来为Org2设置锚节点peer0.org2
设置当前操作节点为peer0.org2
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
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
然后更新Org2锚节点为peer0.org2
:
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
2018-03-19 09:05:58.149 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2018-03-19 09:05:58.164 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
2018-03-19 09:05:58.164 UTC [main] main -> INFO 003 Exiting.....
1.9.4. 安装和实例化链码
我们将利用一个现有的简单链码。要学习如何编写自己的链码,请参考链码服务开发指南
应用程序和区块链账本会通过chaincode相互影响。因此,我们需要在每个会执行以及背书我们交易的peer节点安装chaincode,然后在Channel上实例化chaincode。
首先,将Go或者Node.js示例代码安装到4个peer节点中的其中一个。如果其他的节点也要安装,需要执行4此。这个命令将源代码放到peer节点的文件系统中。
你可以在每个节点上安装任意一种语言的链码。并在稍后指定语言进行初始化。
Golang
# this installs the Go chaincode
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/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/
我们先切换到peer0.org1这个节点:
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
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
安装指定的ChainCode并对其命名:
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
2018-03-19 10:32:51.352 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-19 10:32:51.352 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2018-03-19 10:32:51.532 UTC [main] main -> INFO 003 Exiting.....
安装的过程其实就是对CLI中指定的代码进行编译打包,并把打包好的文件发送到Peer,等待接下来的实例化。
接下来,在channel上实例化chaincode。这将初始化channel上的链码,设置链码的背书策略,为目标peer节点启动一个chaincode容器。注意-P
参数。这是我们需要指定的当这个chaincode的交易需要被验证的时侯的背书策略。
在下面的命令中,你会注意到我们指定-P "OR ('Org0MSP.member','Org1MSP.member')"
作为背书策略。这意味着我们需要Org1或者Org2组织中的其中一个的节点的背书即可(即只有一个背书)。如果我们改变语法为AND
那么我们就需要2个背书者。
有关更多背书策略的详细信息请参考背书策略。
Golang
# 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')"
Node.js
# 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')"
初始化链码:
2018-03-19 10:57:13.838 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-19 10:57:13.838 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2018-03-19 10:57:24.179 UTC [main] main -> INFO 003 Exiting.....
可以使用以下命令查看peer0.org1日志:
docker logs -f peer0.org1.example.com
2018-03-19 10:57:13.847 UTC [golang-platform] GenerateDockerBuild -> INFO 036 building chaincode with ldflagsOpt: '-ldflags "-linkmode external -extldflags '-static'"'
2018-03-19 10:57:13.847 UTC [golang-platform] GenerateDockerBuild -> INFO 037 building chaincode with tags:
2018-03-19 10:57:26.196 UTC [cceventmgmt] HandleStateUpdates -> INFO 038 Channel [mychannel]: Handling LSCC state update for chaincode [mycc]
2018-03-19 10:57:26.202 UTC [kvledger] CommitWithPvtData -> INFO 039 Channel [mychannel]: Committed block [3] with 1 transaction(s)
使用docker ps
命令可以发现新的容器dev-peer0.org1.example.com-mycc-1.0
:
yuyangdeMacBook-Pro:first-network yuyang$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d2fbe3a12558 dev-peer0.org1.example.com-mycc-1.0-384f11f484b9302df90b453200cfb25174305fce8f53f4e94d45ee3b6cab0ce9 "chaincode -peer.add…" 3 minutes ago Up 3 minutes dev-peer0.org1.example.com-mycc-1.0
45b3f41ae42f hyperledger/fabric-tools:latest "/bin/bash" 3 hours ago Up 3 hours cli
26dadcb90749 hyperledger/fabric-peer:latest "peer node start" 3 hours ago Up 3 hours 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.example.com
053b317fc840 hyperledger/fabric-peer:latest "peer node start" 3 hours ago Up 3 hours 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.example.com
0622660b60cd hyperledger/fabric-peer:latest "peer node start" 3 hours ago Up 3 hours 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.example.com
5b6d636ab4ed hyperledger/fabric-peer:latest "peer node start" 3 hours ago Up 3 hours 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.example.com
9c4da795db8c hyperledger/fabric-orderer:latest "orderer" 3 hours ago Up 3 hours 0.0.0.0:7050->7050/tcp orderer.example.com
使用node初始化链码会慢点,因为会先去安装相关镜像。不过一旦在channel初始化过后,我们不再需要
-l
指定语言,只要告诉channel对应链码的名称即可。
实例化链上代码主要是在Peer所在的机器上对前面安装好的链上代码进行包装,生成对应Channel的Docker镜像和Docker容器。并且在实例化时我们可以指定背书策略。
1.9.5. 查询
让我们查询一下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"]}'
2018-03-19 13:05:36.337 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-19 13:05:36.337 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
Query Result: 100
2018-03-19 13:05:36.342 UTC [main] main -> INFO 003 Exiting.....
1.9.6. 发起交易
现在让我们从a
账户转10
到b
账户。这个交易将创建一个新的区块并更新state DB
。调用语法如下:
# be sure to set the -C and -n flags appropriately
peer chaincode invoke -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 -c '{"Args":["invoke","a","b","10"]}'
2018-03-19 13:08:24.534 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-19 13:08:24.534 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2018-03-19 13:08:24.540 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200
2018-03-19 13:08:24.541 UTC [main] main -> INFO 004 Exiting.....
1.9.7. 查询
让我们确认下我们之前的调用被正确地执行了。我们初始化了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"]}'
2018-03-19 13:11:16.917 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-19 13:11:16.917 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
Query Result: 90
2018-03-19 13:11:16.923 UTC [main] main -> INFO 003 Exiting.....
1.9.8. 在另一个节点上查询
前面的操作都是在org1
下面做的,那么处于同一个区块链(同一个Channel下)的org2
,是否会看到org1
的更改呢?我们试着给peer0.org2
安装链上代码:
先切换到peer0.org2
节点:
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
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
安装链码:
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
2018-03-19 13:16:41.437 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-19 13:16:41.437 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2018-03-19 13:16:41.621 UTC [main] main -> INFO 003 Exiting.....
由于mycc
已经在前面org1
的时候实例化了,也就是说对应的区块已经生成了,所以在org2
不能再次初始化。我们直接运行查询命令:
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
2018-03-19 13:18:57.513 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-19 13:18:57.513 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
Query Result: 90
2018-03-19 13:19:07.488 UTC [main] main -> INFO 003 Exiting.....
可以看到依然可以进行查询,且值都是同步的。
这里稍微比之前的查询花费的时间久点,这是因为
peer0.org2
也需要生成Docker镜像,创建对应的容器,才能通过容器返回结果。我们回到终端,执行docker ps,可以看到又多了一个容器:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
927a7a89671d dev-peer0.org2.example.com-mycc-1.0-15b571b3ce849066b7ec74497da3b27e54e0df1345daff3951b94245ce09c42b "chaincode -peer.add…" 2 minutes ago Up 2 minutes dev-peer0.org2.example.com-mycc-1.0
d2fbe3a12558 dev-peer0.org1.example.com-mycc-1.0-384f11f484b9302df90b453200cfb25174305fce8f53f4e94d45ee3b6cab0ce9 "chaincode -peer.add…" 2 hours ago Up 2 hours dev-peer0.org1.example.com-mycc-1.0
45b3f41ae42f hyperledger/fabric-tools:latest "/bin/bash" 5 hours ago Up 5 hours cli
26dadcb90749 hyperledger/fabric-peer:latest "peer node start" 5 hours ago Up 5 hours 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.example.com
053b317fc840 hyperledger/fabric-peer:latest "peer node start" 5 hours ago Up 5 hours 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.example.com
0622660b60cd hyperledger/fabric-peer:latest "peer node start" 5 hours ago Up 5 hours 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.example.com
5b6d636ab4ed hyperledger/fabric-peer:latest "peer node start" 5 hours ago Up 5 hours 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.example.com
9c4da795db8c hyperledger/fabric-orderer:latest "orderer" 5 hours ago Up 5 hours 0.0.0.0:7050->7050/tcp orderer.example.com
1.9.9. 幕后发生了什么?
-
script.sh
脚本被拷贝到CLI容器中。这个脚本驱动了使用提供的channel name
以及信道配置的channel.tx
文件的createChannel
命令。 -
createChannel
命令的产出是一个创世区块-<your_channel_name>.block
-这个创世区块被存储在peer
节点的文件系统中同时包含了在channel.tx
的信道配置。 -
joinChannel
命令被4个peer
节点执行,作为之前产生的genesis block
的输入。这个命令介绍了peer
节点加入<your_channel_name>
以及利用<your_channel_name>.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.org1.example.com
被实例化。实例化过程将链码添加到信道上,并启动peer节点对应的容器,并且初始化和链码服务有关的键值对。示例的初始化的值是[”a“,”100“,”b“,”200“]。实例化的结果是一个名为dev-peer0.org1.example.com-mycc-1.0
的容器启动了。 -
实例化过程同样为背书策略传递相关参数。策略被定义为-P "OR ('Org1MSP.member','Org2MSP.member')",意思是任何交易必须被Org1或者Org2背书。
-
一个针对
a
的查询发往peer0.org1.example.com。链码服务已经被安装在了peer0.org1.example.com。查询的结果也将被返回。没有写操作出现,因此查询的结果的值将为100。 -
一次交易被发往peer0.org1.example.com,从a转移10到b。
-
一个查询请求被发往
peer0.org1.example.com
用于查询a的值。返回a的值为90,正确地反映了之前的交易,a的值被转移了10。 -
在
peer0.org2.example.com
安装链码,用于查询a的值。一个名为dev-peer0.org2.example.com-mycc-1.0
的容器启动了。返回a的值为90,正确地反映了之前的交易,a的值被转移了10。
1.9.10. 这指明了什么?
为了能够正确地在账本上进行读写操作,链码服务必须被安装在peer节点上。此外,每个peer节点的链码服务的容器除了init
或者传统的交易-读/写-针对该链码服务执行(例如查询a的值),在其他情况下不会启动。交易导致容器的启动。当然,所有信道中的节点都持有以块的形式顺序存储的不可变的账本精确的备份,以及状态数据库来保存前状态的快照。这包括了没有在其上安装链码服务的peer节点(例如peer1.org1.example.com
和peer1.org2.example.com
)。最后,链码在被安装后将是可达状态,因为它已经被实例化了(例如peer0.org2.example.com
)。
1.9.11. 我如何查询这些交易?
检查CLI容器的日志。
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 peer1.org2 on channel 'mychannel' is successful =====================
===================== All GOOD, BYFN execution completed =====================
_____ _ _ ____
| ____| | \ | | | _ \
| _| | \| | | | | |
| |___ | |\ | | |_| |
|_____| |_| \_| |____/
你可以滚动这些日志来查看各种交易。
1.9.12. 我如何查看链码日志?
检查每个独立的链码服务容器来查看每个容器内的分隔的交易。下面是每个链码服务容器的日志的组合:
$ 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"}
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
之外,请遵循前面提到的生成配置文件的过程:
使用CouchDB启动网络:
docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml up -d
Creating couchdb0 ... done
Creating couchdb3 ... done
Creating couchdb2 ... done
Creating couchdb1 ... done
aml -f docker-compose-couch.yaml up -d
Starting orderer.example.com ...
Creating couchdb3 ...
Creating couchdb2 ...
Creating couchdb0 ...
Starting orderer.example.com ... done
Recreating peer0.org1.example.com ...
Recreating peer1.org2.example.com ...
Recreating peer0.org2.example.com ... done
Recreating peer1.org1.example.com ... done
Recreating cli ... done
注意:如果你选择将fabric-couchdb容器端口映射到主机端口,请确保你意识到了安全性的影响。在开发环境中映射端口可以使CouchDB REST API可用,并允许通过CouchDB Web界面(Fauxton)对数据库进行可视化。生产环境将避免端口映射,以限制对CouchDB容器的外部访问。
你可以使用上面列出的步骤使用CouchDB来执行chaincode_example02,然而为了执行CouchDB的查询能力,你将需要使用被格式化为JSON的数据(例如marbles02)。你可以在fabric/examples/chaincode/go
目录中找到marbles02
链码。
使用docker ps
查看容器,可以看到除了1Orderer+4Peer+1CLI启动外,还启动了四个couchdb
:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
608e57b03072 hyperledger/fabric-tools:latest "/bin/bash" 14 minutes ago Up 14 minutes cli
2de36bd8f69a hyperledger/fabric-peer:latest "peer node start" 14 minutes ago Up 14 minutes 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.example.com
4f849ff5a190 hyperledger/fabric-peer:latest "peer node start" 14 minutes ago Up 14 minutes 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.example.com
de8fac9c70ab hyperledger/fabric-peer:latest "peer node start" 14 minutes ago Up 14 minutes 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.example.com
92928b49f85f hyperledger/fabric-peer:latest "peer node start" 14 minutes ago Up 14 minutes 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.example.com
cb9dd19c38a3 hyperledger/fabric-couchdb "tini -- /docker-ent…" 14 minutes ago Up 14 minutes 4369/tcp, 9100/tcp, 0.0.0.0:6984->5984/tcp couchdb1
c34ca64ffd3e hyperledger/fabric-couchdb "tini -- /docker-ent…" 14 minutes ago Up 14 minutes 4369/tcp, 9100/tcp, 0.0.0.0:8984->5984/tcp couchdb3
8a6b82e9b7a4 hyperledger/fabric-couchdb "tini -- /docker-ent…" 14 minutes ago Up 14 minutes 4369/tcp, 9100/tcp, 0.0.0.0:7984->5984/tcp couchdb2
03c634c23010 hyperledger/fabric-couchdb "tini -- /docker-ent…" 14 minutes ago Up 14 minutes 4369/tcp, 9100/tcp, 0.0.0.0:5984->5984/tcp couchdb0
9c4da795db8c hyperledger/fabric-orderer:latest "orderer" 21 hours ago Up 14 minutes 0.0.0.0:7050->7050/tcp orderer.example.com
进入CLI容器:
yuyangdeMacBook-Pro:first-network yuyang$ docker exec -it cli bash
切换节点到peer0.org1
:
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
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
安装marbles02
链码:
peer chaincode install -n marbles -v 1.0 -p github.com/chaincode/marbles02/go
设置下channel变量:
export CHANNEL_NAME=mychannel
实例化链码:
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 marbles -v 1.0 -c '{"Args":["init"]}' -P "OR ('Org0MSP.peer','Org1MSP.peer')"
2018-03-20 05:11:55.424 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-03-20 05:11:55.424 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2018-03-20 05:12:06.480 UTC [main] main -> INFO 003 Exiting.....
再次查看容器,发现一个新的docker产生了:
dev-peer0.org1.example.com-marbles-1.0
对数据进行增删改查:
peer chaincode invoke -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 marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'
peer chaincode invoke -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 marbles -c '{"Args":["initMarble","marble2","red","50","tom"]}'
peer chaincode invoke -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 marbles -c '{"Args":["initMarble","marble3","blue","70","tom"]}'
peer chaincode invoke -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 marbles -c '{"Args":["transferMarble","marble2","jerry"]}'
peer chaincode invoke -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 marbles -c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'
peer chaincode invoke -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 marbles -c '{"Args":["delete","marble1"]}'
如果你选择在docker-compose文件中映射你的CouchDB的端口,那么你可以通过CouchDB Web界面(Fauxton)通过打开浏览器输入下列URL:http://localhost:5984/_utils查看数据的变化。
你可以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":"c2b0db43718f40c0d787ebfe298663c4db38257271fba851629aa12fda2d9c71", "Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}, "Timestamp":"2018-03-20 05:30:40.843542497 +0000 UTC", "IsDelete":"false"},{"TxId":"ea869117344ca23a3ee2f768a26added28eb23f46e66a54e3e750224fda6caea", "Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"jerry"}, "Timestamp":"2018-03-20 05:34:14.029638632 +0000 UTC", "IsDelete":"false"},{"TxId":"93242722cfb605a46df083ad3111b34415624d07b3234963dcd58ca47ebd87ef", "Value":null, "Timestamp":"2018-03-20 05:35:48.618124625 +0000 UTC", "IsDelete":"true"}]
你还可以对数据内容执行丰富的查询,例如通过拥有者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. 为何使用CouchDB
CouchDB is a kind of NoSQL solution. It is a document oriented database where document fields are stored as key-value mpas. Fields can be either a simple key/value pair, list, or map. In addition to keyed/composite-key/key-range queries which are supported by LevelDB, CouchDB also supports full data rich queries capability, such as non-key queries against the whole blockchain data, since its data content is stored in JSON format and fully queryable. Therefore, CouchDB can meet chaincode, auditing, reporting requirements for many use cases that not supported by LevelDB.
CouchDB can also enhance the security for compliance and data protection in the blockchain. As it is able to implement field-level security through the filtering and masking of individual attributes within a transaction, and only authorizing the read-only permission if needed.
In addition, CouchDB falls into the AP-type (Availability and Partition Tolerance) of the CAP theorem. It uses a master-master replication model with Eventual Consistency
. More information can be found on the Eventual Consistency page of the CouchDB documentation. However, under each fabric peer, there is no database replicas, writes to database are guaranteed consistent and durable (not Eventual Consistency
).
CouchDB is the first external pluggable state database for Fabric, and there could and should be other external database options. For example, IBM enables the relational database for its blockchain. And the CP-type (Consistency and Partition Tolerance) databases may also in need, so as to enable data consistency without application level guarantee.
怕翻译的不准确,误人子弟。下面的内容是我在其他地方摘取的,可以了解一下:
状态数据库包括LevelDB和CouchDB。LevelDB是嵌入在peer进程中的默认键/值状态数据库,CouchDB是一个可选的外部状态数据库。与LevelDB键/值存储一样,CouchDB可以存储任何以chaincode建模的二进制数据(CouchDB附件函数在内部用于非json二进制数据)。但是,当chaincode值(例如,资产)被建模为JSON数据时,作为JSON文档存储,CouchDB支持对chaincode数据进行丰富的查询。
LevelDB和CouchDB都支持核心chaincode操作,例如获取和设置一个键(资产),并根据键进行查询。键可以通过范围查询,可以对组合键进行建模,以支持针对多个参数的等价查询。例如,作为所有者的组合键,资产id可以用于查询某个实体拥有的所有资产。这些基于key的查询可以用于针对账本的只读查询,以及更新总账的事务。
如果将资产建模为JSON并使用CouchDB,那么就可以使用chaincode中的CouchDB JSON查询语言对chaincode数据值执行复杂的富查询,这些类型的查询对于理解账本上的内容很有帮助。对于这些类型的查询,事务协议响应通常对客户端应用程序有用,但通常不会作为事务提交到排序服务。事实上,也无法保证结果集在chaincode执行与富查询提交时间之间的稳定性,因此使用富查询的结果去执行最终的事务更新操作是不合适的,除非可以保证结果集在chaincode执行时间与提交时间之间的稳定性,或者可以处理在后续交易中的潜在变化。例如,如果对Alice所拥有的所有资产执行一个富查询并将其传输给Bob,那么一个新的资产可能会被另一个事务分配给Alice,这是在chaincode执行时间和提交时间之间的另一个事务,可能此过程中会错过这个“虚值”。
CouchDB作为一个独立的数据库进程与peer一起运行,因此在设置、管理和操作方面有额外的考虑。我们可以考虑从默认的嵌入式LevelDB开始,如果需要额外的复杂的富查询,可以转移到CouchDB。将chaincode资产数据建模为JSON是一种很好的做法,这样我们就可以在将来执行需要的复杂的富查询。
1.13. 关于数据持久化的提示
如果需要在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.14. 故障排除
- 始终保持你的网络是全新的。使用以下命令来移除之前生成的artifacts,crypto,containers以及chaincode images:
./byfn.sh -m down
- 如果提示以下错误:
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}')
- 如果提示以下错误:
[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
。
参考
原文:深入理解Fabric环境搭建的详细过程
作者:深蓝