使用Node.js开发Hyperledger Fabric Chaincode

Hyperledger Fabric术语中的Chaincode是什么?

Hyperledger Fabric中,Chaincode是在网络peer上运行的一段代码,用于实现应用程序如何与分类帐交互的业务逻辑。提出交易时,它会触发Chaincode,该Chaincode决定应将哪种状态更改应用于分类帐。因此为了在Hyperledger Fabric上开发去中心化的应用程序,必须编写称为Chaincode的应用程序逻辑。

使用Node.js开发Hyperledger Fabric Chaincode

Chaincode可以使用Go,Nodejs,Java编写。其他两种语言相比,Node.js是一种更容易理解和使用的语言。由于Node.js文档中可用的信息非常少,但编写和部署Node.js Chaincode并不困难。因此我决定编写一些Node.js Chaincode的概念,并逐步编写和部署简单的Chaincode。

Hyperledger Fabric数据库

在深入研究Chaincode之前,首先让我们看一下Hyperledger Fabric Network中数据的存储位置。Hyperledger Fabric使用键值数据库存储其状态。默认情况下,Fabric使用LevelDB。该数据库保存可以使用其键查询的特定对象的二进制数据。与传统的数据库不同,区块链数据库位于网络的每个peer上。因此它被称为分散网络。

除了LevelDB,还有另一个用于Hyperledger Fabric的数据库称为CouchDB。CouchDB是可选的替代外部可插拔状态数据库。与LevelDB键值存储一样,CouchDB可以存储以链码建模的任何二进制数据。但作为JSON文档存储,当将Chaincode值(例如资产)建模为JSON数据时,CouchDB还可以对Chaincode数据进行丰富的查询。

 Chaincode组件

1.fabric-contract-api:

用于实现智能合约的高级合约API(作为npm模块提供)

2.fabric-shim:

用于实现智能合约的低级合约API(作为npm模块提供)

我们可以将fabric-shim视为fabric-contract-api的递减版本。对新版本的fabric使用高级API是一个好习惯。但是fabric-contract-api可以完成shim可以做的所有事情。当然不止如此。

3.stub:

它是fabric-contract-api中的接口,用于访问和修改分类帐(数据库状态)。因此它是主要数据Chaincode接口,用于在分类账上读写数据,我们如何读写数据?让我们看一下Stub界面中的一些常用方法

Stub接口中的常用方法

1.getState(k):

众所周知,Hyperledger Fabric数据库以键值对的形式存储数据。

此方法从分类帐读取数据。它以输入“ k”作为键,并返回与键“ k”关联的二进制值。

2.putState(k,v):

此方法将数据写入分类帐。它以“ k”为键,“ v”为值。明确地说,假设我们想将Alice的年龄存储到分类帐中,我们可以将Alice作为键,并将AGE作为值。

3.deleteState(k):

此方法从分类账中删除关联密钥“ k”的值。

4.getStateByRange(k1,k9):

此方法在分类帐中的一组键上返回范围迭代器。它将迭代startKey(k1)和endKey(k9)并返回这两个键之间的所有键值。这类似于javascript中的for循环。假设如果我们已经按键k1,k2,k3…k99的顺序存储了一些用户数据,则可以使用此方法简单地获取所有这些值的状态。

5.getTxID():

此方法返回被调用事务的事务ID。交易ID对于链上的每个交易都是唯一的。因此交易ID在跟踪交易中起着至关重要的作用。

6.getTxTimestamp():

此方法返回创建事务时的时间戳。这是从交易ChannelHeader获取的,因此它将指示客户的时间戳,并且在所有背书人中都具有相同的值。

编写您的第一个Chaincode

您已经了解了在node.js中编写链码的一些先决条件。因此就目前而言,您可能会非常兴奋地编写您的第一个chaincode。

由于我们要用Nodejs编写Chaincode,因此我们首先需要创建传统的npm东西,例如package.json和index.js。如果您不知道此package.json。

列出您的项目所依赖的软件包

使用语义版本控制规则指定项目可以使用的软件包的版本

使您的构建具有可复制性,因此更易于与其他开发人员共享.

简而言之,我们的Chaincode取决于fabric-contract-api和fabric-shim模块。我们在package.json中提到了这些软件包和版本。

我们还将添加fabric-chaincode-node start作为我们的启动脚本,这是在peer节点上安装chaincode所需的。

这是我们的package.json:

{


    “name”: “Test-Chaincode”,


    “version”: “1.0.0”,


    “description”: “my first exciting node.js chaincode on Hyperledger-fabric”,


    “main”: “index.js”,


    “engines”: {


        “node”: “>=8”,


        “npm”: “>=5”


    },


    “scripts”: {


        “lint”: “eslint .”,


        “pretest”: “npm run lint”,


        “test”: “nyc mocha –recursive”,


        “start”: “fabric-chaincode-node start”


    },


    “engineStrict”: true,


    “author”: “Hyperledger”,


    “license”: “Apache-2.0”,


    “dependencies”: {


        “fabric-contract-api”: “~1.4.0”,


        “fabric-shim”: “~1.4.0”


    },


    “devDependencies”: {


        “chai”: “^4.1.2”,


        “eslint”: “^4.19.1”,


        “mocha”: “^5.2.0”,


        “nyc”: “^12.0.2”,


        “sinon”: “^6.0.0”,


        “sinon-chai”: “^3.2.0”


    },


    “nyc”: {


        “exclude”: [


            “coverage/**”,


            “test/**”


        ],


        “reporter”: [


            “text-summary”,


            “html”


        ],


        “all”: true,


        “check-coverage”: true,


        “statements”: 100,


        “branches”: 100,


        “functions”: 100,


        “lines”: 100


    }


}

如果您可以仔细观察代码,则有一行“ main”:“ index.js”。这意味着什么—在启动时(安装chaincode期间),npm模块用于检查index.js并在peer节点上安装提到的合约。”因此我们的index.js包含作为模块导出的合约。

这是我们的index.js文件:

‘use strict’;


const testContract = require(’./logic’);


module.exports.contracts = [ testContract ];

智能合约:

我们的业务逻辑是什么?

添加,检索和删除学生标记。

1.添加标记涉及将数据写入分类帐。因此我们将从chaincode的stub接口使用putState(k,v)方法。


2.检索标记涉及从分类帐读取数据。因此我们需要使用getState(k)方法。


3.删除标记涉及删除数据。因此我们需要使用deleteState(k)方法。

chaincode首先从fabric-contract-api模块引入作用域密钥类Contract。此类将用于编写逻辑,所有chaincode函数都应使用This库类。

const { Contract}=require(’fabric-contract-api’);


 class testContract extends Contract {


//Functions go here


}

添加标记:

我们将创建一个JavaScript对象来存储每个学科中学生的成绩,并将该对象存储为一个值,并将StudentId存储为一个键。通过服务器将数据发送到数据库时,数据必须是字符串。因此我们需要使用JSON.stringify()方法将此标记对象转换为字符串,并应用缓冲区以二进制数据的形式发送到数据库。

async addMarks(ctx,studentId,subject1,subject2,subject3) { 


    let marks={


     subj1:subject1, 


     subj2:subject2,


     subj3:subject3 


     }; 


await  ctx.stub.putState(studentId,Buffer.from(JSON.stringify(marks))); 


console.log(’Student Marks added To the ledger Succesfully..’); 


}

删除标记

async deleteMarks(ctx,studentId) {


 await ctx.stub.deleteState(studentId); 


console.log(’Student Marks deleted from the ledger Succesfully..’);


    }

查询学生成绩:

由于我们在先前的addMarks()函数中将值以缓冲区的形式放置。一旦查询,它将返回buffer。因此我们需要将缓冲区转换为字符串并将其解析为原始javascript对象。

async queryMarks(ctx,studentId){

     let marksAsBytes = await ctx.stub.getState(studentId); 


     if (!marksAsBytes || marksAsBytes.toString().length <= 0) { 


       throw new Error(’Student with this Id does not exist: ‘); 


         } 


    let marks=JSON.parse(marksAsBytes.toString()); 


    return JSON.stringify(marks); 


   }

最终版智能合约代码

您可以在此处找到完整的智能合约。https://gist.github.com/Salmandabbakuti/fb25d0429359c6d77ab64d097c5b588c

‘use strict’;

const { Contract} = require(‘fabric-contract-api’);

class testContract extends Contract {

async queryMarks(ctx,studentId) {

    let marksAsBytes = await ctx.stub.getState(studentId); 

    if (!marksAsBytes || marksAsBytes.toString().length <= 0) {

      throw new Error(‘Student with this Id does not exist: ‘);

       }

      let marks=JSON.parse(marksAsBytes.toString());


      return JSON.stringify(marks);

  }

async addMarks(ctx,studentId,subject1,subject2,subject3) {


   let marks={

       subj1:subject1,

       subj2:subject2,

       subj3:subject3

       };

    await ctx.stub.putState(studentId,Buffer.from(JSON.stringify(marks))); 

    console.log(‘Student Marks added To the ledger Succesfully..’);


  }

async deleteMarks(ctx,studentId) {


    await ctx.stub.deleteState(studentId); 


    console.log(‘Student Marks deleted from the ledger Succesfully..’);


    }


}

module.exports=testContract;

为了安装和测试此智能合约,我将使用包含单个peer的基本网络。在这个网络上,我们将安装名为mycc的Node.js的chaincode到peer0.org1.example.com上,并在通道mychannel上实例化它。然后我们可以调用这些chaincode函数。确保在您的设置中安装了docker。为了简单起见,我已经将chaincode文件(logic.js,index.js,package.json)安装在chaincode/newcc目录中。

首先,我们需要启动网络并创建通道。

git clone https://github.com/Salmandabbakuti/hlf-chaincodeTest.git


cd hlf-chaincodeTest/basic-network


./start.sh

等待片刻。建立网络将需要一段时间。如果遇到任何权限错误,只需在root用户权限下运行就行。一旦我们的具有单个peer的网络建立并运行,我们就可以安装chaincode。

为了安装和调用chaincode,我们可以使用Peer的CLI容器。输入CLI容器

docker exec -it cli bash

安装和实例化Chaincode

peer chaincode install -n mycc -v 1.0 -p “/opt/gopath/src/github.com/newcc” -l “node”


peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n mycc -l “node” -v 1.0 -c ‘{“Args”:[]}’

增加学生标志

peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n 


mycc -c ‘{“function”:”addMarks”,”Args”:[“Alice”,”68″,”84″,”89″]}’

查询学生“Alice”的标志

peer chaincode query -o orderer.example.com:7050 -C mychannel -n 


mycc -c ‘{“function”:”queryMarks”,”Args”:[“Alice”]}’

从Ledger删除“Alice”标志

peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n 


mycc -c ‘{“function”:”deleteMarks”,”Args”:[“Alice”]}’

上面的脚本将从账本中删除密钥“ Alice”和相关数据。如果再次查询Alice的标记,则会收到一条错误消息,提示“ studentId不存在”。

我还制作了一个自动化脚本,用于在客户端目录中安装和测试此chaincode。请按照以下步骤进行快速演示

首先,退出CLI容器并在客户端目录中运行脚本

exit # exits from CLI docker container if you’re in


cd ..


cd client  #change your directory to client


chmod a+x start.sh


./start.sh  #Automated script for testing

您也可以使用client / start.sh文件中定义的脚本手动调用chaincode函数。

结论

我们在这里演示了什么是Chaincode,chaincode的stub接口中的方法,chaincode的部署结构以及编写chaincode和在网络上进行部署的难易程度。希望本文能以某种方式帮助您开始编写chaincode并在网络上进行部署。


关键词: Node.js  Hyperledger  

该内容来自于互联网公开内容,非区块链原创内容,如若转载,请注明出处:https://htzkw.com/archives/28354

联系我们

aliyinhang@gmail.com