使用Node SDK 简单实现超级账本 Hyperledger 浏览器

4 篇文章 0 订阅
1 篇文章 0 订阅

此实现主要是用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: 'Admin@org1.example.com', 
    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/Admin@org1.example.com/msp/keystore', 
    signedCert:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem', 
    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: 'Admin@org1.example.com',
    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/Admin@org1.example.com/msp/keystore', 
    signedCert:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem',
    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: 'Admin@org1.example.com', 
    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/Admin@org1.example.com/msp/keystore', 
    signedCert:'/home/jinhongjian/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem', 
    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

<html>
<head>

<title>Hyperledger-Invoke</title>

</head>

<body>

<div id="header" >
<h1 style="margin-bottom:0;">发起交易</h1></div>

<h3>选择交易的参数: </h3> 

<form action="http://223.129.0.191:8081/invoke_get" method="GET">
func: <input type="text" name="func">  <br>
Sen: <input type="text" name="id_1">  <br>
Rec: <input type="text" name="id_2">  <br>
Num: <input type="text" name="num">  <br><br>
<input type="submit" value="上传参数" />
</form>

</body>
</html>

index1.htm

<html>
<head>

<title>Hyperledger-Query</title>

</head>

<body>

<div id="header" >
<h1 style="margin-bottom:0;">问询交易</h1></div>

<h3>选择交易的参数: </h3> 

<form action="http://223.129.0.191:8081/Query_get" method="GET">
id: <input type="text" name="id">   <br><br>
<input type="submit" value="上传参数" />
</form>

</body>
</html>

实现的效果:
1.查询通道区块高度
查询
2.Invoke操作
在这里插入图片描述
在这里插入图片描述
3.query操作
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值