使用BSN开发区块链应用智能合约-Nodejs篇

区块链服务网络BSN
区块链服务网络BSN 机构得得号

Mar 03, 2020 BSN致力于改变目前联盟链局域网式应用的高成本问题

该文章已上链

摘要: 使用BSN开发区块链应用智能合约-Nodejs篇

使用BSN开发区块链应用智能合约-Nodejs篇
00:00
13:01

  作者:时跃堂 陈曦

  智能合约又称链码(Chaincode),是用计算机语言描述合约条款、交易的条件、交易的业务逻辑等,通过调用智能合约实现交易的自动执行和对账本数据的操作。一个BSN应用可以部署多个链码,每个链码包含多个方法。

  链码支持多种语言编写,包括Nodejs、golang、java。每个链码程序都必须实现Chaincode接口,链码包含:Init ,Invoke ,Query三个基本操作:

  ▶Init :链码初始化的方法,在链码实例化或者升级的时候调用一次,以便链码可以执行任何必要的初始化,包括应用程序状态的初始化。

  ▶Invoke :接收和处理链下业务系统调用事务处理提案,其参数包含调用的链码程序中函数的名称和具体业务处理数据参数。即在Invoke中根据不同的方法参数调用其他分支处理响应的业务。Invoke可以简单的理解为链码方法的入口。

  ▶Query:提供查询链码数据的方法,该方法只作为查询使用,不提供操作链上数据的操作。可在Query操作时调用,亦可在Invoke方法中作为某些方法的分支被调用。该方法可以不实现。

  本文主要介绍用户如何用Nodejs语言开发智能合约,以及在BSN中对智能合约开发的一些规范和建议。

  如何开发智能合约

  编写链码,关键是实现 Init 与 Invoke 两个方法。

  ▶Init:在链码实例化或者升级的时候调用一次, 完成初始化数据的工作。建议处理一些简单的处理,禁止使用该方法去初始化大量基础数据,如果有需要初始化的数据,建议在Invoke中处理。

  ▶Invoke:更新或查询提案事务中的帐本数据状态时,Invoke 方法被调用。因此响应调用或查询的业务实现逻辑都需要在此方法中编写实现。

  在实际开发中,开发人员可以自定义一个结构体,然后重写 Chaincode接口的 Init 与 Invoke方法,并将两个方法指定为自定义结构体的成员方法。两个方法被调用时都会传入 一个存根对象(stub),链码可以利用该对象来获取请求的相关信息,例如调用 者身份、目标通道、参数等等。下面具体说一下如何开发智能合约。

  目录说明:main.js:程序入口;bsnchaincode:链码文件夹;models:数据转换包;utils通用工具包;

  chaincode.js 代码示例

  base.js set方法示例

 

  ▶依赖包

  fabric-shim 包为链码提供了 API 用来访问/操作数据状态、事务上下文和调用其他链代码;

  ▶链码方法介绍

  ◆getFunctionAndParameters()

  返回一个方法调用描述对象,第一个值调用的链码方法名,第二个值要传入目标方法的参数对象。

  ◆getArgs()

  从链码调用请求中返回参数字符串数组,等价于getStringArgs()。

  ◆getStringArgs()

  返回链码调用请求中的参数字符串数组。

  ◆getTxID()

  返回当前链码调用请求的交易ID。交易ID在通道范围内唯一标识一个交易。

  ◆getChannelID()

  返回链码处理提议的通道ID

  ◆invokeChaincode(chaincodeName, args, channel)

  跨链提交链码:

  如果被调用的链码在同一个通道,那么它只是简单地将被调用链码的读写集添加到被调用交易中。如果被调用的链码处于不同的通道,那么只会返回响应结果,在被调用链码中的putState调用不会影响账本的状态。

  调用参数:

  chaincodeName:要调用的链码名称。

  args:调用参数列表,字节数组的数组。

  channel:要调用的链码所在通道名称。

  ◆getState(key)

  获取指定状态变量键的当前值。

  ●参数

  key: 要提取当前值的状态变量键。

  ◆putState(key, value)

  更新状态库中指定的状态变量键。如果变量已经存在,那么覆盖已有的值。

  ●参数:

  key:要更新的状态键,字符串。

  value:状态变量的新值,字节数组或字符串。

  ◆deleteState(key)

  从状态库中删除指定的状态变量键。

  ●参数

  key:要从状态库中删除的状态变量键

  ◆getStateByRange(startKey, endKey)

  返回一个账本状态键的迭代器,可用来 遍历在起始键和结束键之间的所有状态键,返回结果按词典顺序排列。当使用完毕后,调用返回的StateQueryIterator迭代器对象的close()方法关闭迭代器。

  ●参数

  startKey:起始键。

  endKey:结束键。

  ◆getStateByPartialCompositeKey(objectType, keys)

  基于给定的部分复合键查询账本状态。 该方法返回的迭代器可用于遍历查询结果集。

  当使用完毕后,调用返回的StateQueryIterator迭代器的close()方法关闭迭代器。

  ●参数

  objectType:结果键前缀。

  keys:用于拼接复合键值的属性值列表,字符串数组。

  ◆createCompositeKey(objectType, attributes)

  通过组合对象类别和给定的属性创建一个组合键。对象类别及属性都必须是 有效的utf8字符串,并且不能包含U+0000 (空字节) 和 U+10FFFF (最大未分配代码点)。 结果组合键可以用作PushState()调用中的参数键。

  ●参数

  objectType:组合键前缀。

  attributes:要拼接到组合键的各属性值,string数组。

  ◆splitCompositeKey(compositeKey)

  将组合键分离,返回数据1:组合键前缀;返回数据2:要拼接到组合键的各属性值,string数组;返回数据3:错误信息。

  ●参数

  compositeKey:组合键。

  ◆getQueryResult(query)

  在状态数据库上执行一个rich查询。该方法 仅在支持rich查询的状态数据库上有效,例如CouchDB。查询语句采用 底层状态数据库的语法。返回的StateQueryIterator可用于遍历查询 结果集。

  ●参数

  query:查询语句。

  ◆getQueryResultWithPagination(query, pageSize, bookmark)

  在状态数据库上执行一个rich查询, 该方法仅在支持rich查询的状态数据库上有效,例如CouchDB。查询语法依据 所采用的底层数据库。

  ●参数

  query:查询语句,字符串。

  pageSize:分页大小,整数。

  bookmark:书签,字符串。

  ◆getHistoryForKey(key)

  返回指定状态键的值历史记录。每次历史更新,都记录有 当时的值和关联的交易id、时间戳。时间戳取自交易提议头。

  ●参数

  key:状态键。

  ◆getCreator()

  返回链码调用者身份。

  ◆getTxTimestamp()

  返回交易创建时的时间戳,值取自交易的ChannelHeader部分, 因此它表示的是客户端的时间戳,并且在所有的背书服务节点上有相同的值。

  ◆setEvent(name, payload)

  设置链码事件,事件只有在Invoke中有效

  ●参数

  name:时间名称

  payload:通知内容。

  ◆getTransient()

  返回交易中带有的一些临时信息,可以存放一些应用相关的保密信息,这些信息不会被写到账本中。

  链码开发规范

  ▶所有链码方法参数信息必须校验。

  ●校验参数个数。

  ●校验参数值(长度、类型等等,根据业务场景定义)。

  ▶Init方法不能大量初始化数据。

  需要初始化数据,单独写方法进行处理。

  ▶引用第三方包,需要使用npm管理。

  使用Nodejs依赖包管理工具:npm。

  安装:

  新版的nodejs已经集成了npm,安装了Nodejs同时就安装了npm,可以通过输入 "npm -v" 来测试是否成功安装。如果你安装的是旧版本的 npm,可以通过 npm 命令来升级,命令如下:

  使用:

  ●进入项目目录

  ●安装所需依赖:npm install package.json

  ●项目目录下自动生成 node_modules 文件

  ▶main函数,必须在项目中所有链码的上级或同级。

  ▶发布服务时,链码包打包时进入项目根目录进行打包,格式为.zip。

  ▶发布服务时,添加链码包的链码名称要与项目名称相同。

 

  链码开发建议

  ▶关于key的定义

  ●描述

  现阶段所有业务数据都存在于一个账本数据库中,并存储方式是以key-value的形式存储,可能存在不同业务的key值相同的情况。

  ●解决方案

  在不同的业务key值添加业务前缀

  ●例子

  如用户和角色他们的标识相同,如果以标识作为key存储时,后者保存会覆盖前者信息;但是如果用户:user_用户标识,角色:role_角色标识这样存储就会避免这个问题

  ▶关于根据key值模糊查询

  ●描述

  根据key查询同一个业务数据时。

  ●解决方案

  查询语句使用正则表达式进行查询的,{\"_id\":{\"$regex\":\"ChargeUnit_.*\"}修改为{\"_id\":{\"$regex\":\"^ChargeUnit_.*\"};前者检索key中只要含有“ChargeUnit”的数据,后者检索key以“ChargeUnit”开头的数据。

  ●例子

  正则表达式:特殊符号转义:例:()[] {} . \

  ▶关于跨链调用(invokeChaincode)

  ●描述

  由于BSN是提供的是公用的Fabric环境,为了保障通道ID(channelId)与链码名称(chaincodeName)的唯一性,链码部署完成后,用户才能拿到通道ID(channelId)与链码名称(chaincodeName)。那么链码中该如何得到这些值?

  ●解决方案

  需要跨链调用的链码,需将channelId和chaincodeName作为业务参数传递。

  ●例子

(1、 内容来自链得得内容开放平台“得得号”,稿件内容仅代表作者观点,不代表链得得官方立场。2、 凡“得得号”文章,原创性和内容的真实性由投稿人保证,如果稿件因抄袭、作假等行为导致的法律后果,由投稿人本人负责。3、 得得号平台发布文章,如有侵权、违规及其他不当言论内容,请广大读者监督,一经证实,平台会立即下线。如遇文章内容问题,请发送至邮箱:chengyiniu@chaindd.com)

链得得仅提供相关信息展示,不构成任何投资建议
本文系作者 区块链服务网络BSN 授权链得得发表,并经链得得编辑,转载请注明出处、作者和本文链接

更多精彩内容,关注链得得微信号(ID:ChainDD),或者下载链得得App

分享到:

相关推荐

    评论(0

    Oh! no

    您是否确认要删除该条评论吗?

    分享到微信