官方文档及网上的文章一般都是介绍如何使用Docker搭建第一个Fabric网络,而且搭建的第一个网络就有四个Peer节点,这样过于复杂,而且下载一大堆Docker镜像,也比较费时间。因此,我这节将首先介绍如何根据fabric源码编译出可执行程序,然后介绍如何直接用这些可执行程序搭建单个Peer节点的网络。
本节相关操作是在64位 Ubuntu 16.04下进行的,开始本节之前,请确保你已经按照了golang 1.9.2,并且已经配置了GO相关的环境变量,例如,我机器的go环境如下:
编译Fabric源码
下载源码
在$GOPATH下建立目录结构src/github.com/hyperledger,切换到此目录后下载fabric 1.1.0-preview版源码:
mkdir -p $GOPATH/src/github.com/hyperledger
cd $GOPATH/src/github.com/hyperledger
wget https://github.com/hyperledger/fabric/archive/v1.1.0-preview.tar.gz
然后,解压文件并将解压后得到的目录重命名为fabric:
tar -zxvf v1.1.0-preview.tar.gz
mv fabric-1.1.0-preview fabric
编译可执行程序
开始编译之前,到fabric源码目录下执行make test-cmd
命令获取-ldflags选项的取值:
接下来,编译每个可执行程序时都要带上-ldflags选项。
为了方便可执行程序的管理及后续编写相关shell脚本,先在当前用户的home目录下建立如下目录结构
fabric-1.1.0-preview-demo(后续所有的例子都放在此目录下)
----bin(编译的可执行程序放在此目录下)
----networks(fabric网络相关配置在此目录下)
----chaincodes(链码相关源码放在此目录下)
相关脚本如下:
mkdir -p ~/fabric-1.1.0-preview-demo/bin
mkdir -p ~/fabric-1.1.0-preview-demo/networks
mkdir -p ~/fabric-1.1.0-preview-demo/chaincodes
cd ~/fabric-1.1.0-preview-demo/bin
切换到刚建立的bin目录下,就可以开始编译了。
编译orderer
Orderer用于处理交易的排队及共识。编译命令如下:
go build -gcflags "-N -l" -ldflags "-X github.com/hyperledger/fabric/common/metadata.Version=1.1.0-preview -X github.com/hyperledger/fabric/common/metadata.BaseVersion=0.4.2 -X github.com/hyperledger/fabric/common/metadata.BaseDockerLabel=org.hyperledger.fabric -X github.com/hyperledger/fabric/common/metadata.DockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.BaseDockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.Experimental=false" github.com/hyperledger/fabric/orderer
编译时加入-gcflags可以让编译器忽略一些优化,方便后面在调试可执行程序时观察变量及对应代码行号。
编译peer
go build -gcflags "-N -l" -ldflags "-X github.com/hyperledger/fabric/common/metadata.Version=1.1.0-preview -X github.com/hyperledger/fabric/common/metadata.BaseVersion=0.4.2 -X github.com/hyperledger/fabric/common/metadata.BaseDockerLabel=org.hyperledger.fabric -X github.com/hyperledger/fabric/common/metadata.DockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.BaseDockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.Experimental=false" github.com/hyperledger/fabric/peer
编译cryptogen
cryptogen用于生成交易签名及SSL通信用到证书及私钥对,可以使用fabric-ca或者其他ca颁发的证书,但测试环境使用crytogen更简单。编译命令如下:
go build -gcflags "-N -l" -ldflags "-X github.com/hyperledger/fabric/common/metadata.Version=1.1.0-preview -X github.com/hyperledger/fabric/common/metadata.BaseVersion=0.4.2 -X github.com/hyperledger/fabric/common/metadata.BaseDockerLabel=org.hyperledger.fabric -X github.com/hyperledger/fabric/common/metadata.DockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.BaseDockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.Experimental=false" github.com/hyperledger/fabric/common/tools/cryptogen
编译configtxgen
configtxgen用于生成Orderer的创世纪块及通道配置交易等内容。编译命令如下:
go build -gcflags "-N -l" -ldflags "-X github.com/hyperledger/fabric/common/metadata.Version=1.1.0-preview -X github.com/hyperledger/fabric/common/metadata.BaseVersion=0.4.2 -X github.com/hyperledger/fabric/common/metadata.BaseDockerLabel=org.hyperledger.fabric -X github.com/hyperledger/fabric/common/metadata.DockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.BaseDockerNamespace=hyperledger -X github.com/hyperledger/fabric/common/metadata.Experimental=false" github.com/hyperledger/fabric/common/tools/configtxgen
上述编译都结束后,bin目录下就有了四个可执行程序:
可以使用chmod
命令给这些程序加上可执行权限,然后可以使用version
子命令查看相应程序的版本:
搭建单个Peer节点网络
本节将演示如何搭建一个Peer节点和一个Orderer节点的网络。开始之前,先在~/fabric-1.1.0-preview-demo/networks下建立如下结构的目录:
single-dev-env
--config(存放配置文件)
----channel-artifacts(存放后面要生成的通道配置交易等文件)
shell命令:
mkdir -p ~/fabric-1.1.0-preview-demo/networks/single-dev-env/config/channel-artifacts
Fabric中搭建一个网络需要如下步骤:
1. 使用cryptogen生成网络需要的证书。
2. 使用configtxgen生成Orderer的创世纪块
3. 使用configtxgen创建通道配置交易
4. 使用peer cli创建及加入通道
注意:如果没有特殊说明,接下来的命令均是在~/fabric-1.1.0-preview-demo目录下执行的。
生成证书
使用cryptogen生成网络需要的证书,需指定配置文件,参考官方fabric-samples的first-network的crypto-config.yaml文件,编写只有一个orderer和一个peer的配置文件crypto-config.yaml(放在single-dev-env/config目录下):
OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
PeerOrgs:
- Name: Org
Domain: example.com
Template:
Count: 1
Hostname: peer
Users:
Count: 1
export PATH=$(pwd)/bin:$PATH
cryptogen generate --config=networks/single-dev-env/config/crypto-config.yaml --output=networks/single-dev-env/config/crypto-config
fabric编译成的二进制文件都可以使用<命令> <命令> –help查看帮助。
命令执行成功后,networks/single-dev-env/config/crypto-config目录下生成了如下结构的证书目录:
orderer.example.com目录存放的是Orderer节点需要的证书,等会启动Orderer节点时需要指定msp目录:
peer.example.com目录存放的是Peer节点需要的证书,等会启动Peer节点时需要指定msp目录:
Admin@example.com
目录下的证书是Peer节点管理员证书,创建、加入通道、发送交易等操作都是用此目录下的证书进行签名,因此,我们使用peer cli时需要指定msp目录:
使用configtxgen生成Orderer的创世纪块
参考官方fabric-samples的first-network的configtx.yaml文件编写一个configtx.yaml文件(放在single-dev-env/config目录下):
Profiles:
SingleSoloOrdererGenesis:
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org
SingleSoloChannel:
Consortium: SampleConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org
Organizations:
- &OrdererOrg
Name: OrdererOrg
ID: OrdererMSP
MSPDir: crypto-config/ordererOrganizations/example.com/msp
- &Org
Name: OrgMSP
ID: OrgMSP
MSPDir: crypto-config/peerOrganizations/example.com/msp
AnchorPeers:
- Host: peer.example.com
Port: 7051
Orderer: &OrdererDefaults
OrdererType: solo
Addresses:
- orderer.example.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 10
AbsoluteMaxBytes: 99 MB
PreferredMaxBytes: 512 KB
Organizations:
Application: &ApplicationDefaults
Organizations:
sudo gedit /etc/hosts
修改/etc/hosts,追加上如下内容并保存:
127.0.0.1 example.com
127.0.0.1 orderer.example.com
127.0.0.1 peer.example.com
Fabric使用X.509证书作为节点及用户的身份,而证书一般优于域名管理,因此,这里做了域名映射。
设置FABRIC_CFG_PATH
环境变量告诉configtxgen去哪个目录寻找configtx.yaml文件,使用“生成证书”一节用到终端执行如下命令:
export FABRIC_CFG_PATH=$(pwd)/networks/single-dev-env/config/
使用上一小节用到终端执行如下命令:
configtxgen -outputBlock networks/single-dev-env/config/genesis.block -profile SingleSoloOrdererGenesis
本步骤及下一步中的-profile选项的取值均是与configtx.yaml文件内容对应。
命令执行成功后,networks/single-dev-env/config目录下就多了一个genesis.block文件:
使用configtxgen创建通道配置交易
使用上一小节用到终端执行如下命令::
export CHANNEL_NAME=mychannel
configtxgen -outputCreateChannelTx networks/single-dev-env/config/channel-artifacts/channel.tx -profile SingleSoloChannel -channelID $CHANNEL_NAME
命令执行成功后,networks/single-dev-env/config/channel-artifacts目录下多了一个channel.tx文件。
启动Orderer和Peer节点
本小节是使用环境变量配置Orderer和Peer节点,其实也可以使用yaml文件配置,后面再介绍。
新打开一个终端,切换到~/fabric-1.1.0-preview-demo目录并设环境变量配置Orderer节点:
export rootDir=$(pwd)
export PATH=$rootDir/bin:$PATH
export ORDERER_GENERAL_LOGLEVEL=DEBUG
export ORDERER_GENERAL_TLS_ENABLED=false
export ORDERER_GENERAL_PROFILE_ENABLED=false
export ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
export ORDERER_GENERAL_LISTENPORT=7050
export ORDERER_GENERAL_GENESISMETHOD=file
export ORDERER_GENERAL_GENESISFILE=$rootDir/networks/single-dev-env/config/genesis.block
export ORDERER_GENERAL_LOCALMSPDIR=$rootDir/networks/single-dev-env/config/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp
export ORDERER_GENERAL_LOCALMSPID=OrdererMSP
export ORDERER_FILELEDGER_LOCATION=$rootDir/networks/single-dev-env/data/orderer
启动Orderer节点:
orderer
为了方便,可以把环境变量的设置及orderer启动的命令写在shell脚本中。
Orderer启动成功后,控制台输出如下:
再打开一个终端,切换到~/fabric-1.1.0-preview-demo目录并设环境变量配置Peer节点:
export rootDir=$(pwd)
export PATH=$rootDir/bin:$PATH
export CORE_PEER_ID=example_org
export CORE_CHAINCODE_MODE=dev
export CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
export CORE_PEER_NETWORKID=dev
export CORE_LOGGING_LEVEL=INFO
export CORE_PEER_TLS_ENABLED=false
export CORE_PEER_PROFILE_ENABLED=false
export CORE_PEER_ADDRESS=0.0.0.0:7051
export CORE_PEER_LISTENADDRESS=0.0.0.0:7051
export CORE_PEER_GOSSIP_ENDPOINT=0.0.0.0:7051
export CORE_PEER_EVENTS_ADDRESS=0.0.0.0:7053
export CORE_PEER_LOCALMSPID=OrgMSP
export CORE_LEDGER_STATE_STATEDATABASE=goleveldb
export CORE_PEER_MSPCONFIGPATH=$rootDir/networks/single-dev-env/config/crypto-config/peerOrganizations/example.com/peers/peer.example.com/msp
export CORE_PEER_FILESYSTEMPATH=$rootDir/networks/single-dev-env/data/peer
启动:
peer node start -o 127.0.0.1:7050 --peer-chaincodedev=true
Peer启动成功后,控制台输出如下:
创建通道
新打开一个终端,切换到~/fabric-1.1.0-preview-demo目录并设环境变量:
export rootDir=$(pwd)
export PATH=$rootDir/bin:$PATH
export CHANNEL_NAME=mychannel
export CORE_CHAINCODE_MODE=dev
export CORE_PEER_ID=peer-cli
export CORE_PEER_ADDRESS=peer.example.com:7051
export CORE_PEER_LOCALMSPID=OrgMSP
export CORE_PEER_MSPCONFIGPATH=$rootDir/networks/single-dev-env/config/crypto-config/peerOrganizations/example.com/users/Admin@example.com/msp
这些环境变量以后操作时经常用到,可以保存在一个文本文件中,新开终端就使用
source <文件名>
使其生效。
peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f $rootDir/networks/single-dev-env/config/channel-artifacts/channel.tx
执行成功后,当前目录生成了一个mychannel.block文件:
加入通道
在上一小节使用到终端执行如下命令:
peer channel join -b $CHANNEL_NAME.block
执行成功后,终端输入日志:
启动Peer节点的终端打印日志:
从日志中可以看到,节点加入通道后会创建一个通道对应的账本并在通道上部署系统链码。
部署及调用官方链码示例
$GOPATH/src/github.com/hyperledger/fabric/examples/chaincode目录下有官方提供的各种语言的链码示例,本节将演示如何部署及调用一个简单的go语言示例——fabric/examples/chaincode/go/chaincode_example02。
链码部署及调用过程如下:
1. 启动链码对应的程序;
2. 部署链码,分install和instantiate两个阶段。其中,install将链码源码打包成链码部署文件(CDS)并上传到Peer节点,instantiate将根据CDS生成链码对应的Docker容器,启动容器并执行链码的实例化操作,一般是调用链码的init方法。这里描述的instantiate是使用“network”模式运行链码,而我们此次测试使用的是“dev”模式,由启动Peer节点时设置的CORE_CHAINCODE_MODE环境变量的值决定的。
3. 调用链码,分invoke和query两种操作。invoke是写操作,可修改账本,能产生块,而query是读操作,不写账本,也不生成块。
启动链码
打开一个终端,执行如下命令:
export CORE_CHAINCODE_ID_NAME=mycc:1.0
export CORE_PEER_ADDRESS=peer.example.com:7052
go run $GOPATH/src/github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02/chaincode_example02.go
启动成功后,控制台输出如下:
这里使用go run
命令直接运行的,当然,也可以先使用go build -gcflags "-N -l" github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
将链码编译成可执行程序再运行。
部署链码
新打开一个终端,切换到~/fabric-1.1.0-preview-demo目录,首先参照“创建通道”一节设置环境变量。
第1步:install链码:
peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
选项说明:
- -n:指定链码名称
- -v:链码版本
- -p:链码源码所在目录
终端输出:
peer节点账本目录的chaincodes目录下生成了一个文件,其名称是由链码名称和版本号组成的。
第2步:instantiate链码
peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc -v 1.0 -c "{"Args":["init","a", "100", "b","200"]}" -P "OR('OrgMSP.member')"
选项说明:
- -o:orderer节点的地址
- -C(大写):通道名称
- -n:链码名称
- -v:链码版本
- -c(小写):链码实例化时调用的方法及参数,JSON字符串,Args数组的第0个元素是调用的方法,实例化时一般都是init方法,其他元素是方法的参数。
- -P(大写):背书策略,即交易由哪些Peer节点签名后才是合法的。以后再详细说明。
当前终端输出内容如下:
初始化了两个状态A和B,其初始值分别为100和200(启动链码的终端输出的内容):
此外,peer和orderer的终端也有相应的日志输出。
调用链码
本节的操作都是使用上一小节部署链码用的终端。
首先,使用invoke操作把A的余额转10到B:
peer chaincode invoke -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc -c "{"Args":["invoke","a","b","10"]}"
接下来,再使用query操作查询A的余额,发现确实少了10:
peer chaincode query -C $CHANNEL_NAME -n mycc -c "{"Args":["query","a"]}"