一、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 +
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
- 下载 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 跨境支付网络拓扑
网络规划
名称 | 域名 | 端口 |
---|---|---|
Orderer CA | ca.orderer.finance.com | 7054:7054 |
Bank1 CA | ca.bank1.finance.com | 8054:7054 |
Bank2 CA | ca.bank2.finance.com | 9054:7054 |
Orderer | orderer.finance.com | 7050:7050 |
Bank1 Peer1 | peer1.bank1.finance.com | 7051:7051,7052:7052 |
Bank2 Peer1 | peer1.bank2.finance.com | 9051:7051,9052:7052 |
Bank1 Peer1 CouchDB | peer1.bank1.couchdb.com | 5984:5984 |
Bank2 Peer1 CouchDB | peer1.bank2.couchdb.com | 6984: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
查看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
7. 生成通道文件
bin/configtxgen -configPath ./configtx -profile CrossBorderPaymentChannel -outputCreateChannelTx ./channel-artifacts/cross-border-payment-channel.tx -channelID cross-border-payment-channel
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
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
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 ..
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
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
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
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
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
目录下
打包链码
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}
安装链码
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
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
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
批准链码
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"
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"
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"
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"
提交链码
只需要任意一个组织提交即可,这里使用 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"
再次查看 docker 容器,我们会发现会多出来两个环境,这就是两个节点的智能合约运行的容器
调用链码
初始化链码
此操作只需要任意组织完成即可
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":[]}'
看到上图红色标注的内容说明链码初始化成功
查询初始账户余额 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"]}'
查询初始账户余额 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"]}'
执行转账操作
账户: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"]}'
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"]}'
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"]}'
到这里我们的网络 Hyperledger Fabric 网络搭建就已经完成了。
打赏作者