Rollup interoperability approaches
Acknowledgments
This landscape mapping was sparked by the whiteboard session organized by 1kx on the topic of rollup interoperability. I would like to further thank Andrew Burger and Marshall Vyletel Jr. for their feedback and for insightful discussions on the topic.
Setting
We consider interoperability between two rollups, ChainA and ChainB.
Wait. Why not have just one chain with massive throughput? Because apps will benefit from slight modifications to the underlying execution engine (e.g. EVM) or state machine. These chains, with minor modifications each, can still be interoperable and provide much better UX. Latency is one aspect, but meeting the users’ needs is much more important, and can only be done if your app is not limited by the underlying tech. We think the app-chain future is a little too specialized and places too much burden on the developers. We’ve seen the complexity kill adoption in ecosystems like Polkadot.
But, we do believe in vertical-specialized chains (e.g. for oracles, DeFi, games).
Let’s dive in, we will cover interop via: L1 Relayer, Shared settlement, and Atomic execution.
Level of interop: | Definition | Rollup Must Opt in? |
---|---|---|
L1 relayer | Tokens pass through the L1 to move between rollups. | No |
Bridges | Issue wrapped tokens on the counterparty chain, or front capital. | No |
Shared Settlement | Aggregate proofs across multiple rollups and settle to shared contract on L1 | Yes |
Atomic Execution | Guarantee execution of two transactions either both succeed or both fail. User is able to generate the Multi party transaction | Yes |
Considerations
For each of the interop levels, we will look at the following considerations:
- Workflow with a grounding example of transferring a token across two chains
- Failure Modes & Trust Assumptions
- Stakeholders
- developers (priority)
- end-users (priority)
- rollups
Examples
While we will explicitly walk through only a token transfer, below we list further illustrative examples of increasing complexity and we encourage the reader to walk through those independently:
- x-chain transfer (same token)
- Swap token X on ChainA for token Y on ChainB
- transfer tokenX from ChainA → ChainB
- swap tokenX for tokenY on an exchange on ChainB
- Same as 2., but transfer token Y back to ChainA. All within one ChainA block.
- transfer tokenX from ChainA → ChainB
- swap tokenX for tokenY on an exchange on ChainB
- transfer tokenY to ChainA
L1 relayer
Each chain hosts its own contract on the L1 for locking assets and for withdrawals back from the L2.
Flow:
- User on ChainA initiates withdrawal of tokens to L1 via a special transaction on ChainA.
- This is the withdrawal initiating transaction
- If this transaction is included in some next block, the user will be able to later prove the inclusion of their transaction in the output root via a Merkle proof.
- User posts the withdrawal request to an L1 contract.
- in the optimistic context, user’s assets are freed up after ~1 week,
- the withdrawal request (withdrawal proving transaction) and actual withdrawal (withdrawal finalizing transaction) are two separate actions that need to be done by the user.
- in the zkRollup context, this is processed as soon* as the proof is verified.
- in the optimistic context, user’s assets are freed up after ~1 week,
- User sends the recently withdrawn funds to a deposit contract of ChainB, on L1 (rollup inbox).
- The ChainB inbox is processed by the L2 operator and the user is credited the tokens on ChainB L2.
*some zkRollups have a safety window in place in case an emergency upgrade is needed.
Failure modes & Trust assumptions
- Rollups with a centralized sequencer have liveness concerns and are vulnerable to censorship, although typically the user is able to force the withdrawal,
- Challenges inherent to rollup type (SNARK bugs or the lack of an honest challenger).
Stakeholders
- App developers don’t need to do anything. Apps are not x-chain aware, which potentially limits their functionality.
- All the burden, both in cost and in initiating actions, falls on the user and involves a heavily manual process.
- Rollups maintain sovereignty over sequencing.
Bridging
Can be one-way or two-way, canonical (native assets deposited into the rollup contract on L1), or 3rd party (liquidity pools maintained on the rollup, by some bridging team/protocol). Not discussed in this doc.
Shared settlement
Shared settlement is a mechanism allowing multiple chains to share messages and values with one another by utilizing a common framework.
This framework can be a shared smart contract on the respective base chain (e.g. for L2s built on AggLayer) or a message exchange standard (Cosmos IBC or Polkadot XCM).
This concept enhances interoperability by allowing different chains to exchange the same underlying assets without the need of creating wrapped tokens or leveraging liquidity providers.
Shared settlement is always opt-in, which means that chains wishing to enable native interoperability for their users must be proactive and adjust their logic to follow the common protocol. Aside from the minimal set of shared rules, the chains are free to choose their own settlement mechanism, consensus protocol and internal logic (e.g. ChainA could be EVM-compatible and use optimistic settlement, ChainB be EVM+ and use SNARKs for verifying state transitions, and ChainC use consensus rules).
We first discuss how Ethereum rollups can leverage a shared contract on L1 and then briefly discuss interoperability via cross-chain message passing protocols.
Shared base layer contract
The setup is the following:
- Multiple L2 rollups, with potentially distinct logic and consensus mechanisms.
- Single rollup contract on L1, which holds asset balances for all of the rollups together,
- Users can move their assets from L1 → any L2 by locking their assets in the rollup contract,
- Users can withdraw from L2 → L1, provided they follow L1 contract’s withdrawal rules which verify that a withdrawal is valid,
- Users can transfer assets between L2s, and still be able to withdraw the same (unwrapped) assets on the L1,
- Light client contract of every L2, deployed on the L1. This can be separate from the shared settlement contract. The light client logic might be different for each chain, depending on the chain’s settlement logic.
Flow
We assume async execution.
- User initiates a transfer from ChainA → ChainB. This involves locking up/burning tokens on ChainA. Proof of locking (not necessarily a SNARK proof, this can be just a Merkle Proof) is sent to the shared L1 contract,
- The L1 verifies the transaction, as well as consensus of ChainA, via a light client contract of ChainA,
- A mint is possible on ChainB, which verifies that ChainA indeed executed the lock. ChainB can now safely mint, which ensures no double spending,
- ChainB sends proof of mint to the base chain,
- The L1 verifies the correctness of ChainB’s state transition,
- ChainA completes the transfer, by marking the lock as finalized (+ potentially burning the tokens).
Failure modes & Trust assumptions
- Race conditions: to ensure atomicity, we must allow for timeouts. This opens the doors to potential race conditions: in the simplest scenario, the mint confirmation is delayed until ChainA timeouts and returns the locked asset to the user, but now ChainB already minted on its side. This is a solvable problem but needs careful design to prevent double spending or losing the assets.
- Each chain has a separate verification protocol.
- Chains using consensus for security pose a higher risk for its counterparties,
- Rollups using optimistic settlement have a significant delay in confirming the transaction on the L1 chain, thus negatively impacting latency and UX. Note that it is possible to have a faster (inter-chain) finality without necessarily requiring finality on L1. If each counterparty rollup runs a full client of the source (optimistic) rollup, they can verify the source’s state transition client side, and continue building their own blocks. This doesn’t scale well, and requires each rollup in such a setup to inherit the optimistic guarantees, but it could be used in certain use cases.
Common message exchange standard
Cosmos / IBC
Cosmos’ IBC is an example of sharing the assets across different chains which speak the same protocol. The very incomplete summary of IBC boils down to two chains opening a channel over which messages are passed and acknowledged, and the chains maintaining light clients of each other to verify the correct action of the counterparty chain.
Token transfers are one of the message types enabled by IBC. In the case of chain-to-chain communication (i.e. without the Cosmos Hub), these tokens are only semi-fungible, due to channel-specific prefixes appended to (or removed from) the token definition upon each transfer.
A journey taken through ChainA → ChainB → ChainA becomes fully fungible again, but a journey ChainA → ChainB → ChainC → ChainA represents a different underlying asset.
The Cosmos Hub, which maintains metadata and registries of the connected chains, could help with the semi-fungibility by acting as an intermediary and facilitating the transfers back through the same path it originated.
Ultimately, the security of the cross-chain swaps relies on trusting the consensus mechanism of the counterparty chain (e.g. validating the consensus signatures via the light client of the counterparty). As a result of this, note that the “same” tokens which traveled different paths will end up having a different security guarantee on the target chain.
Polkadot / XCM / HRMP / XCMP
The lines between a shared base layer and a message exchange protocol get blurred when talking about Polkadot’s XCMP.
Polkadot’s relay chain provides a shared layer of security (unlike in Cosmos), and thus the state of the different connected chains can be safely determined by considering their checkpoints on the relay chain. Rather than using a shared settlement contract, however, the sharing of assets is baked-in to the base protocol itself.
Polkadot separates messaging into two different layers. One part, XCM (Cross-Consensus Messaging) is a layer which abstracts the message format of how two or more different state machines will handle a message within their respective state machine. This includes specific formatted messages such as asset transfers, governance specific messages, and, in the extreme case, raw byte transaction calls specific to the target state machine. XCM can more easily be thought of as a specialized virtual machine smart contract. It has registers, custom registers for signers, contains an XCVM(Cross-Consensus Virtual Machine) and even calls XCM messages which are processed by the XCVM “instructions”.
The second layer of Polkadot messaging is actually how the XCM instructions or messages are transported safely from destination A to destination B. This is where XCMP / HRMP protocols arrive. Since every Polkadot L2 relies on a shared consensus notion derived from the Polkadot base L1, this allows for Polkadot’s L2s to utilize these notions in order to agree on what messages have been sent where, inheriting non-repudiation.
Both XCMP and HRMP utilize channels similar to Cosmos IBC so that L2’s can opt-in to messaging.
HRMP (Horizontal Message Passing) - This approach is very similar to any L1 bridging model. The L2 formulates a set of messages and transfers the raw message bytes to the L1 via a shared queuing system. The L1 then forwards the messages to the respective channel counterparty. This means that every L2 has a representative queue on L1, which the L1 maintains and validates.
XCMP(Cross-Consensus Message Passing) is the next-gen version of HRMP, which offloads all of the messaging to the L2s themselves. It frees up the L1 from any additional computation and work needed to facilitate message passing. This works by having each L2 commit to their messages via their local state machine, and that commitment making its way into the L1 via the standard Polkadot protocol. The target L2 can then receive messages that it can verify against the commitment posted to the L1.
Atomic execution (+ shared settlement)
We envision three core components necessary for atomic execution with a shared settlement layer.
A shared sequencer provides an inclusion guarantee of two transactions meant to be executed atomically across two different chains (atomic inclusion). A good overview of the sequencer’s role is provided by Espresso, although many system components will need modifications for the atomic execution use case.
Rollups provide proofs of correct state transitions w.r.t. the data posted by the sequencer (atomic execution).
A chain-aware wallet constructs the transactions on behalf of the user.
Flow
The user constructs two transactions: burn on ChainA; mint on ChainB. These two transactions are meant to have a dependency on one another: ChainA’s burn should only be valid if ChainB’s mint was valid as well, and vice versa.
Shared sequencer prepares the bundles for each rollup. The high-level bundle structure would be:
ChainA bundle:
Transactions:
- burn X tokens
- …other transfers within ChainA
Dependencies:
- ChainB: mint X tokens
ChainB bundle:
Transactions:
- mint X tokens
- …other transfers within ChainB
Dependencies:
- ChainA: burn X tokens
The bundles are broadcasted to the individual rollups:
- Rollups can already start building the blocks based on the trust in the sequencer. A misbehaving sequencer will be detected early and can be blacklisted,
- Rollups could also start proving their state transition.
The bundles are posted to a shared data availability layer:
- Atomic inclusion comes from trust in the sequencer, this serves as a checkpoint,
- Can be viewed as intent of execution, but there is no guarantee yet.
Rollups prove their state transition. The proofs are broadcast to other rollups that have a dependency on it:
- When ChainA receives a proof from ChainB, it can locally verify that B performed the state transition correctly, and that ChainA’s dependency is fulfilled in ChainB’s state transition,
- If ChainA receives no proof (or faulty proof) from ChainB, it will not proceed with finalizing the transactions which have a dependency on ChainB,
- Receiving a valid proof from ChainB results in ChainA including said proof as a dependency in its next block,
Proofs are sent to a shared proof availability layer. Rollups should have already verified these locally (5a.) and so these serve as checkpoints.
Failure modes & Trust assumptions
We note that rollups offer multiple levels of guarantees for their transactions’ finality:
- Transaction bundles broadcasting (Step 3): a misbehaving sequencer could result in rollup blocks which never get finalized (loss of liveness), because:
- Transactions meant for ChainA could have a dependency which is valid in the atomic execution sense (mint <> burn), but the counterparty on ChainB fails to execute.
- ChainA would never receive a proof of correct state transition from ChainB that includes its dependency.
- Can we protect against malicious sequencer? How to detect if the sequencer was misbehaving on purpose vs. it was the rollup?
- Sequencer could broadcast the (valid) bundle to ChainA, but not broadcast to ChainB.
- We could add a signing step where each rollup operator confirms that they received the bundle. The signatures can be used as a fast (but soft - not posted to L1 yet) guarantee of inclusion. TBD if this is useful or just an unnecessary overhead.
- If the bundle is eventually posted to L1, ChainB will pick it up. Can lead to delays.
- Transactions meant for ChainA could have a dependency which is valid in the atomic execution sense (mint <> burn), but the counterparty on ChainB fails to execute.
- Transaction bundles checkpoints (Step 4): issues similar to those in bundle broadcasting, but without the worry of some chains not receiving the bundles.
- Proof-to-peer (Step 5): ChainA receiving and verifying a proof from its dependency, ChainB, can safely proceed to advance its own state. Since the bundle checkpoints are already available to L1, ChainB will not be able to make another valid proof of a different state transition.
- ChainB can still fail to post its proof to the proof availability layer in due time. This can result in the loss of liveness*.
- Proof checkpoints (Step 6): At this point, the rollup needs only confirm that the proof they received via the broadcast channel from the other rollups match the proofs posted to the proof availability layer.
- Proof verification: We can further unbundle proof verification from proof checkpoints. The typical flow is to verify each single proof directly on L1, but this would incur high gas costs. Alternative verification mechanisms could include:
- proof aggregation, which amortizes verification cost at the expense of a delay to aggregate the proofs. It further places the aggregation-friendliness requirement on the underlying SNARKs.
- optimistic acceptance of proofs: allow for SNARK proofs to be challenged. This is similar to a fraud-proof mechanism of optimistic rollups’ state transitions, but instead of verifying the full STF, the watchers only verify succinct proofs. This completely eliminates the onchain verification costs under the one honest party security assumption.
- separate verification layer: uses 3rd party consensus to agree on the result of SNARK verification. Can potentially offer faster finality by verifying signatures of the 3rd party layer validators, but requires an honest majority assumption.
*It remains to be shown how atomic execution could be turned into a more robust async execution, where rollups’ proofs are not posted within the same block (can still be atomic - either both transactions happen, or neither - but the atomicity is not finalized until the second transaction is included).
Stakeholders
- No modifications needed on the app developers’ side.
- A good wallet UX can abstract away most of the complexity and make for a seamless interoperability.
- Rollup operators: lose out on sequencing and control.
Notes
- We aim for async atomicity at the transaction level, rather than requiring atomic synchronous inclusion of the entire blocks, which is more prone to liveness issues.
- In this section we assumed that atomic execution comes bundled with a shared settlement layer. This facilitates the flow for the user, who can e.g. bridge the tokens L1 → ChainA, move ChainA → ChainB, and withdraw these same tokens on L1.
- Without the shared settlement, the tokens bridged to ChainB would be wrapped, and direct withdrawal to L1 would be impossible (but can be done via 3rd party LPs).
Atomic Execution (without Shared Settlement)
This mode of interoperability is very similar to Atomic Execution with Shared Settlement, except that the chains maintain separate rollup contracts on the L1. It negatively impacts the fungibility of the assets (ChainA’s contract cannot just mint new Ether just because ChainB burned some) resulting in wrapped assets and requiring LPs to facilitate the withdrawals of bridged tokens to the base chain. This approach still requires the chains to agree to some minimal set of rules for interoperability. Justifying those requirements to the rollups, without the upside of shared settlement, seems onerous and we do not dive deeper into this category.
Summary
Ulitmately, there are a myriad ways to achieve the same goals (good UX, users not even aware of transactions happening across chains) with vastly different technical approaches. Each solution has different trust assumptions, tradeoffs and complexity of delivery.
We are dubious of bridges and of using LPs to front liquidity. We believe we can get much better UX (which includes lower fees) by using a shared settlement + atomic execution of cross-chain transactions. Time (and our efforts) will show.
Author: Marti Górny