什么是整数溢出和 unchecked?Solidity 0.8 为什么默认让交易失败

Solidity 0.8 默认在整数溢出或下溢时回退,unchecked 会恢复绕回行为。本文讲清边界、类型转换、位移、Gas 与安全证明。

什么是整数溢出和 unchecked?Solidity 0.8 为什么默认让交易失败

一个 uint8 最大只能保存 255。如果它已经是 255,再加 1 会怎样?在普通数学里答案是 256;在固定位宽整数里,结果要么被拒绝,要么绕回 0。智能合约中的余额、计数、价格和份额都依赖整数,这种边界差异可能直接变成资产漏洞。

Solidity 0.8.0 起,整数算术默认检查溢出和下溢,超出类型范围会回退。开发者也可以用 unchecked 显式恢复绕回行为,但必须证明边界安全或确实需要模运算。

Unchecked 不是“更快模式”,而是开发者主动拿回边界正确性的责任。

什么是溢出和下溢

运算结果大于类型最大值叫溢出,小于最小值叫下溢。uint8 范围是 0 到 255,255+1 超界;无符号 0-1 同样超界。int 类型同时有正负边界,最小负数取反也可能溢出。

Solidity 0.8 之后默认会怎样

加减乘等受检查运算超界时,编译器触发 Panic,常见错误码为 0x11,当前调用状态回退。于是很多过去依赖 SafeMath 的基础检查由语言默认提供,但业务公式和类型转换仍可能出错。

Solidity 0.8 整数运算超出范围默认触发溢出检查回退
默认检查把静默绕回变成显式失败,更容易阻止错误状态继续传播。

0.8 之前为什么常见 SafeMath

旧版 Solidity 的整数算术通常直接绕回,合约需要库函数先检查结果。阅读历史合约时必须确认编译器版本,不能把新版本的默认保护假设套到旧字节码上。

Unchecked 做了什么

写在 unchecked { ... } 语法块内的相关整数运算发生超界时会绕回,不触发默认检查。例如 uint256 的 0-1 会得到最大值。它只影响块内语句,调用的其他函数不会自动继承 unchecked。

Solidity unchecked 优化前必须证明整数边界和循环不变量
每个 Unchecked 都应附带能被测试和审计的安全理由。

哪些运算受影响

官方文档列出的相关操作包括递增递减、加减乘、除法、取模、乘方以及对应复合赋值。除零和模零即使放进 unchecked 也不能关闭检查,仍会 Panic。

操作默认检查Unchecked 内
uint 最大值 + 1回退绕回 0
uint 0 - 1回退绕到最大值
除以 0回退仍回退
显式缩窄转换可能截断行为不由 unchecked 改变
位移不做同类溢出检查同样不检查

类型转换是另一条风险路径

把 uint256 显式转成 uint8,过大的高位会被截断,并不会因为默认算术检查自动回退。开发者要在转换前验证范围,或使用提供安全转换检查的成熟库。

位移也不会替你检查范围

位运算和左移不执行与乘法相同的溢出检查。用左移代替乘以 2 的幂,可能在高位被丢弃而不回退。Gas 优化不能只比较结果在常见输入下相同,还要证明完整输入域一致。

什么时候 Unchecked 可能合理

  • 循环计数器在严格边界下递增,已证明不可能到最大值。
  • 先显式检查 a >= b,再在块内执行 a-b。
  • 算法本身需要模 2 的固定位宽绕回。
  • 性能分析确认检查是热点,且不变量可清楚审计。

什么时候不应该使用

余额、供应量、份额、价格、债务和权限计数通常不应仅为省 Gas 关闭检查。若安全理由需要一长串隐含前提才能成立,保留默认检查往往更可靠。

先检查后 Unchecked 也可能有漏洞

检查的表达式必须与实际运算使用同一变量、同一单位和同一时刻。外部调用后状态可能变化,复杂表达式可能在检查前已经溢出,错误的严格/非严格边界也会漏掉最大值。

循环递增为何常被优化

若循环条件保证 i 小于数组长度,并且数组长度远小于 uint256 最大值,i++ 理论上不会溢出。一些代码把递增放入 unchecked 节省检查。但还要确认循环体不会修改长度或 i,边界证明不会被重构破坏。

错误码如何帮助排查

0x11 通常指 checked 算术溢出或下溢。定位失败交易时查看调用栈、源码行和输入,判断是恶意边界、业务数据异常还是程序不变量被破坏。不要简单把语句改成 unchecked 让交易“通过”。

公式顺序也会造成中间溢出

最终结果在范围内,不代表中间值一定安全。例如先乘后除可能在乘法阶段超界;先除后乘又可能损失精度。需要使用经过审查的精度算法、扩大中间表示或重新排列公式,并验证舍入方向。

边界测试怎么设计

类别代表值
无符号边界0、1、max-1、max
有符号边界min、min+1、-1、0、max
类型转换目标 max、max+1、高位非零
业务组合最大余额、份额总和、极端价格
性质测试守恒、单调、结果范围与可逆条件

使用模糊测试和不变量

与其只列几个样例,不如定义总供应量等于余额和、用户不能提取超过权益、份额不会凭空增加等性质,让工具生成大量边界输入。发现反例后保留为回归测试。

审计每个 Unchecked 的方法

  1. 列出块内全部算术与类型。
  2. 写出每个输入可能范围和来源。
  3. 证明执行前不变量如何建立。
  4. 检查外部调用、继承和代理升级是否破坏前提。
  5. 验证边界、随机输入和失败路径。
  6. 记录为何需要 unchecked 及预期收益。

用户为什么也应关注

审计报告中的 arithmetic、rounding、unsafe cast 和 unchecked 可能影响余额与份额。项目使用 Solidity 0.8+ 并不代表所有数学天然安全;默认检查只能发现超出类型范围,不能判断经济公式是否正确。

官方资料

本文依据 Solidity:Checked or Unchecked Arithmetic。不同编译器版本默认行为不同,分析合约时应核对实际编译设置。

默认溢出检查是重要安全网,unchecked 则是需要证明的例外。真正可靠的优化会把边界、不变量、测试和 Gas 数据一起交给审查者,而不是留下一句“这里不可能溢出”。


本文为链上指南原创科普内容,不构成任何投资建议。整数边界、精度与类型转换可能影响真实资产,开发和交互前请结合测试、验证源码与独立审计。