智能合约最佳实践
所有让以太坊合约安全或不安全的东西同样适用于 OXN。本页先列你应遵循的标准实践,再列 OXN 专属增补。
标准 EVM 安全(不变适用)
- 防重入。 用 OpenZeppelin 的
ReentrancyGuard,或 checks-effects-interactions 模式。外部调用(包括call{value: x})可能递归回你的合约。 - 在每个外部边界校验输入。 不要信任调用者。
- 用等价于
SafeMath的行为。 Solidity 0.8+ 内建溢出检查;没有特定理由不要禁用。 - 遵循 checks-effects-interactions。 状态改动先做完,再做外部调用。
- 支付偏向 pull 而不是 push。 让接收方 来取,不要推给他们。
- 限制循环迭代。 无界循环会撞上 gas limit 变成砖。
- 用久经考验的库。 OpenZeppelin Contracts 是黄金标准。Solmate 是精简替代。
- 访问控制门。 用
AccessControl或显式require(msg.sender == owner)。永远不依赖tx.origin。 - 每一次状态改动都 emit 事件。 审计与监控依赖它们。
OXN 专属增补
不要假设存储不透明
加密存储隐藏值,不隐藏布局。 节点运营方 dump 原始存储看到的是哪些槽被填充、mapping 有多大、每个槽最近何时被写。设计存储布局时把这个考虑进去。极高敏感度场景考虑 oblivious 模式(哪怕只改一个也写所有槽)。
理解加密事件的约束
事件内容只能被 emit 它们的调用者解密。这改变了一些模式:
- 永远不要依赖全局索引器解密你应用的事件。
- 需要公开事件可见性(钱包、浏览器、索引器)时,emit 一份不含敏感字段的明文伴随事件。
- 永远不要把敏感字段设为
indexed——即便加密,索引 topic 通过 Bloom filter 匹配也能泄漏"存在"。
防 Gas 侧信道
如果函数的 Gas 使用量依赖某个秘密的值(基于加密状态的分支),观察到的 Gas 就会泄漏这个值:
// 错:不同分支 gas 用量不同
function bad(uint256 secret) external {
if (secret > 100) {
// 昂贵路径
} else {
// 便宜路径
}
}
观察你交易的攻击者能仅凭 Gas 区分两种情形。安全关键逻辑用无分支代码或常数时间模式:
// 更好:两个分支都执行
function ok(uint256 secret) external {
uint256 pathA = expensiveCompute();
uint256 pathB = otherExpensiveCompute();
result = secret > 100 ? pathA : pathB;
}
用安全随机数
永远不要用 block.timestamp、block.number、block.hash、PREVRANDAO 做随机值。用 Sapphire.randomBytes——见随机数。
锁 evmVersion: "paris"
用更新的 EVM 版本编译会 产出 OXN 无法执行的 opcode。把它做成 CI 检查,不只是配置注释。
每个脚本都显式 gasLimit
每笔交易都显式固定 gasLimit,把它做成代码评审标准——客户端估算不是推荐路径。
用 private 存机密,不用 public
public 存储生成明文无认证 getter,抹掉加密:
// 错:自动 getter 是明文 view
mapping(address => uint256) public balances;
// 对:通过门控 view 暴露
mapping(address => uint256) private balances;
function balanceOf(address who) external view returns (uint256) {
require(msg.sender == who, "not authorized");
return balances[who];
}
永不暴露私钥
如果合约持有签名密钥(见签名),永远不要:
- 通过 getter 暴露。
- Emit 到事件。
- 作为返回值传出。
- 从 view 函数返回。
运行时会加密它,但把它交还给客户端的 bug 让加密毫无意义。
标准工具链
- 静态分析:Slither、Mythril。它们不懂 OXN 专属模式,但抓通用 Solidity bug。
- 形式化验证:Certora、Halmos。同样注意点。
- 模糊测试:Foundry 内建 fuzzer、Echidna。跑在本地无机密性的 EVM 上,但能抓出逻辑 bug。
- 真实 OXN 上的集成测试:抓 fuzzer 抓不到的机密性相关问题。