fabric 联盟链网络简介

创作人 Leo


编辑时间 Sun Mar 14,2021 at 14:35


简介

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

github 地址
安装文档

角色

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

安装

官方 demo 网络示例说明

说明:

  • 推荐使用 centos 虚拟机学习
  • 首先需要安装 docker 并登录 docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
usermod -aG docker root
systemctl start docker
docker login
  • 安装 docker compose
curl -L "https://github.com/docker/compose/releases/download/1.28.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
  • 安装 golang 环境,并设置好 $GOPATH 和 $GOROOT
  • 安装 nodejs 环境,从官网下载二进制包配置 bin 目录到 $PATH 即可,安装后建议把库切到国内的镜像库
npm install -g cnpm --registry=https://registry.npm.taobao.org
  • 安装 gcc
yum -y install gcc*
  • 安装 fabric 镜像,和 fabric-sample 镜像,安装时候可以指定版本,这里使用 1.4.8,这个版本需要保证 fabric 和 fabric-sample 两个 github 仓库都有对应的 tag
wget https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh
sh bootstrap.sh 1.4.8
  • 安装 demo 区块链网络;展示一下初始化 byfn.sh 脚本的功能
[root@localhost first-network]# sh byfn.sh -h
Usage: 
  byfn.sh <mode> [-c <channel name>] [-t <timeout>] [-d <delay>] [-f <docker-compose-file>] [-s <dbtype>] [-l <language>] [-o <consensus-type>] [-i <imagetag>] [-a] [-n] [-v]
    <mode> - one of 'up', 'down', 'restart', 'generate' or 'upgrade'
      - 'up' - bring up the network with docker-compose up
      - 'down' - clear the network with docker-compose down
      - 'restart' - restart the network
      - 'generate' - generate required certificates and genesis block
      - 'upgrade'  - upgrade the network from version 1.3.x to 1.4.0
    -c <channel name> - channel name to use (defaults to "mychannel")
    -t <timeout> - CLI timeout duration in seconds (defaults to 10)
    -d <delay> - delay duration in seconds (defaults to 3)
    -f <docker-compose-file> - specify which docker-compose file use (defaults to docker-compose-cli.yaml)
    -s <dbtype> - the database backend to use: goleveldb (default) or couchdb
    -l <language> - the chaincode language: golang (default) or node
    -o <consensus-type> - the consensus-type of the ordering service: solo (default), kafka, or etcdraft
    -i <imagetag> - the tag to be used to launch the network (defaults to "latest")
    -a - launch certificate authorities (no certificate authorities are launched by default)
    -n - do not deploy chaincode (abstore chaincode is deployed by default)
    -v - verbose mode
  byfn.sh -h (print this message)

Typically, one would first generate the required certificates and 
genesis block, then bring up the network. e.g.:

  byfn.sh generate -c mychannel
  byfn.sh up -c mychannel -s couchdb
        byfn.sh up -c mychannel -s couchdb -i 1.4.0
  byfn.sh up -l node
  byfn.sh down -c mychannel
        byfn.sh upgrade -c mychannel

Taking all defaults:
  byfn.sh generate
  byfn.sh up
  byfn.sh down
  • 区块链网络需要证书和秘钥对,使用 generate 命令生成证书和秘钥对;up 命令开启网络;down 命令可以终止区块链网络
cd /data/boot_fabric/fabric-samples/first-network/
sh byfn.sh generate
sh byfn.sh up

用例测试

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

官方文档

执行 chaincode 让数据上链

开启一个应用,指定特定 chaincode

  • 进入测试项目目录
cd /data/boot_fabric/fabric-samples/fabcar
  • 执行start脚本开启一套本地测试环境,go 代表使用 go 语言版本的 chaincode
./startFabric.sh go
  • 该脚本执行了 167 秒,执行完成会有一个接下来怎么做的提示
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
522d2c37a027   dev-peer0.org2.example.com-fabcar-1.0-264b0a1cb5efbecaac5cf8990339c24474dc8435c6e10f10f2be565d555d0e94   "chaincode -peer.add…"   3 hours ago   Up 3 hours                                                dev-peer0.org2.example.com-fabcar-1.0
c410c471db90   dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba   "chaincode -peer.add…"   3 hours ago   Up 3 hours                                                dev-peer0.org1.example.com-fabcar-1.0
8605f6de6c1c   hyperledger/fabric-tools:latest                                                                          "/bin/bash"              3 hours ago   Up 3 hours                                                cli
66a92bef047f   hyperledger/fabric-peer:latest                                                                           "peer node start"        3 hours ago   Up 3 hours   0.0.0.0:8051->8051/tcp                       peer1.org1.example.com
473608e2ea47   hyperledger/fabric-peer:latest                                                                           "peer node start"        3 hours ago   Up 3 hours   0.0.0.0:7051->7051/tcp                       peer0.org1.example.com
cbe28b66672a   hyperledger/fabric-peer:latest                                                                           "peer node start"        3 hours ago   Up 3 hours   0.0.0.0:9051->9051/tcp                       peer0.org2.example.com
8897dc6aec25   hyperledger/fabric-peer:latest                                                                           "peer node start"        3 hours ago   Up 3 hours   0.0.0.0:10051->10051/tcp                     peer1.org2.example.com
8645ea355f2d   hyperledger/fabric-couchdb                                                                               "tini -- /docker-ent…"   3 hours ago   Up 3 hours   4369/tcp, 9100/tcp, 0.0.0.0:5984->5984/tcp   couchdb0
8487f9d791a7   hyperledger/fabric-couchdb                                                                               "tini -- /docker-ent…"   3 hours ago   Up 3 hours   4369/tcp, 9100/tcp, 0.0.0.0:7984->5984/tcp   couchdb2
430362527d62   hyperledger/fabric-couchdb                                                                               "tini -- /docker-ent…"   3 hours ago   Up 3 hours   4369/tcp, 9100/tcp, 0.0.0.0:8984->5984/tcp   couchdb3
5b12654c2f99   hyperledger/fabric-couchdb                                                                               "tini -- /docker-ent…"   3 hours ago   Up 3 hours   4369/tcp, 9100/tcp, 0.0.0.0:6984->5984/tcp   couchdb1
0f792031bd85   hyperledger/fabric-ca:latest                                                                             "sh -c 'fabric-ca-se…"   3 hours ago   Up 3 hours   0.0.0.0:7054->7054/tcp                       ca_peerOrg1
587f2fe6fdc5   hyperledger/fabric-ca:latest                                                                             "sh -c 'fabric-ca-se…"   3 hours ago   Up 3 hours   7054/tcp, 0.0.0.0:8054->8054/tcp             ca_peerOrg2
d323194e96e1   hyperledger/fabric-orderer:latest                                                                        "orderer"                3 hours ago   Up 3 hours   0.0.0.0:7050->7050/tcp                       orderer.example.com
[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
  • fabric-ca-client
  • fabric-ca-server

进入容器 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]# 

阅读:715
搜索
  • Linux 高性能网络编程库 Libevent 简介和示例 2557
  • Mac系统编译PHP7【20190929更新】 2290
  • zksync 和 layer2 2190
  • web rtc 学习笔记(一) 2172
  • Hadoop 高可用集群搭建 (Hadoop HA) 2160
  • Hadoop Map Reduce 案例:好友推荐 2104
  • react 学习笔记(一) 2065
  • Linux 常用命令 2056
  • 小白鼠问题 2040
  • 安徽黄山游 2037
简介
不定期分享软件开发经验,生活经验