此实现主要是用Node.js Express 框架搭建了一个简单的浏览器界面,通过调用Fabric Node SDK 来对超级账本1.2版本网络org1 peer0节点进行如发起交易,查询等操作。主要参考深蓝大神的这篇文章:https://www.cnblogs.com/studyzy/p/7524245.html
fabricservice.js代码如下:
此代码主要实现了对chaincode_example02.go的Invoke和Query操作,主要包含invoke,query和delete函数接口的调用以及对通道区块高度的查询。
在实现时,尤其是在实现Invoke操作时,要注意背书策略的制定。因为博主制定的背书策略是AND(‘Org1.member’, ‘Org2.member’)" 因而在通道加peer节点时要把两个组织的节点都加上
var getQueryInfo = function (arg){
'use strict';
var hfc = require('fabric-client');
var path = require('path');
var sdkUtils = require('fabric-client/lib/utils')
var fs = require('fs');
var options = {
user_id: '[email protected]',
msp_id:'Org1MSP',
channel_id: 'mychannel',
chaincode_id: 'mycc',
network_url: 'grpcs://localhost:7051',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
privateKeyFolder:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/keystore',
signedCert:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/signcerts/[email protected]',
tls_cacerts:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt',
server_hostname: "peer0.org1.example.com"
};
var channel = {};
var client = null;
const getKeyFilesInDir = (dir) => {
//该函数用于找到keystore目录下的私钥文件的路径
var files = fs.readdirSync(dir)
var keyFiles = []
files.forEach((file_name) => {
let filePath = path.join(dir, file_name)
if (file_name.endsWith('_sk')) {
keyFiles.push(filePath)
}
})
return keyFiles
}
return Promise.resolve().then(() => {
console.log("Load privateKey and signedCert");
client = new hfc();
var createUserOpt = {
username: options.user_id,
mspid: options.msp_id,
cryptoContent: { privateKey: getKeyFilesInDir(options.privateKeyFolder)[0],
signedCert: options.signedCert }
}
//以上代码指定了当前用户的私钥,证书等基本信息
return sdkUtils.newKeyValueStore({
path: "zhx/project"
}).then((store) => {
client.setStateStore(store)
return client.createUser(createUserOpt)
})
}).then((user) => {
channel = client.newChannel(options.channel_id);
let data = fs.readFileSync(options.tls_cacerts);
let peer = client.newPeer(options.network_url,
{
pem: Buffer.from(data).toString(),
'ssl-target-name-override': options.server_hostname
}
);
peer.setName("peer0");
//因为启用了TLS,所以上面的代码就是指定TLS的CA证书
channel.addPeer(peer);
return;
}).then(() => {
console.log("Make query");
var transaction_id = client.newTransactionID();
console.log("Assigning transaction_id: ", transaction_id._transaction_id);
//构造查询request参数
const request = {
chaincodeId: options.chaincode_id,
txId: transaction_id,
fcn: 'query',
//args:['a']
args:[arg]
};
return channel.queryByChaincode(request);
}).then((query_responses) => {
console.log("returned from query");
if (!query_responses.length) {
console.log("No payloads were returned from query");
} else {
console.log("Query result count = ", query_responses.length)
}
if (query_responses[0] instanceof Error) {
console.error("error from query = ", query_responses[0]);
}
console.log("Response is ", query_responses[0].toString());//打印返回的结果
var query_response = query_responses[0];
return query_response;
}).then((query_response) => {
console.log(query_response.toString())
return query_response;
}).catch((err) => {
console.error("Caught Error", err);
});
}
var getInvokeInfo = function(arg){
'use strict';
var hfc = require('fabric-client');
var path = require('path');
var util = require('util');
var sdkUtils = require('fabric-client/lib/utils')
var fs = require('fs');
var options = {
user_id: '[email protected]',
msp_id:'Org1MSP',
channel_id: 'mychannel',
chaincode_id: 'mycc',
peer1_url: 'grpcs://localhost:7051',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
peer2_url: 'grpcs://localhost:9051',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
event_url: 'grpcs://localhost:7053',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
orderer_url: 'grpcs://localhost:7050',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
privateKeyFolder:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/keystore',
signedCert:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/signcerts/[email protected]',
peer_tls_cacerts1:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt',
peer_tls_cacerts2:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt',
orderer_tls_cacerts:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt',
server_hostname1: "peer0.org1.example.com",//douh hao
server_hostname2: "peer0.org2.example.com"
};
var channel = {};
var client = null;
var targets = [];
var tx_id = null;
const getKeyFilesInDir = (dir) => {
//该函数用于找到keystore目录下的私钥文件的路径
var files = fs.readdirSync(dir)
var keyFiles = []
files.forEach((file_name) => {
let filePath = path.join(dir, file_name)
if (file_name.endsWith('_sk')) {
keyFiles.push(filePath)
}
})
return keyFiles
}
return Promise.resolve().then(() => {
console.log("Load privateKey and signedCert");
client = new hfc();
var createUserOpt = {
username: options.user_id,
mspid: options.msp_id,
cryptoContent: { privateKey: getKeyFilesInDir(options.privateKeyFolder)[0],
signedCert: options.signedCert }
}
//以上代码指定了当前用户的私钥,证书等基本信息
return sdkUtils.newKeyValueStore({
path: "zhx/project"
}).then((store) => {
client.setStateStore(store)
//console.log("zhx_info1");
return client.createUser(createUserOpt)
})
}).then((user) => {
channel = client.newChannel(options.channel_id);
let data = fs.readFileSync(options.peer_tls_cacerts1);
let peer1 = client.newPeer(options.peer1_url,
{
pem: Buffer.from(data).toString(),
'ssl-target-name-override': options.server_hostname1
}
);
channel.addPeer(peer1);
let mdata = fs.readFileSync(options.peer_tls_cacerts2);
let peer2 = client.newPeer(options.peer2_url,
{
pem: Buffer.from(mdata).toString(),
'ssl-target-name-override': options.server_hostname2
}
);
channel.addPeer(peer2);
//因为启用了TLS,所以上面的代码就是指定Peer的TLS的CA证书
//接下来连接Orderer的时候也启用了TLS,也是同样的处理方法
let odata = fs.readFileSync(options.orderer_tls_cacerts);
let caroots = Buffer.from(odata).toString();
var orderer = client.newOrderer(options.orderer_url, {
'pem': caroots,
'ssl-target-name-override': "orderer.example.com"
});
channel.addOrderer(orderer);
targets.push(peer1);
targets.push(peer2);
return;
}).then(() => {
tx_id = client.newTransactionID();
console.log("Assigning transaction_id: ", tx_id._transaction_id);
console.log(arg.func,arg.id_1,arg.id_2,arg.num)
if(arg.func=="invoke"){
var request = {
targets: targets,
chaincodeId: options.chaincode_id,
fcn: 'invoke',
//must but do not know why
args: [arg.id_1,arg.id_2,arg.num],
chainId: options.channel_id,
txId: tx_id
};
}else if(arg.func=="delete"){
var request = {
targets: targets,
chaincodeId: options.chaincode_id,
fcn: 'delete',
args: [arg.id_1],
chainId: options.channel_id,
txId: tx_id
};
}
return channel.sendTransactionProposal(request);
}).then((results) => {
var proposalResponses = results[0];
var proposal = results[1];
var header = results[2];
let isProposalGood = false;
if (proposalResponses && proposalResponses[0].response &&
proposalResponses[0].response.status === 200) {
isProposalGood = true;
console.log('transaction proposal was good');
} else {
console.error('transaction proposal was bad');
}
if (isProposalGood) {
console.log(util.format(
'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s',
proposalResponses[0].response.status, proposalResponses[0].response.message,
proposalResponses[0].response.payload, proposalResponses[0].endorsement.signature));
console.log(proposalResponses[0].response.payload.toString());
//res.writeHead(200, {'Content-Type': 'text/plain'});
//res.end(proposalResponses[0].response.payload.toString());
var request = {
proposalResponses: proposalResponses,
proposal: proposal,
header: header
};
// set the transaction listener and set a timeout of 30sec
// if the transaction did not get committed within the timeout period,
// fail the test
var transactionID = tx_id.getTransactionID();
var eventPromises = [];
let eh = client.newEventHub();
//接下来设置EventHub,用于监听Transaction是否成功写入,这里也是启用了TLS
let data = fs.readFileSync(options.peer_tls_cacerts1);
let grpcOpts = {
pem: Buffer.from(data).toString(),
'ssl-target-name-override': options.server_hostname1
}
eh.setPeerAddr(options.event_url,grpcOpts);
eh.connect();
let txPromise = new Promise((resolve, reject) => {
let handle = setTimeout(() => {
eh.disconnect();
reject();
}, 3000000);
//向EventHub注册事件的处理办法
eh.registerTxEvent(transactionID, (tx, code) => {
clearTimeout(handle);
eh.unregisterTxEvent(transactionID);
eh.disconnect();
if (code !== 'VALID') {
console.error(
'The transaction was invalid, code = ' + code);
reject();
} else {
console.log(
'The transaction has been committed on peer ' +
eh._ep._endpoint.addr);
resolve();
}
});
});
eventPromises.push(txPromise);
var sendPromise = channel.sendTransaction(request);
return Promise.all([sendPromise].concat(eventPromises)).then((results) => {
console.log(' event promise all complete and testing complete');
return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call
}).catch((err) => {
console.error(
'Failed to send transaction and get notifications within the timeout period.'
);
return 'Failed to send transaction and get notifications within the timeout period.';
});
} else {
console.error(
'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...'
);
return 'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...';
}
}, (err) => {
console.error('Failed to send proposal due to error: ' + err.stack ? err.stack :
err);
return 'Failed to send proposal due to error: ' + err.stack ? err.stack :
err;
}).then((response) => {
if (response.status === 'SUCCESS') {
console.log('Successfully sent transaction to the orderer.');
// return tx_id.getTransactionID();
return response
} else {
console.error('Failed to order the transaction. Error code: ' + response.status);
return 'Failed to order the transaction. Error code: ' + response.status;
}
}, (err) => {
console.error('Failed to send transaction due to error: ' + err.stack ? err
.stack : err);
return 'Failed to send transaction due to error: ' + err.stack ? err.stack :
err;
});
}
var getBlockChainInfo = function(){
'use strict';
var hfc = require('fabric-client');
var path = require('path');
var sdkUtils = require('fabric-client/lib/utils')
var fs = require('fs');
var options = {
user_id: '[email protected]',
msp_id:'Org1MSP',
channel_id: 'mychannel',
chaincode_id: 'mycc',
network_url: 'grpcs://localhost:7051',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
privateKeyFolder:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/keystore',
signedCert:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/signcerts/[email protected]',
tls_cacerts:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt',
server_hostname: "peer0.org1.example.com"
};
var channel = {};
var client = null;
const getKeyFilesInDir = (dir) => {
//该函数用于找到keystore目录下的私钥文件的路径
var files = fs.readdirSync(dir)
var keyFiles = []
files.forEach((file_name) => {
let filePath = path.join(dir, file_name)
if (file_name.endsWith('_sk')) {
keyFiles.push(filePath)
}
})
return keyFiles
}
return Promise.resolve().then(() => {
console.log("Load privateKey and signedCert");
client = new hfc();
var createUserOpt = {
username: options.user_id,
mspid: options.msp_id,
cryptoContent: { privateKey: getKeyFilesInDir(options.privateKeyFolder)[0],
signedCert: options.signedCert }
}
//以上代码指定了当前用户的私钥,证书等基本信息
return sdkUtils.newKeyValueStore({
path: "zhx/project"
}).then((store) => {
client.setStateStore(store)
return client.createUser(createUserOpt)
})
}).then((user) =>{
channel = client.newChannel(options.channel_id);
let data = fs.readFileSync(options.tls_cacerts);
let peer = client.newPeer(options.network_url,
{
pem: Buffer.from(data).toString(),
'ssl-target-name-override': options.server_hostname
}
);
peer.setName("peer0");
//因为启用了TLS,所以上面的代码就是指定TLS的CA证书
channel.addPeer(peer);
//console.log("Load111 privateKey and signedCert");
//console.log("Response is ", channel.queryInfo(peer).height.toString());//打印返回的结果
return channel.queryInfo(peer);
//console.log("Response is ", channel.queryInfo(peer));//打印返回的结果
}).then((query_responses) => {
console.log("returned from query");
console.log("Response is ", query_responses.height.toString());
return query_responses;
}).catch((err) => {
console.error("Caught Error", err);
});
}
exports.getBlockChainInfo = getBlockChainInfo;
exports.getInvokeInfo = getInvokeInfo;
exports.getQueryInfo = getQueryInfo;
接下来是浏览器界面:
相对于来说比较简单,读者可以通过调用Hyperledger-Invoke界面实现对网络的Invoke操作,通过Hyperledger-Query界面实现对网络的query操作。通过getchannelheight界面获取当前通道的高度。
var co = require('co');
var fabricservice = require('./fabricservice1.js')
var express = require('express');
var app = express();
app.use(express.static('public'));
app.get('/Hyperledger-Invoke', function (req, res) {
res.sendFile( __dirname + "/" + "index.htm" );
});
app.get('/invoke_get', function (req, res) {
co( function * () {
var func = req.query.func
var Sen_person = req.query.id_1
var Rec_person = req.query.id_2
var Tx_Num = req.query.num
//console.log(func,Sen_person,Rec_person,Tx_Num)
//var blockchaininfo = yield fabricservice.getInvokeInfo(func,Sen_person,Rec_person,Tx_Num);
var blockchaininfo = yield fabricservice.getInvokeInfo(req.query);
res.send( "发送交易成功" );
}).catch((err) => {
res.send(err);
})
});
app.get('/Hyperledger-Query', function (req, res) {
res.sendFile( __dirname + "/" + "index1.htm" );
});
app.get('/Query_get', function (req, res) {
co( function * () {
var blockchaininfo_ = yield fabricservice.getQueryInfo(req.query.id);
var response = {
"查询的值为":blockchaininfo_.toString()
};
res.send(JSON.stringify(response));
}).catch((err) => {
res.send(err);
})
});
//获取当前通道的高度
app.get('/getchannelheight', function (req, res) {
co( function * () {
var blockchaininfo = yield fabricservice.getBlockChainInfo();
res.send( blockchaininfo.height.toString() );
}).catch((err) => {
res.send(err);
})
});
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})
index.htm
Hyperledger-Invoke
发起交易
选择交易的参数:
index1.htm
Hyperledger-Query
问询交易
选择交易的参数: