合适的升级方法
现在让我们来看看一些更复杂、更合适的智能合约升级方法。
继承的存储可升级性
这种技术使用三种不同的合约:代理合约来委托调用并充当永久存储;逻辑合约将处理数据;还有存储合约。代理合约和逻辑合约都继承自存储合约,因此它们的存储引用是对齐的。
当逻辑合约更新时,我们只需要更改代理合约所指向的位置即可使用仅管理员功能。由于代理和逻辑协定具有相同的存储指针,因此无需进行外部调用即可查看和修改数据。
不幸的是,这种方法也有其自身的陷阱。由于代理合约和存储合约都是永恒的,因此,如果在任何一个合约中发现错误或漏洞,都无法修复。 因此务必仔细考虑您的代理和存储结构。
非结构化存储可升级性
非结构化存储可能是当前最大的可升级性方法,它使我们能够利用存储中状态变量的布局。此方法仅需要两个合约-代理合约和实施合约-实施合约包含数据和存储。
该技术的工作原理是将可升级性所需的数据保存在存储中的固定位置,以防止被新数据覆盖。我们可以使用SLOAD和SSTORE操作码进行汇编。由于存储插槽只是从0x0开始递增,因此我们使用很高的存储插槽来防止覆盖 我们可以通过对常量变量进行散列来生成存储槽。 由于恒定状态变量不会占用存储空间,因此我们不必担心它会被覆盖。
bytes32 private constant implementationPosition =
keccak256("org.zeppelinos.proxy.implementation");
由于代理不再从存储合约继承而来,因此我们现在也可以更新存储,从而防止存储错误/漏洞变成灾难性的。 但是在升级实施合约时,我们必须继承以前的合约。由于不需要更改实施合约,因此该方法甚至可以与现有合约一起使用。
尽管这可能是当前可升级性最好的方法,但也有不少批评。代理所有者拥有巨大的权力,并且需要一定程度的信任。对于更复杂的系统,这可能也不是合适的解决方案。
升级依赖于构造函数的合约
当使用依赖于构造函数的合约来设置一些初始状态时,与代理工作并不太简单。由于构造函数只运行一次,而代理不知道逻辑合约构造函数中设置的值,因此我们需要一种方法在代理中初始化其中的一些值。
创建逻辑合约后,EVM会丢弃构造函数,因此我们不能简单地重用代码。相反,我们必须采取独特的方法来解决此问题。
初始化函数
一种可能的替代方法是在常规函数中使用构造函数代码。我们只需确保这个函数(我们将调用初始化函数)只能运行一次。
contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private initializing;
/**
* @dev Modifier to use in the initializer function of a contract.
*/
modifier initializer() {
require(initializing || !initialized, "Contract instance has already been initialized");
bool wasInitializing = initializing;
initializing = true;
initialized = true;
_;
initializing = wasInitializing;
}
}
在使用初始值设定项函数时,必须打起十二分精神。考虑逻辑合约继承的基本合约也很重要。这部分特别复杂,因为Solidity也支持多重继承。
结论
确保智能合约是可升级的,并仔细考虑可升级过程,这两点都很重要。虽然这并不是一个关于智能合约可升级性的选项的详尽列表,但这应该是关于这个主题的适当指南。
来源: 区块链研究实验室 作者:链三丰
Copyright © 2012-2021(ent.truecreate.com) 版权所有
本站部份内容来源自网络,文字、素材、图片版权属于原作者,本站转载素材仅供大家欣赏和分享,切勿做为商业目的使用。
如果侵害了您的合法权益,请您及时与我们,我们会在第一时间删除相关内容!