使用 Fabric 完成基础的网络配置和链码的编写和部署后,还需要我们进一步通过上层应用服务,完成对底层Fabric搭建的联盟链的调用。本文提供使用 nodejs 编写的上层 sdk 完成对链码的调用操作。当然,也可以使用 go 语言,异曲同工。enjoy!
1 基础 chaincode 编写
- 在 chaincode 文件夹中创建nodejs环境
# 1.进入 chaincode 目录
cd $GOPATH/src/github.com/hyperledger/fabric/scripts/fabric-samples/chaincode
# 2.创建我们自己的chaincode目录
mkdir helloworldcc
# 3.初始化npm
npm init
# 4.下载fabric 链码的必要依赖
npm install fabric-shim
# 5.编写链码,并保存至 helloworld.js 文件中
# 6.修改 package.json 文件,指定容器启动时候执行的js程序
vim package.json
"scripts": {
"start": "node helloworld.js"
},
- 编写链码 helloworld.js
//导入环境依赖
const shim = require('fabric-shim');
const Chaincode = class{
//链码初始化操作
async Init(stub){
//获取当前方法的名字和参数
var ret = stub.getFunctionAndParameters();
var args = ret.params;
var a = args[0];
var aValue = args[1];
var b = args[2];
var bValue = args[3];
await stub.putState(a,Buffer.from(aValue));
await stub.putState(b,Buffer.from(bValue));
return shim.success(Buffer.from('chaincod init successs'));
}
async Invoke(stub){
let ret = stub.getFunctionAndParameters();
let fcn = this[ret.fcn];
return fcn(stub,ret.params);
}
//查询操作
async query(stub,args){
let a = args[0];
let balance = await stub.getState(a);
return shim.success(balance);
}
};
shim.start(new Chaincode());
2 基础网络环境搭建与链码安装
- 基础网络配置
# 根据fabric提供的基础网络环境配置网络
cd $GOPATH/src/github.com/hyperledger/fabric/scripts/fabric-samples/
cp -r basic-network helloworld-network
- node sdk环境创建
# 创建webapp的工作目录
cd $GOPATH/src/github.com/hyperledger/fabric/scripts/fabric-samples/
mkdir helloworld-webapp
cd helloworld-webapp
# 初始化环境,安装依赖
npm init
npm install fabric-ca-client fabric-client
- 编写cli安装链码脚本
startFabric.sh
#!/bin/bash
set -e
export MSYS_NO_PATHCONV=1
starttime=$(date +%s)
# 启动 helloworld 的网络环境,创建 channel 并将 peer 节点加入网络
cd ../helloworld-network
./start.sh
# 启动 cli 容器,用于安装,实例化链码
docker-compose -f ./docker-compose.yml up -d cli
# 安装链码
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" cli peer chaincode install -l node -n helloworld -v v1.0 -p /opt/gopath/src/github.com/helloworldcc/
# 实例化链码
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" cli peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n helloworld -v v1.0 -c '{"args":["init","yorick","200","tom","100"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
# 等待
sleep 10
# 测试链码调用
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" cli peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n helloworld -c '{"args":["query","yorick"]}'
printf "\nTotal setup execution time : $(($(date +%s) - starttime)) secs ...\n\n\n"
chmod +x startFabric.sh
- 执行脚本,完成网络部署 and 链码安装并调用测试
./startFabric.sh
- result
# 创建 channel 完毕
2022-12-06 11:44:03.909 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-12-06 11:44:03.922 UTC [cli.common] readBlock -> INFO 002 Received block: 0
# peer加入 channel 完毕
2022-12-06 11:44:04.014 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-12-06 11:44:04.090 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
# cli docker容器创建完毕
Creating cli ... done
# 链码安装,实例化,调用
2022-12-06 11:44:05.289 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2022-12-06 11:44:05.289 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2022-12-06 11:44:05.319 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" >
2022-12-06 11:44:05.425 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2022-12-06 11:44:05.425 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
# 成功获取到 yorick 的 200 数据
2022-12-06 11:44:16.778 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 payload:"200"
Total setup execution time : 38 secs ...
3 CA 创建用户
- 编写 node 程序
enrollAdmin.js
,使用 ca 创建admin账户
'use strict';
var Fabric_Client = require('fabric-client');
var Fabric_CA_Client = require('fabric-ca-client');
var path = require('path');
var util = require('util');
var os = require('os');
var fabric_client = new Fabric_Client();
var fabric_ca_client = null;
var admin_user = null;
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log(' Store path:'+store_path);
// 创建 key-value-store,用于保存用户的身份信息(公私钥)
// promise 风格
Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
// 设置存储区块链网络状态的目录路径,比如智能合约的状态和交易的状态
fabric_client.setStateStore(state_store);
var crypto_suite = Fabric_Client.newCryptoSuite();
// 设置存储组织、节点和用户的密钥的目录路径(两个目前使用相同的目录存储)
var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
crypto_suite.setCryptoKeyStore(crypto_store);
fabric_client.setCryptoSuite(crypto_suite);
var tlsOptions = {
trustedRoots: [],
verify: false
};
// be sure to change the http to https when the CA is running TLS enabled
// 设置 ca 客户端
fabric_ca_client = new Fabric_CA_Client('http://localhost:7054', tlsOptions , 'ca.example.com', crypto_suite);
// 首次获取admin账户,查看是否已经登记
return fabric_client.getUserContext('admin', true);
}).then((user_from_store) => {
// 已经登记admin,无需ca再创建
if (user_from_store && user_from_store.isEnrolled()) {
console.log('Successfully loaded admin from persistence');
admin_user = user_from_store;
return null;
} else {
// 需要 ca 服务端完成admin账户创建
// 输入 ca 的id和password,在docker-compose中已经指定
return fabric_ca_client.enroll({
enrollmentID: 'admin',
enrollmentSecret: 'adminpw'
}).then((enrollment) => {
console.log('Successfully enrolled admin user "admin"');
// 创建 admin 账户
return fabric_client.createUser(
{username: 'admin',
mspid: 'Org1MSP',
cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate }
});
}).then((user) => {
// 给 fabric 客户端设置 admin
admin_user = user;
return fabric_client.setUserContext(admin_user);
}).catch((err) => {
console.error('Failed to enroll and persist admin. Error: ' + err.stack ? err.stack : err);
throw new Error('Failed to enroll admin');
});
}
}).then(() => {
console.log('Assigned the admin user to the fabric client ::' + admin_user.toString());
}).catch((err) => {
console.error('Failed to enroll admin: ' + err);
});
- 执行
enrollAdmin.js
程序,得到ca给其办法的证书,hfc-key-store中存有admin用户的公私钥信息
$ node enrollAdmin.js
Store path:/opt/go-project/src/github.com/hyperledger/fabric/scripts/fabric-samples/helloworld-webapp/hfc-key-store
Successfully enrolled admin user "admin"
Assigned the admin user to the fabric client ::{"name":"admin","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"bee3c03e36a357f52a80434a542e8d1e574d576c1f7ad93f95cae78b968ce980","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICATCCAaigAwIBAgIUSQqYMOL+H6y2I8vVFhAOfQ1G2wYwCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjIxMjA2MTE0ODAwWhcNMjMxMjA2MTE1\nMzAwWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAEvkFbiPCqKdeljnWzEuBwVXa87V4l3Dak3mVbyOO6\nQ75OIpdisxulZHxgNmpNYhMyb1pW3sRtqUP/LPXebnq4yKNsMGowDgYDVR0PAQH/\nBAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFB9QLZbsSi5gX4Ew0ctTPN4g\nSxAqMCsGA1UdIwQkMCKAIEI5qg3NdtruuLoM2nAYUdFFBNMarRst3dusalc2Xkl8\nMAoGCCqGSM49BAMCA0cAMEQCIELJMJjIk2Qz19XZh1VmF0tJPshSiU9+KHjAltYq\nOANdAiABN0jioRUQBHlvUNONZP3tm798/Vj7akqs0kHCz1/dzQ==\n-----END CERTIFICATE-----\n"}}}
- 编写 node 程序
registerUser.js
,使用刚刚生成的admin用户,注册user1用户
'use strict';
/*
* Copyright IBM Corp All Rights Reserved
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Register and Enroll a user
*/
var Fabric_Client = require('fabric-client');
var Fabric_CA_Client = require('fabric-ca-client');
var path = require('path');
var util = require('util');
var os = require('os');
//
var fabric_client = new Fabric_Client();
var fabric_ca_client = null;
var admin_user = null;
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log(' Store path:'+store_path);
// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
// assign the store to the fabric client
fabric_client.setStateStore(state_store);
var crypto_suite = Fabric_Client.newCryptoSuite();
// use the same location for the state store (where the users' certificate are kept)
// and the crypto store (where the users' keys are kept)
var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
crypto_suite.setCryptoKeyStore(crypto_store);
fabric_client.setCryptoSuite(crypto_suite);
var tlsOptions = {
trustedRoots: [],
verify: false
};
// be sure to change the http to https when the CA is running TLS enabled
fabric_ca_client = new Fabric_CA_Client('http://localhost:7054', null , '', crypto_suite);
// first check to see if the admin is already enrolled
return fabric_client.getUserContext('admin', true);
}).then((user_from_store) => {
// 创建 user1 普通用户前,必须已经有 admin 账户,否则抛异常
if (user_from_store && user_from_store.isEnrolled()) {
console.log('Successfully loaded admin from persistence');
admin_user = user_from_store;
} else {
throw new Error('Failed to get admin.... run enrollAdmin.js');
}
// at this point we should have the admin user
// first need to register the user with the CA server
return fabric_ca_client.register({enrollmentID: 'user1', affiliation: 'org1.department1'}, admin_user);
}).then((secret) => {
// next we need to enroll the user with CA server
console.log('Successfully registered user1 - secret:'+ secret);
return fabric_ca_client.enroll({enrollmentID: 'user1', enrollmentSecret: secret});
}).then((enrollment) => {
console.log('Successfully enrolled member user "user1" ');
return fabric_client.createUser(
{username: 'user1',
mspid: 'Org1MSP',
cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate }
});
}).then((user) => {
member_user = user;
return fabric_client.setUserContext(member_user);
}).then(()=>{
console.log('User1 was successfully registered and enrolled and is ready to intreact with the fabric network');
}).catch((err) => {
console.error('Failed to register: ' + err);
if(err.toString().indexOf('Authorization') > -1) {
console.error('Authorization failures may be caused by having admin credentials from a previous CA instance.\n' +
'Try again after deleting the contents of the store directory '+store_path);
}
});
- 执行
registerUser.js
程序,hfc-key-store中存有user1用户的公私钥信息
$ node registerUser.js
Store path:/opt/go-project/src/github.com/hyperledger/fabric/scripts/fabric-samples/helloworld-webapp/hfc-key-store
Successfully loaded admin from persistence
Successfully registered user1 - secret:PbjicGKZoMwV
Successfully enrolled member user "user1"
User1 was successfully registered and enrolled and is ready to intreact with the fabric network
- 编写 node 程序
query.js
,调用helloworld链码的query函数,查询“yorick”的值
'use strict';
/*
* Copyright IBM Corp All Rights Reserved
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Chaincode query
*/
var Fabric_Client = require('fabric-client');
var path = require('path');
var util = require('util');
var os = require('os');
//
var fabric_client = new Fabric_Client();
// setup the fabric network
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);
//
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;
// 获取操作链码的用户身份信息
Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
// assign the store to the fabric client
fabric_client.setStateStore(state_store);
var crypto_suite = Fabric_Client.newCryptoSuite();
// use the same location for the state store (where the users' certificate are kept)
// and the crypto store (where the users' keys are kept)
var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
crypto_suite.setCryptoKeyStore(crypto_store);
fabric_client.setCryptoSuite(crypto_suite);
// get the enrolled user from persistence, this user will sign all requests
return fabric_client.getUserContext('user1', true);
}).then((user_from_store) => {
if (user_from_store && user_from_store.isEnrolled()) {
console.log('Successfully loaded user1 from persistence');
member_user = user_from_store;
} else {
throw new Error('Failed to get user1.... run registerUser.js');
}
// 查询请求构建
const request = {
chaincodeId: 'helloworld',
fcn: 'query',
args: ['yorick']
};
// 向peer节点发送请求提案
return channel.queryByChaincode(request);
}).then((query_responses) => {
console.log("Query has completed, checking results");
// query_responses could have more than one results if there multiple peers were used as targets
if (query_responses && query_responses.length == 1) {
if (query_responses[0] instanceof Error) {
console.error("error from query = ", query_responses[0]);
} else {
// 打印查询的结果
console.log("Response is ", query_responses[0].toString());
}
} else {
console.log("No payloads were returned from query");
}
}).catch((err) => {
console.error('Failed to query successfully :: ' + err);
});
- 执行
query.js
程序,使用hfc-key-store中存有user1用户作为身份凭证调用链码,获取yorick的值信息
$ node query.js
Store path:/opt/go-project/src/github.com/hyperledger/fabric/scripts/fabric-samples/helloworld-webapp/hfc-key-store
Successfully loaded user1 from persistence
Query has completed, checking results
Response is 200
至此,完成了通过 node sdk 的方式调用链码。后续可以对sdk进一步封装,对外提供相关访问请求crud接口,供上层服务调用。
网友评论