
前言
本部分主要讲解使用nodejs开发fabric链码的过程
fabric链码开发需要注意的8个事项
- 启用peer节点的开发模式
- 使用Fabric链码的日志
- Golang:shim ChaincodeLogger
- NodeJS:shim newLogger
- Java:可以使用任何标准的日志框架,例如log4j
- 避免在Fabric链码中使用全局键
- 聪明地使用CouchDB查询(Mongo查询)
- 编写确定性的Fabric链码
- 调用其他通道的Fabric链码时要小心
- 目前,跨通道的链码调用不会修改数据,因此, 一个交易一次只能写入一个通道。
- 记得设置Fabric链码的执行超时时间
- 避免从Fabric链码中访问外部资源
fabric-shim
fabric-contract-api
提供了一个高度封装的开发接口,使用该API可以在高层级 编写智能合约的业务逻辑。而fabric-shim
则提供了底层的链码开发接口。
fabric-shim
要求开发者实现ChaincodeInterface接口,即实现Invoke和Init方法:
下载:
1 | npm install fabric-shim |
demo.js:
1 | const Chaincode = class { |
运行链码:
1 | node demo.js |
源码分析
ChaincodeInterface
链码必须实现ChaincodeInterface接口中定义的方法。
Init()
方法在链码初始化或升级时被调用,以便进行必要的应用状态的初始化。
Invoke()
方法当处理交易或查询请求时被调用。两个方法被调用时都会传入一个存根对象(stub),链码可以利用该对象来获取请求的相关信息,例如调用 者身份、目标通道、参数等等,同时也需要利用存根对象与peer节点通信,以便 获取或更新应用状态。
init
invoke
ChaincodeStub
ChaincodeStub封装了链码实现和Fabric的peer节点之间的API。
构造函数
1 | new ChaincodeStub(client, channel_id, txId, chaincodeInput, signedProposal) |
- client:Handler实例
- channel_id:通道id,string
- txId:交易id,string
- chaincodeInput:来自peer节点旳解码消息
- signedProposal:签名提议
putState()
更新状态库中指定的状态变量键。如果变量已经存在,那么覆盖已有的值。
1 | async putState(key, value) { |
- key:要更新的状态键,字符串
- value:状态变量的新值,字节数组或字符串
- 返回:一个Promise对象
createCompositeKey()
通过组合对象类别和给定的属性创建一个组合键。对象类别及属性都必须是有效的utf8字符串,并且不能包含U+0000 (空字节) 和 U+10FFFF (最大未分配代码点)。
- 组合键可以用作
putState()
调用中的参数键key
。
Hyperledger Fabric使用一个简单的key/value模型来保存链码状态。在有些场景下, 可能需要跟踪多个属性,也可能需要使多种属性可搜索。组合键可用来满足这些需求。
类似于关系数据库中的组合键,你可以认为这里的可搜索属性就是组合键的组成列, 属性的值称为键的一部分,因此可以使用像 getStateByRange()
和 getStateByPartialCompositeKey()
这样的方法进行搜索。
1 | function createCompositeKey(objectType, attributes){} |
- objectType:组合键的前缀(一般使用name)
- attributes:拼接到组合键的各属性值,string数组
- 返回:string类型的组合键
getStateByRange()
该方法返回一个账本状态键的迭代器,可用来遍历在起始键和结束键之间的所有状态键,返回结果按词典顺序排列。
- 如果起始键和结束键之间的数量大于节点配置文件
core.yaml
中定义的totalQueryLimit
, 那么返回结果数量将受限于totalQueryLimit
的约定。 - 调用返回的
StateQueryIterator
迭代器对象的close()
方法关闭迭代器。
1 | <async> getStateByRange(startKey, endKey){ |
- startKey:起始键,字符串
- endKey:结束键,字符串
- 返回:一个Promise对象,其解析值为StateQueryIterator迭代器。
getStateByPartialCompositeKey()
基于给定的部分复合键查询账本状态。
该方法返回的迭代器可用于遍历查询结果集。
当使用完毕后,调用返回的
StateQueryIterator
迭代器的close()
方法关闭迭代器。
1 | <async> getStateByPartialCompositeKey(objectType, attributes){ |
- objectType:结果键前缀,字符串
- attributes:用于拼接复合键值的属性值列表,字符串数组
- 返回:一个Promise对象,其解析值为
StateQueryIterator
迭代器对象。
getHistoryForKey()
查看指定状态键的值历史记录。
- 每次历史更新,都记录有当时的值和关联的交易id、时间戳。
- 时间戳取自交易提议头。
该方法需要通过peer节点配置中的如下选项开启:
core.ledger.history.enableHistoryDatabase = true
1
2
3
4
5
```javascript
<async> getHistoryForKey(key){}key:状态键
返回一个Promise对象,其解析值为HistoryQueryIterator对象。
getCreator()
返回链码调用的提交者的身份对象
1 | const creator = ctx.stub.getCreator(); |
- 返回一个 object 对象,其中包含了两个属性
mspid
和idBytes

getChannelID()
返回提案的channelID,以供链码处理。
- 这将是交易建议的“ channel_id”(请参阅protos / common / common.proto中的ChannelHeader),除非链码在一个通道上调用另一个
1 | const channel = ctx.stub.getChannelID(); |
getTxTimestamp()
返回创建事务时的时间戳。
- 这是从事务ChannelHeader中获取的,因此它将指示客户端的时间戳,并且在所有背书人中都具有相同的值。
- 返回的对象:{ seconds: [Long] { low: [int32], high: [int32], unsigned: [bool] }, nanos: [int32] }
1 | const time = ctx.stub.getTxTimestamp(); |
在fabric 2.0 中不再有
getDateTimestamp()
函数,统一使用上面的获取时间
ClientIdentity
ClientIdentity表示有关提交交易的用户的身份信息。
- 链码可以使用此类获取有关提交者的身份信息,包括唯一ID,MSP(会员服务提供者)ID和属性。
- 此类信息对于通过链码实施访问控制很有用。
1 | const ClientIdentity = require('fabric-shim').ClientIdentity; |
使用
1 | // 在 contract.js 中 |
- getID() 返回与调用身份关联的ID,该ID在MSP中保证是唯一的。
- A string in the format:
"x509::{subject DN}::{issuer DN}"
- A string in the format:
- getIDBytes() 返回与调用身份关联的ID字节
- 如果MSP是使用X.509证书实现的,则这些ID字节将是X.509证书的ID字节。如果希望检查X.509证书的内容,则必须使用X.509解析库(例如jsrsasign或@ fidm / x509)来解码这些ID字节。
- getMSPID() 返回调用身份的MSPID。
- 同
ctx.stub.getCreator()
返回中的 ‘mspid’ 相同
- 同

fabric-contract-api
1 | const { Contract, Context } = require('fabric-contract-api'); |
context
源码
1 | class Context { |
使用
1 |
contract
源码
1 | class Contract { |
使用
1 |
- Post title:fabric-Contract-Nodejs
- Post author:Wei Jieyang
- Create time:2021-03-31 13:28:34
- Post link:https://jieyang-wei.github.io/2021/03/31/fabric-Contract-Nodejs/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.