胡蘿蔔、棍子,與「免費簽名」的難題
2020 年 12 月,以太坊信標鏈上線才幾週,史上最早被懲罰的一批驗證者並不是攻擊者。他們是謹慎的營運者,做了在任何其他系統裡都理所當然的事:架了一台備援。兩台機器、同一把金鑰、都在線上——這樣一台掛了,另一台還能繼續見證。結果兩台對同一個職責都簽了名,網路看到同一個驗證者投了兩次票,協定便銷毀了他們的一部分押金。在一般的維運裡,備援是美德;但在質押裡,一台天真的熱備援,是你能架出最危險的東西。
前一篇講了胡蘿蔔:誠實見證與提案的驗證者能賺取穩定收益。但光靠獎勵守不住一條鏈。在工作量證明下,同時在兩條相互衝突的分叉上挖礦要花兩倍電力,礦工自然會擇一。在天真的權益證明下,一份簽名不過幾個位元組——對每一條競爭分叉都簽名毫無成本,還能讓你最大化「壓中贏家」的機會。這就是無利害關係問題;若放任不管,最終性將形同虛設。罰沒正是解答:讓「簽下兩則自相矛盾的訊息」這件事,可被證明地銷毀你的錢。
三種會被罰沒的方式
在以太坊上,可被罰沒的行為恰好只有三種,而這三種全是兩面表態——說出兩件相互矛盾、網路又都看得到並能算在你頭上的話。一種關乎出塊提案,兩種關乎驗證者證明。回想一下:一份證明就是對一組 `(source, target)` 檢查點的投票——這正是 Casper FFG 用來推導最終性的基本單位。
- 重複出塊提案。 身為某個時隙被選中的提案者,你對同一個時隙簽下了兩個不同的區塊。兩份已簽區塊、同一個時隙——這就是對「你究竟出了哪個塊」赤裸裸的兩面表態。
- 雙重投票。 你發布了兩份目標紀元相同、內容卻不同的證明。你等於對同一個檢查點,投了兩個互相衝突的版本。
- 包夾投票。 你發布的證明,其 `(source → target)` 區間嚴格包夾了你先前某份證明、或反被它包夾——這實質上是想改寫你早已承諾過的那段歷史。
把包夾的情況具體化。假設你在紀元 9 證明了 `(source = 8, target = 9)`,稍後又證明 `(source = 6, target = 10)`。第二段區間 `[6, 10]` 嚴格包含第一段 `[8, 9]`,因為 `6 < 8` 且 `9 < 10`。這就是一次包夾投票。協定為何在意?Casper FFG 唯有當三分之二的絕對多數,跨連續紀元把一個 source 連到一個 target(先證成、再最終化)時,才會將某檢查點最終化。而包夾,正是你若想同時支持兩個互相衝突的最終化時,非做不可的那一步——於是這條規則把任何此類嘗試,都變成了自證其罪的證據。
# Ethereum's two attester-slashing conditions (Casper FFG "commandments").
# An attestation votes for a (source, target) checkpoint pair.
def is_slashable_attestation(a1, a2):
# 1) DOUBLE VOTE: two *different* attestations with the SAME target epoch.
double_vote = (a1 != a2) and (a1.target.epoch == a2.target.epoch)
# 2) SURROUND VOTE: a1's (source, target) interval strictly surrounds a2's.
surround_vote = (a1.source.epoch < a2.source.epoch) and \
(a2.target.epoch < a1.target.epoch)
return double_vote or surround_vote
# Proposer slashing is even simpler: two different signed blocks
# for the SAME slot, from the same validator => equivocation.罰金:一次失手,對上一場協同攻擊
被罰沒一次要付多少代價?以太坊的答案刻意設計成非線性,而這正是整個機制的精髓。一次罰沒分為兩部分:一筆當下扣除的固定金額,外加一筆延後執行、且會隨著與你一同被罰沒的人數而放大的罰金。
初始罰金。 你的罪證一進區塊,就會立刻扣一筆——對 32 ETH 的驗證者大約是 1 ETH(在目前主網/Bellatrix 參數下為有效餘額 ÷ 32)。你會被標記為 `slashed`、禁止再賺取獎勵,並被推入退出佇列。一筆小小的檢舉獎勵(約 0.06 ETH,即有效餘額的 512 分之一)會給予把罪證納入區塊的提案者——於是「巡查網路」本身就有報酬,而且人人都能做。
相關性罰金——巧妙之處。 第二筆罰金約在 18 天後、即一段約 36 天視窗的中點執行,並會隨著同一個視窗內被罰沒的其他質押量而放大。寫成比例 `f = 視窗內被罰沒的質押 / 全體活躍質押`。若你是單獨被罰,`f ≈ 0`,這筆罰金幾乎為零:你的總損失約 1 ETH,是一個能承受的失誤。但若你是眾多同時被罰的驗證者之一——這正是協同攻擊的特徵——一個 3 倍的乘數就會咬下去。一次罰沒掉全體三分之一的質押,罰金便會封頂在你的全部餘額。
# Roughly how Ethereum sizes a slashing penalty (mainnet / Bellatrix params). EFFECTIVE_BALANCE = 32 # ETH, capped at 32 per classic validator # (a) Immediate penalty, taken the moment you are slashed: initial_penalty = EFFECTIVE_BALANCE / 32 # = 1 ETH # (b) Correlation penalty, applied ~18 days later (midpoint of a ~36-day window). # Scales with how much stake was ALSO slashed in that same window. f = slashed_stake_in_window / total_active_stake correlation_penalty = EFFECTIVE_BALANCE * min(3 * f, 1) # Lone operator mistake : f ~ 0 -> corr ~ 0 -> lose ~1 ETH (a few %) # Attack with 1/3 stake : f = 0.33 -> 3*0.33 = 1.0 -> lose EVERYTHING
這正是此機制的精妙:它在「意外」與「攻擊」之間劃出一條清晰的界線。誠實營運者犯一次孤立失誤,損失約 3% 的質押與幾週時間;而企圖用三分之一質押推翻最終性的對手,會損失全部——而且由於證明是聚合後的 BLS 簽章,協定握有鐵證。(誠實補充:較新的升級讓單一驗證者能持有遠超過 32 ETH 的有效餘額;罰金會隨該餘額等比例放大,因此上面的百分比維持不變。)更完整的獎勵數學,見驗證者經濟學。
- 有人觀察到驗證者 V 發出的兩則互相衝突、已簽名的訊息——任何人都辦得到,因為它們在網路上公開可見。
- 他們把這一對包成 `ProposerSlashing` 或 `AttesterSlashing`,下一位提案者將其上鏈;當下約 1 ETH 的罰金立即生效,V 也被排入退出佇列。
- V 在佇列中等待期間再也無法賺取獎勵,並因為不再活躍而持續累積一般的見證罰金。
- 約 18 天後,相關性罰金生效,金額依該視窗內與 V 一同被罰沒的總質押量計算。
- 罰沒後約 36 天,V 變為可提領;殘存的餘額終於能被取出。
罪魁禍首幾乎都不是攻擊者,而是備援
這裡有個誠實、卻略顯掃興的真相:縱觀主網數年,絕大多數罰沒根本不是攻擊。它們是營運者罰沒了自己——而頭號肇因,正是維運工程師被訓練去打造的那種備援。為了「高可用」把同一把驗證者金鑰跑在兩台機器上,等到兩台都在線那天,它們就會對同一個職責重複簽名,親手製造出你自己的罰沒罪證。
迄今最大的單一事件發生在 2021 年 2 月:質押服務商 Staked 在一次故障切換/備援設定重複簽名後,約 75 個驗證者一次同時被罰沒。沒有資金被竊、也沒有攻擊——只是一個設定失誤,被複製到許多把金鑰上。這是相關性罰金的一堂鮮活教材:正因為這些罰沒在時間上扎堆,它們合計起來,比 75 次互不相干的罰沒更貴。
防護機制已內建於每一個正經的驗證者用戶端:一個罰沒防護資料庫。在簽署任何東西之前,用戶端會比對一份本地紀錄——它曾提案過的最高區塊時隙、以及曾證明過的最高 source/target 紀元——並拒簽任何可能招致罰沒的內容。一套標準的交換格式(EIP-3076)讓你能匯出這份歷史,使它在你遷移機器時與金鑰一同移動——這是搬移驗證者唯一安全的方式。
{
"metadata": {
"interchange_format_version": "5",
"genesis_validators_root": "0x04700007f..."
},
"data": [{
"pubkey": "0xb845089a1457f811bfc000588fbb4e713669be8c",
"signed_blocks": [{ "slot": "81952" }],
"signed_attestations": [{ "source_epoch": "2290", "target_epoch": "3007" }]
}]
}不活躍洩漏:針對另一種過錯的溫和懲罰
罰沒懲罰的是說了兩種話。那麼什麼都不說——單純離線——又如何?這根本不是罰沒;它由一套截然不同、也溫和得多的機制處理,叫做不活躍洩漏。而搞懂這兩者為何不同,就是搞懂這套協定的靈魂。
在平時,一個只是離線的驗證者,不過是錯過獎勵,並付出與它本可賺得的金額大致相當的微小罰金——離線一天,損失約一天的收益,沒什麼戲劇性。洩漏只在緊急狀態下啟動:當鏈連續超過四個紀元都無法達到最終性時——而這唯有在超過三分之一的全體質押同時離線時才會發生(某雲端區域斷線、某用戶端 bug 同時打掛大量節點)。失去最終性的鏈仍然活著、仍在出塊,卻再也無法把任何東西認證為不可逆。
洩漏期間,每個驗證者都背著一個不活躍分數:在「離線且未最終化」時上升,在「在線」時衰減。每個紀元的罰金與該分數成正比,因此持續離線的驗證者,會以隨時間加速、大致呈二次方的速率損失質押——而在線的驗證者一毛都不洩漏。重點不是報復,而是復原。藉由只榨乾缺席者,洩漏緩緩縮小他們在總質押中的占比,直到仍在線的驗證者再次構成剩餘質押的三分之二絕對多數——此時鏈便重新最終化(證成恢復),洩漏隨之關閉。
# Inactivity leak: only active when the chain FAILS TO FINALIZE for 4+ epochs. # Each validator carries an inactivity_score: # offline & not finalizing -> score += 4 each epoch # online (or once finalized) -> score -= 16 each epoch (decays toward 0) # # Per-epoch leak for ONE validator: leak = effective_balance * inactivity_score / 16_777_216 # 2^24 (Bellatrix) # # score grows linearly with time offline => cumulative loss grows QUADRATICALLY. # Online validators' scores decay => they are NOT leaked. # Draining the absent shrinks their stake until the online set is again 2/3 # of the remainder => finality resumes, leak stops.
於是這兩套機制守護的是兩個不同的承諾,而把它們並排對照,正是把整個階段裝進腦海最乾淨的方式。罰沒守護安全性(絕不把兩段衝突的歷史都最終化):由兩面表態觸發、由相關性放大,它會逐出你,且可能讓你血本無歸。不活躍洩漏守護活性(持續推進):由大規模缺席觸發,溫和且呈二次方,它絕不會因惡意而逐出你,並在你一回歸的瞬間即逆轉。這正是安全性對活性之分的具體化身。驗證者有可能被洩漏到 16 ETH 門檻以下,因餘額過低而被強制退出——但那是一個會計上的結果,不是罰沒:沒有罪行、沒有檢舉者、也沒有相關性乘數。
為什麼這讓最終性值得信任
退一步看,這套架構相當優雅。獎勵讓誠實有利可圖;罰沒讓兩面表態形同自殺;不活躍洩漏讓鏈在故障後能自我痊癒,而非永遠卡死。三者合力,把「利益綁定」從口號變成可強制執行的程式碼——規則靠的不是可信的裁判,而是「破壞它無法獲利」這件事本身。
這正是下一篇核心構想的根基:經濟最終性。要推翻一個已最終化的檢查點,需要三分之二的絕對多數對兩段衝突的歷史投票——而那依定義就是雙重投票或包夾。因此在這場推翻的當下,至少三分之一的全體質押 ETH,都會在行為中被可證明地罰沒。重組已最終化的以太坊,不只需要過半的驗證者;它會讓攻擊者付出約莫三分之一的全體質押——數百萬枚 ETH、數百億美元——被協定銷毀。這個價碼,才是這裡「最終」一詞真正的意義。
也要誠實面對罰沒做不到的事,因為這些界線很重要。它懲罰的是可證明的兩面表態,而非一切壞行為:驗證者可以審查交易、或單純離線,全程都不必簽下兩則矛盾訊息,因此這些危害需要別的工具來對付。它假設攻擊者在乎錢——一個願意燒掉數百億、只為讓鏈停擺一天的國家級行為者,並不會因為損失這些錢而卻步。它還製造了弱主觀性:一個從創世開始同步的全新節點,光憑密碼學無法分辨哪一段已最終化的歷史才是真的,因為那些會因說謊而被罰沒的驗證者,可能早已退出並提領完畢。所以新節點必須從一個近期的可信檢查點起步。這些不是可以揮揮手帶過的瑕疵;它們是密碼經濟學共識所能承諾的,那條誠實的邊界。