摘要:Solana 的并行度来自账户声明,也会被账户声明拖垮
Solana 的高吞吐不是来自“所有交易都并行执行”,而是来自交易在执行前显式声明要读取和写入的账户集合。Sealevel 可以把不冲突的交易分派到多个执行线程;但只要大量交易同时请求写入同一个热点账户,调度器就必须串行化这些交易。写锁耗尽(Write-Lock Exhaustion)的核心风险正是利用这个机制:攻击者不需要伪造签名或破坏共识,只要构造表面合法、但都锁定核心 AMM、订单簿、Oracle 或路由中转账户的交易,就能把局部并行执行退化成长队列。对跨链交换而言,这类风险会表现为报价过期、交易超时、退款延迟和特定 Solana 路径的成功率波动。防御重点不是取消账户锁,而是让热点账户的锁占用被准确计价、提前拒绝和可观测。
问题边界:讨论调度和费用防御,不讨论攻击脚本
本文讨论 Solana/SVM 类高性能链的账户锁机制、局部拥塞和防御调度,不提供可复用 DoS 交易模板、目标账户筛选方法或真实协议攻击步骤。系统角色包括:用户交易、SVM runtime、banking stage、账户锁表、leader、优先费市场、RPC/转发层、DEX/Oracle 程序账户、以及跨链路由系统。攻击者能力被限定为提交大量合法交易、选择交易账户列表、支付一定费用并影响局部热点账户队列。
Solana 要求交易携带账户列表和读写权限。只读账户可以并行共享,写账户必须排他。这个设计让 runtime 在执行前知道冲突关系,从而避免 EVM 那种执行后才发现状态冲突的模型。代价也很明确:应用把状态集中到少数账户时,这些账户会成为并行度的瓶颈。写锁耗尽不是传统“全网带宽打满”的 DoS,而是“局部状态顶点饱和”造成的执行层退化。
冲突图模型:热点账户是图里的割点
把一个 slot 或调度窗口内的交易集合记为 `T = {t1, t2, ... tn}`,每笔交易声明读集合 `R(t)` 和写集合 `W(t)`。若两笔交易满足:
`W(a) ∩ (W(b) ∪ R(b)) != ∅` 或 `W(b) ∩ R(a) != ∅`
它们就不能同时执行。可以构造冲突图 `G = (V, E)`,其中每个顶点是交易,每条边表示账户读写冲突。也可以构造二部图:左侧是交易,右侧是账户;交易到写账户的边带有排他容量。热点账户 `x` 的饱和度可写成:
`sat(x) = demand_write(x) / capacity_write(x)`
在一个调度窗口里,单个账户的写容量近似是一条串行通道。若 `sat(x) >> 1`,即使机器还有 CPU 核、网络还有带宽,涉及 `x` 的交易也会排队。并行执行的全局吞吐被局部最大流限制:不冲突子图可以高速执行,但经过热点顶点的路径会形成最小割。写锁耗尽攻击的目标就是把高价值业务流量推到同一个割点上。
这解释了为什么“增加硬件”不能完全解决问题。更多线程能提高无冲突交易吞吐,却不能让同一可写账户同时被多笔交易安全修改。真正有效的工程方向是:拆分状态账户、设计本地费用市场、在入口层识别长依赖链,并让占用热点锁的交易付出更高代价。
Sealevel 并行执行的工程约束
Sealevel 的优势是提前知道账户访问集合。runtime 可以把交易分成多个 batch,让无冲突交易并行执行;冲突交易则被延后或重试。交易 pipeline、Gulf Stream 和 priority fee 机制共同影响交易到达 leader、进入队列、被调度和被打包的概率。这里的关键不是单笔交易是否有效,而是它在热点账户锁表上占用多少排他时间。
一个简化调度器可以写成:
```text for tx in priority_queue: if locks_available(tx.reads, tx.writes): acquire_locks(tx) dispatch(tx) else: defer_or_drop(tx) ```
问题在 `defer_or_drop`。若调度器总是把冲突交易留在队列里等待,攻击者可以制造长依赖链,让正常用户交易在同一个热点账户后面排队。若调度器过早丢弃,可能伤害真实用户在拥堵时的交易成功率。防御需要把交易价值、优先费、账户热度、历史失败率和锁等待时间结合,而不是只看全局 fee。
还要区分 read 热点和 write 热点。Oracle 喂价账户若多数交易只读,它对并行度的影响小得多;AMM 池状态、订单簿、共享 vault、路由中转账户若需要写入,就会成为排他瓶颈。协议设计应尽量把可并行状态拆成多个可写账户,或让用户/订单维度状态本地化,避免所有路径写同一个全局计数器。
runtime 层还要处理“锁等待”和“计算失败”的区别。两笔交易可能都合法、签名正确、费用充足,但因为写锁冲突只有一笔能先执行;另一笔被延后后,账户状态可能已经变化,最终执行结果和最初模拟结果不同。这对交易模拟和跨链报价很关键:模拟成功不代表调度成功,调度成功也不代表状态没有被前序交易改变。路由系统若只看模拟返回值,会低估热点账户竞争。
账户锁也会影响 leader 的包选择。一个低费但锁定热点账户很久的交易,可能阻塞后续更有价值的交易;一个高费但账户集合分散的交易,可能更容易和其他交易并行。调度器的目标不应只是“按费率排序”,而是最大化单位时间内可执行的有效交易集合。写锁冲突让这个问题接近带资源约束的调度:每笔交易消耗计算单元,也占用一组账户锁。
本地费用市场:让热点写锁变贵,而不是让全网变贵
全局费用市场会把所有用户一起抬价;本地费用市场(Local Fee Markets)的目标是只对拥堵资源加价。对写锁耗尽而言,资源不是抽象的区块空间,而是具体账户的排他写容量。一个防御型费用函数可以写成:
`fee(tx) = base_fee + priority_fee + Σ hotness(a) * write_weight(a)`
其中 `a ∈ W(tx)`,`hotness(a)` 可由近期锁等待、失败重试、写请求密度和账户业务权重计算。更激进的函数可以让热点溢价随饱和度非线性增长:
`hotness(a) = α * max(0, sat(a) - 1)^2`
这不是为了惩罚正常用户,而是避免攻击者用低成本持续占用同一热点写锁。如果一笔交易锁定多个热点账户,它应该支付多个局部资源的价格。反过来,访问冷账户或只读账户的交易不应因为某个热门 DEX 池拥堵而承担同样高的费用。
本地费用市场也有副作用。若价格信号太强,套利和清算机器人会把正常用户挤出热点账户;若价格信号太弱,恶意交易仍能便宜排队。更稳健的设计通常需要配合质量信号:近期失败率、计算单元估算、交易来源信誉、账户锁链长度、是否重复提交近似相同的账户集合。费用是一个过滤器,不是唯一防线。
费用函数还必须防止“多账户稀释”。如果一笔交易同时写入一个极热账户和多个冷账户,费用不应被平均后显得温和;如果一组交易轮流写入多个相关热点账户,也不应因为每个单点饱和度看似不高而逃过定价。更可靠的指标是账户集合的最大热度、加权热度和历史共现矩阵。对于 DEX 池、Oracle、共享 vault 这类基础设施账户,可以给 `business_criticality(a)` 更高权重,因为它们的拥堵会外溢到大量用户路径。
可观测性同样重要。若开发者和路由器无法看到某个账户的锁等待趋势,本地费用市场就会变成黑箱。理想的遥测至少包括:账户级写请求密度、平均锁等待、交易过期率、被 early drop 的比例、priority fee 分位数和执行成功率。即使这些数据只以聚合形式暴露,也足以让钱包、路由器和做市系统做更好的路径选择。
静态截断与早期拒绝:不要等执行层才发现队列坏了
写锁耗尽的防御越早越好。等交易进入执行线程后再发现冲突,已经浪费了转发、签名验证、队列和调度资源。入口层可以做静态截断(Static Truncation)和早期拒绝(Early Drop):当某个账户的待写队列长度超过阈值,且新增交易的优先费、账户组合或历史成功概率不足,就提前拒绝或降级。
一个防御状态机可以写成:
```text on incoming_tx(tx): hot = max_hotness(tx.writes) chain = dependency_chain_length(tx.writes) quality = priority_fee_score(tx) + success_prior(tx) if hot > H and chain > L and quality < Q: early_drop(tx) else: enqueue(tx) ```
这里的 `success_prior` 不应变成中心化白名单,而是可解释的统计信号:近似账户集合是否连续失败、计算预算是否明显不足、是否反复锁定同一热点但从未成交、是否在多个 RPC 入口同时复制提交。安全目标是降低垃圾锁占用,不是按身份审查交易。
静态截断也要保护真实用户。若某个热点账户是大型 AMM 池,完全拒绝低费交易会让小额用户体验恶化。更好的做法是提供明确反馈:交易因热点写锁拥堵被延后、需要更高 priority fee、或建议路由到替代池。对跨链产品,路由器可以自动选择低冲突路径,而不是盲目重试同一账户。
早期拒绝的安全边界在于“基于资源,而不是基于身份”。如果一个算法按地址、应用或 RPC 来源粗暴拒绝,就容易变成中心化审查;如果它按账户热度、锁等待、重复失败和费用不足拒绝,则更接近资源保护。实现上可以把拒绝原因结构化:`hotWriteAccount`、`insufficientLocalFee`、`dependencyChainTooLong`、`staleBlockhashRisk`、`duplicateAccountSet`。这些原因能被上层路由器消费,避免用户只看到模糊的失败。
还有一种防御是交易拆分。若一个复杂路由把多个热点账户放在同一笔交易里,任何一个写锁冲突都会拖慢整笔交易;拆分后可以降低单笔锁集合,但会引入原子性风险和中间状态风险。对跨链兑换来说,拆分不能随意使用,因为用户期望的是确定的执行或退款。是否拆分应由金额、滑点、退款路径和链上原子性要求共同决定。
失败模式:合法交易也能构成局部 DoS
第一类失败是热点 AMM 池写锁拥堵。大量交易写同一个池状态账户,正常 swap 和恶意占位交易在同一个队列里竞争。检测信号包括某些账户的写等待时间上升、同账户交易失败率上升、priority fee 分布异常变宽。
第二类失败是 Oracle 或共享状态的错误写设计。如果业务把本可只读的状态设计成频繁写入,或让多个路径更新同一个全局状态账户,就会把读热点变成写热点。防御是拆分状态、减少共享写、把用户/订单级状态本地化。
第三类失败是长依赖链。攻击者或异常机器人连续提交账户集合高度相似的交易,让调度器反复延后、重试、再延后。检测信号是同一账户组合的排队深度和重复提交率升高。防御是早期拒绝、重复账户集合限速和非线性热点费用。
第四类失败是局部费用外溢。热点账户溢价太高时,真实用户无法进入关键池,路由器转向流动性更差的池,造成滑点上升。防御不是取消费用,而是让路由系统把费用、流动性和锁等待放进同一个评分模型。
第五类失败是 RPC 层误导。用户看到交易已发送,但 leader 侧因锁冲突迟迟不执行或被丢弃。跨链系统若只看“已提交”状态,可能错误估计目标链到账时间。必须区分 submitted、scheduled、executed、confirmed 和 failed。
AllSwap 相关性:账户锁是跨链报价的一部分
AllSwap 的跨链路由不仅要比较价格和手续费,还要判断执行路径是否能在可接受时间内落地。对 Solana 路径而言,账户写锁冲突会直接影响 swap 成功率和报价有效期。一个简化路由评分可以写成:
`routeScore = priceScore + liquidityScore - lockContentionPenalty - priorityFeeCost - timeoutRisk`
`lockContentionPenalty` 来自目标池、Oracle、vault 和路由账户的近期写锁等待;`priorityFeeCost` 来自本地费用市场的实际费用;`timeoutRisk` 来自交易过期、重试和退款路径。若某条路径价格更好但写锁冲突严重,最终用户可能获得更差结果:报价失效、交易失败或等待退款。
产品状态也应更细。`quoteReady` 只表示报价可用;`submitted` 表示交易已发送;`lockContention` 表示热点账户冲突;`rerouting` 表示切换池或替代路径;`executed` 表示链上执行完成;`refundPending` 表示失败路径正在回滚。用户不需要理解 Sealevel,但需要知道等待来自链上拥堵、锁冲突还是跨链确认。
内部风控还可以按账户热度做限额。若某个 Solana 池账户进入高饱和状态,高价值兑换可以要求更保守的报价窗口、拆单或切换流动性来源;小额兑换可以继续尝试但提示费用波动。核心原则是:不要把 Solana 路径当作单一“快链”,而要把具体账户集合的可执行性纳入路由。
更具体地说,AllSwap 的路径评估可以为每个 Solana 交易模板维护账户集合指纹:`accountSetHash = H(reads, writes, programs)`。同一个指纹近期若出现高过期率、高重试率或高 priority fee,报价引擎应降低该路径权重。若替代路径使用不同池、不同 vault 或不同中转账户,即使报价略差,也可能给用户更高的实际成交概率。
退款逻辑也要和锁冲突区分。交易因为写锁迟迟未执行,与交易已执行但跨链确认未完成,是两种不同状态。前者应关注 blockhash 过期、重试和替代路由;后者应关注目标链确认和退款证明。把这些状态混在一个“pending”里,会让用户无法判断等待是否有意义,也让客服和风控难以归因。
未解决问题:高并发链仍需要资源定价语言
第一,本地费用市场的资源粒度仍在演化。账户、程序、写锁、计算单元、数据带宽和 leader 队列都是资源,但哪一种最适合定价并没有完全定论。
第二,热点账户拆分会增加协议复杂度。拆得太粗会拥堵,拆得太细会提高开发和路由成本,还可能降低流动性聚合效率。
第三,早期拒绝容易和审查混淆。防御算法必须可解释、可监控,并尽量基于资源占用而非身份标签。
第四,跨链路由缺少统一的账户锁遥测接口。RPC 通常告诉你交易结果,却不一定告诉你失败是费用不足、计算不足、锁冲突还是账户状态变化。
第五,用户体验需要把锁冲突翻译成可操作信息。理想产品不应显示底层图论术语,但应该能解释为什么换一条路径、提高优先费或等待几秒会改善成功率。
参考资料
[1] Solana, Sealevel: parallel processing thousands of smart contracts, https://solana.com/news/sealevel---parallel-processing-thousands-of-smart-contracts
[2] Solana documentation, Transactions, https://solana.com/docs/core/transactions
[3] Solana documentation, Accounts, https://solana.com/docs/core/accounts
[4] Solana documentation, Transaction pipeline, https://solana.com/docs/core/transactions/transaction-pipeline
[5] Solana documentation, Fees, https://solana.com/docs/core/fees
[6] Solana documentation, Fee structure, https://solana.com/docs/core/fees/fee-structure
[7] Solana guide, How to use priority fees, https://solana.com/developers/guides/advanced/how-to-use-priority-fees
[8] Solana, Gulf Stream transaction forwarding protocol, https://solana.com/news/gulf-stream--solana-s-mempool-less-transaction-forwarding-protocol
[9] Anza Agave runtime source, Bank implementation, https://github.com/anza-xyz/agave/blob/master/runtime/src/bank.rs
[10] Solana Improvement Documents repository, https://github.com/solana-foundation/solana-improvement-documents
[11] Helius, Solana local fee markets, https://www.helius.dev/blog/solana-local-fee-markets
[12] Helius, Priority fees and transaction fee mechanics, https://www.helius.dev/blog/priority-fees-understanding-solanas-transaction-fee-mechanics
常见问题
Solana 写锁耗尽是什么?
它是大量交易同时请求写入同一热点账户造成的局部执行拥堵。交易本身可以是合法的,但因为写账户必须排他执行,Sealevel 无法并行处理这些交易,只能形成队列。
本地费用市场如何缓解写锁冲突?
本地费用市场把费用压力绑定到具体拥堵资源,例如热点可写账户。锁定高饱和账户的交易需要支付更高优先费,而访问冷账户或只读账户的交易不必为全网拥堵买单。
写锁耗尽会影响跨链兑换吗?
会。若目标路径依赖拥堵的 AMM、Oracle 或 vault 账户,交易可能排队、过期或失败。路由器需要把账户锁等待、priority fee 和退款时间纳入报价。
防御写锁耗尽是否等于拒绝低费用户?
不应如此。好的防御应基于资源占用、账户热度、重复失败和队列长度,而不是身份标签。低费用户可以被引导到低冲突路径或获得清晰的等待反馈。
参考资料
- Solana: Sealevel parallel processing
- Solana documentation: Transactions
- Solana documentation: Accounts
- Solana documentation: Transaction pipeline
- Solana documentation: Fees
- Solana documentation: Fee structure
- Solana guide: Priority fees
- Solana: Gulf Stream transaction forwarding
- Anza Agave runtime Bank implementation
- Solana Improvement Documents
- Helius: Solana local fee markets
- Helius: Priority fees and transaction fee mechanics


