矿工就该做优化。更好的 kernel、更紧凑的调度、把每一瓦电都用足——竞争本来就该逼出这些东西。但共识仍然要分得清:哪些是更快完成的诚实工作,哪些只是拿一份更便宜的计算冒充另一回事。
这条线在主网上被试探过。有矿工在一块较小的 GPU 上以 fp8 运行已承诺的模型,然后提交了两份证明,声称用的是基准精度 bfloat16。两个区块都传到了网络上,但都没有拿到任何工作量。
精心伪装的精度谎言
这份伪造是下了真功夫的。攻击者改了矿工端的源码,让它上报一个自己根本没在用的精度;还得把 fp8 的数值对齐到 bf16 的网格上,好让这些数字看上去就是真正的 bf16。但这只解决了存储格式的问题——算出这些数字的计算本身用的是什么精度,它管不着。
证明本身也是自洽的。在全部 256 个生成步上,验证者都从已承诺的输入重新推导出规定的随机抽样值 u,再拿它在矿工提交的支撑集上重放一遍。无论是诚实对照组还是两份 fp8 证明,它都复现出了声称的 token:256 次中命中 256 次。
这证明了 token 确实是按提交的支撑集选出来的,却没有证明这个支撑集来自一次 bf16 的前向传播。
破绽在哪:独立重算一遍模型
完整验证做的是那件一锤定音的事:用 bf16 把已承诺的模型再跑一遍,再拿新算出的 logits 和提交的证据做比对。
经过校准的马氏距离检验会在每一步给出一个 p 值。在它的诚实原假设下,这些 p 值应当大致服从均匀分布。而两份 fp8 证明的 p 值却全都堆在 0 附近:
| 证明 | p < 0.05 的步数 | p < 0.001 的步数 |
|---|---|---|
| 区块 1 | 173 / 256 | 155 / 256 |
| 区块 2 | 105 / 256 | 87 / 256 |

重算还暴露出,提交的前 50 个支撑集 token 的顺序发生了大幅变化。这里的 rank_error 指的是这 50 个提交 token 在 bf16 排序中的总位移,不是被选中 token 在词表中的排名。两份证明的位移中位数分别是 602 和 557,最差单步则达到 110,888 和 15,726。这些数字属于旁证性的取证材料;真正在生产环境里作数的检测结论,是那批校准后 p 值的崩塌。

两个区块都被标记为 RED,分配到的工作量为零。它们在链上表现为孤块,但这不是一次传播竞速的结果:链根本就没有在它们之上延伸过。
更聪明的攻击:先用 fp8 搜索,再用 bf16 精修
实际发生的这次攻击是直接的精度替换。更聪明的版本会设法绕开这套统计比对:
- 用 fp8 低成本地搜索,找到一条中意的候选轨迹。
- 只针对这条候选轨迹,重新算出真正的 bf16 支撑集和 logits。
- 提交这些真实的 bf16 数值——它们现在能通过重算比对。
采样路径的作用就在这里体现出来。每一个 u 都由不断累积的上下文推导而来。fp8 搜索是沿着 fp8 支撑集把这些抽样值消耗掉的。一旦把支撑集换成真实的 bf16 证据,同一个抽样值就可能选出不同的 token。
在一次反事实重放中,把 fp8 的支撑集和 logits 换成重算出的 bf16 证据之后,记录下来的抽样值在 256 步里的 62 步和 69 步上选出了不同的 token。这只是反事实实验中 token 发生变化的步数,既不是生产环境的判定结果,也不是诚实与 fp8 之间的失败率。

只要变一处就够了。token 一变,下一步的上下文就变;由上下文推导出的下一个 u 也跟着变;轨迹的其余部分随之全部改变。低成本 fp8 搜索找到的那条候选,就此消失。
**精修修好了提交的数值,却让攻击者搜出来的那条轨迹失效。**想拿回一条有效的候选,矿工只能在 bf16 下重新采样、重新搜索——捷径也就不存在了。
信任如何兑现
第一种攻击失败,是因为独立重算出的数值对不上。它最直接的那种改进版同样失败,是因为数值和采样轨迹必须描述同一次计算。
在这里,信任兑现的方式就是这样:不是相信我们,而是给出一份任何人都能重算的证明。工作周边的一切,尽管去优化。工作本身,还是得是真的。
自己跑一个验证者。别信这份证明,自己验一遍。
本文由 Imosuke Takakuni 以化名撰写。