接下来我们开始对chaincode开发者模式的学习
1.创建学习目录
mkdir $GOPATH/src/github.com/fabric-study
cd $GOPATH/src/github.com/fabric-study
2.将fabric-samples开发模式模块代码复制到学习模块(chaincode-docker-devmode目录在fabric-samples项目下,根据自己的目录路径复制到fabric-study
cp -r $GOPATH/src/github.com/hyperledger/fabric-samples/chaincode-docker-devmode ./
3.创建自己的chaincode目录(fabric-study目录下)
mkdir -p chaincode/javascript-low-level
4.接下来我们开始编写我们node.js的模块描述文件,package.json(代码如下)
{
"name": "example",
"version": "1.0.0",
"description": "exampe chaincode implemented in node.js",
"engines": {
"node": ">=8.4.0",
"npm": ">=5.3.0"
},
"scripts": {
"start": "node first.js --peer.address grpc://localhost:7052 "
},
"engine-strict": true,
"license": "Apache-2.0",
"dependencies": {
"fabric-shim": "^2.0.0"
}
}
5.我们新建example.js文件,编写我们的chaincode代码(代码如下)
const shim = require('fabric-shim');
const util = require('util');
var Chaincode = class {
//这里写各种函数
// 实例化链码 peer chaincode instantiate
async Init(stub) {
console.info('========= 实例化链码 =========');
let ret = stub.getFunctionAndParameters();
console.info(ret);
let args = ret.params;
// 实例化只能传4个参数进来
if (args.length != 4) {
return shim.error('不正确的参数个数,期望4个参数');
}
let A = args[0];
let B = args[2];
let Aval = args[1];
let Bval = args[3];
if (typeof parseInt(Aval) !== 'number' || typeof parseInt(Bval) !== 'number') {
return shim.error('资产值必须是一个整型数');
}
try {
await stub.putState(A, Buffer.from(Aval));
try {
await stub.putState(B, Buffer.from(Bval));
return shim.success();
} catch (err) {
return shim.error(err);
}
} catch (err) {
return shim.error(err);
}
}
//调用链码,对应peer node invoke
async Invoke(stub) {
let ret = stub.getFunctionAndParameters();
console.info(ret);
let method = this[ret.fcn];//得到要调用的参数
if (!method) {
console.log('函数:' + ret.fcn + ' 未找到');
return shim.success();
}
try {
//与golang不同,这里不需要去if-else判断
let payload = await method(stub, ret.params);
return shim.success(payload);
} catch (err) {
console.log(err);
return shim.error(err);
}
}
//转账
async transfer(stub, args) {
if (args.length != 3) {
throw new Error('不正确的参数个数,期望3个参数');
}
let A = args[0];
let B = args[1];
if (!A || !B) {
throw new Error('资产数不能为空的');
}
// 从账本中获取状态
let Avalbytes = await stub.getState(A);
if (!Avalbytes) {
throw new Error('从资产持有者'+A+'获取状态失败');
}
let Aval = parseInt(Avalbytes.toString());
let Bvalbytes = await stub.getState(B);
if (!Bvalbytes) {
throw new Error('从资产持有者'+B+'获取状态失败');
}
let Bval = parseInt(Bvalbytes.toString());
// Perform the execution
let amount = parseInt(args[2]);
if (typeof amount !== 'number') {
throw new Error('amount必须是一个整型值');
}
if (Aval < amount) {
throw new Error('余额不足');
}
Aval = Aval - amount;
Bval = Bval + amount;
console.info(util.format('Aval = %d, Bval = %d\n', Aval, Bval));
// Write the states back to the ledger
await stub.putState(A, Buffer.from(Aval.toString()));
await stub.putState(B, Buffer.from(Bval.toString()));
}
// 删除账户实体
async delete(stub, args) {
if (args.length != 1) {
throw new Error('不正确的参数个数,期望1个参数');
}
let A = args[0];
// Delete the key from the state in ledger
await stub.deleteState(A);
}
// 查询账户的资产
async query(stub, args) {
if (args.length != 1) {
throw new Error('不正确的参数个数,期望1个参数,该参数是账户实体名')
}
let jsonResp = {};
let A = args[0];
// Get the state from the ledger
let Avalbytes = await stub.getState(A);
if (!Avalbytes) {
jsonResp.error = '从资产持有者'+A+'获取状态失败';
throw new Error(JSON.stringify(jsonResp));
}
jsonResp.name = A;
jsonResp.amount = Avalbytes.toString();
console.info('Query Response:');
console.info(jsonResp);
return Avalbytes;
}
// 查询账户的资产
async querytest(stub, args) {
if (args.length != 1) {
throw new Error('不正确的参数个数,期望1个参数,该参数是账户实体名')
}
let jsonResp = {};
let A = args[0];
// Get the state from the ledger
let Avalbytes = await stub.getState(A);
if (!Avalbytes) {
jsonResp.error = '从资产持有者'+A+'获取状态失败';
throw new Error(JSON.stringify(jsonResp));
}
jsonResp.name = A;
jsonResp.amount = Avalbytes.toString();
console.info('Query Response:');
console.info(jsonResp);
return Avalbytes;
}
// 创建账户实体
async create(stub, args) {
if (args.length != 2) {
throw new Error('不正确的参数个数,期望2个参数')
}
let jsonResp = {};
let A = args[0];
let Aval = args[1];
if (typeof parseInt(Aval) !== 'number') {
return shim.error('期望一个整型值');
}
try {
await stub.putState(A, Buffer.from(Aval));
} catch (err) {
return shim.error(err);
}
}
}
shim.start(new Chaincode());
6.接下来我们需要修改chaincode-docker-devmode文件夹下docker-compose-simple.yaml配置文件
修改前
修改后
这里解释一下为什么修改这里
通过观察我们发现修改前我们使用的是hyperledger/fabric-ccenv镜像,修改后我们使用的是hyperledger/fabric-nodeenv镜像,因为fabric-samples默认提供的镜像以来是fabric-ccenv,此镜像适用于go环境,使用golang开发chaincode的同学则不需要修改,默认使用就可以。
我们通过观察镜像,镜像如下图所示,可以发现,我们下载的镜像默认已经提供了,java,nodejs,golang三种语言的镜像包,我们可以通过自己的需求去选择镜像。
7.到此我们需要修改和编译的地方已经完成,我们进入chaincode-docker-devmode文件夹下,在终端1下运行yaml脚本启动环境
(注意:如果你以前运行了fabric-samples的环境需要down掉,docker ps -a 没有相关运行程序之后再执行以下脚本)
docker-compose -f docker-compose-simple.yaml up
8.脚本运行成功,docker ps 查看程序如下
9.接着我们打开终端2进入chaincode容器
docker exec -it chaincode bash
如果此命令无法进入容器,请使用如下脚本进入容器
docker exec -it chaincode /bin/sh
10.接下来我们开始安装node需要的相关以来(终端2中)
cd /opt/gopath/src/chaincode/javascript-low-level
开始安装依赖(本文默认使用淘宝镜像)
npm install --registry=http://registry.npm.taobao.org
运行chaincode日志记录脚本(运行成功如下图所示)
CORE_CHAINCODE_ID_NAME=mycc:0 node example.js --peer.address peer:7052
11.然后我没打开终端3开始安装和示例化链码
进入cli容器中
docker exec -it cli bash
安装链码
peer chaincode install -p chaincode/javascript-low-level -n mycc -v 0 -l node
示例化链码
peer chaincode instantiate -n mycc -v 0 -c '{"Args":["init","tom","100","bob","200"]}' -C myc
12.开始执行我们chaincode的中的函数进行链码的基本操作(终端3)
新建账户实体
peer chaincode invoke -n mycc -c '{"Args":["create","lily","150"]}' -C myc
查询
peer chaincode query -n mycc -c '{"Args":["query","lily"]}' -C myc
peer chaincode query -n mycc -c '{"Args":["query","tom"]}' -C myc
peer chaincode query -n mycc -c '{"Args":["query","bob"]}' -C myc转账
bob给lily转账10
peer chaincode invoke -n mycc -c '{"Args":["transfer","bob","lily","10"]}' -C myc
转账后再查询
peer chaincode query -n mycc -c '{"Args":["query","lily"]}' -C myc
peer chaincode query -n mycc -c '{"Args":["query","bob"]}' -C myc删除tom账户实体
peer chaincode invoke -n mycc -c '{"Args":["delete","tom"]}' -C myc
至此我们对于chaincode的开发者模式的基本学习已经完毕,希望对大家有所收获