创作人 Leo
编辑时间 Sun Mar 14,2021 at 14:35
fabric 是 Apache 基金会下 HyperLedger (超级账本)区块链项目的区块链网络子项目,它是联盟网络 同以太坊,它支持DAPP编写,它称智能合约为 chaincode
不同于以太坊,fabric 的合约可以用包括 go 、 js 在内的多种编程语言编写
本文主要通过官方的汽车车型数据上链用例,阐述 fabric 各节点的功能以及业务流转
Peer nodes
执行chaincode合约,访问账本数据,背书交易并称为应用程序的接口
Orderer nodes
负责确保此区块链的一致性并传达被背书的交易给网络中的同伴们
MSP服务
证书权威(Certificate Authority)管理X.509证书用于验证成员身份以及角色
说明:
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
usermod -aG docker root
systemctl start docker
docker login
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
npm install -g cnpm --registry=https://registry.npm.taobao.org
yum -y install gcc*
wget https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh
sh bootstrap.sh 1.4.8
[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
cd /data/boot_fabric/fabric-samples/first-network/
sh byfn.sh generate
sh byfn.sh up
官网的区块链汽车数据库用例,目标是通过网络组建、车型上链,车型查询(CRUD)操作,让用户快速理解 fabric 是如何工作的
开启一个应用,指定特定 chaincode
cd /data/boot_fabric/fabric-samples/fabcar
./startFabric.sh go
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
执行
示例:
[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"}}]
注意:
本小结通过官方业务方案 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 命令
进入容器 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 节代表将本地目录挂载到容器目录,挂载后两边是同步的
以一个简单的例子讲述如何开发简单的区块链应用
1. 需求:游戏装备上链
2. 以官方 sample , first-network 为开发网络,如果没有环境,请按照前面的步骤搭建好 first-network 网络,推荐使用 centos7 搭建
3. 开发 chaincode
4. 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
我们首先通过调用 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\"}"
多角色划分
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]#