編者注:一般而言,我們會將 Fault Proof 認爲是與 Layer-2 相關的概念,是 Layer-2 將自己的狀態報告給 Layer-1 時採取的模式。但在本文中,作者使用的是廣義的錯誤性證明概念,考慮的是如何設計一種錯誤性證明模式,使 SPV 節點(近似於所謂的 “輕節點”)獲得更高的安全性。

錯誤性證明(Fraud Proof)是個極爲複雜且煩人的概念,但如果你想知道我的一些心得,請耐着性子跟我一起思考吧。

- Benihime Morgan 的河流藝術作品-

簡而言之,言而簡之

  • SPV 節點非常易於運行及擴展;藉助錯誤性證明,SPV 節點(Simplified Payment Verification)可以具備和全節點相同的安全性。
  • 我要在此引入 “ SPV+ ” 模式;常規的 SPV 節點只需要保存區塊頭(存儲增量每年約 4MB ),而 SPV+ 節點還需要保存每個區塊中的第一筆及最後一筆交易(存儲增量每年約 115 MB)。
  • “SPV+” 節點必須與一個全節點建立支付通道,或是建立一個 LN 連接。
  • 每驗證一個區塊的正確性,SPV+ 節點都必須向這些全節點支付小額費用;我估計這筆費用不會超過 50 刀/月。
  • 後續就是添幾個新操作碼的事:我們需要一個鏈下的 rangeproof ,搭配類似 “ SegWit” 的 witness-commitment (見證-承諾)技術,就能方便且低成本地使用 SPV+ 節點了。

1. 背景

A. 如何讓比特幣更像實體黃金

比特幣在設計上對標的是黃金;雖然在很多方面,比特幣已遠勝於黃金,但是一談到收款,問題就來了——你怎麼知道錢到賬了?如果以黃金等實物手段支付,有沒有到帳是很簡單的——就跟所有一手交錢一手交貨的情形一樣;但如果使用比特幣(數字貨幣)支付,保障資產所有權就會變成一件很抽象的事。

對於這個問題,中本聰(Satoshi)提出了一個完美的軟件解決方案,讓你能夠知道錢到賬沒有——它就是 “比特幣” 軟件。

等會!這不又兜回原點了嗎?究竟這個軟件是如何運作的

謎底揭曉,比特幣軟件使用一種特殊的機制與其它運行軟件的計算機進行同步;這個機制有點類似 Dropbox ,但不同之處在於,由每個文件自身保證同步性,因此不會有版本控制的問題。換言之,“得知錢已到賬” 和 “得知你已實現同步”是一碼事。

中本聰在白皮書中提出了兩種 “確認錢已到賬” 的方法:

  1. 【正向做法(傳統)】運行軟件,等待實現完全同步。
  2. 【反向做法(實驗性)】首先,運行一個 “輕客戶端”——只會策略性地對某些簡單部分進行同步;然後注意是否出現 “alert(警告)”。

第一種方法就是所謂的 “全節點”,依靠的是 positive proof (正向證明)—— 你理應看到 X,一旦你看到 X ,就知道自己已經收到錢了;第二種方法稱爲“ SPV 模式”,依靠的是 negative proof (反向證明)—— 你本不應看到 Y,一旦你看到 Y,就知道自己沒有收到錢。這裏的 Y 就是白皮書中提到的 “alert(警示)”,現在大家可能更常聽到另一種叫法 —— “錯誤性證明(fraud proof)”。

B. “alert” 的理論支撐

我個人覺得最有意思的,反向證明機制(即, “alert”)與實際生活中的很多行爲方式類似。

試想以下例子:

  • 我們不會試圖 100% 杜絕兇案發生,而是在兇案發生後盡全力抓捕犯人(通過庭審定罪,給予犯人應得的懲罰)。
  • 我們不會試圖 100% 杜絕奸商存在,但如果真的出現奸商,我們會期待他被市場淘汰,然後由良商取而代之;如果有太多利益糾葛,我們會通過侵權法或規章制度淘汰我們不想要的商人。
  • 我們不會試圖保證每一項發佈的科研成果是 100% 零錯誤的,而是最大程度地公開它們,並期待能收到批評或指正。
  • 我們不會試圖 100% 防止司法腐敗。但是我們確實要求所有法律訴訟環節都必須記錄下來,保證庭審的公正性可由公衆及法律學者在事後追查。
  • 我們不會試圖變得 “全知全能”。但是我們希望能夠在書籍、網站中找到所需的知識技能,並希望未來會有專家讓這些信息變得更準確。

通常我們會假設一切事情都沒什麼問題,直到出現足夠嚴重的錯誤,我們再去修正。如果不這麼做,現實生活中我們其實很難驗證每一件事情都是 100% 正確的。

C. “alert” 面臨的理論挑戰

“alert” 的問題在於, Satoshi(中本聰)實際上並未實現這個想法。上個月,Eric Lombrozo 也在 推文 中也提到這一點。

- Eric Lombrozo:“許多我聊過的頂尖技術專家都說,錯誤性證明實在是太難實現了,而且在最糟糕的情況下甚至是不可能的。中本聰似乎認識到了其中的難度,因此從未提出過解決方案。”-

若要實現錯誤性證明,主要有以下兩個難點:

  1. 抗 DoS 攻擊:比特幣全節點之所以對 DoS 攻擊有很強的抵禦能力,是因爲工作量證明機制(Proof of Work)所具備的不對稱性——每隔 10 分鐘才能產出一個新區塊,驗證這個區塊卻只要很短的時間。不過這是否適用於“alert”?“alert” 實行 PoW 機制嗎?誰來爲服務買單?如果沒有人買單,如何阻止惡意節點濫發假的 “alert” 來掩蓋真的 “alert” ?
  2. 反向證明:惡意的/粗心的 礦工可能會丟棄區塊內的一部分數據,更極端地說,礦工可能會在根本不知曉區塊裏有什麼的情況下,創建出一個區塊!如果區塊裏包含錯誤交易,我們怎麼發現呢?如果沒人發現得了,又如何提醒他人呢?

針對第一個問題,我們可以採用區塊鏈以外的方法來抵禦 DoS 攻擊,也就是支付通道(Payment Channel)。

針對第二個問題,我們可以將(非常寶貴的)“審覈資源” 放在驗證區塊的特定部分——簡單來說,我們可以讓節點聲明自己確實知道整個區塊(所有部分)所包含的內容,然後讓驗證者驗證該聲明併爲其背書。

2. 問題

A. SPV 模式

中本聰的 SPV 模式( 白皮書 第 8 章處):

  1. 比特幣的區塊頭非常小(每年 4MB 的增量),且易於驗證,不受區塊所含交易數量的影響。
  2. 可以很容易地證明,區塊中包含了某個東西(某筆交易) “ X ” —— 只要有 “X” 本身、區塊頭,以及包含兩者的有效 Merkle Branch (默克爾分支)即可。

還不太明白的人,可以參考下面的例子:

假設我們有三個區塊頭:headerA、headerB、headerC;每個區塊頭都分別包含一個 hashMerkleRoot (默克爾根哈希):hA、hB、hC。

交易 Tx 是否存在於這些區塊( [header A] , [header B] )的任意一個之中?

是的,因爲 h( [tx] ) = ht ,且

h( ht, hs1 ) = hi1

h( hi1, hs2 ) = hi2

h( hi2, hs3 ) = hA

其中:

ht 是交易 Tx 的哈希值;

hs1、hs2、hs3 是由全節點(“Fred”)提供的哈希值。

hi1、 hi2 是 SPV 節點(“Sally”)計算得出的中間哈希值。

上述證明的實際含義是,有一棵以 hA 爲根哈希值的默克爾樹,它有兩個分支 hi2 和 hs3,哈希值爲證,別無其它可能;hi2 也只有 hi1 和 hs2 兩個分支……層層遞推,最終可證,ht 必定存在於這棵默克爾樹中,

Merkle Branch(包含了由全節點提供的哈希值,以及這些哈希值在默克爾分支上的數順序和位置信息)非常小,僅以 log(n) 的速率增長。付款者可以輕鬆 獲得/生成 Merkle Branch,並伴隨着交易一起發送給收款者;這種成本是可以忽略不計的。

也就是說,只要有比特幣區塊頭,你就能知道 “錢是否已經到賬了”。區塊頭又很容易獲得,因此 SPV 模式似乎很容易就能實現無限吞吐量。

B. SPV 模式的問題

問題在於,我們永遠無法確定一個 80 字節的區塊頭是否真的是 “比特幣區塊頭”。

唯一的方法是檢查對應區塊的全部信息。如果存在一筆無效交易或雙花交易(或者說,該區塊存在某種 “缺陷”,詳見下文的 “區塊錯誤(Block Flaws)”),整個區塊就會被視爲無效區塊。

C. 好消息

雖然我們無法驗證一個 80 字節的區塊頭是否是比特幣區塊頭,但是好在我們能對照當前出塊難度,通過計算區塊頭的哈希值來驗證區塊頭的工作量證明。(區塊頭設計得非常友好,本身就包含一組重要信息——出塊難度和時間戳信息;二者可以相互驗證。)

如此一來,我們就能檢驗礦工是否真的進行了哈希運算;可惜的是,我們還是無法確定這個區塊頭是否有價值(譯者注:即該區塊頭所對應的區塊是否不包含無效交易)。這就好比你託礦工 Matthew 幫你買一盒巧克力,你很容易就能驗證 “Matthew 是否真的花了 300 多刀買了一盒巧克力”,但是你無法確定這些巧克力是否好喫,也無法確定它們是否真的含有巧克力成分。

D. 正向/反向證明回顧

你可以喫掉盒子中的每一顆巧克力,證實每一塊巧克力都很好喫,這就是 “正向證明”。

或者你可以順着以下思路進行反向證明:這盒巧克力是被包裝好了的,看起來沒有被動過手腳;再加上這盒巧克力有品牌背書,我國又嚴格執行 品牌法/商標法;已經有很多人買過這個牌子的巧克力,如果質量有問題,我只要隨手搜一下就能看到相關新聞/差評( 實際上我查了之後,並沒有發現任何負面消息 )。

另一個採用反向證明的例子是 退款承諾 。假設你要買輛車(一件不確定質量好壞的商品,就像是新創建出但還未被驗證的比特幣區塊),現在有三款車子(分別爲“Car A”、“Car B”、“Car C”),目前你對 Car C 最感興趣。

若想獲得正向證明,你就要把 Car C 開上個數千英里,隨行配有一支龐大的機械工程師團隊一路檢查這輛汽車每個零部件,並彙報問題給你。

如果是反向證明的話:假設 Car A 和 Car B 都提供一個具有法律效力的聲明 “里程數不到 40000 英里的車子發生故障,即可退款”;但是 Car C 沒有這種承諾 ,那就反向證明了 Car C 的質量不行。

要實現比特幣上的錯誤性證明,我們需要一樣東西——在區塊合法的情況下出現;在區塊不合法的情況下絕不出現(或者反過來也行)。

在博弈論中,這被稱爲 “信號博弈(signaling game)”(或更確切地說是 “ 篩選博弈(screening game) ”)中的 “ 分離均衡(separating equilibrium) ”。其中,錯誤性證明的發送者分爲 “誠實” 和 “不誠實” 兩類,我們正試圖通過低成本的手段淘選掉不誠實的那一類。

E. 我們的需求

我們需要找到一種方法能提醒我們注意 “區塊錯誤” 。理想情況下,這種警示要來的又快又準確(即,在“交易結算之前”,或者 “ 6 個區塊確認之前“ ,或者(出於安全考量)要在“ 20~30 分鐘內”做出響應)。

舉個具體的例子,理想情形應該如下:

  1. “Sally”(SPV 節點)因爲某些原因收到了一筆比特幣,對方向她展示這筆交易的信息,Sally 也看到這筆交易是合法的。
  2. Sally 想要在不運行全節點的情況下知道這筆交易是否經過 6 個區塊的確認。因此,她先是下載了所有比特幣區塊頭,接着向全節點要了 Merkle Branch (包含【1】她的交易【2】最新區塊頭)。她得到了一個 Merkle Branch ,然而不幸的是(她根本不知道):裏面的區塊頭因爲某些原因是無效的……
  3. 就在此時,“Fred“ (全節點)必須意識到出現問題了——有一個區塊存在一到多個 “缺陷”(詳見下文)。
  4. 必須通過某種激勵措施促使 Fred 向 Sally 發起某種警告(即,“alert”)。
  5. 在其他正常情況下,不能讓 Fred 有動力發起警告(即,在沒問題的時候不會出現 “假警告”)。

F. 區塊錯誤的類別

區塊可能會出現很多種缺陷(詳見 validation.ccp, 特別是 “CheckBlock” )。我將它們分爲四類:

  1. “第一類”—— 不良交易 (無效交易、雙花交易,或是 重複的交易 )。
  2. “第二類”—— 區塊數據缺失 (記錄 Sally 交易的默克爾樹(Merkle Tree)上,與 Sally 交易所在位置相鄰的節點數據處於未知或不可見狀態 —— 這可能是人爲或無意導致的)。
  3. “第三類”—— 不良區塊 (coinbase 錯置、 版本 錯誤、“見證” 數據丟失、 [drivechain] 大量更新了 Escrow_DB/Withdrawal_DB)。(譯者注:drivechain 是作者自己提出的一種側鏈架構,可見 Peer-to-Peer Bitcoin Sidechains
  4. “第四類”—— 不當累加 (超過區塊大小/ SigOps 限制、coinbase 交易費【必須等於區塊內所有交易費之和】、 [drivechain] 側鏈的輸出—— Escrow DB 的 “CTIP” 字段)。

第一類錯誤

第一級錯誤非常好對付。Sally 可以直接通過驗證交易並 reversing the outcome (so that "false" validation returens "true") 來檢驗該交易的有效性,詳見下文。在 SPV 模式下,甚至能檢驗 nLockTime 和 CSV ,因爲 Sally 掌握了 Merkle Branch 和所有區塊頭。只要觀察到兩筆交易有相同輸入,就能輕鬆檢查出雙花交易。重複的交易必然無法通過測試,因爲它們必然屬於雙花交易(除非是 coinbase 交易,詳見第三類錯誤)。

第二類缺陷

第二類缺陷與 SPV 用戶的相關性最強—— SPV 用戶必須假設(除了區塊頭之外)區塊的剩餘部分是完整的,但是(根據定義)無法檢查是否真是如此。更糟糕的是,礦工可以在不覈實區塊內容的情況下,創建一個新的區塊,他們也確實會這麼做。因此,新創建出的區塊內可能存在無人知曉的內容,上述假設明顯是不合理的。

我將證明,從理論上來說(雖然未經實踐證實),只要能向 Sally 提供有效的 “區塊頭 + Merkle Branch”,就應該存在一個完整的 Merkle Tree(包含 Sally 的交易以及已知一定數量的有效的比特幣交易)。因此,所有與區塊鏈數據缺失有關的缺陷(即,第二類缺陷),本質上就是 “Merkle Tree 相關數據缺失” 的問題。可以說,這種缺陷就是 未知哈希值原像 的問題(易於處理),又或者說的具體點,可以通過對未知哈希原像 進行採樣 來解決這個問題(譯者注:一個哈希值的 “原像” 是指被輸入某種哈希函數後產生出這個哈希值的原始數據)。

我提出的解決方案是要求 Sally 除了區塊頭,還需要下載每一個區塊的最後一條交易(加上 Merkle Branch)。

第三類缺陷

Class III 第三類缺陷

第三類缺陷非常普遍,但我相信這種小毛病可以通過一種特定的簡單方法解決。舉例來說,區塊版本如果出錯,SPV 節點可以直接從自己維護的區塊頭中獲得正確的區塊版本。

其他大多數的信息缺失,可以從 coinbase 交易 中找到;因此除了所有的區塊頭,SVP 節點還需要保存每個區塊的 coinbase 交易(譯者注:coinbase 即區塊中特殊的增發貨幣的交易)。有了這些信息,SPV 節點就能知道:【1】coinbase 交易是否只出現一次且出現在正確的位置;【2】“見證數據(witness)” 是否存在及見證的內容;【3】確定所有 Withdrawal_DB 和大多數 Escrow_DB 的正確性。

至於 drivechain 的 Escrow_DB,主鏈上的 SPV 節點必須注意區塊中鏈間交易的累加影響;解決的方法放在第四類缺陷(本文第7章)介紹。

所以我們要增加一些開銷 —— 引入 “ SPV + ” 模式(又稱爲 “ SPV 補丁”)。“ SPV + ” 節點除了要同步比特幣區塊頭(每十分鐘增加 80 byte),還要同步每個區塊的第一筆和最末尾交易,外加與這兩筆交易相關的 Merkle Branch。

  • 舊式【中本聰的古典 SPV】:同步區塊鏈中每個區塊的區塊頭(80 byte/區塊);每收到一筆新交易就進行一次彙總(交易 & Merkle Branch)。
  • 新式【SPV + 模式】:80 byte/區塊 + (coinbase 交易 + 區塊中最後一筆交易)/區塊 + 兩個(不相同的)Merkle Branch /區塊;每收到一筆新交易就進行一次彙總(交易 & Merkle Branch); 與其他幾個節點建立支付通道。

SPV+ 模式會增加多少存儲開銷?我不確定,但假如 coinbase 交易約 1000 bytes,“最末尾交易” 約 280 bytes,每個區塊約裝有 5000 筆交易,那麼同步一個區塊的開銷就會提升爲 2192 bytes/區塊,而不僅僅是 80 bytes,而且開銷的增速不只是 O(1),會大幅提高到 O(log(n))。

按照一年約產出 52596 個區塊,因此存儲花銷會變爲約 115 MB/年,不只是 4 MB/年。這看似大幅增加開銷,但從全局的角度來看,SPV+ 仍然是非常節省的方案。如果 Sally 想要完整的檢查交易有效性,她只需要對每個區塊多下載這些數據:(可以是)最近六個月產出的區塊,或是那些記錄她收到比特幣的區塊(以及這些區塊的前後 ~10 個區塊),以及(或是)一些隨機的檢查信息。

第四類缺陷

第四類缺陷非常有意思。本文第 7 章會介紹如何將第四類缺陷轉換成第一類缺陷,不過簡單講,要解決第四類缺陷,我要求交易哈希值不僅僅作爲交易自身的保證,還要說明自己對累加指標造成的影響。舉例來說,交易不僅要保證自己是 “277 bytes”,還要說明 “加上自己之後,宿主區塊大小從 500809 bytes 增加爲 501086 bytes”。這樣一來,所有的 “假交易” 就能被孤立且識別出來了。也就是說,最末尾交易會提供很重要的信息(交易總數、區塊大小、總體交易手續費,等等)。

在我深入更多技術細節之前,爲了避免有人因爲聽不懂而掉隊,我將以故事的形式再次說明 “整個邏輯”。

原文鏈接: http://www.truthcoin.info/blog/fraud-proofs/

作者:Paul Sztorc

翻譯&校對:IAN LIU & 阿劍

相關文章