Solidity

自拜占庭以來在可升級合約中使用高級代表呼叫

  • March 1, 2022

自從拜占庭以來,我們可以通過使用returndatacopyreturndatasize彙編指令更容易地實現可升級的代理合約。這意味著我們不再需要像使用EtherRouter時那樣註冊返回類型和大小。

我們知道如何建構代理合約的最可靠方法就像Zeppelin 代理,它delegatecall是在組裝中製造的。delegatecall然而,當代理合約回退函式看起來像這樣的高級 Solidity 呼叫時,它似乎也可以工作:

function () public {

   bool callSuccess = upgradableContractAddress.delegatecall(msg.data);

   if (callSuccess) {
       assembly {
           returndatacopy(0x0, 0x0, returndatasize)
           return(0x0, returndatasize)
       }
   } else {
       revert();
   }
}

這種方法(請參閱此處的整個代理)更簡潔一些,並且需要較少的彙編知識即可理解。我對這種方法的最小測試似乎有效。

那麼在什麼情況下這種高級方法不起作用呢?

如果沒有,高級委託呼叫的編譯字節碼在 Solidity 版本之間發生變化的可能性有多大,從而破壞了這些版本的這種方法?

一個問題是您將數據複製到從 address 開始的記憶體中0。這將適用於小於 64 字節的返回大小,但此時將開始覆蓋其他記憶體。

相反,您應該做更多類似的事情

let m := mload(0x40)
returndatacopy(m, 0, returndatasize)
return(0, returndatasize)

要緩解@Tjaden Hess 提到的問題,請執行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);
       }
   }

如果呼叫沒有失敗,請像在 Solidity 中通常那樣返回數據。否則,通過程序集還原以找出還原原因。

專業提示:請參閱我在PRBProxy中的實現。

引用自:https://ethereum.stackexchange.com/questions/37601