JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

不只是代幣:通用的跨鏈訊息傳遞

搬動一枚代幣,不過是一則訊息。看懂任意的跨鏈訊息如何讓一條鏈上的合約去呼叫另一條鏈上的合約——以及為何整場較量的核心,全在於「由誰來驗證這則訊息為真」。

從搬動硬幣,到呼叫合約

上一篇裡,跨鏈橋在一條鏈上鎖定代幣、在另一條鏈上鑄出一份包裝副本。很有用——但橋只搬動價值。更深層的需求是「反應」:Polygon 上的一份合約因為你在以太坊質押,就替你鑄出一枚徽章;一個 DAO 在 A 鏈上的投票,自動在 B 鏈上執行一筆金庫轉帳;一個跨鏈 DEX 把你的兌換導向流動性最深的那條鏈。這些都不是代幣轉移,而是一支程式告訴另一條鏈上的另一支程式去做某件事

關鍵的解鎖在這裡:鎖定與鑄造其實只是一個更通用基本元件的特例。「為 Alice 鎖定 1 ETH」是一則訊息;「鑄 1 wETH 給 Alice」是它所攜帶的指令。只要我們能可靠地把一則任意訊息從 A 鏈傳到 B 鏈,那麼代幣橋就只是建在其上的一個應用而已。這就是通用跨鏈訊息傳遞;而幾乎每一套現代互通系統——Cosmos IBC、LayerZero、Chainlink CCIP、Hyperlane——本質上都是一條訊息匯流排,代幣橋只是疊在上頭的第一個應用。

兩件差事:傳輸與驗證

一則訊息的旅程,有兩件可以乾淨切開的差事;把它們混為一談,正是無數糟糕設計的源頭。傳輸(屬於活性的差事):把位元組從 A 實際運到 B,並在目的鏈上提交。這由中繼者完成——一支離鏈程式,盯著 A、把訊息打包,再付 gas 把它送上 B。驗證(屬於安全性的差事):向目的鏈證明這則訊息確實源自 A,未被偽造或竄改。

由此導出一個美妙的結果:中繼者可以是無需許可、且完全不被信任的。只要驗證夠紮實,說謊的中繼者就無法偽造訊息——最糟也只是拒絕投遞,而任何人都能補上。於是跨鏈訊息傳遞的全部安全性,都坍縮成一個問題:目的鏈憑什麼判定一則訊息為真? 答案有四大流派。

  1. 原生驗證——目的鏈在鏈上運行一個來源鏈的輕用戶端,直接檢驗來源鏈自身的共識。信任 = 來源鏈本身的安全性。這是黃金標準(Cosmos IBC、zk 輕用戶端橋)。
  2. 外部驗證——由一組獨立的驗證者、預言機或 MPC 委員會出面背書「這則訊息有效」。信任 = 那組外部集合,而非來源鏈。這是最靈活、也最脆弱的模型——最大型的駭客事件都出在這裡(LayerZero 的驗證者堆疊、Wormhole 的守護者、Axelar 的驗證者)。
  3. 樂觀驗證——先假定訊息有效,但開一段挑戰窗口,期間任何誠實的監看者都能提交詐欺證明。信任 = 一名誠實監看者+那段延遲(Nomad、Hyperlane 的樂觀 ISM)。
  4. 本地驗證——只有交易雙方彼此互查,就像雜湊時間鎖原子交換。完全免信任,卻僅限於兩個自願方之間的兌換——無法把任意訊息傳給任意合約。

Connext 的 Arjun Bhuptani 把這稱為互通性三難困境:一套訊息系統最多只能擁有免信任可擴充(廉價接上任何新鏈)與通用(攜帶任意資料)三者中的兩項。原生驗證免信任又通用,卻難以擴充(每對鏈都得有輕用戶端,且共識要對輕用戶端友善);外部驗證可擴充又通用,卻不免信任;原子交換免信任又可擴充,卻不通用。你遇到的每一種設計,都落在這個三角形上的某一點。

# SOURCE CHAIN — an app dispatches a message (transport boundary)
Mailbox.dispatch(
    destChainId = 137,                       # Polygon
    recipient   = 0xRECEIVER,                 # contract address on dest
    payload     = encode("mintBadge", user, level=1)
)
# -> stores commitment = hash(message) in source state, emits an event

# OFF-CHAIN — a permissionless relayer sees the event, builds a proof
# that the commitment is in the source chain's state, submits both to B.
# A relayer is UNTRUSTED: it can stall delivery, but cannot forge.

# DESTINATION CHAIN — the inbox verifies, THEN delivers
Mailbox.process(message, proof):
    require(verify(message, proof))          # <-- the one security-critical step
    Receiver(message.recipient)
        .handle(message.srcChainId, message.sender, message.payload)
跨鏈訊息傳遞的通用形狀:來源端 dispatch + 承諾值,中間由不受信任的中繼運送,目的端則先驗證再執行。一切都取決於 `verify()` 究竟檢查了什麼。

原生驗證:深入 Cosmos IBC

跨鏈通訊協定(IBC)是原生驗證設計的典範。每條鏈在鏈上各自保有對手鏈的一個輕用戶端。對 Tendermint 鏈而言,輕用戶端很便宜:一個標頭一經簽署即為最終(Tendermint 提供即時最終性,不必機率性等待),而驗證它只需檢查超過 2/3 的驗證者投票權重簽了名。輕用戶端會隨驗證者集合更替而追蹤之,因此 B 鏈隨時能判斷 A 鏈遞來的標頭是否為真。

在那個輕用戶端之上,架著封包機制。兩條鏈先進行一次性的握手(用戶端 → 連線 → 通道),接著交換封包。關鍵洞見在於:封包從不會「因為中繼者說它是真的,就被信任」——它是用 Merkle 證明對著來源鏈的狀態被證實的,而那個狀態根就住在輕用戶端早已驗證過的標頭裡。以下是單一封包的生命週期:

  1. 送出。 A 鏈上的應用呼叫 `sendPacket`。A 的狀態機在一個眾所周知的儲存鍵上寫入一個承諾值——`hash(payload, timeout…)`——並發出事件。此刻還沒有任何東西信任中繼者。
  2. 證明。 一個中繼者讀出該承諾值,取得一份 Merkle 證明(證實它位於 A 在區塊高度 H 的狀態中),外加 A 在高度 H 的已簽署標頭。
  3. 驗證+接收。 中繼者把兩者一併提交給 B 鏈的 `recvPacket`。B 對 A 的輕用戶端先檢查該標頭確實由 A 的驗證者簽署,再對著該標頭的狀態根檢查 Merkle 證明。唯有兩者皆通過,B 的應用才執行酬載,並寫下一筆確認(ack)
  4. 確認/逾時。 中繼者把 ack 帶回 A,讓來源端應用得知結果。若在封包的 `timeoutHeight`/`timeoutTimestamp` 前無人投遞,A 可證明逾時並安全退款——資金絕不會卡在等待某個中繼者上。
Packet {
  sequence:         42,                 // ordering within the channel
  sourcePort:       "transfer",         // ICS-20 token-transfer app
  sourceChannel:    "channel-0",
  destPort:         "transfer",
  destChannel:      "channel-141",
  data:             <opaque app bytes>, // the actual payload
  timeoutHeight:    { revision: 4, height: 9_000_000 },
  timeoutTimestamp: 1719360000000000000
}

// The SENDING chain stores, in its own verifiable state:
//   key   = commitments/ports/transfer/channels/channel-0/sequences/42
//   value = hash(timeout, data)
// The receiving chain's light client + a Merkle proof against this
// key are what make the packet trustworthy -- not the relayer.
一個 IBC 封包。中繼者只是信差;真實性來自一份由來源鏈鏈上輕用戶端驗證的 Merkle 證明。

回報是:信任縮小到兩條鏈各自的共識——只要 A 的驗證者誠實,B 就無法被騙,中間也沒有第三方。代價則是觸及範圍。原生驗證需要一個在鏈上運行成本低廉的輕用戶端,這對快速最終性的 Tendermint 鏈很容易,對工作量證明或最終性緩慢的鏈卻很難——過去你無法以可負擔的成本把一個以太坊輕用戶端塞進另一條鏈。還有一個活性的註腳:輕用戶端必須在信任期間內被更新(與質押解綁窗口掛鉤——一種弱主觀性假設);若閒置太久它就會過期,需要治理出手復活。

外部驗證:LayerZero、CCIP、Hyperlane

多數鏈對之間養不起互設的輕用戶端,於是主流的正式環境訊息層走務實路線:引入一組外部驗證者來為訊息背書。你能即時接上任何鏈——代價是改去信任那組集合,而非來源鏈。你會遇到的幾個名字:

LayerZero 開創了全鏈訊息模型。在 v1 中,每則訊息都需要兩個獨立方——一個 Oracle 遞送區塊標頭、一個 Relayer 遞送交易證明——而它僅在兩者從不串通時才安全。批評者指出,應用(或其擁有者)往往同時掌控這兩個端點,因此實務上你信任的是應用部署者。v2 把這套一般化成可組態的 DVN(去中心化驗證者網路)堆疊:一則訊息只要你所選的 `X-of-Y-of-N` 組 DVN 為它背書即被接受,另由 Executor 負責投遞。它仍是外部驗證——你的安全性取決於你的 DVN 組態,而非以太坊的共識。

Chainlink CCIP 疊上縱深防禦:一個去中心化預言機網路負責提交與執行訊息,另有一個獨立分立的 風險管理網路盯著同一批訊息,一旦察覺異常即可暫停該通道——兩個必須一致同意、由不同程式碼與節點營運者運行的網路。Hyperlane 則把安全性本身模組化:每則訊息都由接收端應用自選的一個跨鏈安全模組(ISM)驗證——可以是驗證者多簽、樂觀模組、數者的聚合,或一個 zk 模組。無需許可的部署意味著任何人都能接上新鏈而不必經核心團隊核可,把信任的抉擇推到每個應用身上。

當訊息傳遞崩壞:驗證的陷阱

通用訊息傳遞比代幣橋更危險,恰恰因為它通用:一則偽造的訊息不只是鑄出一枚假幣,它能呼叫接收端的任何函式——包括特權函式。史上最大的加密駭客事件 Poly Network(2021 年 8 月,約 6.11 億美元)正是如此。Poly Network 是一套跨鏈訊息傳遞協定;它的 `EthCrossChainManager` 會執行它所中繼的任何跨鏈呼叫。攻擊者精心構造了一則訊息,其目標是 Poly 自家的管理合約,酬載則更改了那組獲授權簽署跨鏈交易的「keeper(守護者)」公鑰。把 keeper 換成自己的金鑰後,他們便能簽署任意提領,把以太坊、BSC 與 Polygon 一併掏空。(眾所周知,絕大部分後來由這名「白帽」攻擊者在接下來幾天歸還。)

一年後,Nomad(2022 年 8 月,約 1.9 億美元)展示了同一陷阱的樂觀驗證版本:一次例行升級把受信任的訊息根初始化為 `0x00`,此後每一則訊息的證明都通過驗證。攻擊變成複製貼上——數百個地址重放同一則掏空資金的訊息、只把地址換成自己的。兩種不同架構,一個教訓:若接收端沒有正確認證訊息的來源,一旦攻擊者能讓一則偽造訊息通關,這份合約就歸他了。

// VULNERABLE -- anyone can call handle() and forge a cross-chain message
contract Vault {
    IERC20 token;
    function handle(uint32 srcChain, bytes calldata payload) external {
        (address to, uint256 amount) = abi.decode(payload, (address, uint256));
        token.transfer(to, amount);            // who called this? unchecked!
    }
}

// FIXED -- three checks every cross-chain receiver MUST make
contract Vault {
    IERC20  token;
    address immutable mailbox;       // the local verifier/inbox
    uint32  immutable SRC;           // the one source chain we accept
    bytes32 immutable trustedSender; // the remote app (address as bytes32)

    modifier onlyMailbox() { require(msg.sender == mailbox, "not inbox"); _; }

    // 1) only the verified inbox may deliver (it already ran verify())
    function handle(uint32 srcChain, bytes32 sender, bytes calldata payload)
        external onlyMailbox
    {
        require(srcChain == SRC,            "bad source chain"); // 2) expected chain
        require(sender == trustedSender,    "untrusted sender"); // 3) known peer
        (address to, uint256 amount) = abi.decode(payload, (address, uint256));
        token.transfer(to, amount);
    }
}
接收端不可妥協的檢查清單:(1) 只有進行驗證的收件匣能投遞、(2) 來源鏈 id 是你預期的那條、(3) 遠端發送者是你已知的對手方。Poly Network 的攻擊,正藏在這些檢查所封堵的缺口裡。

請注意,這三項檢查都關乎認證,而且分處兩個層次。那些造成數十億美元損失的攻擊,幾乎從不攻破密碼學——它們攻破的是組態:一個其實沒在驗證的收件匣、一個祕密上只是單一多簽的驗證者集合、一次把根歸零的升級、一個信任了 `srcChain` 卻不檢查發送者的接收端。當你稽核一個跨鏈應用時,你稽核的就是這道接縫。

如何選擇堆疊——以及 zk 前沿

那麼一個應用該用哪種模型?天下沒有白吃的午餐,只有誠實的取捨。原生IBC 式)給你來源鏈自身的安全性、不引入額外的受信任方——但僅限於共識對輕用戶端友善、且具快速最終性的鏈之間,而且需要全節點與在線的中繼者。外部(LayerZero/CCIP/Hyperlane)能在數天內接上任何東西、工具豐富——但你繼承了一組驗證者,其安全預算通常遠低於它所守護的價值,這正是這一類稱霸駭客排行榜的原因。樂觀便宜、只需一名誠實監看者,卻要付出數小時到一天的挑戰延遲。讓驗證者的經濟安全性,匹配所守護的價值:一個 5-of-9 多簽不該守著十億美元。

前沿正在彌合那道鴻溝。zk 輕用戶端(如 Polyhedra 的 zkBridge、Succinct 與 zkIBC 等輕用戶端橋)用一個 SNARK 證明來源鏈的共識:與其在另一條鏈裡運行昂貴的以太坊輕用戶端,你只需驗證一個極小的簡潔證明——證明超過 2/3 的以太坊驗證者簽署了這個標頭。這就把原生等級、免信任的安全性,帶給了過去養不起完整輕用戶端的鏈對——把擴容那一階的證明技術,直接摺進互通性裡。它仍屬早期、證明成本也確實存在,但這是通往「既免信任通用」訊息傳遞最可信的一條路。

搞懂訊息傳遞後,本階的最後一篇會把鏡頭再拉遠一級:與其把訊息傳遞硬栓到各自獨立的鏈上,不如讓整個生態系一開始就圍繞它而設計——Cosmos 的各 zone 透過一個樞紐講 IBC、Polkadot 的各平行鏈共處於一條共享安全的中繼鏈之下,以及 rollup 的「超級鏈」共用一個結算與訊息層。問題於是從兩條鏈如何對話,轉向你該如何架構一百條從第一天起就為對話而生的鏈