2019-06-26

青柠果嗄 · · 706 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本文介绍了fabric中的一个示例应用balance-transfer,fabric版本是1.4.1。本文基于上一章hyperledger基础培训-创建第一个fabric网络的基础上,如果还未了解,请先了解上一章节内容。

balance-transfer是什么

balance-transfer是Hyperledger fabric Node SDK的一个示例应用,主要使用了SDK中fabric-client 和 fabric-ca-client 模块中的API,实现了与Fabric网络交互的各种操作。

1. 预装环境

预装环境在上一章节hyperledger基础培训-创建第一个fabric网络已有详细介绍,可跳转至该文章查看。

2. 运行示例程序

balance-transfer可通过脚本运行,构建一个本地的Fabric网络,所有节点包括:

  • 两个CA节点
  • 一个orderer节点
  • 四个peer节点 (每个组织各两个peer节点)

balance-transfer的目录结构如下:

balance-transfer
├── app // 与fabric网络交互的实现
│ ├── create-channel.js // 创建通道
│ ├── helper.js
│ ├── install-chaincode.js // 安装链码
│ ├── instantiate-chaincode.js // 实例化链码
│ ├── invoke-transaction.js // 执行(invoke)链码
│ ├── join-channel.js // 加入通道
│ ├── update-anchor-peers.js // 更新anchor peer节点
│ └── query.js // 查询(query)链码
├── app.js // 定义与fabric网络交互的API
├── artifacts // 启动fabric网络需要的配置文件
│ ├── base.yaml
│ ├── channel
│ ├── docker-compose.yaml
│ ├── network-config-aws.json
│ ├── network-config.json
│ └── src
├── config.js
├── config.json
├── node_modules
│ └── .......
├── package.json
├── package-lock.json
├── README.md
├── runApp.sh // 启动应用程序脚本
├── typescript
└── testAPIs.sh // 测试API脚本

打开终端窗口1,打开上一章节克隆下来的fabric-samples项目,执行cd fabric-samples
执行cd balance-transfer进入balance-transfer文件夹下,
下面介绍两种运行方式:

2.1 方式一

1)终端窗口1,使用docker-compose启动网络

$ docker-compose -f artifacts/docker-compose.yaml up

2)打开终端窗口2,安装fabric-client 和 fabric-ca-client 模块

$ npm install

3)启动应用程序,监听4000端口

$ PORT=4000 node app

4)打开终端窗口3,通过curl命令进行测试。在第三部分会具体介绍。

2.2 方式二

1)终端窗口1,执行shell脚本启动应用程序

$ ./runApp.sh

执行shell脚本之后会:

  • 启动本地fabric网络
  • 下载fabric-client 和 fabric-ca-client 模块
  • 在PORT 4000启动应用程序

2)打开终端窗口2,执行brew install jq安装jq,使shell脚本能正确解析JSON

$ brew install jq

如果出现-bash: brew: command not found,执行/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"安装brew ,brew是包管理工具,可以很方便地进行安装/卸载/更新各种软件包。
安装完成后,如果还是出现-bash: brew: command not found,则需要修改环境变量:
执行vim ~/.bash_profile打开.bash_profile

vim ~/.bash_profile

在文本编辑框编辑:

export PATH=/usr/local/bin:$PATH

编辑之后保存,然后执行source命令,使之生效:

$ source ~/.bash_profile

3)在终端窗口2,执行shell测试脚本
-如果使用golang版本,则执行./testAPIs.sh -l golang命令
-如果使用nodejs版本,则执行./testAPIs.sh -l node命令

测试脚本实现了以下的功能:

  • 注册用户返回token
  • 创建通道以及将节点加入到通道
  • 安装链码和实例化链码
  • invoke链码
  • query(查询)链码

3. REST API请求

下面具体介绍上面测试脚本实现的功能,这边是用curl模拟POST请求进行测试,同样也可以用postman(用于网页调试、发送网页HTTP请求的Chrome插件)进行接口测试。

3.1 登录

示例是在组织Org1上注册新用户Jim

POST请求,包含两个重要参数:

  • username用户名
  • orgName组织名
$ curl -s -X POST http://localhost:4000/users -H "content-type: application/x-www-form-urlencoded" -d 'username=Jim&orgName=Org1'
{"success": true,"secret": "","message": "Jim enrolled Successfully","token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc"}

注意:响应包含成功/失败状态,secret和token[JSON Web令牌(JWT)],Header 授权必须包含这边返回的token。

3.2 创建通道

示例是创建通道mychannel

POST请求,包含两个重要参数:

  • channel名称
  • channel配置文件路径(../artifacts/channel/mychannel.tx)
$ curl -s -X POST \
  http://localhost:4000/channels \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json" \
  -d '{
    "channelName":"mychannel",
    "channelConfigPath":"../artifacts/channel/mychannel.tx"
}'
{"success":true,"message":"Channel 'mychannel' created Successfully"}

注意:这边请求头必需附上第一步用户登录成功的token,authorization: Bearer ${your token}

3.3 节点加入到通道

示例是将org1中的两个peer节点加入通道中

POST请求,需要指定以下参数:

  • channelName 加入到哪个channel
  • peers 把哪些节点加进去
$ curl -s -X POST \
  http://localhost:4000/channels/mychannel/peers \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json" \
  -d '{
    "peers": ["peer0.org1.example.com","peer1.org1.example.com"]
}'
{"success":true,"message":"Successfully joined peers in organization Org1 to the channel:mychannel"}

3.4 安装链码

示例是安装golang/nodejs类型的链码,链码名称为mycc,链码版本号是v0,chaincodePath指定链码路径。

POST请求,使用sdk安装chaincode时需要指定以下参数:

  • chaincode名称
  • chaincode版本
  • chaincode文件路径
  • chaincode类型
  • 目标节点列表
    1)golang版本(chaincodeType: golang)
$ curl -s -X POST \
  http://localhost:4000/chaincodes \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json" \
  -d '{
    "peers": ["peer0.org1.example.com","peer1.org1.example.com"],
    "chaincodeName":"mycc",
    "chaincodePath":"github.com/example_cc/go",
    "chaincodeType": "golang",
    "chaincodeVersion":"v0"
}'
{"success":true,"message":"Successfully install chaincode"}

2)nodejs版本(chaincodeType: node)

$ curl -s -X POST \
  http://localhost:4000/chaincodes \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json" \
  -d '{
    "peers": ["peer0.org1.example.com","peer1.org1.example.com"],
    "chaincodeName":"mycc",
    "chaincodePath":"$PWD/artifacts/src/github.com/example_cc/node",
    "chaincodeType": "node",
    "chaincodeVersion":"v0"
}'
{"success":true,"message":"Successfully install chaincode"}

安装chaincode会根据本地的链码文件生成chaincode镜像。
注意:请求传递的参数chaincodeType表示链码类型(golang还是node)

3.5 实例化链码

示例是在mychannel通道上实例化链码

POST请求,需要指定以下参数:

  • channel名称
  • chaincode名称
  • chaincode版本
  • 实例化要执行的方法(示例没加,默认为Init)
  • 方法参数
$ curl -s -X POST \
  http://localhost:4000/channels/mychannel/chaincodes \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json" \
  -d '{
    "chaincodeName":"mycc",
    "chaincodeVersion":"v0",
    "chaincodeType": "golang",
    "args":["a","100","b","200"]
}'
{"success":true,"message":"Successfully instantiate chaincode in organization Org1 to the channel 'mychannel'"}

这边初始化a,b两个账户,a账户有100,b账户有200,为了后面3.6转账。
实例化chaincode则会启动该镜像,使链码在docker容器中运行。
注意:这边的chaincodeType主要看链码是nodejs版本的链码还是golang版本的链码。

3.6 invoke(执行)链码

链码安装和实例化之后就可以调用chaincode执行交易。

POST请求,需要指定以下参数:

  • channel名称
  • chaincode名称
  • peers执行交易所在的节点列表
  • fcn链码方法名
  • args方法参数
$ curl -s -X POST \
  http://localhost:4000/channels/mychannel/chaincodes/mycc \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json" \
  -d '{
    "peers": ["peer0.org1.example.com","peer0.org2.example.com"],
    "fcn":"move",
    "args":["a","b","10"]
}'
{"success":true,"message":"Successfully invoked the chaincode in organization Org1 to the channel 'mychannel' transacton ID: 7f844a363d8343e25e76ea878ab582ecd82ade09bc7f1541728d33793eab49c8"}

这里实现了简单的转账交易 a->b,调用链码中的move方法,返回transaction id。

3.7 query(查询)链码

balance-transfer 提供了很多查询接口,包括链码查询,根据区块号查询区块数据,根据交易ID查询交易信息,查询链上的区块数,查询已安装或已实例化的链码,查询通道。

1)链码查询

3.6已经成功执行了转账(A->B),下面我们来查询下A账户现在有多少资产

GET请求,需要指定以下参数:

  • channel名称
  • chaincode名称
  • peer节点
  • fcn链码函数名
  • args方法参数
$ curl -s -X GET \
  "http://localhost:4000/channels/mychannel/chaincodes/mycc?peer=peer0.org1.example.com&fcn=query&args=%5B%22a%22%5D" \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json"
a now has 90 after the move

返回a在执行3.6 move方法转账给b之后,还剩90。
2)按区块号查询

根据块号查询区块信息,示例中展示的是org1组织下的peer0节点上第一个区块的信息

GET请求,需要指定以下参数:

  • channel名称
  • 块号
  • peer节点
$ curl -s -X GET \
  "http://localhost:4000/channels/mychannel/blocks/1?peer=peer0.org1.example.com" \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json"

3)按Transaction ID查询

根据交易id查询交易详细信息,这边的Transaction ID可以用3.6返回的Transaction ID

GET请求,需要指定以下参数:

  • channel名称
  • transaction id
  • peer节点
$ curl -s -X GET http://localhost:4000/channels/mychannel/transactions/7f844a363d8343e25e76ea878ab582ecd82ade09bc7f1541728d33793eab49c8?peer=peer0.org1.example.com \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json"

4) 查询链信息

查询链信息,示例是查询通道mychannel的链信息。

GET请求,需要指定以下参数:

  • channel名称
  • peer节点
$ curl -s -X GET \
  "http://localhost:4000/channels/mychannel?peer=peer0.org1.example.com" \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json"
{"height":{"low":5,"high":0,"unsigned":true},"currentBlockHash":{"buffer":{"type":"Buffer","data":[8,5,18,32,163,72,1,73,191,44,71,104,60,120,71,152,13,14,242,156,232,50,172,247,192,249,112,117,234,155,227,173,200,112,22,106,26,32,201,184,203,75,83,121,30,9,79,164,131,36,71,98,59,141,69,146,166,48,193,40,165,79,80,246,193,202,86,14,169,43]},"offset":4,"markedOffset":-1,"limit":36,"littleEndian":true,"noAssert":false},"previousBlockHash":{"buffer":{"type":"Buffer","data":[8,5,18,32,163,72,1,73,191,44,71,104,60,120,71,152,13,14,242,156,232,50,172,247,192,249,112,117,234,155,227,173,200,112,22,106,26,32,201,184,203,75,83,121,30,9,79,164,131,36,71,98,59,141,69,146,166,48,193,40,165,79,80,246,193,202,86,14,169,43]},"offset":38,"markedOffset":-1,"limit":70,"littleEndian":true,"noAssert":false}}

5) 查询已安装的链码

示例是org1下的peer0节点已经安装的链码信息。

GET请求,需要指定以下参数:

  • peer节点
  • 链码类型(installed)
$ curl -s -X GET \
  "http://localhost:4000/chaincodes?peer=peer0.org1.example.com&type=installed" \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json"
["name: mycc, version: v0, path: github.com/example_cc/go"]

返回org1下的peer1节点已经安装的链码信息:链码名称,链码版本号,链码路径。
6) 查询实例化的链码(跟5)类似,type不同)

示例是查询org1的peer0节点已经实例化的链码信息

GET请求,需要指定以下参数:

  • peer节点
  • 链码类型(instantiated)
$ curl -s -X GET \
  "http://localhost:4000/chaincodes?peer=peer0.org1.example.com&type=instantiated" \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json"
["name: mycc, version: v0, path: github.com/example_cc/go"]

返回org1下的peer1节点已经实例化的链码信息:链码名称,链码版本号,链码路径。
7)查询通道

查询组织org1的peer0节点加入的channel

GET请求,需要指定的参数如下:

  • peer节点
$ curl -s -X GET \
  "http://localhost:4000/channels?peer=peer0.org1.example.com" \
  -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE0ODI4NjcsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1NjE0NDY4Njd9.V8LPaLY-bnD_h6788FMqnYkCfxZFNDmiiMqQuMFhfAc" \
  -H "content-type: application/json"
{"channels":[{"channel_id":"mychannel"}]}

返回组织org1的peer0节点加入的channel列表

4 清理网络

$ docker rm -f $(docker ps -aq)
$ docker rmi -f $(docker images | grep dev | awk '{print $3}')
$ rm -rf fabric-client-kv-org[1-2]

docker rm -f $(docker ps -aq)作用是:清除所有容器
docker rmi -f $(docker images | grep dev | awk '{print $3}')作用是:删除所有chaincode镜像
rm -rf fabric-client-kv-org[1-2]作用是:删除用户注册和登录的数据,如私钥和证书


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:青柠果嗄

查看原文:2019-06-26

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

706 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传