Solana Bankless Leader 设计解析:让 Leader 只读不写、以余额缓存换取出块性能
Solana Bankless Leader 设计解析让 Leader 只读不写、以余额缓存换取出块性能【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana本文围绕 Solana 仓库中的设计提案文档 bankless-leader.md 展开系统讲解 Bankless Leader无 Bank 出块方案的核心思想Leader 在出块时不做完整的账户读写仅做费用账户的一次读取 内存余额缓存从而把出块路径上的账户数据库开销降至最低。读完本文你能理解该方案的动机、余额缓存Balance Cache的生命周期管理、余额检查的五步流程、连续出块场景下的缓存重置机制以及它对客户端交易构造方式的具体影响并可在当前仓库源码中找到与这一设计理念呼应的实现证据。一、问题背景Leader 的工作量是普通验证者的 3 倍Solana 的共识中每个时段的 Leader 负责生产区块其他验证者负责重组 回放区块。提案原文对两者工作量做了清晰对比见 提案文档开头Leader 的职责接收交易ingress、排序并过滤合法交易、把交易组织成 Entries、将 Entries 打碎成 Shreds 并广播普通验证者的职责只需重组区块并回放执行格式正确的 Entries。原文给出了一个关键量化结论The leader does 3x more memory operations before any bank execution than the validator per processed transaction.即每处理一笔交易Leader 在任何 bank 执行之前所做的内存操作是验证者的 3 倍。在 Bankless Leader 方案下Leader 被定义为做最少的必要工作来产出一个有效区块a bankless leader does the minimum amount of work to produce a valid block。这一工作量差异的根源在于传统的 bank 操作流程。以一笔转账spend为例原文 Rationale 一节指出Normal bank operation for a spend needs to do 2 loads and 2 stores.普通 bank 处理一笔转账需要2 次读取 2 次写入读转出账户、读转入账户再写回两个账户的新余额。而 Bankless Leader 方案下With this design leader just does 1 load. so 4x less account_db work before generating the block. The store operations are likely to be more expensive than reads.Leader 只需要1 次读取读费用账户出块前的 accounts_db 开销降低 4 倍且写入操作store通常比读取read更昂贵。方案与当前仓库 Leader 管线的对应关系虽然该提案属于设计文档但当前仓库的出块代码路径可以印证其所针对的重操作环节交易接收与验签fetch_stage.rs 负责抓包签名批量验证实现在 perf/src/sigverify.rs出块主循环consumer.rs 中的批处理逻辑会调用bank.prepare_sanitized_batch_with_results对账户加锁该函数定义于 bank.rs加锁即意味着完整的账户读写语义——这正是提案要削减的环节加锁失败时返回AccountInUse等错误bank.rs提案中的Verify the accounts are not in use检查正是对应这类冲突判定。值得注意的一处呼应consumer.rs 中有注释说明当apply_cost_tracker_during_replayfeature 激活后Leader 不再用实际执行成本去调整区块a behavior more inline with bankless leader——这体现了 Bankless Leader出块阶段尽量少做真实 bank 操作的思路已进入现行代码的演进方向。二、Rationale为什么少写是核心收益提案 Rationale 一节的完整论证可以拆成三点读多写少写更贵普通转账 2 读 2 写Bankless Leader 只读 1 次出块前 accounts_db 工作量降为 1/4且省掉的最贵的是 store 操作回放阶段几乎零增量成本当 replay stage 开始处理同一批交易时可以假设 PoH 有效、所有 entries 可安全并行执行热加载摊薄成本出块时读进内存的费用账户在回放时大概率仍在内存中回放阶段的那次额外 load 是热的warm成本可以被摊薄。这三点构成方案的自洽闭环Leader 端省下的写操作换不回放端额外的冷读——因为状态本来就没落盘还留在缓存里。这也解释了标题中Bankless的含义出块过程不依赖对 bank 状态的真实修改不冻结、不提交写入只在内存层做一次保守的费用预检。三、Fee Account出块前唯一必须验证的账户提案中 Fee Account 一节对应 Solana 术语中的 fee_account规则非常简洁The fee account pays for the transaction to be included in the block. The leader only needs to validate that the fee account has the balance to pay for the fee.费用账户fee account承担交易进入区块的费用Leader唯一需要验证的是该账户余额是否足以支付这笔费用。注意这里的措辞——不是验证交易能否执行成功而是验证能不能付手续费。这大幅缩小了出块前的验证范围交易里其他被引用的账户转入方、程序数据账户等在出块阶段一律不碰留给回放阶段处理。在当前仓库中费用相关的常量与计费逻辑集中在 runtime/src/bank.rs例如 nonce 费用的查询路径durable_nonce_fee等交易构造侧的费用支付者fee payer概念则在 clap-utils/src/fee_payer.rs 与 sdk/program/src/instruction.rs 中定义——客户端构造交易时指定的 fee payer 就是提案中所说的 fee account。四、Balance CacheLeader 连续出块期间的临时余额缓存这是整个方案的核心数据结构。提案 Balance Cache 一节的规则如下作用域在 Leader 连续出块consecutive blocks的整个期间内为所有处理过的费用账户维护一个临时余额缓存结构一个pubkey - lamports的 map生命周期第一个区块开始时缓存为空最后一个区块结束时缓存被销毁基线约束缓存内的查询必须始终引用同一个 base fork贯穿整个缓存生命周期重置时机在区块边界处当 replay stage 完成对上一个区块的验证后缓存可以连同 base fork 一起被重置。base fork指缓存数据所基于的那个冻结的 bank 状态。约束同一 base fork保证了缓存中的 lamports 值始终相对于一个不变的状态基线避免 Leader 在出块过程中读到被其他分叉更新污染的数据。这一约束与仓库中 bank 的冻结机制直接相关bank.rs 定义了freeze()冻结后 bank 状态不可再变更并提供is_frozen()查询bank.rs——newer than the current base fork 的 frozen bank见下文重置流程正是以 bank 是否冻结作为状态稳定的判据。五、Balance Check出块前的五步余额检查流程提案 Balance Check 一节给出了完整的检查步骤前提是先通过交易的所有签名验证Prior to the balance check, the leader validates all the signatures in the transaction验证账户当前未被占用not in use且 BlockHash 有效检查费用账户是否已在缓存中若不在则从 accounts_db 加载该账户并把 lamports 余额存入缓存如果余额小于费用丢弃该交易从缓存中的余额中减去费用对交易中所有Credit-Debit且被某条指令引用的账户将其缓存余额减为 0。原文特别补充费用账户虽然被声明为 Credit-Debit但只要它没有被任何指令实际使用其缓存余额就不会被减为 0。第 5 步体现了提案的一个关键权衡牺牲严格性换取速度。一旦某个账户在出块阶段被按 Credit-Debit 处理余额清零后续对它的检查将直接失败——即使该账户在真实状态里其实有钱。这是有意的保守设计出块路径不追求余额精确只追求绝不收不该收的交易方向的快速判定代价误杀被限制在缓存周期内。对应到当前仓库的现行实现检查流程第 1 步的账户未被占用判定即AccountInUse类错误bank.rs签名验证则由 perf/src/sigverify.rs 的批量验签完成——提案把先验签、后查余额的顺序固化下来确保不会为垃圾交易付出任何 accounts_db 访问成本。六、Leader Replay 与连续出块场景提案用两节分别处理 Leader 自身的回放问题Leader 也必须回放自己的区块Leaders will need to replay their blocks as part of the standard replay stage operation.这一点容易被误解Bankless Leader不是跳过验证而是把验证工作全部移到回放阶段。Leader 产出的区块和别的 Leader 产出的区块一样要经过标准 replay stage 处理当前仓库的回放主逻辑在 core/src/replay_stage.rs。出块时的少做必须以回放时的照做为对价。连续出块时出块与回放在时间上重叠一个 Leader 可能被排期连续生产多个区块。此时会出现这样的时间线Leader 正在生产第 N1 个区块的同时第 N 个区块的 replay stage 还在回放中。提案对此的处理是When the leader finishes the replay stage it can reset the balance cache by clearing it, and set a new fork as the base for the cache which can become active on the next block.即回放完成后清空缓存、以新 fork 作为 base新缓存自下一个区块起生效。注意生效在下个区块这个细节重置不是即时切换而是预留了边界安全保证同一个区块内所有查询都基于同一个 base fork与第四节约束一致。七、Resetting the Balance Cache缓存重置的三步规则提案 Resetting 一节给出可操作的判定算法块开始、缓存未初始化时把 base fork 设为当前区块的父区块parent并创建空缓存缓存已初始化时检查该区块的父区块中是否存在比当前 base fork更新的 frozen bank若存在更新的父 bank把缓存重置到该父 bank。这条算法把何时允许动缓存精确限定在区块边界且以 frozen bank 作为比较对象——只有冻结后的 bank 才能作为缓存基线未冻结仍在写入的 bank 一律不被采信。freeze()/is_frozen()的语义bank.rs、bank.rs正好支撑了这个以冻结为界的判断依据。八、Impact on Clients客户端该如何构造交易提案最后一节直接面向交易发送方给出三条实操结论同一个费用账户可以在同一区块内被大量复用——前提是在被某条指令当作 Credit-Debit 使用前高吞吐客户端应使用专用费用账户如果你要发送大量交易/秒用一个不作为任何指令 Credit-Debit 账户的专用 fee account误用的代价一旦费用账户被用作 Credit-Debit在余额缓存重置之前它会一直通过不了余额检查will fail the balance check until the balance cache is reset。对交易发送端的实际含义是把付手续费的钱包和业务逻辑里被读写的账户分离开来。客户端侧交易构造时可参考仓库中 fee payer 的解析与装配逻辑clap-utils/src/fee_payer.rs、sdk/program/src/instruction.rs理解 fee payer 与指令账户列表的区分——提案中声明为 Credit-Debit 但未被指令引用的豁免规则正是基于这两者的区别设计的。九、总结一份以写换读的出块优化设计Bankless Leader 提案的本质是一次职责重排把传统出块路径上读账户 → 改账户 → 写账户的完整 bank 操作压缩为验签 一次热余额预检其余所有状态变更都推迟到全体验证者含 Leader 自己统一执行的 replay 阶段。其设计要点可以归纳为设计要素内容依据位置工作量目标Leader 出块前 accounts_db 开销降为普通流程的 1/41 load vs 2 loads 2 stores提案 Rationale数据结构pubkey → lamports 的临时 map生命周期绑定 Leader 连续出块期提案 Balance Cache一致性约束缓存查询全程同一 base fork仅在区块边界随 replay 完成而重置提案 Resetting冻结判据只有 frozen bank 可作为缓存基线freeze()、is_frozen()检查顺序先验签再判 not-in-use/BlockHash后查缓存与扣费提案 Balance Check、sigverify.rs客户端约束高 TPS 场景使用专用 fee account避免被 Credit-Debit 清零提案 Impact on Clients需要说明的是该文档位于 docs/src/proposals/ 目录属于设计提案SIMD性质描述的是目标架构从当前仓库源码结构看完整的出块期余额缓存结构在本仓库快照中未见对应实现代码现行出块管线仍走prepare_sanitized_batch_with_results加锁路径bank.rs但 consumer.rs 中more inline with bankless leader的注释表明这一设计理念已在成本记账等细节上持续影响现行实现。阅读时请以提案文本为设计意图的权威表述以源码为现行行为的权威表述二者区分对待。【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考