智能合约从技术角度实现了"codeislaw",在智能合约的世界里,代码本身就是法律规则的体现。这一理念的核心是,智能合约是自执行的协议,由编写好的代码直接控制,无需中介或第三方干预。
大家都说,智能合约从技术角度实现了"code is law",在智能合约的世界里,代码本身就是法律规则的体现。这一理念的核心是,智能合约是自执行的协议,由编写好的代码直接控制,无需中介或第三方干预。一旦智能合约被部署并启动,它就会按照代码中的逻辑自动运行,任何参与者都必须遵循其中的规则。
是的,智能合约是不可改变的,一旦一个合约被部署,意味着你不能在这个地址调整合约的任何功能,你只能与它进行交互。为什么要这么做?试想一下,如果说有一天某个控制者突然对合约中的规则做出了有利于他们的规定,那么这个合约就不值得信赖了。这与传统的系统有着鲜明的区别。
一句话总结:代理让合约升级成为事实上的标准,但是上面我们明明已经说了合约是不可以改变的,这不是前后矛盾吗?
其实,合约不可改变这个点是仍然正确的,但是代理可以解决这个问题。
尽管区块链的不可变性有很多好处,但在多个版本中推送bug修复和补丁是不能忽视的,而且非常需要修补bug和安全漏洞。代理模式解决了这个问题。让我们来看看代理如何工作的。
在深入讨论之前,我们先说明一些背景术语:
代理合约将实现合约地址存储为一个状态变量。与普通合约不同的是,用户实际上并不直接向实现合约发送调用。相反,所有的调用都要经过代理合约,这个代理合约将调用委托给实现合约,并把从实现合约收到的任何数据返回给调用者,或者对错误进行回退。
delegatecall
User ----------> Proxy -----------> Implementation
(storage layer) (logic layer)
这里需要注意的关键是,代理合约通过delegatecall
函数调用实现合约。因此,实际上是由代理合约来存储状态变量,即它是存储层。这就像你只是从实现合约借用逻辑,并在代理合约的上下文中执行,并影响代理合约在存储中的状态变量。
举个例子,考虑一个简单的Box
(实现)合约,以及BoxProxy
(代理)合约。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Box {
uint256 private _value;
function store(uint256 value) public {
_value = value;
}
function retrieve() public view returns (uint256) {
return _value;
}
}
contract BoxProxy {
function _delegate(address implementation) internal virtual {
// delegating logic call to boxImpl...
}
function getImplementationAddress() public view returns (address) {
// Returns the address of the implementation contract
}
fallback() external {
_delegate(getImplementationAddress());
}
}
尽管Box
定义了一个uint256
状态变量_value
,但实际上是BoxProxy
合约存储了与_value
相关的值。
委托相关的逻辑通常被放在代理合约的fallback
函数中。
因此升级机制可以理解为:通过授权改变代理合约存储实现合约地址变量的,以指向新部署的、升级的实现合约。这样升级就完成了。代理合约现在将调用委托给这个新的实现合约。虽然那个旧的合约会永远存在。
upgrade call
Admin -----------> Proxy --x--> Implementation_v1
|
--------> Implementation_v2
很简单吧?但是有一些问题,比如代理和实现合约之间潜在的、由delegatecall
引起Collisions of Solidity Storage Layouts。
我们不能简单地在代理合约中声明 address implementation
,因为这会引起与实现合约的存储发生冲突,即实现合约中的多个变量在存储槽中有重叠。
|Proxy |Implementation |
|------------------------|---------------|
|address implementation |address var1 | <- 碰撞!
| |mapping var2 |
| |uint256 var3 |
| |... |
对实现合约中的var1
的任何写入,实际上都会写入Proxy
合约中的implementation
(存储层)!
解决方案是选择一个伪随机槽,并将 implementation
的地址写入该槽中。这个...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!