感謝Ellie 、 Luca 、 Florian和Ladislaus提供的所有反饋,以及參與審核的各團隊。反饋並不代表認可。
SCOPE 是一個基於推送的同步可組合性的極簡協議,它使以太坊和 Rollup 上的合約能夠相互調用並立即處理結果,就像它們位於同一條鏈上一樣。它支持單個原子執行範圍內的所有方向,即 L1↔L2 和 L2↔L2。極簡概念驗證可在此處找到。
動機
以太坊以 Rollup 為中心的路線圖在保證安全性的同時,也提供了一條實現擴容的途徑,但這是以碎片化為代價的。每個 Rollup 都作為一個獨立的執行環境運行,擁有各自的狀態、用戶和開發者生態系統。這種碎片化削弱了以太坊最初強大的核心屬性:可組合性。
可組合性使智能合約能夠像樂高積木一樣進行交互:無需許可、富有表現力且即時可用。隨著我們跨 Rollup 平臺的水平擴展,我們必須努力彌合這種碎片化。理想的組合性是同步可組合性 (SC),即一條鏈上的智能合約可以直接調用另一條鏈上的合約並立即使用結果,從而保留單一共享區塊空間的開發者體驗。
圍繞跨鏈目標的探索日益增多;然而,這些方法往往侷限於代幣轉移。可組合性是一個更廣泛的目標:它使合約能夠協調跨鏈邏輯,而不僅僅是流動性。Fabric 一直致力於引領基於 Rollup 的協議的發展,因為它們以獨特的方式實現了同步可組合性,不僅在 Rollup 之間,更重要的是在以太坊與 Rollup 之間。在此基礎上,SCOPE(以太坊同步可組合性協議)是一個旨在實現同步可組合性完整願景的框架,最終增強以太坊的網絡效應。
背景
同步可組合性是一種屬性,它允許一條鏈上的合約調用另一條鏈上的函數,並在同一執行上下文(例如,單個 Layer-1 插槽)內立即接收並執行結果。至關重要的是,跨鏈交互必須是原子性的:要麼雙方都成功,要麼都不成功。
兩個值得注意的設計已經證明了原子同步可組合性:
- CIRC (Coordinated Inter-Rollup Communication,協調式 Rollup 間通信)引入了一個基於郵箱的框架,用於在 Rollup 之間實現高效、可驗證的消息傳遞。CIRC 採用拉式設計:一條鏈上的合約可以檢查從另一條鏈發送的消息,並根據這些消息設定執行條件。然而,CIRC 不允許消息觸發執行,它需要兩筆交易:一筆在源鏈上寫入消息,另一筆在目標鏈上消費消息。
- Ultra 交易採用基於推送的模型,將所有跨鏈活動打包成一個包含 blob 和結算證明的 L1 捆綁交易。如果引入
XCALLOPTIONS
預編譯,合約可以無縫調用其他鏈上的合約。任何 L1 合約如果與ExtensionOracle
合約集成,並願意信任 Ultra 交易的證明系統,都可以將其執行推遲到更便宜的 rollup 執行環境。
範圍
它是什麼?
SCOPE 在兩種先前方法的基礎上,為同步、信任最小化的跨鏈函數調用提供了一個通用框架:
- 它從CIRC繼承了使用郵箱承諾的高效、可驗證的消息會計。
- 從超級交易 (Ultra Transactions)來看,它採用基於推送的執行模型,並利用賬戶抽象捆綁器將跨鏈執行統一到單個原子範圍內。
SCOPE 提供以下服務:
- Rollups 可以繼承一組標準化智能合約(例如
ScopedCallable
),以支持 SC 調用。 - Rollups 可以實現一種派生友好協議,以確保跨鏈執行、捆綁和驗證期間的兼容性。
ELI5
想象一下,以太坊是大陸,而每個 rollup 就像近海的島嶼。如今,島嶼之間的交流就像漂流瓶中的信息。信息會在漂流中漂流幾分鐘甚至幾小時才能到達,發送者得不到任何確認,更不用說可用的回覆了。SCOPE 讓所有島嶼都感覺連接到大陸,雙向對話可以即時進行。你可以發言,得到回覆,並立即採取行動,恢復以太坊在島嶼形成之前的無縫協作。
SCOPE 不僅使 rollups 能夠原子性地在彼此之間以及 Layer-1 之間發送消息,還允許一條鏈上的用戶調用其他鏈上的函數並立即接收和處理結果。這不僅提供了在單鏈上操作的體驗,同時保留了 rollups 的可擴展性優勢。
它是如何工作的?
SCOPE 的核心是形式化可驗證的基於推送的跨鏈交易所需的會計模型。每條參與鏈維護四個滾動哈希值和一個單字節映射,用於表示跨鏈請求和響應的集合序列:
-
requestsOutHash
:記錄本鏈發起的傳出跨鏈調用。 -
requestsInHash
:記錄要在此鏈上執行的傳入跨鏈調用。 -
responsesOutHash
:記錄在此鏈上執行的跨鏈調用的傳出響應。 -
responsesInHash
:記錄由該鏈發起的跨鏈調用的傳入響應。 -
responsesIn
:將該鏈發起的跨鏈調用的傳入響應記錄為原始字節。
scopedCallable
接口定義瞭如何更新這些值:
interface IScopedCallable {/// @notice Struct describing a cross-chain function call.struct ScopedRequest {address to;uint256 value;uint256 gasLimit;bytes data;}/// @notice Initiates a synchronous cross-chain call./// @dev Emits an event, updates a nonce, and updates the requestsOutHash./// Reads the result from the responsesIn array (pre-filled by the sequencer)./// @param targetChainId The ID of the chain the `ScopedRequest` will execute on./// @param from The address that initiated the cross-chain call on the source chain./// @param request Encoded function call for the destination chain./// @return response The result bytes returned from the responsesIn array.function scopedCall(uint256 targetChainId,address from,ScopedRequest calldata request) external payable returns (bytes memory response);/// @notice Executes a cross-chain call./// @dev Called by the sequencer. Updates requestsInHash, emits an event, and updates responsesOutHash./// @param sourceChainId The ID of the chain the `ScopedRequest` was initiated from./// @param from The sender address on the origin chain./// @param nonce A unique nonce for deduplication./// @param request Encoded call to execute locally.function handleScopedCall(uint256 sourceChainId,address from,uint256 nonce,ScopedRequest calldata request) external;/// @notice Pre-fills the responsesIn array with pre-simulated responses of cross-chain calls./// @dev Each response updates the responsesInHash for the corresponding chain ID./// @dev All arrays must have the same length (ie, chainIds[i] corresponds to reqHashes[i])./// @param chainIds The chain IDs from which the responses originate./// @param reqHashes The hashes of the original cross-chain requests./// @param responses The execution results from the destination chain.function fillResponsesIn(uint256[] calldata chainIds,bytes32[] calldata reqHashes,bytes[] calldata responses) external;/// @notice Returns the current rolling mailbox hashes for a given chain./// @param chainId The remote chain ID whose rolling hashes are tracked./// @return requestsOut Rolling hash of this chain's outbound requests./// @return requestsIn Rolling hash of this chain's inbound requests./// @return responsesOut Rolling hash of this chain's outbound responses./// @return responsesIn Rolling hash of this chain's inbound responses.function getRollingHashes(uint256 chainId)externalviewreturns (bytes32 requestsOut,bytes32 requestsIn,bytes32 responsesOut,bytes32 responsesIn);}
向開發人員公開的核心原語是scopedCall()
函數。該函數允許一個 rollup 上的合約同步調用另一個 rollup 上的函數並立即使用結果。調用scopedCall()
時,它會將一個唯一的請求標識符附加到源鏈的requestsOutHash
中,並從本地的responsesIn
映射中讀取預先填充的響應。從調用者的角度來看,此交互似乎是同步的,因為定序*scopedCall*
已經在目標鏈上模擬了調用並預先填充了responsesIn
。scopedCall 這個名稱反映了這樣一個事實:整個跨鏈交互(請求、執行和響應)都在單個原子執行範圍內解析,從而給人一種跨鏈本地可組合的感覺。
在目標鏈上,sequencer 執行handleScopedCall()
,它將相同的請求標識符混合到其requestsInHash
中,執行ScopedRequest
,並用結果更新responsesOutHash
。然後,此輸出被轉發回來並插入到源鏈的responsesIn
中。
在結算時,橋接器會驗證兩條鏈是否遵循了請求和響應的正確順序。具體來說,它會檢查:
- 源鏈的
requestsOutHash
與目標鏈的requestsInHash
匹配 - 源鏈的
responsesInHash
與目標鏈的responsesOutHash
匹配
如果任何調用被跳過、重新排序或篡改,這些滾動哈希將不會匹配,並且彙總將無法解決,從而確保原子性。
L2↔L2同步可組合性
SCOPE 在 L2↔L2 交互中運行得尤其乾淨。假設 Rollup A 上的合約需要調用 Rollup B 上的函數。共享的 sequencer 觀察到 A 的scopedCall()
,並立即在 B 上注入一個匹配的handleScopedCall()
在 B 上執行目標函數並獲取response
後,sequencer 會在 A 上預填充一個fillResponsesIn()
交易,這樣,當 A 的scopedCall()
實際運行時,它就可以同步讀取並處理response
,就像本地調用一樣。
從
H(0)
的初始哈希值開始,流程產生: B.requestsInHash = A.requestsOutHash = H(H(0) || H(ScopedRequest))
和A.responsesInHash = B.responsesOutHash = H(H(0) || H(response))
。如果定序器錯誤地注入請求,B 的requestsInHash
將與 A 的requestsOutHash
(源自scopedCall()
)不匹配。如果定序器錯誤地中繼響應,A 的responsesInHash
將與 B 的responsesOutHash
(源自handleScopedCall()
)不匹配。任何一種不匹配都會破壞結算時的相等性檢查,相當於篡改 EVM 執行,標準狀態轉換證明將拒絕這種情況。優勢
- 並行證明:由於兩條鏈都包含完整的有序交易序列,因此每個 rollup 都獨立地並行證明自身的狀態轉換。唯一的跨鏈依賴關係是最終結算時進行的滾動哈希等價性檢查。
- 無需強制共享排序器:雖然共享排序器可以優化延遲,但 SCOPE 可以與獨立排序的 rollup 兼容,只要它們共享一個結算層並相互信任彼此的排序。這使得它與 Optimism 超級鏈等生態系統直接兼容。
- 無需實時證明:與同步的 L1↔L2 不同,L2↔L2 調用不需要在同一 slot 內生成有效性證明。唯一的要求是參與的 rollup 最終需要一起結算,以便驗證滾動哈希等價性。
- 通過共享承諾攤銷成本:當兩個 rollup 承諾共享 L2↔L2 執行時,它們 1) 可以共享 blob 空間;2) 共享單個有效性證明。這減少了每個 rollup 的開銷,並允許較小的 rollup 在多個參與者之間攤銷 blob 和證明成本。
L1 ↔ L2 同步可組合性
L2↔L2 同步調用在假設共享排序和共享結算的情況下推理起來相對簡單,但引入 L1 會使事情變得複雜。為了使同步 L1↔L2 scopedCall()
可行,我們需要解決三個相互交織的挑戰:對 L1 區塊空間的控制、鏈間原子性以及代表用戶執行 L1 交易的能力。
SCOPE 的核心是一個超級建造者,其靈感源自超級交易模型。超級建造者負責模擬整個跨鏈調用(包括 L1 和 L2 兩部分),並確保所有操作在一個 L1 時隙內原子化完成。這需要與 L1 提議者和 L2 排序者緊密協作,理想情況下,超級建造者可以同時充當 L1 提議者和 L2 排序者的角色。
L1 區塊空間控制:第一個挑戰是隻有 L1 提議者才能決定區塊中包含哪些內容。如果 L2 排序器模擬了scopedCall()
並假設 L1 狀態,但 L1 提議者通過插入或重新排序交易使該狀態無效,則 L2 的模擬無效,結算將失敗。為了避免這種情況,超級構建者必須在生成 L2 證明時對 L1 內容具有確定性。這意味著超級構建者要麼是L1 提議者,要麼與其協調排序。
實時結算:其次,原子性要求兩條鏈同時結算,並在滾動哈希校驗失敗時回滾。但在 L1↔L2 的情況下,只有 rollup 狀態可以回滾。為了保持原子性,所有scopedCall()
活動(L1 函數調用、blob 提交和證明驗證)必須捆綁到單個 L1 交易中。如果任何滾動哈希校驗失敗,整個交易捆綁都會回滾,L1 和 rollup 狀態都會回滾。重要的是,由於 L2 必須消費L1 狀態,因此它必須在同一個 L1 slot 內模擬並結算,這引入了 L2↔L2 情況下不存在的實時證明要求。
委託執行:最後,rollup 通常允許其排序器代表用戶注入交易,例如在用戶存款後鑄造 ETH。L1 層原生不支持這種委託,因此為了支持 L2→L1 的scopedCall()
,我們依賴於EIP-7702委託執行。用戶簽署授權操作的有效載荷,打包器將該有效載荷包裝到 L1 層交易中。
例子
此示例展示了使用scopedCall()
進行跨鏈代幣交換,其中 L1 合約與 L2 合約交互以執行交換,並立即將生成的 ERC-20 代幣提取回 L1。sequencer 通過fillResponsesIn()
預填充交換結果,從而允許提取操作在 rollup 結算之前的同一筆交易中進行。與基於 Merkle 證明的標準提取不同,此處的withdraw()
調用無需許可,因為如果證明失敗或滾動哈希值不匹配,整個 bundle(包括提取操作)都受到原子回滾的保護,從而防止任何未經授權的資金流失。
模擬
在模擬和排序時,超級構建者必須確保每個 rollup 都遵循跨鏈調用的部分順序:
- 在源鏈上,所有
scopedCalls
必須按照明確定義的相對順序出現。 - 在目標鏈上,所有相應的
handleScopedCalls
必須按照匹配的相對順序出現。 - 所有其他內容(普通交易)都可以交錯,只要它們不會以改變滾動哈希的方式改變
ScopedRequest
有效負載或計算的response
。
具體來說:
- 在對
scopedCall(req)
進行排序之後,超級構建器不得包含改變req
源鏈交易,例如,更改to
、value
、gasLimit
或data
。 - 在模擬
handleScopedCall(req)
並捕獲response
之後,超級構建器不得包含會改變response
目標鏈交易。
為了模擬,超級建築師將:
- 攔截源鏈的
scopedCall()
- 本地更新源鏈的
requestsOutHash
- 將
handleScopedCall()
插入目標鏈並執行 - 將
response
傳回源鏈的執行
通過重複此過程,超級構建器將確定調用fillResponsesIn()
所需的所有response
值。
附錄
在這裡,我們分析了流行的彙總堆棧,以確定需要進行哪些更改來支持 SCOPE,假設目標是同步 L1↔L2 可組合性。
核心範圍要求 (L1↔L2)
- 共享排序器:公共排序器必須協調所有參與彙總的跨鏈交易流。
- L1 客戶端修改:L1→L2
scopedCall()
在模擬期間和執行期間需要不同的行為。 - L2 客戶端修改:除了核心狀態轉換功能之外,rollups 必須證明所有跨鏈請求和響應的滾動哈希等價性,以確保參與鏈之間的可驗證一致性。
- L1 橋修改:橋必須跟蹤滾動哈希(
requestsInHash
、requestsOutHash
、responsesInHash
、responsesOutHash
)以通過結算期間的等價性檢查來強制執行原子性。 - 實時證明:Rollup 必須在同一個 L1 slot 內生成並提交有效性證明(即可爭議的 rollup),才能參與原子
scopedCall()
執行。 - L1 提議者協調:要麼序列器必須是 L1 提議者,要麼必須獲得狀態鎖以保證
scopedCall()
的 L1 部分完全按照模擬執行。 - 返回值支持:提前模擬跨鏈調用,並跟蹤 Layer1 和 Layer2 上的
responsesOutHash
、responsesInHash
和resultsIn
映射。這使得調用合約能夠像本地調用一樣同步使用返回值。
案例研究:Ethrex
Ethrex 堆棧支持通過特權交易進行基於推送的 L1→L2 跨鏈調用(無返回值)以及基於拉取的 L2→L1 消息傳遞。
L1→L2 今天
調用
CommonBridge.sendToL2()
會發送任意的SendValues
有效載荷,其中包含目標 L2 函數調用的編碼。有效載荷的哈希值會附加到pendingTxHashes
數組中,並觸發PrivilegedTxSent
事件。定序器監聽 PrivilegedTxSent 事件,並將
PrivilegedL2Transaction
注入到 L2 內存池中。該交易執行SendValues
中編碼的函數調用。在結算期間,證明系統收集所有
PrivilegedL2Transactions
,計算它們的滾動哈希,並通過OnChainProposer.verifyBatch()
驗證其是否與鏈上計算的pendingTxHashes
的滾動哈希相匹配。此機制相當於在SCOPE模型下驗證L1的
requestsOutHash
與L2的requestsInHash
是否匹配。
L2→L1 今天
- 調用
L2ToL1Messenger.sendMessageToL1(bytes32 data)
會發出一個L1Message
事件,其中data
是正在發送的消息的哈希值。 - 序列器根據所有這些
data
值構建一個 Merkle 樹,並在 L2 結算期間提交根。 - 為了完成消息,用戶需要提供原始消息和 Merkle 證明,以驗證該消息是由 L2 提交的。
目前,L1 橋僅支持代幣提現。CommonBridge CommonBridge
發起的通用 L1 函數調用尚不支持,但可以很容易地添加。
SCOPE 兼容性要求:
- 用
scopedCall()
sendToL2()
,引入responsesOutHash
、responsesInHash
和resultsIn
映射,以允許調用合約立即使用跨鏈函數調用的返回值。 - 序列器不應等待 L1 上發出的
PrivilegedTxSent
事件,而應在 L1 區塊確認之前預先注入PrivilegedL2Transactions
,從而實現跨鏈同步執行。 - 將基於拉取的 L2→L1 消息傳遞替換為基於推送的
scopedCall()
該函數由 L2 發起,並通過 L1 上的handleScopedCall()
進行處理。這使得任意的 L1 合約調用都可以在同一個 L1 slot 內執行。
案例研究:OP Stack
OP Stack 支持雙向、基於推送的跨鏈調用,無返回值。
SCOPE 還可以應用於 SuperChain,以實現 L2↔L2 同步可組合性,而無需共享排序器或實時證明,只要參與的 rollup 共享結算層並相互信任彼此的排序。
L1→L2 今天
- 調用 L1
CrossDomainMessenger.sendMessage()
允許將任意不透明字節作為調用數據發送到目標 L2 合約。 -
OptimismPortal.depositTransaction()
將任何 ETH 託管在鎖箱中併發出TransactionDeposited
事件。 - 序列器監聽
TransactionDeposited
事件並在 L2 上注入一個調用CrossDomainMessenger.relayMessage()
交易,該交易使用先前發送的數據執行 L2 函數調用。
派生管道確保所有TransactionDeposited
事件都與正在中繼的消息相對應。否則,排序器就犯了欺詐行為。
L2→L1 今天
- 調用 L2
CrossDomainMessenger.sendMessage()
允許將任意不透明字節作為調用數據發送到目標 L1 合約。 -
L2ToL1MessagePasser.initiateWithdrawal()
在sentMessages
映射中記錄消息哈希併發出MessagePassed
事件。 - 序列器提出一個 L2
output
,其中包含一個提交到sentMessages
映射狀態的output_root
。 - 用戶通過使用 merkle 證明調用 L1
OptimismPortal.proveWithdrawalTransaction()
來證明消息包含。 - 在欺詐證明窗口過去後,調用 L1
OptimismPortal.finalizeWithdrawalTransaction()
執行 L1 函數調用。
如果使用有效性證明,這種基於拉取的方法可以簡化為兩個交易:一個在 L2 上發起消息,另一個在 L1 上證明並完成執行。
SCOPE 兼容性要求:
- 支持能夠實時證明的有效性證明,以允許彙總在單個 L1 插槽內結算。
- 通過允許
op-node
設置 [SequencerConfDepth](https://github.com/ethereum-optimism/optimism/blob/f70219a759e1da31e864c0ccdc2c757689aba3ec/op-node/rollup/driver/config.go#L12) = 0
並在 L1 上發出TransactionDeposited
事件之前啟用CrossDomainMessenger.relayMessage()
來支持同步 L1→L2scopedCall()
。 - 通過用單個 L2 發起的
scopedCall()
替換當前的多步驟 L2→L1 消息傳遞過程,實現同步 L2→L1 調用。
案例研究:Taiko
Taiko 堆棧支持雙向、基於拉取的跨鏈調用,且無返回值。
L1→L2
- 調用
Bridge.sendMessage()
可以發送任意Message
,該消息編碼為對目標 L2 合約的函數調用。該Message
會被哈希處理(創建一個信號),並存儲在 L1SignalService
合約中。 - 用戶使用標準
eth_getProof
RPC 生成存儲證明,證明他們的信號存在於 L1 上。 - 每個 Taiko 區塊都以一個錨交易開始,該交易注入當前的 Layer-1 世界狀態根和一系列新信號。這些信號隨後被寫入 Layer-2
SignalService
合約。 - 通過使用
Message
和 L1 存儲證明調用 L2Bridge.processMessage()
,用戶證明該消息已包含在 L1 中。這是通過根據 L1 世界狀態根和 L2SignalService
合約驗證信號來實現的。 - 然後,
_invokeMessageCall()
在目標 L2 合約上執行Message
中編碼的函數調用。 - 在結算期間,系統會驗證錨交易中報告的信號是否與寫入 L1
SignalService
的信號相匹配。
檢查等效信號在功能上等效於在 SCOPE 中比較 L1 requestsOutHash
和 L2 requestsInHash
。
L2→L1
- 用戶調用
L2 Bridge.sendMessage()
,將消息哈希(信號)存儲在 L2SignalService
合約中。 - 一旦 rollup 穩定下來並且其世界狀態最終確定,就可以使用原始
Message
和存儲證明來調用 L1Bridge.processMessage()
以表明該信號存在於 L2SignalService
合約中。 - 然後,
_invokeMessageCall()
在 L1 合約上執行編碼的函數調用。
SCOPE 兼容性要求:
- 將當前基於信號的流程替換為基於推送的模型,其中
Bridge.sendMessage()
等同於scopedCall()
。排序器不再記錄單個信號並將其轉發到目標鏈,而是直接轉發完整的Message
。源鏈將消息附加到滾動的requestsOutHash
中,而目標鏈在handleScopedCall()
期間計算匹配的requestsInHash
。這消除了對 Merkle 證明的需求,因為任何被篡改的Message
都會導致滾動哈希校驗失敗,從而阻止結算。 -
Bridge.processMessage()
等同於handleScopedCall()
,應該立即調用,執行Message
並更新requestsInHash
和responsesOutHash
。此函數可以是無需許可的,因為理性的提議者必須確保消息以正確的順序處理,以便 rollup 得以結算(即,requestsInHash
與requestsOutHash
匹配)。
案例研究:Linea
Linea 堆棧支持雙向跨鏈調用,無返回值,根據是否使用Postman 服務,使用基於推送或拉取的傳遞。
L1→L2 今天
- 調用
L1MessageService.sendMessage()
會將不透明字節(calldata)發送到目標 L2 合約。消息哈希會被合併到滾動哈希中,並在 L1 上觸發MessageSent
事件。 - 協調器服務會監控這些事件,等待兩個 L1 epoch 的最終結果,然後在 L2 上調用
L2MessageManager.anchorL1L2MessageHashes()
。這會將消息哈希寫入 L2,併發出RollingHashUpdated
事件,確保兩條鏈上都能重新計算出相同的滾動哈希。 - 最後,
L2MessageServiceV1.claimMessage()
執行 L2 函數調用,設置一個標誌以防止重放,併發出MessageClaimed
。這可以由用戶手動調用,也可以由Postman 服務自動調用(如果用戶預付了 L1 費用)。 - 在結算時,將 L2 發出的最終
RollingHashUpdated
與 L1 上的滾動哈希進行核對,以驗證跨鏈的消息一致性。
此機制相當於在SCOPE模型下驗證L1的requestsOutHash
與L2的requestsInHash
是否匹配。
L2→L1 今天
- 調用
L2MessageServiceV1.sendMessage()
會發出一個包含消息哈希值的MessageSent
事件,並將任意不透明字節編碼為目標 L1 合約的調用數據。 - 在結算期間,證明者構建所有發出的消息哈希值的 Merkle 樹,並將 Merkle 根提交給 L1。
- 為了完成消息,用戶(或Postman 服務)使用原始消息及其 Merkle 證明調用
L1MessageService.claimMessageWithProof()
,從而執行 L1 函數調用。
SCOPE 兼容性要求:
- 移除
anchorL1L2MessageHashes()
步驟,改為在claimMessage()
期間逐步更新 L2 滾動哈希,這等效於handleScopedCall()
。sequencer 必須強制以正確的順序聲明消息,以保證 L1 和 L2 之間的滾動哈希一致性。 - 將
claimMessageWithProof()
中使用的 Merkle 證明驗證替換為基於推送的模型,該模型使用由 L2 發起的scopedCall()
進行驗證。L2 跟蹤一個requestsOutHash
,而 L1 在調用handleScopedCall()
時計算匹配的requestsInHash
。 -
LineaRollup.submitBlobs()
和LineaService.finalizeBlocks()
必須捆綁在一起並以原子方式執行。這確保了 L2 實時結算。
基於預先確認和範圍
SCOPE 並不嚴格要求預先確認。我們可以設想一個基於“完全無政府狀態”的 Rollup,任何人都可以充當超級構建者,提出包含跨鏈調用的有效 bundle,而無需提供預先確認。這種模式有效,但預先確認自然可以通過讓用戶更早地確定交易結果來提升用戶體驗。
執行預置通常被認為是黃金標準,但 SCOPE 的操作順序使其變得複雜:由於需要填充responsesIn
映射, scopedCall()
的後狀態在模擬和執行之間有所不同。為了提高效率,超級構建器可能會在所有模擬運行之後插入一個fillResponsesIn()
調用,而不是在每個scopedCall()
之前。在rollups上,可以通過在每個scopedCall()
之前立即放置fillResponsesIn()
來解決這個問題,這是 gas 可行的。在L1上,另一種方法是讓超級構建器預先確認滾動前和滾動後哈希及其相應的請求和響應數據。這種方法讓用戶能夠可靠地瞭解跨鏈請求的結果,並且在發生故障時更容易證明,並且更容易從超級構建器發出(因為它不需要中間狀態根)。
CUSTARD等先前的研究探索了在超級構建者槽位之前進行“超級交易”預置的可行性。當ScopedRequests
依賴於無法使用 CUSTARD 描述的技術鎖定的任意狀態數據時(例如, ScopedRequest
可能包含在scopedCall()
) 執行時確定的價格數據,而該價格數據很可能與其模擬和預確認的槽位不同),這種早期的scopedCall()
發出是否安全,需要更多研究來確定。
SCOPE 與 AggLayer
SCOPE 和 AggLayer 都旨在實現無需信任的跨鏈消息傳遞,但它們從不同的角度解決問題。AggLayer 是一個功能完善的互操作性協議,擁有自己的結算規則;而 SCOPE,儘管名稱中帶有“協議”一詞,但主要是一個會計框架,可以疊加在 AggLayer、超級鏈或彈性網絡等現有系統上。
兩個系統都秉承相同的悲觀證明理念:鏈獨立地證明自身的狀態轉換,並且只有在通過密碼等效性檢查後才能結算。在 AggLayer 中,這體現在“本地出口樹”中,其根節點的作用類似於 SCOPE 的requestsOutHash
。區別在於,AggLayer 和類似協議本身並不支持跨鏈調用的同步返回值。消息發出後,在同一次執行中不會有任何返回。
SCOPE 擴展了此模型,它還通過跟蹤入站響應(例如通過responsesInHash
或假設的“本地入口樹”)來擴展。這使得區塊鏈能夠同步使用返回數據,就像它們屬於一個統一的環境一樣。其結果是從簡單的消息傳遞轉變為真正的共享執行範圍,同時保留了相同的結算時安全保障。