2019-06-26

栏目: Go · 发布时间: 4年前

内容简介:本文介绍了fabric中的一个示例应用balance-transfer,fabric版本是1.4.1。本文基于上一章balance-transfer是Hyperledger fabric Node SDK的一个示例应用,主要使用了SDK中fabric-client 和 fabric-ca-client 模块中的API,实现了与Fabric网络交互的各种操作。预装环境在上一章节

本文介绍了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] 作用是:删除用户注册和登录的数据,如私钥和证书


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

プログラミングコンテストチャレンジブック

プログラミングコンテストチャレンジブック

秋葉 拓哉、岩田 陽一、北川 宜稔 / 毎日コミュニケーションズ / 2010-09-11 / JPY 34.44

現在、プログラミングコンテストは数多く開催されています。Google Code Jam、TopCoder、ACM/ICPCなどの名前を聞いたことがある人も少なくないでしょう。本書で扱うのはそれらのような、問題を正確にできるだけ多く解くことを競うプログラミングコンテストです。 プログラミングコンテストは気軽に参加することができます。例えば、Google Code JamやTopCoderはイン......一起来看看 《プログラミングコンテストチャレンジブック》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码