fabric 联盟链网络简介

创作人 Leo


编辑时间 Wed May 20,2020 at 17:42


简介

fabric 是 Apache 基金会下 HyperLedger (超级账本)区块链项目的区块链网络子项目,它是联盟网络 同以太坊,它支持DAPP编写,它称智能合约为 chaincode
不同于以太坊,fabric 的合约可以用包括 go 、 js 在内的多种编程语言编写
本文主要通过官方的汽车车型数据上链用例,阐述 fabric 各节点的功能以及业务流转

github 地址
安装文档

角色

Peer nodes
执行chaincode合约,访问账本数据,背书交易并称为应用程序的接口
Orderer nodes
负责确保此区块链的一致性并传达被背书的交易给网络中的同伴们
MSP服务
证书权威(Certificate Authority)管理X.509证书用于验证成员身份以及角色

安装

安装fabric必要工具 docker,可执行文件和官方用例
linux 安装指定版本 docker
阿里云 docker 镜像

fabric 需要17.06 以上

MacOS 安装,需要提前安装 XCode
1. 确保安装的 xcode 在 /Applications 下,而不是 /Users/{user}/Applications 2. 指定 xcode-select 到 xcode 开发者工具目录 sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

确定 xcode 目录正确,/Applications/Xcode.app/Contents/Developer

macos 直接下载docker客户端并登录
linux 安装 docker

yum install -y --setopt=obsoletes=0 \
   docker-ce-17.06.2.ce-1.el7.centos.x86_64 

linux 安装 docker-composer
docker-composer 是 python 开发的,需要先安装 pip

进行安装compose 第一条语句报错执行第二条,执行成功则跳过第二条
> pip install docker-compose
> pip install docker-compose –ignore-installed requests
> docker-compose -version

安装 go 1.13.x
yum -y install wget
wget https://dl.google.com/go/go1.13.6.linux-amd64.tar.gz

安装 fabric 1.4
从 github 直接下载编译好的包即可
点击进入 releases 页面

用例测试

官网的区块链汽车数据库用例,目标是通过网络组建、车型上链,车型查询(CRUD)操作,让用户快速理解 fabric 是如何工作的

官方文档
首先根据官方文档,将 docker fabric node go docker-compose gcc 安装好 测试机选择 centos 7
这里推荐使用 fabric 1.4.x 稳定版,fabric-samples 也使用 1.4.x
node 使用 v10.15.3
go 使用 1.13.6
js 的 demo 依赖 gcc yum -y install gcc*

注意版本匹配,1.4.x 需要使用老版本的 samples

执行 chaincode 让数据上链

  1. 启动 docker systemctl start docker
  2. 登录 docker hub docker login
  3. 下载官方测试仓库 git clone https://github.com/hyperledger/fabric-samples.git
  4. 进入 first-network 用例 cd fabric-samples/first-network
  5. 下载依赖包以及配置文件 sh ./byfn.sh down

开启一个应用,指定特定 chaincode
官方文档用 JavaScript 版本,本地没有测通
我是使用了 Go 版本 chaincode 测通的,这里推荐使用 Go 版本

  1. 进入测试项目目录 cd /data/fabric-samples/fabcar
  2. 执行start脚本开启一套本地测试环境 ./startFabric.sh go
  3. 该脚本执行了 167 秒,执行完成会有一个接下来怎么做的提示

console 输出

2020-01-13 08:09:14.397 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2020-01-13 08:09:14.397 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
...
+ set +x

Total setup execution time : 167 secs ...

Next, use the FabCar applications to interact with the deployed FabCar contract.
The FabCar applications are available in multiple programming languages.
Follow the instructions for the programming language of your choice:

...

解释一下:这个 startFabric 主要就是执行了 chaincode ,该 go 文件在 fabric-samples/chaincode/fabcar/go/fabcar.go
在它执行的同时,我们可以执行 docker ps 看一看,会打印出启动的各个节点

[root@localhost fabcar]# docker ps
CONTAINER ID        IMAGE                                                                                                    COMMAND                  CREATED             STATUS              PORTS                                        NAMES
de44d7a12d1b        dev-peer0.org2.example.com-fabcar-1.0-264b0a1cb5efbecaac5cf8990339c24474dc8435c6e10f10f2be565d555d0e94   "chaincode -peer.a..."   22 minutes ago      Up 22 minutes                                                    dev-peer0.org2.example.com-fabcar-1.0
a11c4de0d692        dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba   "chaincode -peer.a..."   23 minutes ago      Up 23 minutes                                                    dev-peer0.org1.example.com-fabcar-1.0
a58c936cdd84        hyperledger/fabric-tools:latest                                                                          "/bin/bash"              24 minutes ago      Up 24 minutes                                                    cli
99474175e3a3        hyperledger/fabric-peer:latest                                                                           "peer node start"        24 minutes ago      Up 24 minutes       0.0.0.0:9051->9051/tcp                       peer0.org2.example.com
4b20aaf981ee        hyperledger/fabric-peer:latest                                                                           "peer node start"        24 minutes ago      Up 24 minutes       0.0.0.0:7051->7051/tcp                       peer0.org1.example.com
dc6937e09711        hyperledger/fabric-peer:latest                                                                           "peer node start"        24 minutes ago      Up 24 minutes       0.0.0.0:10051->10051/tcp                     peer1.org2.example.com
3ef7601ffb9f        hyperledger/fabric-peer:latest                                                                           "peer node start"        24 minutes ago      Up 24 minutes       0.0.0.0:8051->8051/tcp                       peer1.org1.example.com
a089b8950f0d        hyperledger/fabric-couchdb                                                                               "tini -- /docker-e..."   24 minutes ago      Up 24 minutes       4369/tcp, 9100/tcp, 0.0.0.0:7984->5984/tcp   couchdb2
c09285ce7bf9        hyperledger/fabric-orderer:latest                                                                        "orderer"                24 minutes ago      Up 24 minutes       0.0.0.0:7050->7050/tcp                       orderer.example.com
1b0631621479        hyperledger/fabric-couchdb                                                                               "tini -- /docker-e..."   24 minutes ago      Up 24 minutes       4369/tcp, 9100/tcp, 0.0.0.0:6984->5984/tcp   couchdb1
1ab630908b57        hyperledger/fabric-ca:latest                                                                             "sh -c 'fabric-ca-..."   24 minutes ago      Up 24 minutes       7054/tcp, 0.0.0.0:8054->8054/tcp             ca_peerOrg2
db47faf8b9f6        hyperledger/fabric-ca:latest                                                                             "sh -c 'fabric-ca-..."   24 minutes ago      Up 24 minutes       0.0.0.0:7054->7054/tcp                       ca_peerOrg1
fc5f18d556c3        hyperledger/fabric-couchdb                                                                               "tini -- /docker-e..."   24 minutes ago      Up 24 minutes       4369/tcp, 9100/tcp, 0.0.0.0:5984->5984/tcp   couchdb0
5e10bec36c7d        hyperledger/fabric-couchdb                                                                               "tini -- /docker-e..."   24 minutes ago      Up 24 minutes       4369/tcp, 9100/tcp, 0.0.0.0:8984->5984/tcp   couchdb3
[root@localhost fabcar]# 

很多,分为数据节点 couchdb,认证节点 ca,共识节点 peer;在实际生产环境,这些节点都分布在世界各地,通过共识机制相互同步构成一个区块链网络
startFabric 将一批汽车数据通过 API 提交到这个区块链网络,供我们后续查询

查询车型

查询项目我们选择 js 版本
国内使用 npm 比较慢,推荐 cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org

安装项目
1. 进入到 js 查询项目目录,cd /data/fabric-samples/fabcar/javascript
2. 如果没有安装 gcc 需要安装 yum -y install gcc*
3. 安装项目 cnpm install --unsafe-perm

执行

  1. 保存 CA 证书到 wallet 文件夹
    node enrollAdmin
  2. 类似 admin ,保存一个 user 的证书
    node registerUser
  3. 提交一个汽车信息到账本
    node invoke
  4. 查询所有车型信息
    node query

示例:

[root@localhost javascript]# rm -rf wallet/*
[root@localhost javascript]# node enrollAdmin
Wallet path: /data/fabric-samples/fabcar/javascript/wallet
Successfully enrolled admin user "admin" and imported it into the wallet
[root@localhost javascript]# node registerUser
Wallet path: /data/fabric-samples/fabcar/javascript/wallet
Successfully registered and enrolled admin user "user1" and imported it into the wallet
[root@localhost javascript]# node invoke
Wallet path: /data/fabric-samples/fabcar/javascript/wallet
Transaction has been submitted
[root@localhost javascript]# node query
Wallet path: /data/fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is: [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},{"Key":"CAR12", "Record":{"colour":"Black","make":"Honda","model":"Accord","owner":"Tom"}},{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]

注意:

  1. 防止错误,最好先删除 wallet 下已生成的数据
  2. 如果执行不成功,可能是node版本问题,目前成功测试版本 node v10.15.3 npm 6.4.1
  3. 安装环境过程中使用npm或者docker下载包可能会用到命令行代理,这里最好关闭代理,否则可能造成网络错误

PaperNet 网络

本小结通过官方业务方案 PaperNet 阐述区块链网络在实际生产生活中存在的意义,以及应用场景
PaperNet 是一个基于Fabric区块链网络的票据管理系统,issue 方通过在网络售卖 paper 的方式获取贷款,多方资金公司可以参与购买和再出售;待 issue 方到期必须赎回 paper;出售和赎回之前会有一个差价,所以资金公司可以赚钱
角色:
1. MagnetoCorp 电机公司,生产电机的工厂公司,需要发布 issue 进行贷款(操作:issue,redeem)
2. BrokerHouse/DigiBank/HedgeMatic/BigFund 放贷的资金公司,互相之间是竞争关系,他们之间会进行再出售和购买(操作 buy,sell)
3. RateM 风险评估公司,资金方会根据 RateM 的评估情况,对 paper 进行估价

业务流转:
MagnetoCorp 需要为 Moto D 汽车公司生产一批电机,需要雇佣一批工人 6 个月时间,Moto D 会在完工后支付给 MC 公司 8M 美金,在这期间 MC 需要向资金公司进行贷款以购买原材料和雇佣工人

MC 在 PaperNet 发布了价值 5M 美金的 paper 00001

Issuer = MagnetoCorp
Paper = 00001
Owner = MagnetoCorp
Issue date = 31 May 2020
Maturity = 30 November 2020
Face value = 5M USD
Current state = issued

DigiBank 公司购买了这个 paper ,注意看 Owner 和 state 的变化

Issuer = MagnetoCorp
Paper = 00001
Owner = DigiBank
Issue date = 31 May 2020
Maturity date = 30 November 2020
Face value = 5M USD
Current state = trading

MagnetoCorp 在 2020年11月30日 支付 5M 美金赎回了该 paper
至此整个 issue 结束了

Issuer = MagnetoCorp
Paper = 00001
Owner = MagnetoCorp
Issue date = 31 May 2020
Maturity date = 30 November 2020
Face value = 5M USD
Current state = redeemed

trading 是一个持续的状态,在 redeemed 之前它将会一直处于 trading 中,在这期间内可能有其他公司对这个 paper 进行再次交易
下面看一下交易事务

Issue:提交一个新的 Paper

Txn = issue
Issuer = MagnetoCorp
Paper = 00001
Issue time = 31 May 2020 09:00:00 EST
Maturity date = 30 November 2020
Face value = 5M USD

Buy:购买 Paper

Txn = buy
Issuer = MagnetoCorp
Paper = 00001
Current owner = MagnetoCorp
New owner = DigiBank
Purchase time = 31 May 2020 10:00:00 EST
Price = 4.94M USD

也许向其他公司转卖该 Paper

Txn = buy
Issuer = MagnetoCorp
Paper = 00001
Current owner = DigiBank
New owner = BigFund
Purchase time = 2 June 2020 12:20:00 EST
Price = 4.93M USD

Redeem:到期赎回该 Paper

Txn = redeem
Issuer = MagnetoCorp
Paper = 00001
Current owner = HedgeMatic
Redeem time = 30 Nov 2020 12:00:00 EST

账本

Fabric 是一个基于区块链概念的分布式账本:一种世界状态(包含了区块链网络中所有物体的当前状态),区块链化(详细记录了整个交易的历史状态)的账本
Fabric 网络要求所有交易必须进行签名

生命周期

PaperNet 中一个 Issue 会经历 Issued->多个trading->最终状态redeemed

账本状态

以一个 Paper 在 PaperNet 中为例,Fabric 通过一条 Paper 的全部信息为该 Paper 生成一个状态 state,所有的 Paper 的状态集合构成账本的世界状态 world state,你可以从 world state 中直接获取某 Paper 的最新属性值,而不需要从初始状态一个一个计算

状态关键字

State keys

key 顾名思义关键字,用来表示一条记录的唯一性
PaperNet 通过 Issuer 和 Paper 字段为记录生成 key:MagnetoCorp00001

交互

这个小结主要讲解如何部署 chaincode,并与本地开发网络进行交互

fabric 命令
configtxgen configtxlator cryptogen discover idemixgen orderer peer

进入容器 sh 交互
docker exec -it peer0.org2.example.com /bin/bash

进入 cli 容器 sh 交互
docker exec -it cli bash

cli 是 fabric 本地开发网络中的代理容器
用户通过 cli 中转与其他 order peer 等容器交互
用户的 chaincode 保存在 cli 容器中
通过 docker-compose-cli.yaml 我们了解到 docker 将 几个本地目录映射到了容器中,包括 chaincode

  cli:
    container_name: cli
    image: hyperledger/fabric-tools:$IMAGE_TAG
    tty: true
    stdin_open: true
    environment:
      - SYS_CHANNEL=$SYS_CHANNEL
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      #- FABRIC_LOGGING_SPEC=DEBUG
      - FABRIC_LOGGING_SPEC=INFO
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.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/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
    depends_on:
      - orderer.example.com
      - peer0.org1.example.com
      - peer1.org1.example.com
      - peer0.org2.example.com
      - peer1.org2.example.com
    networks:
      - byfn

其中 volumes 节代表将本地目录挂载到容器目录,挂载后两边是同步的

docker compose 文档

例:游戏装备上链

以一个简单的例子讲述如何开发简单的区块链应用
1. 需求:游戏装备上链 2. 以官方 sample , first-network 为开发网络,如果没有环境,请按照前面的步骤搭建好 first-network 网络,推荐使用 centos7 搭建 3. 开发 chaincode 4. chaincode 上链,接口调用

编写 chaincode

chaincode 需要实现 Chaincode 接口,我们先来看一下接口描述

// Chaincode interface must be implemented by all chaincodes. The fabric runs
// the transactions by calling these functions as specified.
type Chaincode interface {
	// Init is called during Instantiate transaction after the chaincode container
	// has been established for the first time, allowing the chaincode to
	// initialize its internal data
	Init(stub ChaincodeStubInterface) pb.Response

	// Invoke is called to update or query the ledger in a proposal transaction.
	// Updated state variables are not committed to the ledger until the
	// transaction is committed.
	Invoke(stub ChaincodeStubInterface) pb.Response
}

下面看一下一个简单的 chaincode (go 语言版)

package main

import (
	"encoding/json"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	sc "github.com/hyperledger/fabric/protos/peer"
)

type SmartContract struct {
}

type Equipment struct {
	Hp   string `json:"hp"`
	Sp   string `json:"sp"`
	Move string `json:"move"`
}

func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
	return shim.Success(nil)
}

func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {

	// Retrieve the requested Smart Contract function and arguments
	function, args := APIstub.GetFunctionAndParameters() 
	// Route to the appropriate handler function to interact with the ledger appropriately
	if function == "query" {
		return s.query(APIstub, args)
	} else if function == "addeq" {
		return s.addeq(APIstub, args)
	}

	return shim.Error("Invalid Smart Contract function name.")
}

func (s *SmartContract) query(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) < 1 {
		return shim.Error("incorrect args num")
	}

	eqAsBytes, err := APIstub.GetState(args[0])
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(eqAsBytes)
}

func (s *SmartContract) addeq(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) < 4 {
		return shim.Error("incorrect args num")
	}
	eq := &Equipment{args[1], args[2], args[3]}
	eqAsBytes, err := json.Marshal(eq)

	if err != nil {
		return shim.Error(err.Error())
	}

	APIstub.PutState(args[0], eqAsBytes)
	return shim.Success(nil)
}

func main() {

	// Create a new Smart Contract
	err := shim.Start(new(SmartContract))
	if err != nil {
		fmt.Printf("Error creating new Smart Contract: %s", err)
	}
}

编辑后保存为 chaincode/red-sword-proj/go/main.go;chaincode 就是下载的 fabric-sample 中的 chaincode目录,也是挂载到 cli 容器 /opt/gopath/src/github.com/chaincode 的目录
一个 chaincode 就是一个特殊的 go 语言程序,也必须有 main 主入口程序
shim包是 fabric 提供的用来访问 state、transaction、和调用其他接口的 API
不推荐在 Init 接口写复杂的初始化逻辑,我们可以把全部的逻辑以路由的方式统一通过 Invoke 提供

chaincode 编写完,我们将它上链

在 first-network 测试网络,我们有两个 peer 和一个 order ,我们需要在这三台机器部署这个 chaincode

安装链码到 peer节点 peer0.org1.example.com

docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_ADDRESS=peer0.org1.example.com:7051 \
  -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
  -e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
  cli \
  peer chaincode install \
    -n rsp3 \
    -v 1.0 \
    -p github.com/chaincode/red-sword-proj/go \
    -l golang

github.com/chaincode/red-sword-proj/go 基于 $GOPATH/src 的go语言程序包位置,就像我们平时执行 go install 指定的路径

安装链码到 peer节点 peer0.org2.example.com

docker exec \
  -e CORE_PEER_LOCALMSPID=Org2MSP \
  -e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 \
  -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp \
  -e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \
  cli \
  peer chaincode install \
    -n rsp3 \
    -v 1.0 \
    -p github.com/chaincode/red-sword-proj/go \
    -l golang

安装链码到对应 channel
channel 在 fabric 网络中扮演着一个重要角色,大家可以理解为一个多方通信的管道,各个节点之间通讯,必须通过这个管道

docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
  cli \
  peer chaincode instantiate \
    -o orderer.example.com:7050 \
    -C mychannel \
    -n rsp3 \
    -l golang \
    -v 1.0 \
    -c '{"Args":[]}' \
    -P "AND('Org1MSP.member','Org2MSP.member')" \
    --tls \
    --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
    --peerAddresses peer0.org1.example.com:7051 \
    --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

通过命令行调用 chaincode

我们首先通过调用 addeq 插入一条装备信息

docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
  cli \
  peer chaincode invoke \
    -o orderer.example.com:7050 \
    -C mychannel \
    -n rsp3 \
    -c '{"function":"addeq","Args":["ReadSword", "10", "20", "4"]}' \
    --waitForEvent \
    --tls \
    --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
    --peerAddresses peer0.org1.example.com:7051 \
    --peerAddresses peer0.org2.example.com:9051 \
    --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
    --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

命令行提示

[root@bogon go]# docker exec \
>   -e CORE_PEER_LOCALMSPID=Org1MSP \
>   -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
>   cli \
>   peer chaincode invoke \
>     -o orderer.example.com:7050 \
>     -C mychannel \
>     -n rsp3 \
>     -c '{"function":"addeq","Args":["ReadSword", "10", "20", "4"]}' \
>     --waitForEvent \
>     --tls \
>     --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
>     --peerAddresses peer0.org1.example.com:7051 \
>     --peerAddresses peer0.org2.example.com:9051 \
>     --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
>     --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

2020-02-11 15:01:39.042 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [c7ab00a14da0322526cfd44c001b77608159a6fbbce69c2b4a58d6429168be0d] committed with status (VALID) at peer0.org1.example.com:7051
2020-02-11 15:01:39.202 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [c7ab00a14da0322526cfd44c001b77608159a6fbbce69c2b4a58d6429168be0d] committed with status (VALID) at peer0.org2.example.com:9051
2020-02-11 15:01:39.202 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200 

然后,我们通过query获取一条装备信息

docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
  cli \
  peer chaincode invoke \
    -o orderer.example.com:7050 \
    -C mychannel \
    -n rsp3 \
    -c '{"function":"query","Args":["ReadSword"]}' \
    --waitForEvent \
    --tls \
    --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
    --peerAddresses peer0.org1.example.com:7051 \
    --peerAddresses peer0.org2.example.com:9051 \
    --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
    --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

命令行

[root@bogon go]# docker exec \
>   -e CORE_PEER_LOCALMSPID=Org1MSP \
>   -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
>   cli \
>   peer chaincode invoke \
>     -o orderer.example.com:7050 \
>     -C mychannel \
>     -n rsp3 \
>     -c '{"function":"query","Args":["ReadSword"]}' \
>     --waitForEvent \
>     --tls \
>     --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
>     --peerAddresses peer0.org1.example.com:7051 \
>     --peerAddresses peer0.org2.example.com:9051 \
>     --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
>     --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
2020-02-11 15:02:11.847 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [e8cf5b1f31af9052484b6aba1db025bffcbb6d466936f254855b073a4d6c1710] committed with status (VALID) at peer0.org2.example.com:9051
2020-02-11 15:02:11.862 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [e8cf5b1f31af9052484b6aba1db025bffcbb6d466936f254855b073a4d6c1710] committed with status (VALID) at peer0.org1.example.com:7051
2020-02-11 15:02:11.862 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200 payload:"{\"hp\":\"10\",\"move\":\"4\",\"sp\":\"20\"}"

通过程序调用 chaincode

多角色划分
1. 用户需要从 CA 中心申请一个 admin 用户秘钥对,参考 github.com/hyperledger/fabric-samples/fabcar/javascript/enrollAdmin.js
2. 通过 admin 秘钥对,注册 user 用户秘钥对,参考 github.com/hyperledger/fabric-samples/fabcar/javascript/registerUser.js
3. 最终我们通过 user 跟 peer 的 api 接口进行交互

这里我们以 js 为例,对于上述使用命令行的装备信息操作,这里写一个 js 版使用代码调用的示例
如何创建 admin 和 user 用户,参考官方 js 例子,我们也以该目录(github.com/hyperledger/fabric-samples/fabcar/javascript)为程序目录

invokeEq.js 添加装备信息

/*
 * SPDX-License-Identifier: Apache-2.0
 */

'use strict';

const { FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');

async function main() {
    try {

        // 创建一个基于本地文件文件系统的钱包对象,用来做身份验证
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // 检查 user1 用户是否存在,存在的用户,以同名文件夹形式保存公私钥文件
        const userExists = await wallet.exists('user1');
        if (!userExists) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

        // 连接到 peer
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });

        // 指定我们部署合约的 channel
        const network = await gateway.getNetwork('mychannel');

        // 获取合约对象
        const contract = network.getContract('rsp3');

        // 由于我们要修改账本状态(更新或者添加数据),我们必须提交一个事务 submitTransaction
        // addeq 添加一条装备信息 - 需要 4 个字段, 例: ('addeq', 'SilverShield', '5', '25', '3')
        await contract.submitTransaction('addeq', 'SilverShield', '5', '25', '3');
        console.log('Transaction has been submitted');

        // 断开 peer 连接
        await gateway.disconnect();

    } catch (error) {
        console.error(`Failed to submit transaction: ${error}`);
        process.exit(1);
    }
}

main();

queryEq.js 查询装备信息

/*
 * SPDX-License-Identifier: Apache-2.0
 */

'use strict';

const { FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');

async function main() {
    try {

        // 创建一个基于本地文件文件系统的钱包对象,用来做身份验证
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // 检查 user1 用户是否存在,存在的用户,以同名文件夹形式保存公私钥文件
        const userExists = await wallet.exists('user1');
        if (!userExists) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

        // 连接到 peer
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });

        // 指定我们部署合约的 channel
        const network = await gateway.getNetwork('mychannel');

        // 获取合约对象
        const contract = network.getContract('rsp3');

        // 查询状态信息(存储的数据),使用 evaluateTransaction 
        // query 查询一条装备信息 - 需要 1 个参数, 例: ('query', 'SilverShield')
        const result = await contract.evaluateTransaction('query', 'SilverShield');
        console.log(`Transaction has been evaluated, result is: ${result.toString()}`);

    } catch (error) {
        console.error(`Failed to evaluate transaction: ${error}`);
        process.exit(1);
    }
}

main();

命令行

[root@bogon javascript]# node invokeEq.js 
Wallet path: /data/fabric-samples/fabcar/javascript/wallet
Transaction has been submitted
[root@bogon javascript]# node queryEq.js 
Wallet path: /data/fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is: {"hp":"5","move":"3","sp":"25"}
[root@bogon javascript]# 

阅读:58
搜索
  • Linux 高性能网络编程库 Libevent 简介和示例 1899
  • Mac系统编译PHP7【20190929更新】 1765
  • Windows 安装Swoole 1546
  • Hadoop 高可用集群搭建 (Hadoop HA) 1438
  • Hadoop 高可用YARN 配置 1362
  • 小白鼠问题 1290
  • Hadoop Map Reduce 案例:好友推荐 1241
  • 自动化测试工具 Selenium 1103
  • GIT 分支管理 1022
  • Golang 使用 Grpc 968
简介
不定期分享软件开发经验,生活经验