当前位置: 首页 > web >正文

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 组织 Org1Org2,以及排序组织 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 工具来更新通道配置,为 Org1Org2 各设置一个 Anchor Peer。

如果你的本机尚未安装 jq,请先安装再继续以下步骤。
sudo apt-get install jq

首先将 Org1peer0.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

使用 jqOrg1 的 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

:这里拆分一下上述三个命令。

  1. 第一个configtxlator命令将差异文件转化为json。
  2. 第二个命令将config_update.json外层再包装了一遍,成为标准的信封格式(这是 Fabric 提交配置更新时必须的外层格式),然后交给jp,经过jp检验美化过后输入到config_update_in_envelope.json里面。
  3. 第三个命令重新编码 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

接下来也把 Org2peer0.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 系列命令的使用。

http://www.xdnf.cn/news/18412.html

相关文章:

  • iOS 应用迭代与上架节奏管理 从测试包到正式发布的全流程实践
  • Scikit-learn 预处理函数分类详解
  • 阶跃星辰 StepFun 入驻 GitCode 平台,带来工业级 AI 体验
  • 密码加密算法和JWT无状态认证
  • [系统架构设计师]面向服务架构设计理论与实践(十五)
  • C++ 数据结构 和 STL
  • [Polly智能维护网络] 弹性上下文 | `ResiliencePropertyKey<TValue>`
  • WPF Alert弹框控件 - 完全使用指南
  • 2025年电赛A题省一方案
  • AR 虚实叠加技术在工业设备运维中的实现流程方案
  • 5G-A赋能AR眼镜:毫米级虚实融合的未来已来
  • 通过try-catch判断数据库唯一键字段是否重复
  • 网络流量分析——基础知识
  • MySQL 数据与表结构导出 Excel 技术文档
  • Ubuntu 主机名:精通配置与管理
  • Kafka-Eagle安装
  • SpringBoot + MyBatis-Plus 使用 listObjs 报 ClassCastException 的原因与解决办法
  • 自动驾驶汽车机器学习安全实用解决方案
  • Meta 再次重组人工智能部门
  • 自学嵌入式第二十三天:数据结构(3)-双链表
  • C语言基础:(二十)自定义类型:结构体
  • Linux 文本处理三剑客:awk、grep、sed 完全指南
  • 如何在 Ubuntu 24.04 配置 SFTP Server ?
  • AI 驱动三维逆向:点云降噪算法工具与机器学习建模能力的前沿应用
  • vue3源码reactivity响应式之数组代理的方法
  • MySQL/Kafka数据集成同步,增量同步及全量同步
  • 深入理解数据结构:从数组、链表到B树家族
  • 医疗AI与医院数据仓库的智能化升级:异构采集、精准评估与高效交互的融合方向(上)
  • 【工具使用-Docker容器】构建自己的镜像和容器
  • 栈上创建和堆上创建区别