Fabric搭建及使用

我们需要先行安装这些软件:curl,docker,docker-compose,Go lang,Python,Node.js ,curl。

有些软件如果在系统中已经存在,且版本合适,则请略过相关步骤。

步骤概论:

1.下载fabric-sample代码curl -sSL http://bit.ly/2ysbOFE | bash -s 1.3.0

2.设置环境变量(如下)

export PATH=$PATH:~/fabric/fabric-samples/bin

3.运行byfn.sh up ,控制台打印“All GOOD, BYFN execution completed”,表示运行成功。

4.执行如下命令,返回成功。fabric-sample运行成功。

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

详细步骤及解析:

如无特殊说明,下文中所有命令都以 root 用户执行。如果为非 root 用户,可能有些命令需要加 sudo。

确认 Ubuntu 版本

cat / etc / issue

安装 curl

apt install curl

安装 docker 17.06.2-ce 或更高版本

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu
$(lsb_release -cs) stable"
apt update
apt install docker-ce
usermod -aG docker $(whoami)
docker --version

安装 docker-compose 1.14.0 或更高版本

curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version

安装 Go 语言 1.11.x 版本

curl -O https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.11.2.linux-amd64.tar.gz

设置 Go 语言环境变量

vi ~/.bashrc

在.bashrc 文件中增加以下内容

export PATH=$PATH:/usr/local/go/bin

执行 .bashrc 文件,使之生效

source ~/. bashrc

安装 Node.js 8.9.x 或更高版本

请注意 Fabric 目前不支持 Node.js 9.x 版本。

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

apt install nodejs

安装 Node.js 后,npm 应该被同时自动安装,执行以下命令确认。

npm install npm -g

安装 Python 2.7

apt install python

下载 fabric 可执行文件、示例与 docker 镜像

cd ~

mkdir fabric

cd fabric

curl -sSL http://bit.ly/2ysbOFE | bash -s 1.3.0

如果提示错误,请尝试使用 https:

curl -sSL https://bit.ly/2ysbOFE | bash -s 1.3.0

下载、安装成功后,当前目录下会增加一个 fabric-samples 目录。 并在 docker repository 中会增加一些 docker 镜像。可以通过 docker images 命令查看。

docker images

应该显示类似如下内容。

表 1. docker images 命令查看显示

REPOSITORYTAGIMAGE IDCREATEDSIZE
hyperledger/fabric-javaenv1.3.02476cefaf8335 weeks ago1.7GB
hyperledger/fabric-javaenvlatest2476cefaf8335 weeks ago1.7GB
hyperledger/fabric-ca1.3.05c6b20ba944f5 weeks ago244MB
hyperledger/fabric-calatest5c6b20ba944f5 weeks ago244MB
hyperledger/fabric-tools1.3.0c056cd9890e75 weeks ago1.5GB
hyperledger/fabric-toolslatestc056cd9890e75 weeks ago1.5GB
hyperledger/fabric-ccenv1.3.0953124d802375 weeks ago1.38GB
hyperledger/fabric-ccenvlatest953124d802375 weeks ago1.38GB
hyperledger/fabric-orderer1.3.0f430f581b46b5 weeks ago145MB
hyperledger/fabric-ordererlatestf430f581b46b5 weeks ago145MB
hyperledger/fabric-peer1.3.0f3ea63abddaa5 weeks ago151MB
hyperledger/fabric-peerlatestf3ea63abddaa5 weeks ago151MB
hyperledger/fabric-zookeeper0.4.13e62e0af391935 weeks ago1.39GB
hyperledger/fabric-zookeeperlateste62e0af391935 weeks ago1.39GB
hyperledger/fabric-kafka0.4.134121ea662c475 weeks ago1.4GB
hyperledger/fabric-kafkalatest4121ea662c475 weeks ago1.4GB
hyperledger/fabric-couchdb0.4.131d3266e01e645 weeks ago1.45GB
hyperledger/fabric-couchdblatest1d3266e01e645 weeks ago1.45GB
hyperledger/fabric-baseosamd64-0.4.13f0fe49196c405 weeks ago124MB

将 Fabric 可执行文件目录加入系统路径

vi ~/.bashrc

在. bashrc 文件中增加以下内容:

export PATH=$PATH:~/fabric/fabric-samples/bin

执行. bashrc 文件,使之生效。

source ~/.bashrc

现在,Fabric 必需的系统环境与软件就全部准备好了,我们就要开始下载、部署第一个 Fabric 区块链网络了。

-----------------------byfn自动启动过程 Start------------------------------------------------------------

区块链网络示例 byfn(Building Your First Network.)

first-network 是 hyperledger/fabric-samples 项目的一个区块链示例。该示例向我们展示了一个简单的区块链,以及如何在区块链网络上安装、实例化、查询、调用链码。

常用命令

first-network 提供了一个脚本 byfn.sh,供测试使用。其中,常用的命令有以下三个:

  • byfn.sh generate

生成 first-network 需要的所有证书、创世区块和三个配置交易。即本文“准备工作”一节的全部内容。一般地,先执行 byfn.sh generate,再执行 byfn.sh up

  • byfn.sh up

启动 first-network,并完成 end-to-end 测试。注意,在启动 first-network 之前,byfn.sh up 会检测目录 crypto-config/ 是否存在,该目录是由 byfn.sh generate 生成的,以此判断是否执行过 byfn.sh generate。如果该目录不存在,会先执行与 byfn.sh generate 等效的所有操作,即本文“准备工作”一节的全部内容。

所以,其实不先执行 byfn.sh generate,也可以直接执行 byfn.sh up。本文将采用此做法。

  • byfn.sh down

停止 first-network,删除所有 docker 容器、证书、创世区块和三个配置交易,删除由 byfn.sh up 生成的链码镜像。

byfn.sh up

直接执行 byfn.sh up

$ byfn.sh up

准备工作

生成证书

使用 cryptogen,依据 crypto-config.yaml 生成证书。

# byfn.sh
#  - networkUp
#    - generateCerts
cryptogen generate --config=./crypto-config.yaml

调用 ./ccp-generate.sh 脚本,为 org1 和 org2 生成 ccp 文件。

  • connection-org1.json
  • connection-org1.yaml
  • connection-org2.json
  • connection-org2.yaml

替换私钥

拷贝生成 docker-compose-e2e.yaml 配置文件,并替换 CA1_PRIVATE_KEYCA2_PRIVATE_KEY

# byfn.sh
#  - networkUp
#    - replacePrivateKey
cp docker-compose-e2e-template.yaml docker-compose-e2e.yaml
sed $OPTS "s/CA1_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml
sed $OPTS "s/CA2_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml

生成 artifacts

使用 cryptogen,依据 configtx.yaml 在目录 ./channel-artifacts 下生成 *genesis.block*、*channel.tx*、Org1MSPanchors.tx 和 *Org2MSPanchors.tx*。

# byfn.sh
#  - networkUp
#    - generateChannelArtifacts
# 生成创世区块 genesis.block
configtxgen -profile TwoOrgsOrdererGenesis -channelID $SYS_CHANNEL -outputBlock ./channel-artifacts/genesis.block
# 生成 channel.tx
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
# 生成 Org1MSPanchors.tx
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
# 生成 Org2MSPanchors.tx
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

初始化容器

使用 docker-compose 加载容器。

# byfn.sh
#  - networkUp
IMAGE_TAG=$IMAGETAG docker-compose ${COMPOSE_FILES} up -d 2>&1

以下容器运行成功。

IMAGE                             COMMAND           NAMES
hyperledger/fabric-tools:latest   "/bin/bash"       cli
hyperledger/fabric-peer:latest    "peer node start" peer0.org2.example.com
hyperledger/fabric-orderer:latest "orderer"         orderer.example.com
hyperledger/fabric-peer:latest    "peer node start" peer0.org1.example.com
hyperledger/fabric-peer:latest    "peer node start" peer1.org2.example.com
hyperledger/fabric-peer:latest    "peer node start" peer1.org1.example.com

初始化网络

调用 scripts/script.sh,完成 end-to-end 测试。

 ____    _____      _      ____    _____
/ ___|  |_   _|    / \    |  _ \  |_   _|
\___ \    | |     / _ \   | |_) |   | |
 ___) |   | |    / ___ \  |  _ <    | |
|____/    |_|   /_/   \_\ |_| \_\   |_|

Build your first network (BYFN) end-to-end test

...

引用 scripts/utils.sh

# scripts/script.sh
. scripts/utils.sh

创建 channel

使用 peer channel create,依据 *channel.tx*,创建 channel。

# scripts/script.sh
# - createChannel
docker exec cli peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA

加入 channel

循环调用 joinChannelWithRetry [scripts/utils.sh],使用 peer channel join,把 peer0.org1peer1.org1peer0.org2 和 peer1.org2 加入 channel。

# scripts/script.sh
# - joinChannel
#   - joinChannelWithRetry [scripts/utils.sh]
peer channel join -b $CHANNEL_NAME.block

更新 anchor peer

使用 peer channel update,依据 Org1MSPanchors.tx 和 *Org1MSPanchors.tx*,分别更新 peer0.org1 为 org1 的 auchor peerpeer0.org2 为 org2 的 auchor peer

# scripts/utils.sh
# - updateAnchorPeers
docker exec cli peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA 

安装链码

使用 peer chaincode install,将链码 chaincode_example02 安装到 peer0.org1 和 peer0.org2 上。

# scripts/utils.sh
# - installChaincode
docker exec cli peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH}

实例化链码

使用 peer chaincode instantiate,在 peer0.org2 上实例化链码 *chaincode_example02*。初始化账本上数据:{“a”:“100”,“b”:“200”}。规定背书策略:任何交易必须要有 org1 和 org2 节点的同时背书。

# scripts/utils.sh
# - instantiateChaincode
docker exec cli peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -l ${LANGUAGE} -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" 

查询链码

使用 peer chaincode query,在 peer0.org1 上查询 a 的值是 100。注意,之前是在 peer0.org2 上实例化的链码,现在 peer0.org1 上查询 a 成功,说明账本已经跨组织完成同步。

# scripts/utils.sh
# - chaincodeQuery
docker exec cli peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

调用链码

使用 peer chaincode invoke,在 peer0.org1 和 peer0.org2 上调用链码,实现 a 向 b 转让 10,即 a 减少10,b 增加 10。注意,之前在实例化链码时规定的背书策略是,任何交易必须要有 org1 和 org2 节点的同时背书,该调用符合此背书策略,所以交易可以成功。

# scripts/utils.sh
# - chaincodeInvoke
docker exec cli peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc $PEER_CONN_PARMS -c '{"Args":["invoke","a","b","10"]}'

安装链码

使用 peer chaincode install,将链码 chaincode_example02 安装到 peer1.org2 上。该操作是为下一步在 peer1.org2 上使用链码做准备。peer 想要使用链码,必须预先安装链码。

# scripts/utils.sh
# - installChaincode
docker exec cli peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH}

查询链码

使用 peer chaincode query,在 peer1.org2 上查询 a 的值是 90,说明账本同步成功。

# scripts/utils.sh
# - chaincodeQuery
docker exec cli peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

first-network 启动成功

========= All GOOD, BYFN execution completed ===========


 _____   _   _   ____
| ____| | \ | | |  _ \
|  _|   |  \| | | | | |
| |___  | |\  | | |_| |
|_____| |_| \_| |____/

-----------------------byfn自动启动过程 End-------------------------------------------------------------

byfn 启动过程详解

下面,我们开始按步骤详细解释这个示例的初始化与启动过程。这些详细步骤正是以 byfn.sh 为基础的。

准备

为保证一个干净的运行环境,我们需要先清理之间测试遗留下来的 docker 环境。

注意:本文假定当前系统完全为 Fabric 测试部署,没有其他 docker 容器、镜像存在或运行。如果系统中有其他容器、镜像,请不要执行以下命令,并选择执行适当的命令,以避免不当删除。

进入 first-network

cd fabric/fabric-samples/first-network

清理临时文件、docker 容器、镜像及网络

./byfn.sh down

设置环境变量

清单 1. 环境变量(pwd: 为据对路径 下的xxx/fabric/fabric-samples/bin)

export PATH=${PWD}/../bin:${PWD}:$PATH
export FABRIC_CFG_PATH=${PWD}
export CLI_TIMEOUT=10
export CLI_DELAY=3
export CHANNEL_NAME="mychannel"
export COMPOSE_FILE=docker-compose-cli.yaml
export COMPOSE_FILE_COUCH=docker-compose-couch.yaml
export COMPOSE_FILE_ORG3=docker-compose-org3.yaml
export LANGUAGE=golang
export IMAGETAG="latest"
export VERBOSE=true

显示更多

CHANNEL_NAME 是将要建立的 Blockchain Channel 的名字,默认值是 “mychannel”,也可以设置为你所期望的其他值,我们在以后的步骤中,一般会直接引用 $CHANNEL_NAME。

证书

我们利用 cryptogen 命令,生成这个区块链网络所需要的一系列证书及相关文件。证书是基于 PKI 体系的 x509 格式证书,内容包括:自签名 CA 根证书、节点 (Orderer、Peer) 证书、管理员证书、证书对应的私钥、TLS 证书等。

这个操作需要一个配置文件 crypto-config.yaml,示例中已经存在。crypto-config.yaml 中主要定义了:

  • 1 个 Orderer 节点:orderer.example.com,
  • 4 个 Peer 节点:peer0.org1.example.com, peer1.org1.example.com, peer0.org2.example.com, peer1.org2.example.com。

在此请注意 2 点:

  1. 执行前请先确认当前目录下没有 crypto-config 目录,如果已经存在,请先删除。
  2. cryptogen 仅用作测试目的,可以方便快速的生成网络所需要的所有节点的证书。但一般情况下,cryptogen 不应被用于生产环境。(我们会在以后讲解如何添加节点,加入 Channel,并处理相关证书问题。)

生成证书

./byfn.sh -m generate

证书目录结构

执行成功后,会在当前目录下增加一个新目录 crypto-config,包含有这个网络节点的证书、私钥等加密、签名相关元素。其基本结构如下:

图 1. 证书目录结构

在这个示例网络中,有一个 CA(Certificate Authority),即证书颁发机构,名字是 example.com。它给自己颁发了一个自签名根证书 ca.example.com-cert.pem,在生成的多个目录中都包含了这个 CA 根证书,而其他节点证书(除了 TLS 证书)都由此 CA 颁发。

我们查看其中的一个目录 crypto-config/ordererOrganizations/example.com/ca,有两个文件:

图 2. CA 自签名根证书与私钥

 

其中,ca.example.com-cert.pem 是 CA 自签名根证书。4a7d8de29b3597d425494254605833721a6f121d4941b1ca7611c1364c1436ff_sk 是根证书对应的私钥。

证书中的私钥文件名是随机产生,每次执行 cryptogen 都会产生不一样的文件名,在后续引用中请注意将之改成实际测试中产生的文件名

CA 根证书

显示证书内容

openssl x509 -in ca.example.com-cert.pem -text -noout

显示证书内容如下:

清单 2. CA 根证书内容

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            81:0a:27:99:0a:db:06:c0:34:0f:d1:c3:9a:d5:9f:ed
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=US, ST=California, L=San Francisco, O=example.com, CN=ca.example.com
        Validity
            Not Before: Oct  7 13:15:00 2018 GMT
            Not After : Oct  4 13:15:00 2028 GMT
        Subject: C=US, ST=California, L=San Francisco, O=example.com, CN=ca.example.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:49:52:eb:47:10:b5:ce:cb:8a:9e:d1:96:6e:92:
                    77:6d:f5:1f:53:a6:66:a0:98:be:a9:18:f5:53:e8:
                    fc:dd:1d:fc:58:88:0c:84:2d:40:2f:b6:93:94:54:
                    62:8b:26:df:d6:0f:ac:17:58:70:47:66:5a:14:de:
                    8d:f0:85:c2:11
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment, Certificate Sign, CRL Sign
            X509v3 Extended Key Usage:
                Any Extended Key Usage
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier:
                4A:7D:8D:E2:9B:35:97:D4:25:49:42:54:60:58:33:72:1A:6F:12:1D:49:41:B1:CA:76:11:C1:36:4C:14:36:FF
    Signature Algorithm: ecdsa-with-SHA256
         30:44:02:20:08:e5:a2:bb:54:bf:37:01:b4:60:c8:38:1f:4f:
         7c:0e:1a:21:45:31:a2:45:1f:35:03:3e:70:c4:6a:75:a0:2e:
         02:20:14:5a:e3:36:33:82:20:88:33:d0:e7:b3:33:e3:8e:07:
         a3:b7:eb:55:83:7c:ce:22:73:5c:c8:84:4b:94:7c:bd

显示更多

其中第六行:Signature Algorithm: ecdsa-with-SHA256。ECDSA 的全名是 Elliptic Curve DSA,即椭圆曲线 DSA。它是 Digital Signature Algorithm (DSA) 应用了椭圆曲线加密算法的变种。ecdsa-with-SHA256 使用 SHA256 算法对内容先计算摘要,再应用签名算法对摘要进行签名。

查看 Issuer 与 Subject,可以看到这是一个自签名证书。

PKI 相关内容是区块链技术中的一个重要环节,建议读者对 PKI 并结合 openssl 进行更详细的学习。这里顺便列举一些常用的 openssl 操作,以进一步理解证书体系。

从证书中提取公钥:

openssl x509 -pubkey -in ca.example.com-cert.pem -noout > pubkey.pem cat pubkey.pem

输出内容如下:

—– BEGIN PUBLIC KEY-----

MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESVLrRxC1zsuKntGWbpJ3bfUfU6Zm

oJi+qRj1U+j83R38WIgMhC1AL7aTlFRiiybf1g+sF1hwR2ZaFN6N8IXCEQ==

—– END PUBLIC KEY—–

为方便使用,将私钥文件复制一个文件名比较短的:

cp 4a7d8de29b3597d425494254605833721a6f121d4941b1ca7611c1364c1436ff_sk key.pem

从私钥中输出公钥部分:

openssl ec -in key.pem -pubout

输出内容如下:

read EC key

writing EC key

-----BEGIN PUBLIC KEY-----

MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESVLrRxC1zsuKntGWbpJ3bfUfU6Zm

oJi+qRj1U+j83R38WIgMhC1AL7aTlFRiiybf1g+sF1hwR2ZaFN6N8IXCEQ==

-----END PUBLIC KEY-----

可以看到,这里的内容与之前从证书中提取的公钥内容相同。

使用私钥对测试文件 origcont 进行签名 :

echo TheContent > origcont

openssl dgst -sha256 -hex -c -sign key.pem origcont

输出内容如下:

EC-SHA256(origcont)= 30:44:02:20:15:71:15:71:63:d2:0f:15:83:09:86:67:bb:5d:b6:a9:14:8c:ef:02:c1:6e:52:0e:29:ea:f6:5f:d8:66:4f:62:02:20:17:31:ce:d7:ea:c3:f5:d6:11:37:eb:ed:cd:1d:64:4d:34:ed:da:7c:91:0a:bb:a0:89:da:f1:50:3c:8c:1f:d2

因为有随机数影响,当重复执行命令时,我们可以看到每次输出结果都是不同的。

使用私钥对文件 origcont 进行签名:

openssl pkeyutl -sign -in origcont -inkey key.pem -out origcont.sig

使用公钥验证签名:

openssl pkeyutl -verify -in origcont -sigfile origcont.sig -inkey pubkey.pem -pubin

Orderer 节点管理员证书位于 crypto-config/ordererOrganizations/example.com/msp/admincerts。由 CA 颁发给 Admin@example.com。

查看 Admin@example.com 证书:

openssl x509 -in Admin@example.com-cert.pem -text -noout

显示证书内容如下:

清单 3. 管理员证书内容

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            5e:ca:17:c3:99:28:45:2a:65:eb:07:1e:f7:e7:ea:d7
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=US, ST=California, L=San Francisco, O=example.com, CN=ca.example.com
        Validity
            Not Before: Oct  7 13:15:00 2018 GMT
            Not After : Oct  4 13:15:00 2028 GMT
        Subject: C=US, ST=California, L=San Francisco, CN=Admin@example.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:0a:60:ba:17:9b:54:de:42:7b:82:7e:d5:0b:66:
                    6f:61:8e:de:8d:ab:d2:bc:3c:3f:2c:bb:49:f4:7a:
                    ef:4b:59:e4:74:15:c3:4a:39:db:6c:04:f8:98:64:
                    ef:dd:17:b7:68:9e:a1:f9:4d:a3:6b:66:5f:dc:44:
                    a7:18:db:34:e9
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier:
                keyid:4A:7D:8D:E2:9B:35:97:D4:25:49:42:54:60:58:33:72:1A:6F:12:1D:49:41:B1:CA:76:11:C1:36:4C:14:36:FF
    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:21:00:d6:de:4b:37:30:e6:be:b1:3f:b1:4b:11:0d:
         50:21:dd:d8:b4:59:4c:e8:09:a7:65:f4:eb:1c:e6:66:d8:5f:
         d9:02:20:42:1e:18:f0:cf:b6:79:9b:07:9a:3c:77:55:84:8f:
         b2:c4:2e:8a:dd:c9:7e:e2:2d:0d:ea:89:71:eb:b4:81:3f

显示更多

用根证书验证 A dmin@example.com 证书:

openssl verify -CAfile ../../ca/ca.example.com-cert.pem Admin@example.com-cert.pem

TLS 证书

TLS 证书是自签名证书,与之前的 CA 证书没有关系。位于 crypto-config/ordererOrganizations/example.com/msp/tlscacerts。 查看 TLS 证书:

openssl x509 -in tlsca.example.com-cert.pem -text

清单 4. TLS 证书内容

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            26:46:ff:f7:80:6a:97:8c:77:b4:0d:e3:8e:b7:de:8f
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=US, ST=California, L=San Francisco, O=example.com, CN=tlsca.example.com
        Validity
            Not Before: Oct  7 13:15:00 2018 GMT
            Not After : Oct  4 13:15:00 2028 GMT
        Subject: C=US, ST=California, L=San Francisco, O=example.com, CN=tlsca.example.com

显示更多

TLS 客户端证书

TLS 客户端证书是由 TLS CA 颁发给 Admin@example.com 的用于 TLS 连接的证书。位于 crypto-config/ordererOrganizations/example.com/users/Admin@example.com/tls。 查看 TLS 客户端证书:

openssl x509 -in client.crt -text -noout

显示内容如下(部分内容略):

清单 5. TLS 客户端证书内容

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            91:e8:d1:66:9e:1a:73:d8:32:0e:b4:84:3c:1f:eb:7a
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=US, ST=California, L=San Francisco, O=example.com, CN=tlsca.example.com
        Validity
            Not Before: Oct  7 13:15:00 2018 GMT
            Not After : Oct  4 13:15:00 2028 GMT
        Subject: C=US, ST=California, L=San Francisco, CN=Admin@example.com

显示更多

证书间的关系

如前文所述,cryptogen 命令一般情况下仅用作测试用途,在生产环境中应通过正式合法 CA 颁发证书。但 cryptogen 命令产生的示例证书及其间的关系仍然值得学习与研究。

下面试着通过图表的方式将 ordererOrganizations 目录下的证书及其部分关系展示出来。

  • 颜色相同的线条表示文件内容相同。
  • 红色箭头表示 CA 颁发证书给某个组织或个人。

图 3. 证书间关系

证书间关系

对于 peerOrganizations 目录下证书间的关系这里不再详述,读者可以自行按类似方法进行分析与理解。

Channel 文件

配置文件

以下操作将使用配置文件 configtx.yaml,其中定义了 TwoOrgsOrdererGenesis profile 与 TwoOrgsChannel profile。读者可以先行查看此文件内容,也可以留待生成 channel 文件后再对比分析。其中部分内容如下:

清单 6. configtx.yaml

Profiles:
    TwoOrgsOrdererGenesis:
        <<: *ChannelDefaults
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
            Capabilities:
                <<: *OrdererCapabilities
        Consortiums:
            SampleConsortium:
                Organizations:
                    - *Org1
                    - *Org2
    TwoOrgsChannel:
        Consortium: SampleConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1
                - *Org2
            Capabilities:
                <<: *ApplicationCapabilities

显示更多

生成创世区块 Genesis Block

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

显示更多

生成的文件位于目录 channel-artifacts 下,可以通过以下命令将 Block 详细内容导入到 json 文件方便查看:

configtxgen -inspectBlock channel-artifacts/genesis.block > genesis.block.json

其结构大概如下:

图 4. genesis.block.json

genesis.block.json

在 genesis.block 中也包含了相关的证书内容,如下面这段内容:

"values":
       { "MSP": {
                 "mod_policy": "Admins",
                 "value": {
                         "config": {
                                  "admins": [
                                             "LS0tLS1CRUdJTi<此处省略>RJRklDQVRFLS0tLS0K"
                                           ],

显示更多

“admins” 对应的字符串即为经过 base64 编码的 Admin@org1.example.com 证书。我们可以通过以下命令查看它:

echo LS0tLS1CRUdJTi<此处省略>RJRklDQVRFLS0tLS0K| base64 -d > test.pem
openssl x509 -in test.pem -text -noout

显示更多

生成其他 Channel 文件

生成 Channel 配置 Transaction:

configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

生成锚节点配置 Transaction for Org1:

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

生成锚节点配置 Transaction for Org2:

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

可以通过以下命令将 transaction 导出到 JSON 文件进行查看:

configtxgen -inspectChannelCreateTx channel.tx > channel.tx.json
configtxgen -inspectChannelCreateTx Org1MSPanchors.tx > Org1MSPanchors.tx.json
configtxgen -inspectChannelCreateTx Org2MSPanchors.tx > Org2MSPanchors.tx.json

显示更多

这三个文件的结构类似,但与 genesis.block 的结构很大不同。前者是交易,而后者是 block。

部署示例区块链网络

启动网络将使用三个配置文件,且它们有继承、扩展关系如下:

COMPOSE_FILE = docker-compose-cli.yaml
               base/docker-compose-base.yaml
               base/peer-base.yaml

显示更多

peer-base.yaml

这个配置文件中定义了将要启动的 container 所使用的镜像 image,并且定义了 container 启动后自动执行的命令。

docker-compose-base.yaml

这个配置文件里定义了 5 个 container:orderer.example.com,peer0.org1.example.com,peer1.org1.example.com,peer0.org2.example.com,peer1.org2.example.com。其中,4 个 peer node 的配置继承自 peer-base.yaml, 如:

peer0.org1.example.com:
            container_name: peer0.org1.example.com
            extends:
              file: peer-base.yaml
              service: peer-base

显示更多

而 orderer.example.com 则是单独的定义,并会执行不同的命令 command: orderer。

docker-compose-cli

这个配置文件扩展了 docker-compose-base.yaml 中的内容,并指定了 docker container 所加入的网络 networks: – byfn。而且又启动了一个 container cli, 这是一个 fabric 工具集 docker container。后续中,我们会经常登录这个 container 以与 fabric 进行交互、操作。

启动区块链网络

docker-compose -f $COMPOSE_FILE up -d 2>&1

启动后,执行 docker ps 可以看到 6 个 docker container。

docker ps --format "{{.ID}}\t{{.Command}}\t {{.Image}}\t{{.Names}}"

显示结果类似:

109b538211e2 "/bin/bash"       hyperledger/fabric-tools:latest   cli
d6ca0ef10248 "peer node start" hyperledger/fabric-peer:latest    peer0.org2.example.com
b5143e44f87d "orderer"         hyperledger/fabric-orderer:latest orderer.example.com
4618ecb778cf "peer node start" hyperledger/fabric-peer:latest    peer1.org1.example.com
563244de0109 "peer node start" hyperledger/fabric-peer:latest    peer0.org1.example.com
23dc569f214e "peer node start" hyperledger/fabric-peer:latest    peer1.org2.example.com

显示更多

登录 cli Container

docker exec -it cli bash

登录成功后进入 cli container, 请注意之后的操作如无特别说明, 均 为于 container 内执行命令。

如果退出 container, 再 次登入时,下面的”准备环境变量 ” 操作必须再执行一次。

清单 7. 准备环境变量

export CHANNEL_NAME="mychannel"
export LANGUAGE=golang

export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

export PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export ORG1_MSP=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export ORG2_MSP=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp

export CC_SRC_PATH="github.com/chaincode/chaincode_example02/go/"

export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=$ORG1_MSP
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051

显示更多

这些环境变量将在执行 peer 命令时被使用,这些值也可以以参数的形式直接传递给 peer command。具体请查看 Fabric Commands 文档。

创建 Channel

peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f
./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA

显示更多

执行成功后后,会在当前目录增加文件 mychannel.block,即<$CHANNEL_NAME>.block,可以通过以下命令查看 block 文件内容:

configtxgen -inspectBlock mychannel.block

加入 Channel

将之前在配置文件中定义的 4 个节点加入 channel。

清单 8. 将节点加入 channel

# joinChannelWithRetry 0 1
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=$ORG1_MSP
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
peer channel join -b $CHANNEL_NAME.block

# joinChannelWithRetry 1 1
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=$ORG1_MSP
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
peer channel join -b $CHANNEL_NAME.block

# joinChannelWithRetry 0 2
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=$ORG2_MSP
export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
peer channel join -b $CHANNEL_NAME.block

# joinChannelWithRetry 1 2
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=$ORG2_MSP
export CORE_PEER_ADDRESS=peer1.org2.example.com:7051
peer channel join -b $CHANNEL_NAME.block

显示更多

更新 Anchor Peer

清单 9. 更新 Anchor Peer

# updateAnchorPeers 0 1
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=$ORG1_MSP
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA

# updateAnchorPeers 0 2
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=$ORG2_MSP
export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA

显示更多

安装 Chaincode

如果要通过某个 peer node 访问 chaincode,那么,这个节点必须事先安装这个 chaincode。

我们将要安装的 chaincode 位于 container 中的目录 /opt/gopath/src/github.com/chaincode/chaincode_example02/go (环境变量${CC_SRC_PATH})。 在主机中的位置是 fabric/fabric-samples/chaincode/chaincode_example02/go。我们可以打开文件 chaincode_example02.go 查看代码。

它主要实现了 Init 与 Invoke 接口,并通过 Invoke 接口实现了三种操作:invode, delete, query:

清单 10. chaincode_example02.go

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
    ...
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    ...
}
// Transaction makes payment of X units from A to B
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    ...
}
// Deletes an entity from state
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    ...
}
// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    ...
}

显示更多

在 cli container 中安装 chaincode:

清单 11. 在 cli container 中安装 chaincode

# installChaincode in peer 0 1
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=$ORG1_MSP
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export VERSION="1.0"
peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH}

# installChaincode in peer 0 2
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=$ORG2_MSP
export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
export VERSION="1.0"
peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH}

显示更多

实例化 Chaincode

Chaincode 安装后需要实例化才能使用,在 channel 中,一个 chaincode 只需要进行一次 instantiate 操作即可。这个步骤需要比较长的时间(在当前测试环境里需要 10 秒左右)。

export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=$ORG2_MSP
export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
export VERSION="1.0"
peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED
--cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -l ${LANGUAGE} -v ${VERSION} -c
'{"Args":["init","a","100","b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

显示更多

Chaincode –Query

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

得到结果为 100,即为这个 channel ledger 中 “a” 所对应的初始的值。

peer chaincode query 命令会获得经背书的 chaincode 执行结果。但它不会产生 transaction。

Chaincode –Invoke

export PEER_CONN_PARMS="--peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles
/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles
/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"
peer chaincode invoke -o orderer.example.com:7050 --tls
$CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc
$PEER_CONN_PARMS -c '{"Args":["invoke","a","b","10"]}'

显示更多

通过这个命令会产生一个 transaction,将 a 对应的值减 10,同时将 b 的值加 10。

再次查询:

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

返回结果为 90。

Chaincode – Query(2)

尝试连接 peer1.org1.example.com 进行查询:

export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=$ORG1_MSP
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
export VERSION="1.0"
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

显示更多

返回错误结果如下:

Error: endorsement failure during query. response: status:500 message:"cannot retrieve package for chaincode mycc/1.0, error open /var/hyperledger/production/chaincodes/mycc.1.0: no such file or directory"

因为我们之前并没有在 peer1.org1.example.com 节点上安装 chaincode。我们尝试安装:

peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH}

再次进行查询:

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

Chaincode Container

在节点的 Chaincode 第一次被实例化或使用激活时,会启动一个 container 以运行 chaincode。再次执行 docker ps 可以看到增加了 3 个 container:

d1a150be4270 dev-peer1.org1.example.com-mycc-1.0-cd123150154e6bf2df7ce682e0b1bcbea...
9506d7970113 dev-peer0.org1.example.com-mycc-1.0-384f11f484b9302df90b453200cfb2517...
3d710c8dc821 dev-peer0.org2.example.com-mycc-1.0-15b571b3ce849066b7ec74497da3b27e5...

显示更多

其他 Channel、Chaincode 操作

仍然在 cli Container 中执行以下操作。

列出当前节点所有加入 的 C hannel:

peer channel list

列出当前节点所 有已经安装的 Chaincode :

peer chaincode list --installed

获取特 定 C hanne l 的 区块链信息:

peer channel getinfo -c $CHANNEL_NAME

可得到返回结果类似如下:

Blockchain info: {"height":7,"currentBlockHash":"OV8dnCVwKhfJlLUsIg+orqpdMnn6cEjWDxq+1njNBZM=","previousBlockHash":"6VjT1rtVhY6jSCz+e5pWQzw7eMOkOewcfT7JkRXkw0c="}

其中,block height 的值是 7。

我们可以进一步获取特定的 Block 的详细内容:

peer channel fetch 6 mychannel_6.block -o orderer.example.com:7050 -c $CHANNEL_NAME --tls
--cafile $ORDERER_CA
configtxgen --inspectBlock mychannel_6.block > mychannel_6.json

 

以上内容转自:

https://developer.ibm.com/zh/articles/cl-lo-hyperledger-fabric-practice-analysis/

http://devjue.fun/2019/12/first-network-byfn.sh-%E8%A7%A3%E6%9E%90/

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值