同一组交易,为什么必须同一个顺序?
用区块哈希和状态根重放整条链,看到顺序或执行结果出现分歧时节点如何发现问题。
- 解释区块如何承诺前序哈希与交易顺序
- 从创世状态重放余额和 Nonce
- 使用状态根判断节点是否得出同一结果
- 上一章交易池怎样处理相同 Nonce 的交易?
- 本章同一组交易,为什么必须同一个顺序?
用区块哈希和状态根重放整条链,看到顺序或执行结果出现分歧时节点如何发现问题。
- 下一章确认数和最终性有什么区别?
先想一想这几个问题
展开答案前,可以先在心里做个判断。与预期不同的内容可以加入复习列表。
Q01完全相同的一组交易,只换顺序,余额为什么会不同?+
交易读取并修改前一笔留下的状态。先花钱还是先收钱、先批准还是先调用,都会改变后续交易能否成功,因此共识必须同时确定内容和顺序。
链上系统的关键不是“谁先写入数据库”,而是所有节点能否从相同输入得到相同输出。本章把交易放进连续区块,再从创世状态重放,观察顺序、哈希和状态根如何互相约束。
顺序本身就是状态的一部分
Alice 先给你转账,再由你给 Bob 转账,和反过来的顺序并不总是等价:Nonce、余额、抵押和清算边界都可能在每一步改变。区块因此要承诺交易顺序,而不能只承诺“这一批交易出现过”。
一笔交易如何变成状态?
交易先被签名和广播,再由节点验证并打包进区块。任何一步不满足规则,余额都不会改变。
- Nonce
- 0
- 公钥
- pk_alice_demo
- Nonce
- 0
- 公钥
- pk_bob_demo
- Nonce
- 0
- 公钥
- pk_you_demo
还没有交易。先用正确 Nonce 广播一笔转账。
把交易打进区块,再让节点从创世状态重放。验证会检查交易顺序、前序哈希、状态根和最终余额。
- TIP 哈希
- GENESIS
- 状态检查
- 重放一致
先看三张账户余额和 Nonce。签名只授权一组具体字段,节点还要在打包时检查 Nonce、余额和状态规则。
还没有区块。交易先在本地 Pending,等节点打包。
- 01
创世状态:三个账户各有 1,000 积分,Nonce 从 0 开始。
拖动滑块可以回到此前的步骤。从那里继续操作时,后续记录会替换为新的操作序列。
点击“打包区块”,再验证整条链。实验室会从三个账户各有 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 的储备、预测市场的锁定抵押、代币的总供应和预言机的结算结果,都应该能被表达为状态转换。越复杂的金融机制,越需要清楚记录输入顺序、成功条件和失败时不变的字段。