5. Hyperledger Fabric 专题 - 构建第一个 Hyperledger Fabric 网络

Hyperledger Fabric 专题 - 构建第一个 Hyperledger Fabric 网络

注解

本文中的指令已经通过验证,可与提供的 tar 文件中的最新稳定 Docker 镜像和预编译的实用程序一起使用。如果使用当前主分支中的镜像或工具运行这些命令,则可能会看到配置和 panic 错误。

Hyperledger Fabric 最新的文档基于版本是 v2.0 Alpha release。由于示例中相关的 docker 镜像的版本是 v1.4.3,因为相关文档需要参考的版本为 v1.4.3。这些文档链接在本文的 Reference 部分都有涉及。

构建你的第一个网络 (Building Your First Network, BYFN) 方案将提供一个示例 Hyperledger Fabric 网络,该网络由两个组织组成,每个组织维护两个对端节点。尽管可以使用其他交易排序服务实现,但默认情况下还是部署 Solo 交易排序服务。

1. 安装先决条件

在开始之前,如果你尚未这样做,则不妨检查一下是否已在要开发区块链应用程序和/或运行 Hyperledger Fabric 的平台上安装了所有 必备软件

你还需要 安装示例,二进制文件和 Docker 映像。你会注意到,fabric-samples 仓库中包含许多示例。我们将使用 first-network 示例。现在打开该子目录。

cd fabric-samples/first-network
注解

本文档中提供的命令必须从 fabric-samples 仓库克隆的 first-network 子目录中运行。如果选择从其他位置运行命令,则提供的各种脚本将无法找到二进制文件。

2. 立即运行

我们提供了一个带有完整注释的脚本 — byfn.sh,该脚本利用这些 Docker 镜像来快速引导 Hyperledger Fabric 网络,该网络默认情况下由代表两个不同组织的四个对端节点和一个交易排序器节点组成。它还将启动一个容器来运行脚本化执行,该脚本化执行将对端节点加入到通道中,部署链码并根据已部署的链码推动交易的执行。

这是 byfn.sh 脚本的帮助文本:

Usage:
byfn.sh <mode> [-c <channel name>] [-t <timeout>] [-d <delay>] [-f <docker-compose-file>] [-s <dbtype>] [-l <language>] [-o <consensus-type>] [-i <imagetag>] [-v]"
  <mode> - one of 'up', 'down', 'restart', 'generate' or 'upgrade'"
    - '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"
    - 'upgrade'  - upgrade the network from version 1.3.x to 1.4.0"
  -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), node, or java"
  -o <consensus-type> - the consensus-type of the ordering service: solo (default), kafka, or etcdraft"
  -i <imagetag> - the tag to be used to launch the network (defaults to \"latest\")"
  -v - verbose mode"
byfn.sh -h (print this message)"

Typically, one would first generate the required certificates and
genesis block, then bring up the network. e.g.:"

  byfn.sh generate -c mychannel"
  byfn.sh up -c mychannel -s couchdb"
  byfn.sh up -c mychannel -s couchdb -i 1.4.0"
  byfn.sh up -l node"
  byfn.sh down -c mychannel"
  byfn.sh upgrade -c mychannel"

Taking all defaults:"
      byfn.sh generate"
      byfn.sh up"
      byfn.sh down"

如果选择不提供标志参数,则脚本将使用默认值。

2.1 生成网络组件

准备好尝试了吗?好吧!执行以下命令:

$ cd /path/to/fabric-samples/first-network
$ ./byfn.sh generate

你将看到有关发生的情况的简短说明,以及是/否命令行提示符。响应 y 或按返回键以执行所描述的操作。

Generating certs and genesis block for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y
proceeding ...
/Users/xxx/dev/fabric-samples/bin/cryptogen

##########################################################
##### Generate certificates using cryptogen tool #########
##########################################################
org1.example.com
2017-06-12 21:01:37.334 EDT [bccsp] GetDefault -> WARN 001 Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP.
...

/Users/xxx/dev/fabric-samples/bin/configtxgen
##########################################################
#########  Generating Orderer Genesis block ##############
##########################################################
2017-06-12 21:01:37.558 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.562 EDT [msp] getMspConfig -> INFO 002 intermediate certs folder not found at [/Users/xxx/dev/byfn/crypto-config/ordererOrganizations/example.com/msp/intermediatecerts]. Skipping.: [stat /Users/xxx/dev/byfn/crypto-config/ordererOrganizations/example.com/msp/intermediatecerts: no such file or directory]
...
2017-06-12 21:01:37.588 EDT [common/configtx/tool] doOutputBlock -> INFO 00b Generating genesis block
2017-06-12 21:01:37.590 EDT [common/configtx/tool] doOutputBlock -> INFO 00c Writing genesis block

#################################################################
### Generating channel configuration transaction 'channel.tx' ###
#################################################################
2017-06-12 21:01:37.634 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.644 EDT [common/configtx/tool] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2017-06-12 21:01:37.645 EDT [common/configtx/tool] doOutputChannelCreateTx -> INFO 003 Writing new channel tx

#################################################################
#######    Generating anchor peer update for Org1MSP   ##########
#################################################################
2017-06-12 21:01:37.674 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.678 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2017-06-12 21:01:37.679 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update

#################################################################
#######    Generating anchor peer update for Org2MSP   ##########
#################################################################
2017-06-12 21:01:37.700 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.704 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2017-06-12 21:01:37.704 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update

第一步将为我们的各种网络实体生成所有证书和密钥,用于交易排序服务的创世区块以及配置 通道 所需的配置交易的集合。

2.2 建立网络

接下来,你可以使用以下命令之一启动网络:

$ cd /path/to/fabric-samples/first-network
$ sudo ./byfn.sh up

上面的命令将编译 Golang 链码镜像并旋转相应的容器。 Go 是默认的链码语言,但是还支持 Node.js 和 Java 链码。如果你想使用 Node.js 链代码来完成本教程,请改用以下命令:

# we use the -l flag to specify the chaincode language
# forgoing the -l flag will default to Golang

./byfn.sh up -l node
注解

有关 Node.js 填充程序的更多信息,请参阅其 文档

注解

有关 Java 填充程序的更多信息,请参阅其 文档

使示例使用 Java 链码运行,你必须指定 -l java,如下所示:

$ cd /path/to/fabric-samples/first-network
$ sudo ./byfn.sh up -l java
注解

不要同时运行这些命令。除非你关闭并重新建立网络,否则只能尝试一种语言。

除了支持多种链码语言外,你还可以发出一个标志,该标志将显示一个五节点的 Raft 交易排序服务或 Kafka 交易排序服务,而不是一个节点的 Solo 交易排序服务。有关当前支持的交易排序服务实现的更多信息,请查看 交易排序服务

要使用 Raft 交易排序服务启动网络,请执行:

$ cd /path/to/fabric-samples/first-network
$ sudo ./byfn.sh up -o etcdraft

要使用 Kafka 交易排序服务建立网络,请执行:

$ cd /path/to/fabric-samples/first-network
$ sudo ./byfn.sh up -o kafka

再次提示你是否要继续还是中止。回应 y 或按回车键:

Starting for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n]
proceeding ...
Creating network "net_byfn" with the default driver
Creating peer0.org1.example.com
Creating peer1.org1.example.com
Creating peer0.org2.example.com
Creating orderer.example.com
Creating peer1.org2.example.com
Creating cli


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

Channel name : mychannel
Creating channel...

日志将从此处继续。这将启动所有容器,然后驱动一个完整的端到端应用程序场景。成功完成后,它将在你的终端窗口中报告以下内容:

Query Result: 90
2017-05-16 17:08:15.158 UTC [main] main -> INFO 008 Exiting.....
===================== Query successful on peer1.org2 on channel 'mychannel' =====================

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


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

你可以滚动浏览这些日志以查看各种交易。如果未得到此结果,请跳至 故障排除 部分,让我们看看我们是否可以帮助你发现问题所在。

2.3 关闭网络

最后,让我们关闭所有组件,以便我们可以每次探索一次网络设置。以下内容将杀死你的容器,删除加密组件和四个组件,并从 Docker 注册表中删除链码镜像:

$ cd /path/to/fabric-samples/first-network
$ sudo ./byfn.sh down

再次提示你继续,用 y 响应或按回车键:

Stopping with channel 'mychannel' and CLI timeout of '10'
Continue? [Y/n] y
proceeding ...
WARNING: The CHANNEL_NAME variable is not set. Defaulting to a blank string.
WARNING: The TIMEOUT variable is not set. Defaulting to a blank string.
Removing network net_byfn
468aaa6201ed
...
Untagged: dev-peer1.org2.example.com-mycc-1.0:latest
Deleted: sha256:ed3230614e64e1c83e510c0c282e982d2b06d148b1c498bbdcc429e2b2531e91
...

如果你想进一步了解基础工具和引导机制,请继续阅读。在接下来的这些部分中,我们将逐步介绍构建一个功能齐全的 Hyperledger Fabric 网络的各个步骤和要求。

注解

下面概述的手动步骤假定 cli 容器中的 FABRIC_LOGGING_SPEC 设置为 DEBUG。你可以通过修改 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
    - FABRIC_LOGGING_SPEC=DEBUG
    #- FABRIC_LOGGING_SPEC=INFO

3. 密钥生成器

我们将使用 cryptogen 工具为我们的各种网络实体生成密钥材料 (x509 证书和签名密钥)。这些证书代表身份,它们允许在我们的实体进行通信和交易时进行签名/验证身份授权。

3.1 它是如何工作的?

Cryptogen 使用了一个文件 - crypto-config.yaml,该文件包含网络拓扑,并允许我们为组织以及属于这些组织的组件生成一组证书和密钥。每个组织都有一个唯一的根证书 (ca-cert),它将特定组件 (对端节点和交易排序器) 绑定到该组织。通过为每个组织分配唯一的 CA 证书,我们正在模仿一个典型的网络,参与的 成员 将使用其自己的证书颁发机构。Hyperledger Fabric 中的交易和通信由实体的私钥 (密钥库) 签名,然后通过公钥 (signcerts) 进行验证。

你会在此文件中注意到一个 count 变量。我们使用它来指定每个组织的对端节点数;在我们的案例中,每个单位有两个对端节点。现在,我们不会深入研究 x.509 证书和公钥基础结构 的细节。如果您有兴趣,可以自己阅读这些主题。

在运行 cryptogen 工具之后,生成的证书和密钥将保存到名为 crypto-config 的文件夹中。请注意,crypto-config.yaml 文件列出了五个与交易排序器组织相关的交易排序器。尽管 cryptogen 工具将为所有这五个交易排序器创建证书,除非使用了 RaftKafka 交易排序服务,否则这些交易排序器中只有一个将用于 Solo 交易排序服务实现中,并用于创建系统通道和 mychannel

4. 配置交易生成器

configtxgen 工具用于创建四个配置组件:

  • 交易排序器的创始区块 (genesis block),
  • 通道配置交易 (configuration transaction),
  • 和两个锚点对端节点交易 (anchor peer transactions) - 每个对端节点组织一个。

有关此工具功能的完整说明,请参见 configtxgen

交易排序器区块是交易排序服务的 创世区块,并且在 通道 创建时将通道 配置交易文件广播到交易排序器。顾名思义,锚定对端节点交易 (anchor peer transaction) 指定此通道上每个组织的 锚定对端节点

4.1 它是如何工作的?

Configtxgen 使用一个文件 configtx.yaml,其中包含示例网络的定义。有三个成员 - 一个交易排序器组织 (OrdererOrg) 和两个对端节点组织 (Org1 & Org2),每个组织都管理和维护两个对端节点。该文件还指定了一个联盟 - SampleConsortium - 由我们的两个对端节点组织组成。请特别注意此文件底部的 "Profiles" 部分。你会注意到我们有几个唯一的 profiles。一些值得注意的地方:

  • TwoOrgsOrdererGenesis:为 Solo 交易排序服务生成创世区块。
  • SampleMultiNodeEtcdRaft:生成 Raft 交易排序服务的创世区块。仅在发出 -o 标志并指定 etcdraft 时使用。
  • SampleDevModeKafka:生成 Kafka 交易排序服务的创世区块。仅在发出 -o 标志并指定 kafka 时使用。
  • TwoOrgsChannel:为我们的通道 mychannel 生成创世区块。

这些标题很重要,因为在创建组件时,我们会将它们作为参数传递。

注解

请注意,我们的 SampleConsortium 是在系统级配置文件中定义的,然后由我们的通道级配置文件引用。通道存在于联盟的权限范围内,所有联盟都必须在整个网络范围内定义。

该文件还包含两个值得注意的附加规范。首先,我们为每个对端节点组织指定锚定对端节点 (peer0.org1.example.com & peer0.org2.example.com)。其次,我们指向每个组织 MSP 的目录位置,从而允许我们将每个组织的根证书存储在交易排序器的创始区块中。这是一个关键的概念。现在,与交易排序服务通信的任何网络实体都可以验证其数字签名。

5. 运行上述工具

你可以使用 configtxgencryptogen 命令手动生成证书/密钥和各种配置工件。或者,你可以尝试修改 byfn.sh 脚本以实现目标。

5.1 手动生成组件

你可以参考 byfn.sh 脚本中的 generateCerts 函数,以获取生成将用于 crypto-config.yaml 文件中定义的网络配置的证书所必需的命令。但是,为方便起见,我们还将在此处提供参考。

首先,让我们运行 cryptogen 工具。我们的二进制文件位于 bin 目录中,因此我们需要提供工具所在的相对路径。

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

你应该在终端中看到以下内容:

org1.example.com
org2.example.com

证书和密钥 (即 MSP 材料) 将输出到 first-network 目录的子目录 - crypto-config

接下来,我们需要告诉 configtxgen 工具在哪里寻找需要读取的 configtx.yaml 文件。我们将在当前的工作目录中告诉它:

$ export FABRIC_CFG_PATH=$PWD

然后,我们将调用 configtxgen 工具来创建交易排序器的创始区块:

$ ../bin/configtxgen -profile TwoOrgsOrdererGenesis -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

要输出 Raft 交易排序服务的创始区块,此命令应为:

$ ../bin/configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

请注意此处使用的 SampleMultiNodeEtcdRaft profile。

要输出 Kafka 交易排序服务的创世区块,请执行:

$ ../bin/configtxgen -profile SampleDevModeKafka -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

如果你未使用 RaftKafka,则应看到类似于以下内容的输出:

2017-10-26 19:21:56.301 EDT [common/tools/configtxgen] main -> INFO 001 Loading configuration
2017-10-26 19:21:56.309 EDT [common/tools/configtxgen] doOutputBlock -> INFO 002 Generating genesis block
2017-10-26 19:21:56.309 EDT [common/tools/configtxgen] doOutputBlock -> INFO 003 Writing genesis block
注解

交易排序器的创世区块和我们将要创建的后续组件将输出到该项目的 channel-artifacts 子目录中。上面命令中的 channelID 是系统通道的名称。

5.2 创建通道配置交易

接下来,我们需要创建通道交易组件。确保替换 $CHANNEL_NAME 或将 CHANNEL_NAME 设置为可在以下说明中使用的环境变量:

# The channel.tx artifact contains the definitions for our sample channel

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

请注意,如果你使用的是 RaftKafka 交易排序服务,则不必对通道发出特殊命令。在创建网络的创世纪区块时,TwoOrgsChannel 配置文件将使用你指定的交易排序服务配置。

如果你不使用 RaftKafka 交易排序服务,则应在终端中看到类似于以下内容的输出:

2017-10-26 19:24:05.324 EDT [common/tools/configtxgen] main -> INFO 001 Loading configuration
2017-10-26 19:24:05.329 EDT [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2017-10-26 19:24:05.329 EDT [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 003 Writing new channel tx

接下来,我们将在正在构建的通道上为 Org1 定义锚点。同样,请确保替换 $CHANNEL_NAME 或为以下命令设置环境变量。终端输出将模拟通道交易组件的输出:

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

现在,我们将在同一通道上为 Org2 定义锚点:

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

6. 启动网络

注意

如果你以前运行过 byfn.sh 示例,请确保在继续操作之前已关闭测试网络 (请参阅 关闭网络)。

$ sudo ./byfn.sh down

我们将利用脚本来启动我们的网络。 docker-compose 文件引用了我们先前下载的镜像,并使用我们先前生成的 genesis.block 引导交易排序器程序。

我们希望手动检查命令,以显示每个调用的语法和功能。

首先,我们开始我们的网络:

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

如果要查看网络的实时日志,请不要提供 -d 标志。如果让日志流传输,则需要打开第二个终端以执行 CLI 调用。

6.1 创建 & 加入通道

回想一下,我们在上面的“创建通道配置交易”部分中使用 configtxgen 工具创建了通道配置交易。你可以重复该过程,以使用传递给 configtxgen 工具的 configtx.yaml 中的相同或不同配置文件来创建其他通道配置交易。然后,你可以重复本节中定义的过程以在网络中建立其他通道。

我们将使用 docker exec 命令进入 CLI 容器:

$ sudo docker exec -it cli bash

如果成功,你应该看到以下内容:

root@33ab5acc5622:/opt/gopath/src/github.com/hyperledger/fabric/peer#

为了使以下 CLI 命令起作用,我们需要在命令前添加以下四个环境变量。peer0.org1.example.com 的这些变量被内嵌到 CLI 容器中,因此我们可以在不传递它们的情况下进行操作。但是,如果要将调用发送给其他对端节点或交易排序器,请在进行任何 CLI 调用时覆盖环境变量,如以下示例所示:

# 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.tx) 中创建生成的通道配置交易组件传递给交易排序器。

我们使用 -c 标志指定我们的通道名称,并使用 -f 标志指定我们的通道配置交易。在这种情况下,它是 channel.tx,但是你可以使用其他名称挂载自己的配置交易。再次,我们将在 CLI 容器中设置 CHANNEL_NAME 环境变量,以便我们不必显式传递此参数。通道名称必须全部为小写字母,少于 250 个字符,并且与正则表达式 [a-z][a-z0-9 .-] * 相匹配。

$ 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。这是交易排序器根证书的本地路径,使我们可以验证 TLS 握手。

此命令返回一个创世区块 - <CHANNEL_NAME.block> - 我们将使用它来加入通道。它包含 channel.tx 中指定的配置信息。如果你未对默认通道名称进行任何修改,则该命令将返回一个名为 mychannel.block 的原型。

注意

这些手动命令的其余部分将保留在 CLI 容器中。当定位到除 peer0.org1.example.com 以外的对端节点对象时,你还必须记住在所有命令前加上相应的环境变量。

现在,将 peer0.org1.example.com 加入该通道。

# By default, this joins ``peer0.org1.example.com`` only
# the <CHANNEL_NAME.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

$ peer channel join -b mychannel.block

你可以通过对在上面的 创建和加入通道 部分中使用的四个环境变量进行适当的更改,使其他对端节点加入通道。

与其加入每个对端节点,不如简单地加入 peer0.org2.example.com,以便我们可以正确更新通道中的锚点对端节点定义。由于我们将覆盖内嵌到 CLI 容器中的默认环境变量,因此该完整命令如下:

$ 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:9051
$ 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
注解

在 v1.4.1 之前,docker 网络中的所有对端节点都使用端口 7051。如果使用 v1.4.1 之前的版本的 fabric-samples,请在本教程中将所有出现的 CORE_PEER_ADDRESS 修改为使用端口 7051

另外,你可以选择单独设置这些环境变量,而不是传入整个字符串。设置完后,你只需再次发出 peer channel join 命令,CLI 容器将代表 peer0.org2.example.com

6.2 更新锚定对端节点

以下命令是通道更新,它们将传播到通道的定义。从本质上讲,我们在通道的创始区块上方添加了其他配置信息。请注意,我们不是在修改创世区块,而只是将增量添加到将定义锚点对端节点的链中。

更新通道定义以将 Org1 的锚点对端节点定义为 peer0.org1.example.com

$ 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
$ 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。与 Org2 对端节点的 peer channel join 命令相同,我们需要在此调用前加上适当的环境变量。

$ 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:9051
$ 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

6.3 安装并实例化链码

注解

我们将利用一个简单的现有链码。要了解如何编写自己的链码,请参阅 链码开发者 教程。

应用程序通过链码与区块链账本进行交互。因此,我们需要在将执行并背书我们交易的每个对端节点上安装链码,然后在通道上实例化链码。

首先,将示例 Go,Node.js 或 Java 链码安装到 Org1 中的 peer0 节点上。这些命令将指定的源代码样式放置到我们对端节点的文件系统上。

注解

每个链码名称和版本只能安装一个版本的源代码。源代码在链码名称和版本的上下文中存在于对端节点的文件系统中,它与语言无关。同样,实例化的链码容器将反映对端节点上已安装的任何语言。

首先设置 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=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
Golang
# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
$ 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 to indicate "node" chaincode
# for node chaincode -p takes the absolute path to the node.js chaincode
$ peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/
Java
# make note of the -l flag to indicate "java" chaincode
# for java chaincode -p takes the absolute path to the java chaincode
$ peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/

当我们实例化通道上的链码时,将设置背书策略以要求来自 Org1 和 Org2 中的对端节点的背书。因此,我们还需要在 Org2 的对端节点上安装链码。

修改以下四个环境变量以对 Org2 中的 peer0 发出安装命令:

# Environment variables for PEER0 in 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:9051
$ 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

现在,将示例 Go,Node.js 或 Java 链码安装到 Org2 中的 peer0 上。这些命令将指定的源代码样式放置到我们对端节点的文件系统上。

# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
$ 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 to indicate "node" chaincode
# for node chaincode -p takes the absolute path to the node.js chaincode
$ peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/
Java
# make note of the -l flag to indicate "java" chaincode
# for java chaincode -p takes the absolute path to the java chaincode
$ peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/

接下来,实例化通道上的链码。这将初始化通道上的链码,为链码设置背书策略,并为目标对端节点启动链码容器。注意 -P 参数。这是我们的策略,其中我们针对要验证的链码指定了交易所需的背书级别。

在下面的命令中,你会注意到我们将策略指定为 -P "AND ('Org1MSP.peer','Org2MSP.peer')"。这意味着我们需要来自 Org1 和 Org2 的对端节点的背书 (即两次背书)。如果我们将语法更改为 OR,则只需要一个背书即可。

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
$ 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 mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"
Node.js
注解

Node.js 链码的实例化大约需要一分钟。该命令未挂起;而是在编译镜像时安装了 fabric-shim 层。

# 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 "AND ('Org1MSP.peer','Org2MSP.peer')"
Java
注解

请注意,Java 链码实例化可能会花费一些时间,因为它会在 Java 环境中编译链码并下载 docker 容器。

$ 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 java -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

有关策略实现的更多详细信息,请参阅 背书策略 文档。

如果你希望其他对端节点与帐本进行交互,则需要将它们加入通道,并将链码源的相同名称,版本和语言安装到适当的对端节点的文件系统上。一旦每个对端节点尝试与该特定链码进行交互,就会为每个对端节点启动一个链码容器。同样,请注意 Node.js 镜像的编译速度较慢。

一旦链码已在通道上实例化,我们就可以放弃 l 标志。我们只需要输入通道标识符和链码的名称即可。

6.4 Query

让我们查询 a 的值,以确保链码已正确实例化并填充了状态数据库。查询的语法如下:

# be sure to set the -C and -n flags appropriately

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

6.5 Invoke

现在,将 10a 移到 b。此交易将创建一个新区块并更新状态数据库。调用的语法如下:

# be sure to set the -C and -n flags appropriately

$ peer chaincode invoke -o orderer.example.com:7050 --tls true --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 --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:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'

6.6 Query

让我们确认之前的调用已正确执行。我们将键 a 的值初始化为 100,并在之前的调用中删除了 10。因此,针对 a 的查询应返回 90。查询的语法如下。

# be sure to set the -C and -n flags appropriately

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

我们应该看到以下内容:

Query Result: 90

随意重新开始并操作键值对和后续调用。

6.7 Install

现在,我们将链码安装在第三个对端节点 Org2 中的 peer1 上。修改以下四个环境变量以对 Org2 中的 peer1 发出安装命令:

# Environment variables for PEER1 in 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:10051
$ 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

现在,将示例 Go,Node.js 或 Java 链码安装到 Org2 中的 peer1 上。这些命令将指定的源代码样式放置到我们对端节点的文件系统上。

Golang
# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
$ 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 to indicate "node" chaincode
# for node chaincode -p takes the absolute path to the node.js chaincode
$ peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/
Java
# make note of the -l flag to indicate "java" chaincode
# for java chaincode -p takes the absolute path to the java chaincode
$ peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/

6.8 Query

我们确认可以将查询发布到 Org2 中的 Peer1。我们将键 a 的值初始化为 100,并在之前的调用中删除了 10。因此,针对 a 的查询仍应返回 90

Org2 中的 peer1 必须首先加入通道,然后它才能响应查询。可以通过执行以下命令来加入通道:

$ 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:10051
$ 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

连接命令返回后,可以发出查询。查询的语法如下。

# be sure to set the -C and -n flags appropriately

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

我们应该看到以下内容:

Query Result: 90

随意重新开始并操作键值对和后续调用。

6.9 幕后发生了什么?

注解

这些步骤描述了由 ./byfn.sh up 运行 script.sh 的情况。使用 ./byfn.sh down 清理网络,并确保此命令处于活动状态。然后使用相同的 docker-compose 提示符再次启动网络。

  • 脚本 - script.sh - 内嵌在 CLI 容器中。该脚本根据提供的通道名称驱动 createChannel 命令,并使用 channel.tx 文件进行通道配置。
  • createChannel 的输出是一个创世区块 - <your_channel_name> .block - 它存储在对端节点的文件系统中,并包含从 channel.tx 指定的通道配置。
  • 所有四个对端节点均执行 joinChannel 命令,该命令将先前生成的创世区块作为输入。此命令指示对端节点加入 <your_channel_name> 并创建以 <your_channel_name>.block 开头的链。
  • 现在,我们有一个由四个对端节点和两个组织组成的通道。这是我们的 TwoOrgsChannel profile。
  • peer0.org1.example.compeer1.org1.example.com 属于 Org1。peer0.org2.example.compeer1.org2.example.com 属于 Org2。
  • 这些关系通过 crypto-config.yaml 定义,并且 MSP 路径在我们的 docker compose 中指定。
  • 然后,更新 Org1MSP (peer0.org1.example.com) 和 Org2MSP (peer0.org2.example.com) 的锚点对端节点。为此,我们将 Org1MSPanchors.txOrg2MSPanchors.tx 组件与通道名称一起传递给交易排序服务。
  • 链码 - chaincode_example02 - 安装在 peer0.org1.example.compeer0.org2.example.com 上。
  • 然后在 mychannel 上实例化链码。实例化将链码添加到通道,启动目标对端节点的容器,并初始化与链码关联的键值对。此示例的初始值为 ["a","100" "b","200"]。此实例化导致启动以 dev-peer0.org2.example.com-mycc-1.0 开头的容器。
  • 实例化还为背书策略传递了一个参数。该策略定义为 -P "AND ('Org1MSP.peer','Org2MSP.peer')",这意味着任何交易都必须由与 Org1 和 Org2 绑定的对端节点认可。
  • 针对 "a" 的值查询将发布到 peer0.org2.example.com。实例化链码时,启动了一个名为 dev-peer0.org2.example.com-mycc-1.0 的 Org2 peer0 的容器。返回查询结果。没有发生写操作,因此对 "a" 的查询仍将返回值 "100"。
  • 调用被发送到 peer0.org1.example.compeer0.org2.example.com,以将 "10" 从 "a" 移动到 "b"。
  • 查询发送到 peer0.org2.example.com,以获取 "a" 的值。返回值 90,正确反映了先前的交易,在该交易中,键 "a" 的值被修改了 10。
  • 链码 - chaincode_example02 - 安装在 peer1.org2.example.com 上。
  • 查询发送到 peer1.org2.example.com 以获取 "a" 的值。这将启动名为 dev-peer1.org2.example.com-mycc-1.0 的第三个链码容器。返回值 90,正确反映了先前的交易,在该交易中,键 "a" 的值被修改了 10。

6.10 这说明了什么?

必须在对端节点上安装链码,以使其能够对帐本成功执行读/写操作。此外,直到针对该链码执行了初始化或传统交易 (读/写) (例如查询 "a" 的值) 后,对端节点的链码容器才启动。交易导致容器启动。而且,通道中的所有对端节点都维护账本的精确副本,该副本包括将区块,不可变的顺序记录存储在区块中的区块链,以及维护当前状态快照的状态数据库。这包括那些未安装链码的对端节点 (如上例中的 peer1.org1.example.com`)。最终,由于安装了链码,因此可以对其进行访问 (例如上例中的 peer1.org2.example.com),因为该链码已被实例化。

6.11 如何查看这些交易?

检查 CLI Docker 容器的日志。

$ sudo 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 successful on peer1.org2 on channel 'mychannel' =====================

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


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

你可以滚动浏览这些日志以查看各种交易。

6.12 如何查看链码日志?

检查各个链码容器,以查看针对每个容器执行的单独交易。这是每个容器的合并输出:

$ sudo docker logs dev-peer0.org2.example.com-mycc-1.0
ex02 Init
Aval = 100, Bval = 200
ex02 Invoke
Query Response:{"Name":"a","Amount":"100"}
ex02 Invoke
Aval = 90, Bval = 210
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

$ sudo docker logs dev-peer0.org1.example.com-mycc-1.0
ex02 Invoke
Aval = 90, Bval = 210

$ sudo docker logs dev-peer1.org2.example.com-mycc-1.0
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

7. 了解 Docker Compose 拓扑

BYFN 示例向我们提供了两种 Docker Compose 文件,这两种文件都是从 docker-compose-base.yaml (位于 base 文件夹中) 扩展的。我们的第一个版本 docker-compose-cli.yaml 为我们提供了一个 CLI 容器以及一个交易排序器和四个对端节点。我们将此文件用于此页面上的全部说明。

注意

本节的其余部分介绍了为 SDK 设计的 docker-compose 文件。有关运行这些测试的详细信息,请参考 Node SDK 仓库。

第二种形式 docker-compose-e2e.yaml 被构造为使用 Node.js SDK 运行端到端测试。除了可以使用 SDK 之外,它的主要区别还在于,fabric-ca 服务器具有容器。因此,我们能够将 REST 调用发送到组织 CA,以进行用户注册和登记。

如果要在不首先运行 byfn.sh 脚本的情况下使用 docker-compose-e2e.yaml,则我们将需要进行四个小修改。我们需要指向组织的 CA 的私钥。你可以在 crypto-config 文件夹中找到这些值。例如,要找到 Org1 的私钥,我们将遵循以下路径 - crypto-config/peerOrganizations/org1.example.com/ca/。私钥是一个长哈希值,后跟 _sk。 Org2 的路径为 - crypto-config/peerOrganizations/org2.example.com/ca/

docker-compose-e2e.yaml 中,为 ca0 和 ca1 更新 FABRIC_CA_SERVER_TLS_KEYFILE 变量。你还需要编辑命令中提供的路径以启动 ca 服务器。你为每个 CA 容器提供相同的私钥两次。

8. 使用 CouchDB

可以将状态数据库从默认 (goleveldb) 切换到 CouchDB。CouchDB 可以使用相同的链码功能,但是,还具有对建模为 JSON 数据 的链码状态数据库执行丰富和复杂查询的功能。

要使用 CouchDB 代替默认数据库 (goleveldb),请遵循前面概述的用于生成工件的相同过程,除了在启动网络时还要通过 docker-compose-couch.yaml:

$ sudo docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml up -d

chaincode_example02 现在应该可以在底层使用 CouchDB 了。

注解

如果你选择实现 fabric-couchdb 容器端口到主机端口的映射,请确保你了解安全隐患。在开发环境中端口的映射使 CouchDB REST API 可用,并允许通过 CouchDB Web 界面 (Fauxton) 可视化数据库。生产环境可能会避免实施端口映射,以限制外部对 CouchDB 容器的访问。

你可以按照上述步骤针对 chaincode_example02 链码使用 CouchDB 状态数据库,但是,为了行使 CouchDB 查询功能,你将需要使用数据编码为 JSON 的链码 (例如 marbles02)。你可以在 fabric/examples/chaincode/go 目录中找到 marbles02 链码。

我们将按照上述创建和加入通道一节中所述的相同过程来创建和加入通道。将同伴加入通道后,请执行以下步骤与 marbles02 链码进行交互:

  • peer0.org1.example.com 上安装并实例化链码:
# be sure to modify the $CHANNEL_NAME variable accordingly for the instantiate command

$ peer chaincode install -n marbles -v 1.0 -p github.com/chaincode/marbles02/go
$ 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 ('Org1MSP.peer','Org2MSP.peer')"

创建一些大理石并将其移动:

# be sure to modify the $CHANNEL_NAME variable accordingly

$ 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 端口,则现在可以通过打开浏览器并导航至以下 URL,通过 CouchDB Web 界面 (Fauxton) 查看状态数据库:

http://localhost:5984/_utils

你应该会看到一个名为 mychannel (或你唯一的通道名称) 的数据库以及其中的文档。

注意

对于以下命令,请确保适当地更新 $CHANNEL_NAME 变量。

你可以通过 CLI 运行常规查询 (例如,读取 marble2):

$ peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["readMarble","marble2"]}'

输出应显示 marble2 的详细信息:

Query Result: {"color":"red","docType":"marble","name":"marble2","owner":"jerry","size":50}

你可以检索特定大理石的历史记录 - 例如 marble1:

$ peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'

输出应显示在 marble1 上的交易:

Query Result: [{"TxId":"1c3d3caf124c89f91a4c0f353723ac736c58155325f02890adebaa15e16e6464", "Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}},{"TxId":"755d55c281889eaeebf405586f9e25d71d36eb3d35420af833a20a2f53a3eefd", "Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"jerry"}},{"TxId":"819451032d813dde6247f85e56a89262555e04f14788ee33e28b232eef36d98f", "Value":}]

你还可以对数据内容执行丰富的查询,例如通过所有者 jerry 查询大理石字段:

$ peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["queryMarblesByOwner","jerry"]}'

输出应显示 jerry 拥有的两种大理石:

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

9. 为什么选择 CouchDB

CouchDB 是一种 NoSQL 解决方案。它是一个面向文档的数据库,其中文档字段存储为键值映射。字段可以是简单的键值对,列表或映射。除了 LevelDB 支持的键/复合键/键范围查询外,CouchDB 还支持完整的数据丰富查询功能,例如针对整个区块链数据的非键查询,因为其数据内容以 JSON 格式存储,并且完全可查询的。因此,CouchDB 可以满足 LevelDB 不支持的许多用例的链码,审计,报告要求。

CouchDB 还可以增强区块链中合规性和数据保护的安全性。因为它能够通过过滤和屏蔽交易中的各个属性来实现字段级安全性,并且仅在需要时才授权只读权限。

另外,CouchDB 属于 CAP 定理的 AP 类型 (可用性和分区容错)。它使用具有最终一致性的主-主复制模型。可以在 CouchDB 文档的最终一致性 (Eventual Consistency) 页面上找到更多信息。但是,在每个 Fabric 对端节点,没有数据库副本,可以保证对数据库的写入是一致且持久的 (不是最终一致性)。

CouchDB是第一个用于Fabric的外部可插入状态数据库,并且可能并且应该有其他外部数据库选项。例如,IBM为其区块链启用关系数据库。并且可能还需要CP类型(一致性和分区容差)数据库,以便在不保证应用程序级别的情况下实现数据一致性。

10. 关于数据持久性的说明

如果需要在对端节点容器或 CouchDB 容器上保持数据持久性,一种选择是将 docker-host 中的目录挂载到容器中的相关目录中。例如,你可以在 docker-compose-base.yaml 文件的对端节点容器规范中添加以下两行:

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

对于 CouchDB 容器,你可以在 CouchDB 容器规范中添加以下两行:

volumes:
 - /var/hyperledger/couchdb0:/opt/couchdb/data

11. 故障排除

  • 始终重新启动网络。使用以下命令删除组件:密钥,容器和链码镜像:
$ sudo ./byfn.sh down
注意

如果不删除旧的容器和镜像,将会看到错误。

  • 如果你看到 Docker 错误,请首先检查你的 Docker 版本 (先决条件),然后尝试重新启动 Docker 进程。Docker 的问题通常无法立即识别。例如,你可能会看到由于无法访问安装在容器中的加密组件而导致的错误。

    如果它们仍然存在,请删除你的镜像并从头开始:

$ docker rm -f $(docker ps -aq)
$ docker rmi -f $(docker images -q)
  • 如果在创建,实例化,调用或查询命令时看到错误,请确保已正确更新了通道名称和链码名称。提供的示例命令中包含占位符值。

  • 如果看到以下错误:

Error: Error endorsing chaincode: rpc error: code = 2 desc = Error installing chaincode code mycc:1.0(chaincode /var/hyperledger/production/chaincodes/mycc.1.0 exits)

你可能有以前运行的链码镜像 (例如 dev-peer1.org2.example.com-mycc-1.0dev-peer0.org1.example.com-mycc-1.0)。删除它们,然后重试。

$ sudo docker rmi -f $(docker images | grep peer[0-9]-peer[0-9] | awk '{print $3}')
  • 如果你看到类似以下内容的内容:
Error connecting: rpc error: code = 14 desc = grpc: RPC failed fast due to transport failure
Error: rpc error: code = 14 desc = grpc: RPC failed fast due to transport failure

确保针对重新标记为 "last" 的 "1.0.0" 运行网络。

  • 如果看到以下错误:
[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,然后重新创建侈的通道工件。

  • 要清理网络,请使用 down 选项:
$ sudo ./byfn.sh down
  • 如果看到错误消息指出你仍然具有 "active endpoints",请修剪 Docker 网络。这将清除以前的网络,并为你提供一个全新的环境:
$ sudo 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

  • 如果看到类似于以下内容的错误:
/bin/bash: ./scripts/script.sh: /bin/bash^M: bad interpreter: No such file or directory

确保有问题的文件 (在本示例中为 script.sh) 以 Unix 格式编码。这很可能是由于未在 Git 配置中将 core.autocrlf 设置为 false 引起的 (请参阅 Windows 其他功能)。有几种解决方法。例如,如果你有权访问 vim 编辑器,请打开文件:

vim ./fabric-samples/first-network/scripts/script.sh

然后通过执行以下 vim 命令来更改其格式:

:set ff=unix
注意

如果仍然看到错误,请在 Hyperledger Rocket ChatStackOverflow 的结构问题通道上共享日志。

附录 1. 常用 Docker 命令

1.1 进入 Docker 容器

可以通过下面的命令进入 Docker 容器 cli 查看。

$ sudo docker exec -it cli /bin/sh

1.2 查看 Docker 容器日志

可以通过下面的命令查看 Docker 容器 cli 的日志。

$ sudo docker logs -f cli

Reference

项目源代码

项目源代码会逐步上传到 Github,地址为 https://github.com/windstamp

Contributor

  1. Windstamp, https://github.com/windstamp
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270