由于Byzantium,我们可以通过使用returndatacopy和returndatasize程序集指令实现可升级的代理契约。这意味着我们不再需要注册返回类型和大小,比如在使用EtherRouter时。
我们知道如何构造代理契约的最可靠的方法是类似于齐柏林星代理,其中delegatecall是在程序集中生成的。但是,当将delegatecall作为一个高级别的可靠调用执行时,它似乎也起作用,代理契约回退函数看起来如下所示:
function () public {
bool callSuccess = upgradableContractAddress.delegatecall(msg.data);
if (callSuccess) {
assembly {
returndatacopy(0x0, 0x0, returndatasize)
return(0x0, returndatasize)
}
} else {
revert();
}
}这种方法(请参阅这里的整个代理)更加简洁,需要更少的组装知识才能理解。对于这种方法,我的最小测试似乎奏效了。
那么,在什么情况下,这种高层次的方法会不起作用呢?
如果没有,那么高级委托的编译后的字节码有多大可能会在不同的版本之间发生变化,从而打破了这些版本的这种方法?
发布于 2018-05-23 04:11:00
一个问题是从address 0开始将数据复制到内存中。这将适用于小于64个字节的返回大小,但此时将开始覆盖其他内存。
相反,你应该做一些更像
let m := mload(0x40)
returndatacopy(m, 0, returndatasize)
return(0, returndatasize)发布于 2021-08-31 09:48:47
为了缓解@Tjaden提到的问题,什么 OpenZeppelin做了以下工作:
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}如果调用没有失败,则返回数据,就像正常情况下一样。否则,通过程序集还原,使还原原因冒泡。
小贴士:请参阅我在PRBProxy中的实现。
https://ethereum.stackexchange.com/questions/37601
复制相似问题