本文是在閱讀《區(qū)塊鏈開(kāi)發(fā)實(shí)戰(zhàn)-Hyperledger Fabric關(guān)鍵技術(shù)與案例分析》一書(shū)的同時(shí)皿伺,在實(shí)踐中記錄的一些實(shí)踐步驟與經(jīng)驗(yàn)分享。
Hyperledger Fabric開(kāi)發(fā)實(shí)戰(zhàn)-07 開(kāi)發(fā)流程
Fabric的Orderer節(jié)點(diǎn)和Peer節(jié)點(diǎn)都提供了基于Grpc協(xié)議的接口,通過(guò)這些接口可以和這些節(jié)點(diǎn)進(jìn)行交互举庶,但是如果直接使用這些接口,需要自己定義很多protobuf協(xié)議的文件,為了簡(jiǎn)化開(kāi)發(fā)琳轿,F(xiàn)abric提供了Node.js,Go,Java等語(yǔ)言的相關(guān)SDK,方便用戶與相關(guān)節(jié)點(diǎn)進(jìn)行交互耿芹。在項(xiàng)目的開(kāi)發(fā)中崭篡,我們大都通過(guò)調(diào)用Peer模塊的Grpc接口,與系統(tǒng)進(jìn)行交互吧秕。
Fabric Node.js SDK是目前相對(duì)比較完善且成熟的SDK琉闪,因此以Node.js為例進(jìn)行學(xué)## 習(xí)。
環(huán)境準(zhǔn)備
由于需要使用到Node.js砸彬,需要先安裝颠毙,由于我的8.11.1版本在安裝依賴(lài)時(shí)一直報(bào)錯(cuò)斯入,之后重新安裝了8.9.3版本,才能正常使用
wget https://nodejs.org/dist/v8.9.3/node-v8.9.3-linux-x64.tar.xz
tar -xvf node-v8.9.3-linux-x64.tar.xz
sudo mv node-v8.9.3-linux-x64 /usr/local
sudo ln -s /usr/local/node-v8.9.3-linux-x64/bin/node /usr/local/bin/node
sudo ln -s /usr/local/node-v8.9.3-linux-x64/bin/npm /usr/local/bin/npm
驗(yàn)證版本
node -v
npm -v
編寫(xiě)Node.js時(shí)蛀蜜,為了調(diào)試方便刻两,推薦使用Visual Studio Code
。
編寫(xiě)程序
1.安裝依賴(lài)
首先創(chuàng)建一個(gè)目錄sdkstudy
涵防,用來(lái)存放需要源代碼闹伪,之后在里面創(chuàng)建package.json文件,
{
"name": "fabric-sdk-node-study",
"version": "0.0.1",
"description": "fabric-sdk-node-study",
"main": "main.js",
"keywords": [],
"engines": {
"node": ">=8.0.0",
"npm": ">=3.10.10"
},
"dependencies": {
"fabric-ca-client": "^1.0.0",
"fabric-client": "^1.0.0"
},
"license": "Apache-2.0"
}
dependencies
里面是我們需要使用到的sdk壮池,在sdkstudy
中輸入npm install
命令安裝偏瓤。如果出現(xiàn)錯(cuò)誤,請(qǐng)嘗試使用sudo npm install --unsafe-perm
2.編寫(xiě)代碼
在使用sdk時(shí)椰憋,我們首先要?jiǎng)?chuàng)建client對(duì)象厅克,并指定Orderer,Peer和Channel橙依,之后通過(guò)本地證書(shū)或caserver獲取證書(shū)证舟,最后再與Peer進(jìn)行交互。
// import
var co = require("co")
var path = require("path")
var fs = require("fs")
var util = require("util")
var hfclient = require("fabric-client")
var Peer = require("fabric-client/lib/Peer.js");
var EventHub = require("fabric-client/lib/ChannelEventHub");
var User = require("fabric-client/lib/User.js");
var crypto = require("crypto")
var FabricCAService = require("fabric-ca-client")
// 證書(shū)文件的緩存目錄
var tempdir = "/home/shisj/mycode/github_wksp/fabric/nodejs-client/client-study/kvs";
// fabric client agent
var client = new hfclient();
var cryptoSuite = hfclient.newCryptoSuite()
cryptoSuite.setCryptoKeyStore(hfclient.newCryptoKeyStore({path:tempdir}))
client.setCryptoSuite(cryptoSuite);
const ORDERER_IP = "xxx.xx.xxx.xxx";
const PEER_IP = "xx.xxx.xx.xxx";
// 創(chuàng)建CA客戶端
var channel = client.newChannel("cmbcchannel666");
var order = client.newOrderer("grpc://" + ORDERER_IP + ":7050");
channel.addOrderer(order);
var peer = client.newPeer("grpc://"+PEER_IP+":7051");
channel.addPeer(peer);
使用本地證書(shū)
使用本地證書(shū)的時(shí)候窗骑,首先需要獲取Org1組織的Admin的證書(shū)和私鑰女责,再創(chuàng)建用戶
function readAllFiles(dirPath){
var files = fs.readdirSync(dirPath);
var data = fs.readFileSync(path.join(dirPath,files[0]), 'utf8');
return data;
}
/**
* 根據(jù)cryptogen模塊生成的帳號(hào)通過(guò)Fabric接口進(jìn)行相關(guān)的操作
*/
function getOrgUser4Local(){
var keyPath = "/home/shisj/mycode/github_wksp/fabric/nodejs-client/client-study/fabric-config/msp/keystore";
var keyPEM = Buffer.from(readAllFiles(keyPath)).toString()
var certPath = "/home/shisj/mycode/github_wksp/fabric/nodejs-client/client-study/fabric-config/msp/signcerts";
var certPEM = readAllFiles(certPath).toString()
return hfclient.newDefaultKeyValueStore({path:tempdir})
.then((store) =>{
client.setStateStore(store);
return client.createUser({
username:"user87",
mspid:"Org1MSP",
cryptoContent:{
privateKeyPEM:keyPEM,
signedCertPEM:certPEM
}
})
})
}
最后,就可以連接到Peer查看賬本的相關(guān)信息了
co((function *(){
let member = yield getOrgUser4Local()
let resultPeerInfo = yield channel.queryInfo(peer)
console.info("============當(dāng)前peer加入的某個(gè)channel的區(qū)塊數(shù)============");
console.info(JSON.stringify(resultPeerInfo))
console.info("============當(dāng)前peer加入了那些channel============");
var result = yield client.queryChannels(peer)
console.info(JSON.stringify(result))
//console.info("============當(dāng)前peer加入的某個(gè)channel的區(qū)塊數(shù)的區(qū)塊號(hào)============");
//result = yield client.queryChannels(2,peer,null)
//console.info(JSON.stringify(result))
console.info("============當(dāng)前peer加入chaincode[install]的信息============");
result = yield client.queryInstalledChaincodes(peer)
var chaincodes = result.chaincodes;
chaincodes.forEach(element => {
console.info("chaincode name is %s, version is %s ",element.name,element.version);
});
// console.info(JSON.stringify(result))
})())
與Peer通信時(shí)创译,還可以調(diào)用Chaincode
let tx_id = client.newTransactionID();
var request = {
targets: peer,
chaincodeId:"statechaincode",
fcn:"invoke",
args:["a","b","1"],
chainId:"cmbcchannel666",
txId:tx_id
};
//
let chaincodeInvokeResult = yield channel.sendTransactionProposal(request);