作者:toly,Solana 聯合創始人

編譯:Felix,PANews

 

每天大約有 100 萬個新賬戶被添加到 Solana 中,現在的總狀態已超 5 億,而快照大小約爲 70GB。隨着硬件的改進,這些數字本身是完全可管理的,但是 SVM 運行時的目標是提供最便宜的硬件訪問方式,爲了實現這一點,必須在當前硬件限制內管理狀態和內存。

PCI 帶寬

截至 2024 年,最新的 PCI 帶寬可以達到 0.5 Tbs 到 1 Tb 的吞吐量。或者每秒 64GB 到 128GB。雖然聽起來很大,但如果一個 tx 讀取 / 寫入爲 128MB, 128GBps 的 PCI 帶寬會將鏈的 TPS 限制在 1000 左右。實際上,大多數 txs 訪問的是最近加載並緩存到 RAM 中的內存。理想的設計應該是允許加載 1000 個具有 128MB 新狀態的 txs,再加上 10k 或更多讀取和寫入現有緩存狀態的 txs。

帳戶索引

創建新帳戶需要證明該帳戶當前不存在。這通常是在每個驗證器上自動完成,因爲每個驗證器都有當前所有有效帳戶的完整索引。即使帳戶數據不存儲在本地,只存儲數據的哈希,5 億個帳戶也將是 32 字節的密鑰 + 32 字節的數據哈希或者每項 64 字節,即 32 GB。這已經足可以保證 RAM 和磁盤的分離。

快照大小

在某些快照大小(Snapshot Size)下,如果部分網絡出現硬件故障,冷啓動新系統所需的時間足以延長最壞情況的重啓時間。隨着帶寬和硬件的改進,情況每天都在變化,而 Solana 並沒有接近這個限制,但該限制在任何時間點都存在。

概要

內存和磁盤具有不同的性能特徵和限制。如果 SVM 不區分,那麼交易和限制就必須針對最壞的情況進行定價,進而限制了性能。在交易執行期間,所有帳戶密鑰至少必須可用,並且總帳戶數量將影響 RAM 和磁盤 PCIi 帶寬利用率。快照不能任意增大。理想的解決方案是:

  • 允許將更多不需要 PCI 資源的 txs 打包到區塊中

  • 管理總索引大小和快照大小

Chilly、Avocado、LSR。糟糕的名字通常是優秀軟件設計的標誌。Anza 和 Firedancer 的工程師想出了以下方案。

Chilly

帳戶運行時的緩存由所有實例(instances)進行確定性管理。從更高層次看,這是訪問狀態的 LRU 緩存。在區塊構建和調度期間,該實現(implementation)可以很容易檢查帳戶,不需要鎖定或迭代 LRU 緩存。緩存是用一個非常簡單的計數器機制實現。

  • 總加載字節被跟蹤爲 Bank::loaded_bytes:u64

  • 每個帳戶在使用時都用當前運行總數 account::load_counter:u64 進行標記

  • 加載帳戶時,如果 Bank::loaded_bytes - Account::load_counter > CACHE_SIZE,則帳戶被認爲是冷帳戶,其大小是根據每個區塊的 LOAD_LIMIT 計算

  • 新帳戶 load_counter 爲 0,因此所有新帳戶都是冷帳戶

  • Leader 的調度程序將 LOAD_LIMIT 作爲一個水印,類似於寫鎖 CU 限制。

這種設計的絕妙之處在於,它很自然地適合當前的調度程序。用戶只需要擔心他們的優先費。調度程序必須處理將所有低於 LOAD_LIMIT 和帳戶寫鎖限制的 tx 放入揹包問題。最高優先級的 tx 可以首先加載並使用 LOAD_LIMIT。一旦達到這個限制,所有其他 tx 仍然可以放入一個區塊中。因此,驗證器可以最大化緩解 txs 的緩存局部性。

Avocado

Avacado 由兩部分組成,狀態壓縮和索引壓縮。首先用哈希替換帳戶數據,然後將帳戶索引遷移到 Binary Trie / patricia Trie。新帳戶必須提供證明,證明他們不在「trie」中。

狀態壓縮

大致設計如下:

  • 在分配期間,每個帳戶每字節綁定 X 個 lamports。

  • 如果 X < 當前經濟底價,則將賬戶保留在內存中,該賬戶將被壓縮

  • 壓縮是一個多步驟的過程,運行在一個 epoch 上

  • 帳戶數據被替換爲哈希值(data)

  • 帳戶密鑰仍處於狀態之中

  • 引用壓縮帳戶的交易失敗

  • 解壓需要上傳類似於加載程序的數據

  • 解壓的成本應該與分配一個新帳戶的成本相同

估計 75% 的賬戶在超過 6 個月的時間裏沒有被訪問,而且很可能永遠不會被訪問。壓縮它們可以節省 50% 的快照大小。

索引壓縮

這是一個更難解決的問題。僅通過狀態壓縮,驗證器仍然擁有系統中所有可能的有效帳戶。創建新帳戶需要檢查此數據庫。驗證器存儲此數據庫的成本很高,但用戶創建新帳戶的成本很低。要保證新私鑰不會與現有帳戶發生任何衝突。

Binary Trie mining

  • Binary Trie 作爲快照的一部分被跟蹤

  • 想要獲得額外 sol 的驗證者可以創建一個交易,從狀態中刪除壓縮的帳戶 kv 對,並將它們添加到 Binary Trie 中

  • 用戶可以在解壓過程中將 kv 從 Trie 中移除,從而在不被允許的情況下反向執行此操作(這可能需要在解壓時進行原子操作,以便在後臺服務壓縮帳戶時更容易)。

  • 對於驗證器,無論它包含多少 kv 對,Trie 根的大小都是恆定的

  • 使用 zkp,每個 tx 可以壓縮約 30 個帳戶

  • 假設每個區塊只有一個,那麼壓縮 5 億個賬戶需要大約 80 天的時間

這個過程的關鍵之處在於,執行此操作的驗證者將獲得獎勵,但並不是所有驗證者都必須執行此操作。如果所有驗證器都必須執行此操作,那麼所有驗證器都必須維護當前 Binary Trie 中的內容,這意味着整個狀態必須是快照的一部分。想要維護整個狀態的驗證器應該提交一個交易,將索引中的 N 個帳戶壓縮到 Trie 中。

新帳戶證明

要創建一個新帳戶,用戶必須證明該帳戶在 Trie 中不存在。維護整個狀態的驗證器可以生成帳戶不在 Trie 中的證明。這給用戶帶來了負擔,他們必須始終與大型狀態提供者連接以生成這些證明。

或者,用戶可以證明他們的帳戶是用最近的 PoH 哈希創建的。支持這一點的最簡單的方法是:

  • 生成新的 PKI

  • 帳戶地址是哈希(最近的 PoH 哈希,PKI::public_key)

鑑於 Trie 中的帳戶必須首先進行狀態壓縮,這需要一個完整的 epoch。Trie 中的任何帳戶都不可能使用最近的 PoH 哈希來生成地址。

其他可以支持的方法是 PKI 創建本身可以提供一個證明,證明私鑰是用哈希(用戶隱藏的祕密,最近的 PoH 哈希)創建的。

LSR

Lightweight Simple Rent,又稱 Less Stupid Rent。如何爲分配新帳戶的成本定價,以及如何確保舊的廢棄賬戶最終得到壓縮,並減少系統的整體負載和新用戶的價格?

需要恢復租金(Rent)制度。Rent 是指當前狀態下的賬戶應該支付 X 美元 / 字節 / 天的費用,就像 AWS 上的賬戶支付存儲費用一樣。

Rent Rate bonding curve

RentRate = K*(state_size)^N

無論當前狀態大小如何,如果很小,費率應該很低,如果接近快照限制,費率應該非常高。

Allocation Minimum Bonding Price

賬戶必須至少存在一個 epoch。分配需要將帳戶帶入 Hot 狀態。熱帳戶應該在緩存期間存在。

New Account bond = Epoch Slots * RentRate * Account::size

新賬戶的餘額中必須至少有這麼多的 lamports 才能創建。

Hot Account Burn

lruturnverrate = 每個帳戶在 LRU 緩存中平均佔用的時間,最大值爲 1 epoch。這個值可以是一個常數,也可以在鏈下計算,並作爲中位數權益加權常數報告給 SVM。

壓縮

當(current slot - account::creation_slot) * RentRate * account::size > account::lamports 時,壓縮帳戶並燒燬所有 lamports。

上述解決方案,應該會讓 State 很便宜,因爲隨着時間的推移,未使用的帳戶最終會達到 lamports 0,並將被壓縮。所以數據開銷會減少,甚至索引開銷也會減少,這將減少當前狀態的大小。減少狀態的大小將降低超二次分配的成本。