# （Import Entry）以太坊 Safe Head 機制介紹（一） - imToken - Medium

By [imToken Labs](https://paragraph.com/@imtoken-labs) · 2023-02-10

---

原文連結：[https://medium.com/imtoken/safe-head-part-1-14071f14016b](https://medium.com/imtoken/safe-head-part-1-14071f14016b)

以太坊九月 The Merge 成功後，將共識機制切換為 PoS，同時引入了 Finalized 及 Safe Head 兩個新的 Block Tag，本文將介紹 Safe Head 的運作機制。

Photo by [Nick Fewings](https://unsplash.com/@jannerboy62?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)

如果想複習以太坊 The Merge 的其他改動，可以參考這篇：

讀者會需要對以太坊 PoS 的術語和機制有基本的了解。

Safe Head 做什麼用？
---------------

在 PoW 中，如果沒有指定你要哪個區塊的狀態，節點預設就會回傳給你 `latest` 區塊的狀態，也就是最新的狀態。但是進到 PoS 後，PoS 的區塊比 PoW 區塊更不可靠，因為 PoS 產生區塊不需要任何 “work”，而是只要是被指派的 proposer 都可以產生一個合法區塊。這表示在 PoS 裡取 `latest` 區塊的狀態會更容易發生區塊、狀態被回滾（revert），也因此才會出現 Safe Head 這個 Block Tag（`safe`）：一個比 `latest` 區塊還舊一些些但是可靠許多的區塊，讓 dApp 呈現數據給使用者看的時候，不會因為區塊不可靠、經常因為 reorg 而被 revert 導致使用體驗變很差。

*   正常情況下對新區塊而言，在出塊後約四秒會被標為 `safe`，成為 `safe` 區塊，使該區塊更可靠
    
*   不過當網路出現問題或有攻擊發生時，`safe` 區塊還是有可能 revert 回舊的區塊
    

Proof of Stake
--------------

在介紹 Safe Head 之前，先快速複習一下以太坊的 PoS 共識機制。

在 PoS 中，時間被劃分為每 12 秒為一個 slot，每 32 個 slot 為一個 epoch。PoS 的 Validator 當前約有接近 43.8 萬個，在每個 epoch 全部的 Validator 會被分配到不同的 slot（所以每個 slot 約有 13700 個 Validator），並負責在該 slot 產生 Attestation，產生 Attestation 可以視為投票的動作。此外，每個 slot 的 Validator 還會細分為 64 個不同的 committee（所以每個 committee 約有 214 個 Validator），這是為了支援 Eth2 計畫中會有 64 個 Shard Chain 的設計：每個 committee 分別投給不同 Shard Chain 的區塊，但在 Sharding 推出前這些 committee 可以先視為同一個 committee。

每一個 Validator 在其 Attestation 中主要要填入兩個投票的對象：他支持的區塊及 epoch。一個 slot 有 13700 個 Validator，所以一個區塊最多會有 13700 張選票投給它；一個 epoch 有 43.8 萬個 Validator，所以一個 epoch 最多會有 43.8 萬張選票投給它。

針對區塊及 epoch 的投票其實分別就是以太坊 PoS 中 Fork Choice Rule 及 Casper FFG 兩層共識機制的核心，下面一層是持續不斷在增長的區塊，共識是靠 Fork Choice Rule 決定，以區塊為單位，看的是每個區塊的得票數及其子孫區塊的得票數；搭建在上面的是運作比較緩慢的 Casper FFG，以 epoch 為單位，看的是每個 epoch 的得票數。

不斷增長的區塊，使用的共識是 Fork Choice Rule

Casper FFG 是以 epoch 為單位的共識

兩層共識是並行運作的

這篇[分析五月 PoS reorg 的文章](https://barnabe.substack.com/i/56929847/proof-of-stake-in-ethereum)也有簡短但是更細節的共識機制介紹。

Fork Choice Rule
----------------

你可能會好奇已經有 Fork Choice Rule 幫忙從多條分叉鏈中算出目前哪條才是最長鏈，為什麼還會需要再算一個 Safe Head？或是反過來，為什麼不乾脆用 Safe Head 算法取代 Fork Choice Rule？

其實 Fork Choice Rule 和 Safe Head 可以視為兩個不一樣的機制。節點需要 Fork Choice Rule 來決定目前最長鏈是哪一條，proposer 才能將區塊建立在最長鏈之上，即便最新的區塊獲得的得票數不高，但節點算出的最長鏈就是這條，所以還是得接在這個最新區塊後面。而 Safe Head 是給 dApp、給使用者看的，是節點替他們找出的一個比較舊但是可靠許多的區塊。

針對 Fork Choice Rule 其實有不少攻擊，例如 [Balancing Attack](https://ethresear.ch/t/balancing-attack-lmd-edition/11853) 會[讓節點不斷在兩條分叉鏈之間切換](https://ethresear.ch/t/change-fork-choice-rule-to-mitigate-balancing-and-reorging-attacks/11127)。其他攻擊可以參考最下方 Safe Head 投影片裡的第 3 頁到第 7 頁。這些攻擊都有可能造成 reorg，而 Safe Head 其實不是要算出一個區塊是可以在攻擊發生時還能不被影響，因為 reorg 是沒辦法避免的。

Casper FFG
----------

因為接下來會提到 Casper FFG 的相關名詞，所以在這裡先做一個重點提要，在文章末段會有更深入的 Casper FFG 介紹。

*   Casper FFG 有兩個產物：Justified Checkpoint 及 Finalized Checkpoint
    
*   當 Checkpoint 獲得超過 2/3 Validator 投票會變成 Justified Checkpoint，是很安全的區塊
    
*   當區塊連續兩個 epoch 都獲得超過 2/3 Validator 投票則會變成 Finalized Checkpoint，具有 Finality 性質，是非常非常安全的區塊
    

接下來將會進入今天的正題：Safe Head

Safe Head
---------

在網路正常且沒有攻擊的情況下，出塊後約四秒會被標為 `safe`，成為 `safe` 區塊，使該區塊更可靠。 這是因為 Safe Head 是基於每個區塊的得票率去做計算，當網路出現問題或攻擊正在進行時，一個正常節點所觀察到的區塊得票率會開始下降，因為那些消失的票數可能投給另一條分叉鏈上的區塊。在這種情況出現時，Safe Head 會停住（而 `latest` 區塊會持續更新）或甚至 revert 回舊的區塊。

PoS 節點可以由觀察到的得票率變化來做應對，相反地在 PoW 裡攻擊者暗中建造的鏈一但成為最長鏈，正常節點手上的區塊就會直接被 reorg 掉，節點沒有辦法提前觀察出異狀。

使用範例
----

*   讀取 Vitalik 最新的餘額：`await provider.getBalance("vitalik.eth")`
    
*   讀取 Vitalik （在 Safe Head 區塊時間點）的餘額：`await provider.getBalance("vitalik.eth", "safe")`
    

要找出 Safe Head 主要分成兩步驟：先算出區塊得票率，再算出一個區塊包含其所有子孫區塊的得票率平均。

先算出區塊得票率
--------

要能算出 Safe Head，首先第一步是算出每個區塊的得票率。一開始有提到目前每個 slot（也就是每個區塊）最多可以獲得 13700 張選票，所以得票數除上 13700 就可以算出得票率。

註：如果某個 slot 被指派的 proposer 沒有 propose 區塊，則被分配到該 slot 的 Validator 就會投給前一個區塊。所以其實一個區塊的得票數是有可能會高於該 slot 所被分配到的 Validator 數量的。

每個 Attestation 裡要填入的值會包含 `slot`、`index` 及 `beaconBlockRoot` 這三個。`slot` 就是該 Validator 被分配到的 slot，`index` 是他被分到的 committee 編號，`beaconBlockRoot` 是它投的區塊的 block root。有了這些資料我們就可以為每個區塊算出得票數及得票率：針對每個區塊找出所有`beaconBlockRoot` 和它的 block root 一樣的 Attestation，接著要去掉重複的 Validator，這是因為 Validator 簽完 Attestation 後會廣播到 p2p 網路中，然後這些 Attestation 會被合併（aggregate）起來，而因為負責合併的人可能有多個，所以同一個 Attestation 可能會出現在不同合併者合併後的 Attestation 中。去掉這些重複後的 Attestation 數量就是得票數。

接著算出一個區塊包含其所有子孫區塊的得票率平均
-----------------------

這裡比較難解釋，因此直接搭配圖示解釋比較清楚：

*   從 Justified Checkpoint 往後的區塊開始計算
    
*   一個區塊本身得票率加上所有子孫區塊的得票率平均就是一個區塊的 Safe Head 分數
    
*   當一個區塊的 Safe Head 分數超過 50（即平均得票率超過 50%）則可視為 Safe Head
    

區塊五得票率超過 50，視為 Safe Head，也是最新的 Safe Head

Block 2 的分數為其本身得票率（95%）加上每個子孫區塊的得票率（Block 3: 95%、Block 4: 95%、Block 5: 90%）的平均：`(95 + 95 + 95 + 90) / 400 = 94` %。以此類推，Block 6 是最新區塊，沒有得票率，所以 Block 5 分數即為本身得票率。

Block 4 平均得票率為 45%，沒辦法被視為 Safe Head

*   即便子孫區塊出現分叉也算，因為投給這些子孫區塊都隱含投給這個區塊本身
    
*   沒出塊的 slot 的得票率只能打五折
    

如果出現分叉，得票率（Block 5 及 Block 5'）可以相加，因為都隱含投給 Block 4 及其祖先區塊

Slot 4 沒出塊，在算它自身得票率時需打五折：55/2 = 27.5 %

Slot 4 沒區塊但得票率有超過 50%

以上是 Safe Head 的算法，看起來還算能理解，但每個新的區塊的得票率都會影響所有祖先區塊，且每個區塊都要考慮所有（包含分叉）的子孫區塊的得票率，所以實作上其實不是這麼簡單。

還沒實作的 Safe Head 算法
------------------

但目前這個 Safe Head 算法還沒被實作出來，dApp 如果現在使用 `safe` 這個 Block Tag 去查詢鏈的狀態，都會得到 Justified Checkpoint 那個時間點的狀態，也就是目前的 Safe Head 等於 Justified Checkpoint。

Justified Checkpoint 太久了，如果使用 Justified Checkpoint 則 dApp 前端顯示的是至少 6.4 分鐘以前的資訊，或著是使用者送出交易要 6.4 分鐘才會顯示出來，這都是非常糟糕的使用體驗。所以現在還不適合用 `safe` 這個 Block Tag，但只能用 latest 區塊嗎？其實到目前為止網路一直都正常且 PoS 運作也都非常順利，每個區塊得票率幾乎都是九成以上，所以在過渡期內先用 `latest` 區塊或 Block Confirmation Rule 也不需要擔心會常常碰到 reorg（只要沒被攻擊或是網路沒出問題）。

註：只單純看區塊數而不看區塊「得票數」的方式都不可靠，例如 PoS 裡用 Block Confirmation Rule 就不可靠。不過如同上述，PoS 到現在都運作的非常好，所以不需太擔心。

下一篇將介紹 imToken 嘗試實作的 Safe Head 版本、除了 Safe Head 之外能做的事、Casper FFG 以及該怎麼使用 Checkpoint 和 Safe Head。

Reference
---------

*   [https://notes.ethereum.org/@adiasg/safe-head](https://notes.ethereum.org/@adiasg/safe-head)
    
*   [https://mirror.xyz/jmcook.eth/YqHargbVWVNRQqQpVpzrqEQ8IqwNUJDIpwRP7SS5FXs](https://mirror.xyz/jmcook.eth/YqHargbVWVNRQqQpVpzrqEQ8IqwNUJDIpwRP7SS5FXs)
    

介紹 Safe Head 算法的投影片：

[https://docs.google.com/presentation/d/1MUVaFyd9ce3hPQ5L-UhqVSfxf1ajMYFbkActkp5xNKI/edit#slide=id.gf55e72f86f\_0\_121](https://docs.google.com/presentation/d/1MUVaFyd9ce3hPQ5L-UhqVSfxf1ajMYFbkActkp5xNKI/edit#slide=id.gf55e72f86f_0_121)

_Special thanks to_ [_Chih-Cheng Liang_](https://medium.com/u/5c031577a87d?source=post_page-----14071f14016b--------------------------------), [_Chang-Wu Chen_](https://medium.com/u/e77f8b591fa3?source=post_page-----14071f14016b--------------------------------), Steven Wu and [_doublespending_](https://medium.com/u/2a4e8d3cc984?source=post_page-----14071f14016b--------------------------------) for reviewing and improving this post

---

*Originally published on [imToken Labs](https://paragraph.com/@imtoken-labs/import-entry-safe-head-imtoken-medium)*
