Skip to content

Conversation

@MatheusFranco99
Copy link
Contributor

@MatheusFranco99 MatheusFranco99 commented Oct 20, 2025

Overview

Introduces the CDCP protocol for the integrated/included rollups composability (here denoted native vs. external).

It describes the protocol, the new mailbox, the syncing with the ER client, and the modifications to the settlement pipeline.

@MatheusFranco99 MatheusFranco99 marked this pull request as ready for review October 20, 2025 16:56
Copilot AI review requested due to automatic review settings October 20, 2025 16:56
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the Cross-Domain Composability Protocol (CDCP), which enables atomic composability between native rollups in the Compose network and external rollups. The protocol ensures that cross-domain transactions either succeed on all participating rollups or fail on all of them, maintaining atomicity guarantees.

Key changes:

  • Defines the CDCP protocol with message flows between Shared Publisher (SP), Native Sequencers (NSs), Wrapped Sequencer (WS), and External Rollup Client (ER)
  • Introduces a StagedMailbox contract variant for external rollups with pre-populated messages
  • Establishes synchronization mechanisms between WS and ER to maintain consistent state snapshots during protocol execution

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


## System Model

We system encompasses 5 main components:
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected grammar: 'We system' should be 'The system'.

Suggested change
We system encompasses 5 main components:
The system encompasses 5 main components:

Copilot uses AI. Check for mistakes.
Then, each NS runs **exactly** the protocol rules of the [SCP protocol](TODO). Namely:
1. Once it receives the `StartCDCP` message from the SP, it starts a timer and selects the transactions from the `xD_transactions` list that are meant for its chain.
2. Then, it simulates its transactions, meaning that it executes them with a tracer at the mailbox, so that it can intercept `mailbox.Read` and `mailbox.Write` operations.
3. Once a `mailbox.Write` operation is intercepted, it sends a `Mailbox` message to the couterparty chain sequencer (either the WS or another NS).
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'couterparty' to 'counterparty' (found in line 273's context).

Suggested change
3. Once a `mailbox.Write` operation is intercepted, it sends a `Mailbox` message to the couterparty chain sequencer (either the WS or another NS).
3. Once a `mailbox.Write` operation is intercepted, it sends a `Mailbox` message to the counterparty chain sequencer (either the WS or another NS).

Copilot uses AI. Check for mistakes.
The Shared Publisher (SP) runs a slightly modified version of the SCP protocol:
1. It sends a `StartCDCP` message to the appropriate sequencers, starts a timer and waits for NSs' `Vote` messages.
2. If a timeout occurs or if a `Vote(0)` message is received (indicates a failure), it sends a `Decided(0)` message to the NSs and a `NativeDecided(0)` message to the WS, and terminates.
3. If it receives a `Vote(1)` message from all NSs, it stops the timer (as it not longer will be used) and sends a `NativeDecided(1)` message to the WS, indicating that the NSs are willing to proceed.
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected grammar: 'as it not longer will be used' should be 'as it will no longer be used'.

Suggested change
3. If it receives a `Vote(1)` message from all NSs, it stops the timer (as it not longer will be used) and sends a `NativeDecided(1)` message to the WS, indicating that the NSs are willing to proceed.
3. If it receives a `Vote(1)` message from all NSs, it stops the timer (as it will no longer be used) and sends a `NativeDecided(1)` message to the WS, indicating that the NSs are willing to proceed.

Copilot uses AI. Check for mistakes.
// Message is valid to be read.

// Mark as used
usedKeys[key] = true;
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The read function is marked as view but attempts to modify state by setting usedKeys[key] = true (line 192). This will cause a compilation error. The function should either be changed to non-view or the state modification logic should be removed/refactored.

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +257
bytes memory data = outbox[key];

// Update the chain-specific outbox root
if (outboxRootPerChain[destChainID] == bytes32(0)) {
chainIDsOutbox.push(destChainID);
}
outboxRootPerChain[destChainID] = keccak256(
abi.encode(outboxRootPerChain[destChainID], key, data)
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable shadowing: data is declared as a function parameter (line 219) and then re-declared as a local variable (line 250). This shadows the parameter and may cause confusion. Use a different variable name for the local variable.

Suggested change
bytes memory data = outbox[key];
// Update the chain-specific outbox root
if (outboxRootPerChain[destChainID] == bytes32(0)) {
chainIDsOutbox.push(destChainID);
}
outboxRootPerChain[destChainID] = keccak256(
abi.encode(outboxRootPerChain[destChainID], key, data)
bytes memory messageData = outbox[key];
// Update the chain-specific outbox root
if (outboxRootPerChain[destChainID] == bytes32(0)) {
chainIDsOutbox.push(destChainID);
}
outboxRootPerChain[destChainID] = keccak256(
abi.encode(outboxRootPerChain[destChainID], key, messageData)

Copilot uses AI. Check for mistakes.
# Wait for mailbox message arrival from another sequencer
return

else if sim.failReason == OTHER:
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect Python syntax: should be elif instead of else if.

Suggested change
else if sim.failReason == OTHER:
elif sim.failReason == OTHER:

Copilot uses AI. Check for mistakes.
function release_snapshot_lease(store: SnapshotStore):
lock(store.mu):
store.read_lease = false
if store.read_lease == false and store.swap_pending:
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant condition check: store.read_lease is already set to false on line 681, so checking store.read_lease == false on line 682 is redundant. This condition will always be true at this point.

Suggested change
if store.read_lease == false and store.swap_pending:
if store.swap_pending:

Copilot uses AI. Check for mistakes.

Following the SBCP (v2), at the end of the superblock period, sequencers submit an `AggregationProof` to the SP,
commiting to their final state and to the associated mailbox roots.
With an external rollup, the mailbox roots from natives should also be compared to the roots stores in the ER.
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'stores' to 'stored'.

Suggested change
With an external rollup, the mailbox roots from natives should also be compared to the roots stores in the ER.
With an external rollup, the mailbox roots from natives should also be compared to the roots stored in the ER.

Copilot uses AI. Check for mistakes.
if (outboxRootPerChain[destChainID] == bytes32(0)) {
chainIDsOutbox.push(destChainID);
}
outboxRootPerChain[destChainID] = keccak256(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatheusFranco99 if it writes the new root it doesnt qualify as "staging" the message. Does it?

> Space optimizations can be made to the contract by letting the `read` and `write` calls automatically remove used messages from storage.

## Protocol

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still stick with the SP being a coordinator? Or can the sequencers do it independently

The special atomic transaction to the ER, `safe_execute`, allows an atomic execution of the mailbox staging and the main transaction.
It has the following pseudo-code:
```solidity
function safe_execute(stagedInboxMsgs, stagedOutboxMsgs, mainTx) external {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it also receive signature from the SP that the tx can be executed?

It has the following pseudo-code:
```solidity
function safe_execute(stagedInboxMsgs, stagedOutboxMsgs, mainTx) external {
// 1. Pre-populate the staged inbox messages

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should better define the responsibilities and structure of the SP

Structure:

  • A committee of operators
  • Selected, potentially, via some dPOS protocol
  • Each operator has a signing key

Responsibilities

  • Validate the simulation (the messages are correct since once we write to an ER we can't un-write it)
  • Validate no transitive dependencies
  • Sign (each member of the SP committee) that the tx can safely execute

}
```

![cdcp](./images/cdcp.png)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we discussed that the WS needs to write messages one by one during simulation because otherwise it can't guarantee the actual values during execute (say a msg returns some value from a contract)

```solidity
function safe_execute(stagedInboxMsgs, stagedOutboxMsgs, mainTx) external {
// 1. Pre-populate the staged inbox messages
for (msg in stagedInboxMsgs) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add some execution deadline? say within the same block or following block?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants