該文教程分一下三步:
- 啟動一個用于測試的區(qū)塊鏈網(wǎng)絡(luò)
- 學(xué)習(xí)如何使用智能合約
- 開發(fā)一個可以查詢和修改的應(yīng)用
啟動網(wǎng)絡(luò)
這次我們定位到fabcar文件夾:
git clone https://github.com/hyperledger/fabric-samples.git
cd fabric-samples/fabcar
啟動:
./startFabric.sh
這個腳本做了以下事情:
- 啟動一個peer節(jié)點(diǎn),order節(jié)點(diǎn),證書和CLI容器
- 創(chuàng)建一個channel,并把peer添加到channel
- 安裝智能合約(例如chaincode)到peer的文件系統(tǒng),然后初始化chaincode到channel,實(shí)例化一個chaincode容器
- 調(diào)用
initLedger
方法來初始化10輛汽車到channel賬本
可以通過docker ps
命令來查看當(dāng)前腳本運(yùn)行后的進(jìn)程情況,接下來我們將重點(diǎn)放在應(yīng)用上。下圖描述了一個應(yīng)用是如何與區(qū)塊鏈網(wǎng)絡(luò)交互的:
應(yīng)用與網(wǎng)絡(luò)的交互
應(yīng)用使用API來調(diào)用智能合約(即這個demo中的chaincode),這些智能合約在主機(jī)的網(wǎng)絡(luò)中用名字和版本唯一標(biāo)識。例如,我們的chaincode容器名為dev-peer0.org1.example.com-fabcar-1.0
,名字是fabcar
,版本是1.0
,peer運(yùn)行在dev-peer0.org1.example.com
查詢賬本
你可以通過單參數(shù)、多參數(shù)、或者是JSON形式的參數(shù)來查詢賬本
我們之前已經(jīng)把賬本中初始化了10輛汽車,現(xiàn)在我們可以用query.js
的代碼來查詢這些數(shù)據(jù)了。
再次之前,我們需要先安裝一些node模塊的sdk,定位到fabcar文件夾,執(zhí)行:
npm install
然后運(yùn)行腳本query.js
,來得到賬本中的汽車列表,這腳本中我們使用了queryAllCars
方法:
node query.js
Query result count = 1
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"}}]
現(xiàn)在我們來看一下query.js
代碼里面到底發(fā)生了什么。
首先定義了一些變量,例如chaincode ID、channel名字以及網(wǎng)絡(luò)端點(diǎn)等:
var options = {
wallet_path : path.join(__dirname, './network/creds'),
user_id: 'PeerAdmin',
channel_id: 'mychannel',
chaincode_id: 'fabcar',
network_url: 'grpc://localhost:7051',
下面是構(gòu)造一個查詢請求:
// queryCar - requires 1 argument, ex: args: ['CAR4'],
// queryAllCars - requires no arguments , ex: args: [''],
const request = {
chaincodeId: options.chaincode_id,
txId: transaction_id,
fcn: 'queryAllCars',
args: ['']
我們定義chaincode_id
為fabcar
,然后調(diào)用了queryAllCars
方法,這個方法被定義在chaincode中。查看chaincode\fabcar\fabcar.go
文件,我們可以看到這里面還定義了一些其他的方法,我們可以找到queryAllCars
方法的位置:
func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {
startKey := "CAR0"
endKey := "CAR999"
resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
方法中使用shim接口的函數(shù)GetStateByRange
來獲得賬本中startKey
和endKey
之間的數(shù)據(jù)。下圖表示了一個app是如何調(diào)用chaincode中的不同方法的:
回到query.js
,我們將request調(diào)用的方法修改為queryCar,并在args里面加上參數(shù),來獲取某一輛汽車的數(shù)據(jù):
const request = {
chaincodeId: options.chaincode_id,
txId: transaction_id,
fcn: 'queryCar',
args: ['CAR4']
運(yùn)行:
node query.js
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}
現(xiàn)在,你應(yīng)該知道了如何在chaincode中做基本的查詢數(shù)據(jù)的操作,下面讓我們來講如何更新賬本。
更新賬本
賬本更新的第一件事情,是要創(chuàng)建一個交易提議,類似于查詢,我們需要先創(chuàng)建出一個request,然后調(diào)用channel.SendTransactionProposal
的API來發(fā)送給peer背書。
然后會從network接收到這個交易提議的回復(fù),應(yīng)用可以將這個回復(fù)信息用于創(chuàng)建和簽名一個交易請求。然后我們調(diào)用channel.sendTransaction
API來將請求發(fā)送到ordering service,這筆交易將會被打包成一個block,然后投遞到該channel的各個peer節(jié)點(diǎn)來進(jìn)行驗證(這個例子中我們只有一個單獨(dú)的結(jié)點(diǎn))。
最終,應(yīng)用將使用eh.setPeerAddr
API來連接結(jié)點(diǎn)的監(jiān)聽端口,調(diào)用eh.registerTxEvent
來注冊與這個交易ID相關(guān)的事件,我們可以通過這個API來得知這次事件的成功與否。
在invode.js
腳本中,就有用到上述提到的交易流程:
var request = {
targets: targets,
chaincodeId: options.chaincode_id,
fcn: 'createCar',
args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'],
chainId: options.channel_id,
txId: tx_id
我們創(chuàng)建了一個CAR10的請求,執(zhí)行:
node invoke.js
The transaction has been committed on peer localhost:7053
節(jié)點(diǎn)發(fā)送了這次事件的通知,然后我們的應(yīng)用通過eh.registerTxEvent
API接收到了這個通知,并知道了事件的成功。我們可以再調(diào)用查詢接口驗證一下:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"}
最后,讓我們來看看修改數(shù)據(jù)。將request中的方法改為changeCarOwner
,修改對應(yīng)參數(shù)如下:
var request = {
targets: targets,
chaincodeId: options.chaincode_id,
fcn: 'changeCarOwner',
args: ['CAR10', 'Barry'],
chainId: options.channel_id,
txId: tx_id
執(zhí)行:
node invoke.js
查詢驗證:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Barry"}