Cảm ơn Ellie , Luca , Florian và Ladislaus vì tất cả phản hồi cũng như các nhóm khác đã đánh giá. Phản hồi không nhất thiết là sự xác nhận.
SCOPE là một giao thức tối thiểu cho khả năng kết hợp đồng bộ dựa trên cơ chế đẩy, cho phép các hợp đồng trên Ethereum và các hợp đồng rollup gọi lẫn nhau và xử lý kết quả ngay lập tức như thể chúng nằm trên một chuỗi duy nhất. Nó hỗ trợ tất cả các hướng, L1↔L2 và L2↔L2, trong một phạm vi thực thi nguyên tử duy nhất. Có thể tìm thấy bằng chứng khái niệm tối thiểu tại đây .
Động lực
Lộ trình tập trung vào rollup của Ethereum mang đến một con đường mở rộng quy mô mà vẫn đảm bảo tính bảo mật, nhưng phải trả giá bằng sự phân mảnh. Mỗi rollup hoạt động như một môi trường thực thi riêng biệt với trạng thái, người dùng và hệ sinh thái nhà phát triển riêng. Sự phân mảnh này làm suy yếu một đặc tính cốt lõi đã tạo nên sức mạnh của Ethereum ngay từ đầu: khả năng kết hợp.
Khả năng kết hợp cho phép các hợp đồng thông minh tương tác như các khối Lego: không cần cấp phép, biểu cảm và tức thời. Khi chúng ta mở rộng theo chiều ngang trên các khối rollup, chúng ta phải nỗ lực khắc phục sự phân mảnh này. Lý tưởng nhất là khả năng kết hợp đồng bộ (SC), trong đó một hợp đồng thông minh trên một chuỗi có thể gọi trực tiếp một hợp đồng trên một chuỗi khác và ngay lập tức sử dụng kết quả, bảo toàn trải nghiệm của nhà phát triển trên một không gian khối được chia sẻ duy nhất.
Đang có những nỗ lực ngày càng tăng xung quanh các ý định liên chuỗi; tuy nhiên, những cách tiếp cận này có xu hướng tập trung hẹp vào việc chuyển giao token. Khả năng kết hợp là một mục tiêu rộng hơn: nó cho phép các hợp đồng phối hợp logic trên toàn chuỗi, chứ không chỉ thanh khoản. Fabric tập trung vào việc dẫn dắt sự phát triển của các rollup dựa trên nền tảng vì chúng cho phép khả năng kết hợp đồng bộ một cách độc đáo, không chỉ giữa các rollup, mà quan trọng hơn là giữa Ethereum và các rollup. Dựa trên nền tảng này, SCOPE (Giao thức Kết hợp Đồng bộ cho Ethereum) là một khuôn khổ được thiết kế để hiện thực hóa tầm nhìn đầy đủ về khả năng kết hợp đồng bộ, cuối cùng là củng cố hiệu ứng mạng lưới của Ethereum.
Lý lịch
Khả năng kết hợp đồng bộ là đặc tính cho phép một hợp đồng trên một chuỗi gọi một hàm trên chuỗi khác và ngay lập tức nhận và hành động dựa trên kết quả trong cùng một bối cảnh thực thi (ví dụ: một khe L1 duy nhất) . Quan trọng là, các tương tác xuyên chuỗi phải mang tính nguyên tử: cả hai bên đều thành công hoặc không bên nào thành công.
Hai thiết kế đáng chú ý đã chứng minh khả năng kết hợp đồng bộ nguyên tử:
- CIRC (Giao tiếp Liên kết Phối hợp) đã giới thiệu một khuôn khổ dựa trên hộp thư cho việc nhắn tin hiệu quả và có thể xác minh giữa các lần rollup. CIRC là một thiết kế dựa trên cơ chế kéo : các hợp đồng trên một chuỗi có thể kiểm tra các tin nhắn được gửi từ một chuỗi khác và điều kiện thực thi của chúng dựa trên các tin nhắn đó. Tuy nhiên, CIRC không cho phép tin nhắn kích hoạt thực thi, mà yêu cầu hai giao dịch: một giao dịch để ghi tin nhắn trên chuỗi nguồn và một giao dịch khác để sử dụng tin nhắn đó trên chuỗi đích.
- Giao dịch Ultra áp dụng mô hình đẩy , đóng gói tất cả hoạt động xuyên chuỗi thành một giao dịch L1 được đóng gói duy nhất, bao gồm các blob và bằng chứng thanh toán. Nếu biên dịch trước
XCALLOPTIONS
được triển khai, các hợp đồng có thể gọi hợp đồng trên các chuỗi khác một cách liền mạch. Bất kỳ hợp đồng L1 nào cũng có thể hoãn việc thực thi sang một môi trường thực thi roll-up rẻ hơn nếu chúng tích hợp với hợp đồngExtensionOracle
và sẵn sàng tin tưởng hệ thống bằng chứng của giao dịch Ultra.
PHẠM VI
Đó là gì?
SCOPE được xây dựng dựa trên cả hai phương pháp trước đó để cung cấp một khuôn khổ mục đích chung cho các cuộc gọi hàm chuỗi chéo đồng bộ, giảm thiểu độ tin cậy:
- Từ CIRC , nó kế thừa khả năng kiểm soát tin nhắn hiệu quả và có thể xác minh bằng cách sử dụng các cam kết hộp thư.
- Từ Ultra Transactions , nó áp dụng mô hình thực thi dựa trên đẩy và tận dụng các gói trừu tượng tài khoản để hợp nhất thực thi chuỗi chéo thành một phạm vi nguyên tử duy nhất.
SCOPE cung cấp cả:
- Một tập hợp các hợp đồng thông minh được chuẩn hóa (ví dụ:
ScopedCallable
) mà các bản tổng hợp có thể kế thừa để hỗ trợ các lệnh gọi SC. - Một giao thức thân thiện với việc phái sinh mà các rollup có thể triển khai để đảm bảo khả năng tương thích trong quá trình thực hiện, đóng gói và xác minh chuỗi chéo.
ELI5
Hãy tưởng tượng Ethereum như một vùng đất liền và mỗi điểm kết nối như một hòn đảo ngoài khơi. Ngày nay, việc giao tiếp giữa các hòn đảo giống như gửi tin nhắn trong chai. Nó trôi dạt hàng phút hoặc hàng giờ trước khi đến nơi, và người gửi không nhận được bất kỳ phản hồi nào, chứ đừng nói đến phản hồi hữu ích. SCOPE tạo cảm giác như tất cả các hòn đảo đều được kết nối với đất liền, nơi các cuộc trò chuyện đầy đủ có thể diễn ra tức thời theo cả hai hướng. Bạn có thể nói, nhận câu trả lời và hành động ngay lập tức, khôi phục lại sự phối hợp liền mạch mà Ethereum đã từng có trước khi các hòn đảo hình thành.
SCOPE cho phép các rollup không chỉ gửi tin nhắn giữa các chuỗi và L1 một cách nguyên tử, mà còn cho phép người dùng trên một chuỗi gọi các hàm trên các chuỗi khác và ngay lập tức nhận và xử lý kết quả. Điều này mang lại trải nghiệm vận hành trên một chuỗi duy nhất trong khi vẫn duy trì lợi ích về khả năng mở rộng của rollup.
Nó hoạt động như thế nào?
Về cốt lõi, SCOPE chính thức hóa mô hình kế toán cần thiết cho các giao dịch xuyên chuỗi dựa trên cơ chế đẩy có thể xác minh. Mỗi chuỗi tham gia duy trì bốn hàm băm liên tục và một ánh xạ byte đơn đại diện cho chuỗi tập hợp các yêu cầu và phản hồi xuyên chuỗi:
-
requestsOutHash
: ghi lại các cuộc gọi liên chuỗi đi được khởi tạo bởi chuỗi này. -
requestsInHash
: ghi lại các lệnh gọi liên chuỗi đến sẽ được thực hiện trên chuỗi này. -
responsesOutHash
: ghi lại các phản hồi gửi đi từ các cuộc gọi chuỗi chéo được thực hiện trên chuỗi này. -
responsesInHash
: ghi lại các phản hồi đến từ các cuộc gọi chuỗi chéo được khởi tạo bởi chuỗi này. -
responsesIn
: ghi lại các phản hồi đến từ các cuộc gọi chuỗi chéo được khởi tạo bởi chuỗi này dưới dạng byte thô.
Giao diện scopedCallable
xác định cách các giá trị này được cập nhật:
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);}
Nguyên hàm trung tâm được cung cấp cho các nhà phát triển là hàm scopedCall()
. Hàm này cho phép một hợp đồng trên một rollup đồng bộ gọi một hàm trên một rollup khác và ngay lập tức sử dụng kết quả. Khi scopedCall()
được gọi, nó sẽ thêm một mã định danh yêu cầu duy nhất vào requestsOutHash
của chuỗi nguồn và đọc một phản hồi được điền sẵn từ ánh xạ responsesIn
cục bộ. Từ góc nhìn của người gọi, tương tác này có vẻ đồng bộ vì trình sắp xếp đã mô phỏng cuộc gọi trên chuỗi đích và đã điền sẵn responsesIn
trước. Tên *scopedCall*
phản ánh thực tế là toàn bộ tương tác xuyên chuỗi (yêu cầu, thực thi và phản hồi) được giải quyết trong một phạm vi thực thi nguyên tử duy nhất , tạo ra ảo giác về khả năng kết hợp cục bộ giữa các chuỗi.
Trên chuỗi đích, trình sắp xếp thực thi handleScopedCall()
, trộn cùng một mã định danh yêu cầu vào requestsInHash
, thực thi ScopedRequest
và cập nhật responsesOutHash
bằng kết quả. Đầu ra này sau đó được chuyển tiếp trở lại và chèn vào responsesIn
của chuỗi nguồn.
Tại thời điểm thanh toán, cầu nối sẽ xác minh rằng cả hai chuỗi đều tuân thủ đúng thứ tự yêu cầu và phản hồi. Cụ thể, nó kiểm tra:
-
requestsOutHash
của chuỗi nguồn khớp vớirequestsInHash
của chuỗi đích -
responsesInHash
của chuỗi nguồn trong Hash khớp vớiresponsesOutHash
của chuỗi đích trong Hash
Nếu bất kỳ cuộc gọi nào bị bỏ qua, sắp xếp lại hoặc bị can thiệp thì các hàm băm liên tục này sẽ không khớp và các lần tổng hợp sẽ không được giải quyết, đảm bảo tính nguyên tử.
Khả năng kết hợp đồng bộ L2↔L2
SCOPE hoạt động đặc biệt hiệu quả cho các tương tác L2↔L2. Giả sử một hợp đồng trên Rollup A cần gọi một hàm trên Rollup B. Trình tự chia sẻ quan sát scopedCall()
của A và ngay lập tức chèn một handleScopedCall()
tương ứng vào B. Sau khi thực thi hàm mục tiêu trên B và nhận được response
, trình tự sẽ điền trước một giao dịch fillResponsesIn()
trên A để khi scopedCall()
của A thực sự chạy, nó có thể đọc và xử lý response
một cách đồng bộ, giống hệt như một lệnh gọi cục bộ.
Bắt đầu từ các băm ban đầu của
H(0)
, luồng sẽ trả về: B.requestsInHash = A.requestsOutHash = H(H(0) || H(ScopedRequest))
và A.responsesInHash = B.responsesOutHash = H(H(0) || H(response))
. Nếu trình sắp xếp đưa yêu cầu không chính xác, requestsInHash
của B sẽ không khớp với requestsOutHash
của A (được suy ra từ scopedCall()
). Nếu trình sắp xếp chuyển tiếp phản hồi không chính xác, responsesInHash
của A sẽ không khớp với responsesOutHash
của B (được suy ra từ handleScopedCall()
). Cả hai trường hợp không khớp đều phá vỡ các kiểm tra tính bằng nhau khi thanh toán và tương đương với việc giả mạo quá trình thực thi EVM, mà các bằng chứng chuyển đổi trạng thái tiêu chuẩn sẽ từ chối.Thuận lợi
- Chứng minh song song: Mỗi rollup chứng minh độc lập quá trình chuyển đổi trạng thái của riêng nó song song, vì cả hai chuỗi đều có trình tự giao dịch được sắp xếp đầy đủ. Sự phụ thuộc xuyên chuỗi duy nhất là kiểm tra tương đương băm lăn cuối cùng tại thời điểm thanh toán.
- Không cần trình tự chia sẻ bắt buộc: Mặc dù trình tự chia sẻ có thể tối ưu hóa độ trễ, SCOPE vẫn hoạt động với các rollup được giải trình tự độc lập miễn là chúng cùng chia sẻ một lớp giải quyết và tin tưởng trình tự của nhau. Điều này làm cho nó tương thích trực tiếp với các hệ sinh thái như Optimism Superchain.
- Không yêu cầu chứng minh thời gian thực: Không giống như các lệnh gọi L1↔L2 đồng bộ, L2↔L2 không yêu cầu chứng minh tính hợp lệ phải được tạo trong cùng một khe. Yêu cầu duy nhất là các rollup tham gia cuối cùng phải được giải quyết cùng nhau để có thể xác minh tính tương đương băm liên tục.
- Chi phí được khấu hao thông qua các cam kết chia sẻ: Khi hai rollup cam kết thực thi L2↔L2 chung, chúng 1) có thể chia sẻ không gian blob và 2) chia sẻ một bằng chứng xác thực duy nhất. Điều này giúp giảm chi phí chung cho mỗi rollup và cho phép các rollup nhỏ hơn khấu hao chi phí blob và bằng chứng trên nhiều bên tham gia.
Khả năng kết hợp đồng bộ L1 ↔ L2
Việc lập luận các lệnh gọi đồng bộ L2↔L2 tương đối dễ hiểu khi giả định trình tự và giải quyết được chia sẻ, nhưng việc đưa L1 vào sẽ làm mọi thứ trở nên phức tạp. Để làm cho scopedCall()
đồng bộ L1↔L2 khả thi, chúng ta cần giải quyết ba thách thức đan xen: kiểm soát không gian khối L1, tính nguyên tử giữa các chuỗi và khả năng thực hiện các giao dịch L1 thay mặt người dùng.
Trung tâm của SCOPE là một siêu xây dựng (super builder) , lấy cảm hứng từ mô hình Giao dịch Siêu việt (Ultra Transactions). Siêu xây dựng chịu trách nhiệm mô phỏng toàn bộ lệnh gọi xuyên chuỗi (cả chân L1 và L2) và đảm bảo mọi thứ được xử lý nguyên tử trong một khe L1. Điều này đòi hỏi sự phối hợp chặt chẽ với bộ đề xuất L1 và bộ sắp xếp L2, hoặc lý tưởng nhất là để siêu xây dựng hoạt động như cả hai.
Kiểm soát không gian khối L1: Thách thức đầu tiên là chỉ có người đề xuất L1 quyết định những gì được đưa vào một khối. Nếu trình sắp xếp L2 mô phỏng scopedCall()
với giả định trạng thái L1, nhưng người đề xuất L1 lại vô hiệu hóa trạng thái đó bằng cách chèn hoặc sắp xếp lại các giao dịch, thì mô phỏng của L2 sẽ không hợp lệ và việc thanh toán sẽ thất bại. Để tránh điều này, siêu xây dựng phải có tính xác định đối với nội dung L1 tại thời điểm tạo bằng chứng L2. Điều này ngụ ý rằng siêu xây dựng hoặc là người đề xuất L1 hoặc phối hợp sắp xếp thứ tự với họ.
Giải quyết theo thời gian thực: Tiếp theo, tính nguyên tử yêu cầu cả hai chuỗi phải giải quyết cùng nhau và khôi phục lại nếu kiểm tra băm liên tục không thành công. Tuy nhiên, với L1↔L2, chỉ trạng thái roll-up mới có thể khôi phục lại. Để duy trì tính nguyên tử, tất cả hoạt động scopedCall()
(gọi hàm L1, gửi blob và xác minh bằng chứng) phải được đóng gói thành một giao dịch L1 duy nhất. Nếu bất kỳ kiểm tra băm liên tục nào không thành công, toàn bộ gói sẽ được khôi phục, khôi phục lại trạng thái L1 và roll-up. Quan trọng là, vì L2 phải sử dụng trạng thái L1, nó phải mô phỏng và khôi phục lại trong cùng một khe L1, đưa ra yêu cầu chứng minh theo thời gian thực không có trong trường hợp L2↔L2.
Thực thi Ủy quyền: Cuối cùng, các rollup thường cho phép trình sắp xếp chuỗi của chúng inject các giao dịch thay mặt người dùng, ví dụ: đúc ETH sau khi gửi tiền. L1 không hỗ trợ ủy quyền này, vì vậy để hỗ trợ L2→L1 scopedCall()
, chúng tôi dựa vào thực thi ủy quyền EIP-7702 . Người dùng ký một payload ủy quyền một hành động, và bundler sẽ đóng gói payload đó vào một giao dịch L1.
Ví dụ
Ví dụ này minh họa một giao dịch hoán đổi token chuỗi chéo sử dụng scopedCall()
trong đó hợp đồng L1 tương tác với L2 để thực hiện hoán đổi và ngay lập tức rút các token ERC-20 thu được trở lại L1. Trình sắp xếp sẽ điền trước kết quả hoán đổi thông qua fillResponsesIn()
, cho phép việc rút tiền diễn ra trong cùng giao dịch trước khi thanh toán rollup. Không giống như các giao dịch rút tiền dựa trên bằng chứng Merkle tiêu chuẩn, lệnh withdraw()
ở đây có thể không cần cấp phép vì toàn bộ gói, bao gồm cả giao dịch rút tiền, được bảo vệ bởi cơ chế khôi phục nguyên tử (atomic rollback) nếu bằng chứng không thành công hoặc giá trị băm không khớp, ngăn chặn mọi hành vi rút tiền trái phép.
Mô phỏng
Khi mô phỏng và sắp xếp, trình xây dựng siêu dữ liệu phải đảm bảo rằng mỗi lần cuộn lên đều tuân theo thứ tự một phần so với các lệnh gọi chuỗi chéo:
- Trên chuỗi nguồn, tất cả
scopedCalls
phải xuất hiện theo thứ tự tương đối được xác định rõ ràng. - Trên chuỗi đích, tất cả
handleScopedCalls
tương ứng phải xuất hiện theo thứ tự tương đối phù hợp. - Mọi thứ khác (giao dịch thông thường) có thể được xen kẽ miễn là chúng không làm thay đổi tải trọng
ScopedRequest
hoặcresponse
được tính toán theo cách làm thay đổi các hàm băm liên tục.
Cụ thể:
- Sau khi sắp xếp
scopedCall(req)
, trình xây dựng siêu không được bao gồm các giao dịch chuỗi nguồn làm thay đổireq
, ví dụ: đột biếnto
,value
,gasLimit
hoặcdata
. - Sau khi mô phỏng
handleScopedCall(req)
và nắm bắtresponse
, trình xây dựng siêu dữ liệu không được bao gồm các giao dịch chuỗi đích có thể thay đổiresponse
.
Để mô phỏng, siêu thợ xây sẽ:
- Chặn
scopedCall()
của chuỗi nguồn - Cập nhật cục bộ
requestsOutHash
của chuỗi nguồn - Chèn và thực thi
handleScopedCall()
vào chuỗi đích - Truyền
response
trở lại quá trình thực thi của chuỗi nguồn
Bằng cách lặp lại quá trình này, trình xây dựng siêu dữ liệu sẽ xác định tất cả các giá trị response
cần thiết để gọi fillResponsesIn()
.
Phụ lục
Tại đây, chúng tôi phân tích các ngăn xếp tổng hợp phổ biến để xác định những thay đổi cần thiết để hỗ trợ SCOPE với giả định mục tiêu là khả năng kết hợp L1↔L2 đồng bộ.
Yêu cầu cốt lõi của SCOPE (L1↔L2)
- Bộ sắp xếp chung : Một bộ sắp xếp chung phải phối hợp luồng giao dịch chuỗi chéo trên tất cả các cuộn tham gia.
- Sửa đổi máy khách L1 : L1→L2
scopedCall()
yêu cầu hành vi khác nhau trong quá trình mô phỏng so với khi thực thi. - Sửa đổi máy khách L2: Ngoài chức năng chuyển đổi trạng thái cốt lõi, các bản tổng hợp phải chứng minh tính tương đương băm liên tục cho tất cả các yêu cầu và phản hồi chuỗi chéo, đảm bảo tính nhất quán có thể xác minh được giữa các chuỗi tham gia.
- Sửa đổi cầu nối L1 : Cầu nối phải theo dõi các hàm băm liên tục (
requestsInHash
,requestsOutHash
,responsesInHash
,responsesOutHash
) để thực thi tính nguyên tử thông qua các kiểm tra tương đương trong quá trình thanh toán. - Chứng minh theo thời gian thực : Các bản tổng hợp phải tạo và gửi bằng chứng xác thực (tức là bản tổng hợp không thể tranh cãi) trong cùng một khe L1 để tham gia vào quá trình thực thi
scopedCall()
nguyên tử. - Phối hợp người đề xuất L1 : Trình sắp xếp phải là người đề xuất L1 hoặc phải có khóa trạng thái để đảm bảo nhánh L1 của
scopedCall()
thực thi chính xác như mô phỏng. - Hỗ trợ giá trị trả về : Mô phỏng trước các lệnh gọi chuỗi chéo và theo dõi các ánh xạ
responsesOutHash
,responsesInHash
vàresultsIn
trên cả L1 và L2. Điều này cho phép các hợp đồng gọi đồng bộ sử dụng các giá trị trả về như thể lệnh gọi đó là lệnh gọi cục bộ.
Nghiên cứu điển hình: Ethrex
Ngăn xếp Ethrex hỗ trợ các lệnh gọi chuỗi chéo L1→L2 dựa trên đẩy mà không trả về giá trị thông qua các giao dịch đặc quyền và truyền tin nhắn L2→L1 dựa trên kéo.
L1→L2 Hôm nay
Việc gọi
CommonBridge.sendToL2()
sẽ gửi một payloadSendValues
tùy ý, mã hóa lệnh gọi hàm L2 đích. Giá trị băm của payload được thêm vào mảngpendingTxHashes
, và một sự kiệnPrivilegedTxSent
được phát ra.Trình sắp xếp lắng nghe sự kiện PrivilegedTxSent và đưa
PrivilegedL2Transaction
vào bộ nhớ L2. Giao dịch này thực thi lệnh gọi hàm được mã hóa trongSendValues
.Trong quá trình thanh toán, hệ thống chứng minh thu thập tất cả
PrivilegedL2Transactions
, tính toán hàm băm liên tục của chúng và xác minh rằng chúng khớp với hàm băm liên tục củapendingTxHashes
được tính toán trên chuỗi thông quaOnChainProposer.verifyBatch()
.Cơ chế này tương đương với việc xác minh rằng L1
requestsOutHash
khớp với L2requestsInHash
theo mô hình SCOPE.
L2→L1 Hôm nay
- Gọi
L2ToL1Messenger.sendMessageToL1(bytes32 data)
sẽ phát ra sự kiệnL1Message
trong đódata
là hàm băm của tin nhắn đang được gửi. - Bộ sắp xếp sẽ xây dựng một cây Merkle từ tất cả các giá trị
data
đó và xác nhận gốc trong quá trình giải quyết L2. - Để hoàn thiện tin nhắn, người dùng cung cấp tin nhắn thô và bằng chứng Merkle, cho phép xác minh rằng tin nhắn đã được L2 xác nhận.
Hiện tại, cầu nối L1 chỉ hỗ trợ rút token. Các lệnh gọi hàm L1 đa năng do hợp đồng CommonBridge
khởi tạo vẫn chưa được hỗ trợ nhưng sẽ dễ dàng bổ sung.
Khả năng tương thích của SCOPE yêu cầu:
- Thay thế
sendToL2()
bằngscopedCall()
, giới thiệuresponsesOutHash
,responsesInHash
và ánh xạresultsIn
để cho phép các hợp đồng gọi sử dụng ngay các giá trị trả về từ các lệnh gọi hàm chuỗi chéo. - Thay vì chờ các sự kiện
PrivilegedTxSent
được phát ra trên L1, trình sắp xếp sẽ đưa trướcPrivilegedL2Transactions
trước khi xác nhận khối L1, cho phép thực hiện đồng bộ trên các chuỗi. - Thay thế tin nhắn L2→L1 dựa trên pull bằng
scopedCall()
dựa trên push được khởi tạo từ L2 và được xử lý thông quahandleScopedCall()
trên L1. Điều này cho phép các lệnh gọi hợp đồng L1 tùy ý được thực thi trong cùng một khe L1.
Nghiên cứu điển hình: OP Stack
OP Stack hỗ trợ các lệnh gọi chuỗi chéo theo cơ chế đẩy, hai chiều mà không cần giá trị trả về .
SCOPE cũng có thể được áp dụng cho SuperChain để cho phép khả năng kết hợp đồng bộ L2↔L2 mà không cần trình sắp xếp chung hoặc chứng minh thời gian thực miễn là các rollup tham gia chia sẻ một lớp thanh toán và tin tưởng lẫn nhau về trình tự của nhau.
L1→L2 Hôm nay
- Gọi L1
CrossDomainMessenger.sendMessage()
cho phép gửi các byte mờ tùy ý dưới dạng calldata đến hợp đồng L2 mục tiêu. -
OptimismPortal.depositTransaction()
sẽ ký quỹ bất kỳ ETH nào trong hộp khóa và phát ra sự kiệnTransactionDeposited
. - Bộ sắp xếp lắng nghe các sự kiện
TransactionDeposited
và đưa một giao dịch vào L2 để gọiCrossDomainMessenger.relayMessage()
, thực thi lệnh gọi hàm L2 bằng cách sử dụng dữ liệu đã gửi trước đó.
Đường ống dẫn xuất đảm bảo rằng tất cả các sự kiện TransactionDeposited
đều tương ứng với một thông điệp đang được chuyển tiếp. Nếu không, trình sắp xếp đã thực hiện hành vi gian lận.
L2→L1 Hôm nay
- Gọi L2
CrossDomainMessenger.sendMessage()
cho phép gửi các byte mờ tùy ý dưới dạng calldata đến hợp đồng L1 mục tiêu. -
L2ToL1MessagePasser.initiateWithdrawal()
ghi lại hàm băm tin nhắn trong ánh xạsentMessages
và phát ra sự kiệnMessagePassed
. - Bộ sắp xếp đề xuất
output
L2 chứaoutput_root
cam kết với trạng thái ánh xạsentMessages
. - Người dùng chứng minh việc bao gồm tin nhắn bằng cách gọi L1
OptimismPortal.proveWithdrawalTransaction()
với bằng chứng merkle. - Sau khi cửa sổ chống gian lận trôi qua, việc gọi L1
OptimismPortal.finalizeWithdrawalTransaction()
sẽ thực thi lệnh gọi hàm L1.
Nếu sử dụng bằng chứng xác thực, phương pháp dựa trên kéo này có thể được giảm xuống còn hai giao dịch: một để khởi tạo tin nhắn trên L2 và một để chứng minh và hoàn tất việc thực thi trên L1.
Khả năng tương thích của SCOPE yêu cầu:
- Hỗ trợ bằng chứng xác thực có khả năng chứng minh theo thời gian thực để cho phép quá trình tổng hợp diễn ra trong một khe L1 duy nhất.
- Hỗ trợ L1→L2
scopedCall()
đồng bộ bằng cách cho phépop-node
đặt [SequencerConfDepth](https://github.com/ethereum-optimism/optimism/blob/f70219a759e1da31e864c0ccdc2c757689aba3ec/op-node/rollup/driver/config.go#L12) = 0
và cho phép gọiCrossDomainMessenger.relayMessage()
trước khi sự kiệnTransactionDeposited
được phát ra trên L1. - Cho phép các cuộc gọi L2→L1 đồng bộ bằng cách thay thế quy trình truyền tin nhắn L2→L1 nhiều bước hiện tại bằng một
scopedCall()
do L2 khởi tạo.
Nghiên cứu điển hình: Taiko
Ngăn xếp Taiko hỗ trợ các lệnh gọi chuỗi chéo theo cơ chế kéo, hai chiều mà không cần giá trị trả về .
L1→L2
- Gọi
Bridge.sendMessage()
cho phép gửi mộtMessage
tùy ý, mã hóa lệnh gọi hàm đến hợp đồng L2 đích.Message
được băm (tạo ra một tín hiệu ) và được lưu trữ trong hợp đồngSignalService
L1. - Người dùng tạo bằng chứng lưu trữ bằng cách sử dụng RPC
eth_getProof
chuẩn, chứng minh rằng tín hiệu của họ tồn tại trên L1. - Mỗi khối Taiko bắt đầu bằng một giao dịch neo chèn gốc trạng thái thế giới L1 hiện tại và một danh sách các tín hiệu mới. Các tín hiệu này sau đó được ghi vào hợp đồng L2
SignalService
. - Bằng cách gọi L2
Bridge.processMessage()
vớiMessage
và bằng chứng lưu trữ L1, người dùng chứng minh được tin nhắn đã được đưa vào L1. Điều này được thực hiện bằng cách xác minh tín hiệu với gốc trạng thái thế giới L1 và hợp đồngSignalService
L2. -
_invokeMessageCall()
sau đó thực thi lệnh gọi hàm được mã hóa trongMessage
trên hợp đồng L2 mục tiêu. - Trong quá trình thanh toán, hệ thống sẽ xác minh rằng các tín hiệu được báo cáo trong giao dịch neo khớp với những gì được ghi vào L1
SignalService
.
Việc kiểm tra các tín hiệu tương đương về mặt chức năng tương đương với việc so sánh L1 requestsOutHash
và L2 requestsInHash
trong SCOPE.
L2→L1
- Người dùng gọi
L2 Bridge.sendMessage()
để lưu trữ hàm băm tin nhắn ( tín hiệu ) trong hợp đồng L2SignalService
. - Khi quá trình rollup hoàn tất và trạng thái toàn cầu của nó được hoàn thiện, L1
Bridge.processMessage()
có thể được gọi vớiMessage
gốc và bằng chứng lưu trữ cho thấy tín hiệu tồn tại trong hợp đồng L2SignalService
. -
_invokeMessageCall()
sau đó thực thi lệnh gọi hàm được mã hóa trên hợp đồng L1.
Khả năng tương thích của SCOPE yêu cầu:
- Thay thế luồng tín hiệu hiện tại bằng mô hình đẩy, trong đó
Bridge.sendMessage()
tương đương vớiscopedCall()
. Thay vì ghi lại các tín hiệu riêng lẻ và chuyển tiếp chúng đến chuỗi đích, trình sắp xếp sẽ chuyển tiếp trực tiếp toàn bộMessage
. Chuỗi nguồn sẽ thêm message vào mộtrequestsOutHash
liên tục, trong khi chuỗi đích tính toán mộtrequestsInHash
tương ứng trong quá trìnhhandleScopedCall()
. Điều này loại bỏ nhu cầu sử dụng bằng chứng Merkle vì bất kỳMessage
bị giả mạo đều khiến việc kiểm tra băm liên tục bị lỗi, ngăn chặn việc thanh toán. -
Bridge.processMessage()
trở thành tương đương vớihandleScopedCall()
và cần được gọi ngay lập tức, thực thiMessage
và cập nhật cảrequestsInHash
vàresponsesOutHash
. Hàm này có thể không cần cấp phép, vì người đề xuất hợp lý phải đảm bảo các message được xử lý theo đúng thứ tự để rollup được hoàn tất (tức làrequestsInHash
khớp vớirequestsOutHash
).
Nghiên cứu điển hình: Linea
Ngăn xếp Linea hỗ trợ các cuộc gọi chuỗi chéo hai chiều mà không có giá trị trả về , sử dụng phương thức phân phối đẩy hoặc kéo tùy thuộc vào việc có sử dụng Dịch vụ Postman hay không.
L1→L2 Hôm nay
- Gọi
L1MessageService.sendMessage()
sẽ gửi các byte mờ (calldata) đến hợp đồng L2 đích. Hàm băm tin nhắn được tích hợp vào hàm băm liên tục và sự kiệnMessageSent
được phát ra trên L1. - Dịch vụ điều phối sẽ giám sát các sự kiện này, chờ hai kỷ nguyên L1 để hoàn tất, sau đó gọi
L2MessageManager.anchorL1L2MessageHashes()
trên L2. Thao tác này ghi các băm tin nhắn vào L2 và phát ra sự kiệnRollingHashUpdated
, đảm bảo cùng một băm lăn có thể được tính toán lại trên cả hai chuỗi. - Cuối cùng,
L2MessageServiceV1.claimMessage()
thực thi lệnh gọi hàm L2, đặt cờ để ngăn chặn việc phát lại và phát raMessageClaimed
. Người dùng có thể gọi thủ công hoặc tự động bởi Dịch vụ Bưu chính nếu người dùng đã trả trước phí L1. - Vào thời điểm thanh toán,
RollingHashUpdated
cuối cùng do L2 phát ra sẽ được kiểm tra so với hàm băm lăn trên L1 để xác minh tính nhất quán của thông báo trên các chuỗi.
Cơ chế này tương đương với việc xác minh rằng L1 requestsOutHash
khớp với L2 requestsInHash
theo mô hình SCOPE.
L2→L1 Hôm nay
- Gọi
L2MessageServiceV1.sendMessage()
sẽ phát ra sự kiệnMessageSent
chứa hàm băm tin nhắn và mã hóa các byte mờ tùy ý dưới dạng calldata cho hợp đồng L1 mục tiêu. - Trong quá trình thanh toán, người chứng minh sẽ xây dựng một cây Merkle của tất cả các giá trị băm tin nhắn được phát ra và cam kết gốc Merkle cho L1.
- Để hoàn tất tin nhắn, người dùng (hoặc Dịch vụ Postman ) gọi
L1MessageService.claimMessageWithProof()
với tin nhắn thô và bằng chứng Merkle của nó, thực thi lệnh gọi hàm L1.
Khả năng tương thích của SCOPE yêu cầu:
- Xóa bước
anchorL1L2MessageHashes()
và thay vào đó cập nhật hàm băm lăn L2 theo từng bước trongclaimMessage()
, tương đương vớihandleScopedCall()
. Trình sắp xếp phải đảm bảo các thông điệp được yêu cầu theo đúng thứ tự để đảm bảo tính nhất quán của hàm băm lăn giữa L1 và L2. - Thay thế xác minh bằng chứng Merkle hiện tại được sử dụng trong
claimMessageWithProof()
bằng mô hình đẩy sử dụngscopedCall()
được khởi tạo từ L2. L2 theo dõirequestsOutHash
và L1 tính toánrequestsInHash
tương ứng khihandleScopedCall()
được gọi. -
LineaRollup.submitBlobs()
vàLineaService.finalizeBlocks()
phải được đóng gói lại với nhau và thực thi một cách nguyên tử. Điều này đảm bảo L2 được xử lý theo thời gian thực.
Dựa trên Xác nhận trước và PHẠM VI
Xác nhận trước không phải là yêu cầu bắt buộc đối với SCOPE. Người ta có thể hình dung một hệ thống tổng hợp dựa trên "hoàn toàn hỗn loạn" (toàn bộ anarchy), trong đó bất kỳ ai cũng có thể đóng vai trò là người xây dựng siêu cấp, đề xuất các gói hợp lệ chứa các lệnh gọi xuyên chuỗi, mà không cần cung cấp preconf. Mô hình này hoạt động hiệu quả, nhưng tất nhiên, preconf có thể cải thiện trải nghiệm người dùng (UX) bằng cách cung cấp cho người dùng sự chắc chắn sớm hơn về kết quả giao dịch của họ.
Các preconf thực thi thường được coi là tiêu chuẩn vàng, nhưng thứ tự hoạt động của SCOPE làm phức tạp nó: trạng thái sau của scopedCall()
khác nhau giữa mô phỏng và thực thi do nhu cầu điền vào ánh xạ responsesIn
. Để đạt hiệu quả, một siêu xây dựng có thể chèn một lệnh gọi fillResponsesIn()
duy nhất sau khi tất cả các mô phỏng đã chạy, thay vì trước mỗi scopedCall()
. Điều này có thể được giải quyết trên các bản tổng hợp bằng cách đặt fillResponsesIn()
ngay trước mỗi scopedCall()
, điều này khả thi về mặt gas. Trên L1 , một giải pháp thay thế là siêu xây dựng xác nhận trước các băm trước và sau khi lăn cùng với dữ liệu yêu cầu và phản hồi tương ứng của chúng. Cách tiếp cận này cho phép người dùng tìm hiểu kết quả của các yêu cầu chuỗi chéo một cách đáng tin cậy theo cách dễ chứng minh hơn trong trường hợp có lỗi và dễ phát hành hơn từ siêu xây dựng (vì nó không yêu cầu gốc trạng thái trung gian).
Các công trình trước đây như CUSTARD khám phá việc mở rộng khả năng tồn tại của các preconf "siêu giao dịch" được tạo trước khe của trình xây dựng siêu. Cần nghiên cứu thêm để xác định liệu việc phát hành scopedCall()
sớm như vậy có an toàn hay không khi ScopedRequests
phụ thuộc vào dữ liệu trạng thái tùy ý không thể bị khóa bằng các kỹ thuật mà CUSTARD mô tả (ví dụ: ScopedRequest
có thể chứa dữ liệu pricefeed được xác định tại thời điểm scopedCall()
được thực thi, có thể khác với khe mà nó được mô phỏng và xác nhận trước).
SCOPE so với AggLayer
SCOPE và AggLayer đều hướng đến mục tiêu cho phép truyền tải thông điệp xuyên chuỗi không cần tin cậy, nhưng chúng tiếp cận vấn đề từ các góc độ khác nhau. AggLayer là một giao thức tương tác hoàn chỉnh với các quy tắc thanh toán riêng, trong khi SCOPE, mặc dù có chữ "giao thức" trong tên, chủ yếu là một khuôn khổ kế toán có thể được chồng lên các hệ thống hiện có như AggLayer, Superchain hoặc Elastic Network.
Cả hai hệ thống đều chia sẻ cùng một triết lý chứng minh bi quan: các chuỗi tự chứng minh các chuyển đổi trạng thái của riêng chúng một cách độc lập và chỉ giải quyết nếu các kiểm tra tương đương mật mã đạt yêu cầu. Trong AggLayer, điều này thể hiện thông qua "Cây Thoát Cục bộ", với gốc đóng vai trò tương tự như requestsOutHash
của SCOPE. Điểm khác biệt là AggLayer và các giao thức tương tự không hỗ trợ giá trị trả về đồng bộ từ các lệnh gọi xuyên chuỗi. Các thông báo được gửi đi, nhưng không có gì được trả về trong cùng một lần thực thi.
SCOPE mở rộng mô hình này bằng cách theo dõi các phản hồi đến, ví dụ thông qua responsesInHash
hoặc một "Cây Truy cập Cục bộ" giả định. Điều này cho phép các chuỗi đồng bộ hóa dữ liệu trả về như thể chúng là một phần của một môi trường thống nhất. Kết quả là sự chuyển đổi từ việc phân phối tin nhắn đơn giản sang phạm vi thực thi được chia sẻ thực sự, đồng thời vẫn duy trì các đảm bảo an toàn về thời gian xử lý.