安全模式 #
一、检查-生效-交互模式 #
1.1 模式描述 #
这是最重要的安全模式,确保状态在交互前正确更新。
1.2 错误示例 #
solidity
// 错误:先交互,后更新状态
function withdraw() public {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// 交互:先转账
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
// 生效:后更新状态 - 危险!
balances[msg.sender] = 0;
}
1.3 正确示例 #
solidity
// 正确:检查-生效-交互
function withdraw() public {
// 1. 检查
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// 2. 生效:先更新状态
balances[msg.sender] = 0;
// 3. 交互:后转账
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
二、互斥锁模式 #
2.1 基本实现 #
solidity
contract Mutex {
bool private _locked;
modifier nonReentrant() {
require(!_locked, "Reentrant call");
_locked = true;
_;
_locked = false;
}
mapping(address => uint256) public balances;
function withdraw() public nonReentrant {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
balances[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
2.2 OpenZeppelin实现 #
solidity
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract MyContract is ReentrancyGuard {
mapping(address => uint256) public balances;
function withdraw() public nonReentrant {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
balances[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
三、拉取支付模式 #
3.1 问题:推送支付 #
solidity
// 有问题:推送支付可能导致DoS
contract PushPayment {
mapping(address => uint256) public balances;
function withdrawAll() public {
for (uint256 i = 0; i < investors.length; i++) {
payable(investors[i]).transfer(balances[investors[i]]);
}
}
}
3.2 解决:拉取支付 #
solidity
contract PullPayment {
mapping(address => uint256) public balances;
mapping(address => bool) public withdrawn;
function withdraw() public {
require(!withdrawn[msg.sender], "Already withdrawn");
require(balances[msg.sender] > 0, "No balance");
withdrawn[msg.sender] = true;
uint256 amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
四、紧急停止模式 #
4.1 基本实现 #
solidity
contract Pausable {
bool public paused;
address public owner;
event Paused(address account);
event Unpaused(address account);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier whenNotPaused() {
require(!paused, "Contract is paused");
_;
}
modifier whenPaused() {
require(paused, "Contract is not paused");
_;
}
function pause() public onlyOwner whenNotPaused {
paused = true;
emit Paused(msg.sender);
}
function unpause() public onlyOwner whenPaused {
paused = false;
emit Unpaused(msg.sender);
}
}
4.2 OpenZeppelin实现 #
solidity
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyContract is Pausable, Ownable {
function sensitiveOperation() public whenNotPaused {
// 敏感操作
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
}
五、访问控制模式 #
5.1 基于角色 #
solidity
import "@openzeppelin/contracts/access/AccessControl.sol";
contract RoleBased is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
}
function adminOperation() public onlyRole(ADMIN_ROLE) {
// 管理员操作
}
function operatorOperation() public onlyRole(OPERATOR_ROLE) {
// 操作员操作
}
}
六、总结 #
安全模式要点:
| 模式 | 说明 |
|---|---|
| 检查-生效-交互 | 先更新状态,后外部调用 |
| 互斥锁 | 防止重入攻击 |
| 拉取支付 | 避免批量转账DoS |
| 紧急停止 | 可暂停合约 |
| 访问控制 | 基于角色的权限 |
接下来,让我们学习最佳实践!
最后更新:2026-03-27