利用Hyperledger Fabric開發你的第一個區塊鏈應用

原文地址:www.xuanzhangjiong.top/2019/04/16/…javascript

做者:TopJohnhtml

利用Hyperledger Fabric開發你的第一個區塊鏈應用

本文示例源於fabric-samples中的fabcar github.com/hyperledger…java

在這個例子中,咱們經過一個簡單的示例程序來了解Fabric應用是如何運行的。在這個例子中使用的應用程序和智能合約(鏈碼)統稱爲FabCar。這個例子很好地提供了一個開始用於理解Hyperledger Fabric。在這裏,你將學會如何開發一個應用程序和智能合約來查詢和更新帳本,如何利用CA來生成一個應用程序須要的用於和區塊鏈交互的X.509證書。node

咱們使用應用程序SDk來執行智能合約中的查詢更新帳本的操做,這些操做在智能合約中藉助底層接口實現。git

咱們將經過3個步驟來進行講解:github

  1. 搭建開發環境。咱們的應用程序須要和網絡交互,所以咱們須要一個智能合約和應用程序使用的基礎網絡。

  1. 學習一個簡單的智能合約,FabCar。咱們使用JavaScript開發智能合約。咱們經過查看智能合約來學習應用程序如何使用智能合約發送交易,如何使用智能合約來查詢和更新帳本。docker

  2. 使用FabCar開發一個簡單的應用程序。咱們的應用程序會使用FabCar智能合約來查詢及更新帳本上的汽車資產。咱們將進入應用程序的代碼中去了解如何建立交易,包括查詢一輛汽車的信息,查詢一批汽車的信息以及建立一輛汽車。typescript

設置區塊鏈網絡

注意:下面的部分須要進入你克隆到本地的fabric-samples倉庫的first-network子目錄。shell

若是你已經學習了Building Your First Network,你應該已經下載了fabric-samples並且已經運行起了一個網絡。在你進行本教程以前,你須要中止這個網絡:數據庫

./byfn.sh down
複製代碼

若是你以前運行過這個教程,使用下面的命令關掉全部中止或者運行的容器。注意,這將關掉全部的容器,不管是否和Fabric有關。

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images | grep fabcar | awk '{print $3}')
複製代碼

若是你沒有這個網絡和應用相關的開發環境和構件,請訪問 Prerequisites頁面,確保你的機器安裝了必要的依賴。

接下來,若是你尚未這樣作的話,請瀏覽 Install Samples, Binaries and Docker Images頁面,跟着上面的操做進行。當你克隆了fabric-samples倉庫,下載了最新的穩定版Fabric鏡像和相關工具以後回到教程。

若是你使用的是Mac OS和Mojava,你須要安裝Xcode

啓動網絡

下面的部分須要進入fabric-samples倉庫的fabcar子目錄。

使用startFabric.sh來啓動你的網絡。這個命令將啓動一個區塊鏈網絡,這個網絡由peer節點、排序節點、證書受權服務等組成。同時也將安裝和初始化javascript版本的FabCar智能合約,咱們的應用程序將經過它來操做帳本。咱們將經過本教程學習更過關於這些組件的內容。

./startFabric.sh javascript
複製代碼

如今,咱們已經運行起來了一個示例網絡,還安裝和初始化了FabCar智能合約。爲了運行咱們的應用程序,咱們須要安裝一些依賴,同時讓咱們看一下它們是如何工做的。

安裝應用程序

注意:下邊的章節須要進入你克隆到本地的fabric-samples倉庫的fabcar/javascript子目錄。 下面的命令來安裝應用程序所需的Fabric有關的依賴。大概將話費1分鐘左右的時間:

npm install
複製代碼

這個指令用於安裝應用程序所需的依賴,這些依賴被定義在package.json中。其中最重要的是fabric-network類;它使得應用程序可使用身份、錢包和鏈接到通道的網關,以及提交交易和等待通知。本教程也將使用fabric-ca-client類來註冊用戶以及他們的受權證書,生成一個fabric-network使用的合法的身份。

一旦npm install執行成功,運行應用程序所需的一切就準備好了。在這個教程中,你將主要使用fabcar/javascript目錄下的JavaScript文件來操做應用程序。讓咱們來了解一下里面有哪些文件:

ls
複製代碼

你將看到下列文件:

enrollAdmin.js  node_modules       package.json  registerUser.js
invoke.js       package-lock.json  query.js      wallet
複製代碼

裏面也有一些其餘編程語言的文件,好比fabcar/typescript目錄中。當你使用過JavaScript示例以後-其實都是相似的。

若是你在使用Mac OS並且運行的是Mojava你須要[安裝Xcode](https://hyperledger-fabric.readthedocs.io/en/latest/tutorial/installxcode.html)

登記管理員用戶

下面的部分涉及執行和CA服務器通信的過程。你在執行下面的程序的時候,打開一個終端執行docker logs -f ca.example.com來查看CA的日誌,會是十分有幫助的。

當咱們建立網絡的時候,一個叫admin的用戶已經被受權服務器(CA)建立爲登記員。咱們第一步要作的是使用enroll.js程序爲admin生成私鑰,公鑰和x.509證書。這個程序使用一個證書籤名請求 (CSR)--先在本地生成私鑰和公鑰,而後把公鑰發送到CA,CA會發佈一個應用程序使用的證書。這三個憑證會保存在錢包中,以便於咱們以管理員的身份使用CA。

接下來咱們會註冊和登記一個新的應用程序用戶,咱們將使用這個用戶來經過應用程序和區塊鏈進行交互。

讓咱們登記一個admin用戶:

node enrollAdmin.js
複製代碼

這個命令將CA管理員證書保存在wallet目錄。

註冊和登記user1

如今咱們在錢包裏放了管理員的證書,咱們能夠登記一個新用戶--user1--用這個用戶來查詢和更新帳本:

node registerUser.js
複製代碼

和登記管理員相似,這個程序使用了CSR來登記user1並把它的證書保存到admin所在的錢包中。如今咱們有了2個獨立的用戶--adminuser1--它們都將用於咱們的應用程序。

接下來是帳本交互時間...

查詢帳本

區塊鏈網絡中的每一個節點都擁有一個帳本的副本,應用程序能夠經過執行智能合約查詢帳本上的最新舒徐來實現查詢帳本操做,將結果返回給應用程序。

這是一個如何查詢的簡單闡述:

應用程序使用查詢從ledger讀取數據。最多見的就是查詢當前帳本中的最新值--世界狀態。世界狀態是一個鍵值對的集合,應用程序能夠根據一個鍵或者多個鍵來查詢數據。並且,當鍵值對是以JSON形式存在的時候,世界狀態能夠經過配置使用數據庫(例如CouchDB)來支持富查詢。這個特性對於查詢匹配特定的鍵的值是頗有幫助的,好比查詢一我的的全部汽車。

首先,讓咱們使用query.js程序來查詢帳本上的全部汽車。這個程序使用咱們的第二個身份--user1--來操做帳本。

node query.js
複製代碼

輸出結果以下:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result 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"}}]
複製代碼

讓咱們近距離看一下這個程序。使用文本編輯器(如atom或者visual studio)打開query.js

應用程序開始的時候就從fabric-network模塊引入了兩個關鍵的類FileSystemWalletGateway。這兩個類將用於定位錢包中user1的身份,而且使用這個身份鏈接網絡:

const { FileSystemWallet, Gateway } = require('fabric-network');
複製代碼

應用程序使用網關鏈接網絡:

const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: 'user1' });
複製代碼

這段代碼建立了一個新的網關,而後經過它來讓應用程序鏈接網絡。cpp描述了網關經過wallet中的user1來鏈接網絡。打開 ../../basic-network/connection.json來查看cpp是如何解析一個JSON文件的:

const ccpPath = path.resolve(__dirname, '..', '..', 'basic-network', 'connection.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);
複製代碼

若是你想了解更多關於鏈接配置文件的結構以及它是怎麼定義網絡的,請查閱 the connection profile topic

一個網絡能夠被拆分紅不少個通道,代碼中下一個很重要的地方是將應用程序鏈接到特定的通道mychannel上:

在這個通道中,咱們能夠經過fabcar智能合約來和帳本進行交互:

const contract = network.getContract('fabcar');
複製代碼

fabcar中有許多不一樣的交易,咱們的應用程序先使用queryAllCars交易來查詢帳本的世界狀態:

const result = await contract.evaluateTransaction('queryAllCars');
複製代碼

evaluateTransaction方法呈現了一種和區塊鏈網絡中的智能合約交互的最簡單的方法。它只是根據配置文件中的定義鏈接一個節點,而後向節點發送請求,在節點內執行該請求。智能合約查詢了節點帳本上的全部汽車,而後把結果返回給應用程序。此次交互並無更新帳本。

FabCar智能合約

讓咱們看一看FabCar智能合約裏的交易。進入fabric-samples下的子目錄chaincode/fabcar/javascript/lib,而後用你的編輯器打開fabcar.js

看一下咱們的智能合約是如何經過Contract類來定義的:

class FabCar extends Contract {...
複製代碼

在這個類結構中,你將看到定義瞭如下交易: initLedgerqueryCarqueryAllCarscreateCarchangeCarOwner。例如:

async queryCar(ctx, carNumber) {...}
async queryAllCars(ctx) {...}
複製代碼

讓咱們更進一步看一下 queryAllCars ,看一下它是怎麼和帳本交互的。

async queryAllCars(ctx) {

  const startKey = 'CAR0';
  const endKey = 'CAR999';

  const iterator = await ctx.stub.getStateByRange(startKey, endKey);
複製代碼

這段代碼定義了 queryAllCars 將要從帳本獲取的汽車的範圍。從 CAR0 到 CAR999 的每一輛車 -- 一共 1000 輛車,假定每一個鍵都被合適地錨定了 -- 將會做爲查詢結果被返回。 代碼中剩下的部分,經過迭代將查詢結果打包成 JSON 並返回給應用。

下邊將展現應用程序如何調用智能合約中的不一樣交易。每個交易都使用一組 API 好比 getStateByRange 來和帳本進行交互。瞭解更多API請閱讀detail

你能夠看到咱們的queryAllCars交易,還有另外一個叫作createCar。咱們稍後將在教程中使用他們來更新帳本,和添加新的區塊。

可是在那以前,返回到query程序,更改evaluateTransaction的請求來查詢爲CAR4query程序如今以下:

const result = await contract.evaluateTransaction('queryCar', 'CAR4');
複製代碼

保存程序,而後返回到fabcar/javascript目錄。如今,再次運行query程序:

node query.js
複製代碼

你應該會看到以下所示:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}
複製代碼

若是你查看一下以前queryAllCars的交易結果,你會看到CAR4Adriana黑色 Tesla model S,也就是這裏返回的結果,是同樣的。

咱們可使用queryCar交易來查詢任意汽車,使用它的鍵(好比CAR0)獲得車輛的製造商、型號、顏色和車主等相關信息。

很是好。如今你應該已經瞭解了智能合約中基礎的查詢交易,也手動修改了查詢程序中的參數。

是時候進行更新帳本了。

更新帳本

如今咱們已經完成一些帳本的查詢操做,添加了一些代碼,咱們已經準備好更新帳本了。有很 的更新操做咱們能夠作,可是咱們從建立一輛新車開始。

從一個應用程序的角度來講,更新一個帳本很簡單。應用程序向區塊鏈網絡提交一個交易, 當交易被驗證和提交後,應用程序會收到一個交易成功的提醒。可是在底層,區塊鏈網絡中各組件中不一樣的共識程序協同工做,來保證帳本的每個更新提案都是合法的,並且有一個你們一致承認的順序。

上圖中,咱們能夠看到完成這項工做的主要組件。同時,多個節點中每個節點都擁有一份帳本的副本,並可選的擁有一份智能合約的副本,網絡中也有一個排序服務。排序服務保證網絡中交易的一致性;它也將鏈接到網絡中不一樣的應用程序的交易以定義好的順序生成區塊。

咱們對帳本的的第一個更新是建立一輛新車。咱們有一個單獨的程序叫作invoke.js,用來更新帳本。和查詢同樣,使用一個編輯器打開程序定位到咱們構建和提交交易到網絡的代碼段:

await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');
複製代碼

看一下應用程序如何調用智能合約的交易createCar來建立一輛車主爲Tom的黑色Honda Accord汽車。咱們使用CAR12做爲這裏的鍵,這也說明了咱們沒必要使用連續的鍵。

保存並運行程序:

node invoke.js
複製代碼

若是執行成功,你將看到相似輸出:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
2018-12-11T14:11:40.935Z - info: [TransactionEventHandler]: _strategySuccess: strategy success for transaction "9076cd4279a71ecf99665aed0ed3590a25bba040fa6b4dd6d010f42bb26ff5d1"
Transaction has been submitted
複製代碼

注意inovke程序使用的是submitTransactionAPI和區塊鏈網絡交互的,而不是evaluateTransaction

await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');
複製代碼

submitTransactionevaluateTransaction要複雜的多。不僅是和單個節點交互,SDK將把submitTransaction提案發送到區塊鏈網絡中每個必要的組織的節點。每個節點都將根據這個提案執行請求的智能合約,並生成一個該節點簽名的交易響應並返回給SDK 。SDK將全部通過簽名的交易響應收集到一個交易中,這個交易將會被髮送到排序節點。排序節點蒐集並排序每一個應用的交易,並把這些交易放入到一個交易區塊。而後排序節點將這些區塊分發到網絡中的節點,每一筆交易都會在節點中進行驗證和提交。最後,SDK會後到提醒,並把控制權返回給應用程序。

submitTransaction也會包括一個監聽器用於確保交易已經被校驗和提交到帳本里了。應用程序須要利用監聽器或者使用submitTransaction接口,它內部已經實現了監聽器。若是沒有監聽器,你可能沒法肯定交易是否被排序校驗以及提交。

應用程序中的這些工做由submitTransaction完成!應用程序、智能合約、節點和排序服務一塊兒工做來保證網絡中帳本一致性的程序被稱爲共識。

爲了查看這個被寫入帳本的交易,返回到query.js並將參數CAR4更改成CAR12

換句話說就是將:

const result = await contract.evaluateTransaction('queryCar', 'CAR4');
複製代碼

改成:

const result = await contract.evaluateTransaction('queryCar', 'CAR12');
複製代碼

再次保存,而後查詢:

node query.js
複製代碼

將返回:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
{"colour":"Black","make":"Honda","model":"Accord","owner":"Tom"}
複製代碼

恭喜。你建立了一輛汽車並驗證了它記錄在帳本上!

如今咱們已經完成了,咱們假設Tom很大方,想把他的Honda Accord送給一個叫Dave的人。

爲了完成這個,返回到invoke.js而後利用輸入的參數,將智能合約的交易從createCar改成changeCarOwner

await contract.submitTransaction('changeCarOwner', 'CAR12', 'Dave');
複製代碼

第一個參數 ---CAR12--- 表示將要易主的車。第二個參數 ---Dave--- 表示車的新主人。

再次保存並執行程序:

node invoke.js
複製代碼

如今咱們來再次查詢帳本,以肯定Dave和CAR12鍵已經關聯起來了:

node query.js
複製代碼

將返回以下結果:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
{"colour":"Black","make":"Honda","model":"Accord","owner":"Dave"}
複製代碼

CAR12的主人已經從Tom變成了Dave。

在實際的應用中,智能合約有權限控制邏輯。舉個例子,只有有權限的用戶能夠建立新車,只有車子的擁有者能夠轉移車輛所屬權。

總結

如今咱們已經完成了帳本的查詢和更新,你也應該比較瞭解如何經過智能合約和區塊鏈進行交互來查詢帳本和更新帳本了。在教程中已經講解了查詢和更新的智能合約,API和SDK,想必你對其餘商業場景也有了必定的瞭解和認識。

經過FabCar這個例子,咱們能夠快速學習如何基於Node SDK開發應用程序。

AwesomeBlockchain
相關文章
相關標籤/搜索