Fabric实战(11)SDK编程接口

本文章所有操作基于的操作系统版本是:ubuntu16.04 64位
本文章基于的Fabric网络环境是《Fabric实战(2)运行一个简单的fabric网络(容器外)》

1 Fabric接口协议和功能

Fabric的Peer节点和Orderer节点都提供了基于Grpc协议的通信接口,通过这些接口可以和Peer以及Orderer节点进行交互。

1.1Grpc协议简介

Grpc是由Google开发的一款语言中立、平台中立、开源的远程过程调用RPC系统。目前Grpc支持JAVA、GO、C、C++、Node.js、Python、Ruby、Objective-C、PHP、C#等语言。Grpc默认使用protocol buffers作为接口定义语言来描述服务接口和消息结构。

1.2 Fabric的Grpc接口

Fabric一共有5个模块(peer、orderer、cryptogen、configtxgen、configtxlator),其中Peer模块和Orderer模块提供了Grpc相关的接口,但是Orderer模块是Peer模块等调用的,在项目开发中并不需要直接调用orderer模块的GRpc接口,所以这里主要介绍Peer模块的Grpc接口。另外fabric-ca-server项目也是提供了JsonRPC接口共第三方应用程序调用,由于fabric-ca-server在实际项目开发中是不可或缺的,所以这里也会介绍Fabric-ca-server的grpc接口调用方式。

1.3 Peer模块的接口功能

Fabric的Peer模块提供的Grpc接口按照功能大致可以分为两类:系统管理和chaincode操作。

1.3.1 系统管理

Peer模块提供的Grpc接口提供了如下与系统管理相关的功能:

  • 获取当前Peer加入了哪些些Channel
  • 获取当前Peer加入的某个channel的区块数量
  • 通过区块号获取当前的Peer加入的某个Channel的某个区块的详细信息
  • 通过区块hash值获取当前的Peer加入的某个Channel的某个区块的详细信息
  • 通过交易的hash值获取交易的详细信息
  • 获取当前peer服务器中状态为install的chaincode信息
  • 获取当前peer加入的某个channel状态为Instantiate的chaincode的详细信息

1.3.2 chaincode相关操作

Peer模块提供的Grpc接口提供了如下与chaincode相关的功能:

  • 调用chaincode的query方法
  • 调用一个已经部署好的chaincode的invoke方法

2 Fabric Node SDK

2.1 Fabric SDK 安装

2.1.1安装node和npm

必须要安装8.9.x或者更高版本
下载二进制包:https://nodejs.org/dist/latest-v10.x/node-v10.13.0-linux-x64.tar.gz

#解压二进制包
tar zvxf node-v10.13.0-linux-x64.tar.gz -C 

#将可执行文件目录加入环境变量
echo "export PATH=$PATH:$HOME/node-v10.13.0-linux-x64/bin" >> ~/.bashrc

#验证是否安装成功
node -v
npm -v

#安装co库
npm install co

2.1.2安装node sdk

创建一个工程目录,并切换到目录下:

mkdir -p ~/fabric-ws/fabric-node-sdk/node-test

cd ~/fabric-ws/fabric-node-sdk/node-test

编辑一个npm的配置文件,如下:

{
    "name": "node-sdk-study",
    "version": "1.0.0",
    "description": "Hyperledger Fabric Node SDK Test Application",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "dependencies": {
        "fabric-ca-client": "^1.0.0",
        "fabric-client": "^1.0.0"
    },
    "author": "Your name",
    "license": "Apache-2.0",
    "keywords": [
        "Hyperledger",
        "Fabric",
        "Test",
        "Application"
    ]
}

下载依赖模块:

编辑保存好该文件后,我们就可以运行npm install命令来下载所有相关的依赖模块,但是由于npm服务器在国外,所以下载可能会很慢,感谢淘宝为我们提供了国内的npm镜像,使得安装npm模块快很多

运行命令:

npm install --registry=https://registry.npm.taobao.org

运行完毕后我们查看一下nodeTest目录,可以看到多了一个node_modules文件夹。这里就是使用刚才的命令下载下来的所有依赖包。

2.2 编写一个APP

编写一个通过Nodejs sdk调用fabric的例子。

'use strict';

var co = require('co');
var hfc = require('fabric-client'); 
var path = require('path'); 
var sdkUtils = require('fabric-client/lib/utils');
var fs = require('fs'); 
var FabricCAService = require('fabric-ca-client');
var User = require('fabric-client/lib/User.js');
var options = { 
    channel_id: 'testchannel', 
    chaincode_id: 'r_test_cc6', 
};


var tempdir = "/home/zym/fabric-ws/simple-demo/client-kvs"

var client = new hfc();

var cryptoSuite = hfc.newCryptoSuite();
cryptoSuite.setCryptoKeyStore(hfc.newCryptoKeyStore({path:tempdir}));
client.setCryptoSuite(cryptoSuite);

//创建CA客户端
var caClient = new FabricCAService('http://0.0.0.0:7054', null, '', cryptoSuite)

//创建通道客户端代理, 通道名称为:testchannel
var channel = client.newChannel('testchannel');

//创建order,并将order加入到channel中
var order = client.newOrderer('grpc://orderer.simple-network.com:7050');
channel.addOrderer(order);

//创建peer节点的客户端代理
var peer = client.newPeer('grpc://peer0.org1.simple-network.com:7051');
channel.addPeer(peer);

var peer2 = client.newPeer('grpc://peer0.org2.simple-network.com:7061');
channel.addPeer(peer2);

function 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 
}

function getOrgUserLocal(){

    	var user_id = '[email protected]';
    	var msp_id = 'Org1MSP';
    	var privateKey = '/home/zym/fabric-ws/simple-demo/crypto-config/peerOrganizations/org1.simple-network.com/users/[email protected]/msp/keystore/882aff48d7e4fabb31b756dcac1aaa48326ddbd3a0d2424918c64ccefbe2351f_sk';
    	var signedCert = '/home/zym/fabric-ws/simple-demo/crypto-config/peerOrganizations/org1.simple-network.com/users/[email protected]/msp/signcerts/[email protected]';


	return hfc.newDefaultKeyValueStore({path:tempdir}).then((store) => {
					client.setStateStore(store);
					return client.createUser({
						username:user_id,
						mspid:msp_id,
						cryptoContent:{
							privateKey: privateKey,
							signedCert: signedCert
						}
					});
				});
};

function getUserCa(username, password){
	var member
	return hfc.newDefaultKeyValueStore({path:tempdir}).then((store)=>{
		client.setStateStore(store);
		client._userContext = null;
		return client.getUserContext(username, true).then((user)=>{
			if (user && user.isEnrolled()){
				console.info('success enroll admin');
				return user;
			}else{
				return caClient.enroll({enrollmentID:username, enrollmentSecret:password}).then((enrollment)=>{
					console.info('successfully enrolled user\''+ username + '\'');
					member = new User(username);
					member.setCryptoSuite(client.getCryptoSuite());
					return member.setEnrollment(enrollment.key, enrollment.certificate, 'Org1MSP');

				}).then(()=>{
					return  client.setUserContext(member)
				}).then(()=>{
					return member;
				}).catch((err)=>{
					console.error('enroll admin err' + err.stack);
					return null
				})
			}
		})
	})
}

co(
(function*(){

	let member = yield getOrgUserLocal();
	//请先启动fabric ca服务器,并使用admin账号注册userttest账号
	let member = yield getUserCa("usertest", "userwd")

	//获取当前通道的账本信息
	let result = yield channel.queryInfo(peer);
	console.info(JSON.stringify(result))
})()
)

2.3 运行APP

node query.js

输出:

{
	"height": {
		"low": 133,
		"high": 0,
		"unsigned": true
	},
	"currentBlockHash": {
		"buffer": {
			"type": "Buffer",
			"data": [8, 133, 1, 18, 32, 93, 222, 144, 190, 178, 147, 223, 86, 197, 50, 152, 157, 114, 12, 23, 83, 28, 78, 96, 232, 45, 2, 24, 130, 120, 246, 84, 192, 47, 15, 211, 128, 26, 32, 126, 82, 175, 9, 215, 58, 226, 121, 163, 12, 109, 243, 35, 247, 254, 237, 252, 207, 7, 89, 101, 207, 60, 182, 46, 174, 141, 70, 121, 176, 32, 214]
		},
		"offset": 5,
		"markedOffset": -1,
		"limit": 37,
		"littleEndian": true,
		"noAssert": false
	},
	"previousBlockHash": {
		"buffer": {
			"type": "Buffer",
			"data": [8, 133, 1, 18, 32, 93, 222, 144, 190, 178, 147, 223, 86, 197, 50, 152, 157, 114, 12, 23, 83, 28, 78, 96, 232, 45, 2, 24, 130, 120, 246, 84, 192, 47, 15, 211, 128, 26, 32, 126, 82, 175, 9, 215, 58, 226, 121, 163, 12, 109, 243, 35, 247, 254, 237, 252, 207, 7, 89, 101, 207, 60, 182, 46, 174, 141, 70, 121, 176, 32, 214]
		},
		"offset": 39,
		"markedOffset": -1,
		"limit": 71,
		"littleEndian": true,
		"noAssert": false
	}
}

2.3 NodeJS SDK常用方法

以下实例代码只能在co((function(){})())函数中进行。*

2.3.1 系统管理

2.3.1.1 获取当前Peer加入了哪些channel

let resultChannels = yield client.queryChannels(peer) 

console.info(JSON.stringify(resultChannels, null, 4))

输出:

{
    "channels": [
        {
            "channel_id": "testchannel"
        }
    ]
}

2.3.1.2 获取当前Peer加入的channel的账本信息

let result = yield channel.queryInfo(peer);
console.info(JSON.stringify(result, null, 4))

2.3.1.3 通过区块号获取区块的详细信息

 let resultBlockInfo = yield channel.queryBlock(1, peer, null)
 console.info(JSON.stringify(resultBlockInfo, null, 4))

2.3.1.4 通过区块hash获取区块的详细信息

let resultBlockInfo = yield channel.queryBlockByHash("hashcode", pe    er)
console.info(JSON.stringify(resultBlockInfo))

2.3.1.5 通过交易的hash获取交易的详细信息

let resultTxInfo = yield channel.queryTransaction("a113aa4f1ab582bd3    54212fa70092b255919ccdd60e0e87e477b355ad6b24ec0", peer)
console.info(JSON.stringify(resultTxInfo))

2.3.1.6 查看当前Peer服务器中状态为install的chaincode信息

let resultChaincodes = yield client.queryInstalledChaincodes(peer)
console.info(JSON.stringify(resultChaincodes))

2.3.1.7 查看当前Peer服务器中状态为Instantiate的chaincode信息

let resultChaincodes = yield channel.queryInstantiatedChaincodes(peer)
console.info(JSON.stringify(resultChaincodes))

2.3.2 channelcode相关操作

2.3.2.1 调用chaincode的query方法

let tx_id = client.newTransactionID();
	var request = {
		chaincodeId: "r_test_cc6",
		txId: tx_id,
		fcn: "query",
		args:["a"]
	};
let resultQuery = yield channel.queryByChaincode(request, peer);
console.info(JSON.stringify(resultQuery))

2.3.2.2 调用chaincode的invoke方法

// 发送提案,前面channel.addPeer(peer)添加几个节点,就会向几个节点发送,所以应该根据背书策略,只向背书节点发送。
let tx_id = client.newTransactionID();
	var request = {
		chaincodeId: "r_test_cc9",
		txId: tx_id,
		fcn: "invoke",
		args:["a", "b", "1"]
	};
	let chaincodeInvokeResult = yield channel.sendTransactionProposal(request);

	var proposalResponses = chaincodeInvokeResult[0]
	var proposal = chaincodeInvokeResult[1]
	var header = chaincodeInvokeResult[2]

	var all_good = true

    //背书策略判断
	for (var i in proposalResponses){
		
		//console.info(JSON.stringify(proposalResponses))
		let one_good = false;
		if (proposalResponses && proposalResponses[i].response && proposalResponses[i].response.status === 200){
			one_good = true;
			console.info('transaction proposal was good')
		}else{
			console.info('transaction proposal was bad')
		}
		all_good = all_good & one_good;
	}
	
	//如果背书成功,则将提案封装成交易发送给order节点
	if (all_good){

		var request = {
			proposalResponses:proposalResponses,
			proposal : proposal,
			header: header
		};
		var transactionID = tx_id.getTransactionID();
		var sendPromise = yield channel.sendTransaction(request);
	}
})()

你可能感兴趣的:(Fabric)