0315 分钟
ORDER · REPLAY · STATE ROOT

同一组交易,为什么必须同一个顺序?

用区块哈希和状态根重放整条链,看到顺序或执行结果出现分歧时节点如何发现问题。

本章目标学完后,你应该能:
  • 解释区块如何承诺前序哈希与交易顺序
  • 从创世状态重放余额和 Nonce
  • 使用状态根判断节点是否得出同一结果
章节位置第 3 / 4 章
  1. 上一章交易池怎样处理相同 Nonce 的交易?
  2. 本章同一组交易,为什么必须同一个顺序?

    用区块哈希和状态根重放整条链,看到顺序或执行结果出现分歧时节点如何发现问题。

  3. 下一章确认数和最终性有什么区别?
阅读前

先想一想这几个问题

展开答案前,可以先在心里做个判断。与预期不同的内容可以加入复习列表。

Q01完全相同的一组交易,只换顺序,余额为什么会不同?
简要回答

交易读取并修改前一笔留下的状态。先花钱还是先收钱、先批准还是先调用,都会改变后续交易能否成功,因此共识必须同时确定内容和顺序。

和原先判断相比:

链上系统的关键不是“谁先写入数据库”,而是所有节点能否从相同输入得到相同输出。本章把交易放进连续区块,再从创世状态重放,观察顺序、哈希和状态根如何互相约束。

顺序本身就是状态的一部分

Alice 先给你转账,再由你给 Bob 转账,和反过来的顺序并不总是等价:Nonce、余额、抵押和清算边界都可能在每一步改变。区块因此要承诺交易顺序,而不能只承诺“这一批交易出现过”。

LEDGER SIMULATION虚拟积分 · 无真实密钥
LEDGER 001 · SIGN / VERIFY / APPLY

一笔交易如何变成状态?

交易先被签名和广播,再由节点验证并打包进区块。任何一步不满足规则,余额都不会改变。

当前链高#0TIP GENESIS
待打包交易0Pending
你的 Nonce0下一个可用序号
已确认0Included
被拒绝0Rejected
最终性高度#00 个区块等待
账户状态余额 + Nonce 是可验证状态
Nonce
0
公钥
pk_alice_demo
Nonce
0
公钥
pk_bob_demo
Nonce
0
公钥
pk_you_demo
交易队列0 TXS

还没有交易。先用正确 Nonce 广播一笔转账。

FOCUS EXPERIMENT · REPLAY区块重放与状态根

把交易打进区块,再让节点从创世状态重放。验证会检查交易顺序、前序哈希、状态根和最终余额。

TIP 哈希
GENESIS
状态检查
重放一致
观察提示

先看三张账户余额和 Nonce。签名只授权一组具体字段,节点还要在打包时检查 Nonce、余额和状态规则。

链上区块0 BLOCKS

还没有区块。交易先在本地 Pending,等节点打包。

节点事件1 EVENTS
  1. 01

    创世状态:三个账户各有 1,000 积分,Nonce 从 0 开始。

操作记录实验时间轴
1 个状态快照

拖动滑块可以回到此前的步骤。从那里继续操作时,后续记录会替换为新的操作序列。

初始状态
查看实验记录 →

点击“打包区块”,再验证整条链。实验室会从三个账户各有 1,000 积分的创世状态开始,按区块顺序重放每笔交易,重新计算余额、Nonce 和状态根。

状态根压缩了“执行结果”

状态根不是把所有账户余额展示在区块头里,而是把这组状态压缩成一个确定性摘要。两个节点拿到相同的前状态和顺序,应该得到相同的状态根;如果不相同,就需要追查执行规则、交易输入或客户端实现。

展开:这一步如何实现
replay(chain):
  state = genesis
  for block in chain:
    require block.previousHash == previous.hash
    for tx in block.transactionIds:
      apply_or_reject(state, tx)
    require block.stateRoot == hash(state)

教学模拟把复杂的 Merkle Patricia Trie 压缩成确定性账户摘要,但保留了最重要的工程习惯:执行和验证使用同一套纯规则,测试可以完整重放。

理解检查

状态根最重要的用途是什么?

把重放思维带到其他模块

AMM 的储备、预测市场的锁定抵押、代币的总供应和预言机的结算结果,都应该能被表达为状态转换。越复杂的金融机制,越需要清楚记录输入顺序、成功条件和失败时不变的字段。

本章小结完成练习和理解检查后,可以保存本章