搭建基于hyperledger fabric的联盟社区(六) --搭建node.js服务器
接下来我要做的是用fabric sdk来做出应用程序,代替CLI与整个区块链网络交互。并且实现一个http API,向社区提供一个简单的接口,使社区轻松的与区块链交互。
官方虽然提供了Node.JS,Java,Go(最近刚出了python)等多种语言的SDK,但是很多SDK还不成熟和完善,有的甚至文档都没有。我使用Node.js的原因有三。1.官方例子使用的是Node.js SDK 2.以前我做以太坊智能合约开发的时候也用过Node.js 3.最后是因为Node.js实现一个http服务器是非常简单的。
我们可以选择将node.js安装在本地(1.1介绍),或者将node应用部署在docker。需要注意的是fabric目前不支持node7.x版本,需要6.9.x或更高版本和NPM。
本地安装node的话我们只需要执行以下命令即可安装NodeJS的最新v6版本:
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install -y nodejs
国内使用NPM会遇到很多问题,可用淘宝NPM镜像:
npm install -g cnpm --registry=https://registry.npm.taobao.org
使用方法是用cnpm代替npm
而我这里主要介绍的方法是将node.js部署在docker。
一. 先拉取基础镜像
sudo docker pull node:6.11.3
再提醒一遍fabric目前不支持node7.x版本,需要6.9.x或更高版本。这一步,也可以省略,后面的Dockerfile文件,会自动拉取该镜像。
二.创建node.js程序
2.1创建 package.json,并写入相关信息和依赖
mkdir -p node/mynodeapp && cd node/mynodeapp
touch package.json
vi package.json
{
"name": "nodeTest",
"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": "Devin Zeng",
"license": "Apache-2.0",
"keywords": [
"Hyperledger",
"Fabric",
"Test",
"Application"
]
}
2.2 创建server.js
将与fabric交互的代码放在这里,监听8888端口。
touch server.js
vi server.js
由于我们这次是为peer0.org2开发API,所以本机IP地址为10.0.2.12,代码如下
var http = require('http');
var url = require('url'); http.createServer(function(req, res){
var arg = url.parse(req.url, true).query; //方法二arg => { aa: '001', bb: '002' }
console.log(arg.func);//返回001
if (arg.func == "queryPost" || arg.func == "richQueryPosts" || arg.func == "getPostNum"){
query(arg);
}else if (arg.func == "addPost" || arg.func == "updatePost"){
invoke(arg);
}
}).listen(8888);//建立服务器并监听端口 console.log('Server running at http://127.0.0.1:8888/'); function query(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@org2.example.com',
msp_id:'Org2MSP',
channel_id: 'mychannel',
chaincode_id: 'mycc',
network_url: 'grpcs://10.0.2.12:7051',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
privateKeyFolder: path.join(__dirname,'./crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore'),
signedCert: path.join(__dirname,'./crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem'),
tls_cacerts:path.join(__dirname,'./crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt'),
server_hostname: "peer0.org2.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
}
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: "/tmp/fabric-client-stateStore/"
}).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参数
if(arg.func=="queryPost"){
const request = {
chaincodeId: options.chaincode_id,
txId: transaction_id,
fcn: 'queryPost',
args: ["POST"+arg.id]
};
}else if(arg.func=="richQueryPosts"){
const request = {
chaincodeId: options.chaincode_id,
txId: transaction_id,
fcn: 'richQueryPosts',
args: [arg.attribute,arg.operator,arg.value]
};
}else if(arg.func=="getPostNum"){
const request = {
chaincodeId: options.chaincode_id,
txId: transaction_id,
fcn: 'getPostNum',
args: [arg.attribute,arg.operator,arg.value]
};
}
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]);
}
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(query_responses[0]);
console.log("Response is ", query_responses[0].toString());//打印返回的结果
}).catch((err) => {
console.error("Caught Error", err);
});
} function invoke(arg){
'use strict'; var hfc = require('fabric-client');
var path = require('path');
var util = require('util');
var sdkUtils = require('fabric-client/lib/utils')
const fs = require('fs');
var options = {
user_id: 'Admin@org2.example.com',
msp_id:'Org2MSP',
channel_id: 'mychannel',
chaincode_id: 'mycc',
peer_url: 'grpcs://10.0.2.12:7051',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
event_url: 'grpcs://10.0.2.12:7053',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
orderer_url: 'grpcs://10.0.2.10:7050',//因为启用了TLS,所以是grpcs,如果没有启用TLS,那么就是grpc
privateKeyFolder: path.join(__dirname,'./crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore'),
signedCert:path.join(__dirname,'./crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem'),
peer_tls_cacerts: path.join(__dirname,'./crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt'),
orderer_tls_cacerts:path.join(__dirname,'./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt'),
server_hostname: "peer0.org2.example.com"
}; var channel = {};
var client = null;
var targets = [];
var tx_id = null;
const getKeyFilesInDir = (dir) => {
//该函数用于找到keystore目录下的私钥文件的路径
const files = fs.readdirSync(dir)
const keyFiles = []
files.forEach((file_name) => {
let filePath = path.join(dir, file_name)
if (file_name.endsWith('_sk')) {
keyFiles.push(filePath)
}
})
return keyFiles
}
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: "/tmp/fabric-client-stateStore/"
}).then((store) => {
client.setStateStore(store)
return client.createUser(createUserOpt)
})
}).then((user) => {
channel = client.newChannel(options.channel_id);
let data = fs.readFileSync(options.peer_tls_cacerts);
let peer = client.newPeer(options.peer_url,
{
pem: Buffer.from(data).toString(),
'ssl-target-name-override': options.server_hostname
}
);
//因为启用了TLS,所以上面的代码就是指定Peer的TLS的CA证书
channel.addPeer(peer);
//接下来连接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(peer);
return;
}).then(() => {
tx_id = client.newTransactionID();
console.log("Assigning transaction_id: ", tx_id._transaction_id);
if(arg.func=="addPost"){
var request = {
targets: targets,
chaincodeId: options.chaincode_id,
fcn: 'addPost',
args: [arg.originalwebsite,arg.originalid,arg.title,arg.content,arg.authorid,arg.publishtime,arg.updatetime,arg.category,arg.sourceid,arg.labels,arg.follower_num,arg.browse_num,arg.star_num],
chainId: options.channel_id,
txId: tx_id
};
}else if(arg.func=="updatePost"){
var request = {
targets: targets,
chaincodeId: options.chaincode_id,
fcn: 'updatePost',
args: [arg.id,arg.originalwebsite,arg.originalid,arg.title,arg.content,arg.authorid,arg.publishtime,arg.updatetime,arg.category,arg.sourceid,arg.labels,arg.follower_num,arg.browse_num,arg.star_num],
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_cacerts);
let grpcOpts = {
pem: Buffer.from(data).toString(),
'ssl-target-name-override': options.server_hostname
}
eh.setPeerAddr(options.event_url,grpcOpts);
eh.connect(); let txPromise = new Promise((resolve, reject) => {
let handle = setTimeout(() => {
eh.disconnect();
reject();
}, 30000);
//向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();
} 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;
});
}
三.创建Dockerfile
Docker会依照Dockerfile的内容来构建一个镜像。
cd ..
touch Dockerfile
vi Dockerfile
#设置基础镜像,如果本地没有该镜像,会从Docker.io服务器pull镜像
FROM node:6.11.3 #创建app目录,保存我们的代码
RUN mkdir -p /usr/src/node
#设置工作目录
WORKDIR /usr/src/node #复制所有文件到 工作目录。
COPY . /usr/src/node #编译运行node项目,使用npm安装程序的所有依赖,利用taobao的npm安装 WORKDIR /usr/src/node/mynodeapp
RUN npm install --registry=https://registry.npm.taobao.org #暴露container的端口
EXPOSE 8888 #运行命令
CMD ["node", "server.js"]
四.构建image
在Dockerfile文件所在目录下,运行下面命令来构建一个Image
sudo docker build -t fabric/node .
最后提示
Removing intetmediate container <临时容器ID>
Successfully built <镜像ID>
就算构建成功了。(这一步时常失败,要看网络状况)
构建完后查看一下刚构建的镜像:
sudo docker images
多出来了一个fabric/node镜像。
五.运行镜像
sudo docker run -d --name nodeapp -p : fabric/node:latest
-d 表示容器在后台运行
--name 表示给容器别名 nodeapp
-p 表示端口映射。把本机的8888端口映射到容器的8888端口,这样外网就能通过本机的8888端口,访问我们的web了。
后面的 fabric/node 是image的REPOSITORY, latest的镜像的TAG
六.测试
我使用python写了一个简单的发送和接受http请求的程序,当然用插件发送请求来测试会更加便捷(推荐使用postman,十分便捷)。代码如下:
import urllib.request url = "http://localhost:8888/select?func=richQueryPosts&attribute=title&operator=0&value=d" req = urllib.request.Request(url)
print(req) res_data = urllib.request.urlopen(req)
res = res_data.read()
print (res)
成功接收到包含对应帖子信息的response就说明node部署成功了。
之后对应的社区就可以调用HTTP API来完成社区和区块链的对接了!整个基于hyperledger fabric的社区联盟开发的内容就到这里了。
搭建基于hyperledger fabric的联盟社区(六) --搭建node.js服务器的更多相关文章
- 搭建基于hyperledger fabric的联盟社区(一) --前言
三个月前上面发了一个关于智群汇聚和问题求解研究的项目,我们公司做其中的一个子项目,就是基于区块链的科技信息联盟构建.利用区块链的去中心化特性,构建一个基于区块链的科技社区,以提供科技群智汇聚采集的基础 ...
- 搭建基于hyperledger fabric的联盟社区(五) --启动Fabric网络
现在所有的文件都已经准备完毕,我们可以启动fabric网络了. 一.启动orderer节点 在orderer服务器上运行: cd ~/go/src/github.com/hyperledger/fab ...
- 搭建基于hyperledger fabric的联盟社区(四) --chaincode开发
前几章已经分别把三台虚拟机环境和配置文件准备好了,在启动fabric网络之前我们要准备好写好的chaincode.chaincode的开发一般是使用GO或者JAVA,而我选择的是GO语言.先分析一下官 ...
- 搭建基于hyperledger fabric的联盟社区(七) --升级chaincode
上个版本的chaincode有很多功能不完备,所以要部署新版本的chaincode.Fabric支持在保留现有状态的前提对chaincode进行升级. 一.新版chaincode 新版本的chainc ...
- 搭建基于hyperledger fabric的联盟社区(三) --生成公私钥证书及配置文件
一.生成公私钥和证书 Fabric中有两种类型的公私钥和证书,一种是给节点之前通讯安全而准备的TLS证书,另一种是用户登录和权限控制的用户证书.这些证书本来应该是由CA来颁发,但是目前只有两个社区,所 ...
- 搭建基于hyperledger fabric的联盟社区(二) --环境配置
接下来讲一下在本地测试区块链网络的过程.我要部署的是2peer+1orderer架构,所以需要准备三台虚拟机,为了方便起见可以先把一台配置好,然后复制出剩余两台即可.搭建虚拟机我用的是virtualb ...
- 搭建基于hyperledger fabric的联盟社区(八) --Fabric证书解析
一.证书目录解析 通过cryptogen生成所有证书文件后,以peerOrgannizations的第一个组织树org1为例,每个目录和对应文件的功能如下: ca: 存放组织的根证书和对应的私 ...
- 搭建基于hyperledger fabric的联盟社区(九) --检索状态数据库
一.启动elasticsearch服务 官网下载压缩包解压,进入bin目录启动: ./elasticsearch 通过ip访问 localhost:9200,可以看到如下信息 { name: &quo ...
- 搭建一个简单的node.js服务器
第一步:安装node.js.可以去官网:https://nodejs.org/en/进行下载. 查看是否成功,只需在控制台输入 node -v.出现版本号的话,就证明成功了. 第二步:编写node.j ...
随机推荐
- Python批量修改图片格式和尺寸
Python批量修改图片格式和尺寸 备注: 1.导入了PIL库,是处理图片用的,很强大; 2.导入了的win32库,是判断隐藏文件用的,我们的项目需要删除隐藏文件,不需要的可以直接找到删除. 3.导入 ...
- Idea_01_安装与激活
一.前言 二.安装 1.下载 https://www.jetbrains.com/idea/ 2.安装 默认安装即可 三.激活 Idea激活有如下两种方式 Activation code Lisenc ...
- javascript 事件委托 event delegation
事件委托 event delegation 一.概念: 假设我们有很多个子元素,每个元素被点击时都会触发相应事件,普通的做法是给每个子元素添加一个事件监听. 而,事件委托则是给它们的父元素添加一个事件 ...
- ide 下spingboot 实现热部署
需要从maven中下载devtools插件pom.xml:step1:修改pom.xml<dependencies><dependency><groupId>org ...
- python 中 property 属性的讲解及应用
Python中property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回 property属性的有两种方式: 1. 装饰器 即:在方法上应用装饰器 2. 类属性 即 ...
- 20165202 预备作业3 Linux安装及学习
一.虚拟机安装 娄老师的<基于VirtualBox安装Ubuntu图文教程>对于安装过程的介绍很易懂,但在安装过程中还是遇到了一些问题 Q1:安装教程中下载地址的VM提示安装包损坏 解决办 ...
- Linux文件操作及管理
---恢复内容开始--- 一.Linux系统的结构 1.Linux是一个倒树型结构,最大的目录名称为“/”(根目录) 2.Linux系统的二级目录 /bin ##binary二进制可执行文件, ...
- java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\xB3' for column 'Content' at row 1
在尝试将 emoji 表情符号 插入MySQL数据库时,遇到以下错误信息: ### The error occurred while setting parameters ### SQL: INSER ...
- Android 编程 高德地图 (实现显示地图以及定位功能)
本文参考文章: http://www.apkbus.com/blog-904057-63610.html 本人实现的 定位代码:(具体配置省略,可见参考文章) package com.example. ...
- weight decay (权值衰减)
http://blog.sina.com.cn/s/blog_890c6aa30100z7su.html 在机器学习或者模式识别中,会出现overfitting,而当网络逐渐overfitting时网 ...