
Solana 快照验证机制从提案设计到 Accounts Hash 校验的源码级解析【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana本文以 Solana 仓库中已实现的提案文档 snapshot-verification.md 为主体完整讲解验证器从快照启动时如何确认账户状态与全网一致这一问题的设计动机、哈希方案选型、440 字节扩展图像与 XOR 状态合并的原理及其安全性分析并结合当前仓库源码说明该机制在 accounts-db、runtime 与 core 中的落地形态。读完本文后你将能够理解快照验证为何不用 bank hash 链或朴素 Merkle 树、零 lamport 账户为何必须清除以及验证器启动时verify_accounts_hash_and_lamports的完整调用链。一、问题背景验证器从快照启动时的信任问题提案文档开宗明义地描述了问题当验证器从快照启动时它需要一种快速的方式来验证自己的账户集合与网络其他节点看到的一致。潜在的攻击路径是攻击者可以喂给验证器一份错误的状态错误快照然后诱导它接受一笔在正确状态下本应被拒绝的交易。快照snapshot是 Solana 验证器快速追赶链上状态的机制——不回放全部历史交易而是直接加载某一刻的账户状态。但加载一份不受信任的数据就意味着必须有一个独立于快照本身的指纹来证明这份数据是正确的。提案文档给出的约束是quickly快速地验证成本不能随账户总量线性膨胀到不可接受的程度否则快照加速启动的意义就被抵消了。二、两种朴素方案的缺陷文档对两个直观方案分别给出了否定性分析理解它们有助于理解最终方案的取舍方案 1bank hash 链。当前及当时的bank hash 是从一个 slot 内账户的增量状态delta state哈希得出再与上一个 bank hash 值组合。问题在于哈希列表会随链上处理的 slot 数量增长其长度与已处理 slot 数成正比传输和验证负担都会随之变大。从可证明性的角度看这是一条不断加长的哈希链验证者必须拿到整条链才能归约到一个根。方案 2对全部账户状态建 Merkle 树。缺点在于每更新一个账户就需要基于系统中所有存活账户的完整状态重算 Merkle 树——单次账户写入的代价是 O(账户总量) 级别的与 Solana 高吞吐的交易处理模型不相容。因此最终方案追求的性质是状态指纹只与当前全部账户相关而非与历史路径相关且账户变更可以局部更新指纹这正是下文每账户哈希 XOR 合并设计的目标。三、提案核心设计账户哈希、扩展图像与 XOR 状态以下按 snapshot-verification.md 原文脉络完整继承其技术细节。3.1 对哪些数据做哈希对所有非零 lamport 账户的账户存储逐账户对如下数据计算哈希Account owner账户所属程序/所有者Account data账户数据Account pubkey账户公钥Account lamports balancelamports 余额Fork the account is stored on账户所存储的分叉/slot注意两点其一fork/slot参与哈希意味着账户被写入哪个分叉上的状态也是指纹的一部分其二non-zero lamport accounts是反复出现的前提条件后文 3.4 节会解释为什么零余额账户必须被排除。3.2 扩展函数从 32 字节哈希到 440 字节图像每个账户的哈希值并不是直接参与合并而是先作为**扩展函数expansion function**的输入被扩展成一个图像值image扩展结果是一个440 字节的数据块前 32 字节是哈希值本身其余 440 − 32 408 字节由ChaCha RNG以该哈希为种子生成。这个设计的意义在于两个完全不同的账户哈希会展开成几乎完全无关的两段 440 字节伪随机位模式为后续 XOR 合并提供雪崩效应式的混淆基础。3.3 XOR 合并与增量更新全局状态值是所有账户图像的XOR 叠加。账户更新时利用 XOR 的自反性a XOR a 0做增量维护更新前把旧账户图像XOR 进状态等价于移除旧值的贡献更新后把新账户图像XOR 进状态加入新值的贡献。这使得单次账户更新的代价是 O(单个账户)而非重建整棵树。同时文档指出投票voting账户和 sysvar 的哈希值是对完整图像值做哈希得到的——即这两类账户不走普通的 32 字节哈希路径而是对 440 字节完整图像取哈希以保留更多区分信息。3.4 零 lamport 账户必须清除文档明确给出了一条硬性约束快照在创建之前、以及验证过程中都必须清除purge所有零 lamport 账户原因是零 lamport 账户不影响哈希值但可能导致验证器的 bank 误读某个账户不存在而实际上它应该存在。即零余额账户是哈希上不可见、但运行时语义上可见的脏数据。若快照里残留一条零 lamport 账户记录加载后读取该地址可能得到账户存在但余额为 0与正确状态下账户不存在产生语义差异。3.5 启动验证与 SPV 交叉核对文档描述的启动流程是验证器从快照加载后用账户集合本地重算哈希值并与快照声明的值比对随后利用SPV简化支付验证展示网络中为该哈希值投票的节点占比把我算出来的与全网共识认可的两层证据关联起来。最终值可以被任何验证器独立验证为所有当前账户状态 XOR 到一起的结果。3.6 对 XOR 状态的攻击分析与 128 位安全性XOR 合并存在天然弱点攻击者若能构造账户组合使若干图像 XOR 结果趋近于 0或任意给定位模式就能在不改变全局状态值的情况下篡改账户集合——这就是文档所说的对 XOR 状态的影响攻击。440 字节的图像长度正是针对这一攻击的防御参数其出处是一篇学术论文原文档中给出了 Springer 的论文链接。原文给出的安全性数学推导如下n为图像位宽k为账户数O(k * 2^(n/(1lg(k)))) k 2^40 accounts n 440 2^(40) * 2^(448 * 8 / 41) ~ O(2^(128))即当账户数达到 2^40 量级、图像位宽 n 440对应 448 bit 量级参与计算时构造 XOR 碰撞的复杂度约在2^128量级也就是文档所称的128 位安全。四、当前仓库源码中的落地形态上述提案文档位于docs/src/implemented-proposals/目录下属于已实现提案。对照当前源码可以看到设计的核心思想被继承下来具体数据结构则演化为了更工程化的形态。以下均以仓库实际代码为准。4.1 每账户哈希hash_account提案中对账户 owner / data / pubkey / lamports 哈希一步当前实现位于 accounts_db.rs 的 hash_account。实现要点使用blake3哈希器参与哈希的字段为lamports、rent_epoch、executable、data、owner、pubkey以to_le_bytes/原始字节拼接进缓冲超过 200 字节的大数据直接流式update避免多余拷贝零 lamport 账户直接返回Hash::default()全零哈希——这正是提案零 lamport 账户不影响哈希值这一性质在代码中的直接体现if lamports 0 { return AccountHash(Hash::default()); }配套的加载侧过滤在 accounts.rs 的is_loadablepub fn is_loadable(lamports: u64) - bool { // Dont ever load zero lamport accounts into runtime because // the existence of zero-lamport accounts are never deterministic!! lamports 0 }注释明确指出零 lamport 账户的存在性永远不确定与提案 3.4 节的 purge 要求互为印证不加载零余额账户就从源头上避免了账户存在性歧义。4.2 全局状态指纹按 pubkey 排序的 Merkle 树提案原稿用440 字节图像 XOR聚合全部账户从源码结构看当前聚合层已演化为对逐账户哈希按 pubkey 排序后构建 Merkle 树相关实现在 accounts_hash.rs其中定义了扇出常量pub const MERKLE_FANOUT: usize 16;逐账户哈希通过 mmap 临时文件按 pubkey 顺序落盘再归约成根哈希AccountHashesFile/MmapAccountHashFile结构负责该过程。这一形态同时满足提案提出的两个目标根哈希只描述当前全部账户而非历史路径且逐账户哈希可局部更新、并行计算——只是用排序 Merkle 归约替代了XOR 扩展图像安全性论证也由 2^128 的 XOR 碰撞分析转为标准 Merkle 哈希族假设。4.3 启动验证链路verify_accounts_hash_and_lamports提案中On validator boot, when it loads from a snapshot, it would verify the hash value with the accounts set在当前代码中对应一条清晰的调用链Bank 层入口bank.rs 的Bank::verify_accounts_hash注释写明 Would be used to verify a snapshot ... Only called from startup or test code.仅由启动或测试代码调用。它处理了 root 约束非 root slot 会递归回退到父 bank 或 panic并支持前台同步验证与后台线程验证solBgHashVerify线程两种模式。Accounts 层转发accounts.rs 的verify_accounts_hash_and_lamports返回布尔值失败时仅记录warn!供调用方决定是继续还是中止。AccountsDb 层核心逻辑accounts_db.rs 的verify_accounts_hash_and_lamports。它的语义与提案重算并比对一致并额外覆盖了增量快照场景base为None时对[0, slot]计算全量 accounts hashbase为Some((base_slot, base_capitalization))时先递归验证 base slot 的全量哈希再对(base_slot, slot]的增量存储计算 incremental accounts hash校验两项总 lamportscapitalization必须等于total_lamports以及重算哈希必须等于快照中记录的get_accounts_hash(slot)任一不符返回MismatchedTotalLamports/MismatchedAccountsHash错误。这一哈希 总 lamports双重比对是提案哈希验证的工程化加强单改账户字段可能碰巧保持总额不变但总额校验能独立捕捉大规模余额篡改。4.4 快照生产侧AccountsHashVerifier服务验证不只发生在加载端。快照打包端由 core/src/accounts_hash_verifier.rs 中的AccountsHashVerifier服务负责线程名solAcctHashVer它按优先级从AccountsPackage通道取包get_next_accounts_package中对 EpochAccountsHash、全量/增量快照、纯 hash 验证包做了精细排序与重新入队策略文件内 L114-L203 有完整注释_calculate_full_accounts_hash在重算哈希与资本化后与包内expected_capitalization做硬断言不一致时先用 index 路径、再开store_detailed_debug_info_on_failure重算一遍用于定位最后assert_eq!( accounts_package.expected_capitalization, lamports, accounts hash capitalization mismatch );见 L379-L410哈希计算完成后通过reserialize_bank_with_new_accounts_hash把计算所得的 accounts hash 写回 bank 快照元数据L299-L306再把SnapshotPackage交给打包服务归档。也就是说生产方在快照出包前自证哈希与资本化加载方在启动时重算比对——提案中快照必须携带可验证哈希值的闭环由这两端共同完成。其包调度策略的正确性由同文件mod tests中的test_get_next_accounts_package1/2用乱序注入的多包场景覆盖。五、要点小结回到提案文档可以把整套机制浓缩为五条可独立引用的结论bank hash 链与全量 Merkle 重算都不可行前者验证成本随 slot 数增长后者单账户更新代价为 O(全量账户)账户哈希输入为 owner、data、pubkey、lamports、所在 fork/slot 五要素当前实现另含rent_epoch与executable字段见 hash_account_data440 字节扩展图像32 字节哈希 ChaCha RNG 派生 408 字节 XOR 合并使单账户更新为 O(1) 图像运算并依据 XOR 碰撞分析达到约 2^128 的安全强度零 lamport 账户必须在快照创建前与验证时清除当前代码通过零余额哈希为默认值 is_loadable拒绝加载落地了同一约束启动验证闭环加载端Bank::verify_accounts_hash → verify_accounts_hash_and_lamports重算比对哈希与总 lamports生产端AccountsHashVerifier出包前断言资本化并重序列化 bank两端共同保证快照哈希值可信。对于维护者或审计者建议的阅读顺序是先读 snapshot-verification.md 理解设计权衡再按 4.3 节调用链进入 accounts-db 与 bank.rs最后用 accounts_hash_verifier.rs 理解快照生产侧的对称校验。【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考