每笔从外部账户发出的交易都会让账户的 nonce 加一,这个「序号计数器」兼做两件事:防止交易重放,以及为合约地址推导提供输入。在早期规范里,它被定义成一个无界无符号整数——理论上永远加得下去。EIP-2681 在 2020 年 4 月 25 日创建、现已是 Final 状态,规则一句话:账户 nonce 必须落在 0 与 2 的 64 次方减一之间。它没有标注重启计算的升级区块,条款写的是追溯至创世适用。
给无界计数器设限,动机是证明与实现
无界整数听起来安全,实际是工程负担。状态证明(比如为轻客户端构造的 witness)要把 nonce 装进编码里,变长大数让 proofs 的实现和带宽都不优;交易格式层面的多项提案也希望 nonce 字段有固定宽度。实现上,主流客户端早已按 64 位存储这个字段——提案文本点名 go-ethereum 的状态账户结构里 nonce 就是 64 位数。EIP-2681 与其说是发明新规则,不如说是把事实标准写进规范,顺便让客户端不再需要内部保留 256 位表示。
两条规则:交易无效与部署返回零
规范给了两条追溯条款。其一,任何交易若其 nonce 字段达到或超过 2 的 64 次方减一,视为无效——否则被打包后发送方计数器的值就会越界。其二,当一个账户的 nonce 恰好是 2 的 64 次方减一时,它执行 CREATE 或 CREATE2 的结果直接是在栈上压一个零,连 initcode 的 gas 都不扣。为什么是返回零而不是撤销:因为部署会先给新地址写代码、新账户 nonce 从一或零起步,若放任这个「最后一次部署」,被创建的合约账户自身状态更新与后续序号语义都会踩在边界上。把部署变成一次免费失败,是最干净的边界处理。
这条线到底能不能摸到
靠发交易把 nonce 刷到 2 的 64 次方减一,每笔至少 21000 gas,提案的算术段给出了精确到个位的账单:387,381,625,547,900,583,915,000 这个 gas——在物理上不具备可达性。合约账户每被调用或部署一次序号也加一,同样到不了。文本还顺带纠了一个历史误会:早年的 Morden 测试网曾给新账户从 2 的 20 次方起算 nonce 来防跨网重放,这种土办法在 EIP-155 用链标识做重放保护后就退休了——它提醒读者,nonce 历史上真被设计成过大跨度,但现行规则回到了从一小步一小步。作为安全性的注脚:这条上限在可预见的账户行为下永远不会被触碰,它的价值在于「有界」带来的证明与实现确定性,而非限制正常用户。
一次编号演进的旁证
nonce 的历史比这条规则曲折。合并前的黄皮书时代,「任意精度无符号整数」是学术化的偷懒写法:反正现实到不了,不如不设界。真正让工程界下决心的是无状态化路线:状态见证要进区块与证明,字段宽度必须先钉死;EIP-155 又把链标识从 nonce 技巧里接管过来,让序号回归纯计数器本职。回头看,2681 是一次典型的技术债清算——先有 EIP-155 这类功能提案把用途收窄,再有本提案把宽度收窄,两步之间隔了四年。对协议演化感兴趣的读者可以把它当作样本:规范里每个「显然到不了」的无界字段,最后都要有人来补一刀。
快速问答
问:它是在某次硬分叉区块号激活的吗? 答:文件没有标注升级区块。两条规则按规范从创世追溯适用,属于把既成实现事实写成规则的定稿提案。
问:普通用户要关心这个数字吗? 答:日常无需关心。你的交易 nonce 离上限相差几十个数量级,这条规则更像是给协议与证明系统上的保险。
问:nonce 和挖矿的哈希 nonce 是一回事吗? 答:不是。工作量证明里的 nonce 是区块头里随便改的凑数域,账户 nonce 是防重放的序号计数器,两者除了撞名毫无关系。
风险提示
本文为协议规则解读,不构成投资建议。规范条款以提案仓库与当前客户端实现为准,历史测试网行为不代表主网。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。