Раскрыта ошибка, связанная с функцией delegatecall в Solidity

В этот раз мы поговорим о критически важной функции нижнего уровня delegatecall. Она широко используется в паттерне прокси, например, при использовании паттерна модернизации OpenZepplin. По сути, эта функция выполняет код в другом контракте, но использует данные из вызывающей концепции. Давайте копнем глубже и рассмотрим подробнее.

Столкновение хранилищ

При использовании функции delegatecall крайне важно отметить, что переменные состояния должны находиться в одинаковом порядке в обоих контрактах. Если это не так, то могут произойти странные вещи. Например, данные будут перезаписаны.

Лабиринт данных

Возьмем контракт Executor, который сохранит наш счастливый номер, адрес отправителя и значение Ether, отправленное ему.

pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Executor {
  uint256 public luckyNumber;
  address public sender;
  uint256 public value;

  function setLuckyNumber(uint256 _luckyNumber) public payable {
    luckyNumber = _luckyNumber;
    sender = msg.sender;
    value = msg.value;
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Давайте развернем контракт Executor и получим развернутый адрес.

После этого мы можем создать смарт-контракт Caller.

contract Caller {
  uint256 public luckyNumber;
  address public sender;
  uint256 public value;

  function setLuckyNumber(address executor, uint256 _luckyNumber) public payable {
    (bool success, bytes memory data) = executor.delegatecall(
      abi.encodeWithSignature("setLuckyNumber(uint256)", _luckyNumber)
    );

    console.log(success);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, когда мы взаимодействуем с контрактом Caller и устанавливаем счастливое число, мы видим, что данные были изменены только в этом контракте, а не в контракте Executor. Чтобы проверить это, мы можем получить счастливое число из обоих контрактов.

Потенциальная уязвимость

Использование функции delegatecall является привлекательной возможностью в языке программирования Solidity. С большой силой приходит большая ответственность. Ее неправильное использование может быть опасным.

Двумя основными уязвимостями являются ошибки при сохранении контекста вызывающей стороны и несоответствие компоновки хранилища. В этот раз мы не будем вдаваться в подробности, но рассмотрим их в следующих статьях.

TL;DR

delegatecall — это функция нижнего уровня в языке программирования Solidity. Она разблокирует возможность взаимодействия одного смарт-контракта с другим, используя контекст вызывающей стороны. При использовании delegatecall следует быть очень осторожным, так как неправильное ее использование может привести к серьезным уязвимостям.

Ссылки

  • Код примера

  • Официальная документация

  • Delegatecall — Solidity на примере

  • EIP-1967: Стандартные слоты для хранения прокси

  • Обновления OpenZepplin

  • Уязвимости Delegatecall

Оцените статью
devanswers.ru
Добавить комментарий