Pinvon's Blog

所见, 所闻, 所思, 所想

Fabric官方案例first-network

准备

注: 使用的版本为1.1.0

下载源代码

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

# 如果想切换到其他版本, 则输入以下命令. 我直接使用主分支的版本, 因此不切换
git checkout {TAG}

下载工具

想要运行这里的官方案例, 需要准备一些工具. 你可以使用脚本下载, 如果编译过Fabric的, 也可以选另一种方式, 因为如果编译过, 已经有很多镜像文件了, 不需要使用脚本重新下载.

使用脚本下载

可以直接执行以下命令:

# 能翻墙
curl -sSL https://goo.gl/6wtTN5 | bash -s 1.1.0

# 不能翻墙
curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh | bash -s 1.1.0

也可以直接到这个网站, 将脚本复制下来, 放在 fabric-samples 根目录下的 bootstrap.sh 中, 这个文件要自己创建.

# 让其可执行
chmod +x ./bootstrap.sh
./bootstrap.sh

编译过Fabric的

如果有根据编译所说的进行编译过, 可以先打开 bootstrap.sh, 在里面查找 dockerFabricPull() 方法, 将该方法以后(包括该方法)的内容全部注释. 添加两句:

echo ${ARCH}
echo ${VERSION}
./bootstrap.sh

查看输出, 根据输出的信息用浏览器到以下两个网站去下载. (终端的curl太慢了)

https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/

https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric-ca/hyperledger-fabric-ca/

下载完后解压到 fabric-samples/bin/ 目录下, 如果没有 bin目录, 则创建一个. 这里的工具, 有几个也可以在 fabric/build/docker/bin 里面找到, 直接复制过去也行.

一键运行

cd first-network

# 生成配置
./byfn.sh -m generate

# 启动网络
./byfn.sh -m up

# 关闭网络
./byfn.sh -m down

如果成功, 可以看到类似下面两个图中的内容.

9.png

10.png

手动运行

cryptogen工具

cryptogen工具为网络节点生成证书信息(x509证书). 这些证书是节点身份的代表, 它们使得我们可以在网络中交易时进行签名/验证身份.

cryptogen工具根据 crypto-config.yaml 文件里的配置进行工具, 这个文件包含了网络拓扑, 并且使得我们可以为Organizations和属于Organizations的节点生成证书与密钥. 每个Organization都有唯一的根证书(ca-cert), 它将组件(peers, orders)绑定到Organization. 通过为每个Organization颁发唯一的CA证书, 我们可以模仿一个典型的区块链网络, 这个网络中的成员将使用自己的数字证书获取授权. Hyperledger Fabric中的交易和通信, 都是通过存储在 keystore 中的实体的私钥签名, 然后使用公钥进行身份验证.

去掉 crypto-config.yaml 中的注释部分, 可以清楚地看清其内容.

sed '/#/d' crypto-config.yaml > cryp.yaml
emacs cryp.yaml

可以看到, 里面的内容如下:

11.png

crypto-config.yaml 中的 count 表示Organization中的 peer 的数量.

还要注意 OrdererOrgs 下的 Name, Domain, Specs 这些字段. 网络实体的命名规则为: {Hostname}.{Domain}

因此, Orderer节点的命名为 orderer.example.com, MSP ID为Orderer.

使用 cryptogen 生成的数字证书和密钥信息保存在 crypto-config 文件夹中.

configtxgen工具(配置交易生成器)

configtxgen会生成4个配置信息:

  1. orderer genesis block
  2. channel configuration transaction
  3. 两个 anchor peer transactions

其中, orderer block是Orderer Service的创世区块. Channel configuration transaction文件在Channel创建的时候广播给Order. Anchor peer transactions指定了每个Organization在此Channel上的代表节点.

configtxgen的配置文件是 configtx.yaml. 去掉其中的注释可以看得更清晰些.

该配置文件定义了3个成员: 一个Ordering Service组织(Organization) OrdererOrg, 两个节点组织 Org1 和 Org2, 每个组织由2个Peer组成. 每个组织还指定了Anchor Peer(peer0.org1.example.com和peer0.org2.example.com). 还为每个成员指定了MSP文件夹, 用来存储每个组织在orderer genesis block中指定的根证书, 有了这些证书, 任意和Ordering service通信的节点都可以对其数字签名进行验证.

使用工具

其实怎么去使用这些工具, 在一键式脚本 byfn.sh 中都有写明. 如根据 crypto-config.yaml 中的配置来生成用于相关数字证书, 可以查看 generateCerts() 中的写法.

手动执行, 可加深了解.

创建数字证书:

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

生成的数字证书存放在 crypto-config 文件夹中.

生成Ordering Service的创世区块: 设置环境变量 FABRIC_CFG_PATH, 告诉configtxgen工具, 要到哪里去寻找配置文件 configtx.yaml:

export FABRIC_CFG_PATH=${PWD}
../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

这样, Ordering Service的创世区块就生成了, 放在 channel-artifacts 目录中.

创建Channel配置交易:

export CHANNEL_NAME=mychannel  && ../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

创建Channel上Org1的anchor peer:

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

创建Channel上Org2的anchor peer:

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

到此为止, 我们有了Channel, Org1, Org2, Org1的anchor peer, Org2的anchor peer

启动网络

我们使用docker-compose脚本来启动网络, 该脚本使用之前下载的镜像文件, 通过 genesis.block 引导Orderer.

docker-compose -f docker-compose-cli.yaml up -d

如果不使用 -d, 则日志会实时显示, 这样需要新开一个终端做接下来的工作. 使用了 -d 就表示后台执行.

启动CLI容器, CLI容器主要用来发送一些管理命令.

docker start cli

设置环境变量

为了能在 peer0.org1.example.com 上执行下面的CLI命令, 需要先配置几个环境变量, 这些环境变量已经默认在CLI容器里设置好了, 可以直接使用. 但是, 如果想发送命令到其他的Peers或Orderer中使用, 则需要相应的设置对应的环境变量.

# 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

创建/加入Channel

我们可以使用configtxgen工具来创建Channel.

首先进行CLI容器:

docker exec -it cli bash

之前, 我们使用configtxgen创建了Channel的配置交易channel.tx, 现在将它作为参数传递给Orderer, 作为创建Channel请求的一部分.

创建Channel的命令包含一些参数. -c 表示Channel名字, -f 表示Channel配置交易, 这边是channel.tx, 当然你也可以挂载你自己的配置交易, 名字也可以不一样, --cafile 允许我们验证TLS握手, 参数为证书根路径.

注: 不使用SSL/TLS的HTTP通信, 就是不加密的通信. SSL/TSL协议的基本过程为:

  1. Client向Server索要并验证公钥
  2. 双方协商生成"对话密钥"
  3. 双方采用"对话密钥"进行加密

前两步就是握手阶段.

export CHANNEL_NAME=mychannel
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. 在CLI容器当前目录下, 会生成一个mychannel.block的区块. 如图所示:

18.png

将 peer0.org1.example.com 加到 channel 中:

peer channel join -b mychannel.block

如果要将其他节点加入Channel, 需要修改相应的环境变量. 这边以加入 peer0.org2.example.com 到Channel为例.

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

注意, 这会改掉原有的环境变量. 如果全部写在一行去执行, 则不会改变默认的环境变量.

关于Channel.

在Fabric中, Channel是很重要的概念. 一个Peer要想与另一个Peer发生交易, 必须处于同一个Channel中, 账本与Channel也是一对一的关系. Channel需要使用 peer channel ... 这样的命令进行维护.

create(Channel在Orderer结点内部): peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel.tx

join(加入一个Channel): peer channel join -b mychannel.block

update(更新channel的某Org的配置): peer channel update -o orderer.example.com:7050 -c mychannel -f ./Org1MSPanchors.tx

Channel分成System Channel和Application Channel. 通过 peer channel ... 命令维护的都是Application Channel. 对Application Channel发起维护命令的Peer节点, 必须是提交的配置文件中所配置的Org中的一员, 提交的配置文件一般为 channel.tx, mychannel.block, Org1MSPanchors.tx. 本质的意思是说, 该Peer节点要持有该组织所颁发的证书.

create, join, update 三个命令, 都使用了配置文件.

  1. channel.tx: 这是创建Application Channel的配置文件. channel.tx由configtxgen工具根据指定的profile从configtx.yaml中读取配置数据, profile指的是configtx.yaml中Profiles项下定义的某一个配置项. configtx.yaml文件规定了Channel中包含哪些组织, 创建Channel的命令会根据configtx.yaml生成配置信息, 导入到channel.tx中.
  2. mychannel.block: 它是Application Channel的创世区块. 通过 peer channel create 生成. channel.tx只是配置原型, 在create过程中, 会根据System Channel的配置进行详细填补, 最后生成一个block. 要想加入Application Channel, 就要先获取这个Channel的genesis block.
  3. Org1MSPanchors.tx: 更新组织的配置文件, 由configtxgen工具根据Org ID从configtx.yaml中指定的profile项生成. configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP 是指从configtx.yaml的Profiles下的TwoOrgsChannel项中获取Org ID为Org1MSP的组织的配置数据, 更新mychannel后, 把获取生成的配置数据导入到./Org1MSPanchors.tx文件中.

更新锚节点

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

将Org2的锚节点定义为 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 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的安装与初始化

应用程序通过Chaincode与BlockChain交互. 因此, 我们要在每个Peer上安装Chaincode来执行交易, 并在Channel中对其实例化.

将Go语言编写的Chaincode放在Peer的文件系统中:

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

将Node.js语言编写的Chaincode放在Peer的文件系统中:

peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/

然后, 在Channel上进行实例化. 实例化会先初始化Chaincode, 为Chaincode设置背书策略, 为目标Peer启动Chaincode容器. -P 参数指定了在Chaincode上, 一个交易被认可所需要的背书级别.

如果策略是 =-P "OR ('Org0MSP.peer','Org1MSP.peer')"=, 表示Org1或Org2中的Peer认可, 就认可该交易. 如果把OR改成AND, 就表示需要两个都认可, 交易才会被认可.

实例化Go语言的Chaincode:

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语言的Chaincode:

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 的值, 以确认Chaincode是否已实例化, state DB是否已填充. 查询的语法如下:

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

查询结果如下图所示:

19.png

调用

假设我们要从 a 里面减去10给 b.

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"]}'

此时, 再次查询 a 的值, 应该会从100变为90.

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

20.png

背后的原理

以上所说的步骤, 就是 script.sh 文件中的 ./byfn.sh up. 现在, 使用 ./byfn.sh down 来关闭它们.

总结一下, 主要步骤包含以下几点:

  1. script.sh 被拷贝到CLI容器中. script.sh 使用设定的Channel名字和Channel的配置文件 channel.tx 作为参数执行 createChannel 命令.
  2. createChannel 输出创世区块, 以 channel_name.block 命名. 该区块保存在Peer的文件系统里, 包含了 channel.tx 所指定的Channel的配置信息.
  3. joinChannel 被执行. 它使用 channel_name.block 作为输入, 将四个Peer节点加入到Channel, 并建立一个以 channel_name.block 为起始块的链.
  4. 现在的Channel = 2 * Org = 4 * Peer. 其中, Org1 = peer0.org1.example.com + peer1.org1.example.com, Org2 = peer0.org2.example.com + peer1.org2.example.com
  5. 将Org1MSPanchor.tx, Org2MSPanchor.tx, Channel作为参数给Ordering Service, 更新Org1MSP的Anchor Peer(peer0.org1.example.com)和Org2MSP的Anchor Peer(peer0.org2.example.com).
  6. 在peer0.org2.example.com上实例化Chaincode, 实例化过程将添加Chaincode到Channel上, 并启动Peer节点对应的容器, 初始化和Chaincode有关的键值对. 在这边, 初始化的值为["a", "100", "b", "200"]. 实例化后会启动一个名为 dev-peer0.org2.example.com-mycc-1.0 的容器.
  7. 实例化过程还会以背书策略为参数.
  8. 在peer0.org1.example.com上查询 a 的值. 之前, Chaincode已经安装在peer0.org1.example.com上了, 因此查询操作会启动一个名为 dev-peer0.org1.example.com-mycc-1.0 的容器.
  9. 在peer0.org1.example.com上执行转账.
  10. 发送查询到peer1.org2.example.com. 这时会启动第3个容器 dev-peer1.org2.example.com-mycc-1.0.

总结

要想对账本进行操作, 需要先在Peer上安装Chaincode. Chaincode容器在需要时(如查询)才会启动. Channel中每个Peer都有账本的副本, 存储了不可改变的, 序列化的记录区块和State Database用于保存当前的Fabric状态. 没有安装Chaincode的Peer也会同步账本.

查看日志

CLI容器的日志:

docker logs -f cli

Chaincode的日志:

docker logs dev-peer0.org2.example.com-mycc-1.0

CouchDB

状态数据库使用goleveldb或CouchDB. 默认为goleveldb.

数据持久化

如果要在Peer容器中进行数据持久化, 可以将docker容器内相应的目录挂载到宿主机器的一个目录中. 如, 添加下面的内容到 docker-compose-base.yaml 的Peer的约定中:

volumes:
 - /var/hyperledger/peer0:/var/hyperledger/production

Comments

使用 Disqus 评论
comments powered by Disqus