什么是智能合约?
智能合约(Smart Contract) 是一种运行在区块链上的自动执行的计算机程序,当预设条件被满足时,合约会自动执行相应的操作,无需第三方中介。
核心特点
- 自动执行:条件触发后自动运行,无需人工干预
- 不可篡改:部署后代码无法修改,保证执行结果可信
- 透明公开:合约代码对所有网络参与者可见
- 去信任化:通过代码而非信任来执行协议
Solidity 智能合约执行原理
1. 合约部署流程
solidity// 示例:简单的存储合约 pragma solidity ^0.8.0; contract SimpleStorage { uint256 private storedData; event DataChanged(uint256 newValue); function set(uint256 x) public { storedData = x; emit DataChanged(x); } function get() public view returns (uint256) { return storedData; } }
部署步骤:
- 编译:Solidity 代码编译为 EVM 字节码
- 交易创建:创建部署交易,包含字节码
- 矿工打包:交易被矿工打包进区块
- 合约创建:EVM 执行创建操作,分配合约地址
- 状态存储:合约代码和初始状态存储在区块链上
2. EVM 执行模型
shell交易发起 → Gas 费用检查 → EVM 执行 ↓ 栈(Stack)操作 内存(Memory)操作 存储(Storage)操作 ↓ 状态更新或回滚 ↓ 交易收据生成
Gas 机制:
- 每笔操作消耗 Gas,防止无限循环
- Gas Price × Gas Limit = 最大交易费用
- Gas 不足时交易回滚,但已消耗的 Gas 不退还
3. 合约调用方式
| 调用方式 | 特性 | 使用场景 |
|---|---|---|
call | 不创建新上下文,可指定 Gas | 调用外部合约 |
delegatecall | 在当前合约上下文执行 | 代理合约模式 |
staticcall | 只读调用,不修改状态 | 查询操作 |
常见安全漏洞
1. 重入攻击(Reentrancy Attack)
漏洞代码:
solidityfunction withdraw() public { uint256 amount = balances[msg.sender]; require(amount > 0); (bool success, ) = msg.sender.call{value: amount}(""); // 外部调用 require(success); balances[msg.sender] = 0; // 状态更新在后 }
攻击原理:
- 攻击者合约在接收 ETH 时触发 fallback 函数
- fallback 函数再次调用 withdraw
- 形成递归调用,重复提取资金
防护措施:
solidityfunction withdraw() public { uint256 amount = balances[msg.sender]; require(amount > 0); balances[msg.sender] = 0; // 先更新状态 (bool success, ) = msg.sender.call{value: amount}(""); require(success); }
2. 整数溢出/下溢(Integer Overflow/Underflow)
漏洞代码:
solidityfunction transfer(address to, uint256 amount) public { balances[msg.sender] -= amount; // 可能下溢 balances[to] += amount; // 可能溢出 }
防护措施:
- 使用 Solidity 0.8.0+(内置溢出检查)
- 或使用 SafeMath 库
solidityusing SafeMath for uint256; function transfer(address to, uint256 amount) public { balances[msg.sender] = balances[msg.sender].sub(amount); balances[to] = balances[to].add(amount); }
3. 访问控制漏洞
漏洞代码:
solidityfunction destroy() public { selfdestruct(payable(msg.sender)); // 任何人可调用 }
防护措施:
solidityaddress public owner; modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } function destroy() public onlyOwner { selfdestruct(payable(owner)); }
4. 前端运行攻击(Front-running)
攻击原理:
- 攻击者监控内存池(mempool)中的待处理交易
- 支付更高 Gas Price 抢先执行类似交易
- 常见于 DEX 交易和拍卖场景
防护措施:
- 提交哈希承诺,延迟揭示
- 使用提交-揭示模式(Commit-Reveal)
安全最佳实践
- 使用成熟的安全库:OpenZeppelin Contracts
- 代码审计:部署前进行专业安全审计
- 形式化验证:使用工具验证合约逻辑
- 测试覆盖:编写全面的单元测试和集成测试
- 权限最小化:遵循最小权限原则
- 紧急暂停:实现可暂停功能应对紧急情况
面试要点
- 理解 EVM 的执行模型和 Gas 机制
- 掌握重入攻击的原理和防护方法
- 了解常见的安全漏洞类型
- 熟悉 OpenZeppelin 等安全库的使用
- 能够设计安全的合约架构