Hyperledger Fabric術語中的Chaincode是什麼?
在Hyperledger Fabric中,Chaincode是在網路peer上執行的一段程式碼,用於實現應用程式如何與分類帳互動的業務邏輯。提出交易時,它會觸發Chaincode,該Chaincode決定應將哪種狀態更改應用於分類帳。因此為了在Hyperledger Fabric上開發去中心化的應用程式,必須編寫稱為Chaincode的應用程式邏輯。
Chaincode可以使用Go,Nodejs,Java編寫。其他兩種語言相比,Node.js是一種更容易理解和使用的語言。由於Node.js文件中可用的資訊非常少,但編寫和部署Node.js Chaincode並不困難。因此我決定編寫一些Node.js Chaincode的概念,並逐步編寫和部署簡單的Chaincode。
Hyperledger Fabric資料庫
在深入研究Chaincode之前,首先讓我們看一下Hyperledger Fabric Network中資料的儲存位置。Hyperledger Fabric使用鍵值資料庫儲存其狀態。預設情況下,Fabric使用LevelDB。該資料庫儲存可以使用其鍵查詢的特定物件的二進位制資料。與傳統的資料庫不同,區塊鏈資料庫位於網路的每個peer上。因此它被稱為分散網路。
除了LevelDB,還有另一個用於Hyperledger Fabric的資料庫稱為CouchDB。CouchDB是可選的替代外部可插拔狀態資料庫。與LevelDB鍵值儲存一樣,CouchDB可以儲存以鏈碼建模的任何二進位制資料。但作為JSON文件儲存,當將Chaincode值(例如資產)建模為JSON資料時,CouchDB還可以對Chaincode資料進行豐富的查詢。
Chaincode元件
1.fabric-contract-api:
用於實現智慧合約的高階合約API(作為npm模組提供)
2.fabric-shim:
用於實現智慧合約的低階合約API(作為npm模組提供)
我們可以將fabric-shim視為fabric-contract-api的遞減版本。對新版本的fabric使用高階API是一個好習慣。但是fabric-contract-api可以完成shim可以做的所有事情。當然不止如此。
3.stub:
它是fabric-contract-api中的介面,用於訪問和修改分類帳(資料庫狀態)。因此它是主要資料Chaincode介面,用於在分類賬上讀寫資料,我們如何讀寫資料?讓我們看一下Stub介面中的一些常用方法
Stub介面中的常用方法
1.getState(k):
眾所周知,Hyperledger Fabric資料庫以鍵值對的形式儲存資料。
此方法從分類帳讀取資料。它以輸入“ k”作為鍵,並返回與鍵“ k”關聯的二進位制值。
2.putState(k,v):
此方法將資料寫入分類帳。它以“ k”為鍵,“ v”為值。明確地說,假設我們想將Alice的年齡儲存到分類帳中,我們可以將Alice作為鍵,並將AGE作為值。
3.deleteState(k):
此方法從分類賬中刪除關聯金鑰“ k”的值。
4.getStateByRange(k1,k9):
此方法在分類帳中的一組鍵上返回範圍迭代器。它將迭代startKey(k1)和endKey(k9)並返回這兩個鍵之間的所有鍵值。這類似於javascript中的for迴圈。假設如果我們已經按鍵k1,k2,k3…k99的順序儲存了一些使用者資料,則可以使用此方法簡單地獲取所有這些值的狀態。
5.getTxID():
此方法返回被呼叫事務的事務ID。交易ID對於鏈上的每個交易都是唯一的。因此交易ID在跟蹤交易中起著至關重要的作用。
6.getTxTimestamp():
此方法返回建立事務時的時間戳。這是從交易ChannelHeader獲取的,因此它將指示客戶的時間戳,並且在所有背書人中都具有相同的值。
編寫您的第一個Chaincode
您已經瞭解了在node.js中編寫鏈碼的一些先決條件。因此就目前而言,您可能會非常興奮地編寫您的第一個chaincode。
由於我們要用Nodejs編寫Chaincode,因此我們首先需要建立傳統的npm東西,例如package.json和index.js。如果您不知道此package.json。
列出您的專案所依賴的軟體包
使用語義版本控制規則指定專案可以使用的軟體包的版本
使您的構建具有可複製性,因此更易於與其他開發人員共享.
簡而言之,我們的Chaincode取決於fabric-contract-api和fabric-shim模組。我們在package.json中提到了這些軟體包和版本。
我們還將新增fabric-chaincode-node start作為我們的啟動指令碼,這是在peer節點上安裝chaincode所需的。
這是我們的package.json:
{
"name": "Test-Chaincode",
"version": "1.0.0",
"description": "my first exciting node.js chaincode on Hyperledger-fabric",
"main": "index.js",
"engines": {
"node": ">=8",
"npm": ">=5"
},
"scripts": {
"lint": "eslint .",
"pretest": "npm run lint",
"test": "nyc mocha --recursive",
"start": "fabric-chaincode-node start"
},
"engineStrict": true,
"author": "Hyperledger",
"license": "Apache-2.0",
"dependencies": {
"fabric-contract-api": "~1.4.0",
"fabric-shim": "~1.4.0"
},
"devDependencies": {
"chai": "^4.1.2",
"eslint": "^4.19.1",
"mocha": "^5.2.0",
"nyc": "^12.0.2",
"sinon": "^6.0.0",
"sinon-chai": "^3.2.0"
},
"nyc": {
"exclude": [
"coverage/**",
"test/**"
],
"reporter": [
"text-summary",
"html"
],
"all": true,
"check-coverage": true,
"statements": 100,
"branches": 100,
"functions": 100,
"lines": 100
}
}
如果您可以仔細觀察程式碼,則有一行“ main”:“ index.js”。這意味著什麼—在啟動時(安裝chaincode期間),npm模組用於檢查index.js並在peer節點上安裝提到的合約。”因此我們的index.js包含作為模組匯出的合約。
這是我們的index.js檔案:
'use strict';
const testContract = require(’./logic’);
module.exports.contracts = [ testContract ];
智慧合約:
我們的業務邏輯是什麼?
新增,檢索和刪除學生標記。
1.新增標記涉及將資料寫入分類帳。因此我們將從chaincode的stub介面使用putState(k,v)方法。
2.檢索標記涉及從分類帳讀取資料。因此我們需要使用getState(k)方法。
3.刪除標記涉及刪除資料。因此我們需要使用deleteState(k)方法。
chaincode首先從fabric-contract-api模組引入作用域金鑰類Contract。此類將用於編寫邏輯,所有chaincode函式都應使用This庫類。
const { Contract}=require(’fabric-contract-api’);
classtestContractextendsContract{
//Functions go here
}
新增標記:
我們將建立一個JavaScript物件來儲存每個學科中學生的成績,並將該物件儲存為一個值,並將StudentId儲存為一個鍵。透過伺服器將資料傳送到資料庫時,資料必須是字串。因此我們需要使用JSON.stringify()方法將此標記物件轉換為字串,並應用緩衝區以二進位制資料的形式傳送到資料庫。
asyncaddMarks(ctx,studentId,subject1,subject2,subject3) {
let marks={
subj1:subject1,
subj2:subject2,
subj3:subject3
};
await ctx.stub.putState(studentId,Buffer.from(JSON.stringify(marks)));
console.log(’Student Marks added To the ledger Succesfully..’);
}
刪除標記
asyncdeleteMarks(ctx,studentId) {
await ctx.stub.deleteState(studentId);
console.log(’Student Marks deleted from the ledger Succesfully..’);
}
查詢學生成績:
由於我們在先前的addMarks()函式中將值以緩衝區的形式放置。一旦查詢,它將返回buffer。因此我們需要將緩衝區轉換為字串並將其解析為原始javascript物件。
async queryMarks(ctx,studentId){
let marksAsBytes = await ctx.stub.getState(studentId);
if (!marksAsBytes || marksAsBytes.toString().length <= 0) {
throw newError(’Student with this Id does not exist: ');
}
let marks=JSON.parse(marksAsBytes.toString());
return JSON.stringify(marks);
}
最終版智慧合約程式碼
您可以在此處找到完整的智慧合約。https://gist.github.com/Salmandabbakuti/fb25d0429359c6d77ab64d097c5b588c
'use strict';
const { Contract} = require('fabric-contract-api');
classtestContractextendsContract{
async queryMarks(ctx,studentId) {
let marksAsBytes = await ctx.stub.getState(studentId);
if (!marksAsBytes || marksAsBytes.toString().length <= 0) {
thrownewError('Student with this Id does not exist: ');
}
let marks=JSON.parse(marksAsBytes.toString());
returnJSON.stringify(marks);
}
async addMarks(ctx,studentId,subject1,subject2,subject3) {
let marks={
subj1:subject1,
subj2:subject2,
subj3:subject3
};
await ctx.stub.putState(studentId,Buffer.from(JSON.stringify(marks)));
console.log('Student Marks added To the ledger Succesfully..');
}
async deleteMarks(ctx,studentId) {
await ctx.stub.deleteState(studentId);
console.log('Student Marks deleted from the ledger Succesfully..');
}
}
module.exports=testContract;
為了安裝和測試此智慧合約,我將使用包含單個peer的基本網路。在這個網路上,我們將安裝名為mycc的Node.js的chaincode到peer0.org1.example.com上,並在通道mychannel上例項化它。然後我們可以呼叫這些chaincode函式。確保在您的設定中安裝了docker。為了簡單起見,我已經將chaincode檔案(logic.js,index.js,package.json)安裝在chaincode/newcc目錄中。
首先,我們需要啟動網路並建立通道。
git clone https://github.com/Salmandabbakuti/hlf-chaincodeTest.git
cd hlf-chaincodeTest/basic-network
./start.sh
等待片刻。建立網路將需要一段時間。如果遇到任何許可權錯誤,只需在root使用者許可權下執行就行。一旦我們的具有單個peer的網路建立並執行,我們就可以安裝chaincode。
為了安裝和呼叫chaincode,我們可以使用Peer的CLI容器。輸入CLI容器
docker exec -it cli bash
安裝和例項化Chaincode
peer chaincode install -n mycc -v 1.0 -p "/opt/gopath/src/github.com/newcc" -l "node"
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n mycc -l "node" -v 1.0 -c '{"Args":[]}'
增加學生標誌
peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n
mycc -c '{"function":"addMarks","Args":["Alice","68","84","89"]}'
查詢學生“Alice”的標誌
peer chaincode query -o orderer.example.com:7050 -C mychannel -n
mycc -c '{"function":"queryMarks","Args":["Alice"]}'
從Ledger刪除“Alice”標誌
peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n
mycc -c '{"function":"deleteMarks","Args":["Alice"]}'
上面的指令碼將從賬本中刪除金鑰“ Alice”和相關資料。如果再次查詢Alice的標記,則會收到一條錯誤訊息,提示“ studentId不存在”。
我還製作了一個自動化指令碼,用於在客戶端目錄中安裝和測試此chaincode。請按照以下步驟進行快速演示
首先,退出CLI容器並在客戶端目錄中執行指令碼
exit# exits from CLI docker container if you're in
cd ..
cd client #change your directory to client
chmod a+x start.sh
./start.sh #Automated script for testing
您也可以使用client / start.sh檔案中定義的指令碼手動呼叫chaincode函式。
結論
我們在這裡演示了什麼是Chaincode,chaincode的stub介面中的方法,chaincode的部署結構以及編寫chaincode和在網路上進行部署的難易程度。希望本文能以某種方式幫助您開始編寫chaincode並在網路上進行部署。