注意:本文所使用的 fabric 版本为 v1.4.3,与其它版本的网络存在差异。
手动启动 first-network 网络系列分为三部分:
第一篇单纯使用命令行的形式执行 byfn.sh 脚本中的内容,第二篇和第三篇是对手动启动网络过程所使用的命令和配置文件的解释。
1 生成组织结构与身份证书
1.1 crypto-config.yaml 文件
fabric 网络必须指定成员参与才能正常进行交易,因此创建 fabric 网络环境中所需的组织结构及身份证书等密码信息是开发的第一步。证书信息在网络中代表实体的身份,用于实体间通信及交易时的签名与验证。
此步骤使用 cryptogen 工具为各种网络实体生成密码信息(x509 证书和签名密钥),而 cryptogen 需要将文件 crypto-config.yaml
作为参数配置,这个文件包含网络拓扑。下面是 first-network/crypto-config.yaml
文件:
# "OrdererOrgs" -定义 Orderer 节点所属的组织信息 OrdererOrgs: - Name: Orderer # Orderer 组织的名称 Domain: example.com # Orderer 组织的域名 EnableNodeOUs: true # 设置为 true,会在 msp 目录生成 config.yaml 文件 # "Specs" -组织中的节点 Specs: # hostname + Domain 的值组成 Orderer 节点的完整域名 - Hostname: orderer # orderer.example.com 节点 - Hostname: orderer2 - Hostname: orderer3 - Hostname: orderer4 - Hostname: orderer5 # "PeerOrgs" - 定义 Peer 节点所属的组织信息 PeerOrgs: # 定义 Org1 组织 - Name: Org1 # Org 组织的名称 Domain: org1.example.com # Org 组织的域名 EnableNodeOUs: true # 设置为 true,会在 msp 目录生成 config.yaml 文件 Template: # 这里的 count 是当前组织 Peer 节点数量 Count: 2 # Peer 节点的域名默认为: peer + 起始数字0 + Domain # org1 的两个节点域名为: # peer0.org1.example.com # peer1.org1.example.com # Start 如果使用,那么下标将从 5 开始根据 Count 域数量生成: # peer5.org1.example.com # peer6.org1.example.com # Start: 5 # # Hostname 如果使用,那么效果等于上面的 Specs.Hostname,而 Count 和 Start 域将失去作用 # Hostname: {{.Prefix}}{{.Index}} # default Users: # 这里的 Count 指除了 Admin 外要建立的用户数量. # Count 为 1 时,会生成用户 user1@org1.example.com 和管理员 Admin@org1.example.com Count: 1 # 定义 Org2 组织 - Name: Org2 Domain: org2.example.com EnableNodeOUs: true Template: Count: 2 Users: Count: 1
该配置文件中,网络实体的命名约定为 {{.Hostname}}.{{.Domain}}
。
1.2 生成组织结构及身份证书
使用 cryptogen 工具为 fabric 网络生成指定拓扑结构的组织关系和身份证书,工具二进制文件位于 fabric-samples/bin
目录下,运行如下命令:
$ cd ./first-network/ $ ../bin/cryptogen generate --config=./crypto-config.yaml org1.example.com org2.example.com
产生的证书和密钥等密码信息会保存在 crypto-config
文件夹中。
cryptogen 工具的常用子命令与参数如下:
子命令:
- generate:生成指定拓扑结构的组织关系和身份证书
- showtemplate:显示默认配置文件模板
- version:显示版本信息
参数:
- --config:指定要使用的配置文件
- --output:指定生成内容的输出目录(默认为
crypto-config
)
1.3 crypto-config 目录
crypto-config
是根据 crypto-config.yaml
配置文件生成的目录,该目录下包含以下两个子目录:
- ordererOrganizations:存放 Orderer 组织相关的身份信息
- peerOrganizations:存放 Peer 节点组织相关身份信息,包括两个组织。其中,最关键的是 MSP 目录, 代表了实体的身份信息
进入 ordererOrganizations 文件夹,使用 tree 命令:
$ cd ./ordererOrganizations $ tree -L 5 . └── example.com # 根域名为 example.com 的 Orderer 组织的证书目录 ├── ca # 存放组织的根证书(自签名)和对应的私钥文件,该证书用于表明身份 │ ├── 2b7d1880f1af171bfee737ce15d75d0cea5f40d492b859f47fca653b530802e1_sk │ └── ca.example.com-cert.pem ├── msp # 存放代表 Orderer 组织身份的密码信息 │ ├── admincerts # Orderer 组织管理员的身份证书 │ ├── cacerts # 组织的身份根证书,同 ca 目录下文件 │ │ └── ca.example.com-cert.pem │ ├── config.yaml # 记录 OrganizationalUnitIdentifiers 信息,包括根证书位置和 ID 信息 │ └── tlscacerts # 组织 TLS 通信根证书,同 tlsca 目录下文件 │ └── tlsca.example.com-cert.pem ├── orderers # 存放该组织 orderer 节点的密码信息 # 节点过多,此处仅列出 orderer.example.com 节点的信息 │ └── orderer.example.com # 存放 orderer.example.com 节点的密码信息 │ ├── msp # 存放代表该节点身份的密码信息 │ │ ├── admincerts # 组织管理员身份证书 │ │ ├── cacerts # 组织身份根证书,同 ca 目录下文件 │ │ ├── config.yaml # 记录 OrganizationalUnitIdentifiers 信息 │ │ ├── keystore # 该节点身份证书对应的私钥,用于签名 │ │ ├── signcerts # 该节点身份证书,被组织身份根证书签名 │ │ └── tlscacerts # 组织 TLS 通信的根证书,同 tlsca 目录下文件 │ └── tls # 存放该节点 TLS 通信的密码信息 │ ├── ca.crt # 组织 TLS 通信的根证书,同 tlsca 目录下文件 │ ├── server.crt # 该节点 TLS 通信证书,被通信根证书签名 │ └── server.key # 该节点 TLS 通信证书对应的私钥 ├── tlsca # 存放组织的 TLS 通信的根证书(自签名)和对应的私钥文件 │ ├── f7facb0a6ea731f9b71be84b8dcbb80efe8467aa76388a064e1838963f45fc77_sk │ └── tlsca.example.com-cert.pem └── users # 存放该组织用户的密码信息 └── Admin@example.com # 存放管理员 Admin 的密码信息 ├── msp # 存放代表管理员身份的密码信息 │ ├── admincerts # 组织管理员身份证书,被身份根证书签名 │ ├── cacerts # 组织身份根证书,同 ca 目录下文件 │ ├── config.yaml # 记录 OrganizationalUnitIdentifiers 信息 │ ├── keystore # 管理员身份证书对应的私钥,用于签名 │ ├── signcerts # 管理员身份证书,被组织身份根证书签名 │ └── tlscacerts # 组织 TLS 通信的根证书,同 tlsca 目录下文件 └── tls # 存放管理员 TLS 通信的密码信息 ├── ca.crt # 组织 TLS 通信的根证书,同 tlsca 目录下文件 ├── client.crt # 管理员 TLS 通信证书,被通信根证书签名 └── client.key # 管理员 TLS 通信证书对应的私钥
进入 ordererOrganizations 文件夹,该目录存放 org1.example.com
和 org2.example.com
的身份信息:
$ cd ./peerOrganizations $ tree -L 1 . ├── org1.example.com └── org2.example.com
任意进入一个组织目录,使用 tree 命令查看目录结构:
$ cd ./org1.example.com $ tree -L 5 . ├── ca # org1 的身份根证书(自签)及其对应私钥 │ ├── 0ce82aa81d6911c55ce2c4320df5dfe9f8ae0e675fa5fc0cc3bd6ad3e3b69391_sk │ └── ca.org1.example.com-cert.pem ├── msp # 存放代表 org1 组织身份的密码信息 │ ├── admincerts │ ├── cacerts │ │ └── ca.org1.example.com-cert.pem │ ├── config.yaml │ └── tlscacerts │ └── tlsca.org1.example.com-cert.pem ├── peers # 存放该组织 peer 节点的密码信息 │ ├── peer0.org1.example.com # 存放 peer0.org1.example.com 节点的密码信息 │ │ ├── msp │ │ │ ├── admincerts │ │ │ ├── cacerts │ │ │ │ └── ca.org1.example.com-cert.pem │ │ │ ├── config.yaml │ │ │ ├── keystore │ │ │ │ └── 7983a02e6899486faf0f6d9d63c4d3e906d698b56d9ddf863870acb42767041d_sk │ │ │ ├── signcerts │ │ │ │ └── peer0.org1.example.com-cert.pem │ │ │ └── tlscacerts │ │ │ └── tlsca.org1.example.com-cert.pem │ │ └── tls │ │ ├── ca.crt │ │ ├── server.crt │ │ └── server.key │ └── peer1.org1.example.com # 存放 peer1.org1.example.com 节点的密码信息 │ ├── msp │ │ ├── admincerts │ │ ├── cacerts │ │ │ └── ca.org1.example.com-cert.pem │ │ ├── config.yaml │ │ ├── keystore │ │ │ └── 9c7aacf4a3ad747e39d81ccdf65e790a2ca8bdfff6f41d911de03c4d2948f58b_sk │ │ ├── signcerts │ │ │ └── peer1.org1.example.com-cert.pem │ │ └── tlscacerts │ │ └── tlsca.org1.example.com-cert.pem │ └── tls │ ├── ca.crt │ ├── server.crt │ └── server.key ├── tlsca # 存放组织的 TLS 通信的根证书(自签名)和对应的私钥文件 │ ├── c5d91235d19d070a80e6113822dd700dbde88d087aaa3dfc5a7bbef8a97636e1_sk │ └── tlsca.org1.example.com-cert.pem └── users # 存放该组织用户的密码信息 ├── Admin@org1.example.com # 存放该组织管理员 Admin 的密码信息 │ ├── msp │ │ ├── admincerts │ │ ├── cacerts │ │ │ └── ca.org1.example.com-cert.pem │ │ ├── config.yaml │ │ ├── keystore │ │ │ └── 3545abf8f6703e28ec1212554707f1eaab20816ac16c7c7de036fc13fb33503f_sk │ │ ├── signcerts │ │ │ └── Admin@org1.example.com-cert.pem │ │ └── tlscacerts │ │ └── tlsca.org1.example.com-cert.pem │ └── tls │ ├── ca.crt │ ├── server.crt │ └── server.key └── User1@org1.example.com # 存放该组织用户 User1 的密码信息 ├── msp │ ├── admincerts │ ├── cacerts │ │ └── ca.org1.example.com-cert.pem │ ├── config.yaml │ ├── keystore │ │ └── a12d61eb50c0b60ccf0c2e6591871d864dba8d6f90d54343a9e41dcd7b086626_sk │ ├── signcerts │ │ └── User1@org1.example.com-cert.pem │ └── tlscacerts │ └── tlsca.org1.example.com-cert.pem └── tls ├── ca.crt ├── client.crt └── client.key
在生成的目录结构中最关键的是各个实体目录下的 MSP 目录内容,存储了代表实体身份的各种证书文件,用于在交易时进行签名及验证,一般包括:
- admincerts:管理员的身份证书文件
- cacerts:组织根证书文件(身份证书)
- keystore:实体的签名私钥文件
- signcerts:实体的签名身份证书文件
- tlscacerts:组织根证书文件(通信证书)
- intermediatecerts (可选):信任的中间证书
- crls (可选):证书撤销列表
- config.yaml(可选):记录 OrganizationalUnitldentifiers 信息,包括根证书位置和ID信息
2 生成网络启动的配置文件
2.1 configtx.yaml 文件
生成组织结构与身份证书、密钥之后,接下来使用 configtxgen 工具创建四个配置网络的文件:genesis.block
(创世区块)、channel.tx
(应用通道配置交易)、Org1MSPanchors.tx
(锚节点更新配置交易)和 Org2MSPanchors.tx
。其中,创世区块是排序服务的创始区块,即系统 Channel 的创世区块;应用通道配置交易在应用通道创建时广播给 orderer;锚节点更新配置交易指定应用通道上每个组织的锚节点。
上述信息的配置被定义在 configtx.yaml
文件中,configtxgen 工具会将该文件作为参数配置,下面是 first-network/configtx.yaml
文件:
# configtx.yaml主要用到了以下语法: # << 合并到当前数据 # - 数组 # * 引用锚点数据 # & 定义数据锚点 # 指定 OrdererOrg 和 PeerOrg 的组织信息 Organizations: - &OrdererOrg # OrdererOrg 组织定义 Name: OrdererOrg # 组织名称 ID: OrdererMSP # 组织 MSP 标识(ID是引用组织的关键) MSPDir: crypto-config/ordererOrganizations/example.com/msp # MSP 证书目录路径 Policies: # 定义 Orderer 组织的策略 Readers: Type: Signature Rule: "OR('OrdererMSP.member')" Writers: Type: Signature Rule: "OR('OrdererMSP.member')" Admins: Type: Signature Rule: "OR('OrdererMSP.admin')" - &Org1 # Org1 组织定义 Name: Org1MSP # 组织名称 ID: Org1MSP # 组织 MSP 标识 MSPDir: crypto-config/peerOrganizations/org1.example.com/msp # MSP 证书目录路径 Policies: Readers: Type: Signature Rule: "OR('Org1MSP.admin', 'Org1MSP.peer', 'Org1MSP.client')" Writers: Type: Signature Rule: "OR('Org1MSP.admin', 'Org1MSP.client')" Admins: Type: Signature Rule: "OR('Org1MSP.admin')" AnchorPeers: # 定义锚节点信息,用于跨组织 Gossip 通信 - Host: peer0.org1.example.com # 锚节点主机名(域名) Port: 7051 # 锚节点的端口号 - &Org2 # Org2 组织定义 Name: Org2MSP ID: Org2MSP MSPDir: crypto-config/peerOrganizations/org2.example.com/msp Policies: Readers: Type: Signature Rule: "OR('Org2MSP.admin', 'Org2MSP.peer', 'Org2MSP.client')" Writers: Type: Signature Rule: "OR('Org2MSP.admin', 'Org2MSP.client')" Admins: Type: Signature Rule: "OR('Org2MSP.admin')" AnchorPeers: - Host: peer0.org2.example.com Port: 9051 # Capabilities 段定义了 fabric 程序要加入网络所必须支持的特性 Capabilities: # channel 配置同时应用于排序节点和对等节点,并且必须被两种节点同时支持 # 将该配置项设置为 ture 表明要求节点具备该能力 Channel: &ChannelCapabilities V1_4_3: true V1_3: false V1_1: false # Orderer 配置仅应用于排序节点,不需考虑对等节点的升级 # 将该配置项设置为 true 表明要求排序节点具备该能力 Orderer: &OrdererCapabilities V1_4_2: true V1_1: false # Application 配置仅应用于对等网络,不需考虑排序节点的升级 # 将该配置项设置为 true 表明要求对等节点具备该能力 Application: &ApplicationCapabilities V1_4_2: true V1_3: false V1_2: false V1_1: false # Application 段用来定义要写入创世区块或配置交易的应用参数 Application: &ApplicationDefaults Organizations: # Organizations 配置列出参与到网络中的机构清单 Policies: # 定义本层级的应用控制策略 Readers: Type: ImplicitMeta Rule: "ANY Readers" Writers: Type: ImplicitMeta Rule: "ANY Writers" Admins: Type: ImplicitMeta Rule: "MAJORITY Admins" <<: *ApplicationCapabilities # Orderer 段用来定义要编码写入创世区块或通道交易的排序节点参数 Orderer: &OrdererDefaults # 排序节点类型用来指定要启用的排序节点实现,不同的实现对应不同的共识算法 OrdererType: solo # 目前可用的类型为:solo、kafka 和 etcdraft Addresses: - orderer.example.com:7050 # Orderer 服务节点地址,可扩展成多个 Orderer 节点 # 打包交易出块规则,下面四个参数的设置十分重要,直接影响 fabric 网络的性能 BatchTimeout: 2s # 打包交易消息出块的超时时间 BatchSize: MaxMessageCount: 10 # 打包交易消息出块的最大消息个数 AbsoluteMaxBytes: 99 MB # 打包交易消息出现的最大字节数 PreferredMaxBytes: 512 KB # 通常情况下打包交易消息出块的建议字节数,如果一个交易消息的大小超过了这个值, 就会被放入另外一个更大的区块中 Kafka: Brokers: # 排序服务 Kafka Broker 服务器地址列表,允许有多个 - 127.0.0.1:9092 Organizations: # Orderer 组织 Policies: Readers: Type: ImplicitMeta Rule: "ANY Readers" Writers: Type: ImplicitMeta Rule: "ANY Writers" Admins: Type: ImplicitMeta Rule: "MAJORITY Admins" BlockValidation: Type: ImplicitMeta Rule: "ANY Writers" # Channel 段用来定义要写入创世区块或配置交易的通道参数 Channel: &ChannelDefaults # 定义本层级的通道访问策略 Policies: Readers: Type: ImplicitMeta Rule: "ANY Readers" # Writes 策略定义了调用 Broadcast API 提交交易的许可规则 Writers: Type: ImplicitMeta Rule: "ANY Writers" # Admin策略定义了修改本层级配置的许可规则 Admins: Type: ImplicitMeta Rule: "MAJORITY Admins" # Capabilities配置描通道层级的能力需求,这里直接引用 Capabilities: <<: *ChannelCapabilities # Profiles 定义了 Orderer 系统通道和应用通道配置信息 Profiles: # TwoOrgsOrdererGenesis 定义了一个使用 Solo 排序节点的简单配置 # 包括一个 OrdererOrg 组织和一个 SampleConsortium 联盟(有 Org1 和 Org2 两个组织) TwoOrgsOrdererGenesis: <<: *ChannelDefaults Orderer: # 系统通道配置信息 <<: *OrdererDefaults # 具体配置信息,引用 OrdererDefaults Organizations: # Orderer 系统通道组织,引用 OrdererOrg 组织 - *OrdererOrg Capabilities: # 定义全局 Capabilities 功能特性 <<: *OrdererCapabilities Consortiums: # 联盟列表 SampleConsortium: # SampleConsortium 联盟包括 Org1 和 Org2 两个组织 Organizations: - *Org1 - *Org2 # TwoOrgsChannel 是一个应用通道配置,包含两个组织 Org1 和 Org2 TwoOrgsChannel: Consortium: SampleConsortium # 应用通道关联的联盟名称 <<: *ChannelDefaults Application: # 应用通道信息 <<: *ApplicationDefaults # 具体配置信息 Organizations: # 定义应用通道组织 - *Org1 - *Org2 Capabilities: <<: *ApplicationCapabilities # 应用通道 Capabilities 功能特性 # SampleDevModeKafka 定义了一个使用 Kfaka 排序节点的配置 # 包括一个 OrdererOrg 组织和一个 SampleConsortium 联盟(有 Org1 和 Org2 两个组织) SampleDevModeKafka: <<: *ChannelDefaults Capabilities: <<: *ChannelCapabilities Orderer: <<: *OrdererDefaults OrdererType: kafka Kafka: Brokers: - kafka.example.com:9092 Organizations: - *OrdererOrg Capabilities: <<: *OrdererCapabilities Application: <<: *ApplicationDefaults Organizations: - <<: *OrdererOrg Consortiums: SampleConsortium: Organizations: - *Org1 - *Org2 # SampleDevModeKafka 定义了一个使用 etcdraft 排序节点的配置 # 包括一个 OrdererOrg 组织和一个 SampleConsortium 联盟(有 Org1 和 Org2 两个组织) SampleMultiNodeEtcdRaft: <<: *ChannelDefaults Capabilities: <<: *ChannelCapabilities Orderer: <<: *OrdererDefaults OrdererType: etcdraft EtcdRaft: Consenters: - Host: orderer.example.com Port: 7050 ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt - Host: orderer2.example.com Port: 7050 ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt - Host: orderer3.example.com Port: 7050 ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt - Host: orderer4.example.com Port: 7050 ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer4.example.com/tls/server.crt ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer4.example.com/tls/server.crt - Host: orderer5.example.com Port: 7050 ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer5.example.com/tls/server.crt ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer5.example.com/tls/server.crt Addresses: - orderer.example.com:7050 - orderer2.example.com:7050 - orderer3.example.com:7050 - orderer4.example.com:7050 - orderer5.example.com:7050 Organizations: - *OrdererOrg Capabilities: <<: *OrdererCapabilities Application: <<: *ApplicationDefaults Organizations: - <<: *OrdererOrg Consortiums: SampleConsortium: Organizations: - *Org1 - *Org2
2.2 生成系统通道创世区块
首先我们需要告诉 configtxgen
工具在哪里寻找它需要的 configtx.yaml
文件,告诉它查看当前的工作目录:
$ export FABRIC_CFG_PATH=$PWD
使用 configtxgen 工具指定 configtx.yaml
文件中定义的 TwoOrgsOrdererGenesis 模块,生成 Orderer 系统通道的初始区块文件:
$ ../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
2019-11-30 16:02:37.983 CST [common.tools.configtxgen] doOutputBlock -> INFO 007 Generating genesis block 2019-11-30 16:02:37.983 CST [common.tools.configtxgen] doOutputBlock -> INFO 008 Writing genesis block
在 channel-artifacts
目录下会生成系统通道的创世区块文件 genesis.block
。
configtxgen 工具常用参数如下:
- -asOrg:用于指定有权设置的写集中的值的 Org 组织名称
- -channnelID:指定应用通道 ID(如果不指定,默认为 “testchainid”)
- -inspectBlock:根据指定的路径输出区块包含的配置信息
- -inspectChannelCreateTx:根据指定的路径输出事务包含的配置信息
- -outputBlock:如果指定此参数,则生成的初始区块文件保存在指定的路径中
- -outputCreateChannelTx:如果指定此参数,则生成的应用通道配置交易文件保存在指定的路径中
- -profile:
configtx.yaml
中的 Profiles 的配置项,用于指定生成创始区块还是配置交易配置文件 - -version:显示版本信息
2.3 生成应用通道交易配置文件
使用 configtxgen 工具指定 configtx.yaml
文件中定义的 TwoOrgsChannel 模块,创建应用通道配置交易文件:
$ export CHANNEL_NAME=mychannel && ../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
成功执行,返回如下信息:
2019-11-30 16:10:56.330 CST [common.tools.configtxgen] doOutputChannelCreateTx -> INFO 005 Generating new channel configtx 2019-11-30 16:10:56.332 CST [common.tools.configtxgen] doOutputChannelCreateTx -> INFO 006 Writing new channel tx
在 channel-artifacts
目录下会生成应用通道交易配置文件 channel.tx
。
2.4 生成锚节点更新配置文件
使用 configtxgen 工具指定 configtx.yaml
文件中定义的 TwoOrgsChannel 模块,分别为 org1 和 org2 生成应用通道上的锚节点更新配置文件:
$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
成功执行,会返回如下信息:
2019-11-30 16:16:52.853 CST [common.tools.configtxgen] doOutputAnchorPeersUpdate -> INFO 005 Generating anchor peer update 2019-11-30 16:16:52.853 CST [common.tools.configtxgen] doOutputAnchorPeersUpdate -> INFO 006 Writing anchor peer update
在 channel-artifacts
目录下会生成锚节点更新配置文件 Org1MSPanchors.tx
和 Org2MSPanchors.tx
。
注意:configtxgen 工具生成的四个配置文件默认保存 first-network/channel-artifacts
目录下,该目录必须存在,如果不存在或者无意删除,需要手动创建。
参考
- 《Hyperledger Fabric 菜鸟进行攻略》
- 《Hyperledger Fabric技术内幕 架构设计与实现原理》
- https://blog.csdn.net/qq_25870633/article/details/81184781
来源:https://www.cnblogs.com/zongmin/p/12180582.html