摘要: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 和退款時間納入報價。

防禦寫鎖耗盡是否等於拒絕低費用戶?

不應如此。好的防禦應基於資源占用、帳戶熱度、重複失敗和隊列長度,而不是身份標籤。低費用戶可以被引導到低衝突路徑或獲得清晰的等待回饋。

參考資料