Message Expiry
Table of Contents
- Overview
- Flow
- Safety
- Assumptions
- Invariants
- iME-001: A message is never both relayed and expired
- iME-002: Only the destination chain can attest
- iME-003: Only two functions send as the L1CrossDomainMessenger
- iME-004: Expiry is final
- iME-005: A message that is never relayed can always expire
- iME-006: Each message is emitted once and every relay is recorded
- Activation
Overview
Executing messages cannot be included through deposit transactions (see Depositing an Executing Message). A destination chain's sequencer that refuses to relay a message can therefore keep it from ever being delivered. Once the expiry window has passed, no valid block can relay the message at all.
Message expiry lets the source chain learn that a message can never be delivered, using only withdrawals and
deposits, which a sequencer cannot censor. The source chain's L2ToL2CrossDomainMessenger records this per message
in expiredMessages, and applications can undo the send, as
SuperchainETHBridge.refundETH does.
This page describes the design and why it is safe. The contract functions are specified with their contracts:
L2ToL2CrossDomainMessenger:sentMessageTimestamps,expiredMessages,expireMessageandEXPIRY_PERIOD;UndeliveredMessageExporter:exportUndeliveredMessage;L1CrossDomainMessenger:relayUndeliveredMessage.
Flow
For a message sent from a source chain to a destination chain and never relayed there:
- On the source chain,
L2ToL2CrossDomainMessenger.sendMessagerecordssentMessageTimestamps[messageHash]. - On the destination chain, anyone calls
UndeliveredMessageExporter.exportUndeliveredMessagewith the message's preimage and the source chain'sL1CrossDomainMessenger. The exporter computesmessageHashwith the destination chain as the destination, checks that the destination chain has not relayed it, and sendsrelayUndeliveredMessage(messageHash, undeliveredAt)through the destination chain'sL2CrossDomainMessenger, whereundeliveredAtis the destination chain's block timestamp. This is not an executing message, so it can be included through a deposit if the destination chain's sequencer censors it. - On L1, once the withdrawal is proven and finalized, the destination chain's
L1CrossDomainMessengerrelays it to the source chain'sL1CrossDomainMessenger.relayUndeliveredMessage, which sendsexpireMessage(messageHash, undeliveredAt)to the source chain as a deposit. - On the source chain,
expireMessagemarks the message expired ifundeliveredAt > sentMessageTimestamps[messageHash] + EXPIRY_PERIOD. - On the source chain, applications read
expiredMessages(messageHash)to undo the send.
sequenceDiagram
participant Source as Source chain
participant L1 as L1
participant Dest as Destination chain
Source->>Source: sendMessage records sentMessageTimestamps[messageHash]
Note over Dest: the message is never relayed
Dest->>L1: exportUndeliveredMessage withdraws relayUndeliveredMessage(messageHash, undeliveredAt)
L1->>L1: destination's L1CrossDomainMessenger relays to source's L1CrossDomainMessenger
L1->>Source: deposit expireMessage(messageHash, undeliveredAt)
Source->>Source: expiredMessages[messageHash] = true if undeliveredAt > sentMessageTimestamps[messageHash] + EXPIRY_PERIOD
Safety
A message is never both relayed and expired (iME-001). This holds because:
-
An executing message is invalid if its block timestamp is more than
EXPIRY_WINDOWafter the timestamp of its initiating message (see Message Expiry Invariant).sentMessageTimestamps[messageHash]is that initiating timestamp, and it is written once. -
The exporter on the destination chain attests that the message was not relayed by its block timestamp
undeliveredAt. Any later call on the destination chain, including one later in the same block or the same transaction, has a timestamp of at leastundeliveredAt. IfundeliveredAt > sentMessageTimestamps[messageHash] + EXPIRY_PERIODandEXPIRY_PERIODis at leastEXPIRY_WINDOW(aME-002), no later call can relay the message. -
Only the destination chain can attest for the message, because the exporter hashes it with its own chain ID as the destination (iME-002).
-
Only chains whose portals the source chain's
ETHLockboxauthorizes can attest (aME-001). -
Only
relayUndeliveredMessagecan make the source chain'sL1CrossDomainMessengerthe sender ofexpireMessage(iME-003). -
The trusted L2 sender is the
UndeliveredMessageExporter, whose proxy had no implementation before the upgrade that introduced it, so no withdrawal from it can predate that upgrade. -
The source chain emits each message once, and the destination chain records every relay (iME-006).
Assumptions
aME-001: Authorized chains run standard contracts
We assume that every chain whose portal the source chain's ETHLockbox authorizes runs the standard
UndeliveredMessageExporter, L2ToL2CrossDomainMessenger, L2CrossDomainMessenger and L2ToL1MessagePasser at
their predeploy addresses. We assume that L1 finalizes withdrawals only from blocks that are valid under the interop
derivation rules.
relayUndeliveredMessage does not compare the calling chain with the message's destination. The binding comes only
from the exporter, which hashes the message with its own chain ID as the destination, so this assumption is
load-bearing.
Authorization of a portal in an ETHLockbox is permanent. A chain whose portal moves to another ETHLockbox stays
authorized in the source chain's ETHLockbox. It can still attest for expiry, but it no longer shares the source
chain's ETH or pause.
Mitigations
- Only the
ProxyAdminowner can authorize a portal in anETHLockbox(seeETHLockbox).
aME-002: The expiry period is at least the expiry window
We assume that every node and proof program of each destination chain enforces the
expiry window of EXPIRY_WINDOW. We assume that each chain's EXPIRY_PERIOD is
at least EXPIRY_WINDOW and stays so across upgrades. The messenger cannot check this: initialize checks only
0 < EXPIRY_PERIOD <= 365 days.
Mitigations
EXPIRY_WINDOWis a protocol constant, not a configuration value.- On production networks,
EXPIRY_PERIODis one day more thanEXPIRY_WINDOW, and network upgrades keep it (see Expiring Messages).
aME-003: Applications bind expiry to their own sends
We assume that an application that undoes a send on expiry computes the message hash with itself as the sender. We
assume that it undoes each send at most once and treats an expired message as never delivered. expiredMessages is
public, so an application that trusts a message hash from its caller can be fooled.
Mitigations
SuperchainETHBridge.refundETHis the reference pattern: it computes the hash from its own arguments, with itself as the sender, and records each refund.
aME-004: Every chain in the dependency set is authorized
We assume that the source chain's ETHLockbox authorizes the portal of every chain in the source chain's
dependency set, and that each such chain runs the UndeliveredMessageExporter. A message to
a chain whose portal the ETHLockbox does not authorize cannot expire until the ETHLockbox authorizes it.
Mitigations
- The network upgrade that activates interop installs the
UndeliveredMessageExporteron every chain. - A chain joins a dependency set only through a network upgrade.
Invariants
iME-001: A message is never both relayed and expired
A message MUST NOT be both relayed on its destination chain and marked expired in expiredMessages on its source
chain.
Impact
Severity: Critical
If this invariant is broken, every application that undoes a send on expiry pays out twice. The
SuperchainETHBridge would both relay and refund the same ETH.
Dependencies
iME-002: Only the destination chain can attest
expireMessage MUST mark a message expired only on an attestation from the UndeliveredMessageExporter of the chain
that the message hash names as its destination.
Impact
Severity: Critical
If this invariant is broken, another chain could attest that a message was not relayed after its destination relayed it, breaking iME-001.
Dependencies
iME-003: Only two functions send as the L1CrossDomainMessenger
This chain's L1CrossDomainMessenger MUST be the L1 sender of a message on L2 only through
relayUndeliveredMessage and
syncETHLiquidityPause. Its
relayMessage refuses messages that target
itself.
Impact
Severity: Critical
expireMessage and ETHLiquidity.setPaused both trust this sender. If this invariant is broken, anyone could
expire a message that can still be relayed, or pause and unpause ETHLiquidity.
Dependencies
None
iME-004: Expiry is final
expiredMessages[messageHash] MUST change only from false to true, and only for a message with a recorded send
timestamp.
Impact
Severity: High
If this invariant is broken, an application could see a send that it already undid become deliverable again, or a message that was never sent from this chain marked expired.
Dependencies
None
iME-005: A message that is never relayed can always expire
Anyone MUST be able to make a message expire once its destination chain has not relayed it at a block timestamp
greater than sentMessageTimestamps[messageHash] + EXPIRY_PERIOD. This requires a destination chain whose portal
the source chain's ETHLockbox authorizes, withdrawals that finalize on L1, and deposits that are included on the
source chain. Each step that fails, such as for gas or while a chain is paused, MUST be replayable or repeatable from
a new export.
This does not cover a message with no recorded send timestamp, such as one sent before the messenger recorded
timestamps. Such a message can never expire. It also does not cover a message to a chain whose portal the source chain's
ETHLockbox does not authorize, until the ETHLockbox authorizes it.
Impact
Severity: High
If this invariant is broken, value sent in a message that is never relayed, such as ETH sent through the
SuperchainETHBridge, can never be recovered.
Dependencies
iME-006: Each message is emitted once and every relay is recorded
The L2ToL2CrossDomainMessenger MUST NOT emit a SentMessage event for a message hash more than once. Every way of
relaying a message MUST set successfulMessages[messageHash].
Impact
Severity: Critical
If this invariant is broken, a message could be relayed after it expired, or the exporter could attest that a relayed message was not relayed. Both break iME-001.
Dependencies
None
Activation
- The network upgrade that activates interop MUST install an
L2ToL2CrossDomainMessengerwith expiry and theUndeliveredMessageExporter. - An
L2ToL2CrossDomainMessengerwithout expiry MUST NOT be live on a chain with expiry, because it could resend a message after the message expired.