2019-06-26

本文介紹了fabric中的一個示例應(yīng)用balance-transfer,fabric版本是1.4.1。本文基于上一章hyperledger基礎(chǔ)培訓(xùn)-創(chuàng)建第一個fabric網(wǎng)絡(luò)的基礎(chǔ)上,如果還未了解,請先了解上一章節(jié)內(nèi)容。

balance-transfer是什么

balance-transfer是Hyperledger fabric Node SDK的一個示例應(yīng)用,主要使用了SDK中fabric-client 和 fabric-ca-client 模塊中的API,實現(xiàn)了與Fabric網(wǎng)絡(luò)交互的各種操作。

1. 預(yù)裝環(huán)境

預(yù)裝環(huán)境在上一章節(jié)hyperledger基礎(chǔ)培訓(xùn)-創(chuàng)建第一個fabric網(wǎng)絡(luò)已有詳細介紹,可跳轉(zhuǎn)至該文章查看。

2. 運行示例程序

balance-transfer可通過腳本運行,構(gòu)建一個本地的Fabric網(wǎng)絡(luò),所有節(jié)點包括:

  • 兩個CA節(jié)點
  • 一個orderer節(jié)點
  • 四個peer節(jié)點 (每個組織各兩個peer節(jié)點)

balance-transfer的目錄結(jié)構(gòu)如下:

balance-transfer
├── app // 與fabric網(wǎng)絡(luò)交互的實現(xiàn)
│ ├── create-channel.js // 創(chuàng)建通道
│ ├── helper.js
│ ├── install-chaincode.js // 安裝鏈碼
│ ├── instantiate-chaincode.js // 實例化鏈碼
│ ├── invoke-transaction.js // 執(zhí)行(invoke)鏈碼
│ ├── join-channel.js // 加入通道
│ ├── update-anchor-peers.js // 更新anchor peer節(jié)點
│ └── query.js // 查詢(query)鏈碼
├── app.js // 定義與fabric網(wǎng)絡(luò)交互的API
├── artifacts // 啟動fabric網(wǎng)絡(luò)需要的配置文件
│ ├── 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 // 啟動應(yīng)用程序腳本
├── typescript
└── testAPIs.sh // 測試API腳本

打開終端窗口1,打開上一章節(jié)克隆下來的fabric-samples項目,執(zhí)行cd fabric-samples。
執(zhí)行cd balance-transfer進入balance-transfer文件夾下,
下面介紹兩種運行方式:

2.1 方式一

1)終端窗口1,使用docker-compose啟動網(wǎng)絡(luò)

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

2)打開終端窗口2,安裝fabric-client 和 fabric-ca-client 模塊

$ npm install

3)啟動應(yīng)用程序,監(jiān)聽4000端口

$ PORT=4000 node app

4)打開終端窗口3,通過curl命令進行測試。在第三部分會具體介紹。

2.2 方式二

1)終端窗口1,執(zhí)行shell腳本啟動應(yīng)用程序

$ ./runApp.sh

執(zhí)行shell腳本之后會:

  • 啟動本地fabric網(wǎng)絡(luò)
  • 下載fabric-client 和 fabric-ca-client 模塊
  • 在PORT 4000啟動應(yīng)用程序

2)打開終端窗口2,執(zhí)行brew install jq安裝jq,使shell腳本能正確解析JSON

$ brew install jq

如果出現(xiàn)-bash: brew: command not found,執(zhí)行/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"安裝brew ,brew是包管理工具,可以很方便地進行安裝/卸載/更新各種軟件包。
安裝完成后,如果還是出現(xiàn)-bash: brew: command not found,則需要修改環(huán)境變量:
執(zhí)行vim ~/.bash_profile打開.bash_profile

vim ~/.bash_profile

在文本編輯框編輯:

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

編輯之后保存,然后執(zhí)行source命令,使之生效:

$ source ~/.bash_profile

3)在終端窗口2,執(zhí)行shell測試腳本
-如果使用golang版本,則執(zhí)行./testAPIs.sh -l golang命令
-如果使用nodejs版本,則執(zhí)行./testAPIs.sh -l node命令

測試腳本實現(xiàn)了以下的功能:

  • 注冊用戶返回token
  • 創(chuàng)建通道以及將節(jié)點加入到通道
  • 安裝鏈碼和實例化鏈碼
  • invoke鏈碼
  • query(查詢)鏈碼

3. REST API請求

下面具體介紹上面測試腳本實現(xiàn)的功能,這邊是用curl模擬POST請求進行測試,同樣也可以用postman(用于網(wǎng)頁調(diào)試、發(fā)送網(wǎng)頁HTTP請求的Chrome插件)進行接口測試。

3.1 登錄

示例是在組織Org1上注冊新用戶Jim

POST請求,包含兩個重要參數(shù):

  • 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"}

注意:響應(yīng)包含成功/失敗狀態(tài),secret和token[JSON Web令牌(JWT)],Header 授權(quán)必須包含這邊返回的token。

3.2 創(chuàng)建通道

示例是創(chuàng)建通道m(xù)ychannel

POST請求,包含兩個重要參數(shù):

  • 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 節(jié)點加入到通道

示例是將org1中的兩個peer節(jié)點加入通道中

POST請求,需要指定以下參數(shù):

  • channelName 加入到哪個channel
  • peers 把哪些節(jié)點加進去
$ 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時需要指定以下參數(shù):

  • chaincode名稱
  • chaincode版本
  • chaincode文件路徑
  • chaincode類型
  • 目標(biāo)節(jié)點列表
    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會根據(jù)本地的鏈碼文件生成chaincode鏡像。
注意:請求傳遞的參數(shù)chaincodeType表示鏈碼類型(golang還是node)

3.5 實例化鏈碼

示例是在mychannel通道上實例化鏈碼

POST請求,需要指定以下參數(shù):

  • channel名稱
  • chaincode名稱
  • chaincode版本
  • 實例化要執(zhí)行的方法(示例沒加,默認(rèn)為Init)
  • 方法參數(shù)
$ 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轉(zhuǎn)賬。
實例化chaincode則會啟動該鏡像,使鏈碼在docker容器中運行。
注意:這邊的chaincodeType主要看鏈碼是nodejs版本的鏈碼還是golang版本的鏈碼。

3.6 invoke(執(zhí)行)鏈碼

鏈碼安裝和實例化之后就可以調(diào)用chaincode執(zhí)行交易。

POST請求,需要指定以下參數(shù):

  • channel名稱
  • chaincode名稱
  • peers執(zhí)行交易所在的節(jié)點列表
  • fcn鏈碼方法名
  • args方法參數(shù)
$ 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"}

這里實現(xiàn)了簡單的轉(zhuǎn)賬交易 a->b,調(diào)用鏈碼中的move方法,返回transaction id。

3.7 query(查詢)鏈碼

balance-transfer 提供了很多查詢接口,包括鏈碼查詢,根據(jù)區(qū)塊號查詢區(qū)塊數(shù)據(jù),根據(jù)交易ID查詢交易信息,查詢鏈上的區(qū)塊數(shù),查詢已安裝或已實例化的鏈碼,查詢通道。

1)鏈碼查詢

3.6已經(jīng)成功執(zhí)行了轉(zhuǎn)賬(A->B),下面我們來查詢下A賬戶現(xiàn)在有多少資產(chǎn)

GET請求,需要指定以下參數(shù):

  • channel名稱
  • chaincode名稱
  • peer節(jié)點
  • fcn鏈碼函數(shù)名
  • args方法參數(shù)
$ 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在執(zhí)行3.6 move方法轉(zhuǎn)賬給b之后,還剩90。
2)按區(qū)塊號查詢

根據(jù)塊號查詢區(qū)塊信息,示例中展示的是org1組織下的peer0節(jié)點上第一個區(qū)塊的信息

GET請求,需要指定以下參數(shù):

  • channel名稱
  • 塊號
  • peer節(jié)點
$ 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查詢

根據(jù)交易id查詢交易詳細信息,這邊的Transaction ID可以用3.6返回的Transaction ID

GET請求,需要指定以下參數(shù):

  • channel名稱
  • transaction id
  • peer節(jié)點
$ 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) 查詢鏈信息

查詢鏈信息,示例是查詢通道m(xù)ychannel的鏈信息。

GET請求,需要指定以下參數(shù):

  • channel名稱
  • peer節(jié)點
$ 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節(jié)點已經(jīng)安裝的鏈碼信息。

GET請求,需要指定以下參數(shù):

  • peer節(jié)點
  • 鏈碼類型(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節(jié)點已經(jīng)安裝的鏈碼信息:鏈碼名稱,鏈碼版本號,鏈碼路徑。
6) 查詢實例化的鏈碼(跟5)類似,type不同)

示例是查詢org1的peer0節(jié)點已經(jīng)實例化的鏈碼信息

GET請求,需要指定以下參數(shù):

  • peer節(jié)點
  • 鏈碼類型(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節(jié)點已經(jīng)實例化的鏈碼信息:鏈碼名稱,鏈碼版本號,鏈碼路徑。
7)查詢通道

查詢組織org1的peer0節(jié)點加入的channel

GET請求,需要指定的參數(shù)如下:

  • peer節(jié)點
$ 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節(jié)點加入的channel列表

4 清理網(wǎng)絡(luò)

$ 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]作用是:刪除用戶注冊和登錄的數(shù)據(jù),如私鑰和證書

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容