一、Hyperledger Fabric 网络搭建

Hyperledger Fabric 2.4.0 文档

https://hyperledger-fabric.readthedocs.io/en/release-2.4/prereqs.html

Hyperledger Fabric CA 最新版文档

https://hyperledger-fabric-ca.readthedocs.io/en/latest

Hyperledger Fabric 案例项目

https://github.com/hyperledger/fabric-samples

1. 环境要求

  • 主机:VM
  • 操作系统:CentOS 7
  • CPU:2 core +
  • 内存:4G +
  • 存储:50G +
image-20220926143611955

2. 环境安装

  • 创建项目目录以下命令在项目目录下运行
  mkdir -p /home/hyperledger/fabric/finance
  cd /home/hyperledger/fabric/finance
  • 安装通用包
  # 更新软件包
  yum -y update
  # 安装通用包
  yum -y install vim tree wget git maven

  yum -y install epel-release
  yum -y install jq
  • 安装 Chaincode 编程语言环境(JDK / NodeJS / Go)
  # 安装 JDK
  yum -y install java-1.8.0-openjdk*
  # 验证安装
  java -version

  # 安装 nvm
  git clone git://github.com/nvm-sh/nvm.git ~/nvm
  echo "source ~/nvm/nvm.sh" >> ~/.bashrc
  source ~/.bashrc


  # 以上地址无法访问时,用以下方式安装
  git clone https://gitee.com/mirrors/nvm.git ~/.nvm && cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`
  vim ~/.bash_profile
  export NVM_DIR="$HOME/.nvm"
  [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

  # 验证安装
  nvm --version
  # 安装 NodeJS
  nvm install v10.16.0
  export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node
  # 验证 NodeJS 和 NPM 版本
  node -v
  npm -v

  # 安装 Go
  yum -y install go
  # 验证安装
  go version

NVM:Node Version Manager

NPM:Node Package Manager

  • 安装 docker-ce
  # 删除旧版本 Docker
  yum remove docker \
      docker-client \
      docker-client-latest \
      docker-common \
      docker-latest \
      docker-latest-logrotate \
      docker-logrotate \
      docker-engine

  # 设置存储库
  yum -y install yum-utils
  yum-config-manager \
      --add-repo \
      https://download.docker.com/linux/centos/docker-ce.repo
  # 安装 docker-ce
  yum -y install docker-ce-18.06.3.ce-3.el7 docker-ce-cli-18.06.3.ce-3.el7 containerd.io
  # 启动 Docker
  systemctl start docker
  # 开启自启动
  systemctl enable docker

  # 验证安装
  docker --version
  • 安装 docker-compose
  # 安装 docker-compose
  curl -L "https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  # 设置可执行权限
  chmod +x /usr/local/bin/docker-compose
  # 验证安装
  docker-compose --version
  • 下载 docker 镜像
  # fabric-ca 镜像
  docker pull hyperledger/fabric-ca:1.4.9
  # fabric-peer 镜像
  docker pull hyperledger/fabric-peer:2.4.1
  # fabric-orderer 镜像
  docker pull hyperledger/fabric-orderer:2.4.1
  # fabric-tools 镜像
  docker pull hyperledger/fabric-tools:2.4.1
  # 编程语言的链码部署镜像(Go | Java | NodeJS)
  docker pull hyperledger/fabric-ccenv:2.4.1
  docker pull hyperledger/fabric-javaenv:2.4.1
  docker pull hyperledger/fabric-nodeenv:2.4.1
  # 链码运行时的基本操作系统镜像
  docker pull hyperledger/fabric-baseos:2.4.1
  # CouchDB镜像(一个用于存储账本状态的NOSQL数据库)
  docker pull couchdb:3.1.1

查看下载的镜像

  docker images
image-20220926181415340
  • 下载 Fabirc Binaries
  # Fabric CA
  wget https://github.com/hyperledger/fabric-ca/releases/download/v1.4.9/hyperledger-fabric-ca-linux-amd64-1.4.9.tar.gz
  # Fabric
  wget https://github.com/hyperledger/fabric/releases/download/v2.4.1/hyperledger-fabric-linux-amd64-2.4.1.tar.gz

  tar zxf hyperledger-fabric-ca-linux-amd64-1.4.9.tar.gz
  tar zxf hyperledger-fabric-linux-amd64-2.4.1.tar.gz

3. Hyperledger Fabric 跨境支付网络拓扑

image-20220929125237495

网络规划

名称域名端口
Orderer CAca.orderer.finance.com7054:7054
Bank1 CAca.bank1.finance.com8054:7054
Bank2 CAca.bank2.finance.com9054:7054
Ordererorderer.finance.com7050:7050
Bank1 Peer1peer1.bank1.finance.com7051:7051,7052:7052
Bank2 Peer1peer1.bank2.finance.com9051:7051,9052:7052
Bank1 Peer1 CouchDBpeer1.bank1.couchdb.com5984:5984
Bank2 Peer1 CouchDBpeer1.bank2.couchdb.com6984:5984

修改 /etc/hosts 文件添加以下内容

vim /etc/hosts

127.0.0.1 ca.orderer.finance.com
127.0.0.1 ca.bank1.finance.com
127.0.0.1 ca.bank2.finance.com

127.0.0.1 orderer.finance.com
127.0.0.1 peer1.bank1.finance.com
127.0.0.1 peer1.bank2.finance.com

127.0.0.1 peer1.bank1.couchdb.com
127.0.0.1 peer1.bank2.couchdb.com

4. 部署 CA 服务

创建 docker-compose 存放目录

mkdir docker

编写 docker-compose-ca.yaml

vim docker/docker-compose-ca.yaml
# docker/docker-compose-ca.yaml

version: '2.0'

networks:
  orderer:
    name: orderer
  bank1:
    name: bank1
  bank2:
    name: bank2
  finance:
    name: finance

services:
  ca.orderer.finance.com:
    hostname: ca.orderer.finance.com
    container_name: ca.orderer.finance.com
    image: hyperledger/fabric-ca:1.4.9
    command: sh -c 'fabric-ca-server start -d -b admin:adminpw --port 7054'
    environment:
      - FABRIC_CA_SERVER_HOME=/tmp/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_TLS_ENABLED=true
      - FABRIC_CA_SERVER_CSR_HOSTS=ca.orderer.finance.com
      - FABRIC_CA_SERVER_DEBUG=true
      # - FABRIC_CA_SERVER_DB_TYPE=mysql
      # - FABRIC_CA_SERVER_DB_DATASOURCE=root:root@tcp(192.168.0.104:3306)/orderer_ca?parseTime=true
    volumes:
      - ../organizations/fabric-ca/orderer:/tmp/hyperledger/fabric-ca-server
    networks:
      - orderer
      - finance
    ports:
      - 7054:7054
  ca.bank1.finance.com:
    hostname: ca.bank1.finance.com
    container_name: ca.bank1.finance.com
    image: hyperledger/fabric-ca:1.4.9
    command: sh -c 'fabric-ca-server start -d -b admin:adminpw --port 7054'
    environment:
      - FABRIC_CA_SERVER_HOME=/tmp/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_TLS_ENABLED=true
      - FABRIC_CA_SERVER_CSR_HOSTS=ca.bank1.finance.com
      - FABRIC_CA_SERVER_DEBUG=true
      # - FABRIC_CA_SERVER_DB_TYPE=mysql
      # - FABRIC_CA_SERVER_DB_DATASOURCE=root:root@tcp(192.168.0.104:3306)/bank1_ca?parseTime=true
    volumes:
      - ../organizations/fabric-ca/bank1:/tmp/hyperledger/fabric-ca-server
    networks:
      - bank1
      - finance
    ports:
      - 8054:7054
  ca.bank2.finance.com:
    hostname: ca.bank2.finance.com
    container_name: ca.bank2.finance.com
    image: hyperledger/fabric-ca:1.4.9
    command: sh -c 'fabric-ca-server start -d -b admin:adminpw --port 7054'
    environment:
      - FABRIC_CA_SERVER_HOME=/tmp/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_TLS_ENABLED=true
      - FABRIC_CA_SERVER_CSR_HOSTS=ca.bank2.finance.com
      - FABRIC_CA_SERVER_DEBUG=true
      # - FABRIC_CA_SERVER_DB_TYPE=mysql
      # - FABRIC_CA_SERVER_DB_DATASOURCE=root:root@tcp(192.168.0.104:3306)/bank2_ca?parseTime=true
    volumes:
      - ../organizations/fabric-ca/bank2:/tmp/hyperledger/fabric-ca-server
    networks:
      - bank2
      - finance
    ports:
      - 9054:7054
# Fabric CA 配置详解
# FABRIC_CA_SERVER_HOME                指定 fabric-ca-server 运行的根目录
# FABRIC_CA_SERVER_TLS_ENABLED        是否启用 TLS
# FABRIC_CA_SERVER_CSR_CN            指定证书主体的 cn 字段(https://hyperledger-fabric-ca.readthedocs.io/en/latest/users-guide.html#initializing-the-server)
# FABRIC_CA_SERVER_CSR_HOSTS        指定证书主体的 hosts 字段
# FABRIC_CA_SERVER_DEBUG            是否启用 debug 模式
# FABRIC_CA_SERVER_DB_TYPE            CA使用的DB类型(https://hyperledger-fabric-ca.readthedocs.io/en/latest/users-guide.html#mysql)
# FABRIC_CA_SERVER_DB_DATASOURCE    数据源信息

使用 docker-compose 命令启动 3个 CA 服务器,一个 Orderer 组织的 CA(Orderer CA)和两个银行组织的 CA(Bank1 CA & Bank2 CA)

docker-compose -f docker/docker-compose-ca.yaml up -d

# -f                指定使用的docker compose 文件
# up                启动docker compose文件中配置的所有服务
# -d                后台启动

启动后查看容器是否启动成功

docker ps -a
image-20220926191359346

查看docker容器内部的启动日志

# docker logs [container id]
# 查看 Orderer CA 日志
docker logs e9ae3e555254
# 查看 Orderer Bank1 CA 日志
docker logs a07c4e9be072
# 查看 Orderer Bank2 CA 日志
docker logs baf413d9215e

# 成功启动容器后,将在 CA 容器的日志中看到以下输出
[INFO] Listening on https://0.0.0.0:7054

查看 CA 启动后的目录解构

[root@blockchain finance]# tree organizations/fabric-ca/
organizations/fabric-ca/
├── bank1
│   ├── ca-cert.pem # Bank1 CA 受信的任根证书
│   ├── fabric-ca-server-config.yaml
│   ├── fabric-ca-server.db
│   ├── IssuerPublicKey
│   ├── IssuerRevocationPublicKey
│   ├── msp
│   │   ├── cacerts
│   │   ├── keystore
│   │   │   ├── 02ad9797470eb769c8e9463767d247d8fc45744ebb68dd7c37bf131775c648a2_sk
│   │   │   ├── a1f0a6137fb4247b3f44410a5bc1f3cfb14783953382374f4a7cd7c6bd53f022_sk
│   │   │   ├── IssuerRevocationPrivateKey
│   │   │   └── IssuerSecretKey
│   │   ├── signcerts
│   │   └── user
│   └── tls-cert.pem
├── bank2
│   ├── ca-cert.pem # Bank2 CA 受信的任根证书
│   ├── fabric-ca-server-config.yaml
│   ├── fabric-ca-server.db
│   ├── IssuerPublicKey
│   ├── IssuerRevocationPublicKey
│   ├── msp
│   │   ├── cacerts
│   │   ├── keystore
│   │   │   ├── 34c218016b065dfef8c94ce12424dddb100b13b417ba91eceda3a36e25e841aa_sk
│   │   │   ├── e77175a5fe4ec9e43f3aba4bbe9baa76df0206f105b5f33e38077f91bccb7304_sk
│   │   │   ├── IssuerRevocationPrivateKey
│   │   │   └── IssuerSecretKey
│   │   ├── signcerts
│   │   └── user
│   └── tls-cert.pem
└── orderer
    ├── ca-cert.pem # Orderer CA 受信的任根证书
    ├── fabric-ca-server-config.yaml
    ├── fabric-ca-server.db
    ├── IssuerPublicKey
    ├── IssuerRevocationPublicKey
    ├── msp
    │   ├── cacerts
    │   ├── keystore
    │   │   ├── 72a02120c7d294e6743c452bb74d46be8db7d8dbfd79265d42ea36535823200e_sk
    │   │   ├── afabf4617ef6b27e746644bf5679a694a16cd8e0c5d32d1fe6c88c55069ba5ed_sk
    │   │   ├── IssuerRevocationPrivateKey
    │   │   └── IssuerSecretKey
    │   ├── signcerts
    │   └── user
    └── tls-cert.pem

5. 生成网络中成员的证书文件

1. Orderer 组织

Enroll Orderer CA 管理员

# 指定 Orderer CA 服务器受信的任根证书
export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/orderer/ca-cert.pem
# 指定 Orderer CA 管理员证书存放的目录
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/ordererOrganizations/finance.com
# 执行登记操作
bin/fabric-ca-client enroll -d -u https://admin:adminpw@ca.orderer.finance.com:7054

# 生成用于不同组织通信的证书,由于该CA同时充当组织 CA 和 TLS CA,因此直接将 Orderer CA 启动时生成的CA服务器受信的任根证书复制到组织级 msp 目录中
mkdir -p "${PWD}/organizations/ordererOrganizations/finance.com/msp/tlscacerts"
cp "${PWD}/organizations/fabric-ca/orderer/ca-cert.pem" "${PWD}/organizations/ordererOrganizations/finance.com/msp/tlscacerts/tlsca.finance.com-cert.pem"

编写 config.yaml

创建 Orderer 组织的OU配置文件

echo 'NodeOUs:
  Enable: true
  ClientOUIdentifier:
    Certificate: cacerts/ca-orderer-finance-com-7054.pem
    OrganizationalUnitIdentifier: client
  PeerOUIdentifier:
    Certificate: cacerts/ca-orderer-finance-com-7054.pem
    OrganizationalUnitIdentifier: peer
  AdminOUIdentifier:
    Certificate: cacerts/ca-orderer-finance-com-7054.pem
    OrganizationalUnitIdentifier: admin
  OrdererOUIdentifier:
    Certificate: cacerts/ca-orderer-finance-com-7054.pem
    OrganizationalUnitIdentifier: orderer' > "${PWD}/organizations/ordererOrganizations/finance.com/msp/config.yaml"

# https://hyperledger-fabric.readthedocs.io/en/release-2.4/membership/membership.html?#node-ou-roles-and-msps

Register Orderer

bin/fabric-ca-client register -d --id.name orderer --id.secret ordererpw --id.type orderer -u https://ca.orderer.finance.com:7054

Register Orderer Admin

bin/fabric-ca-client register -d --id.name ordereradmin --id.secret ordereradminpw --id.type admin --id.attrs "hf.Registrar.Roles=*,hf.Registrar.DelegateRoles=*,hf.AffiliationMgr=true,hf.Registrar.Attributes=*,hf.Revoker=true,hf.GenCRL=true,admin=true:ecert" -u https://ca.orderer.finance.com:7054

# https://hyperledger-fabric-ca.readthedocs.io/en/latest/users-guide.html#registering-a-new-identity

Enroll Orderer ECert 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/orderer/ca-cert.pem
# 证书会存放在该目录下 ${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com
export FABRIC_CA_CLIENT_MSPDIR=msp
bin/fabric-ca-client enroll -d -u https://orderer:ordererpw@ca.orderer.finance.com:7054

# 构造 Orderer MSP 目录
cp "${PWD}/organizations/ordererOrganizations/finance.com/msp/config.yaml" "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp/config.yaml"

Enroll Orderer TLS 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/orderer/ca-cert.pem
# 证书会存放在该目录下 ${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com
export FABRIC_CA_CLIENT_MSPDIR=tls

bin/fabric-ca-client enroll -d -u https://orderer:ordererpw@ca.orderer.finance.com:7054 --enrollment.profile tls --csr.hosts "orderer.finance.com,localhost ,127.0.0.1"

# 构造 orderer 的 TLS 证书目录并格式化文件名——用于启动 orderer docker 容器
# TLS 根证书
cp "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/tlscacerts/"* "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/ca.crt"
# TLS 签名证书
cp "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/signcerts/"* "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/server.crt"
# TLS 私钥
cp "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/keystore/"* "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/server.key"

mkdir -p "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp/tlscacerts"

cp "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/tlscacerts/"* "${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp/tlscacerts/tlsca.orderer.finance.com-cert.pem"

Enroll Orderer Admin ECert 证书

# 构造Orderer Admin 的msp证书目录,因为不用于组织间通信,所以不用配置tls
export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/orderer/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/ordererOrganizations/finance.com/users/Admin@finance.com
export FABRIC_CA_CLIENT_MSPDIR=msp

bin/fabric-ca-client enroll -d -u https://ordereradmin:ordereradminpw@ca.orderer.finance.com:7054

# 构造 Admin MSP 目录
cp "${PWD}/organizations/ordererOrganizations/finance.com/msp/config.yaml" "${PWD}/organizations/ordererOrganizations/finance.com/users/Admin@finance.com/msp/config.yaml"

2. Bank1 组织

Enroll Bank1 CA 管理员

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank1/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank1.finance.com

bin/fabric-ca-client enroll -d -u https://admin:adminpw@ca.bank1.finance.com:8054

# 用于不同组织通信,由于该CA同时充当组织 CA和 TLS CA,因此直接将CA启动时生成的组织根证书复制到组织级 msp 目录中
mkdir -p "${PWD}/organizations/peerOrganizations/bank1.finance.com/msp/tlscacerts"
cp "${PWD}/organizations/fabric-ca/bank1/ca-cert.pem" "${PWD}/organizations/peerOrganizations/bank1.finance.com/msp/tlscacerts/tlsca.bank1.finance.com-cert.pem"

编写 config.yaml

echo 'NodeOUs:
  Enable: true
  ClientOUIdentifier:
    Certificate: cacerts/ca-bank1-finance-com-8054.pem
    OrganizationalUnitIdentifier: client
  PeerOUIdentifier:
    Certificate: cacerts/ca-bank1-finance-com-8054.pem
    OrganizationalUnitIdentifier: peer
  AdminOUIdentifier:
    Certificate: cacerts/ca-bank1-finance-com-8054.pem
    OrganizationalUnitIdentifier: admin
  OrdererOUIdentifier:
    Certificate: cacerts/ca-bank1-finance-com-8054.pem
    OrganizationalUnitIdentifier: orderer' > "${PWD}/organizations/peerOrganizations/bank1.finance.com/msp/config.yaml"

Register Bank1 Peer1

bin/fabric-ca-client register -d --id.name peer1 --id.secret peer1pw --id.type peer -u https://ca.bank1.finance.com:8054

Register Bank1 Admin

bin/fabric-ca-client register -d --id.name bank1admin --id.secret bank1adminpw --id.type admin --id.attrs "hf.Registrar.Roles=*,hf.Registrar.DelegateRoles=*,hf.AffiliationMgr=true,hf.Registrar.Attributes=*,hf.Revoker=true,hf.GenCRL=true,admin=true:ecert" -u https://ca.bank1.finance.com:8054

Enroll Bank1 Peer1 ECert 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank1/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com
export FABRIC_CA_CLIENT_MSPDIR=msp

bin/fabric-ca-client enroll -d -u https://peer1:peer1pw@ca.bank1.finance.com:8054

cp "${PWD}/organizations/peerOrganizations/bank1.finance.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/msp/config.yaml"

Enroll Bank1 Peer1 TLS 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank1/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com
export FABRIC_CA_CLIENT_MSPDIR=tls

bin/fabric-ca-client enroll -d -u https://peer1:peer1pw@ca.bank1.finance.com:8054 --enrollment.profile tls --csr.hosts "peer1.bank1.finance.com,localhost,127.0.0.1"

# TLS 根证书
cp "${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/tlscacerts/"* "${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt"
# TLS 签名证书
cp "${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/signcerts/"* "${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt"
# TLS 私钥
cp "${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/keystore/"* "${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key"

Enroll Bank1 Admin ECert 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank1/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com
export FABRIC_CA_CLIENT_MSPDIR=msp

bin/fabric-ca-client enroll -d -u https://bank1admin:bank1adminpw@ca.bank1.finance.com:8054

cp "${PWD}/organizations/peerOrganizations/bank1.finance.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp/config.yaml"

3. Bank2组织

Enroll Bank2 CA 管理员

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank2/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank2.finance.com

bin/fabric-ca-client enroll -d -u https://admin:adminpw@ca.bank2.finance.com:9054

# 用于不同组织通信,由于该CA同时充当组织 CA和 TLS CA,因此直接将CA启动时生成的组织根证书复制到组织级 msp 目录中
mkdir -p "${PWD}/organizations/peerOrganizations/bank2.finance.com/msp/tlscacerts"
cp "${PWD}/organizations/fabric-ca/bank2/ca-cert.pem" "${PWD}/organizations/peerOrganizations/bank2.finance.com/msp/tlscacerts/tlsca.bank2.finance.com-cert.pem"

编写 config.yaml

echo 'NodeOUs:
  Enable: true
  ClientOUIdentifier:
    Certificate: cacerts/ca-bank2-finance-com-9054.pem
    OrganizationalUnitIdentifier: client
  PeerOUIdentifier:
    Certificate: cacerts/ca-bank2-finance-com-9054.pem
    OrganizationalUnitIdentifier: peer
  AdminOUIdentifier:
    Certificate: cacerts/ca-bank2-finance-com-9054.pem
    OrganizationalUnitIdentifier: admin
  OrdererOUIdentifier:
    Certificate: cacerts/ca-bank2-finance-com-9054.pem
    OrganizationalUnitIdentifier: orderer' > "${PWD}/organizations/peerOrganizations/bank2.finance.com/msp/config.yaml"

Register Bank2 Peer1

bin/fabric-ca-client register -d --id.name peer1 --id.secret peer1pw --id.type peer -u https://ca.bank2.finance.com:9054

Register Bank2 Admin

bin/fabric-ca-client register -d --id.name bank2admin --id.secret bank2adminpw --id.type admin --id.attrs "hf.Registrar.Roles=*,hf.Registrar.DelegateRoles=*,hf.AffiliationMgr=true,hf.Registrar.Attributes=*,hf.Revoker=true,hf.GenCRL=true,admin=true:ecert" -u https://ca.bank2.finance.com:9054

Enroll Bank2 Peer1 ECert 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank2/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com
export FABRIC_CA_CLIENT_MSPDIR=msp

bin/fabric-ca-client enroll -d -u https://peer1:peer1pw@ca.bank2.finance.com:9054

cp "${PWD}/organizations/peerOrganizations/bank2.finance.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/msp/config.yaml"

Enroll Bank2 Peer1 TLS 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank2/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com
export FABRIC_CA_CLIENT_MSPDIR=tls

bin/fabric-ca-client enroll -d -u https://peer1:peer1pw@ca.bank2.finance.com:9054 --enrollment.profile tls --csr.hosts "peer1.bank2.finance.com,localhost ,127.0.0.1"

# TLS 根证书
cp "${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/tlscacerts/"* "${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt"
# TLS 签名证书
cp "${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/signcerts/"* "${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.crt"
# TLS 私钥
cp "${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/keystore/"* "${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.key"

Enroll Bank2 Admin ECert 证书

export FABRIC_CA_CLIENT_TLS_CERTFILES=${PWD}/organizations/fabric-ca/bank2/ca-cert.pem
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com
export FABRIC_CA_CLIENT_MSPDIR=msp

bin/fabric-ca-client enroll -d -u https://bank2admin:bank2adminpw@ca.bank2.finance.com:9054

cp "${PWD}/organizations/peerOrganizations/bank2.finance.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com/msp/config.yaml"

6. 生成创世区块

编写 configtx.yaml 用于生成创世区块

mkdir configtx

vim configtx/configtx.yaml
# configtx/configtx.yaml

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

---
################################################################################
#
#   ORGANIZATIONS
#
#   This section defines the organizational identities that can be referenced
#   in the configuration profiles.
#
################################################################################
Organizations:

    # SampleOrg defines an MSP using the sampleconfig. It should never be used
    # in production but may be used as a template for other definitions.
    - &Orderer
        # Name is the key by which this org will be referenced in channel
        # configuration transactions.
        # Name can include alphanumeric characters as well as dots and dashes.
        Name: Orderer

        # SkipAsForeign can be set to true for org definitions which are to be
        # inherited from the orderer system channel during channel creation.  This
        # is especially useful when an admin of a single org without access to the
        # MSP directories of the other orgs wishes to create a channel.  Note
        # this property must always be set to false for orgs included in block
        # creation.
        SkipAsForeign: false

        # ID is the key by which this org's MSP definition will be referenced.
        # ID can include alphanumeric characters as well as dots and dashes.
        ID: OrdererMSP

        # MSPDir is the filesystem path which contains the MSP configuration.
        MSPDir: ../organizations/ordererOrganizations/finance.com/msp

        # Policies defines the set of policies at this level of the config tree
        # For organization policies, their canonical path is usually
        #   /Channel/<Application|Orderer>/<OrgName>/<PolicyName>
        Policies:
            Readers:
                Type: Signature
                Rule: "OR('OrdererMSP.member')"
                # If your MSP is configured with the new NodeOUs, you might
                # want to use a more specific rule like the following:
                # Rule: "OR('SampleOrg.admin', 'SampleOrg.peer', 'SampleOrg.client')"
            Writers:
                Type: Signature
                Rule: "OR('OrdererMSP.member')"
                # If your MSP is configured with the new NodeOUs, you might
                # want to use a more specific rule like the following:
                # Rule: "OR('SampleOrg.admin', 'SampleOrg.client')"
            Admins:
                Type: Signature
                Rule: "OR('OrdererMSP.admin')"
            Endorsement:
                Type: Signature
                Rule: "OR('OrdererMSP.member')"

        # OrdererEndpoints is a list of all orderers this org runs which clients
        # and peers may to connect to to push transactions and receive blocks respectively.
        OrdererEndpoints:
            - "orderer.finance.com:7050"

        # AnchorPeers defines the location of peers which can be used for
        # cross-org gossip communication.
        #
        # NOTE: this value should only be set when using the deprecated
        # `configtxgen --outputAnchorPeersUpdate` command. It is recommended
        # to instead use the channel configuration update process to set the
        # anchor peers for each organization.
        # AnchorPeers:
        #     - Host: 127.0.0.1
        #       Port: 7051

    - &Bank1
        # DefaultOrg defines the organization which is used in the sampleconfig
        # of the fabric.git development environment
        Name: Bank1

        # ID to load the MSP definition as
        ID: Bank1MSP

        MSPDir: ../organizations/peerOrganizations/bank1.finance.com/msp

        # Policies defines the set of policies at this level of the config tree
        # For organization policies, their canonical path is usually
        #   /Channel/<Application|Orderer>/<OrgName>/<PolicyName>
        Policies:
            Readers:
                Type: Signature
                Rule: "OR('Bank1MSP.admin', 'Bank1MSP.peer', 'Bank1MSP.client', 'Bank1MSP.member')"
            Writers:
                Type: Signature
                Rule: "OR('Bank1MSP.admin', 'Bank1MSP.client', 'Bank1MSP.member')"
            Admins:
                Type: Signature
                Rule: "OR('Bank1MSP.admin')"
            Endorsement:
                Type: Signature
                Rule: "OR('Bank1MSP.peer')"

        # leave this flag set to true.
        AnchorPeers:
            # AnchorPeers defines the location of peers which can be used
            # for cross org gossip communication.  Note, this value is only
            # encoded in the genesis block in the Application section context
            - Host: peer1.bank1.finance.com
              Port: 7051

    - &Bank2
        # DefaultOrg defines the organization which is used in the sampleconfig
        # of the fabric.git development environment
        Name: Bank2

        # ID to load the MSP definition as
        ID: Bank2MSP

        MSPDir: ../organizations/peerOrganizations/bank2.finance.com/msp

        # Policies defines the set of policies at this level of the config tree
        # For organization policies, their canonical path is usually
        #   /Channel/<Application|Orderer>/<OrgName>/<PolicyName>
        Policies:
            Readers:
                Type: Signature
                Rule: "OR('Bank2MSP.admin', 'Bank2MSP.peer', 'Bank2MSP.client', 'Bank2MSP.member')"
            Writers:
                Type: Signature
                Rule: "OR('Bank2MSP.admin', 'Bank2MSP.client', 'Bank2MSP.member')"
            Admins:
                Type: Signature
                Rule: "OR('Bank2MSP.admin')"
            Endorsement:
                Type: Signature
                Rule: "OR('Bank2MSP.peer')"

        # leave this flag set to true.
        AnchorPeers:
            # AnchorPeers defines the location of peers which can be used
            # for cross org gossip communication.  Note, this value is only
            # encoded in the genesis block in the Application section context
            - Host: peer1.bank2.finance.com
              Port: 9051

################################################################################
#
#   CAPABILITIES
#
#   This section defines the capabilities of fabric network. This is a new
#   concept as of v1.1.0 and should not be utilized in mixed networks with
#   v1.0.x peers and orderers.  Capabilities define features which must be
#   present in a fabric binary for that binary to safely participate in the
#   fabric network.  For instance, if a new MSP type is added, newer binaries
#   might recognize and validate the signatures from this type, while older
#   binaries without this support would be unable to validate those
#   transactions.  This could lead to different versions of the fabric binaries
#   having different world states.  Instead, defining a capability for a channel
#   informs those binaries without this capability that they must cease
#   processing transactions until they have been upgraded.  For v1.0.x if any
#   capabilities are defined (including a map with all capabilities turned off)
#   then the v1.0.x peer will deliberately crash.
#
################################################################################
Capabilities:
    # Channel capabilities apply to both the orderers and the peers and must be
    # supported by both.
    # Set the value of the capability to true to require it.
    Channel: &ChannelCapabilities
        # V2.0 for Channel is a catchall flag for behavior which has been
        # determined to be desired for all orderers and peers running at the v2.0.0
        # level, but which would be incompatible with orderers and peers from
        # prior releases.
        # Prior to enabling V2.0 channel capabilities, ensure that all
        # orderers and peers on a channel are at v2.0.0 or later.
        V2_0: true

    # Orderer capabilities apply only to the orderers, and may be safely
    # used with prior release peers.
    # Set the value of the capability to true to require it.
    Orderer: &OrdererCapabilities
        # V1.1 for Orderer is a catchall flag for behavior which has been
        # determined to be desired for all orderers running at the v1.1.x
        # level, but which would be incompatible with orderers from prior releases.
        # Prior to enabling V2.0 orderer capabilities, ensure that all
        # orderers on a channel are at v2.0.0 or later.
        V2_0: true

    # Application capabilities apply only to the peer network, and may be safely
    # used with prior release orderers.
    # Set the value of the capability to true to require it.
    Application: &ApplicationCapabilities
        # V2.0 for Application enables the new non-backwards compatible
        # features and fixes of fabric v2.0.
        # Prior to enabling V2.0 orderer capabilities, ensure that all
        # orderers on a channel are at v2.0.0 or later.
        V2_0: true

################################################################################
#
#   APPLICATION
#
#   This section defines the values to encode into a config transaction or
#   genesis block for application-related parameters.
#
################################################################################
Application: &ApplicationDefaults
    ACLs: &ACLsDefault
        # This section provides defaults for policies for various resources
        # in the system. These "resources" could be functions on system chaincodes
        # (e.g., "GetBlockByNumber" on the "qscc" system chaincode) or other resources
        # (e.g.,who can receive Block events). This section does NOT specify the resource's
        # definition or API, but just the ACL policy for it.
        #
        # Users can override these defaults with their own policy mapping by defining the
        # mapping under ACLs in their channel definition

        #---New Lifecycle System Chaincode (_lifecycle) function to policy mapping for access control--#

        # ACL policy for _lifecycle's "CheckCommitReadiness" function
        _lifecycle/CheckCommitReadiness: /Channel/Application/Writers

        # ACL policy for _lifecycle's "CommitChaincodeDefinition" function
        _lifecycle/CommitChaincodeDefinition: /Channel/Application/Writers

        # ACL policy for _lifecycle's "QueryChaincodeDefinition" function
        _lifecycle/QueryChaincodeDefinition: /Channel/Application/Writers

        # ACL policy for _lifecycle's "QueryChaincodeDefinitions" function
        _lifecycle/QueryChaincodeDefinitions: /Channel/Application/Writers

        #---Lifecycle System Chaincode (lscc) function to policy mapping for access control---#

        # ACL policy for lscc's "getid" function
        lscc/ChaincodeExists: /Channel/Application/Readers

        # ACL policy for lscc's "getdepspec" function
        lscc/GetDeploymentSpec: /Channel/Application/Readers

        # ACL policy for lscc's "getccdata" function
        lscc/GetChaincodeData: /Channel/Application/Readers

        # ACL Policy for lscc's "getchaincodes" function
        lscc/GetInstantiatedChaincodes: /Channel/Application/Readers

        #---Query System Chaincode (qscc) function to policy mapping for access control---#

        # ACL policy for qscc's "GetChainInfo" function
        qscc/GetChainInfo: /Channel/Application/Readers

        # ACL policy for qscc's "GetBlockByNumber" function
        qscc/GetBlockByNumber: /Channel/Application/Readers

        # ACL policy for qscc's  "GetBlockByHash" function
        qscc/GetBlockByHash: /Channel/Application/Readers

        # ACL policy for qscc's "GetTransactionByID" function
        qscc/GetTransactionByID: /Channel/Application/Readers

        # ACL policy for qscc's "GetBlockByTxID" function
        qscc/GetBlockByTxID: /Channel/Application/Readers

        #---Configuration System Chaincode (cscc) function to policy mapping for access control---#

        # ACL policy for cscc's "GetConfigBlock" function
        cscc/GetConfigBlock: /Channel/Application/Readers

        # ACL policy for cscc's "GetChannelConfig" function
        cscc/GetChannelConfig: /Channel/Application/Readers

        #---Miscellaneous peer function to policy mapping for access control---#

        # ACL policy for invoking chaincodes on peer
        peer/Propose: /Channel/Application/Writers

        # ACL policy for chaincode to chaincode invocation
        peer/ChaincodeToChaincode: /Channel/Application/Writers

        #---Events resource to policy mapping for access control###---#

        # ACL policy for sending block events
        event/Block: /Channel/Application/Readers

        # ACL policy for sending filtered block events
        event/FilteredBlock: /Channel/Application/Readers

    # Organizations lists the orgs participating on the application side of the
    # network.
    Organizations:

    # Policies defines the set of policies at this level of the config tree
    # For Application policies, their canonical path is
    #   /Channel/Application/<PolicyName>
    Policies: &ApplicationDefaultPolicies
        LifecycleEndorsement:
            Type: ImplicitMeta
            Rule: "MAJORITY Endorsement"
        Endorsement:
            Type: ImplicitMeta
            Rule: "MAJORITY Endorsement"
        Readers:
            Type: ImplicitMeta
            Rule: "ANY Readers"
        Writers:
            Type: ImplicitMeta
            Rule: "ANY Writers"
        Admins:
            Type: ImplicitMeta
            Rule: "MAJORITY Admins"

    # Capabilities describes the application level capabilities, see the
    # dedicated Capabilities section elsewhere in this file for a full
    # description
    Capabilities:
        <<: *ApplicationCapabilities

################################################################################
#
#   ORDERER
#
#   This section defines the values to encode into a config transaction or
#   genesis block for orderer related parameters.
#
################################################################################
Orderer: &OrdererDefaults

    # Orderer Type: The orderer implementation to start.
    # Available types are "solo", "kafka" and "etcdraft".
    OrdererType: etcdraft

    # Addresses used to be the list of orderer addresses that clients and peers
    # could connect to.  However, this does not allow clients to associate orderer
    # addresses and orderer organizations which can be useful for things such
    # as TLS validation.  The preferred way to specify orderer addresses is now
    # to include the OrdererEndpoints item in your org definition
    Addresses:
        - orderer.finance.com:7050

    # Batch Timeout: The amount of time to wait before creating a batch.
    BatchTimeout: 2s

    # Batch Size: Controls the number of messages batched into a block.
    # The orderer views messages opaquely, but typically, messages may
    # be considered to be Fabric transactions.  The 'batch' is the group
    # of messages in the 'data' field of the block.  Blocks will be a few kb
    # larger than the batch size, when signatures, hashes, and other metadata
    # is applied.
    BatchSize:

        # Max Message Count: The maximum number of messages to permit in a
        # batch.  No block will contain more than this number of messages.
        MaxMessageCount: 500

        # Absolute Max Bytes: The absolute maximum number of bytes allowed for
        # the serialized messages in a batch. The maximum block size is this value
        # plus the size of the associated metadata (usually a few KB depending
        # upon the size of the signing identities). Any transaction larger than
        # this value will be rejected by ordering.
        # It is recommended not to exceed 49 MB, given the default grpc max message size of 100 MB
        # configured on orderer and peer nodes (and allowing for message expansion during communication).
        AbsoluteMaxBytes: 10 MB

        # Preferred Max Bytes: The preferred maximum number of bytes allowed
        # for the serialized messages in a batch. Roughly, this field may be considered
        # the best effort maximum size of a batch. A batch will fill with messages
        # until this size is reached (or the max message count, or batch timeout is
        # exceeded).  If adding a new message to the batch would cause the batch to
        # exceed the preferred max bytes, then the current batch is closed and written
        # to a block, and a new batch containing the new message is created.  If a
        # message larger than the preferred max bytes is received, then its batch
        # will contain only that message.  Because messages may be larger than
        # preferred max bytes (up to AbsoluteMaxBytes), some batches may exceed
        # the preferred max bytes, but will always contain exactly one transaction.
        PreferredMaxBytes: 2 MB

    # Max Channels is the maximum number of channels to allow on the ordering
    # network. When set to 0, this implies no maximum number of channels.
    MaxChannels: 0

    Kafka:
        # Brokers: A list of Kafka brokers to which the orderer connects. Edit
        # this list to identify the brokers of the ordering service.
        # NOTE: Use IP:port notation.
        Brokers:
            - kafka0:9092
            - kafka1:9092
            - kafka2:9092

    # EtcdRaft defines configuration which must be set when the "etcdraft"
    # orderertype is chosen.
    EtcdRaft:
        # The set of Raft replicas for this network. For the etcd/raft-based
        # implementation, we expect every replica to also be an OSN. Therefore,
        # a subset of the host:port items enumerated in this list should be
        # replicated under the Orderer.Addresses key above.
        Consenters:
        - Host: orderer.finance.com
          Port: 7050
          ClientTLSCert: ../organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/server.crt
          ServerTLSCert: ../organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls/server.crt

        # Options to be specified for all the etcd/raft nodes. The values here
        # are the defaults for all new channels and can be modified on a
        # per-channel basis via configuration updates.
        Options:
            # TickInterval is the time interval between two Node.Tick invocations.
            TickInterval: 500ms

            # ElectionTick is the number of Node.Tick invocations that must pass
            # between elections. That is, if a follower does not receive any
            # message from the leader of current term before ElectionTick has
            # elapsed, it will become candidate and start an election.
            # ElectionTick must be greater than HeartbeatTick.
            ElectionTick: 10

            # HeartbeatTick is the number of Node.Tick invocations that must
            # pass between heartbeats. That is, a leader sends heartbeat
            # messages to maintain its leadership every HeartbeatTick ticks.
            HeartbeatTick: 1

            # MaxInflightBlocks limits the max number of in-flight append messages
            # during optimistic replication phase.
            MaxInflightBlocks: 5

            # SnapshotIntervalSize defines number of bytes per which a snapshot is taken
            SnapshotIntervalSize: 16 MB

    # Organizations lists the orgs participating on the orderer side of the
    # network.
    Organizations:

    # Policies defines the set of policies at this level of the config tree
    # For Orderer policies, their canonical path is
    #   /Channel/Orderer/<PolicyName>
    Policies:
        Readers:
            Type: ImplicitMeta
            Rule: "ANY Readers"
        Writers:
            Type: ImplicitMeta
            Rule: "ANY Writers"
        Admins:
            Type: ImplicitMeta
            Rule: "MAJORITY Admins"
        # BlockValidation specifies what signatures must be included in the block
        # from the orderer for the peer to validate it.
        BlockValidation:
            Type: ImplicitMeta
            Rule: "ANY Writers"

    # Capabilities describes the orderer level capabilities, see the
    # dedicated Capabilities section elsewhere in this file for a full
    # description
    Capabilities:
        <<: *OrdererCapabilities

################################################################################
#
#   CHANNEL
#
#   This section defines the values to encode into a config transaction or
#   genesis block for channel related parameters.
#
################################################################################
Channel: &ChannelDefaults
    # Policies defines the set of policies at this level of the config tree
    # For Channel policies, their canonical path is
    #   /Channel/<PolicyName>
    Policies:
        # Who may invoke the 'Deliver' API
        Readers:
            Type: ImplicitMeta
            Rule: "ANY Readers"
        # Who may invoke the 'Broadcast' API
        Writers:
            Type: ImplicitMeta
            Rule: "ANY Writers"
        # By default, who may modify elements at this config level
        Admins:
            Type: ImplicitMeta
            Rule: "MAJORITY Admins"


    # Capabilities describes the channel level capabilities, see the
    # dedicated Capabilities section elsewhere in this file for a full
    # description
    Capabilities:
        <<: *ChannelCapabilities

################################################################################
#
#   PROFILES
#
#   Different configuration profiles may be encoded here to be specified as
#   parameters to the configtxgen tool. The profiles which specify consortiums
#   are to be used for generating the orderer genesis block. With the correct
#   consortium members defined in the orderer genesis block, channel creation
#   requests may be generated with only the org member names and a consortium
#   name.
#
################################################################################
Profiles:
    SystemGenesis:
        <<: *ChannelDefaults
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *Orderer
            Capabilities:
                <<: *OrdererCapabilities
        Consortiums:
            FinanceConsortium:
                Organizations:
                    - *Bank1
                    - *Bank2
    CrossBorderPaymentChannel:
        Consortium: FinanceConsortium
        <<: *ChannelDefaults
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Bank1
                - *Bank2
            Capabilities:
                <<: *ApplicationCapabilities

生成创世区块文件

bin/configtxgen -configPath ./configtx -profile SystemGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block
image-20220927172340598

7. 生成通道文件

bin/configtxgen -configPath ./configtx -profile CrossBorderPaymentChannel -outputCreateChannelTx ./channel-artifacts/cross-border-payment-channel.tx -channelID cross-border-payment-channel
image-20220927173047152

8. 生成锚节点配置更新文件

# Bank1 锚节点配置更新文件
bin/configtxgen -configPath ./configtx -profile CrossBorderPaymentChannel -outputAnchorPeersUpdate ./channel-artifacts/Bank1MSPanchors.tx -channelID cross-border-payment-channel -asOrg Bank1
# Bank2 锚节点配置更新文件
bin/configtxgen -configPath ./configtx -profile CrossBorderPaymentChannel -outputAnchorPeersUpdate ./channel-artifacts/Bank2MSPanchors.tx -channelID cross-border-payment-channel -asOrg Bank2

# https://hyperledger-fabric.readthedocs.io/zh_CN/latest/gossip.html#id5
image-20220927173413115

9. 部署 Orderer 和 Peer 节点

编写 docker-compose-finance.yaml

vim docker/docker-compose-finance.yaml
# docker/docker-compose-finance.yaml
version: '2.0'
volumes:
  orderer.finance.com:
  peer1.bank1.finance.com:
  peer1.bank2.finance.com:

networks:
  orderer:
    name: orderer
  bank1:
    name: bank1
  bank2:
    name: bank2
  finance:
    name: finance

services:

  orderer.finance.com:
    container_name: orderer.finance.com
    image: hyperledger/fabric-orderer:2.4.1
    environment:
      - FABRIC_LOGGING_SPEC=INFO
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
      - ORDERER_GENERAL_LISTENPORT=7050
      - ORDERER_GENERAL_LOCALMSPID=OrdererMSP
      - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
      - ORDERER_GENERAL_GENESISMETHOD=file
      - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
      # Enabled TLS
      - ORDERER_GENERAL_TLS_ENABLED=true
      - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
      - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
      - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
      - ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/var/hyperledger/orderer/tls/server.crt
      - ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/var/hyperledger/orderer/tls/server.key
      - ORDERER_GENERAL_CLUSTER_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric
    command: orderer
    volumes:
      - ../system-genesis-block/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
      - ../organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp:/var/hyperledger/orderer/msp
      - ../organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/tls:/var/hyperledger/orderer/tls
      - orderer.finance.com:/var/hyperledger/production/orderer
    ports:
      - 7050:7050
    networks:
      - finance
      - orderer

  peer1.bank1.finance.com:
    container_name: peer1.bank1.finance.com
    image: hyperledger/fabric-peer:2.4.1
    environment:
      # Generic peer variables
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      # the following setting starts chaincode containers on the same
      # bridge network as the peers
      # https://docs.docker.com/compose/networking/
      - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=bank1
      - FABRIC_LOGGING_SPEC=INFO
      #- FABRIC_LOGGING_SPEC=DEBUG
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
      # Peer specific variabes
      - CORE_PEER_ID=peer1.bank1.finance.com
      - CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:7051
      - CORE_PEER_CHAINCODEADDRESS=peer1.bank1.finance.com:7052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.bank1.finance.com:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.bank1.finance.com:7051
      - CORE_PEER_LOCALMSPID=Bank1MSP
      - CORE_CHAINCODE_GOLANG_RUNTIME=hyperledger/fabric-baseos:2.4.1
      - CORE_CHAINCODE_JAVA_RUNTIME=hyperledger/fabric-javaenv:2.4.1
      - CORE_CHAINCODE_NODE_RUNTIME=hyperledger/fabric-nodeenv:2.4.1
    volumes:
      - /var/run/:/host/var/run/
      - ../organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/msp:/etc/hyperledger/fabric/msp
      - ../organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls:/etc/hyperledger/fabric/tls
      - peer1.bank1.finance.com:/var/hyperledger/production
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    ports:
      - 7051:7051
    networks:
      - finance
      - bank1

  peer1.bank2.finance.com:
    container_name: peer1.bank2.finance.com
    image: hyperledger/fabric-peer:2.4.1
    environment:
      # Generic peer variables
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      # the following setting starts chaincode containers on the same
      # bridge network as the peers
      # https://docs.docker.com/compose/networking/
      - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=bank2
      - FABRIC_LOGGING_SPEC=INFO
      #- FABRIC_LOGGING_SPEC=DEBUG
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
      # Peer specific variabes
      - CORE_PEER_ID=peer1.bank2.finance.com
      - CORE_PEER_ADDRESS=peer1.bank2.finance.com:9051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:9051
      - CORE_PEER_CHAINCODEADDRESS=peer1.bank2.finance.com:9052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:9052
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.bank2.finance.com:9051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.bank2.finance.com:9051
      - CORE_PEER_LOCALMSPID=Bank2MSP
      - CORE_CHAINCODE_GOLANG_RUNTIME=hyperledger/fabric-baseos:2.4.1
      - CORE_CHAINCODE_JAVA_RUNTIME=hyperledger/fabric-javaenv:2.4.1
      - CORE_CHAINCODE_NODE_RUNTIME=hyperledger/fabric-nodeenv:2.4.1
    volumes:
      - /var/run/:/host/var/run/
      - ../organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/msp:/etc/hyperledger/fabric/msp
      - ../organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls:/etc/hyperledger/fabric/tls
      - peer1.bank2.finance.com:/var/hyperledger/production
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    ports:
      - 9051:9051
    networks:
      - finance
      - bank2
# Peer 配置详解
# FABRIC_LOGGING_SPEC                            指定日志级别
# CORE_PEER_ID                                    Peer 在网络中的 ID 信息,用于辨识不同的节点
# CORE_PEER_LISTENADDRESS                        服务监听的本地地址,本地有多个网络接口时可指定仅监听某个接口
# CORE_PEER_ADDRESS                                对同组织内其他节点的监听连接地址。当服务在NAT设备上运行时,该配置可以指定服务对外宣称的可访问地址。如果是客户端,则作为其连接的 Peer 服务地址
# CORE_PEER_LOCALMSPID                            Peer 所关联的 MSPID ,一般为所属组织名称,需要与通道配置内名称一致
# CORE_PEER_MSPCONFIGPATH                        MSP 目录所在的路径,可以为绝对路径,或相对配置目录的路径
# CORE_PEER_TLS_ENABLED                            是否开启 server 端 TLS 检查
# CORE_PEER_TLS_CERT_FILE                        server 端使用的 TLS 证书路径
# CORE_PEER_TLS_KEY_FILE                        server 端使用的 TLS 私钥路径
# CORE_PEER_TLS_ROOTCERT_FILE                    server 端使用的根CA的证书,签发服务端的TLS证书
# CORE_PEER_GOSSIP_USELEADERELECTION            是否允许节点之间动态进行组织的代表(leader)节点选举,通常情况下推荐开启
# CORE_PEER_GOSSIP_ORGLEADER                    本节点是否指定为组织的代表节点,与useLeaderElection不能同时指定为true
# CORE_PEER_GOSSIP_EXTERNALENDPOINT                节点向组织外节点公开的服务地址,默认为空,代表不被其他组织所感知
# CORE_VM_ENDPOINT                                docker daemon 的地址
# CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE            运行链码容器的网络

# Orderer 配置详解
# ORDERER_HOME                                    orderer 运行的根目录
# ORDERER_HOST                                    orderer 运行的主机
# ORDERER_GENERAL_LOCALMSPID                    orderer 所关联的 MSPID ,一般为所属组织名称,需要与通道配置内名称一致
# ORDERER_GENERAL_LISTENPORT                    服务绑定的监听端口
# ORDERER_GENERAL_LISTENADDRESS                    服务绑定的监听地址,一般需要指定为所服务的特定网络接口的地址或全网(0.0.0.0)
# ORDERER_GENERAL_BOOTSTRAPMETHOD                获取引导块的方法,2.x版本中仅支持file或none
# ORDERER_CHANNELPARTICIPATION_ENABLED            是否提供参与通道的 API
# ORDERER_GENERAL_GENESISMETHOD                    指定创世区块类型
# ORDERER_GENERAL_GENESISFILE                    指定创世区块位置
# ORDERER_GENERAL_LOCALMSPDIR                    本地 MSP 文件路径
# ORDERER_GENERAL_LOGLEVEL                        日志级别
# ORDERER_GENERAL_TLS_ENABLED                    启用TLS时的相关配置
# ORDERER_GENERAL_TLS_CERTIFICATE                Orderer 身份证书
# ORDERER_GENERAL_TLS_PRIVATEKEY                Orderer 签名私钥
# ORDERER_GENERAL_TLS_ROOTCAS                    信任的根证书
# ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE        双向TLS认证时,作为客户端证书的文件路径,如果没设置会使用 TLS.Certificate
# ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY        双向TLS认证时,作为客户端私钥的文件路径,如果没设置会使用 TLS.PrivateKey
# ORDERER_GENERAL_CLUSTER_ROOTCAS                信任的根证书
# ORDERER_ADMIN_TLS_ENABLED                        是否启用 orderer 的管理服务面板
# ORDERER_ADMIN_TLS_CERTIFICATE                    管理服务的证书
# ORDERER_ADMIN_TLS_PRIVATEKEY                    管理服务的私钥
# ORDERER_ADMIN_TLS_ROOTCAS                        管理服务的可信根证书
# ORDERER_ADMIN_TLS_CLIENTROOTCAS                管理服务客户端的可信根证书
# ORDERER_ADMIN_LISTENADDRESS                    管理服务监听地址
# ORDERER_METRICS_PROVIDER                        统计服务类型,可以为statsd(推送模式),prometheus(拉取模式),disabled
# ORDERER_OPERATIONS_LISTENADDRESS                RESTful 管理服务的监听地址
# ORDERER_DEBUG_BROADCASTTRACEDIR                广播请求的追踪路径

在 Fabric 中支持两种类型的状态数据库。

  • LevelDB 是嵌入对等节点的默认状态数据库。LevelDB 将链码数据存储为简单的键值对,仅支持键、键范围和复合键查询。
  • CouchDB 是一个可选的备用状态数据库,它允许您将分类帐上的数据建模为 JSON,并针对数据值而不是键发出丰富的查询。

我们在这里不使用Peer节点嵌入的 LevelDB,而是使用 CouchDB 作为数据库。

编写 docker-compose-couchdb.yaml

vim docker/docker-compose-couchdb.yaml
# docker/docker-compose-couchdb.yaml
version: '2.0'
networks:
  bank1:
    name: bank1
  bank2:
    name: bank2
  finance:
    name: finance

services:
  peer1.bank1.couchdb.com:
    hostname: peer1.bank1.couchdb.com
    container_name: peer1.bank1.couchdb.com
    image: couchdb:3.1.1
    # Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
    # for CouchDB.  This will prevent CouchDB from operating in an "Admin Party" mode.
    environment:
      - COUCHDB_USER=admin
      - COUCHDB_PASSWORD=adminpw
    # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service,
    # for example map it to utilize Fauxton User Interface in dev environments.
    ports:
      - "5984:5984"
    networks:
      - finance
      - bank1

  peer1.bank1.finance.com:
    environment:
      - CORE_LEDGER_STATE_STATEDATABASE=CouchDB
      - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=peer1.bank1.couchdb.com:5984
      # The CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME and CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD
      # provide the credentials for ledger to connect to CouchDB.  The username and password must
      # match the username and password set for the associated CouchDB.
      - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin
      - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=adminpw
    depends_on:
      - peer1.bank1.couchdb.com

  peer1.bank2.couchdb.com:
    hostname: peer1.bank2.couchdb.com
    container_name: peer1.bank2.couchdb.com
    image: couchdb:3.1.1
    # Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
    # for CouchDB.  This will prevent CouchDB from operating in an "Admin Party" mode.
    environment:
      - COUCHDB_USER=admin
      - COUCHDB_PASSWORD=adminpw
    # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service,
    # for example map it to utilize Fauxton User Interface in dev environments.
    ports:
      - "7984:5984"
    networks:
      - finance
      - bank2

  peer1.bank2.finance.com:
    environment:
      - CORE_LEDGER_STATE_STATEDATABASE=CouchDB
      - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=peer1.bank2.couchdb.com:5984
      # The CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME and CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD
      # provide the credentials for ledger to connect to CouchDB.  The username and password must
      # match the username and password set for the associated CouchDB.
      - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin
      - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=adminpw
    depends_on:
      - peer1.bank2.couchdb.com

使用 docker-compose 命令启动 Orderer、Peer 和 CouchDB

docker-compose -f docker/docker-compose-finance.yaml -f docker/docker-compose-couchdb.yaml up -d
image-20220927180044717

10. 创建并加入通道

Bank1 Peer1 创建跨境支付通道

# Bank1 创建通道
export FABRIC_CFG_PATH=${PWD}/config
export ORDERER_ADDRESS=orderer.finance.com:7050
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp/tlscacerts/tlsca.orderer.finance.com-cert.pem
export CHANNEL_NAME=cross-border-payment-channel

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt

export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

cd channel-artifacts

../bin/peer channel create -o ${ORDERER_ADDRESS} -c ${CHANNEL_NAME} -f ${CHANNEL_NAME}.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA

cd ..
image-20220928150112001

Bank1 Peer1 加入通道

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

bin/peer channel join -b ./channel-artifacts/${CHANNEL_NAME}.block
image-20220928150212405

Bank2 Peer1 加入通道

export CORE_PEER_LOCALMSPID=Bank2MSP
export CORE_PEER_ADDRESS=peer1.bank2.finance.com:9051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com/msp

bin/peer channel join -b ./channel-artifacts/${CHANNEL_NAME}.block
image-20220928150300796

Bank1 Peer1 更新锚节点

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

bin/peer channel update -o ${ORDERER_ADDRESS} -c ${CHANNEL_NAME} -f ./channel-artifacts/Bank1MSPanchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA
image-20220928150342938

Bank2 Peer1 更新锚节点

export CORE_PEER_LOCALMSPID=Bank2MSP
export CORE_PEER_ADDRESS=peer1.bank2.finance.com:9051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com/msp

bin/peer channel update -o ${ORDERER_ADDRESS} -c ${CHANNEL_NAME} -f ./channel-artifacts/Bank2MSPanchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA
image-20220928150430926

11. 测试网络

测试链码内容

@Contract(
        name = "Cross Border Payment Contract",
        info = @Info(
                title = "Cross Border Payment Contract",
                description = "Hyperledger FabricCross Border Payment Contract",
                version = "1.0.0-SNAPSHOT",
                license = @License(
                        name = "Apache 2.0 License",
                        url = "http://www.apache.org/licenses/LICENSE-2.0.html"
                ),
                contact = @Contact(
                        email = "joe@simnectz.com",
                        name = "Joe",
                        url = "https://blockchain.simnectz.com"
                )
        )
)
@Default
public class SimpleContract implements ContractInterface {

    private final static Logger log = LoggerFactory.getLogger(SimpleContract.class);

    @Transaction(name = "Init", intent = Transaction.TYPE.SUBMIT)
    public String init(final Context context) {
        ChaincodeStub chaincodeStub = context.getStub();

        long transactionTimestamp = chaincodeStub.getTxTimestamp().toEpochMilli();

        AccountContext account1 = new AccountContext("CN0000000001", BigDecimal.valueOf(100));
        AccountContext account2 = new AccountContext("CN0000000002", BigDecimal.valueOf(100));

        chaincodeStub.putStringState(account1.getAccountNumber(), JSON.toJSONString(account1));
        chaincodeStub.putStringState(account2.getAccountNumber(), JSON.toJSONString(account2));

        return JSON.toJSONString(new ResultUtil<>(
                20000, "Chaincode initialization successfully.", transactionTimestamp));
    }

    @Transaction(name = "Transfer", intent = Transaction.TYPE.SUBMIT)
    public String invoke(
            final Context context,
            final String fromAccountNumber,
            final String toAccountNumber,
            final String transferAmount
    ) {
        ChaincodeStub chaincodeStub = context.getStub();
        long transactionTimestamp = chaincodeStub.getTxTimestamp().toEpochMilli();

        // validation fromAccountNumber
        if (fromAccountNumber == null || "".equals(fromAccountNumber) ||
                fromAccountNumber.length() == 0 || accountNotExists(context, fromAccountNumber)
        ) {
            return JSON.toJSONString(new ResultUtil<>(
                    40000,
                    String.format("Wrong account number transferred out: '%s'", fromAccountNumber),
                    transactionTimestamp
            ));
        }

        // validation toAccountNumber
        if (toAccountNumber == null || "".equals(toAccountNumber) ||
                toAccountNumber.length() == 0 || accountNotExists(context, toAccountNumber)
        ) {
            return JSON.toJSONString(new ResultUtil<>(
                    40000,
                    String.format("Wrong account number transferred in: '%s'", toAccountNumber),
                    transactionTimestamp
            ));
        }

        AccountContext fromAccountContext = JSON.parseObject(
                chaincodeStub.getStringState(fromAccountNumber), AccountContext.class);

        // validation fromAccountNumber balance
        BigDecimal transferAmountNumber;
        try {
            transferAmountNumber = new BigDecimal(transferAmount);
        } catch (Exception e) {
            e.printStackTrace();
            return JSON.toJSONString(new ResultUtil<>(
                    40000,
                    String.format("Incorrect transfer amount: '%s'", transferAmount),
                    transactionTimestamp
            ));
        }

        if (fromAccountContext.getBalance().compareTo(transferAmountNumber) < 0) {
            return JSON.toJSONString(new ResultUtil<>(
                    40000,
                    "Sorry, your credit is running low.",
                    transactionTimestamp));
        }

        AccountContext toAccountContext = JSON.parseObject(
                chaincodeStub.getStringState(toAccountNumber), AccountContext.class);

        // Execute transfer
        fromAccountContext.setBalance(fromAccountContext.getBalance().subtract(transferAmountNumber));
        toAccountContext.setBalance(toAccountContext.getBalance().add(transferAmountNumber));

        chaincodeStub.putStringState(fromAccountContext.getAccountNumber(), JSON.toJSONString(fromAccountContext));
        chaincodeStub.putStringState(toAccountContext.getAccountNumber(), JSON.toJSONString(toAccountContext));

        return JSON.toJSONString(new ResultUtil<>(
                20000,
                "Successful Operation",
                transactionTimestamp
        ));
    }

    @Transaction(name = "QueryBalance", intent = Transaction.TYPE.EVALUATE)
    public String invoke(
            final Context context,
            final String accountNumber
    ) {
        ChaincodeStub chaincodeStub = context.getStub();
        long transactionTimestamp = chaincodeStub.getTxTimestamp().toEpochMilli();

        // validation toAccountNumber
        if (accountNumber == null || "".equals(accountNumber) ||
                accountNumber.length() == 0 || accountNotExists(context, accountNumber)
        ) {
            return JSON.toJSONString(new ResultUtil<>(
                    40000,
                    String.format("Wrong account number: %s", accountNumber),
                    transactionTimestamp
            ));
        }

        // query account information
        AccountContext accountContext = JSON.parseObject(
                chaincodeStub.getStringState(accountNumber), AccountContext.class);

        return JSON.toJSONString(new ResultUtil<>(
                20000,
                "Successful Operation",
                accountContext, transactionTimestamp
        ));
    }

    private boolean accountNotExists(final Context context, final String accountNumber) {
        return context.getStub().getStringState(accountNumber) == null;
    }

}

创建链码 (chaincode) 存放的目录

mkdir -p chaincode/network-test-chaincode/java
mkdir temp

将我们准备好的测试链码上传到虚拟的的 /home/hyperledger/fabric/finance/chaincode/network-test-chaincode/java 目录下

image-20220928173339077

打包链码

export FABRIC_CFG_PATH=${PWD}/config
export ORDERER_ADDRESS=orderer.finance.com:7050
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp/tlscacerts/tlsca.orderer.finance.com-cert.pem
export CHANNEL_NAME=cross-border-payment-channel

export CC_LANG=java
export CC_PATH=${PWD}/chaincode/network-test-chaincode/java
export CC_NAME=network-test-chaincode
export CC_VERSION=v1.0.0
export CC_LABEL=${CC_NAME}_${CC_VERSION}
export CC_SEQ=1
export CC_POLICY="OR('Bank1MSP.peer', 'Bank2MSP.peer')"

bin/peer lifecycle chaincode package ./temp/${CC_LABEL}.tar.gz --path ${CC_PATH} --lang $CC_LANG --label ${CC_LABEL}
image-20220928181056995

安装链码

Bank1 Peer1 安装链码

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

bin/peer lifecycle chaincode install ./temp/${CC_LABEL}.tar.gz
image-20220928175226978

Bank2 Peer1 安装链码

export CORE_PEER_LOCALMSPID=Bank2MSP
export CORE_PEER_ADDRESS=peer1.bank2.finance.com:9051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com/msp

bin/peer lifecycle chaincode install ./temp/${CC_LABEL}.tar.gz
image-20220928175352008

Bank1 Peer1 查询链码ID

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

bin/peer lifecycle chaincode queryinstalled --output json
image-20220928175717393

批准链码

Bank1 Peer1 批准链码

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

export PACKAGE_ID=cross-border-payment-chaincode_v1.0.0:ce95ae8bd5d0439b9517656a4411d753899d02ae344bc99ae3602386c6afeab1

bin/peer lifecycle chaincode approveformyorg \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    --channelID $CHANNEL_NAME \
    --name ${CC_NAME} \
    --version ${CC_VERSION} \
    --init-required \
    --package-id ${PACKAGE_ID} \
    --sequence $CC_SEQ \
    --waitForEvent \
    --signature-policy "$CC_POLICY"
image-20220928181246772

Bank1 Peer1查询批准的链码

bin/peer lifecycle chaincode checkcommitreadiness \
  --channelID $CHANNEL_NAME \
  --name ${CC_NAME} \
  --version ${CC_VERSION} \
  --sequence $CC_SEQ \
  --output json \
  --init-required \
  --signature-policy "$CC_POLICY"
image-20220928181550451

Bank2 Peer1 批准链码

export CORE_PEER_LOCALMSPID=Bank2MSP
export CORE_PEER_ADDRESS=peer1.bank2.finance.com:9051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com/msp

export PACKAGE_ID=network-test-chaincode_v1.0.0:30a89d5785d323a5c24821d1af591dcdd7422b98156fb7c2d85cf7c13b34197e

bin/peer lifecycle chaincode approveformyorg \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    --channelID $CHANNEL_NAME \
    --name ${CC_NAME} \
    --version ${CC_VERSION} \
    --init-required \
    --package-id ${PACKAGE_ID} \
    --sequence $CC_SEQ \
    --waitForEvent \
    --signature-policy "$CC_POLICY"
image-20220928181718800

Bank2 Peer1查询批准的链码

bin/peer lifecycle chaincode checkcommitreadiness \
  --channelID $CHANNEL_NAME \
  --name ${CC_NAME} \
  --version ${CC_VERSION} \
  --sequence $CC_SEQ \
  --output json \
  --init-required \
  --signature-policy "$CC_POLICY"
image-20220928182034902

提交链码

只需要任意一个组织提交即可,这里使用 Bank1 组织提交

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

# 提交链码需要两个组织的节点的地址和TLS根证书
export PEER1_BANK1_ADDRESS=peer1.bank1.finance.com:7051
export PEER1_BANK2_ADDRESS=peer1.bank2.finance.com:9051
export PEER1_BANK1_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export PEER1_BANK2_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt

bin/peer lifecycle chaincode commit \
    -o ${ORDERER_ADDRESS} \
    -C $CHANNEL_NAME \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    --peerAddresses $PEER1_BANK1_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK1_TLS_ROOTCERT_FILE \
    --peerAddresses $PEER1_BANK2_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK2_TLS_ROOTCERT_FILE \
    --name ${CC_NAME} \
    --version ${CC_VERSION} \
    --sequence $CC_SEQ \
    --init-required \
    --signature-policy "$CC_POLICY"
image-20220928183006051

再次查看 docker 容器,我们会发现会多出来两个环境,这就是两个节点的智能合约运行的容器

image-20220928183254690

调用链码

初始化链码

此操作只需要任意组织完成即可

export FABRIC_CFG_PATH=${PWD}/config
export CHANNEL_NAME=cross-border-payment-channel

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

export PEER1_BANK1_ADDRESS=peer1.bank1.finance.com:7051
export PEER1_BANK2_ADDRESS=peer1.bank2.finance.com:9051
export PEER1_BANK1_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export PEER1_BANK2_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt

bin/peer chaincode invoke \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    -C $CHANNEL_NAME \
    -n ${CC_NAME} \
    --peerAddresses $PEER1_BANK1_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK1_TLS_ROOTCERT_FILE \
    --peerAddresses $PEER1_BANK2_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK2_TLS_ROOTCERT_FILE \
    --isInit \
    -c '{"Function":"Init","Args":[]}'
image-20220928184329633

看到上图红色标注的内容说明链码初始化成功

查询初始账户余额 Bank1 Peer1

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

export PEER1_BANK1_ADDRESS=peer1.bank1.finance.com:7051
export PEER1_BANK2_ADDRESS=peer1.bank2.finance.com:9051
export PEER1_BANK1_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export PEER1_BANK2_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt

# Bank1 Peer1 查询账户 CN0000000001
bin/peer chaincode invoke \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    -C $CHANNEL_NAME \
    -n ${CC_NAME} \
    --peerAddresses $PEER1_BANK1_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK1_TLS_ROOTCERT_FILE \
    --peerAddresses $PEER1_BANK2_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK2_TLS_ROOTCERT_FILE \
    -c '{"Function":"QueryBalance","Args":["CN0000000001"]}'
image-20220928185521966

查询初始账户余额 Bank2 Peer1

export CORE_PEER_LOCALMSPID=Bank2MSP
export CORE_PEER_ADDRESS=peer1.bank2.finance.com:9051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com/msp

export PEER1_BANK1_ADDRESS=peer1.bank1.finance.com:7051
export PEER1_BANK2_ADDRESS=peer1.bank2.finance.com:9051
export PEER1_BANK1_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export PEER1_BANK2_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt

# Bank2 Peer1 查询账户 CN0000000002
bin/peer chaincode invoke \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    -C $CHANNEL_NAME \
    -n ${CC_NAME} \
    --peerAddresses $PEER1_BANK1_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK1_TLS_ROOTCERT_FILE \
    --peerAddresses $PEER1_BANK2_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK2_TLS_ROOTCERT_FILE \
    -c '{"Function":"QueryBalance","Args":["CN0000000002"]}'
image-20220928185827147

执行转账操作

账户:CN0000000001 向 CN0000000002 转账 50

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

export PEER1_BANK1_ADDRESS=peer1.bank1.finance.com:7051
export PEER1_BANK2_ADDRESS=peer1.bank2.finance.com:9051
export PEER1_BANK1_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export PEER1_BANK2_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt

# 账户:CN0000000001 向 CN0000000002 转账 50
bin/peer chaincode invoke \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    -C $CHANNEL_NAME \
    -n ${CC_NAME} \
    --peerAddresses $PEER1_BANK1_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK1_TLS_ROOTCERT_FILE \
    --peerAddresses $PEER1_BANK2_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK2_TLS_ROOTCERT_FILE \
    -c '{"Function":"Transfer","Args":["CN0000000001","CN0000000002","50"]}'
image-20220928185920325

Bank1 Peer1 再次查询账户余额

export CORE_PEER_LOCALMSPID=Bank1MSP
export CORE_PEER_ADDRESS=peer1.bank1.finance.com:7051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank1.finance.com/users/Admin@bank1.finance.com/msp

export PEER1_BANK1_ADDRESS=peer1.bank1.finance.com:7051
export PEER1_BANK2_ADDRESS=peer1.bank2.finance.com:9051
export PEER1_BANK1_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export PEER1_BANK2_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt

# Bank1 Peer1 查询账户 CN0000000001
bin/peer chaincode invoke \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    -C $CHANNEL_NAME \
    -n ${CC_NAME} \
    --peerAddresses $PEER1_BANK1_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK1_TLS_ROOTCERT_FILE \
    --peerAddresses $PEER1_BANK2_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK2_TLS_ROOTCERT_FILE \
    -c '{"Function":"QueryBalance","Args":["CN0000000001"]}'
image-20220929122834513

Bank2 Peer1 再次查询账户余额

export CORE_PEER_LOCALMSPID=Bank2MSP
export CORE_PEER_ADDRESS=peer1.bank2.finance.com:9051
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_TLS_CERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/bank2.finance.com/users/Admin@bank2.finance.com/msp

export PEER1_BANK1_ADDRESS=peer1.bank1.finance.com:7051
export PEER1_BANK2_ADDRESS=peer1.bank2.finance.com:9051
export PEER1_BANK1_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank1.finance.com/peers/peer1.bank1.finance.com/tls/ca.crt
export PEER1_BANK2_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/bank2.finance.com/peers/peer1.bank2.finance.com/tls/ca.crt

# Bank2 Peer1 查询账户 CN0000000002
bin/peer chaincode invoke \
    -o ${ORDERER_ADDRESS} \
    --ordererTLSHostnameOverride orderer.finance.com \
    --tls $CORE_PEER_TLS_ENABLED \
    --cafile $ORDERER_CA \
    -C $CHANNEL_NAME \
    -n ${CC_NAME} \
    --peerAddresses $PEER1_BANK1_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK1_TLS_ROOTCERT_FILE \
    --peerAddresses $PEER1_BANK2_ADDRESS \
    --tlsRootCertFiles $PEER1_BANK2_TLS_ROOTCERT_FILE \
    -c '{"Function":"QueryBalance","Args":["CN0000000002"]}'
image-20220929122920731

到这里我们的网络 Hyperledger Fabric 网络搭建就已经完成了。

打赏作者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

CAPTCHA