Hyperledger Fabric官方中文教程-改进笔记(十三)-使用测试网络创建通道
本Fabric中文文档专栏的阅读前言:前言
文章目录
- 使用测试网络创建通道
- 开始之前
- 先决条件
- 启动测试网络
- 准备 configtxgen 工具
- `configtx.yaml` 文件
- 第一步:生成通道的创世区块
- 第二步:创建应用通道
- Consenter 与 Follower
- Active 与 Onboarding
- 后续步骤
- 查看某个 orderer 参与的通道
- 让 peers 加入通道
- 设置锚节点
- 向新通道部署链码
- 不使用测试网络创建通道
注:该章节的中文文档仍然采用的是 2.3 版本以前使用“系统通道”的内容;在之后的版本里系统通道被废弃,配置也发生了改变,不能再基于系统通道创建应用通道。因此本章节依据==更新后的英文版本==翻译而来。
使用测试网络创建通道
请结合 test network 学习如何创建通道创世区块(channel genesis block),并创建一个新的应用通道供测试网络中的 peer 加入。本教程无需你自己搭建 orderer,而是复用 Fabric 示例项目中的测试网络节点。由于测试网络会为你部署排序服务与 peers,本教程将只聚焦在创建通道的流程。需要说明的是,测试网络自带了一个用于创建通道的子命令 createChannel
,但本教程会演示手动创建的过程——这也是不使用测试网络时所必须掌握的流程。
在本教程中,我们将使用 configtxgen 工具生成通道创世区块,然后使用 osnadmin channel 命令创建通道。
如果你不使用测试网络,请改为遵循文档:如何部署一个排序服务。
要在测试网络中创建通道,本教程会带你完成以下步骤并介绍相关概念:
-
先决条件
-
第一步:生成通道的创世区块
-
第二步:创建应用通道
-
后续步骤
开始之前
为了运行测试网络,你需要克隆 fabric-samples
仓库并下载最新的 Fabric 生产镜像。请确保已完成 先决条件 和 安装 Samples、二进制与 Docker 镜像。
注意: 创建通道并让 peers 加入后,你还需要为通道添加 Anchor Peer(锚节点),以支持 Service Discovery 与私有数据功能。本文会包含设置 Anchor Peer 的步骤,但这需要你本机安装 jq 工具。
先决条件
启动测试网络
我们将使用正在运行的 Fabric 测试网络来创建新通道。为保证从“已知干净状态”开始,下面的命令会销毁任何活动容器并移除已有的构件(artifacts)。本教程默认你在 fabric-samples
下的 test-network
目录中操作;如果尚未进入该目录,请执行:
cd fabric-samples/test-network
先将网络关闭:
./network.sh down
然后启动测试网络:
./network.sh up
该命令会创建一个包含 两个 Peer 组织 和 一个排序组织(仅 1 个排序节点) 的 Fabric 网络。两个 Peer 组织各运行一个 peer,排序服务管理员运行一个排序节点。你会在日志中看到类似的节点创建信息:
Creating network "fabric_test" with the default driver
Creating volume "net_orderer.example.com" with default driver
Creating volume "net_peer0.org1.example.com" with default driver
Creating volume "net_peer0.org2.example.com" with default driver
Creating peer0.org2.example.com ... done
Creating orderer.example.com ... done
Creating peer0.org1.example.com ... done
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b6b117c81c7f hyperledger/fabric-peer:latest "peer node start" 2 seconds ago Up 1 second 0.0.0.0:7051->7051/tcp peer0.org1.example.com
703ead770e05 hyperledger/fabric-orderer:latest "orderer" 2 seconds ago Up Less than a second 0.0.0.0:7050->7050/tcp, 0.0.0.0:7053->7053/tcp orderer.example.com
718d43f5f312 hyperledger/fabric-peer:latest "peer node start" 2 seconds ago Up 1 second 7051/tcp, 0.0.0.0:9051->9051/tcp peer0.org2.example.com
请注意,Org1 的 peer 运行在 7051
端口,Org2 的 peer 运行在 9051
端口,而 orderer 的端口是 7050
(管理端口 7053
)。我们会在后续命令中用到这些端口。
默认情况下,刚启动的测试网络不包含任何通道。下文将演示如何在该网络中创建名为 channel1
的通道。
准备 configtxgen 工具
通道的创建是通过在创世区块中生成“通道创建交易(channel creation transaction)”,然后将该创世区块随“加入请求(join request)”一并提交给排序节点。通道创建交易用于声明该通道的初始配置,它可以由 configtxgen 工具生成。该工具读取定义通道配置的 configtx.yaml
文件,将相关信息写入通道创建交易,并输出一个包含该交易的创世区块。完成 Fabric 安装 后,configtxgen
已位于 fabric-samples/bin
。
确保你仍处于本地 fabric-samples
克隆中的 test-network
目录,先将二进制加入 PATH:
export PATH=${PWD}/../bin:$PATH
接着,为了使用 configtxgen
,需要把 FABRIC_CFG_PATH
指向包含 configtx.yaml
的测试网络配置目录(这里为 configtx
文件夹):
export FABRIC_CFG_PATH=${PWD}/configtx
现在打印帮助信息,确认 configtxgen
可用:
configtxgen --help
configtx.yaml
文件
在测试网络中,configtxgen
会使用 configtx/configtx.yaml
中定义的通道配置概要(channel profiles) 来构建通道配置,并将其序列化为 Fabric 可读取的 protobuf 格式。
configtx.yaml
包含如下与我们新通道相关的信息:
-
Organizations(组织):可以成为你通道成员的 Peer 组织和排序组织。每个组织都引用了用于构建 通道 MSP 的加密材料。
-
Ordering service(排序服务):将由哪些排序节点组成排序服务,以及用于就交易顺序达成一致的共识方法。该节也定义了排序服务的 consenter 集。测试网络只有一个排序节点;在生产网络中推荐至少 5 个 排序节点(允许同时有 2 个宕机仍能维持共识)。
EtcdRaft:Consenters:- Host: orderer.example.comPort: 7050ClientTLSCert: ../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crtServerTLSCert: ../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
-
Channel policies(通道策略):文件的多个部分共同定义了治理通道交互与更新审批的策略。出于教程目的,我们使用 Fabric 的默认策略。
-
Channel profiles(通道概要):每个概要会引用
configtx.yaml
其他部分的信息来组装通道配置。概要用于生成应用通道的创世区块。测试网络的configtx.yaml
中包含一个名为ChannelUsingRaft
的概要,我们将用它生成创建通道所需的交易。
ChannelUsingRaft:<<: *ChannelDefaultsOrderer:<<: *OrdererDefaultsOrdererType: etcdraftEtcdRaft:Consenters:- Host: orderer.example.comPort: 7050ClientTLSCert: ../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crtServerTLSCert: ../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crtOrganizations:- *OrdererOrgCapabilities: *OrdererCapabilitiesApplication:<<: *ApplicationDefaultsOrganizations:- *Org1- *Org2Capabilities: *ApplicationCapabilities
该概要同时包含两个 peer 组织 Org1
与 Org2
,以及排序组织 OrdererOrg
。后续也可以通过通道更新交易增删排序节点或排序组织的 consenter 集。
想进一步了解该文件以及如何编写自己的通道概要?参见:使用 configtx.yaml 创建通道创世区块。也可以查看本专栏中的==《详细拆解configtx通道配置文件》==。现在我们回到“创建通道”的操作流程,后续步骤中还会引用本文件的部分内容。
第一步:生成通道的创世区块
测试网络已启动,接下来可以创建新通道。我们已经设置好了使用 configtxgen
所需的环境变量。
执行以下命令为 channel1
生成创世区块:
configtxgen -profile ChannelUsingRaft -outputBlock ./channel-artifacts/channel1.block -channelID channel1
-
-profile
:通过该参数指定测试网络用于创建应用通道的概要ChannelUsingRaft
(定义在configtx.yaml
中)。 -
-outputBlock
:命令的输出是通道创世区块,写入./channel-artifacts/channel1.block
。 -
-channelID
:该参数即未来通道的名称。教程中使用channel1
。通道名必须是小写、小于 250 个字符,并匹配正则表达式[a-z][a-z0-9.-]*
。
命令成功后,你会看到 configtxgen
加载 configtx.yaml
并打印创建通道交易的日志:
[common.tools.configtxgen] main -> INFO 001 Loading configuration
[common.tools.configtxgen.localconfig] completeInitialization -> INFO 002 orderer type: etcdraft
[common.tools.configtxgen.localconfig] completeInitialization -> INFO 003 Orderer.EtcdRaft.Options unset, setting to tick_interval:"500ms" election_tick:10 heartbeat_tick:1 max_inflight_blocks:5 snapshot_interval_size:16777216
[common.tools.configtxgen.localconfig] Load -> INFO 004 Loaded configuration: /Users/fabric-samples/test-network/configtx/configtx.yaml
[common.tools.configtxgen] doOutputBlock -> INFO 005 Generating genesis block
[common.tools.configtxgen] doOutputBlock -> INFO 006 Creating application channel genesis block
[common.tools.configtxgen] doOutputBlock -> INFO 007 Writing genesis block
第二步:创建应用通道
有了通道创世区块后,就可以用 osnadmin channel join
命令在排序服务上创建通道。为简化后续命令,我们还需要设置一些环境变量,指明测试网络各节点证书的位置:
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export ORDERER_ADMIN_TLS_SIGN_CERT=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
export ORDERER_ADMIN_TLS_PRIVATE_KEY=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.key
现在在排序服务上创建名为 channel1
的通道:
osnadmin channel join --channelID channel1 --config-block ./channel-artifacts/channel1.block -o localhost:7053 --ca-file "$ORDERER_CA" --client-cert "$ORDERER_ADMIN_TLS_SIGN_CERT" --client-key "$ORDERER_ADMIN_TLS_PRIVATE_KEY"
-
--channelID
:指定在生成创世区块时使用的通道名。 -
--config-block
:指定用configtxgen
生成的通道创世区块或最新配置块(latest config block)的路径。 -
-o
:指定排序节点的管理端点主机名与端口;测试网络为localhost:7053
。
另外,由于 osnadmin channel
与排序节点之间使用 双向 TLS,需提供以下证书:
-
--ca-file
:排序组织 TLS CA 根证书的位置和文件名。 -
--client-cert
:由 TLS CA 签发的管理员客户端证书位置和文件名。 -
--client-key
:管理员客户端私钥的位置和文件名。
成功后输出类似:
Status: 201
{"name": "channel1","url": "/participation/v1/channels/channel1","consensusRelation": "consenter","status": "active","height": 1
}
此时通道已激活,peer 可以加入。
Consenter 与 Follower
注意上述排序节点以 "consensusRelation": "consenter"
身份加入通道。如果你把命令指向一个不在 configtx.yaml
(或通道配置的 consenter 集)之列的排序节点,它会以 follower 身份加入。关于加入更多排序节点的考虑,参见:加入额外的排序节点。
Active 与 Onboarding
排序节点既可以通过通道创世区块加入,也可以通过最新配置块加入。如果通过“最新配置块”加入,节点状态会先是 onboarding
,当其账本追上该配置块后状态变为 active
。此时你可以提交通道更新交易把该节点加入 consenter 集,其 consensusRelation
将从 follower
变为 consenter
。
后续步骤
创建通道后,接下来要让 peers 加入通道并部署智能合约(链码)。本节将使用测试网络演示这些过程。
查看某个 orderer 参与的通道
在让 peers 加入之前,你可能还会创建更多通道。随着通道增多,osnadmin channel list
可用于查看该 orderer 所在的通道集合。其参数与前面 osnadmin channel join
相同:
osnadmin channel list -o localhost:7053 --ca-file "$ORDERER_CA" --client-cert "$ORDERER_ADMIN_TLS_SIGN_CERT" --client-key "$ORDERER_ADMIN_TLS_PRIVATE_KEY"
输出类似:
Status: 200
{"systemChannel": null,"channels": [{"name": "channel1","url": "/participation/v1/channels/channel1"}]
}
让 peers 加入通道
测试网络包含两个 peer 组织,各有一个 peer。在使用 peer CLI 前,需要设置环境变量来指定我们以哪个用户(client MSP)执行以及目标 peer 是谁。下面以 Org1 管理员身份操作,并指向 Org1 的 peer:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID=Org1MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
为使用 peer CLI,还需调整 FABRIC_CFG_PATH
:
export FABRIC_CFG_PATH=$PWD/../config/
向 channel1
发送加入请求(携带创世区块):
peer channel join -b ./channel-artifacts/channel1.block
成功后输出类似:
[channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
[channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
然后为 Org2
重复以上步骤。设置以下环境变量,以 Org2 管理员身份操作,并将目标 peer 设为 peer0.org2.example.com
:
export CORE_PEER_LOCALMSPID=Org2MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
再次加入 channel1
:
peer channel join -b ./channel-artifacts/channel1.block
成功后输出类似:
[channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
[channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
设置锚节点
当某组织的 peers 加入通道后,应为该组织至少选择一个 peer 作为 Anchor Peer(锚节点)。只有配置 Anchor Peer,才能使用私有数据与服务发现等功能。每个组织应为通道配置多个 Anchor Peer 以增强冗余。更多关于 Gossip 与 Anchor Peer 的信息,参见 Gossip 数据分发协议。
各组织的 Anchor Peer 端点信息存储于通道配置中。通道成员可通过更新通道来设置其 Anchor Peer。我们将使用 configtxlator 工具来更新通道配置,为 Org1
与 Org2
各设置一个 Anchor Peer。
如果你的本机尚未安装 jq,请先安装再继续以下步骤。
sudo apt-get install jq
首先将 Org1
的 peer0.org1.example.com
设为 Anchor Peer。第一步使用 peer channel fetch
拉取最新通道配置块。设置环境变量,以 Org1 管理员身份操作:
export CORE_PEER_LOCALMSPID=Org1MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
拉取配置块:
peer channel fetch config channel-artifacts/config_block.pb -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c channel1 --tls --cafile "$ORDERER_CA"
由于当前最新配置块就是通道创世区块,上述命令会返回通道的第 0
块。日志类似:
[channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
[cli.common] readBlock -> INFO 002 Received block: 0
[channelCmd] fetch -> INFO 003 Retrieving last config block: 0
[cli.common] readBlock -> INFO 004 Received block: 0
配置块 config_block.pb
会存放在 channel-artifacts
目录,以便与其他构件分离。切换进去完成下一步:
cd channel-artifacts
接下来使用 configtxlator
处理通道配置。先把区块从 protobuf 解码为可读可编辑的 JSON,并剥离无关的块数据,仅保留通道配置本体:
configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
jq '.data.data[0].payload.data.config' config_block.json > config.json
现在我们得到简化后的通道配置 config.json
,作为更新的基线。为避免直接修改原始配置,先拷贝一份进行编辑:
cp config.json config_copy.json
使用 jq
将 Org1
的 Anchor Peer 信息添加到通道配置中:
jq '.channel_group.groups.Application.groups.Org1MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org1.example.com","port": 7051}]},"version": "0"}}' config_copy.json > modified_config.json
完成后,更新后的 JSON 配置位于 modified_config.json
。现在把原始与修改后的配置都编码回 protobuf,并计算它们之间的差异(即“配置更新”):
configtxlator proto_encode --input config.json --type common.Config --output config.pb
configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
configtxlator compute_update --channel_id channel1 --original config.pb --updated modified_config.pb --output config_update.pb
新的 protobuf 文件 config_update.pb
中包含了要应用到通道配置的 Anchor Peer 更新。将该更新包裹进交易信封以形成“通道配置更新交易”:
configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate --output config_update.json
echo '{"payload":{"header":{"channel_header":{"channel_id":"channel1", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . > config_update_in_envelope.json
configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope --output config_update_in_envelope.pb
注:这里拆分一下上述三个命令。
- 第一个configtxlator命令将差异文件转化为json。
- 第二个命令将
config_update.json
外层再包装了一遍,成为标准的信封格式(这是 Fabric 提交配置更新时必须的外层格式),然后交给jp,经过jp检验美化过后输入到config_update_in_envelope.json里面。- 第三个命令重新编码 json → pb
回到 test-network
目录:
cd ..
现在通过 peer channel update
提交更新。由于我们只更新了与 Org1
相关的配置,其他通道成员不需要批准该更新:
peer channel update -f channel-artifacts/config_update_in_envelope.pb -c channel1 -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
成功后会看到:
[channelCmd] update -> INFO 002 Successfully submitted channel update
接下来也把 Org2
的 peer0.org2.example.com
设为 Anchor Peer。因为是第二次,我们会更快地过一遍流程。先以 Org2 管理员身份设置环境变量:
export CORE_PEER_LOCALMSPID=Org2MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
拉取最新配置块(此时已是通道的第二个区块):
peer channel fetch config channel-artifacts/config_block.pb -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c channel1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
进入 channel-artifacts
:
cd channel-artifacts
解码并拷贝配置:
configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
jq '.data.data[0].payload.data.config' config_block.json > config.json
cp config.json config_copy.json
将 Org2
的 Anchor Peer 信息写入配置:
jq '.channel_group.groups.Application.groups.Org2MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org2.example.com","port": 9051}]},"version": "0"}}' config_copy.json > modified_config.json
重新编码并计算差异:
configtxlator proto_encode --input config.json --type common.Config --output config.pb
configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
configtxlator compute_update --channel_id channel1 --original config.pb --updated modified_config.pb --output config_update.pb
打包为交易:
configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate --output config_update.json
echo '{"payload":{"header":{"channel_header":{"channel_id":"channel1", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . > config_update_in_envelope.json
configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope --output config_update_in_envelope.pb
回到 test-network
:
cd ..
提交更新,将 Org2
的 Anchor Peer 写入通道配置:
peer channel update -f channel-artifacts/config_update_in_envelope.pb -c channel1 -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
如果你想进一步了解如何提交通道更新请求,请参见:更新通道配置。
你可以通过 peer channel getinfo
验证通道已更新成功:
peer channel getinfo -c channel1
现在通道在创世块基础上又新增了两个配置块,高度应为 3,区块哈希也会更新,例如:
Blockchain info: {"height":3,"currentBlockHash":"GKqqk/HNi9x/6YPnaIUpMBlb0Ew6ovUnSB5MEF7Y5Pc=","previousBlockHash":"cl4TOQpZ30+d17OF5YOkX/mtMjJpUXiJmtw8+sON8a8="}
注:配置更新(包括锚节点变更)一定会产生新区块;但不会“强制结束”当前区块,而是生成下一个区块;
向新通道部署链码
可以通过在通道上部署链码来验证通道创建成功。我们可使用 network.sh
脚本把“资产转移基础(Basic)”示例链码部署到任一测试网络通道。对新通道执行:
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go/ -ccl go -c channel1
执行后,你应在日志中看到链码已部署到通道的输出:
Committed chaincode definition for chaincode 'basic' on channel 'channel1':
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]
Query chaincode definition successful on peer0.org2 on channel 'channel1'
Chaincode initialization is not required
然后初始化一些资产:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" -C channel1 -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}'
成功后会看到:
[chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200
查询以确认资产已写入账本:
peer chaincode query -C channel1 -n basic -c '{"Args":["getAllAssets"]}'
输出类似:
[{"ID":"asset1","color":"blue","size":5,"owner":"Tomoko","appraisedValue":300},{"ID":"asset2","color":"red","size":5,"owner":"Brad","appraisedValue":400},{"ID":"asset3","color":"green","size":10,"owner":"Jin Soo","appraisedValue":500},{"ID":"asset4","color":"yellow","size":10,"owner":"Max","appraisedValue":600},{"ID":"asset5","color":"black","size":15,"owner":"Adriana","appraisedValue":700},{"ID":"asset6","color":"white","size":15,"owner":"Michel","appraisedValue":800}
]
不使用测试网络创建通道
本教程带你用 osnadmin channel join
在测试网络上创建通道并完成基础实践。当你准备搭建自己的网络时,请参见:Create a channel,以进一步学习 osnadmin channel
系列命令的使用。