Fabric链码开发的8个原则

Stella981
• 阅读 667

我相信智能合约(链码)是Hyperledger Fabric区块链网络的核心。正确开发链码可以真正发挥一个安全区块链的优势,反之则会带来灾难性的后果。在这篇文章里我不打算探讨Hyperledger Fabric链码设计的特定模式的好与坏,而是希望分享我在开发若干Hyperledger Fabric概念验证应用过程中总结的一些基本准则。

Hyperledger Fabric区块链开发教程: Node.js | Java | Golang

1、启用peer节点的开发模式

使用开发模式开启你的Hyperledger Fabric链码开发流程。这一点无论怎么强调都不过分,这会节省你大量的时间和精力,因为你可以自由地修改代码而无需重新部署并激活链码,也无需一遍遍地重启网络。

参考文档:https://gist.github.com/arnabkaycee/d4c10a7f5c01f349632b42b67cee46db

2、使用Fabric链码的日志

这可能是能帮助你调试Hyperledger Fabric链码并快速找出链码bug的第一个有用的技能。链码日志很简单易用,使用Fabric内建的logger即可。

参考文档:

3、避免在Fabric链码中使用全局键

在开发Hyperledger Fabric链码时,我们经常会发现在搜索数据方面限制很多,因此要跟踪在键值库中注册的键,我们有时会尝试使用某些全局数据。

例如,当你再Hyperledger Fabric应用中跟踪注册的弹珠时,可能想创建一个全局的计数器以便生成弹珠的下一个ID。但是这么做的时候,你就引入了对这个变量的依赖。在开始的时候这看起来不是个问题,但是当你提交并发交易时就会出错。为什么?让我解释一下。

看一下链码:

package main
import (
    //other imports
    "github.com/hyperledger/fabric/core/chaincode/shim"
       pb "github.com/hyperledger/fabric/protos/peer"
)

//不要这么做!    
totalNumberOfMarbles := 0

func (t *SimpleChaincode) initMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    var err error
    
    marbleId := fmt.Sprintf("MARBLE_%06d",totalNumberOfMarbles)
    marbleName := args[0]
    color := strings.ToLower(args[1])
    owner := strings.ToLower(args[3])
    size, err := strconv.Atoi(args[2])
    
    //other code to initialize
    objectType := "marble"
    marble := &marble{objectType, marbleId, marbleName, color, size, owner}
    
    //--------------CODE SMELL----------------
    //BIG source of Non-determinism as well as performance hit.
    totalNumberOfMarbles = totalNumberOfMarbles + 1 
    //--------------CODE SMELL----------------
    

    //regular stuff...        
    err = stub.PutState(marbleId, marbleJSONasBytes)
    if err != nil {
        return shim.Error(err.Error())
    }
}

那么,为什么我不喜欢这样?

第一个原因。假设你已经完成这个Fabric链码,一切都很正常,直到有一天,某个运行这个链码的peer节点,崩溃了。虽然账本数据还在,但是内部有些可怕的事情已经发生了。你可能重新启动peer节点,起初一切看起来都正常。但是突然,这个节点背书的所有交易都开始失败了。为什么?就是因为那个全局计数变量已经不能正确跟踪真实的值了。其他的peer节点都计数到比如15K了,而这个节点突然从零开始计数,你的弹珠的ID又从零开始了。因此,当你将这个交易发送给排序节点(Orderer)并到达提交节点(Peer)时,提交节点上的验证系统(Validation System Chaincode)会比较所有背书交易的提议响应,同时检查是否有足够的签名存在,只要有一个提议响应不匹配,提交节点就会抛出一个ENDORSEMENT_POLICY_FAILURE异常。

第二个原因。现在让我们尝试解决上面的问题,在Fabric链码的最后添加如下的代码:

stub.PutState("marble_count", totalNumberOfMarbles)

这样会好一些吗?Noooooooooooooooooo!

想象一下,有两个并发交易都试图插入新的弹珠。

例如,一个交易要将marble_count的值更新为34,marble_count状态的新版本为10。而另一个交易则要将marble_count的值更新为35, 它也认为marble_count的新版本为10。记住,由于这两个交易是并发的,两个交易看到的都是current_version(marble_count) = 09。

现在其中一个交易将在另一个交易之前到达Fabric的排序节点,marble_count键已经更新到新的值,这时marble_count的版本已经是10,因此后到的交易将失败,因为marble_count的版本已经是10 ,而后续交易还认为它读的是版本09并且将更新到版本10。这是区块链中经典的双花问题(double spending)。

Hyperledger Fabric在提交交易时使用一种优化的锁模型。正如我已经解释过的,提议响应由客户端从背书节点采集,然后发送给排序节点并最终由排序节点将其分发给提交节点。着这个两步过程中,如果有些在背书阶段读取的键的版本发生了变化,你就会得到MVCC_READ_CONFLICT错误。当存在并发交易同时更新相同的键时,就有可能出现这个问题。

关于这一点的详细说明,可以参考这篇文章

4、聪明地使用CouchDB查询

Couch DB查询(又称为Mongo查询)在搜索Fabric节点的键值库中的数据时非常有用,但是有一些坑你需要注意。

  • Couch DB查询不会修改交易的READ SET

Mongo查询仅用来查询节点的键值库也就是状态库。它不会修改交易的read set。这可能会在交易中 导致幻读(phantom reads)。

  • 只能搜索已经存入CouchDB的数据

不要试图用Mongo查询按键名搜索。虽然你可以访问CouchDB的Fauxton控制台,但你无法按键查询。例如,不允许查询 channelName\0000KeyName。更好的方法时将键作为你自己数据的属性保存。

5、编写确定性的Fabric链码

永远不要编写不确定的链码。意思是说如果我在多个不同的时间、不同的环境下执行链码,总应该得到相同的结果。例如,避免使用像rand.New(...)t := time.Now() 这样的代码,或者依赖于某个没有在账本中持久化的变量。

这是因为,如果生成的读写集不一样,Hyperledger Fabric的验证系统链码(Validation System Chaincode)会拒绝交易并抛出ENDORSEMENT_POLICY_FAILURE异常。

6、调用其他通道的Fabric链码时要小心

在链码中调用同一个通道中的另一个链码没问题,但是要了解的是,如果是要调用另一个通道的链码,你只能得到链码方法的返回结果,而不会在另一个通道账本中有任何提交。目前,跨通道的链码调用不会修改数据,因此,一个交易一次只能写入一个通道。

7、记得设置Fabric链码的执行超时时间

在高负载的情况下,你的Hyperledger Fabric链码可能不会在30s内完成。因此一个好的实践是根据需求定制链码执行超时值。这是由core.yaml中的参数决定的。你可以在docker compose 文件中如下设置:

Example: CORE_CHAINCODE_EXECUTETIMEOUT=60s

8、避免从Fabric链码中访问外部资源

访问外部资源可能会暴露系统漏洞并给你的Hyperledger Fabric链码引入安全威胁。无论如何你不会希望外部资源中的恶意代码影响你的链码逻辑。因此请尽可能的避免再Fabric链码中访问区块链外部的资源。


原文链接:Hyperledger Fabric链码开发的8条军规 — 汇智网

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
2年前
Python从零实现区块链仿真【含源码】
在区块链或数字货币领域,Python并不是主流的开发语言。但是如果你的目的是研究区块链技术的原理,或者需要在自己的笔记本上仿真一个区块链网络并进行一些研究性的实验,比如完成自己的毕业设计项目或科研课题,那么Python就是合适的。在这个教程里,我们将学习如何使用Python从零开发一个多节点的区块链网络,并基于这个仿真区块链网络,开发一个去中心化的数据分享应
可莉 可莉
2年前
10个开源的Python区块链项目
Python不是主流的区块链底层平台开发语言,但是在DApp开发、区块链仿真与数据分析、智能合约安全分析等领域,Python依然是不错的选择。本文介绍了10个最流行的Python区块链项并提供了相应的源代码下载地址。<!more区块链开发教程链接:以太坊(https://www.oschina.net/action/GoToLink
Stella981 Stella981
2年前
Fabric CA证书管理教程
FabricCA是HyperledgerFabric自带的证书管理工具,对于开发和测试非常方便。在这个教程中我们将探索FabricCA的使用方法并利用它完成用户的注册/Register和登记/Enrollment。HyperledgerFabric是一个许可制的区块链平台,在访问Fabric网络之前必须先进行身份识别并获得访问许可。Fabric网
Stella981 Stella981
2年前
Hyperledger Explorer官方安装文档中文版
HyperledgerExplorer是一个简单易用的开源工具,可以用于监视区块链网络中的活动。HyperledgerExplorer支持Fabric、Iroha等多种区块链,可以运行在MocOS和Ubuntu下。HyperledgerFabric区块链开发教程:Fabric区块链Node.js开发详解(https://
Stella981 Stella981
2年前
Fabric多通道网络实战
HyperledgerFabric支持在一组相同的机构之间的多通道部署,每个通道都相当于一个单独的区块链。Fabric的多通道特性不仅可以满足机构之间不同的数据共享需求,同时也可以提高整个Fabric网络的吞吐量。本文将演示如何使用HyperledgerFabric1.4.3搭建一个多通道的区块链网络、部署并访问链码。1、HyperledgerF
Stella981 Stella981
2年前
Hyperledger Fabric如何启用双向TLS安全通信?
HyperlederFabric区块链支持在通信节点之间启用TLS传输层安全通信,TLS支持单向验证仅验证服务节点身份,或双向验证同时验证服务节点和客户端节点的身份。本文将介绍如何在HyperledgerFabric网络中启用双向TLS安全通信。HyperledgerFabric链码与应用开发相关教程:Hype
Stella981 Stella981
2年前
Hyperledger Caliper
在这个教程中,我们将学习如何使用HyperledgerCaliper对包含多个排序节点的Fabric网络进行基准测试,我们使用DockerSwarm作为容器编排工具。HyperledgerFabric区块链开发教程:Fabric区块链Node.js开发详解(https://www.oschina.net/action/G
Wesley13 Wesley13
2年前
NEO从源码分析看UTXO交易
_0x00前言_社区大佬:“交易是操作区块链的唯一方式。”_0x01交易类型_在NEO中,几乎除了共识之外的所有的对区块链的操作都是一种“交易”,甚至在“交易”面前,合约都只是一个小弟。交易类型的定义在Core中的TransactionType中:源码位置:neo/Core/TransactionType