2020-03-23

Hyperledger Fabric

Linux基金会于2015年创建了Hyperledger项目, 以推进跨行业的区块链技术, 而不是声明单个区块链标准, 它鼓励通过社区流程采用协作方式开发区块链技术, 鼓励开放式发展的知识产权, 并且随着时间的推移采用关键标准;

github 2.0官方文档

分布式分类账(A Distributed Ledger)

区块链网络的核心是一个分布式分类账, 记录网络上发生的所有交易;

区块链分类账通常被描述为分散的, 因为它被许多网络参与者复制, 每个参与者都在维护中进行协作, 我们将看到分散化和协作是反映现实世界中企业交换商品和服务方式的强大属性;

简而言之理解为所有人维护的一个账本

分类账的子系统

Hyperledger Fabric有一个包含两个组件的分类账子系统:世界状态交易日志, 每个参与者都拥有他们所属的每个Hyperledger Fabric网络的分类账副本;

  • 世界状态组件

世界状态组件描述了在给定时间点的分类账的状态, 它是分类账的数据库;

首先, 有一个世界状态——一个数据库, 它保存一组分类账状态的当前值; 世界状态使程序很容易直接访问状态的当前值, 而不必通过遍历整个事务日志来计算它;

  • 交易日志组件

交易日志组件记录导致世界状态的当前值的所有交易; 这是世界状态的更新历史, 然后,分类账是世界状态数据库和交易日志历史记录的组合;

分类帐具有可替换的世界状态数据存储, 默认情况下, 这是LevelDB键值存储数据库, 交易日志不需要是可插拔的, 它只记录区块链网络使用的分类帐数据库的前后值;

世界状态 ‘数据库’, 当前账本的状态, 交易, 还没有达成共识的交易? 交易日志 ‘操作数据库的记录’,记录导致世界状态的当前值的所有交易; 这是世界状态的更新历史

智能合约(chaincode)

为了支持信息的一致更新 - 并启用整个分类账功能(交易, 查询等) - 区块链网络使用智能合约来提供对分类账的访问控制;

Hyperledger Fabric智能合约以链码编写, 当该应用程序需要与分类帐交互时, 由区块链外部的应用程序调用; 在大多数情况下, 链码只与分类帐的数据库组件, 世界状态(例如, 查询它)交互, 而不与交易日志交互;

简而言之理解为 与世界状态(‘数据库’/账本)交互/操作, 应该主要在这里开发, 需要自己编写 官方JAVA 链码

共识

貌似这里与排序重合, 参考下 交易排序

在整个网络中保持总账交易同步的过程 - 确保总账仅在交易获得适当参与者批准时才更新, 并且当总账确实更新时, 他们以相同的顺序更新相同的交易 - 称为共识;

简而言之理解为 足够多的参与者达成共识/允许’提交’ 更新账本, 类似btc的打包区块

关键概念 (https://hyperledger-fabric.readthedocs.io/en/release-2.0/blockchain.html)

关于Peers

区块链网络主要由一系列的Peers节点组成; Peers是整个网络的基础, 因为它是账本和智能合约的载体; 通过智能合约, 账本以不可篡改的方式记录了交易的全过程; 在区块链中, 智能合约和账本被用来封装整个网络中的共享处理工程和共享信息;

Peer节点的主要功能是调用智能合约执行交易和记账;

1, Fabric链码通过grpc与peer节点交互, 当peer节点收到客户端请求的输入(propsal)后, 会通过发送一个链码消息对象(带输入消息, 调用者消息)给对应的链码;

2, 链码调用ChaincodeBase里面的invoke方法, 通过发送获取数据(getState)和写入数据(putState)消息, 向peer节点获取账本状态信息和发送预提交状态;

3, 链码发送最终输出结果给peer节点, 节点对输入(propsal)和输出(propsalreponse)进行背书签名, 完成第一段签名提交; 之后客户端收集所有peer节点的第一段提交信息, 组装事务(transaction)并签名, 发送事务到orderer节点排队, 最终orderer产生区块, 并发送给各个peer节点, 把输入输出落到账本上, 完成第二段提交过程;

https://blog.csdn.net/JueJingZhiKe/article/details/80000379

实例

安装示例文档 需要 docker ,

curl -sSL https://bit.ly/2ysbOFE| bash -s

https://bit.ly/2ysbOFE 这个重定向到 https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh

上面的命令下载并执行一个 bash 脚本, 该脚本将下载并提取所有特定于平台的二进制文件, 您将需要这些文件来设置您的网络并将它们放置到上面创建的克隆回复文件中;

考虑到国内恶心的***, 从脚本入手, 看看怎么手动处理

下载已经发布二进制文件

2.0.1 https://github.com/hyperledger/fabric/releases/download/v2.0.1/hyperledger-fabric-linux-amd64-2.0.1.tar.gz

要设置环境变量 vi /etc/profile export PATH=$PATH:/opt/hyperledger-fabric-2.0.1/bin source /etc/profile

Docker

  • 安装 Docker, 一定要改Docker镜像源.

  • 安装 docker-compose

yum install -y epel-release yum install -y docker-compose

Docker 镜像

把脚本改成只运行pullDockerImages 函数, 运行下

总是下不完, 多运行几次. 玄学GFW

  • 另 node 环境安装 nade官方下载对应的发布包, inter CPU 是x86架构的, 虚拟机也同样

最后环境变量

export PATH=$PATH:/opt/hyperledger-fabric-2.0.1/fabric-samples/bin/
export NODE_HOME=/opt/node-v12.16.1-linux-x64/
export PATH=$NODE_HOME/bin:$PATH
export FABRIC_CFG_PATH=/opt/hyperledger-fabric-2.0.1/fabric-samples/config/

示例项目

克隆示例项目 git clone -b master https://github.com/hyperledger/fabric-samples.git && cd fabric-samples && git checkout v${VERSION}

脚本代码也是有问题的, 没有2.0.0分支

  • 示例脚本

使用test-network测试Hyperledger Fabric 2.0

cd test-network/
./network.sh up 
  • 错误
  1. ERROR! Peer binary and configuration files not found..

这有个坑, 示例项目没有二进制文件, 需要把下载的release的bin目录和config目录复制到示例项目的根路径, 最好环境变量也改下

  1. orderer 容器退出状态的问题

hyperledger/fabric-orderer:latest “orderer” 14 hours ago Exited (2) 14 seconds ago

//查看这个容器日志 docker logs f59efc835259

容器挂载文件问题 unable to bootstrap orderer. Error reading genesis block file: read /var/hyperledger/orderer/orderer.genesis.block: is a directory

找到启动脚本

function networkUp() {
 
  checkPrereqs
  # generate artifacts if they don't exist
  if [ ! -d "organizations/peerOrganizations" ]; then
    createOrgs
    createConsortium
  fi
 
  COMPOSE_FILES="-f ${COMPOSE_FILE_BASE}"

COMPOSE_FILE_BASE 定义在前面COMPOSE_FILE_BASE=docker/docker-compose-test-net.yaml

最后 找到配置文件 docker/docker-compose-test-net.yaml

...
    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/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
        - ../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls
        - orderer.example.com:/var/hyperledger/production/orderer
...

这个 networkUp 会用configtxgen 生成一系列的orderer.genesis.block, fabric-ca-server.db , ca-cert.pem 等文件, 而文件路径又不对.. fabric-samples 项目,换成Pre-release v2.0.0 Beta的

2.1. 错误无法运行 configtxgen 工具

最终问题是 脚本无法运行 configtxgen 工具; 工具本身无法运行, 错误 Segmentation fault

https://github.com/hyperledger/fabric/blob/master/cmd/configtxgen/main.go ; 貌似代码没问题

最后用2.0.0的configtxgen 替换之

清掉(down) 在运行一遍

./network.sh down 
./network.sh up 

finally !

 
[root@localhost test-network]# ./network.sh up
Starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb'
 
LOCAL_VERSION=2.0.1
DOCKER_IMAGE_VERSION=2.0.0
=================== WARNING ===================
  Local fabric binaries and docker images are
  out of  sync. This may cause problems.
===============================================
Creating network "net_test" with the default driver
Creating volume "net_orderer.example.com" with default driver
Creating volume "net_org1.example.com" with default driver
Creating volume "net_peer0.org2.example.com" with default driver
Creating peer0.org2.example.com ... done
Creating peer0.org2.example.com ...
Creating orderer.example.com ...
 

超级账本Fabric 2.0 安装步骤

Using the Fabric test network

创建 channel

您可以使用 network.sh 脚本在 org 1和 org 2之间创建一个通道, 并将他们的同伴加入到这个通道中; 运行以下命令创建默认名为 testchannel 的通道:

./network.sh createChannel

Creating a channel

在你创建了一个渠道之后, 你可以开始使用智能合同与渠道分类账进行交互; 智能合同包含管理区块链分类账上资产的业务逻辑; 由网络成员运行的应用程序可以调用智能合同在分类账上创建资产, 以及更改和转移这些资产; 应用程序还查询智能合同来读取分类账上的数据;

启动链码(智能合约)

//TODO 使用 network.sh 创建通道后, 可以使用以下命令在通道上启动链码:

./network.sh deployCC -l java

Deploycc 子命令将在 org1.example.com 和 peer0.org2.example.com 上安装 fabcar 链码, 然后将链码部署到使用通道标志指定的通道上(如果没有指定通道, 则部署 testchannel); 如果您是第一次部署链接代码, 脚本将安装链接代码依赖项; 默认情况下, 脚本安装 fabcar 链码的 Go 版本; 但是, 您可以使用语言标记-l 来安装链接代码的 Java 或 javascript 版本;

又TM错误:

  • What went wrong: Task ‘shadowJar’ not found in root project ‘fabcar’.

可以不用 network.sh , 使用 ./fabcar/startFabric.sh脚本来初始化环境

write_first_app

write_first_app

cd fabric-samples/fabcar ./startFabric.sh java // 初始化java版, 也会拉镜像, 包括服务端等等.

java 文件夹下的项目 2.0 API没了,也是错误的, 无法运行!! javasrcipt 初始化用户:

cd /opt/hyperledger-fabric-2.0.1/fabric-samples/fabcar/javascript
// npm install
// 需要安装 gcc , 因为root用户 外加unsafe参数
yum install -y gcc-c++
npm install --save --unsafe-perm
 
///////////
//注册管理员
node enrollAdmin.js
Successfully enrolled admin user "admin" and imported it into the wallet
///////////
//现在我们钱包里有了管理员的证书, 我们可以注册一个新用户 user1, 它将用于查询和更新分类账:
node registerUser.js
// Failed to register user "user1": TypeError: gateway.getClient is not a function 2.0.0 不能用艹

还是要改java项目, 用1.4的api跑

依赖 fabric-chaincode-shimfabric-gateway-java项目, com.google.guava 等

依赖 netty-tcnative <os.detected.classifier>windows-x86_64</os.detected.classifier> <os.detection.classifierWithLikes>windows</os.detection.classifierWithLikes>

io.netty netty-tcnative 2.0.29.Final ${os.detected.classifier} io.netty netty-tcnative-boringssl-static 2.0.29.Final ${os.detected.classifier}
  • 安装 Installs适用于Windows的OpenSSL OPENSSL for Windows , 以及 , and
  • 将包含.DLL文件的目录添加到 Add a directory containing. dll files to the%PATH% . 参考

无法链接 netty_tcnative_windows_x86 问题 最终是用System.loadLibrary(libName); 加载的, netty 把dll释放到 C:\Users[USERNAME]\AppData\Local\Temp, 且名称不对, 先改成把 netty_tcnative_windows_x86_64.dll 放到${JAVA_HOME}/bin

每次运行./startFabric.sh java都回清掉重新来过, 记得ca.org1.example.com-cert.pem 替换证书 修改 connection-org1.yaml 的localhost地址

纯手动部署

准备组织结构 MSP证书(cryptogen)

推倒再来一遍:: 确定目录 /opt/deploy2_temp 部署的临时目录 /opt/deploy2 部署目录

(使用cryptogen 工具)

创建一个配置文件crypto-config.yaml, 可参考示例项目的 \fabric-samples\test-network\organizations\cryptogen\

照着官方脚本 network.sh的createOrgs函数

把 cryptogen目录复制过去

cryptogen generate --config=./cryptogen/crypto-config-org1.yaml --output="organizations"
cryptogen generate --config=./cryptogen/crypto-config-org2.yaml --output="organizations"
cryptogen generate --config=./cryptogen/crypto-config-org3.yaml --output="organizations"
cryptogen generate --config=./cryptogen/crypto-config-org4.yaml --output="organizations"
cryptogen generate --config=./crypto-config-orderer.yaml --output="organizations"
 

cryptogen generate —config=./crypto-config-orderer.yaml —output=“organizations” cryptogen generate —config=./crypto-config-org1.yaml —output=“organizations” cryptogen generate —config=./crypto-config-org2.yaml —output=“organizations” 这是一个组织证书树结构

以 ordererOrganizations/orderers/orderer.example.com 为例

organizations/
├── ordererOrganizations
│   └── example.com
│       ├── ca
│       │   ├── ca.example.com-cert.pem
│       │   └── priv_sk
│       ├── msp
│       │   ├── admincerts
│       │   ├── cacerts
│       │   │   └── ca.example.com-cert.pem
│       │   ├── config.yaml
│       │   └── tlscacerts
│       │       └── tlsca.example.com-cert.pem
│       ├── orderers
│       │   └── orderer.example.com
│       │       ├── msp
│       │       │   ├── admincerts
│       │       │   ├── cacerts
│       │       │   │   └── ca.example.com-cert.pem
│       │       │   ├── config.yaml
│       │       │   ├── keystore
│       │       │   │   └── priv_sk
│       │       │   ├── signcerts
│       │       │   │   └── orderer.example.com-cert.pem
│       │       │   └── tlscacerts
│       │       │       └── tlsca.example.com-cert.pem
│       │       └── tls
│       │           ├── ca.crt
│       │           ├── server.crt
│       │           └── server.key
│       ├── tlsca
│       │   ├── priv_sk
│       │   └── tlsca.example.com-cert.pem
│       └── users
│           └── Admin@example.com
│               ├── msp
│               │   ├── admincerts
│               │   ├── cacerts
│               │   │   └── ca.example.com-cert.pem
│               │   ├── config.yaml
│               │   ├── keystore
│               │   │   └── priv_sk
│               │   ├── signcerts
│               │   │   └── Admin@example.com-cert.pem
│               │   └── tlscacerts
│               │       └── tlsca.example.com-cert.pem
│               └── tls
│                   ├── ca.crt
│                   ├── client.crt
│                   └── client.key
 

tls 目录中的内容很好理解, 它是order对外服务时使用的私钥(server.key)和证书(server.crt), ca.crt是签注这个证书的CA, 需要提供给发起请求的一端;

msp中有五个目录, 对区块链进行操作时需要使用这些文件;

msp/admincerts中存放的是用户证书, 使用该证书的用户对orderer.example.com具有管理权限;

msp/cacerts是签署msp/signcerts中用户证书的ca, 可以用来校验用户的证书:

openssl verify -CAfile ./caca.example.com-cert.pem adminAdmin@example.com-cert.pem adminAdmin@example.com-cert.pem: OK

msp/keystore是orderer.example.com操作区块, 进行签署时使用的的私钥;

msp/signcerts是orderer.example.com提供其它组件用来核实它的签署的公钥;

msp/tlscacerts文件与tls/ca.crt相同;

上一级 example.com

orderers 中存放就签名分析的每个orderer组件的证书文件 users 中存放的用户的证书文件, 与orderer.example.com中内容基本相同, 不过tls目录中文件名变成了client.X:

准备组织结构 MSP证书(FabricCA)

参考下 [加入Fabric CAS 构建组织架构]

orderer 节点

创建一个目录:/opt/deploy2/orderer.example.com/

复制orderers 证书文件 cp -rf ordererOrganizations/example.com/orderers/orderer.example.com/* /opt/deploy2/orderer.example.com/

复制orderer二进制文件 cp orderer /opt/deploy2/orderer.example.com/

准备orderer的配置文件/opt/deploy2/orderer.example.com/orderer.yaml

注意 FileLedger.Location 配置 替换路径 /opt/deploy2/orderer.example.com/data

可参考示例项目的 \fabric-samples\config\orderer.yaml 替换一些工作数据路径 WALDir, SnapDir Location 创建它们 mkdir -p /opt/deploy2/orderer.example.com/data/etcdraft/wal mkdir -p /opt/deploy2/orderer.example.com/data/etcdraft/snapshot mkdir -p /opt/deploy2/orderer.example.com/data

orderer 创世块文件

参考脚本 network.sh的 createConsortium 函数

通过 configtx.yaml 配置文件, 参考:fabric-samples\test-network\configtx\configtx.yaml’ 注意替换 MSPDir 路径! 注意ClientTLSCert 路径!

使用 configtxgen 工具 生成创世块文件; export FABRIC_CFG_PATH=$PWD configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block

复制 genesis.block, orderer发布目录

修改 orderer的配置文件

General.BootstrapFile = ./genesis.block
# 必须启用TLS
General.TLS.Enabled = true 
 
错误

Error reading configuration: Unsupported Config Type "" 没有找到配置文件 export FABRIC_CFG_PATH=$PWD

启动 疯狂的警告/批准链码超时 Could not connect to ordering service: could not dial endpoint 'orderer.example.com:7050':

注意生成创世区块的 configtx.yaml 文件配置Orderer.Addresses 的地址!

orderer 报 TLS handshake failed 确保peer启用TLS!!

peer 不能互联 另一个 peer 报 TLS handshake failed

peer 的core.yaml,该配置是需要配置对等方!!

peer
  gossip
        # This is an endpoint that is published to peers outside of the organization.
        # If this isn't set, the peer will not be known to other organizations.
        externalEndpoint:

启动

./orderer

错误 panic: TLS is required for running ordering nodes of type etcdraft. 参考: https://github.com/davidkhala/fabric-common

[raft] etcdraft does not support non TLS …

必须启用tls General.TLS.Enabled=true

peer 节点

org1.example.com

类似 orderer

cp -rf /opt/deploy_/peerOrganizations/peer0.org1.example.com/peers/peer0.org1.example.com/* /opt/deploy2/peer0.org1.example.com

复制peer二进制文件 cp peer /opt/deploy2/peer0.org1.example.com

准备peer0.org1.example.com的配置文件core.yaml可参考示例项目的 \fabric-samples\config\core.yaml 和peer-base.yaml

修改数据路径 fileSystemPath /opt/deploy2/peer0.org1.example.com/data

mkdir /opt/deploy2/peer0.org1.example.com/data

peer1.org1.example.com & peer0.org2.example.com

类似 将其中修改为peer1.org1.example.com 修改数据路径 fileSystemPath 同一台机器还需要注意: peer.chaincodeListenAddress //端口要设置,不然冲突 operations.listenAddress //端口要设置

启动

./peer node start

directory /opt/deploy2/peer0.org1.example.com/msp/signcerts: stat /opt/deploy2/peer0.org1.example.com/msp/signcerts: no such file or directory

Cannot run peer because error when setting up MSP of type bccsp from directory /opt/deploy2/peer0.org1.example.com/msp: Setup error: nil conf re ference

大概率 复错文件了, 应该复制peers/下面的 R

… INFO 002 Auto-detected peer address: 192.168.33.138:7051

… YES~

(Channel)创建 & 命令行连接

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051

用户目录

如果每个组件的日志中没有错误, 那么fabric启动就完成; 现在的问题是如何使用?

首先要有用户, 在前面用cryptogen准备证书的时候, 默认创建了用户;

还记得certs目录下的几个users目录吗?那里面就是用户证书;

users目录一共有三个, 分别是联盟的用户, 和每个组织的用户:

./ordererOrganizations/example.com/users ./peerOrganizations/peer0.org1.example.com/users ./peerOrganizations/org2.example.com/users

其中每个组织有两个用户, Admin 和 User1: ~~Admin和User1唯一的区别是, Admin的用户证书被添加到了对一个peer的msp/admincerts目录中; ~~(没看到,有证书..)

命令行连接

//创建用户文件夹 mkdir -p /opt/deploy2/manager/peer/Admin@org1.example.com

//复制用户证书 cp -rf /opt/deploy_/peerOrganizations/peer0.org1.example.com/users/User1@org1.example.com/* /opt/deploy2/manager/peer/Admin@org1.example.com

将core.yaml + peer 复制到用户目录下: cp org1.example.com/core.yaml Admin@org1.example.com/ cp org1.example.com/peer Admin@org1.example.com/

//定义配置根目录( 它是环境变量优先的 我*) export FABRIC_CFG_PATH=$PWD

//连接 / 查看已加入的channel ./peer channel list

//启用debug export FABRIC_LOGGING_SPEC=DEBUG

错误

-TLS handshake failed 错误 ERRO 01f TLS handshake failed with error remote error: tls: bad certificate

总是 tls 握手错误: 原因是必须以域名形式访问 org1.example.com

//修访问地址的环境变量 export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051

export CORE_PEER_ADDRESS=peer0.org1.joinken.cn:7051

//或者 ordererTLSHostnameOverride 选项覆盖 —ordererTLSHostnameOverride orderer.example.com

//其次只有连接配置文件的 core.yaml tls.enabled=true//确保启用tls

  • connect: connection refused 错误

core.yaml 配置的监听地址问题

之前, 先确保已经在每台机器的/etc/hosts文件中添加下列域名映射:

192.168.88.10 orderer.example.com 192.168.88.10 org1.example.com 192.168.88.11 peer1.org1.example.com 192.168.88.12 peer0.org2.example.com ///////////

192.168.33.138 orderer.example.com 192.168.33.138 org1.example.com 192.168.33.138 peer1.org1.example.com 192.168.33.138 peer0.org2.example.com

访问 order的 tls证书

要访问orderer.example.com, 需要将验证orderer.example.com的根证书复制到用户目录

cp ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem Admin\@org1.example.com/

创建相关文件

创建 channel 文件

参考脚本 fabric-samples\test-network\scripts\createChannel.sh

需要再次回到deploy_temp目录中, 用configtxgen 依据configtx.yaml 生成channel文件:

createChannelTx

configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./testchannel.tx -channelID testchannel configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./jkchannel.tx -channelID jk

生成了./testchannel.tx

创建 锚点文件

生成 Org1MSPanchors Org2MSPanchors

configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org1MSPanchors.tx -channelID testchannel -asOrg Org1MSP configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org2MSPanchors.tx -channelID testchannel -asOrg Org2MSP

configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org1MSPanchors.tx -channelID jk -asOrg Org1MSP configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org2MSPanchors.tx -channelID jk -asOrg Org2MSP

创建 channel

到管理用户

./peer channel create -o orderer.example.com:7050 -c testchannel -f ./testchannel.tx --tls true --cafile tlsca.example.com-cert.pem

./peer channel create -o orderer.joinken.cn:7050 -c jk -f ./jkchannel.tx —tls true —cafile tlsca.joinken.cn-cert.pem

错误

用户端 Error: got unexpected status: BAD_REQUEST — error validating channel creation transaction for new channel ‘testchannel’, could not successfully apply update to template configuration: error authorizing update: error validating DeltaSet: policy for [Group] /Channel/Application not satisfied: implicit policy evaluation failed - 0 sub-policies were satisfied, but this policy requires 1 of the ‘Admins’ sub-policies to be satisfied

orderer 端 principal deserialization failure (MSP SampleOrg is unknown) for identity …

无法识别 MSP ID ?

生成创世块文件的相关权限配置

在 configtx.yaml TwoOrgsOrdererGenesis 用来配置创世区块信息, TwoOrgsChannel 来配置初始交易信息;

基本都是 *ChannelDefaults 引用上面的配置 (&)

TwoOrgsOrdererGenesis 模板用来生成Orderer服务的初始区块文件, 该模板由三部分组成: 1.1 Capabilities 指定通道的权限信息; 1.2 Orderer 指定了Orderer服务的信息(OrdererOrg)及权限信息; 1.3 Consortiums 定义了联盟组成成员(Org1&Org2);

TwoOrgsChannel 模板用来生成应用通道交易配置文件; 由两部分组成: 2.1 Consortium 指定了联盟信息; 2.2 Application 指定了组织及权限信息

看上面 &Org1.ID: Org1MSP 定义的ID 是Org1MSP 而复制 core.yaml 定义的是SampleOrg, 另外注意order.yaml也是 SampleOrg 万年巨坑!!! 脚本不出错应该是环境变量优先的原因!

  • 再错误

orderer 端 principal deserialization failure (the supplied identity is not valid: x509: certificate signed by unknown authority) for identity

在这种情况下, orderer 服务识别了您的MSP ID 但无法验证您的证书是由您的组织的证书颁发机构颁发的;

用户证书不对? 推倒再跑一遍证书!! ok

参考 stackoverflow

此时会接受到 orderer 的第0个块testchannel.block文件, 加入 channel 需要这个块文件

2020-03-28 16:58:44.552 CST [cli.common] readBlock INFO 00c Received block: 0

  • 关于疯狂出现 [gossip.gossip] [gossip.discovery] 的警告

gossip 配置是配置对等方相关信息.

仅在同一组织的同级之间或不同组织的同级之间使用; 它始终限于特定渠道;

1)单个组织中的同级之间的通信

一个对等方可能是领导者, 可以连接到订购服务, 并向自己组织中的其他对等方交付块 一个对等方可以连接到其组织中的其他对等方以获取丢失的数据块

CORE_PEER_GOSSIP_BOOTSTRAP //这里列出的引导节点必须与当前节点属于同一机构, 否则连接将被拒绝; CORE_PEER_GOSSIP_EXTERNALENDPOINT //- 外部访问点 向机构外的节点发布的访问端结点; 如果未设置该参数, 节点 将不为其他机构所知;

参考: first-network\base\docker-compose-base.yaml 示例的配置

peer0.org1.example.com:
  container_name: peer0.org1.example.com
  extends:
    file: peer-base.yaml
    service: peer-base
  environment:
    - CORE_PEER_ID=peer0.org1.example.com
    - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
    - CORE_PEER_LISTENADDRESS=0.0.0.0:7051
    - CORE_PEER_CHAINCODEADDRESS=peer0.org1.example.com:7052
    - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
    - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.example.com:8051
    - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051
    - CORE_PEER_LOCALMSPID=Org1MSP
 
 
peer1.org1.example.com:
  container_name: peer1.org1.example.com
  extends:
    file: peer-base.yaml
    service: peer-base
  environment:
    - CORE_PEER_ID=peer1.org1.example.com
    - CORE_PEER_ADDRESS=peer1.org1.example.com:8051
    - CORE_PEER_LISTENADDRESS=0.0.0.0:8051
    - CORE_PEER_CHAINCODEADDRESS=peer1.org1.example.com:8052
    - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:8052
    - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:8051
    - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051
    - CORE_PEER_LOCALMSPID=Org1MSP

加入 channel

./peer channel join -b testchannel.block -o orderer.example.com:7050 --tls true --cafile tlsca.example.com-cert.pem

peer channel join -b jk.block -o orderer.joinken.cn:7050 —tls true —cafile tlsca.joinken.cn-cert.pem

Successfully submitted proposal to join channel ok

查看 ./peer channel list

指定channel 锚点

每个组织需要指定anchor peer, anchor peer是组织用来接收orderer下发的区块的peer

每个参与者可以将自己的一个Peer地址写入到system chain中, 这样的Peer被称为”锚点”(Anchor Peer); Orderer们从system chain中获得Anchor Peer的地址, 并将形成的共识通知给它们;

还需要为每个组织的peer生成一个anchor文件, 每个组织只需要一个: 回到/opt/deploy2_temp

configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org1MSPanchors.tx -channelID testchannel -asOrg Org1MSP
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org2MSPanchors.tx -channelID testchannel -asOrg Org2MSP

生成了 Org1MSPanchors.tx, Org2MSPanchors.tx

在去到管理用户: 指定 ./peer channel update -o orderer.example.com:7050 -c testchannel -f ./Org1MSPanchors.tx --tls true --cafile ./tlsca.example.com-cert.pem

./peer channel update -o orderer.example.com:7050 -c testchannel -f ./Org2MSPanchors.tx --tls true --cafile ./tlsca.example.com-cert.pem

peer channel update -o orderer.joinken.cn:7050 -c jk -f ./Org1MSPanchors.tx —tls true —cafile tlsca.joinken.cn-cert.pem peer channel update -o orderer.joinken.cn:7050 -c jk -f ./Org2MSPanchors.tx —tls true —cafile tlsca.joinken.cn-cert.pem

转为 Docker compose 部署

方便一点 , 端口很麻烦问题…

参考配置文件: fabric-samples-2.0.0-beta\first-network\base\docker-compose-base.yamlpeer-base.yaml

peer

把单个peer的docker-compose配置 抽出来.

 
version: '2'
 
volumes:
  peer0.org1.joinken.cn:
networks:
  byfn:
 
services:
 
 
  peer0.org1.joinken.cn:
    container_name: peer0.org1.joinken.cn
    environment:
      - CORE_PEER_ID=peer0.org1.joinken.cn
      - CORE_PEER_ADDRESS=peer0.org1.joinken.cn:7051
      - CORE_PEER_GOSSIP_ENDPOINT=peer0.org1.joinken.cn:7051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:7051
      - CORE_PEER_CHAINCODEADDRESS=peer0.org1.joinken.cn:7052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
      # - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.joinken.cn:8051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.joinken.cn:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        # peer0 的 msp tls
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/msp:/etc/hyperledger/fabric/msp
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/tls:/etc/hyperledger/fabric/tls
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/data:/var/hyperledger/production
    ports:
      - 7051:7051
    extends:
      file: peer-base.yaml
      # 这个值是与 peer-base.yaml 的 service相同的
      service: peer-base
    networks:
      - byfn

Orderer

替换一些路径

# docker-compose-base.yaml
...
services:
 
  orderer.example.com:
    container_name: orderer.example.com
    extends:
      file: peer-base.yaml
      service: orderer-base
    volumes:
    # 创世块 orderer 文件
        - /opt/deploy2/orderer.example.com/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
    # orderer 的 msp tls
        - /opt/deploy2/orderer.example.com/msp:/var/hyperledger/orderer/msp
        - /opt/deploy2/orderer.example.com/tls/:/var/hyperledger/orderer/tls
    # 数据目录
        - /opt/deploy2/orderer.example.com/data:/var/hyperledger/production/orderer
    ports:
 
  peer0.org1.joinken.cn:
    container_name: peer0.org1.joinken.cn
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      ...
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        # peer0 的 msp tls
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/msp:/etc/hyperledger/fabric/msp
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/tls:/etc/hyperledger/fabric/tls
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/data:/var/hyperledger/production
 
...
 
 
 
  peer0.org1.joinken.cn:
    container_name: peer0.org1.joinken.cn
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer0.org1.joinken.cn
      - CORE_PEER_ADDRESS=peer0.org1.joinken.cn:7051
      - CORE_PEER_GOSSIP_ENDPOINT=peer0.org1.joinken.cn:7051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:7051
      - CORE_PEER_CHAINCODEADDRESS=peer0.org1.joinken.cn:7052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
      # - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.joinken.cn:8051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.joinken.cn:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        # peer0 的 msp tls
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/msp:/etc/hyperledger/fabric/msp
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/tls:/etc/hyperledger/fabric/tls
        - /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/data:/var/hyperledger/production
        
# peer-base.yaml
 
 
 
version: '2'
 
services:
  peer-base:
    image: hyperledger/fabric-peer:$IMAGE_TAG
 
# 缺少了环境变量 镜像版本
# 运行前定义镜像版本
# export IMAGE_TAG="2.0.0"
 

//这只启动 orderer + peer (自己sdk开发,仅需这个两个就够了) export IMAGE_TAG=“2.0.0” docker-compose -f ./docker-compose-base.yaml up docker ps

客户端

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051 export FABRIC_CFG_PATH=$PWD

配置文件参考: fabric-samples-2.0.0-beta\first-network\docker-compose-cli.yaml

version: '2'
 
volumes:
  orderer.example.com:
  peer0.org1.example.com:
  peer0.org2.example.com:
 
networks:
  byfn:
 
services:
 
  orderer.example.com:
    extends:
    # docker-compose-base.yaml 路径
      file: docker-compose-base.yaml
      service: orderer.example.com
    container_name: orderer.example.com
    networks:
      - byfn
 
  peer0.org1.example.com:
    container_name: peer0.org1.example.com
    extends:
      file: docker-compose-base.yaml
      service: peer0.org1.example.com
    networks:
      - byfn
      
  peer0.org2.example.com:
    container_name: peer0.org2.example.com
    extends:
      file: docker-compose-base.yaml
      service: peer0.org2.example.com
    networks:
      - byfn
 
  cli:
    container_name: cli
    image: hyperledger/fabric-tools:$IMAGE_TAG
    tty: true
    stdin_open: true
    # 默认连接环境 peer0.org1.example.com / peer0.org2.example.com
    environment:
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      # 日志级别
      - FABRIC_LOGGING_SPEC=INFO
      #- FABRIC_LOGGING_SPEC=DEBUG / INFO
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org2.example.com:7051
      # Org1MSP / Org2MSP
      - CORE_PEER_LOCALMSPID=Org2MSP
      - CORE_PEER_TLS_ENABLED=true
      # tls and msp 
      # - CORE_PEER_TLS_CERT_FILE=/opt/deploy2/peer0.org1.example.com/tls/server.crt 
      #- CORE_PEER_TLS_KEY_FILE=/opt/deploy2/peer0.org1.example.com/tls/server.key 
      #- CORE_PEER_TLS_ROOTCERT_FILE=/opt/deploy2/peer0.org1.example.com/tls/ca.crt 
      #- CORE_PEER_MSPCONFIGPATH=/opt/deploy2/peer0.org1.example.com/msp 
      ### org2
      - CORE_PEER_TLS_CERT_FILE=/opt/deploy2/peer0.org2.example.com/tls/server.crt 
      - CORE_PEER_TLS_KEY_FILE=/opt/deploy2/peer0.org2.example.com/tls/server.key 
      - CORE_PEER_TLS_ROOTCERT_FILE=/opt/deploy2/peer0.org2.example.com/tls/ca.crt 
      - CORE_PEER_MSPCONFIGPATH=/opt/deploy2/peer0.org2.example.com/msp 
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: /bin/bash
    volumes:
        - /var/run/:/host/var/run/
        - ./../chaincode/:/opt/gopath/src/github.com/hyperledger/fabric-samples/chaincode
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
        - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
        - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
        # 映射msp路径, 进入容器命令行 使用的
        - /opt/deploy2/manager/peer/Admin@org1.example.com/:/opt/deploy2/peer0.org2.example.com
    depends_on:
      - orderer.example.com
      - peer0.org1.example.com
      - peer0.org2.example.com
    networks:
      - byfn
 
 
恶心的environment

CORE_PEER_ADDRESS - 节点P2P连接地址 同一机构中其他Peer节点要连接此节点需指定的P2P连接地址; 要设置跨机构的P2P连接地址, 请使用CORE_PEER_GOSSIP_EXTERNAENDPOINT;

CORE_PEER_GOSSIP_ENDPOINT - gossip访问端结点 节点公开的可供本机构其他节点访问的端结点; 供其他机构节点 访问的端结点, 参考 CORE_PEER_GOSSIP_EXTERNALENDPOINT; CORE_PEER_GOSSIP_ENDPOINT无默认值;

CORE_PEER_LISTENADDRESS - Peer节点监听地址 设置或读取对等节点的监听地址; 默认情况下Peer节点在所有 地址上监听请求;

默认值 CORE_PEER_LISTENADDRESS的默认值为: 0.0.0.0:7051;

CORE_PEER_CHAINCODEADDRESS - 链码连接地址 链码连接该Peer节点的地址;

CORE_PEER_CHAINCODELISTENADDRESS - 链码连接监听地址 Peer节点监听链码连接请求的地址; 如果未设置该参数, 将自动选择 节点地址的7052端口;

CORE_PEER_GOSSIP_EXTERNALENDPOINT - 外部访问点 向机构外的节点发布的访问端结点; 如果未设置该参数, 节点 将不为其他机构所知;

环境变量 手册

//环境变量 export IMAGE_TAG=“2.0.0” export COMPOSE_PROJECT_NAME=“tttx” //构建+启动, cli + orderer + peer docker-compose -f docker-compose-cli.yaml up

//启动 docker-compose -f ./docker-compose-cli.yaml start

docker-compose up //up: 构建, 启动容器 docker-compose build //build: 构建或者重新构建服务 docker-compose start eureka //start: 启动指定服务已存在的容器 docker-compose stop eureka //: 停止已运行的服务的容器

CORE_LOGGING_LEVEL=debug 
CORE_VM_DOCKER_ATTACHSTDOUT=true
CORE_CHAINCODE_LOGGING_LEVEL=debug 
CORE_CHAINCODE_LOGGING_SHIM=debug



CORE_PEER_TLS_CERT_FILE=/opt/deploy2/peer0.org1.example.com/tls/server.crt 
CORE_PEER_TLS_KEY_FILE=/opt/deploy2/peer0.org1.example.com/tls/server.key 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/deploy2/peer0.org1.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/deploy2/peer0.org1.example.com/msp 

docker exec -it cli bash   # 进入CLI(命令行)容器

ORG2

启动cli 容器即可

如法炮制 org2 的用户连接 目录 , 修改 core.yaml // 9051端口 // org2 加入channel ./peer channel join -b testchannel.block -o orderer.example.com:7050 --tls true --cafile tlsca.example.com-cert.pem ./peer channel list

指定锚点 ./peer channel update -o orderer.example.com:7050 -c testchannel -f ./Org2MSPanchors.tx --tls true --cafile ./tlsca.example.com-cert.pem

(链码) 安装合约

chaincode的instantiate过程这样的; 首先peer节点需要对chaincode做编译, 它会在fabric-ccenv环境里面把chaincode build, 利用fabric-ccenv创建一个新的docker container并把chaincode装载进去, chaincode在这个新的docker container里面运行起来, chaincode首先会去到peer节点去注册一下, 然后peer节点会允许它注册成功, peer节点做初始化, 调用chaincode的init方法, 之后等待别人调用; 由于他们隔离在不同的container里面, 之后chaincode和peer一直都会有一个keepalive的长链接(互相发送数据包)来保护他们之间的链接不被断开;

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051

示例项目 fabric-samples-2.0.0-beta\chaincode\fabcar 下, 有各语言的链码

关于链码生命周期的命令

参考 Deploying a smart contract to a channel

在打包链码之前, 我们需要安装链码依赖项;

复制 javascript 到 deploy2_temp

[root@orderer deploy2_temp]# cd chaincode/javascript/

npm i -g firebase-tools --unsafe-perm=true --allow-root npm install -g --unsafe-perm=true --allow-root //npm 如果是root运行, 默认会使用无权限的另外用户运行

打包/安装

打包链码 //javascript (node) 版 ./peer lifecycle chaincode package fabcar.tar.gz --path /opt/deploy2_temp/chaincode/javascript --lang node --label fabcar_2.0

peer lifecycle chaincode package fabcar.tar.gz —path /opt/deploy2_temp/gxcoc/chaincode/javascript —lang node —label fabcar

生成了 fabcar.tar.gz

//org1 和org2 都要安装链码 (只要是背书节点都需要安装) ./peer lifecycle chaincode install fabcar.tar.gz

peer lifecycle chaincode install gxcoc.tar.gz

out

[root@orderer Admin@org1.example.com]# ./peer lifecycle chaincode install fabcar.tar.gz
2020-03-30 11:52:03.076 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nKfabcar_2.0:960ddf9650ddd5185824e2068026e8423b0b77601a973f4dc4d9e5a3f5843f0a\022\nfabcar_2.0" >

有点慢, 会启动docker 镜像, 经常安装 time out 包括SDK安装 , 中国特色的网络..

记得给 npm 设置镜像!!!

//查看已安装的链码 peer lifecycle chaincode queryinstalled

错误: Error: failed to endorse chaincode install: rpc error: code = Unavailable desc = transport is closing

docker 容器日志 ERRO 04c failed to invoke chaincode _lifecycle, error: timeout expired while executing transaction 安装执行超时?

  • CORE_CHAINCODE_EXECUTETIMEOUT=60S
  • CORE_PEER_KEEPALIVE_CLIENT_TIMEOUT=60S
  • CORE_PEER_KEEPALIVE_DELIVERYCLIENT_TIMEOUT=60S

org2 错误: failed to invoke chaincode _lifecycle, error: timeout expired while executing transaction

需要一个 cli? 参考上转Docker comopose 部署. 客户端配置

把包 id 存到环境变量 //不能去掉label! 不能去掉label! 不能去掉label! 不然会报链码名称未在channel定义, 摸不着头脑的错误!! CC_PACKAGE_ID=fabcar_2.0:960ddf9650ddd5185824e2068026e8423b0b77601a973f4dc4d9e5a3f5843f0a

批准/链码定义

在安装了链码包之后, 您需要批准组织的链码定义; 该定义包括链码治理的重要参数, 如名称, 版本和链码背书策略;

//批准 链码定义 (每个peer都要) ./peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID testchannel --name fabcar --version 2.0 --init-required --package-id $CC_PACKAGE_ID --sequence 5 --tls true --cafile ./tlsca.example.com-cert.pem

// ./peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 —ordererTLSHostnameOverride orderer.example.com —channelID testchannel —name fabcar —version 2.0 —init-required —package-id $CC_PACKAGE_ID —sequence 1 —tls true —cafile ./tlsca.example.com-cert.pem

2020-03-30 12:03:15.346 CST [chaincodeCmd] ClientWait -> INFO 001 txid [254161391c769d6a1c8b86f59defb84c9f9007d7cec7c932e24da1666a365e95] committed with status (VALID) at

///检查 其他peer 的批准状态, 所有为true的时候即可 提交 ./peer lifecycle chaincode checkcommitreadiness --channelID testchannel --name fabcar --version 2.0 --init-required --sequence 5 --tls true --cafile ./tlsca.example.com-cert.pem --output json

sequence 1 --tls true --cafile ./tlsca.example.com-cert.pem --output json
{
        "approvals": {
                "Org1MSP": true,
                "Org2MSP": false
        }
}
 
背书策略

背书节点的确认依赖于背书策略, 背书策略由链码确定, 它定义了一个交易在被整个网络接受以前需要哪些组织背书;

在2.0的生命周期中 是在批准合约时指定 signature-policy

We could have provided a --signature-policy or --channel-config-policy argument to the approveformyorg command to specify achaincode endorsement policy. The endorsement policy specifies how many peers belonging to different channel members need to validate a transaction against a given chaincode. Because we did not set a policy, the definition of Fabcar will use the default endorsement policy, which requires that a transaction be endorsed by a majority of channel members present when the transaction is submitted. This implies that if new organizations are added or removed from the channel, the endorsement policy is updated automatically to require more or fewer endorsements. In this tutorial, the default policy will require a majority of 2 out of 2 and transactions will need to be endorsed by a peer from Org1 and Org2. If you want to specify a custom endorsement policy, you can use the Endorsement Policies operations guide to learn about the policy syntax.

例如: —-signature-policy "AND('Org1.member', 'Org2.member')"

背书策略

表达式

Endorsement Policy 的语法结构如下所示;

// 基础表达式形式 EXPR(E[, E…]) EXPR 可以是是 AND 或者 OR 逻辑符; E 是实体或者嵌套的表达式;

// 需要三个实体都提供签名 AND(‘Org1.member’, ‘Org2.member’, ‘Org3.member’)

// 需要两个实体任一提供签名 OR(‘Org1.member’, ‘Org2.member’)

// 需要Org1的admin提供签名, 或者Org2, Org3的member同时提供签名 OR(‘Org1.admin’, AND(‘Org2.member’, ‘Org3.member’))

链码提交

Committing the chaincode definition to the channel

/opt/deploy2_temp/organizations/peerOrganizations

./peer lifecycle chaincode commit -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID testchannel \
--name fabcar --version 2.0 --sequence 4 --init-required --tls true --cafile ./tlsca.example.com-cert.pem /
--peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt /
--peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

//需要所有peer的 证书..

./peer lifecycle chaincode commit -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID testchannel --name fabcar --version 2.0 --sequence 5 --init-required --tls true --cafile ./tlsca.example.com-cert.pem --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
2020-03-30 17:01:32.363 CST [chaincodeCmd] ClientWait -> INFO 002 txid [a914667bd92036d21604470b0c04ed182d9f61d83a308515f41146a9ed20a52b] committed with status (VALID) at peer0.org1.example.com:7051
2020-03-30 17:01:32.363 CST [chaincodeCmd] ClientWait -> INFO 001 txid [a914667bd92036d21604470b0c04ed182d9f61d83a308515f41146a9ed20a52b] committed with status (VALID) at peer0.org2.example.com:9051

//查询已提交的链码 ./peer lifecycle chaincode querycommitted --channelID testchannel --name fabcar

Committed chaincode definition for chaincode 'fabcar' on channel 'testchannel':
Version: 2.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]
 

链码调用

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051

Invoking the chaincode

在将链码定义提交给通道之后, 链码将从安装链码的通道的连接点开始; Fabcar 链码现在可以被客户端应用程序调用了; 使用以下命令在分类账上创建一组初始汽车; 请注意, invoke 命令需要目标有足够数量的对等点来满足 chaincode 背书策略;

///ori
 
//调用初始化 //  实际函数是initLedger去掉首字母大写 //invoke是需要所有背书节点的证书
./peer chaincode invoke -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --tls true --cafile ./tlsca.example.com-cert.pem -C testchannel --name fabcar --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --isInit -c '{"function":"initLedger","Args":[]}'
 
//查询所有 //所有节点都有自己账本,
./peer chaincode query -C testchannel -n fabcar -c '{"Args":["queryAllCars"]}'
 
 
/// 调用 changeCarOwner 函数 //CAR0  
./peer chaincode invoke -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --tls true --cafile ./tlsca.example.com-cert.pem -C testchannel --name fabcar --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"changeCarOwner","Args":["CAR0","yang"]}'
// Chaincode invoke successful. result: status:200
 
 
//再查询
./peer chaincode query -C testchannel -n fabcar -c '{"Args":["queryAllCars"]}'
 

peer chaincode 命令参考

(https://www.lijiaocn.com/%E9%A1%B9%E7%9B%AE/2018/07/17/hyperledger-fabric-chaincodes-example.html)

链码升级

upgrade

./peer chaincode upgrade -o orderer.example.com:7050 —tls —cafile $ORDERER_CA -C mychannel -n mycc -v 1.2 -c ’{“Args”:[“init”,“a”,“100”,“b”,“200”,“c”,“300”]}’ -P “AND (‘Org1MSP.peer’,‘Org2MSP.peer’)“

客户端&应用开发

Java SDK 连接调用 文档

项目

参考示例项目 fabric-samples-2.0.0-beta\fabcar\java , 有对应的测试 … 但这里用户 都是通过ca注册的

(https://programmer.help/blogs/fabric-java-sdk-1.4-concise-tutorial.html)

<properties>
		<os.detected.classifier>windows-x86_64</os.detected.classifier>
		<os.detection.classifierWithLikes>windows</os.detection.classifierWithLikes>
	</properties>
 
<dependencies>
  <dependency>
      <groupId>org.hyperledger.fabric-sdk-java</groupId>
      <artifactId>fabric-sdk-java</artifactId>
      <version>1.4.7</version>
  </dependency>
  <!--  //依赖 `netty-tcnative` -->
  <dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-tcnative</artifactId>
    <version>2.0.29.Final</version>
    <classifier>${os.detected.classifier}</classifier>
  </dependency>
 
  <dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-tcnative-boringssl-static</artifactId>
    <version>2.0.29.Final</version>
    <classifier>${os.detected.classifier}</classifier>
  </dependency>
 
  .....
/**
 * API文档 https://sdkjavadocs.github.io/org/hyperledger/fabric/sdk/HFClient.html 
 * @author yangfh
 * 2020年3月31日
 */
public class MainTest {
	/**
	 * @param args
	 * @author yangfh
	 * 2020年3月31日
	 * @throws IOException 
	 */
	public static void main(String[] args) throws Exception {
		/*******************************************************用户*******************************************************************************/
		String prk = "F:\\test\\fabric\\orguser\\Admin@org1.example.com\\msp\\keystore\\priv_sk";
		String cert =  "F:\\test\\fabric\\orguser\\Admin@org1.example.com\\msp\\signcerts\\Admin@org1.example.com-cert.pem";
		//用户证书 对应的是: peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/priv_sk
		//和: peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@example.com-cert.pem
		Enrollment enrollment =	LocalUser.loadFromPemFile(prk, cert);
	    LocalUser user = new LocalUser("admin","Org1MSP",enrollment);
	    //
	    HFClient client = HFClient.createNewInstance();
        CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
        client.setCryptoSuite(cryptoSuite);
        client.setUserContext(user);
		/******************************************************* peer org1****************************************************************************/
        Properties properties = new Properties();
        //节点TLS证书 对应的是: peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem 
        properties.put("pemBytes", Files.readAllBytes(Paths.get("F:\\test\\fabric\\orgca\\tlsca.org1.example.com-cert.pem")) );
     // 其实只需要上面 根证书就可以了, 比如TLS相关的秘钥等都是可选的
        // 对应的是:organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls
//	    properties.put("clientKeyBytes", Files.readAllBytes(Paths.get("F:\\test\\fabric\\orguser\\Admin@org1.example.com\\tls\\client.key")) );
//	    properties.put("clientCertBytes", Files.readAllBytes(Paths.get("F:\\test\\fabric\\orguser\\Admin@org1.example.com\\tls\\client.crt")) );
	    properties.setProperty("sslProvider", "openSSL");
	    properties.setProperty("negotiationType", "TLS");
	    properties.setProperty("hostnameOverride", "peer0.org1.example.com"); //可以覆盖证书校验的主机名, 这样可以不修改 host
	    //properties.setProperty("trustServerCertificate", "false");//仅用于开发 /暂不明意义
        Peer peer = client.newPeer("peer0.org1.example.com", "grpcs://192.168.33.138:7051", properties);
        
		/******************************************************* peer org2       ****************************************************************************/
        Properties properties2 = new Properties();
        properties2.put("pemBytes", Files.readAllBytes(Paths.get("F:\\test\\fabric\\org2ca\\tlsca.org2.example.com-cert.pem")) );
        properties2.setProperty("sslProvider", "openSSL");
        properties2.setProperty("negotiationType", "TLS");
        properties2.setProperty("hostnameOverride", "peer0.org2.example.com"); 
        Peer peer2 = client.newPeer("peer0.org2.example.com", "grpcs://192.168.33.138.com:9051", properties2);
		/******************************************************* orderer****************************************************************************/
        Properties ordererProperties = new Properties();
        ordererProperties.put("pemBytes", Files.readAllBytes(Paths.get("F:\\test\\fabric\\ordererca\\tlsca.example.com-cert.pem")) );
        ordererProperties.setProperty("sslProvider", "openSSL");
        ordererProperties.setProperty("negotiationType", "TLS");
        ordererProperties.setProperty("hostnameOverride", "orderer.example.com"); 
        Orderer orderer = client.newOrderer("orderer","grpcs://192.168.33.138:7050", ordererProperties);
        //
        Channel channel = client.newChannel("testchannel");
        ///需要足够的背书节点 才能发起交易, 但查询不用
        channel.addPeer(peer);
     //   channel.addPeer(peer2);
     //   channel.addOrderer(orderer);
        channel.initialize();
		////////////查询账本
        QueryByChaincodeRequest req = client.newQueryProposalRequest();
        ChaincodeID chaincode = ChaincodeID.newBuilder().setName("fabcar").build();
        req.setChaincodeID(chaincode);
        req.setFcn("queryAllCars");
        ProposalResponse[] rsp = channel.queryByChaincode(req).toArray(new ProposalResponse[0]);
        System.out.format("查询结果====>%s\n",rsp[0].getProposalResponse().getResponse().getPayload().toStringUtf8());
        if(1==1 )return;
        CompletableFuture<BlockEvent.TransactionEvent> future =   sendTranstion(client,channel ,chaincode , user);
        try {
            TransactionEvent result =  future.get();
            Iterable<TransactionActionInfo>  it =  result.getTransactionActionInfos();
            it.forEach( e->{
            	System.out.println("投票信息" +e.getResponseStatus()+" -- "+e.getResponseMessage());
            });
		} catch (Exception e) {
			System.out.println("执行====>结果失败!");
			e.printStackTrace();
		}
	}
 
	/**
	 * 发起交易
	 * 调用已经部署的chaincode的invoke方法
	 */
    private static CompletableFuture<BlockEvent.TransactionEvent> sendTranstion(HFClient client, Channel channel, ChaincodeID ccID, User user) throws ProposalException, InvalidArgumentException{
        Collection<ProposalResponse> successful = new LinkedList<>();
        TransactionProposalRequest transactionProposalRequest = client.newTransactionProposalRequest();
        transactionProposalRequest.setChaincodeID(ccID);
        transactionProposalRequest.setFcn("invoke");
        transactionProposalRequest.setFcn("changeCarOwner");
        transactionProposalRequest.setArgs(new String[] {"CAR0","HongGe"});
        transactionProposalRequest.setProposalWaitTime(300000);
        transactionProposalRequest.setUserContext(user);
        Collection<ProposalResponse> invokePropResp = channel.sendTransactionProposal(transactionProposalRequest);
        for(ProposalResponse pr: invokePropResp) {
            if(pr.getStatus() == Status.SUCCESS) {
              //这里可以获取 txid
                System.out.printf("successful transaction proposal response Txid: %s from peer: %s", pr.getTransactionID(), pr.getPeer().getName());
                successful.add(pr);
            }else {
                System.out.printf("提案发起失败!");
            }
        }
        return channel.sendTransaction(successful,user);
    }
 
}
  • TLS 握手错误

然而并不是证书问题: 正解是grpc://peer0.org1.example.com:7051 地址要改 grpcs://peer0.org1.example.com:7051, 加个s, 我!!!

  • 无法加载 tcnative.dll netty_tcnative_windows_x86_64.dll 参考上 [write_first_app] 调用的是 io.netty.util.internal.NativeLibraryUtil:: loadLibrary方法 最终使用 System.loadLibrary(libName); 加载dll的

  • 发起交易错误

Received invalid transaction event. Transaction ID 5787cb6d5d208c4534afb1d48beecdb91a0b41b3c46601d191c8830a932e9e90 status 10

这即是 没有足够背书节点 同意交易

SDK 安装链码 初始化等

org.hyperledger.fabric-sdk-java fabric-sdk-java 2.0.0

https://www.jianshu.com/p/bfb081a96337

2.0 参考

End2endIT.java 示例文件, 包含:

Register and enroll users with Fabric certificate authority. Constructing channel first time. Installing chaincode. DEPRECATED see v2.0 release notes! Instantiating chaincode. DEPRECATED see v2.0 release notes! Executing chaincode. Querying channel for block information. Chaincode event listener Traversing block for information. Prerequisite for all other testcases.

2.0 推荐 End2endLifecycleIT.java New lifecycle chaincode management APIs (v2.0) 🔥

YAML 背书策略定义

identities: # list roles to be used in the policy
    user1: {"role": {"name": "member", "mspId": "Org1MSP"}} # role member in org with mspid Org1MSP
    user2: {"role": {"name": "member", "mspId": "Org2MSP"}}
    user3: {"role": {"name": "member", "mspId": "Org3MSP"}}
    user4: {"role": {"name": "member", "mspId": "Org4MSP"}}
    admin1: {"role": {"name": "admin", "mspId": "Org1MSP"}} # admin role.
    admin2: {"role": {"name": "admin", "mspId": "Org2MSP"}}
    admin3: {"role": {"name": "admin", "mspId": "Org3MSP"}}
    admin4: {"role": {"name": "admin", "mspId": "Org4MSP"}}
 
policy: # the policy  .. could have been flat but show grouping.
    2-of: # signed by one of these groups  can be <n>-of  where <n> is any digit 2-of, 3-of etc..
      - 1-of:
        - signed-by: "user1" # a reference to one of the identities defined above.
        - signed-by: "user2"
        - signed-by: "admin1"
        - signed-by: "admin2"
      - 1-of:
        - signed-by: "user3"
        - signed-by: "user4"
        - signed-by: "admin3"
        - signed-by: "admin4"

该文件包含两部分:

身份实体 (格式为json字符串)

(1)普通用户角色(姓名和成员服务提供者的ID)

(2)管理员用户角色(姓名和成员服务提供者的ID)

策略 策略下的”1-of”以下数组中任意一个, 说白了就是任意组织中的一个;

而”1-of”则是代表组织中的任意一个用户;

// n-of 指定需要组内多少个进行签名, 1-of 等价于 OR, max-of 等价于AND, 此处2与后面的组相同, 因此是AND

注意 (1)“1-of”中的数字最大不能超过其下数组的长度; “1-of”代表任意一个, “2-of”代表任意两个, 以此类推; 实际上代表策略要求任意数目的组织下的任意数目的用户同意即可;

(2)如果这个数组是数组最大值则代表全部, 即策略要求所有的组织下所有用户同意;

通过txid 查询内容

通过txid 查询到的是原始的事务信息, 如何反序列化出内容, 从TransactionInfo解析出数据?

https://stackoverflow.com/questions/57571732/how-to-parse-protobuf-data-of-envelope-payload-data

项目模块 SDK单元测试 部署主要配置文件

交易排序/共识 Kafka

排序服务

阶段一: 提案

在第一阶段, 客户端应用程序将交易提案发送给一组节点, 这些节点将调用智能合约来生成一个账本更新提案, 然后背书该结果; 背书节点此时不将提案中的更新应用于其账本副本; 相反, 背书节点将向客户端应用程序返回一个提案响应; 已背书的交易提案最终将在第二阶段经过排序生成区块, 然后在第三阶段分发给所有节点进行最终验证和提交;

阶段二: 将交易排序并打包到区块中

在完成交易的第一阶段之后, 客户端应用程序已经从一组节点接收到一个经过背书的交易提案响应; 现在是交易的第二阶段;

在此阶段,应用程序客户端把包含已背书交易提案响应的交易提交到排序服务节点;排序服务创建交易区块, 这些交易区块最终将分发给通道上的所有 Peer 节点, 以便在第三阶段进行最终验证和提交;

排序服务节点同时接收来自许多不同应用程序客户端的交易; 这些排序服务节点一起工作, 共同组成排序服务; 它的工作是将提交的交易按定义好的顺序安排成批次, 并将它们打包成区块; 这些区块将成为区块链的区块!

区块中的交易数量取决于区块的期望大小和最大间隔时间相关的通道配置参数(确切地说, 是 BatchSize 和 BatchTimeout 参数); 然后将这些区块保存到排序节点的账本中, 并分发给已经加入通道的所有节点; 如果此时恰好有一个 Peer 节点关闭, 或者稍后加入通道, 它将在重新连接到排序服务节点或与另一个 Peer 节点通信之后接收到这些区块; 我们将在第三阶段看到节点如何处理这个区块;

值得注意的是, 一个区块中交易的顺序不一定与排序服务接收的顺序相同, 因为可能有多个排序服务节点几乎同时接收交易; 重要的是, 排序服务将交易放入严格的顺序中, 并且 Peer 节点在验证和提交交易时将使用这个顺序;

区块内交易的严格排序使得 Hyperledger Fabric 与其他区块链稍有不同, 在其他区块链中, 相同的交易可以被打包成多个不同的区块, 从而形成一个链; 在 Hyperledger Fabric 中, 由排序服务生成的区块是最终的; 一旦一笔交易被写进一个区块, 它在账本中的地位就得到了保证; 正如我们前面所说, Hyperledger Fabric 的最终性意味着没有账本分叉, 也就是说, 经过验证的交易永远不会被重写或删除;

阶段三: 验证和提交 交易工作流的第三个阶段涉及到从排序节点到 Peer 节点的区块的分发和随后的验证, 这些区块可能会被提交到账本中;

第三阶段排序节点将区块分发给连接到它的所有 Peer 节点开始; 同样值得注意的是, 并不是每个 Peer 节点都需要连接到一个排序节点, Peer 节点可以使用 gossip 协议将区块关联到其他节点;

每个节点将独立地以确定的方式验证区块, 以确保账本保持一致; 具体来说, 通道中每个节点都将验证区块中的每个交易, 以确保得到了所需组织的节点背书, 也就是节点的背书和背书策略相匹配, 并且不会因最初认可该事务时可能正在运行的其他最近提交的事务而失效 ;无效的交易仍然保留在排序节点创建的区块中, 但是节点将它们标记为无效, 并且不更新账本的状态;

基于Kafka 的排序服务

启用基于 Kafka 的排序服务

概览

每个通道映射到 Kafka 中一个单独的单分区主题(topic); 当一个 OSN 通过 Broadcast RPC 接收到交易时, 它会进行检查以确认广播的客户端有写入通道的权限, 然后将交易转发(或者说是生产)到 Kafka 中合适的分区中; 这个分区被 OSN 消费, 将接受到的交易打包到本地区块, 持久化保存在他们的本地账本, 然后通过 Deliver RPC 将他们发送给接收客户端; 底层的细节请参考 the document that describes how we came to this design , 图表 8 阐述了上述过程;

图 8

  1. 客户端提交交易 OSN0

  2. OSN0 检查以确认广播的客户端有写入通道的权限, 然后将交易转发**(或者说是生产)到 Kafka 中合适的分区中;**

3.这个分区被 OSN 消费, 将接受到的交易打包到本地区块, 持久化保存在他们的本地账本, 然后通过 Deliver RPC 将他们发送给接收客户端;

简而言之, 将排序过程转移到Kafka中 这里面的共识, 就是客户端收集到足够背书的提案提交到orderer, orderer再把交易生产到kafka 主题分区中, 同时 orderer 也是消费者, 消费排序好的交易 然后再打包区块, 同步区块

先部署kafka

参考 Kafka - (N_Hadoop.md)

部署4个

配置文件放在 /opt/fabric/manager/kafka

zookeeper

cd 
`bin/zookeeper-server-start.sh -daemon /opt/fabric/manager/kafka/zookeeper.properties `

**kafka broker**
bin/kafka-server-start.sh /opt/fabric/manager/kafka/server0.properties

bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server0.properties
bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server1.properties 
bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server2.properties 
bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server3.properties 

停止
bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server3.properties
bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server2.properties
bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server1.properties
bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server0.properties
//脚本停不掉.. 
 ps ax | grep -i 'kafka\.Kafka' | grep java | grep -v grep | awk '{print $1}'

bin/zookeeper-server-stop.sh /opt/fabric/manager/kafka/zookeeper.properties
端口
broker0.joinken.cn:9090
192.168.10.140 broker0.joinken.cn //9090
192.168.10.140 broker1.joinken.cn
192.168.10.140 broker2.joinken.cn
192.168.10.140 broker3.joinken.cn

测试下:

创建 // bin/kafka-topics.sh —create —bootstrap-server 192.168.10.140:9092 —replication-factor 2 —partitions 4 —topic kafka-test //kafka_2.12-2.5.0 的

bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 2 --partitions 4 --topic kafka-test

查看 // bin/kafka-topics.sh --describe --bootstrap-server broker0.joinken.cn:9092 --topic kafka-test// //kafka_2.12-2.5.0 的

bin/kafka-topics.sh —describe —zookeeper localhost:2181 —topic kafka-test

Topic:kafka-test        PartitionCount:4        ReplicationFactor:2     Configs:segment.bytes=1073741824
        Topic: kafka-test       Partition: 0    Leader: 2       Replicas: 2,3   Isr: 2,3
        Topic: kafka-test       Partition: 1    Leader: 3       Replicas: 3,1   Isr: 3,1
        Topic: kafka-test       Partition: 2    Leader: 1       Replicas: 1,0   Isr: 1,0
        Topic: kafka-test       Partition: 3    Leader: 0       Replicas: 0,2   Isr: 0,2

//列出主题列表 ./bin/kafka-topics.sh —list —zookeeper localhost:2181

bin/kafka-topics.sh —describe —zookeeper localhost:2181 —topic system-channel

重新部署 orderer

Fabric不支持切换共识机制! 一旦选定了共识机制后, 无法修改, 除非清空所有数据, 重新部署;

修改configtx.yaml中Orderer部分的内容, 将共识机制修改为kafka, 并填入kafka节点的地址:

多orderer

目前只有一个 orderer, 改至4个 orderer, 从生成证书开始…

//证书生成配置

OrdererOrgs:
  - Name: Orderer
    Domain: joinken.cn
    EnableNodeOUs: true
    Specs:
      - Hostname: orderer0
      - Hostname: orderer1
      - Hostname: orderer2
      - Hostname: orderer3

cryptogen generate --config=./crypto-config-orderer2.yaml --output="organizations2"

把orderer 相关证书替换了…

vi /etc/hosts

192.168.10.140 orderer0.joinken.cn 192.168.10.140 orderer1.joinken.cn 192.168.10.140 orderer2.joinken.cn 192.168.10.140 orderer3.joinken.cn

创世块 配置
Orderer: &OrdererDefaults
    # 改为kafka
    OrdererType: kafka
    # 所有 orderer 地址
    Addresses:
      - orderer0.joinken.cn:7050
      - orderer1.joinken.cn:7051
      - orderer2.joinken.cn:7052
      - orderer3.joinken.cn:7053
    ## 区块配置 看着办吧 
    # 多长时间产生一个区块
    BatchTimeout: 10s
    # Batch Size: Controls the number of messages batched into a block
    BatchSize:
        # 交易的最大数据量, 数量达到之后会产生区块, 建议100左右
        MaxMessageCount: 30
 
        #数据量达到这个值, 会产生一个区块, 最优化 与Kafka broker 配置关联.. 有点复杂
        AbsoluteMaxBytes: 8 MB
 
        # 默认区块大小; Kafka 对于相对较小的消息有较高的吞吐量; 所以该值不要大于1 MiB; 
        PreferredMaxBytes: 512 KB
    ## 包含 至少两个 你集群中的 Kafka broker 的 IP:port ; 列表中不需要是所有的节点; (这些是你的引导 broker ; )
    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:
            - broker0.joinken.cn:9090
            - broker1.joinken.cn:9091
            - broker2.joinken.cn:9092
            - broker3.joinken.cn:9093

详细说明 参考官方

Kafka broker 配置关联**

这里指的是kafka的server.properties 配置, 可以参考:[附录 kafka server.properties 配置] - (./N_Hadoop.png) 也可以参考 官方文档

unclean.leader.election.enable = false — 数据持久化是区块链环境中的重要环节; 我们不能在同步复制集合之外选择一个领导通道, 或者我们冒着覆盖上一个领队产生的偏移量的风险, 并因此重写排序节点产生的区块;

min.insync.replicas = M //大概的意思是 数据写入至少 M 个副本之后才认为被提交 default.replication.factor = N 根上面差不多, 如果你设置为 N = K(K是broker) (那么只要有一个 broker 宕机了, 就意味着区块链网络就不能创建通道了, 也就是说排序服务的崩溃容错就不存在了;

注意 message.max.bytesreplica.fetch.max.bytes 的值 应该大与 A(Orderer.AbsoluteMaxBytes) 的值, 再为头部数据增加一些空间(多余 1 MiB 就够了)

message.max.bytes: Broker 接受 Producer 消息的最大字节数 replica.fetch.max.bytes : leader复制时候的socket缓存大小 fetch.max.bytes :决定消费者单次从 Broker 获取消息的最大字节数

实在使用的主要配置 1024102410=‭10485760‬//10MB

broker.id=0
listeners=PLAINTEXT://:9090
log.dirs=/tmp/kafka-logs-0
num.partitions=4
message.max.bytes=‭10485760‬
replica.fetch.max.bytes=‭10485760‬

//生成创世块 configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block

docker compose

参考: fabric-samples\first-network\docker-compose-etcdraft2.yaml

docker-compose-base.yaml

services:
  orderer0.joinken.cn:
      container_name: orderer0.joinken.cn
      extends:
        file: peer-base.yaml
        service: orderer-base
      environment:
        - ORDERER_GENERAL_LISTENPORT=7050
      volumes:
          # 创世块 orderer 文件 不复制了 麻烦...
          - /opt/fabric/manager/system-genesis-block/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
          # orderer 的 msp tls
          - /opt/fabric/orderer/joinken.cn/orderers/orderer0.joinken.cn/msp:/var/hyperledger/orderer/msp
          - /opt/fabric/orderer/joinken.cn/orderers/orderer0.joinken.cn/tls:/var/hyperledger/orderer/tls
          # 工作目录
          - /opt/fabric/orderer/joinken.cn/orderers/orderer0.joinken.cn/data:/var/hyperledger/production/orderer
      ports:
        - 7050:7050
 
  orderer1.joinken.cn:
      container_name: orderer1.joinken.cn
      extends:
        file: peer-base.yaml
        service: orderer-base
      environment:
        - ORDERER_GENERAL_LISTENPORT=7051
      volumes:
          - /opt/fabric/manager/system-genesis-block/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
          - /opt/fabric/orderer/joinken.cn/orderers/orderer1.joinken.cn/msp:/var/hyperledger/orderer/msp
          - /opt/fabric/orderer/joinken.cn/orderers/orderer1.joinken.cn/tls:/var/hyperledger/orderer/tls
          - /opt/fabric/orderer/joinken.cn/orderers/orderer1.joinken.cn/data:/var/hyperledger/production/orderer
      ports:
      # 注意端口啊
        - 7051:7051
 
循环几遍...
 
 
`peer配置都一样, 略
  ....

改下 peer-base.yaml

orderer 和 peer都 引用

  orderer-base:
  extra_hosts:
     - broker0.joinken.cn:192.168.10.140
     - broker1.joinken.cn:192.168.10.140
     - broker2.joinken.cn:192.168.10.140
     - broker3.joinken.cn:192.168.10.140

extra_hosts: //相当与 修改 容器的 host

改下 docker-compose-cli.yaml

version: '2'
 
volumes:
  orderer0.joinken.cn:
  orderer1.joinken.cn:
  orderer2.joinken.cn:
  orderer3.joinken.cn:
  peer0.org1.joinken.cn:
  peer0.org2.joinken.cn:
  peer0.org3.joinken.cn:
  peer0.org4.joinken.cn:
networks:
  byfn:
 
services:
 
  orderer0.joinken.cn:
    extends:
      file: docker-compose-base.yaml
      service: orderer0.joinken.cn
    container_name: orderer0.joinken.cn
    networks:
      - byfn
  orderer1.joinken.cn:
    extends:
      file: docker-compose-base.yaml
      service: orderer1.joinken.cn
    container_name: orderer1.joinken.cn
    networks:
    ...
 
 
    depends_on:
      - orderer0.joinken.cn
      - orderer1.joinken.cn
      - orderer2.joinken.cn
      - orderer3.joinken.cn
      - peer0.org1.joinken.cn
      - peer0.org2.joinken.cn
      - peer0.org3.joinken.cn
      - peer0.org4.joinken.cn
 

有客户端 比较好调试

启动看下 export IMAGE_TAG=“2.1.0”

cd /opt/fabric/manager/docker-config export IMAGE_TAG=“2.0.0” export COMPOSE_PROJECT_NAME=“tttx”

docker-compose -f docker-compose-cli.yaml up

Bind for 0.0.0.0:7051 failed: port is already allocated 我.. orderer 7051 与peer 有冲突.. orerer 关联创世块, 智能改peer org1

创建 channel 文件 configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./joinken.tx -channelID joinken

Orderer不能连接kafka 错误

错误:

  • Error: got unexpected status: SERVICE_UNAVAILABLE -- backing Kafka cluster has not completed booting; try again later
  • FABRIC_LOGGING_SPEC=DEBUG
debug 日志看下
  Failed to connect to broker broker0.joinken.cn:9090: dial tcp 192.168.10.140:9090: connect: no route to host
orderer 连不上
 
//进入容器 可以ping通
docker exec -it 52c7cfd69972 sh 
 
//原因是端口不可访问
wget 192.168.10.140:9090
 
//再原因是 防火墙的问题MD..
 

///最最最终 查下主题列表 ./bin/kafka-topics.sh --list --zookeeper localhost:2181

[root@blocknode1 kafka_2.11-2.2.2]# ./bin/kafka-topics.sh --list --zookeeper localhost:2181
kafka-test
system-channel

多了个system-channel fabric的系统channel主题

kafka_2.11-1.0.0.tgz

  • Failed to connect to broker orderer0.joinken.cn:9093: dial tcp 172.20.0.7:9093: connect: connection refused 解析的IP不对. broker 的域名也不对,

正解是忘了

advertised.listeners=PLAINTEXT://192.168.10.140:9091

当Kafka broker启动时, 它会在ZK上注册自己的IP和端口号, 客户端就通过这个IP和端口号来连接; 默认注册到ZK上的IP是内网才能访问的内网IP; 此时就需要显示指定 advertised.host.name, advertised.listeners参数, 让注册到ZK上的IP是外网IP;

参考stackoverflow, 简直一毛一样

The way Kafka works, the client connects into a broker from an initial list (in the Fabric case, provided through the channel config). Then that broker replies with a list of information about other brokers that exist, which partitions they are leaders of, etc. The client then uses this second list to pick the correct broker to connect to.

Kafka的工作方式是, 客户端从初始列表(在Fabric情况下, 通过通道配置提供)连接到代理; 然后, 该代理将提供有关存在的其他代理的信息列表, 它们是其领导者的分区, 等等; 然后, 客户端使用第二个列表来选择要连接的正确代理;

所以第一个是可以连接, 创建channel, 然后其他都失败了, 万年巨坑!!…

bin/kafka-topics.sh —describe —zookeeper localhost:2181 —topic joinken bin/kafka-topics.sh —describe —zookeeper localhost:2181 —topic system-channel

加入Fabric CAS 构建组织架构

可以完全独立的节点,负责颁发和验证证书等, 单独运行, 暂不用..

Fabric 是一个被许可的网络, 由一系列Peers节点组成, 如一个Peers需要参到交易中,
需要一个CA(证书颁发机构)节点(多个RCA(Root CA)根证书以及ICA(Intermediate CA)中间证书; 有一个信任链 如果RCA得不到信任, 那么其下的ICA也无法认证通过; ), 颁发一个(私钥/公钥), ordering service节点上的MSP包含公钥 验证附加到交易上的签名是否有效. 所有节点上都有一个或多个自己的MSP在交互过程使用自己证书签名信息 这个MSP机制(每个组织都可以有不同的MSP, 另channel上有一个全局的MSP)可以为成员提供一组网络中的角色和权限

ca 与 msp

简而言之, msp 是一个抽象提供, 验证用户 生成证书等, 定义规范接口, ca 是其的实现

下载 fabric-ca-client的二进制文件

先缺省启动一下(或者使用./fabric-ca-server init -b admin:pass 只初始化)

./fabric-ca-server start -b admin:adminpw

它会生成一下配置文件

 ca-cert.pem
├── fabric-ca-client
├── fabric-ca-server
├── fabric-ca-server-config.yaml
└── msp
    └── keystore
        └── 2123e7a56d3173727ca7ff5f5fceb9929b9335591f7258f358fec51354d2da16_sk


ca-cert.pem为生成的自签署证书, 是根据fabric-ca-server-config.yaml中的csr生成, 通过openssl命令查看证书内容 openssl x509 -in ca-cert.pem -noout -text 如果要生成被其它CA签署的证书, 使用-u指定上一级CA 如果要签署已经准备好的key, 需要用ca.certfile和ca.keyfile指定

fabric-ca-config.yaml为生成的默认配置文件, 如果需要自定义配置, 可以使用–config xx-config.yaml来进行启动配置. 也可设置FABRIC_CA_SERVER_HOME环境变量, 在该环境变量所指向的目录下配置fabric-ca-server-config.yaml(默认寻找的配置文件, 如无则创建默认配置文件),并在该目录下启动Server, 则Server便会使用当前目录下的配置文件;

msp/keystore 存储登记用户的 私钥和CA证书链PEM文件 的目录(后面生成凭证)

关键是配置文件fabric-ca-config.yaml

Configuring the database

//配置数据库

db:
  type: mysql
  datasource: yang:sql123321@tcp(192.168.33.139:3306)/fabric_ca?parseTime=true&tls=false
  tls:
      enabled: false
 

Fabric CA默认使用SQLite数据库, 这是一种嵌入式的文件数据库, 如果需要将在集群中部署Fabric CA, 那么就需要使用PostgreSQL或者MySQL数据库

//启动 ./fabric-ca-server start

生成fabric-ca admin的凭证

cd ~
$> ./fabric-ca-client enroll -u http://admin:adminpw@localhost:7054 -H $HOME/fabric-ca-admin
 
# 用-H参数指定client目录: 
# 也可以用环境变量FABRIC_CA_CLIENT_HOME指定了client的工作目录, 生成的用户凭证将存放在这个目录中; 
 
2020/03/26 18:10:43 [INFO] generating key: &{A:ecdsa S:256}
2020/03/26 18:10:43 [INFO] encoded CSR
2020/03/26 18:10:43 [INFO] Stored client certificate at /root/fabric-ca/clients/admin/msp/signcert.pem
2020/03/26 18:10:43 [INFO] Stored root CA certificate at /root/fabric-ca/clients/admin/msp/calocalhost-7054.pem
2020/03/26 18:10:43 [INFO] Stored Issuer public key at /root/fabric-ca/clients/admin/msp/IssuerPublicKey
2020/03/26 18:10:43 [INFO] Stored Issuer revocation public key at /root/fabric-ca/clients/admin/msp/IssuerRevocationPublicKey

生成了msp目录 复制客户端文件 cp fabric-ca-client $HOME/fabric-ca-admin 到目录 之后就可以通过msp目录中的证书, 访问fabric-ca, 不需要在输入密码;

  • 注意

mysql 要去掉严格模式,不然会初始化表失败 set @@global.sql_mode=(select replace(@@global.sql_mode,'NO_ZERO_IN_DATE,NO_ZERO_DATE','')); 临时全局设置, 永久需要修改my.ini文件

新用户流程

例如注册一个名为admin2的用户, 先指定要使用的用户的client目录: $ export FABRIC_CA_CLIENT_HOME=$HOME/fabric-ca-admin 也可以用-H参数指定: $ fabric-ca-client -H /opt/app/fabric-ca/clients/admin <其它参数...>

  • 登记

登记新用户:

$ fabric-ca-client register --id.name admin2 --id.affiliation org1.department1 --id.attrs 'hf.Revoker=true,admin=true:ecert'
2018/04/28 10:20:42 [INFO] Configuration file location: /opt/app/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2018/04/28 10:20:42 [INFO] 127.0.0.1:38036 POST /register 201 0 "OK"
Password: wvkpvMzLsjPz

新用户注册后, 会自动为新用户生成一个密码, 新用户需要使用这个密码, 生成自己的凭证, 就像第一个用户那样;

  • 生成凭证

然后生成凭证:

fabric-ca-client enroll -u http://admin2:wvkpvMzLsjPz@localhost:7054 之后可以看到新的目录中也生成了fabric-ca-client-config.yaml和msp/, 这是admin2用户的凭证:

  • 用户属性

在注册admin2的时候, 指定了这样一些属性--id.attrs 'hf.Revoker=true,admin=true:ecert': 这些属性是一定要了解的, 以hf.开头的属性, 是fabric-ca的内置属性:

用户自定义的属性可以写入到用户的凭证中, 在合约中可以获取发起请求的用户属性, 根据用户属性决定用户是否由操作权限;

  • 查看用户

可以用fabric-ca-client identity list 查看 有权限查看的用户的详情

组织

//操作用户主目录, 这样可以不用 每次-H 参数 export FABRIC_CA_CLIENT_HOME=$HOME/fabric-ca-admin

初始化话的时候会默认创建两个组织

查看组织

查看组织命令: ./fabric-ca-client -H $HOME/fabric-ca-admin affiliation list

affiliation: .
   affiliation: org2
      affiliation: org2.department1
   affiliation: org1
      affiliation: org1.department1
      affiliation: org1.department2

组织是有层级的

org2 下面包含 org2.department1

删除组织

删除组织组织: ./fabric-ca-client -H $HOME/fabric-ca-admin affiliation remove --force org1

默认是禁止删除联盟, 如果需要开启, 需要在启动fabric-ca-server时传入参数—cfg.affiliations.allowremove

添加组织

手动添加组织:

./fabric-ca-client  -H $HOME/fabric-ca-admin  affiliation add com //需先添加顶层
./fabric-ca-client  -H $HOME/fabric-ca-admin  affiliation add com.example

example.com 准备msp, 将ca证书等存放example.com组织的目录中,

msp目录, 包含都是CA根证书, 分别是TLS加密的根证书, 和用于身份验证的根证书; 另外还需要admin用户的证书

./fabric-ca-client getcacert -M pwd/fabric-ca-files/example.com/msp //-M需要指定绝对路径

这里是用getcacert为每个组织准备需要的ca文件, 在生成创始块的时候会用到;

为每个组织准备msp

就是组织用来 签署用户的根证书?

//目录
mkdir -p ./fabric-ca-files/example.com/msp

//生成
./fabric-ca-client getcacert -M `pwd`/fabric-ca-files/example.com/msp    //-M需要指定绝对路径

Fabric-CA的使用演示

注册组织
 
$> fabric-ca-client register --id.name admin2 --id.affiliation org1.department1 --id.attrs 'hf.Revoker=true,admin=true:ecert'

命令使用管理员身份的凭据注册一个新身份, 其注册 id 为” admin2” , 从属于”org.department1” , 属性名为” hf”; 值为” true” , 属性名为” admin” , 值为” true”; ”: ecert”后缀意味着默认情况下” admin”属性及其值将插入到标识的注册证书中, 然后可以使用该证书作出访问控制决策; 密码, 也称为注册密码, 会被打印出来; 注册身份必须使用此密码(使用—id.secret 可以指定);

Enrolling a peer identity 登记peer身份

组织结构

FabricCA的基本概念与用法 fabric ca操作指南

拓扑图

!图

链码 Java开发指南

智能合约/链码/chaincode

智能合约Java开发指南

um…麻烦的一批, 用node 开发

node 开发

Fabric链码API文档 - Node.js

//使用fabric-contract-api的链码下, 除了构造函数之外的每个方法都自动成为链码的方法, 可供外部应用调用 
const { Contract } = require('fabric-contract-api');
 
class Coc extends Contract {
    async initLedger(ctx) {
        console.info('============= START: Initialize Ledger ===========');
        const data =  {
            desc: '1.0',
        };
        await ctx.stub.putState('desciption', Buffer.from(JSON.stringify(data)));
        console.info('============= END: Initialize Ledger ===========');
    }
 
    async deleteRecord(ctx, id) {
        await ctx.stub.deleteState(id);
    }
 
    async updateRecord(ctx, id, info) {
        await ctx.stub.putState(id, Buffer.from(JSON.stringify(info)));
    }
 
    async queryRecordByID(ctx, id) {
        const carAsBytes = await ctx.stub.getState(id);
        if (!carAsBytes || carAsBytes.length === 0) {
            throw new Error(`${id} does not exist`);
        }
        console.log(carAsBytes.toString());
        return carAsBytes.toString();
    }
    /**
     * 查询历史记录
     * 参考 https://hyperledger.github.io/fabric-chaincode-node/master/api/tutorial-using-iterators.html)
     * @param {*} ctx 
     * @param {*} id 
     */
    async queryHistoryRecordByID(ctx, id) {
        const promiseOfIterator = ctx.stub.getHistoryForKey(id);
        const results = [];
        for await (const keyMod of promiseOfIterator) {
            const resp = {
                timestamp: keyMod.timestamp,
                txid: keyMod.tx_id
            }
            if (keyMod.is_delete) {
                resp.data = 'KEY DELETED';
            } else {
                resp.data = keyMod.value.toString('utf8');
            }
            results.push(resp);
        }
        return JSON.stringify(allResults);
    }
}
module.exports = Coc;
 

理解

hyperledger fabric 架构详解 Fabric掰开揉碎, 一文解惑

configtx.yaml 配置

通道配置(configtx)

fabric配置讲解

Orderer 的配置

BatchTimeout 是配置多久产生一个区块, 默认是2秒, 通常在项目实践中, 我们发现交易量并不大, 如果配置的时间过小就会产生很多空的区块, 配置时间太长, 则发现等待产生区块的时间太长; 具体时间由交易频率和业务量决定; 我们实际项目中, 通常配置在30秒;

MaxMessageCount 是配置在一个区块中允许的交易数的最大值; 默认值是10; 交易数设置过小, 导致区块过多, 增加orderer的负担, 因为要orderer要不断的打包区块, 然后deliver给通道内的所有peer,这样还容易增加网络负载, 引起网络拥堵; 我们实际项目中通常配置500, 不过具体还应该看业务情况, 因为如果每个交易包含的数据的size如果太大, 那么500个交易可能导致一个区块太大, 因此需要根据实际业务需求权衡;

BatchTimeout和MaxMessageCount的任何一个参数条件满足, 都会触发产生新的区块; (https://hyperledger-fabric.readthedocs.io/zh_CN/latest/orderer/ordering_service.html?highlight=BatchTimeout#null)

2.0 示例项目默认是 etcdraft

配置并使用 Raft 排序服务

fabric 交易流程

1.应用程序客户端通过SDK调用证书服务(CA)服务, 进行注册和登记, 并获取身份证书;

2.应用程序客户端通过SDK创建好交易提案(Proposal), 交易提案把带有本次交易要调用的合约标识, 合约方法和参数信息以及客户端签名等信息发送给背书(Endorser)节点;

发起端(一般是SDK), 需要指定交易发给哪些节点进行背书验证(fabric不会自动发送), 而是由sdk发送;

Endorsement policy 设计 背书策略有两个主要组成部分:

主体(principal): P定义了期望的签名来源实体

门槛(thshold gate): T有两个参数: 整数t(阈值)和n个主体, 表示从这n个主体中获取t个签名

例如:

T(2, ‘A’, ‘B’, ‘C’)表示需要A, B, C中任意2个主体的签名背书

T(1, ‘A’, T(2, ‘B’, ‘C’))表示需要来自主体A的签名或者来自B和C两者的签名背书

https://www.cnblogs.com/preminem/p/8305054.html

3.背书(Endorser)节点收到交易提案(Proposal)后, 开始进行验证, 验证的内容如下:

交易预案是完好的 该预案以前没有提交过(防止重放攻击) 携带的签名是合法的 交易发起者是否满足区块链写策略, 即ACL 权限检查 满足以上要求后, 背书节点把’交易预案’作为输入参数, 调用chaincode中的函数, chaincode根据当前的账本状态计算出一个’交易结果’, 该结果包括返回值, 读写集; 此时, 区块链账本并不会被更新;’交易结果’在被签名后与一个是/否的背书结果一同返回, 称之为’预案回复’;

4.应用程序客户端收到背书(Endorser)节点返回的信息后,

判断提案结果是否一致, 以及是否收到足够多的背书节点返回的结果(参照指定的背书策略执行), 如果没有足够的背书, 则中止处理, 这个交易就会被舍弃; 否则, 将交易提案, 模拟交易结果和背书信息打包组成一个交易并签名发给Orderer节点(一个排序服务);

5.Orderer节点对来自客户端(SDK)的交易信息进行共识排序, 分通道对’交易消息’按时间排序,并按通道将交易打包成块, 发送给提交(Committer)节点;

6.提交(Committer)节点收到区块后, 会对区块中的每笔交易进行校验, 检查交易依赖的输入输出是否符合当前区块链的状态, 验证背书策略是否满足,验证完成后将区块追加到本地的区块链, 更新账本, 并修改世界状态; 具体过程如下:

运行验证逻辑(VSCC检查背书策略) 在区块中指明哪些交易是有效和无效的; 在内存或文件系统上把区块加入区块链 将区块内的有效交易写入状态数据库; 发出Event消息, 使得客户端通过SDK监听知道哪些交易是有效的或无效的;

所有的peer节点都是committer(记账节点), 而又有可能担任的角色有endorser(背书节点), Leader(主节点), Anchor(锚节点);

https://segmentfault.com/a/1190000017172518

逻辑节点

  1. Client节点

client代表由最终用户操作的实体, 它必须连接到某一个peer节点或者orderer节点上与区块链网络通信;客户端向endorser提交交易提案, 当收集到足够背书后, 向排序服务广播交易, 进行排序, 生成区块; 但是该节点的故障不会影响区块链网络的正常运行;

  1. CA节点 CA节点是hyperledger 1.0的证书颁发机构, 由服务器(fabric-ca-service)和客户端组件(fabric-ca-client)组成; CA节点接收客户端的注册申请, 返回注册密码用于用户登入, 以便获取身份证书, 在区块链网络上所有的操作都会验证用户的身份; 因此该节点的故障只会影响到用户的注册申请;

  2. Orderer节点 orderer负责接收包含背书签名的交易, 对未打包的交易进行排序生成区块, 广播给peer节点;

排序服务可以采用集中式服务(solo, 不适合实际生产环境), 也可以采用分布式协议(目前正是发布的版本只支持Apache Kafka集群, 只能实现崩溃故障容错); BFT(拜占庭容错)的排序服务会在1.x周期内发布; 我们也可以为fabric写一个共识实现, 一个共识插件需要实现consensus package中定义的Consenter和Chain接口, 我们可以来研究已经针对这些接口构建的plugin(solo和kafka)为自己提供线索;

  1. Peer节点

首先所有的peer节点都是committer(记账节点), 而又有可能担任的角色有endorser(背书节点), Leader(主节点), Anchor(锚节点);

Committer

记账节点使用基于Gossip的p2p数据分发, 节点会定期跟其他节点交换信息; 如果在这个过程中有节点发生故障, 则会从存活的节点中删除这个节点的信息;

Leader

主节点连接到排序服务, 负责把接受到的批量区块转发给其他节点; 因此主节点与排序服务的稳定连接至关重要;

Endorser 背书节点为动态的角色与具体的chaincode绑定, 背书节点的故障对网络的影响取决于chaincode对应的背书策略, 例如背书策略指定只要3个组织其中的2个组织的成员完成背书, 该交易就是有效的, 那么只有一个组织的成员节点出现故障对交易完成没有影响;

Anchor 锚节点是在一个channel上可以被所有其他peer发现的peer, channel上的每个成员都有一个anchor Peer(或多个anchor peer 来防止单点故障), 允许属于不同成员的peer发现channel上的所有现有peer; 锚节点的配置文件可以通过configtxgen工具生成;

https://www.cnblogs.com/preminem/p/8729781.html

农产品合格证 部署

链码

./peer lifecycle chaincode package fabcar.tar.gz —path /opt/deploy2_temp/chaincode/javascript —lang node —label fabcar2.0 ./peer lifecycle chaincode install fabcar.tar.gz

//打包
./peer lifecycle chaincode package gx_coc.tar.gz --path /opt/deploy2_temp/chaincode/gxcoc --lang node --label gx_coc_2.2
cp gx_coc.tar.gz /opt/deploy2/manager/peer/Admin@org2.example.com/
cp gx_coc.tar.gz /opt/deploy2/manager/peer/Admin@org1.example.com/
//安装
./peer lifecycle chaincode install gx_coc.tar.gz
 
//查询
./peer lifecycle chaincode queryinstalled
 
CC_PACKAGE_ID=gx_coc_2.2:28b6e1c901ed20d7c77f1e1ab356e023835562b9cb14213ea4d6c7185de2c074
SEQUEUE=5
 
//批准
./peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID testchannel --name gxcoc --version 2.0 --init-required --package-id $CC_PACKAGE_ID --sequence $SEQUEUE --tls true --cafile ./tlsca.example.com-cert.pem
// —-signature-policy "AND('Org1.member', 'Org2.member')" //背书策略
 
//查询批准 (安装中查询会中断..)
./peer lifecycle chaincode checkcommitreadiness --channelID testchannel --name gxcoc --version 2.0 --init-required --sequence $SEQUEUE --tls true --cafile ./tlsca.example.com-cert.pem --output json 
 
 
//提交
./peer lifecycle chaincode commit -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID testchannel --name gxcoc --version 2.0 --sequence $SEQUEUE --init-required --tls true --cafile ./tlsca.example.com-cert.pem --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
 
 
//初始化 first
./peer chaincode invoke -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --tls true --cafile ./tlsca.example.com-cert.pem -C testchannel --name gxcoc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/deploy2_temp/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --isInit -c '{"function":"initLedger","Args":[]}'
 

command

./peer chaincode query -C testchannel -n gxcoc -c '{"function":"queryHistoryRecordByID","Args":["0xff"]}'
./peer chaincode query -C testchannel -n gxcoc -c '{"function":"queryRecordByID","Args":["0xff"]}'
service docker start 
 
export IMAGE_TAG="2.0.0"
export COMPOSE_PROJECT_NAME="tttx"
 
cd /opt/deploy2/docker/ 
docker-compose -f docker-compose-cli.yaml up
 
--ordererTLSHostnameOverride peer0.org1.example.com
 
export CORE_PEER_ADDRESS=peer0.org1.joinken.cn:7051
export  FABRIC_CFG_PATH=$PWD
cd /opt/deploy2/manager/Admin@org1.joinken.cn/
peer channel list
 
 
export CORE_PEER_ADDRESS=peer0.org2.joinken.com:9051
export FABRIC_CFG_PATH=$PWD
cd /opt/deploy2/manager/Admin@org2.joinken.cn/
peer channel list
 

启动

cd /opt/deploy2/orderer.joinken.cn
export  FABRIC_CFG_PATH=$PWD
nohup orderer& >/dev/null 2>&1 &
 
 
cd /opt/deploy2/peer0.org1.joinken.cn
export  FABRIC_CFG_PATH=$PWD
nohup peer node start >/dev/null 2>&1 &
 
cd /opt/deploy2/peer0.org2.joinken.cn
export  FABRIC_CFG_PATH=$PWD
nohup peer node start >/dev/null 2>&1 &
# export CORE_PEER_ADDRESS=peer0.org2.joinken.cn:8051
# peer channel list

部署 140

证书

cryptogen generate --config=./crypto-config-org1.yaml --output="organizations"
cryptogen generate --config=./crypto-config-org2.yaml --output="organizations"
cryptogen generate --config=./crypto-config-org3.yaml --output="organizations"
cryptogen generate --config=./crypto-config-org4.yaml --output="organizations"
cryptogen generate --config=./crypto-config-orderer.yaml --output="organizations"

configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block

// 部署根
cd /opt/fabric

目录/主机

// 停止所有容器
docker  stop $(sudo docker ps -a -q) 
docker  rm $(sudo docker ps -a -q) 

rm -rf  /opt/fabric/manager/system-genesis-block/genesis.block
rm -rf  /opt/fabric/orderer/joinken.cn/orderers/orderer0.joinken.cn/data
rm -rf  /opt/fabric/orderer/joinken.cn/orderers/orderer1.joinken.cn/data
rm -rf  /opt/fabric/orderer/joinken.cn/orderers/orderer2.joinken.cn/data
rm -rf  /opt/fabric/orderer/joinken.cn/orderers/orderer3.joinken.cn/data
rm -rf  /opt/fabric/peer/org1.joinken.cn/peers/peer0.org1.joinken.cn/data
rm -rf  /opt/fabric/peer/org2.joinken.cn/peers/peer0.org2.joinken.cn/data
rm -rf  /opt/fabric/peer/org3.joinken.cn/peers/peer0.org3.joinken.cn/data
rm -rf  /opt/fabric/peer/org4.joinken.cn/peers/peer0.org4.joinken.cn/data

//kafka
rm -rf  /tmp/kafka-logs-0
rm -rf  /tmp/kafka-logs-1
rm -rf  /tmp/kafka-logs-2
rm -rf  /tmp/kafka-logs-3
rm -rf  /tmp/zookeeper

bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server3.properties
bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server2.properties
bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server1.properties
bin/kafka-server-stop.sh /opt/fabric/manager/kafka/server0.properties
ps ax | grep -i 'kafka\.Kafka' | grep java | grep -v grep | awk '{print $1}'
bin/zookeeper-server-stop.sh /opt/fabric/manager/kafka/zookeeper.properties


//////host
192.168.10.140 orderer0.joinken.cn
192.168.10.140 orderer1.joinken.cn
192.168.10.140 orderer2.joinken.cn
192.168.10.140 orderer3.joinken.cn
192.168.10.140 peer0.org1.joinken.cn
192.168.10.140 peer0.org2.joinken.cn
192.168.10.140 peer0.org3.joinken.cn
192.168.10.140 peer0.org4.joinken.cn

/////kafka broker0
192.168.10.140 broker0.joinken.cn
192.168.10.140 broker1.joinken.cn
192.168.10.140 broker2.joinken.cn
192.168.10.140 broker3.joinken.cn



ping peer0.org4.joinken.cn

docker comp

cd /opt/fabric/manager/docker-config
export IMAGE_TAG="2.0.0"
export COMPOSE_PROJECT_NAME="tttx"

CORE_PEER_ADDRESSAUTODETECT=true

docker-compose -f ./docker-compose-base.yaml start | up
docker-compose -f docker-compose-cli.yaml start | up

用户


//用户1
cd /opt/fabric/manager/peer/Admin@org1.joinken.cn
export  FABRIC_CFG_PATH=$PWD
export CORE_PEER_ADDRESS=peer0.org1.joinken.cn:7061
peer channel list 

//用户2
cd /opt/fabric/manager/peer/Admin@org2.joinken.cn
export FABRIC_CFG_PATH=$PWD
export CORE_PEER_ADDRESS=peer0.org2.joinken.cn:8051

//用户3
cd /opt/fabric/manager/peer/Admin@org3.joinken.cn
export FABRIC_CFG_PATH=$PWD
export CORE_PEER_ADDRESS=peer0.org3.joinken.cn:9051

//用户4
cd /opt/fabric/manager/peer/Admin@org4.joinken.cn
export FABRIC_CFG_PATH=$PWD
export CORE_PEER_ADDRESS=peer0.org4.joinken.cn:10051
$FABRIC_CFG_PATH

频道(channel)

///频道 channel

`configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./joinken.tx -channelID joinken`
 
Org MSPanchors
 
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org1MSPanchors.tx -channelID joinken -asOrg Org1MSP
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org2MSPanchors.tx -channelID joinken -asOrg Org2MSP
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org3MSPanchors.tx -channelID joinken -asOrg Org3MSP
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org4MSPanchors.tx -channelID joinken -asOrg Org4MSP
 
export CHANNEL_NAME=joinken
 
//创建
peer channel create -o orderer0.joinken.cn:7050 -c ${CHANNEL_NAME} -f ./joinken.tx --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
 
//第一个
//peer channel join -b joinken.block -o orderer0.joinken.cn:7050  --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
//其他join
peer channel join -b /opt/fabric/manager/peer/Admin@org1.joinken.cn/joinken.block -o orderer.joinken.cn:7050  --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
////更新
//peer channel update -c joinken -f ./joinken.tx -o orderer.joinken.cn:7050  --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
 
 
 
//锚点, 一定要全部加入了...
peer channel update -o orderer.joinken.cn:7050 -c joinken -f /opt/fabric/manager/Org1MSPanchors.tx --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
 
peer channel update -o orderer.joinken.cn:7050 -c joinken -f /opt/fabric/manager/Org2MSPanchors.tx --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
 
peer channel update -o orderer.joinken.cn:7050 -c joinken -f /opt/fabric/manager/Org3MSPanchors.tx --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
 
peer channel update -o orderer.joinken.cn:7050 -c joinken -f /opt/fabric/manager/Org4MSPanchors.tx --tls true --cafile /opt/fabric/manager/orderer/tlsca.joinken.cn-cert.pem
 

链码(chaincode)

//查询安装
peer lifecycle chaincode queryinstalled
 
CC_PACKAGE_ID=gxcoc:a66d786b63bd18ccfa4bd0e553a02580493c8e11c8b83c77dea68176994bf6b2
SEQUEUE=1
VERSION=1.0
CHANNEL_ID=jk
NAME=test
 
//批准
peer lifecycle chaincode approveformyorg --channelID $CHANNEL_ID  --name test --version $VERSION --init-required --package-id $CC_PACKAGE_ID --sequence $SEQUEUE  -o orderer.joinken.cn:7050  --tls true --cafile tlsca.joinken.cn-cert.pem
 
 
//查询提交
peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_ID --name test --version $VERSION --init-required --sequence $SEQUEUE --tls true --cafile tlsca.joinken.cn-cert.pem --output json 
 
//提交
peer lifecycle chaincode commit  -o orderer.joinken.cn:7050 --channelID $CHANNEL_ID --name test --version $VERSION --sequence $SEQUEUE --init-required  --tls --cafile tlsca.joinken.cn-cert.pem --peerAddresses peer0.org1.joinken.cn:7051 --tlsRootCertFiles /opt/deploy2/back/config/organizations/peerOrganizations/org1.joinken.cn/peers/peer0.org1.joinken.cn/tls/ca.crt --peerAddresses peer0.org2.joinken.cn:8051 --tlsRootCertFiles /opt/deploy2/back/config/organizations/peerOrganizations/org2.joinken.cn/peers/peer0.org2.joinken.cn/tls/ca.crt 
 
 
//调用初始化 
peer chaincode invoke commit --channelID $CHANNEL_ID  --name test  -o orderer.joinken.cn:7050 --tls --cafile tlsca.joinken.cn-cert.pem --peerAddresses peer0.org1.joinken.cn:7051 --tlsRootCertFiles /opt/deploy2/back/config/organizations/peerOrganizations/org1.joinken.cn/peers/peer0.org1.joinken.cn/tls/ca.crt --peerAddresses peer0.org2.joinken.cn:8051 --tlsRootCertFiles /opt/deploy2/back/config/organizations/peerOrganizations/org2.joinken.cn/peers/peer0.org2.joinken.cn/tls/ca.crt --isInit -c '{"function":"initLedger","Args":[]}'
 
 
//调函数
peer chaincode query -C $CHANNEL_ID -n test -c '{"function":"queryRecordByID","Args":["0xff"]}'
 

kafka

cd /opt/kafka_2.11-1.0.0

**zookeeper**
//端口 2181
bin/zookeeper-server-start.sh -daemon /opt/fabric/manager/kafka/zookeeper.properties



**broker**
bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server0.properties 
bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server1.properties 
bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server2.properties 
bin/kafka-server-start.sh -daemon /opt/fabric/manager/kafka/server3.properties 


192.168.10.140 broker0.joinken.cn //端口9090
192.168.10.140 broker1.joinken.cn
192.168.10.140 broker2.joinken.cn
192.168.10.140 broker3.joinken.cn

可优化TPS的地方

https://stackoverflow.com/questions/54702112/hyperledger-tps 官方实测报告