Начиная с Byzantium, мы можем реализовать обновляемые прокси-контракты намного проще с помощью инструкций по returndatacopy
сборке returndatasize
. Это означает, что нам больше не нужно регистрировать возвращаемые типы и размеры, как при использовании 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, нарушая этот подход для этих версий?
Одна проблема заключается в том, что вы копируете свои данные в память, начиная с адреса 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 .
сканг404
хорошо
Пол Разван Берг