Solidity. Смарт контракты и аудит
2.62K subscribers
246 photos
7 videos
18 files
555 links
Обучение Solidity. Уроки, аудит, разбор кода и популярных сервисов
Download Telegram
Ethernaut. Задача 17. Recovery

В этой задаче нам нужно вывести все токены контракта, который был создан, и чей адрес не записан. Другими словами нам нужно восстановить потерянный адрес сгенерированного контракта.
Ссылка на задачу

В чем суть?

Код задачи простой: по сути, нам нужно восстановить адрес и потом вызвать selfdestruct(), чтобы вернуть деньги. Все умещается в одной функции.

Давайте лучше поговорим, как генерируется адрес контракта, чтобы можно было решать подобные задачи в будущем.

Мы не будем углубляться в EVM и его процессы, а разберем базу для задачи.

Итак, есть два способы генерации адреса: старый и новый. Начнем с первого.

В старом способе генерация адреса контракта происходила с помощью данных о номере транзакции / количестве созданный контрактов из Фабрики (nonce) и адресе вызывающего. Также нам потребуется знать, что такое RLP.

Целью RLP (RECURSIVE-LENGTH PREFIX) является кодирование произвольно вложенных массивов двоичных данных. Это основной метод кодирования, используемый для сериализации объектов на исполнительном уровне Ethereum. Подробнее про него можно прочитать тут.

В случае 20 битового адреса RLP будет 0хd6 и 0х94.
Также, в задаче это был первый созданный контракт, поэтому nonce будет равен 1.

Код в Solidity выглядел так:

bytes1(0xd6), bytes1(0x94), address(sender), bytes1(0x01)

Все это нам нужно будет перевести в байткод, затем в хеш => uint265 =>uint160, отсюда получим адрес:

address lostContract = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), address(sender), bytes1(0x01))))));

Второй способ генерации адреса связан с opcode Create2, предложенный Виталиком Бутериным (EIP-1014).

Create2 использует другую формулу для генерации:

keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:]

где:

address — адрес смарт-контракта, который будет вызывать CREATE2;
salt — случайное значение;
init_code — байт-код смарт-контракта для развертывания;

Таким образом гарантируется, что адрес, который мы предоставляем пользователю, действительно будет содержать желаемый байт-код.

Функция может выглядеть так:

function getAddress(bytes memory bytecode, uint _salt)
public view returns (address)
{
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode))
);
return address(uint160(uint(hash)));
}

Полный пример с кодом можно посмотреть тут.

Я много гуглил по работе EVM и RLP, и мне стоило больших усилий понять, как это работает. Если вы захотите, после всех тем по безопасности, я могу написать отдельный пост про генерацию адресов с разбором RLP.

#ethernaut #generation #address #creat2