fabric2.0 部署详解—(4)—使用开发模式编写chaincode

通过上文我们成功在fabric-samples的基础上加入了一个新的组织,相信大家对fabric的基础架构已经有了一定的了解和熟悉,接下来我们开始对chaincode的学习,本文主要介绍如何在开发模式中部署并学习chaincode

学习之前我们需要注意一下几点

  • 本文使用的chaincode的开发语言为nodejs(当然其他语言也能借鉴此文)
  • 本文fabric-samples版本为最新版本
  • 本文的chaincode代码主要参考fabric-samples中abstore示例中的合约代码

接下来我们开始对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的开发者模式的基本学习已经完毕,希望对大家有所收获

?mid=&wid=51824&sid=&tid=8731&rid=FINISHED&custom1=mp.csdn.net&t=1587091854959?mid=cd1d2&wid=51824&sid=&tid=8731&rid=MNTZ_INJECT&t=1587091858535?mid=90f06&wid=51824&sid=&tid=8731&rid=MNTZ_LOADED&t=1587091859013

发布了22 篇原创文章 · 获赞 11 · 访问量 5773

猜你喜欢

转载自blog.csdn.net/tank_ft/article/details/105573757