本文介紹了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)至該文章查看。
- Docker - v1.12或更高版本
- Docker Compose - v1.8或更高版本
- Git客戶端
- Node.js v8.4.0或更高版本
- 下載Docker鏡像
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ù),如私鑰和證書