摘要:如果 Beam 僅需要下載 3000 個狀態樹節點,而快速同步需要下載 3 億個樹節點,那麼我們就可以確定 Beam Sync 方法的速度上限:在同步主網時,可在 “從啓動到執行” 時間上獲得最多 10 萬倍的提升。快速同步方法必須在執行第一個區塊(即啓動塊)之前下載全部狀態數據,而 Beam 同步僅需要下載一個區塊的見證數據,如果所下載的區塊見證數據包含完整狀態的三分之一,那麼 Beam 同步的運行效率將大約是快速同步的三倍。

爲什麼要改善節點同步體驗?

每當我想起目前還有多少人仍在使用 Infura(通過 Metamask、Gnosis-Safe 等)與鏈上應用程序交互時,我就感到有點難受。Infura 的服務很棒,但如果大部分用戶都不自己運行節點,顯然也不太對頭。即使是非常有能力且積極性非常高的開發人員,也不能完全擺脫對 Infura 的依賴。從這點看來,我們仍未完成以太坊的 “自主驗證” 願景中的重要部分。

我們的團隊 希望儘自己的力量扭轉這一趨勢。我們的使命是儘可能增加網絡上的節點數量,特別是那些由愛好者、研究人員和開發人員運行的節點。當我們問起他們爲何不運行自己節點時,答案不外乎是:“我安裝了客戶端軟件,也嘗試了同步區塊鏈,但好像總是同步不上。於是我就停下來了,因爲我還有別的事要做。”

所以,如果我們想讓更多的人來運行節點,就得讓節點同步更快一些,並且在同步過程中能夠反饋進度信息。許多團隊都在這個領域耕耘。 專用硬件 或許是一條重要途徑。但在這篇文章裏,我們想講的是 “Beam Sync” 同步方法如何能夠大幅提升同步速度。

現有的同步方法

爲了更好地理解 Beam Sync 的原理,我們先來看看現有的幾種同步方法。

Full Sync(完全同步方法)

完全同步方法是執行自創世區塊以來的每一個區塊。創世區塊標誌着一個起始創世狀態(狀態的內容包括帳戶餘額、合約字節碼、合約存儲內容等)。所謂 “執行區塊” 就是,每下載到一個區塊,就讀取前一個狀態並(根據區塊內容)產生新狀態,並以該新狀態來驗證區塊頭中的狀態根(以驗證該區塊是不是一個有效的區塊)。在以太坊主網上完全同步的速度非常慢,而且隨着網絡的老化,使用完全同步方法來同步到最新區塊的時間也會越來越長。所以人們開發出了 “快速同步” 方法。

Fast Sync(快速同步方法)

快速同步方法就是下載過去所有的區塊和區塊頭,並選擇最近的區塊作爲 “啓動塊”。啓動塊以前的區塊都跳過執行,到了啓動塊再開始執行區塊。這種方法假設了從創世塊到啓動塊都正確地遵循了所有 EVM 規則。這個假設是合理的,因爲礦工有動力遵循誠信不作惡原則,生產正常區塊,拒絕可能具有攻擊性的區塊。

在快速同步可以執行啓動塊之前,需要的區塊狀態包括:合約字節碼、帳戶和合約存儲內容。執行交易時可能需要讀取所有這些值中的任何一個。因此,快速同步方法要求從其他對等節點處獲得啓動塊之前的狀態快照。快照是用狀態根哈希值來標記的;所謂狀態根哈希值,就是所有狀態內容的哈希 默克爾樹 根值。節點使用該狀態根哈希來驗證從其他對等節點處下載的狀態數據是否與礦工在該區塊中聲明的狀態相匹配。

快速同步方法下載完所需的所有狀態後,等於節點已經有了執行交易所需的一切數據。那麼這時候開始,節點就可以切換成完全同步模式了,從啓動塊開始可以逐個逐個執行區塊了,就跟完成了啓動塊以前的完全同步過程的節點一樣。

簡化之後的過程就像下面這張動圖所顯示的:

其他方法

其他的快速同步方法包括 Warp Sync 以及一些目前尚未得到驗證的同步方法。抽象一些來看,它們都屬於快速同步方法的不同形式。另外,即使瞭解這些其它同步方法的原理,也無助於理解 Beam 同步策略,所以這些同步原理不是我們這篇文章的重點,我以後再講。

快速同步方法有多快?

快速同步方法在目前的主網運行環境下面臨一些挑戰,因爲同步需要下載很多數據,甚至超過 100GB 的數據,所以,可能在上圖所示的第二步 “Get All State(獲得所有的狀態)” 就要卡住很長時間。

更糟糕的是,(在快速同步模式下)對等節點不會逐塊給你提供狀態數據,只會提供啓動塊前一段時間內的狀態,比如啓動塊前 100 個區塊(即開始同步前 30 分鐘)的狀態數據。Geth 客戶端的默認設置是前 120 個區塊

如果你不能在 30 分鐘內下載完所有的狀態數據( 劇透警告:你真的下不完 ),你就需要做切換(pivot),就是換一個新的啓動塊,重新開始同步,雖然不是從 0 開始,但是也增加了下載和驗證區塊的時間。

Geth 客戶端在提高同步速度上取得了 非凡的成績 ,快速同步和完全同步模式都有巨大進步,而且 Geth 客戶端的每一次更新都會有所推進,但即使你有非常完美的電腦硬件,同步過程依然需要持續至少 4 小時。對於第一次同步來說,這樣的過程確實略顯艱難。

那麼,我們團隊正在開發一個用 Python 語言編寫的客戶端,叫 “Trinity”。Python 在速度性能上不會比 Go 語言更快。如果以性能爲中心的 Geth 代碼不能像我們希望的那樣快速同步,Trinity 客戶端又有什麼機會呢?完全有理由預計 Trinity 客戶端執行一次快速同步需要幾個星期。但是如果客戶端不能同步主網,那麼它就沒意義,同樣,花費幾周時間去同步也沒有意義。出於這種需要,我們構思了一種新的同步策略,我們現在稱之爲:Beam 同步。

Beam 同步

概述

Beam 同步方法是直接改進快速同步方法的結果,這兩種同步方法的區別是 Beam 同步方法是一開始就直接執行啓動塊,並且只請求本地數據庫中缺少的狀態數據,並把輸入狀態和輸出狀態保存在本地。執行完一個塊後就同步到下一個塊並重復該過程,按需請求缺少的數據。

隨着時間的推移,缺少的數據會越來越少。注意,如果某個狀態從未被訪問過,那麼客戶端將永遠不會請求它(因此永遠也不會獲得這部分狀態數據),因此我們在後臺運行另一個進程來填補這些空白。通過該回填過程,Beam 同步最終會取得所有狀態數據並保存在本地,然後節點就可以切換到完全同步狀態。

我們將執行每個塊所需的數據集稱爲 “區塊見證數據”(block witness)。 得益於默克爾樹的結構 ,我們不用完整地下載某個狀態,就可以證明見證數據真的是從這個狀態中取出來的(譯者注:就是所謂的 “默克爾證明”)。

區塊見證數據大小

爲了簡單起見,我們用 “區塊見證數據大小” 來指稱執行區塊所需的數據元素數量。這類數據元素可能是在主要賬戶狀態樹上的一個節點,或者是合約存儲樹上的一個節點,或者是某個合約的完整字節碼(譯者注:此處說的 “狀態樹”、“存儲樹” 都是默克爾樹結構的數據)。

分析區塊見證數據大小是理解 Beam 同步方法性能的關鍵。快速同步方法必須在執行第一個區塊(即啓動塊)之前下載全部狀態數據,而 Beam 同步僅需要下載一個區塊的見證數據,如果所下載的區塊見證數據包含完整狀態的三分之一,那麼 Beam 同步的運行效率將大約是快速同步的三倍。

所以,顯然,下一個步驟是看主網的見證數據實際是多大。可能直接下結論還爲時尚早,但是早期試驗結果表明 3000 個狀態樹節點的數據量是一個合理的估計(90% 置信度)。而主網的全部狀態信息有超過 3 億個樹節點。

Beam 同步加速

讓我們新定義一個標準:“從啓動到執行” 的時間。這是從用空數據庫啓動節點到完成最近一個區塊的完全導入所需的時間。

如果 Beam 僅需要下載 3000 個狀態樹節點,而快速同步需要下載 3 億個樹節點,那麼我們就可以確定 Beam Sync 方法的速度上限:在同步主網時,可在 “從啓動到執行” 時間上獲得最多 10 萬倍的提升!

但是,Beam Sync 方法往往無法真正做到 10 萬倍提升。理由包括但不限於:

  1. 狀態數據的下載並不是建立全節點的所有工作,例如,我們還要下載區塊頭來驗證我們所同步的區塊鏈是最長鏈
  2. 區塊見證數據是按需確定的,這就意味着我們無法提前預知需要哪些狀態數據(收到一個數據,才能確定下一個需要的數據是什麼)。所以我們向對等節點請求數據時,一次只能請求一個狀態數據。相反,快速同步一次最多可請求 384 個樹節點,這使得 Beam 同步對對等節點的網絡延遲更敏感。
  3. 尋找高質量,低延遲的同步節點需要花些時間。說實在的,會遇上什麼樣的對等節點,那是真·隨機事件。

與快速同步方法不同,Beam 同步方法會持續下載啓動塊之後的區塊狀態,這樣也會拖慢區塊導入時間。如果你對這一點有一些直觀認識,那麼你可能會注意到,如果收集區塊見證的平均時間比區塊生產的平均時間還要長,那問題會更加嚴重。

Beam 同步滯後性

獲得第一份區塊見證數據所需的時間必定長於網絡產生一個區塊的時間(約 15 秒)。同樣,我們也可以預見,見證數據要幾個區塊的時間才能送達,我們把這種情況稱爲“滯後”,大約是最新一個導入區塊和鏈頂端區塊之間的時間間隔。

區塊見證數據的收集延遲問題可能會逐漸加重,然後你會發現你的 Beam 同步節點延遲了 5 分鐘。這就是說,你本地擁有的最新區塊其實是整個網絡 5 分鐘之前產生的區塊,這意味着當 RPC 調用你現在節點賬戶餘額的時候,你的節點會反饋五分鐘之前的餘額。

在坊間的測試中,很常見的一個現象是滯後時間變化很大,從 1 分鐘到 20 分鐘不等。幸運的是,我們有一些技巧可以使得區塊同步從滯後的情況中恢復,事實上,一般來說,落後越多反而恢復得越好,這就導致了滯後時間的極大波動:先是不斷落後,然後是迅速追趕,循環往復。

我們可以在區塊落後的情況下更快地趕上的一個原因是,我們可以提前、同時爲多個區塊生成見證數據。畢竟,只有落後時,你才能利用這些未來的區塊。當然,如果收集所需區塊數據的時間總是超過區塊產生時間,那麼毫無疑問你將會越來越落後於區塊鏈的增長,我們希望這種情況永遠不會發生,但我們需要爲此做好計劃。

Beam Sync Pivot

就像快速同步一樣,如果你落後太多,對等節點可能就不願意爲你提供你需要的數據了。切換(pivoting)機制是解決這個問題的關鍵。

Beam 同步裏的 pivoting 機制就像快速同步裏的一樣,你的節點選擇一系列要跳過的塊,並在鏈頂端附近選擇一個新的區塊塊區塊頭,然後,Beam 同步再一次開始,節點並沒有完全從頭開始同步,它仍然擁有來自上一次同步的所有數據。

無論你是採用 Beam 同步或者快速同步,只要使用切換機制你就需要付出相應的成本,切換機制意味着需要下載更多數據,這些數據中還會有一些你的節點並沒有親自驗證過其執行的區塊。好消息是:如果你不是落後超過 30 分鐘,Beam 同步方法就不需要激活切換機制,相反,是用快速同步方法時,你非得切換幾次不可。

好了,讓我們看看真實的情況是什麼樣

Trinity 客戶端上的 Beam 同步

原型公佈

一個新的 阿爾法版本的 Trinity 客戶端 上週公佈,這個版本包括一個可在高端硬件上運行的 Beam 同步方法原型。

我們一直在測試針對主網的同步過程。通常可在第一個小時內執行第一個區塊,多數時候都能在 5 分鐘之內搞定!這裏不包括下載區塊頭的時間和偶爾因爲缺少好的對等節點所耽誤的時間。

註釋:把區塊 Gas Limit 從 8M 提升到 10M 似乎會增加平均滯後時間,伊斯坦布爾升級後可能會減少滯後時間,因爲提高了在區塊鏈上寫入狀態數據所需的 gas 耗費量。

目前 Trinity 還是阿爾法版本,最新的版本依然有很多問題,比如同步可能會在一兩天後突然中止,即使沒有中止,也會落後很多導致激活切換機制。安裝 Trinity 客戶端需要額外的工作,命令行輸出更是一團糟。所以,Trinity 目前只准備好了給那些既好奇、又不介意實際操作一下的開發者和研究人員使用。

懸而未決的問題基本上都會在日常開發中暴露出來,所以這些問題都是 “後臺日誌上的 bug”。在這一點上,可以說,沒有人擔心 Beam Sync 會不會是一個難以企及的夢想。這種對 Beam 同步的信心是嶄新的。但我們也認爲未來有可能有突發的未知問題亟待解決,就像一個月前一樣!

剩下的工作

除了基礎的調試和實現工作,我們依然還有許多其它工作要做。Trinity 還沒有實現狀態的回填機制(即下載啓動塊之前的舊事件、事務和數據)。目前激活切換機制的唯一方法是重新啓動 Trinity,而且尚不清楚 Beam 同步方法所需的最低硬件配置(我們歡迎大家幫我們蒐集相關數據)。

以上所有問題都處於積極的研發攻克之中,並且這只是 Trinity 上許多工作之一,謝謝以太坊基金會對這些工作的贊助和支持。

Beam Sync 的創新之處在哪裏?

Beam 同步方法,和其他的理論一樣,都是建立在之前的研究工作之上的。通過下載最新狀態、跳過舊區塊執行來加速同步的方法(快速同步方法),並不是我們發明的。依靠見證數據而不是完整狀態來執行區塊,從而加速執行的方法,也不是我們發明的,詳細情況可以看 無狀態客戶端

真正的進展是我們綜合了這兩者:首先使用引導式快速同步來模擬無狀態客戶端,然後逐漸過渡到完全同步模式。 我們丟掉了無狀態客戶端的一個優點,即低硬盤開銷,但是我們保留了快速執行最近一個塊的優點。通過在本地保存狀態輸入和狀態輸出數據,我們減輕了關於無狀態客戶端的一個重要隱憂,即被大量見證數據 DOS 攻擊 的風險,Beam 同步方法執行的時間越長,被 DOS 攻擊的風險越小。

在我們運行以太坊本地客戶端的時候,Beam 同步模式可以提供更好的反饋和更快的執行結果,我們覺得這對於用戶愉快地運行本地節點來說是重要的一步。

感謝 Piper Merriam、Brian Cloutier、Alex Stokes、Voith Mascarenhas 和 Noel Maersk。

(完)

原文鏈接: https://medium.com/@jason.carver/intro-to-beam-sync-a0fd168be14a

作者:Jason Carver

翻譯&校對:陳亮 & 阿劍

本文作者使用了 CC4.0 版權協議 來限制本文的版權。只需附上作者署名並維持同樣的版權規定,即可自由使用本文版權。因此,本譯本版權遵循同樣的版權規定(保證作者署名,後續使用維持 CC4.0 協議)。

相關文章