Hyperledger Fabric 開發——Node.js SDK與fabric鏈碼交互

1、本篇背景

前面已經對鏈碼開發作了比較詳細的介紹,并且對官方提供的 fabcar 鏈碼進行了解讀,本篇將介紹如何使用 Node.js SDK 與區塊鏈網絡中的鏈碼進行交互。

本篇內容基本來自官方 Hyperledger Fabric 文檔中的 Writing Your First Application 章節,對文檔進行翻譯,原文網址如下:

http://hyperledger-fabric.readthedocs.io/en/latest/write_first_app.html

主要根據谷歌來翻譯的,并稍微做一些修改。由于水平有限,最終的翻譯質量不太好,歡迎大家拍磚。

2、編寫你的第一個應用

提示:
如果您還不熟悉Fabric網絡的基礎架構,則可能需要在繼續之前訪問 簡介建立您的第一個網絡 文檔。

在本節中,我們將查看一些示例程序,以了解Fabric應用程序的工作方式。這些應用程序(以及他們使用的智能合約) - 統稱為 fabcar --提供了Fabric功能的廣泛演示。值得注意的是,我們將展示與證書頒發機構交互并生成注冊證書的過程,之后我們將利用這些身份來查詢和更新賬本。

我們將通過三個主要步驟:
1.建立一個開發環境。我們的應用程序需要一個網絡來進行交互,因此我們將下載一個簡化為注冊/登記,查詢和更新所需的組件:

應用程序與鏈碼交互概述

2.學習我們的應用將使用的示例智能合約的參數。我們的智能合約包含各種功能,使我們能夠以不同的方式與賬本進行交互。 我們將進入并檢查該智能合約,以了解我們的應用程序將使用的功能。

3.開發應用程序以便能夠查詢和更新Fabric記錄。 我們將自己進入應用程序代碼(我們的應用程序已經使用JavaScript編寫),并手動操作變量以運行不同類型的查詢和更新。

完成本教程后,您應該基本了解如何將應用程序與智能合約一起編程,以便與Fabric網絡上的賬本(即節點peer)進行交互。

3、設置您的開發環境

如果您已經完成 建立您的第一個網絡,您應該設置好您的開發環境,并下載好 fabric-samples 以及附帶的工件。要運行本教程,您現在需要做的是移除您擁有的任何現有網絡,您可以通過執行以下操作來完成此操作:

./byfn.sh down

如果您沒有開發環境以及網絡和應用程序的附帶工件,請訪問 先決條件 頁面,并確保您的計算機上安裝了必要的依賴項。

接下來,如果您尚未這樣做,請訪問安裝示例,二進制文件和Docker鏡像 頁面并按照提供的說明進行操作。

克隆 fabric-samples 庫后,返回到本教程,并下載最新穩定版的Fabric鏡像和可用的工具。

此時應該安裝好了一切。進入到 fabric-samples 庫中的 fabcar 子目錄,并查看內部內容:

cd fabric-samples/fabcar  && ls

你應該看到以下內容:

enrollAdmin.js  invoke.js   package.json    registerUser.js
hfc-key-store   node_modules    query.js    startFabric.sh
fabcar所在目錄

在開始之前,我們還需要做一點準備工作。運行以下命令來殺死當前運行或者活躍的容器:

docker rm -f $(docker ps -aq)

清除所有緩存網絡:

# Press 'y' when prompted by the command
# 命令提示時請輸入'y'

docker network prune

最后,如果您已經完成了本教程,您還需要刪除 fabcar 智能合約的底層鏈碼鏡像。如果您是第一次瀏覽此內容的用戶,那么您的系統上不會有此鏈接代碼鏡像:

docker rmi dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba
3.1 安裝客戶端并啟動網絡

提示:
以下說明需要您在克隆在本地的 fabric-samples 庫中的 fabcar 子目錄中。在本教程接下來的部分,請確保在此子目錄的根目錄下。

運行以下命令為應用程序安裝Fabric依賴庫。我們關注 fabric-ca-client ,它將允許我們的應用程序與CA服務器進行通信并檢索身份資料,以及使用 fabric-client ,它允許我們加載身份資料并與節點交互并訂閱服務。

npm install

通過使用 startFabric.sh shell腳本啟動您的網絡。該命令將啟動我們的各種Fabric工具,并啟動用Golang編寫的鏈碼的智能合約容器:

./startFabric.sh

您還可以選擇運行本教程,以針對使用Node.js編寫的鏈碼。如果您想追求這種方式,請改為執行以下命令:

./startFabric.sh node

提示:
請注意,Node.js鏈代碼方案大約需要90秒才能完成或者更久;該腳本并未掛起,反而增加的時間是在構建鏈碼鏡像時安裝了fabric-shim的結果。

好吧,現在你已經有了一個示例網絡和一些代碼,讓我們來看看不同部分如何組合在一起。

4、應用程序如何與網絡進行交互

要更深入地了解我們 fabcar 網絡中的組件(以及它們如何部署)以及應用程序如何與更細粒度級別的組件進行交互,請參閱了解Fabcar網絡

開發者更感興趣的是應用程序之間作了什么 - 以及查看代碼本身以了解應用程序是如何構建的。目前,更需要了解的事情是,應用程序使用軟件開發工具包(SDK)訪問允許查詢和更新賬本的API。

5、注冊管理員用戶

提示:
以下兩節涉及與證書頒發機構的通信。在運行即將推出的程序時,您可能會發現流式傳輸CA日志很有用。

要流式處理您的CA日志,拆分您的終端或打開一個新的shell并發出以下命令:

docker logs -f ca.example.com

現在返回到您的終端 fabcar 內容。。。

當我們啟動我們的網絡時,管理員用戶 - admin - 已在我們的認證中心注冊。現在我們需要向CA服務器發送一個注冊呼叫,并為該用戶檢索注冊證書(eCert)。我們不會在這里詳細介紹注冊的詳細信息,但可以說SDK和擴展我們的應用程序需要此證書才能形成管理員的用戶對象。然后我們將使用這個管理對象來注冊并注冊一個新用戶。將管理員注冊呼叫發送到CA服務器:

node enrollAdmin.js

該程序將調用證書簽名請求(CSR),并最終將eCert和密鑰材料輸出到此項目的根目錄中新創建的文件夾- hfc-key-store 中。然后,我們的應用程序將在他們需要為我們的各種用戶創建或加載身份對象時查找此位置。

6、注冊普通用戶user1

使用我們新生成的管理員eCert,我們現在將再次與CA服務器進行通信,以注冊和注冊新用戶。這個user - user1 - 將是我們在查詢和更新賬本時使用的身份。這里需要注意的一點是,為我們的新用戶發布注冊和注冊呼叫的管理員身份(即,此用戶正在扮演注冊員的角色)。發送注冊并為 user1 注冊呼叫:

node registerUser.js

注:終端顯示如下

wenzildeiMac:fabcar wenzil$ node registerUser.js
Store path:/Users/wenzil/Desktop/study/fabric-samples/fabcar/hfc-key-store
Successfully loaded admin from persistence
Successfully registered user1 - secret:PBpPiXbnokEz
Successfully enrolled member user "user1" 
User1 was successfully registered and enrolled and is ready to intreact with the fabric network

與管理員注冊類似,此程序調用CSR并將密鑰和eCert輸出到 hfc-key-store 子目錄中。所以現在我們擁有兩個獨立用戶的身份資料 - adminuser1 。 與賬本交互的時間。。。

7、查詢賬本

查詢是指如何從賬本中讀取數據。這些數據存儲為一系列鍵值對,您可以查詢單個鍵,多個鍵的值,或者 - 如果賬本是使用JSON等豐富的數據存儲格式編寫的,則可以對其執行復雜的搜索(例如,查找包含特定關鍵字的所有資產)。

這是查詢如何工作的表示形式:


查詢賬本

首先,讓我們運行我們的 query.js 程序返回賬本上所有汽車的列表。我們將使用我們的第二個身份 - user1 - 作為此應用程序的簽名實體。我們程序中的以下行將user1指定為簽名者:

fabric_client.getUserContext('user1', true);

回想一下,user1 注冊資料已經被放入我們的 hfc-key-store 子目錄,所以我們只需要告訴我們的應用程序來獲取這個身份。通過定義用戶對象,我們現在可以繼續從賬本中讀取數據。查詢所有汽車的函數 queryAllCars 在應用程序中預加載,所以我們可以簡單地按照原樣運行程序:

node query.js

它應該返回像這樣的東西:

Successfully loaded user1 from persistence
Query has completed, checking results
Response is  [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},
{"Key":"CAR1",   "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},
{"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"}}]

這是10輛車。 由Adriana擁有的黑色特斯拉Model S,由Brad擁有的紅色Ford Mustang,由Pari擁有的紫色Fiat Punto等等。賬本是基于關鍵值的,在我們的實施中,關鍵是 CAR0CAR9 。這一點將變得特別重要。

讓我們仔細看看這個程序。 使用編輯器(例如atom或visual studio)并打開 query.js

應用程序的初始部分定義了某些變量,例如通道名稱,證書存儲位置和網絡端點。在我們的示例應用程序中,這些變量已被內置,但在真實應用程序中,這些變量必須由應用程序開發者指定。

var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);

var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;

這是我們構建查詢的代碼塊:

// queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
// queryAllCars chaincode function - requires no arguments , ex: args: [''],
const request = {
  //targets : --- letting this default to the peers assigned to the channel
  chaincodeId: 'fabcar',
  fcn: 'queryAllCars',
  args: ['']
};

當應用程序運行時,它調用節點peer上的 fabcar 鏈碼,在其中運行 queryAllCars 函數,并且不傳遞任何參數。

要查看我們智能合約中的可用功能,進入到 fabric-samples 根目錄下的 chaincode/fabcar/go 子目錄,然后在您的編輯器打開 fabcar.go

提示:
這些相同的功能在 fabcar 鏈碼的Node.js版本中定義。

你會看到我們有以下功能可供調用:initLedgerqueryCarqueryAllCarscreateCarchangeCarOwner

讓我們仔細看看 queryAllCars 函數,看看它如何與賬本交互。

func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {

      startKey := "CAR0"
      endKey := "CAR999"

      resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)

這定義了queryAllCars的范圍。CAR0和CAR999之間的每輛車 - 總共1,000輛汽車,假設每個鑰匙都被正確標記 - 將由查詢返回。

以下是應用程序如何在鏈碼中調用不同功能的表示形式。每個功能必須根據鏈碼shim接口中的可用API進行編碼,這反過來又允許智能合同容器與對等賬本正確對接。


運行Sample

我們可以看到我們的 queryAllCars 函數以及一個叫做 createCar 的函數,它允許我們更新賬本并最終在鏈中添加一個新塊。

但首先,返回 query.js 程序并編輯構造函數請求以查詢CAR4。我們通過將 query.js 中的函數從 queryAllCars 更改為 queryCar 并將CAR4作為特定鍵傳遞來實現此目的。

query.js 程序現在應該如下所示:

const request = {
  //targets : --- letting this default to the peers assigned to the channel
  chaincodeId: 'fabcar',
  fcn: 'queryCar',
  args: ['CAR4']
};

保存程序并返回到您的 fabcar 目錄。 現在再次運行該程序:

node query.js

您將看到如下內容:

{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}

如果你回頭看看我們之前查詢過每輛車的結果,可以看到 CAR4 是Adriana的黑色特斯拉模型S,這是在這里返回的結果。

使用 queryCar 函數,我們可以查詢任何關鍵字(例如 CAR0 )并獲取與該車相對應的任何品牌,型號,顏色和所有者。

很好。此時,您應該熟悉智能合約中的基本查詢功能以及查詢程序中的少量參數。 時間更新賬本。。。

8、更新賬本

現在我們已經完成了幾個賬本查詢并添加了一些代碼,我們已經準備好更新賬本。有很多潛在的更新我們可以做,但我們先創建一輛新車。

下面我們可以看到這個過程如何工作。提案更新,通過認可,然后返回到應用程序,然后將其發送到訂單并寫入每個節點peer賬戶:


更新賬本

我們對賬本的第一次更新將是創造一輛新車。我們有一個單獨的Javascript程序 - invoke.js - 我們將用它來進行更新。與查詢一樣,使用編輯器打開程序并進入到構建我們的調用的代碼塊:

// createCar chaincode function - requires 5 args, ex: args: ['CAR12', 'Honda', 'Accord', 'Black', 'Tom'],
// changeCarOwner chaincode function - requires 2 args , ex: args: ['CAR10', 'Barry'],
// must send the proposal to endorsing peers
var request = {
  //targets: let default to the peer assigned to the client
  chaincodeId: 'fabcar',
  fcn: '',
  args: [''],
  chainId: 'mychannel',
  txId: tx_id
};

您會看到我們可以調用兩個函數之一 - createCarchangeCarOwner 。首先,讓我們創建一個紅色雪佛蘭Volt并將其交給名為Nick的所有者。我們在賬本上使用 CAR9 ,因此我們將在此使用 CAR10 作為識別密鑰。編輯代碼如下:

var request = {
  //targets: let default to the peer assigned to the client
  chaincodeId: 'fabcar',
  fcn: 'createCar',
  args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'],
  chainId: 'mychannel',
  txId: tx_id
};

保存并運行程序:

node invoke.js

終端中會有一些關于 ProposalResponse 和Promise的輸出。 然而,我們所關心的只是這個信息:

The transaction has been committed on peer localhost:7053

要查看此事務已寫入,請返回 query.js 并將參數從 CAR4 更改為 CAR10

換句話說,把這里:

const request = {
  //targets : --- letting this default to the peers assigned to the channel
  chaincodeId: 'fabcar',
  fcn: 'queryCar',
  args: ['CAR4']
};

改為:

const request = {
  //targets : --- letting this default to the peers assigned to the channel
  chaincodeId: 'fabcar',
  fcn: 'queryCar',
  args: ['CAR10']
};

再次保存,然后查詢:

node query.js

這應該返回這個:

Response is  {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"}

恭喜!您已經創建了一輛車!

所以,現在我們已經做到了,讓我們說Nick很慷慨,他想把他的雪佛蘭Volt交給一個名叫Dave的人。

要做到這一點,請返回到 invoke.js 并將函數從 createCar 更改為 changeCarOwner 并輸入如下所示的參數:

var request = {
  //targets: let default to the peer assigned to the client
  chaincodeId: 'fabcar',
  fcn: 'changeCarOwner',
  args: ['CAR10', 'Dave'],
  chainId: 'mychannel',
  txId: tx_id
};

第一個參數 - CAR10 - 表明將改變車主的汽車。第二個參數 - Dave - 表明為汽車的新主人。

再次保存并執行該程序:

node invoke.js

現在,讓我們再次查詢賬本并確保Dave現在與 CAR10 鍵相關聯:

node query.js

它應該返回這個結果:

Response is  {"colour":"Red","make":"Chevy","model":"Volt","owner":"Dave"}

CAR10 的所有權從Dave變成了Dave。

提示:
在現實世界的應用程序中,鏈碼可能會有一些訪問控制邏輯。例如,只有特定的授權用戶可以創建新車,并且只有車主才可以將車輛轉移給其他人。

9、總結

現在,我們已經完成了一些查詢和一些更新,您應該對應用程序如何與網絡進行交互有一個很好的理解。您已經了解了智能合約,API和SDK在查詢和更新中扮演的角色的基本知識,您應該了解如何使用不同類型的應用程序來執行其他業務任務和操作。

在隨后的文檔中,我們將學習如何實際編寫智能合約,以及如何利用這些更低級別的應用程序功能中的一些功能(特別是與身份和會員服務有關的功能)。

10、額外的資源

Hyperledger Fabric Node SDK repo是很好的資源,里面有更深入的文檔和示例代碼。 您還可以在Hyperledger Rocket Chat上咨詢Fabric社區和組件專家。

由于水平有限,翻譯質量不太好,歡迎大家拍磚。

對應官網連接地址如下:
Hyperledger Fabric - Writing Your First Application

PS:剛入坑的小白,很多不懂,還請各位大佬多賜教,謝謝!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容