题目
对账页面展示某地址在指定区块的余额,团队想用 eth_getProof 防止 RPC 返回错误数据。请给出账户证明的验证步骤,说明 stateRoot 如何成为可信输入,并解释 ERC-20 余额与共识最终性还需要什么。
考察目标
- 理解证明验证的是某个确定状态根下的数据。
- 分清可信区块头、账户证明与存储证明。
- 评估前端轻客户端的成本和能力边界。
说明账户和存储证明的验证路径、可信状态根来源,以及证明与最终性的区别。
对账页面展示某地址在指定区块的余额,团队想用 eth_getProof 防止 RPC 返回错误数据。请给出账户证明的验证步骤,说明 stateRoot 如何成为可信输入,并解释 ERC-20 余额与共识最终性还需要什么。
从独立可信路径获得区块头及 stateRoot,再验证地址哈希对应的账户 trie 路径,解码账户叶子并核对 balance 等字段。合约存储用已验证账户的 storageRoot 继续验证。只取 proof 不等于验证;同一家 RPC 给出的根和证明可能一起伪造。证明只说明数据属于这个根,根是否规范、已最终确认仍依赖共识验证。
账户 trie 的 key 为地址的 Keccak256,按 Merkle Patricia Trie 的分支、扩展和叶子编码验证 accountProof 与可信 stateRoot 的关系。账户叶子以 RLP 编码 nonce、balance、storageRoot、codeHash;解码后核对返回字段,不能直接信任 RPC 对象的 balance。空账户需要验证不存在路径,而不是把空 proof 随意解释为余额零。
先验证 token 合约账户,取得其 storageRoot;按已核实的存储布局推导 owner 的余额槽,再验证 storageProof 的槽 key 和 RLP 值。代理、动态布局或运行时计算的 balanceOf 会改变所需验证路径。对复杂调用可用支持该能力的轻客户端验证执行所需状态,不把任意存储槽等价为 balanceOf 返回值。
可信输入可来自自建同步节点、独立确认的区块头或基于可信检查点的轻客户端。如果独立获得的只是区块哈希,还需验证取回区块头的编码哈希与它相同,再采用头中的 stateRoot。仅用同一个可疑 RPC 的 getBlock 和 getProof 互相检查,不能建立独立信任。
Helios 的 Ethereum 模式依赖支持 eth_getProof 的执行 RPC,通过共识轻客户端及检查点建立信任。检查点过旧或恶意、共识端点不可用都是需要处理的失败,不把“浏览器有轻客户端”说成零信任前提。
proof 绑定区块哈希/状态根,缓存也按它绑定。requireCanonical 是向节点提出要求,不是本地证明共识;重组后重新选择 canonical/finalized 根。区分已验证、未验证缓存、验证失败和暂时不可用。按业务选择结算级读数验证、普通列表缓存或可选高安全模式,衡量 proof 体积、同步等待和移动端计算成本。
此函数只获取指定区块的 proof,返回的数据尚未验证。调用方另行使用成熟 trie verifier 和独立可信区块头;不在面试示例中手写安全关键的 trie 实现。
import { createPublicClient, http, type Address, type Hash } from "viem";
import { mainnet } from "viem/chains";
export async function readAccountProof(
rpcUrl: string,
address: Address,
blockHash: Hash,
) {
const client = createPublicClient({
chain: mainnet,
transport: http(rpcUrl),
});
return client.getProof({
address,
storageKeys: [],
blockHash,
requireCanonical: true,
});
}
发现这道题有问题? 反馈此题