跳到正文

状态根(State Root)

状态根是以太坊对区块执行后世界状态的紧凑承诺。本文解释节点如何计算状态根、账户与存储证明能证实什么,以及状态根无法保证什么。

更新于

仅供教育参考,不构成投资建议;投资可能产生损失。

直接答案

状态根是以太坊区块头中的一个 32 字节密码学承诺,对应区块处理完成后的世界状态。世界状态把地址映射到账户。每个账户承诺其 nonce、余额、存储根和代码哈希;每个合约的存储根又承诺该账户的各个存储槽。

状态根是摘要,并不是可下载的快照。节点可用它比较各自独立计算的结果,验证者也可依据可信区块核验账户证明或存储证明。仅凭状态根无法重建状态、证明状态数据可用、确认区块已最终确定,也无法表明合约在经济上安全。

“状态根”取决于具体协议。以太坊目前使用修改版 Merkle-Patricia trie 承诺执行层状态。其他网络可能采用不同的状态模型、编码、哈希函数或认证数据结构,因此名称相同并不代表其根或证明可以互换。

工作原理

执行客户端从父区块状态开始,按照当前生效的协议规则验证并执行新区块,再应用由此产生的账户和存储变更。可用下式概括:

S_n = Υ(S_(n-1), B_n)

其中 S_(n-1) 是父状态,B_n 是新区块规定的全部协议处理,S_n 是结果状态。客户端以确定性方式把该状态编码进状态树并计算根哈希。有效区块头必须包含相同结果;若不一致,该客户端就必须判定区块无效。

在以太坊状态树中,账户路径由地址派生,编码后的账户包含 nonce、余额、存储根和代码哈希。合约代码通过其哈希引用,每个合约另有一棵存储树。这种嵌套关系意味着一个存储槽变化可依次改变合约存储根、编码后的账户,最终改变全局状态根。

状态根不同于同一区块头中的交易根和收据根。交易根承诺按顺序排列的交易数据,收据根承诺执行收据,三者不能彼此替代。

EIP-1186 定义了 eth_getProof,可返回指定区块的账户证明和所请求的存储证明。验证者仍需取得经过认证的区块哈希或状态根,使用正确的 trie 与编码规则,并采用合适的确认或最终性策略。

示例

假设一笔交易把 ETH 从 Alice 转给 Bob。正确执行可能改变 Alice 的 nonce 和余额、Bob 的余额以及手续费接收方的余额。若交易调用合约,存储槽和合约存储根也可能变化。即使绝大多数账户未被触及,这些更新仍会产生新的全局状态根。

两个诚实客户端若从同一父状态出发,并依照相同规则处理同一有效区块,应计算出相同的根。若某个客户端计入错误金额或使用错误的 trie 编码,其结果会与区块头不同;它必须拒绝该区块,而不能默默接受本地状态。

若想在不下载整个世界状态的情况下核验 Bob 的余额,验证者可取得区块头和账户证明。重新计算证明路径,可以判断编码后的账户是否与该区块头的状态根一致。这并不能证明所选区块头属于规范链或已经最终确定;这些结论来自验证者的链选择与最终性检查。

风险

  • 不可信的根: 针对攻击者选择或已经过时的根,即使证明有效,也只是证明了错误的参照点。应把根绑定到已验证的区块哈希、链 ID 和区块高度。
  • 重组与最终性: 证明可能正确对应一个后来脱离规范链的区块。确认深度或最终性要求应与应用的损失承受能力相匹配。
  • 编码错误: 地址哈希、RLP 编码、nibble 路径、内嵌节点和存储键处理必须严格遵循协议规则。通用的二叉 Merkle 证明库并不足够。
  • 数据缺失: 根承诺状态,却不会让 trie 节点、历史状态或证明生成服务自动可用。经过剪枝的节点可能无法提供旧证明。
  • 夸大保证: 根一致能发现执行结果不一致,但不会审计合约逻辑、认证预言机输入、保护 RPC 端点、保证资产价值,也不能阻止密钥泄露后签出的交易。

常见误区

  • 状态根存放了每个账户的余额。 它只是对编码后 trie 的定长承诺,底层 trie 数据必须另行取得。
  • 状态根相同就证明两个节点的数据库完全相同。 它们承诺的是协议规则下相同的逻辑世界状态,但客户端可以采用不同的存储、索引、剪枝或缓存方式。
  • 状态根不同就能指出哪笔交易出错。 它只能显示最终承诺状态存在分歧,不能定位分歧从何处开始;客户端必须追踪执行过程才能诊断。
  • 有效的账户证明同时证明最终性和安全性。 它只证明数据与某一个根一致。链选择、最终性、数据新鲜度、合约行为和经济风险仍是彼此独立的问题。

相关主题

来源

导航

搜索知识库...