智能合约的可升级改造实践

转载 2017年12月29日 00:00:00

背景起因


近期开发基于以太坊的智能合约,为实验室的分布式电商系统提供可信的“第三方信用担保”功能。


由于初期项目需求变动会比较频繁,并且智能合约一经发布于区块链上就无法修改,即使智能合约中有Bug需要修复或者业务逻辑变更,它也不能直接在原有的合约上直接修改再重新发布。因此在即将发布第一版之前,需要结合业务场景考虑合理的升级改造机制。


初版的智能合约主要包括如下功能:


1. 买家创建订单,合约记录订单基本信息并且需要买方向合约支付交易费用
2. 卖家取消订单,卖家在发货前可以取消订单,合约自动将交易费用退回给买家
3. 卖家发货,合约记录相应订单的物流信息,修改订单状态
4. 买家确认收货,合约将交易费用转移至卖家,并且修改订单状态为已完成
5. 基本的订单查询功能


通过简单的需求介绍,可以看出合约中会保存着交易费用,订单数据以及基本的电商交易流程动作。


设计方案


起初写的智能合约全部集中在一个contract,里面会保存交易费用,订单数据以及电商交易的全部流程。一旦发布就不能修改(哪怕是添加一个方法,修改方法逻辑以及数据中添加新的字段,,,),因为重新发布新的合约之后,之前的订单数据以及交易费用全部在旧的合约中,从工程的角度来看,同时维护新旧合约(也许好多个旧合约,,,,)简直不能更糟糕。


因此决定将业务逻辑和数据从合约代码层面就做好分离,即合约分为了两类:逻辑合约以及数据合约。逻辑合约通过访问数据合约获得数据,并对数据做逻辑处理,然后写回数据合约,专注于对数据的逻辑处理和对外提供服务。逻辑合约不存储任何状态;数据合约专注于数据结构定义与所存储数据的读写接口。


逻辑合约与数据合约存在操作关系,逻辑上分为四类:


1. 逻辑合约与数据合约 1对1
2. 逻辑合约与数据合约 1对多
3. 逻辑合约与数据合约 多对1
4. 逻辑合约与数据合约 多对多


根据本项目的业务场景,采取逻辑合约与数据合约1对1的关系(后续业务复杂,可以改造为多对1),综上,初期的设计图如下:


0?wx_fmt=png


实践


首先进行数据与逻辑的分离。


数据合约


数据合约包括状态,以及每个状态对应读写方法(set&&get),部分代码如下:


0?wx_fmt=png


逻辑合约


逻辑合约因为仅包含业务逻辑,不会存储任何的数据状态,因此使用Solidity的library进行编写。


Solidity的library


library的好处是它是单实例,只会更新一个library文件,不会像contract那样因为各种依赖产生‘涟漪’效应,对于经常变动的业务逻辑代码,使用library每次更新会节省大量gas消耗。


library是需要被合约调用才能执行的,调用library的contract(下面简称A),使用的delegatecall方法,因此library执行的上下文环境在A中,也就是library的msg.sender是A,并且可以直接修改A中的storage。直白说,A调用library,就是把library的代码import到自己的内部执行了。


入口合约


因为业务逻辑合约使用了library编写,library的调用是需要合约触发的,因此需要一个入口合约,来去调用library操作数据合约,而且对于本项目的实际需求,上面将数据以及逻辑进行分离后,忘记了一个重要的方面,就是资金存储,引入入口合约后,正好解决了这个问题,客户端调用入口合约代码,将资金充入入口合约,并且由入口合约进行资金的转移过程。


入口合约调用library来去操作数据合约的数据,因此在入口合约中,需要引入调用的library(或者是library的interface),这样子,如果library添加一个新的方法,不仅library需要更新,入口合约也是需要更新的,而入口合约的作用包括交易资金托管以及调用library方法,更新发生时,需要有适当的方法将旧的入口合约资金转移到新的入口合约中,因此写了kill函数,当更新发生时,将新的合约地址作为参数,执行如下旧合约的kill方法:


0?wx_fmt=png


Solidity的using关键词


上面反复提到“入口合约调用library来去操作数据合约的数据”,这个地方使用到了using这个Solidiy关键词。


using关键词是Solidity的contract引用library时使用,例子如下:


pragma solidity ^0.4.15;
library SomeLibrary  {
 function add(uint self, uint b) returns (uint) {
   return self+b;
 }
}
contract SomeContract {
    
    using SomeLibrary for uint;
    
    function add3(uint number) returns (uint) {
        return number.add(3);    
    }
}


using SomeLibrary for uint 意思是在SomContract合约中,uint类型的参数可以直接调用SomeLibrary的方法,并且将uint自身作为第一个参数传递到library的方法中。


回到项目中,“入口合约调用library来去操作数据合约的数据”,即调用library方法的同时,需要将数据合约作为参数提供给library,因此代码可以写成如下:


0?wx_fmt=png


在入口合约初始化的时候,将数据合约赋值给tradeData,因为有了using LogicLibrary for address, 因此address类型的tradeData可以直接调用library的方法,并且将其自身(数据合约的地址)传递给了library。


合约间调用的return问题


按照上面的方式改造,在测试过程中,发现当执行查询数据的时候,合约间返回非定长类型的string,会出错,上stackexchange的Ethereum板块中,找到如下解答:


0?wx_fmt=png


也就是说Solidity对于合约外部调用,返回变长类型的字段,EVM是不支持的。回到项目本身,因为订单数据中定义了多个string类型数据,没有办法去改变数据结构,因此采取折中的办法,对于查询操作,客户端直接读取数据合约(缺点是目前的设计方案数据合约是不可改的,但是对于查询,可以将数据合约中全部状态写好get方法,并由客户端对返回的状态进行整理是可以满足需求的)。


部署


本项目智能合约使用了Truffle进行开发编写,对于升级改造之后的部署脚本,代码如下:


0?wx_fmt=png


对于Library与Contract的关联,是通过deployer.link实现在字节码级别上的,并不需要合约代码显式写出来。


最终架构


经过上述实践,智能合约成功完成可升级的改造,架构图如下:


0?wx_fmt=png

后续


经过上面改造,基本满足实验室分布式电商智能合约的升级需求,但是这种方式应用到其他实际场景中,可能存在的问题包括数据合约无法修改;读写操作客户端需要对两个合约进行操作;缺少proxy合约(使得智能合约升级对于客户端dapp无感知)等。所以智能合约的升级改造,还是要和实际需求相结合,此文尽量满足本身项目需求的同时,使用业内通用方法来去实践,若有不足,望批评指正。


参考文章


1. Writing upgradable contracts in Solidity:https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88

2. Solidity’s ‘using’ keyword:https://medium.com/@gus_tavo_guim/soliditys-using-keyword-c05c18aaa088

3. One reason to start using Solidity Libraries:http://blog.ethereum-alarm-clock.com/blog/2015/10/25/one-reason-to-start-using-libraries


出处:http://blog.liuhongnan.com/2017/12/24/智能合约的可升级改造实践/#背景起因


版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。


-END-640?wx_fmt=jpeg


架构文摘

ID:ArchDigest

互联网应用架构丨架构技术丨大型网站丨大数据丨机器学习

640?wx_fmt=jpeg

更多精彩文章,请点击下方:阅读原文

智能合约的可升级改造实践

智能合约的可升级改造实践 背景起因 近期开发基于以太坊的智能合约,为实验室的分布式电商系统提供可信的“第三方信用担保”功能。 由于初期项目需求变动会比较频繁,并且智能合约一经发布于...
  • huangshulang1234
  • huangshulang1234
  • 2018年01月14日 09:50
  • 35

Hello World——一个关于智能合约的信仰

前言任何程序员学习一个新的编程语言,一个最重要的仪式就是写出一个能输出Hello World!的程序。完成后,这才算打开一个新世界的大门。本文的步骤说明十分简单(但步骤完整),大家如果想要看详细的步骤...
  • u013137970
  • u013137970
  • 2016年11月03日 09:48
  • 2091

智能合约和存储空间在哪里?

一. 在对整个区块链有了较为清晰地理解之后,回过头来,我想到了以下的问题: 既然ETH交易不仅有转账的交易,还有部署合约,以及存储空间的使用,那么,智能合约/存储空间如何实现,他们又在哪里,智能合约的...
  • LNZ001
  • LNZ001
  • 2017年06月02日 11:25
  • 920

智能合约编写实例

前言本文主要介绍智能合约的工作原理及其部署过程。合约部署流程一般来说,部署智能合约的步骤为1: 启动一个以太坊节点 (例如geth或者testrpc)。 使用solc编译智能合约。 => 获得二进制代...
  • u013137970
  • u013137970
  • 2016年11月03日 09:44
  • 10559

【转载】智能合约简介

一个简单的智能合约 先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。存储contract SimpleStorage { uint storedData;...
  • binbinxyz
  • binbinxyz
  • 2016年10月11日 17:13
  • 1256

如何开发编译部署调用智能合约

视频链接:http://yuntv.letv.com/bcloud.html?uu=obyao0lchj&vu=c0adea3148&auto_play=0&width=640&height=360&...
  • u013096666
  • u013096666
  • 2017年02月27日 20:04
  • 3044

Fabric 智能合约具体代码模板分析

Fabric的智能合约称为链码(chaincode),分为系统链码和用户链码。系统链码用来实现系统层面的功能,用户链码实现用户的应用功能。链码被编译成一个独立的应用程序,运行于隔离的Docker容器中...
  • w497629433
  • w497629433
  • 2017年07月19日 21:26
  • 1318

区块链2.0:智能合约

区块链2.0:智能合约 区块链2.0是对整个市场的去中心化,利用区块链技术来转换许多不同的资产而不仅仅是比特币,通过转让来创建不同资产单元的价值。   区块链技术的去中心化账本功能可以被用来注册...
  • wangdd_199326
  • wangdd_199326
  • 2017年06月15日 21:03
  • 844

【智能合约】客户端和web端对智能合约的事件Event进行调用的代码示例

客户端和web端对智能合约的事件Event进行调用的代码示例web truffle按官网的例子 http://truffleframework.com/boxes/pet-shoptruffle作为一...
  • diandianxiyu
  • diandianxiyu
  • 2017年11月07日 09:24
  • 1743

以太坊智能合约部署与交互

启动容器来执行geth命令root@ubu-blockchain2:~# docker run -i blockchain101/ethereum-geth:1.6.5 geth attach htt...
  • DDFFR
  • DDFFR
  • 2017年08月01日 18:07
  • 3036
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:智能合约的可升级改造实践
举报原因:
原因补充:

(最多只允许输入30个字)