@@ -15,6 +15,74 @@ timezone: UTC+8
1515## Notes
1616
1717<!-- Content_START -->
18+ # 2025-08-15
19+
20+ # How to prevent reentrancy attack
21+ ### 使用重入鎖
22+ 重入鎖是一種重入函數的modifier,包含一開始是0的狀態變量(_ status),一開始檢查_status是否為0,接著馬上把_status改為1,等最後結束後才把_status改為0。這樣攻擊合約一定要等調用結束才能再發起第二次的調用。也因此重入攻擊無法成功。
23+
24+ ``` jsx
25+ uint256 private _status; // 重入锁
26+
27+ // 重入锁
28+ modifier nonReentrant () {
29+ // 在第一次调用 nonReentrant 时,_status 将是 0
30+ require (_status == 0 , " ReentrancyGuard: reentrant call" );
31+ // 在此之后对 nonReentrant 的任何调用都将失败
32+ _status = 1 ;
33+ _;
34+ // 调用结束,将 _status 恢复为0
35+ _status = 0 ;
36+ }
37+ ```
38+ ### Openzeppelin code
39+
40+ [ OpenZeppelin ReentrancyGuard] ( https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.1/contracts/utils/ReentrancyGuard.sol )
41+
42+ ``` jsx
43+ abstract contract ReentrancyGuard {
44+ uint256 private constant NOT_ENTERED = 1 ;
45+ uint256 private constant ENTERED = 2 ;
46+
47+ uint256 private _status;
48+
49+ error ReentrancyGuardReentrantCall ();
50+
51+ constructor () {
52+ _status = NOT_ENTERED ;
53+ }
54+
55+ // 重入鎖在這裡
56+ modifier nonReentrant () {
57+ _nonReentrantBefore ();
58+ _;
59+ _nonReentrantAfter ();
60+ }
61+
62+ function _nonReentrantBefore () private {
63+ // On the first call to nonReentrant, _status will be NOT_ENTERED
64+ if (_status == ENTERED ) {
65+ revert ReentrancyGuardReentrantCall ();
66+ }
67+
68+ // Any calls to nonReentrant after this point will fail
69+ _status = ENTERED ;
70+ }
71+
72+ function _nonReentrantAfter () private {
73+ _status = NOT_ENTERED ;
74+ }
75+
76+ function _reentrancyGuardEntered () internal view returns (bool) {
77+ return _status == ENTERED ;
78+ }
79+ }
80+ ```
81+ ### Checks-effect-interaction
82+ 1 . 檢查狀態變量
83+ 2 . 馬上更新狀態變量
84+ 3 . 再把錢給別人
85+
1886# 2025-08-14
1987
2088# 第5讲(數據儲存位置)
0 commit comments