概述
现代软件的设计原则是“敏捷开发,迅速迭代”,功能升级或bug修复是所有软件系统都要面对的问题。甚至可以说软件质量在很大程度上依赖于升级和修补源代码的能力。当然Dapp(去中心化应用)也不例外,尤其Dapp一切都是透明的,这使得任何级别的bug都会被成倍的放大,因此可升级的智能合约成为所有Dapp的必然选择。
本文主要以openzeppelin为基础来阐述构建可升级智能合约的一般流程和注意事项。
原理
openzeppelin通过在用户与智能合约中间加入一个代理来实现合约的透明升级,用户直接与代理交互,代理将用户的请求转发到实际合约,同时将合约的执行结果响应给用户。
示意图
如上图所示,升级时只需要让Proxy指向Implementation合约即可。
上图有如下三种类型合约:
- Proxy
- 用户直接也该合约交互;
- 所有的状态变量都在该合约中维护;
- 该合约符合EIP1967标准;
- 该合约将用户请求透明的转发到Implementation合约,同时将Implementation合约的返回响应给用户;
- Implementation
该合约被称为逻辑合约,Dapp的所有逻辑都在该合约中完成,Proxy以delegatecall的形式调用该合约中的方法。
- ProxyAdmin
在介绍该合约前我们先考虑一个问题——我们如何调用Proxy本身的方法?比如Proxy与Implementation都有一个方法upgradeTo(address),那么当用户调用该方法时,Proxy是该调用其自身方法还是以delegatecall的形式调用Implementation?
OpenZeppelin是通过”透明代理“(transparent proxy )的模式来解决这个问题的。该模式通过发起调用的地址来决定如何调用方法。
- 发起调用的地址为Proxy的管理地址(部署Proxy的地址)时,Proxy将执行自己的方法。
- 发起调用的地址为其它地址时,Proxy将以delegatecall的形式向Implementation发起调用。
假设Proxy有owner()和upgradeTo()方法,Implementation有owner()和transfer()方法,则不同用户发起调用时具体调用方法如下:
msg.sender | owner() | upgradeto() | transfer() |
---|---|---|---|
Owner |
returns proxy.owner() |
returns proxy.upgradeTo() |
fails |
O |