丁沛靈「無涯社區」分享:跨鏈協議以及在 Forge 上的實現

分享: 丁沛靈
編輯: 紅軍大叔(無涯社區)
9 月 26 日北京時間中午 12 點 30 分,ArcBlock 區塊基石工程師丁沛靈應邀參加無涯社區舉辦的第 33 期「價值探索」線上分享活動,從技術的角度分享跨鏈的技術難點與原理,以及 ArcBlock 在跨鏈技術的取捨。

開場
大家好,我是丁沛靈,在 ArcBlock 主要從事後端以及區塊鏈相關的設計與開發。
之前我主要負責 OCAP(開放鏈訪問協議)的數據索引工作,將 BTC 和 ETH 的數據從鏈上索引下來,並確保 OCAP 能高效、完整、正確返回這些數據。現在主要負責跨鏈協議和 ABT DID Authentication 協議的設計與實現。今天我很高興能有這個機會和大家分享一下過去一段時間來我們在設計和實現跨鏈功能時遇到的問題以及從中學習到的經驗。
想必大家都很清楚跨鏈的重要性了,那麼我就不再贅述,今天讓我們來主要聊一聊怎麼跨鏈。今天分享的大致流程是這樣的,首先我會從整體上去討論一下實現跨鏈所需要具備的基本要素和難點,然後我會分享一下我們 ArcBlock 是如何設計並且實現跨鏈功能的,最後如果大家有什麼問題,我很樂意盡我所能為大家解答。
01 跨鏈概要
在開始之前我想先引用一下 V 神之前提出的觀點。他將跨鏈總共分成以下幾個情況:可轉移資產(Portable assets)、原子交換(Atomic Swap)、跨鏈預言機(Cross-chain oracles)、資產鎖定(Asset encumbrance)、跨鏈合約(General cross-chain contracts)。
今天我們只專注於第二種情況:原子交換,因為這種跨鏈方式是最接近於交易的。並且我們只討論如何在兩條由 Forge 框架搭成的鏈上實現原子交換,至於如何用原子交換把比特幣和以太坊連接起來,那將是另外一個話題。
02 跨鏈的基本要素
那麼好,下面開始進入正題,我們先來聊聊跨鏈的基本要素。
在現實生活中,我們很容易把一個蘋果從一個籃子裝到另外一個籃子裡面,但是在區塊鏈的世界裡面,我們無法把一個鏈上的數據搬到另外一條鏈上。
跨鏈的問題,本質上和傳統的跨數據庫數據傳輸沒有區別。 我們能做的僅僅只是在一條鏈上對數據進行一次更改,然後在另外一條鏈上再對數據做一些更改,兩次更改需要滿足一定的邏輯關係。
就好比平時跨行轉賬的時候,並不是工行派一輛運鈔車把錢運到農行去,它們僅僅只是在各自的數據庫上做了一些改動。
03 跨鏈的難點
這個問題的難點就在於,這兩次數據改動需要是原子性的,也就是說要麼兩次改動都成功,要麼兩次改動都不成功, 我們不允許只有一次成功的情況發生。
在傳統的中心化數據庫領域,這個問題並不難解決,因為我們可以很容易根據第一個數據庫裡的數據更改情況來決定是否對第二個數據庫做更改,並且很容易回滾第一個數據庫。
但是,在區塊鏈的世界中這一點非常難辦到,而難點就在於這 「根據」 兩字。
對於一條鏈來講,凡是無法直接從該鏈上取得的數據,我們都稱為鏈下數據。假設我們有 A,B 兩條鏈,對於鏈 A 來講,B 鏈上的信息它是無法直接取得的,所以是鏈下信息,反之對於 B 鏈來講,A 鏈上的信息也是鏈下信息。
在區塊鏈的世界中,很難根據鏈下信息來做出相應的判斷,而其原因在於鏈下信息源的去中心化。
對於鏈上的所有信息,該鏈上的所有節點都已經達成共識,但是我們應該怎麼樣讓所有節點對鏈下信息達成共識呢?
如果我們只採用單一信息源,那豈不是違背了去中心化這個宗旨?這一個單一的信息源將完全控制整條鏈。但是如果我們採用多信息源,我們又怎麼才能以可接受的代價來讓本條鏈的所有節點就這些信息源達成共識呢?
所以跨鏈的本質就是在於獲取鏈下信息, V 神提出那 5 種情況,其背後都是一個問題,就是獲取鏈下信息。
在討論了跨鏈交易的要素和難點之後,我們來看看到底應該如何去設計與實現。我們採用的方法叫 Atomic Swap,或者叫原子交換。我們先看看它的大致流程,以及實現上的一些考量,最後我們回過頭來看看它是如何解決上面提到的跨鏈難點的。
04 Hash Lock 和 Hash Key
在介紹 Atomic Swap 之前,我們先引入一個概念:Hash Lock 和 Hash Key 或者叫哈希鎖和哈希鑰匙。
一個 Hash Lock 其實就是一個隨機數的 Hash 值,而這個隨機數本身就是哈希鑰匙。
假定我們有一個隨機數 x,然後我們對它進行一次 Hash 運算,得到它的 Hash 值 y,那麼我們就稱 y 是 Hash Lock,而對應的我們可以稱 x 為 Hash Key 或者叫哈希鑰匙。我們可以有如下形式化的表示:Hash(x) = y.
05 Atomic Swap
好,有了這個概念之後,下面我們就來談談怎麼做 Atomic Swap。
我們假定有如下場景:Alice 和 Bob 想做一次跨鏈交易,Alice 願意以她在 A 鏈上的 100 個 token 來買 Bob 在 B 鏈上的一個 asset,該 asset 的地址為 z123。
那麼假定在做 Atomic Swap 之前,Alice 和 Bob 的狀態如下:在 A 鏈上,Alice 有 100 個 token,Bob 沒有 token;在 B 鏈上,Alice 沒有 Asset,而 Bob 有一個 Asset。

在這個基礎狀態的前提下,我們來看 Atomic Swap 的流程:
第一步,由 Alice 發起,Alice 先產生一個隨機數 x 作為哈希鑰匙,然後生成相應的哈希鎖。Alice 用這個哈希鎖把 100 個 token 鎖在 A 鏈上。在鎖定的時候要指明如下信息:
- 待鎖定的 token 數量,在此例中為 100
- 解鎖人是誰,在此例中為 Bob
- 設置一個鎖定時間,在這個鎖定時間之後,如果 Bob 還沒有取走 token 的話,Alice 可以單方面撤走這些 token,但是在鎖定時間之前 Alice 不能這麼做
- 用的是哪個哈希鎖

鎖定好之後,這 100 個 token 就從 Alice 的名下劃走了,確保她不能再使用這些 token。
這條 Transaction 的內容在 A 鏈上是公開可見的,所以 Bob 可以很清楚的知道裡面的所有內容。
第二步,Bob 在確定 Alice 已經鎖好 token 的情況下,用同樣的哈希鎖把 asset 鎖在 B 鏈上,同樣的,鎖定的時候需要指明:
- 待鎖定的 asset 地址,在此例中為 z123
- 解鎖人是誰,在此例中為 Alice
- 設置一個鎖定時間,在這個鎖定時間之後,如果還沒有取走 asset 的話,Bob 可以單方面撤走這些 asset,但是在鎖定時間之前 Bob 不能這麼做
- 和 Alice 用同一把鎖

第三步,由 Alice 先去解鎖鏈上的 asset。 在解鎖的時候,Alice 必須提供哈希鑰匙,當驗證通過之後,Alice 即可取走鎖定的資產。

第四步,由 Bob 解鎖 A 鏈上的 token。 由於 Alice 已經在 B 鏈上解鎖,所以哈希鑰匙也被公佈了出來,這個時候 Bob 就可以很輕鬆的知道鑰匙是什麼,從而完成在 A 鏈上的解鎖,拿到相應的 token。

按照上面的流程,Alice 和 Bob 就可以順利的完成 Atomic Swap,並且最終的狀態應該是這樣的: 在 A 鏈上,100 個 token 從 Alice 的賬戶轉移到了 Bob 的賬戶下;在 B 鏈上 asset 從 Bob 的賬戶下轉移到了 Alice 的賬戶下。

但是,還有另外一種情況,就是在第三步的時候,Alice 如果改變了主意,決定不去取 B 鏈上的資產。如果是這樣的話,那麼 Alice 也不會洩露相應的哈希鑰匙,所以 Bob 也無法取得 A 鏈上的 token。在這種情況下,Alice 和 Bob 只需在鎖定時間之後,將各自鎖定的 token 以及 asset 取回即可。

06 流程小結
下面,我們來總結下這個流程:
- 首先,整個 Atomic Swap 是在鏈上實現,並且自始至終都沒有第三方參與,完全由 Alice 和 Bob 完成。
- Alice 和 Bob 之間不需要互相信任,因為這套機制保證了雙方的財產安全。如果 Alice 取走了 asset,那麼 Bob 一定知道哈希鑰匙從而可以取走 token。如果 Alice 不取走 asset,Bob 也無法得知哈希鎖,所以也無法取走 token。
- 正因為上面的這個特性,我們才給這個方法取名為 Atomic Swap。整個交易是原子性的,要麼雙方都能得到想要的東西,要麼雙方都得不到。
- 雖然 Alice 是首先鎖定和解鎖的那一方,但是這並不代表 Bob 是處於一個被動的局面。Bob 在鎖定 asset 之前,可以先查看 Alice 在 A 鏈上鎖定的 token。他需要檢查鎖定的 token 數量對不對,解鎖人是不是他,以及很重要的,那個鎖定時間到底是多少。因為在這個鎖定時間之後,Alice 是可以單方面撤走 token 的,所以如果這個鎖定時間和當前時間很接近的話 Bob 是有可能損失掉資產的。所以 Bob 在鎖定資產之前,應該確保這個鎖定時間是在當前時間之後的一個合理範圍。比如,當前區塊高度是 10000,並且平均 15 秒鐘一個塊,那麼一個合理的鎖定時間可能是 15760, 也就是說在第 15760 個塊之後,Alice 才能單方面撤走 token。這個大致相當於一天的時間。
- 進一步的,Bob 在鎖定資產的時候,也應該設置一個合理的鎖定時間,而這個時間應該小於 Alice 設置的鎖定時間,比如 Bob 可以設置為 10240。我們仍然假設平均 15 秒一個塊,塊高從 10000 走到 10240,大概需要一個小時,所以這相當於是只給了 Alice 一個小時的時間做決定是否取走 asset。如果 Alice 取走了 asset,那麼 Bob 至少還有 23 小時去取走 token。
上面介紹的是 Atomic Swap 的大致流程,在有了一個整體概念之後,我們來看看具體在 Forge 上怎麼實現。
07 Forge 上如何實現
首先是鎖定。
我們設計並實現了 SetUpSwap 這個 Forge Transaction 來讓用戶鎖定 token 和 assets。
在這個 Transaction 中,發送方需要填入 Receiver address, Hash Lock, Locktime(鎖定時間), 以及想要鎖定的 token 數量和 asset 地址。
鏈節點在執行這個 Transaction 的時候會驗證 Locktime 是否大於當前塊高,以及發送者是否持有相應的 token 和 asset。如果不滿足條件的話,Transaction 會失敗。
當 Transaction 通過後,Forge 會生成一個 SwapState。這個 SwapState 的地址是根據 Transaction Hash 來生成的,因為 Forge 不允許重復的 Transaction Hash,所以 SwapState 的地址也是不會重復的。
這一點也同樣保證了,每一個 SwapState 是單獨的,互不影響的。
SwapState 本身不屬於任何賬號,它只按照 Atomic Swap 的規則運行。當 SetUpSwap Transaction 上鏈之後,相應的信息會記錄在 SwapState 上,如 Sender address, Receiver address, Locktime, Hashlock,Token 以及 Asset addresses。其中 Token 和 Assets 是從 Sender 轉來的,這樣確保發送方無法再更改這些 Token 和 Assets。
第二步是解鎖, 對應的 Transaction 是 ReceiveSwap Transaction。
在這個 Transaction 中,我們需要填入想要取回的 SwapState 的地址,以及相應的 Hashkey.
鏈節點在執行這個 Transaction 的時候會驗證 SwapState 中 Receiver 的地址和此 Transaction 發送者的地址是否一致,Hashkey 是否和 Hashlock 匹配,以及 SwapState 中是否還有 Token 或者 Assets。
當條件滿足,Transaction 執行通過之後,Hashkey 會被寫入 SwapState 以供所有人查閱。SwapState 中的 token 和 assets 會轉移到相應的賬戶中。如果中途想終止交易,那麼就需要撤回鎖定的 token 或者 assets。我們用 RevokeSwap Transaction 來實現這一步。
在這個 Transaction 中,我們只需要填入 SwapState 的地址即可。
Forge 會驗證 SetUpSwap Transaction 和 RevokeSwap Transaction 的發送方是否為同一個人。同時也會驗證,當前區塊高度是否已經超過 SwapState 裡面記錄的 Locktime,以及 SwapState 裡面是否還有 token 和 assets。
如果 Transaction 成功被執行 SwapState 裡面的 token 和 assets 會被轉移給 Transaction 發送方,從而達到撤回的效果。
在整個設計和實現的時候有很多細節需要去思考。
- 首先,整個 Atomic Swap 裡面最重要的因素就是哈希鑰匙,所以對哈希鑰匙的保護是我們考慮的第一個點。在具體實現時,和其他 Forge 上的 Transaction 相比,我們做了一個改動。那就是當一個 Forge 節點在驗證一個 ReceiveSwap Transaction 的時候,如果這個 Transaction 失敗了,那麼它是不會被記錄到鏈上的,而且也不會被廣播給其他節點。這樣最大限度的減少 ReceiveSwap Transaction 中的 Hashkey 被曝露的範圍。
當然,即使這個 Transaction 不被寫到鏈上,不被廣播,但是它仍然至少被一個節點驗證過,如果這個節點本身就是作惡的節點,那麼它完全有可能記錄下該 Hashkey。所以為了避免這種情況,合理的設置 Locktime 就顯得尤為重要。因為在 Locktime 之前,都只有對應的 Receiver 被允許轉走 SwapState 裡面的 token 和 assets。那麼 Receiver 應該利用這段時間,排查失敗的原因並且不斷的進行重試。
在實際中,失敗的原因可能是網絡問題或者是 gas 不夠等。為此我們在錢包裡面也做了相應的優化。錢包會在用戶發送 ReceiveSwap Transaction 之前比較當前塊高和 Locktime,如果兩者過於接近,錢包會有相應提示。
- 第二個潛在的安全因素是 Hashkey 的大小。 在 SetUpSwap Transaction 中,我們只包含 Hashlock 的值。由於該值是一個 Hash 值,所以大小是固定的。但是我們並不知道 Hashkey 的大小,Hashkey 可以是任何值。在一些極限情況下,一個特別大的 Hashkey 可以導致單方面的 ReceivSwap Transaction 驗證失敗。所以我們把 Hashkey 的大小限制在 64 字節。
08 Atomic Swap 小結
最後讓我們來總結一下 Atomic Swap 吧。
在本次分享的最開始我就提出,跨鏈的最終難點在於鏈下信息的獲取。那麼在瞭解了 Atomic Swap 的流程之後,請大家想想我們是怎麼在這個流程中解決這個難點的呢?
其實,想必大家也意識到了,我們並沒有在 Atomic Swap 中真正的解決這個問題,而只是巧妙的規避了它。
在整個流程中,其實真正跨鏈了的東西就只是一個隨機數,也就是 Hashkey。 我們並沒有像最開始說的那樣,根據第一條鏈的信息,來對第二條鏈做數據更改,而是把相應的責任完全轉移到了 Hashkey 的身上。
這種轉移是一種妥協,並沒有完完全全的解決這個問題。之所以說是一種妥協,是因為我們假定,Bob 知道 Hashkey 這件事和 Alice 已經成功取走 asset 互為充分必要條件,所以我們不會在 Bob 取走 token 的時候驗證 Alice 是否真的成功取走了 asset。然而在真實的環境中,這種假定還是有可能不準確的,就像在談及安全問題時我們所提到的那樣。
但是我認為,在鏈下信息源去中心化這個巨大的障礙面前,做出這個妥協是值得的。我們曾經也探索過其他的方案,這些方案要麼會讓系統規則變得極其複雜(比如引入一個中間人去驗證鏈上信息),要麼會巨大的增加運行節點的成本以及降低系統的可擴展性(比如將區塊鏈擴展為區塊鏈網),所以最終我們選擇了 Atomic Swap。
以上就是我今天的全部分享,謝謝大家。
提問
問:Forge 目前支持哪些開發語言?以及你們是通過什麼方式來支持多語言的?這裡有涉及到類似 WASM 這樣的機制嗎?
Forge 其實分鏈上和鏈下兩個部分。
鏈上部分我只支持 erlang 或者 elixir 因為 Forge 本身可以說是建立在 erlang 虛擬機之上的鏈。鏈下部分的話我們為各個語言開發了 SDK,比如 elixir, Javascript, Python。同時我們也正在支持 Rust 和 C/C++。
目前 Forge 裡面並沒有涉及 WASM。
問:Forge 的定位是什麼?是開發 Dapp 的還是開發鏈?還是二者皆可?有側重嗎?對於開發者來說開發一個 dapp 和在類似以太坊上開發一個智能合約應用你覺得主要不同點是哪裡?
在回答這個問題之前,首先我得說明一下我對 Dapp 的理解。Dapp 這個概念的來由應該是以太坊。
在以太坊上之所以有這個概念是因為它本身是一台“世界計算機”,它把整個網絡都模擬成了一台計算機。那麼以太坊本身相當於這台計算機的操作系統,而運行在上面的智能合約就相當於是應用程序。那麼因為以太坊本身是一條公鏈,所以理所當然的運行在上面的程序就有了 DApp 這個稱呼。
在此我們把鏈和 Dapp 分開講是很合理的。但是在一條聯盟鏈上,或者叫可定制化的鏈上還要套用這個模式就有點說不通了。
因為一條聯盟鏈是專門為一個特定的業務邏輯設計的,比如這條鏈可能承載了一個網絡遊戲,或者是某個領域的供應鏈。
我們不會把這個聯盟鏈拿出去和別人共享,所以也不會有輕易的允許其他人到一條聯盟鏈上部署一個 DApp。
這條特別定制化的聯盟鏈一定是圍繞你的業務邏輯去實現的。而 Forge 就是一個鏈開發框架,而且是專注於聯盟鏈的開發。你可以用 Forge 來開發出具備你所期望的特定功能的鏈,你不需要再為了達到這些功能而去額外的開發 DApp,這個鏈本身就是一個 Dapp 了。
所以,回到這個問題上來,從我上面說的角度看,Forge 是即用來開發鏈又用來開發 DApp 的。
至於開發 Dapp 和開發智能合約的不同點這個問題, 也正如我上面說的,我認為 DApp 就是智能合約。可能包圍在這個合約之外還有一些鏈下的部分。但是其核心應該就是鏈上的合約,所以我不認為他們有本質的不同。
紅軍大叔:OK, 所以一個定制化的鏈本身就是一個專有領域的應用,所以鏈和 dapp 是難分彼此的。
問:ArcBlock 的鏈的開發上關於治理有什麼方式?比如如果需要升級一般是什麼樣的流程以及背後的原理?
鏈的升級還是通過 Transaction 來實現的。這條 Transaction 會把一些信息寫進 Global State 之中,比如應該在哪個塊開始升級以及應該升級到哪個版本。然後每個節點在當鏈到達指定高度之後會停下來,然後把可執行文件升級到指定的版本,之後節點會重新啓動起來。
另外一種情況是部署新的 Transaction, 這種情況和以太坊相似,對應的 Transaction 代碼會寫進 Global State,之後鏈就可以執行新的 Transaction 了, 基本的原理就是這樣.
問:這個升級過程需要對歷史數據做重新處理嗎?
不需要。之前的數據就是用老版本的鏈產生出來的, 升級後的鏈在已有的數據上產生新的數據, 簡單的來講就是鏈上的歷史不可更改,這也是區塊鏈本身最大的特性之一.
問:使用 Forge 開發會給開發者帶來最大的價值是什麼?是學習成本低?還是鏈的功能強大?還是靈活性更好?或其他?
Forge 是一套鏈的開發框架,它極大的簡化了開發一條鏈所需要關心的事情。我們經常把它比作區塊鏈領域的 Ruby on Rails。所以簡單的來講 Forge 的好處就相當於用 Ruby on Rails 的好處。熟悉 Ruby 的朋友可以想想,只有 Ruby 沒有 Ruby on Rails 時整個的開發體驗是怎麼樣的。
原文鏈接: 跨鏈協議以及在 FORGE 上的實現