Solana 聯創:Solana 狀態增長有何解決方案?
作者: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::loadedbytes - Account::loadcounter > CACHESIZE,則帳戶被認為是冷帳戶,其大小是根據每個區塊的 LOADLIMIT 計算
新帳戶 load_counter 為 0,因此所有新帳戶都是冷帳戶
Leader 的調度程序將 LOAD_LIMIT 作為一個水印,類似於寫鎖 CU 限制。
這種設計的絕妙之處在於,它很自然地適合當前的調度程序。用戶只需要擔心他們的優先費。調度程序必須處理將所有低於 LOADLIMIT 和帳戶寫鎖限制的 tx 放入背包問題。最高優先級的 tx 可以首先加載並使用 LOADLIMIT。一旦達到這個限制,所有其他 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,並將被壓縮。所以數據開銷會減少,甚至索引開銷也會減少,這將減少當前狀態的大小。減少狀態的大小將降低超二次分配的成本。