Skip to content
Merged
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 106 additions & 1 deletion specs/interop/predeploys.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@
- [`Identifier` Getters](#identifier-getters)
- [L2ToL2CrossDomainMessenger](#l2tol2crossdomainmessenger)
- [`relayMessage` Invariants](#relaymessage-invariants)
- [`revert` Invariants](#revert-invariants)
- [`receiveMessageHash` Invariants](#receivemessagehash-invariants)
- [Message Versioning](#message-versioning)
- [No Native Support for Cross Chain Ether Sends](#no-native-support-for-cross-chain-ether-sends)
- [Interfaces](#interfaces)
- [Sending Messages](#sending-messages)
- [Relaying Messages](#relaying-messages)
- [Sending Back Messages](#sending-back-messages)
- [Storing Sent-back Messages](#storing-sent-back-messages)
- [L1Block](#l1block)
- [Static Configuration](#static-configuration)
- [Dependency Set](#dependency-set)
Expand Down Expand Up @@ -159,6 +163,24 @@ as well as domain binding, ie the executing transaction can only be valid on a s
- The `_destination` chain id MUST be equal to the local chain id
- The `CrossL2Inbox` cannot call itself

### `revert` Invariants

- The Source chain id MUST not be `block.chainid`
- The `_destination` chain id MUST be equal to the local chain id
- The message MUST have not been successfully relayed
- The `RETURN_DELAY` MUST have elapsed since the message first failed to be relayed
- The sent-back message MUST not have been previously relayed
- The sent-back message MUST not be relayable after being sent back

### `receiveMessageHash` Invariants

- Only callable by the `CrossL2Inbox`
- The message source MUST be `block.chainid`
- The sender MUST be `address(L2ToL2CrossDomainMessenger)`
- The `Identifier.origin` MUST be `address(L2ToL2CrossDomainMessenger)`
- The `Identifier.chainId` MUST be `_destination`
- The `returnedMessages` mapping MUST only contain messages that originated in this chain and failed to be relayed on destination.

Comment thread
tynes marked this conversation as resolved.
### Message Versioning

Versioning is handled in the most significant bits of the nonce, similarly to how it is handled by
Expand Down Expand Up @@ -245,7 +267,19 @@ function relayMessage(uint256 _destination, uint256 _source, uint256 _nonce, add
_calldata: _message
});

Comment thread
tynes marked this conversation as resolved.
require(success);
if (success) {
successfulMessages[messageHash] = true;
delete failedMessages[messageHash];
emit RelayedMessage(messageHash);
} else {
// Will set the timestamp only once on the *first* failed execution to protect against extending
// the time-window and blocking the rollback.
Comment thread
tynes marked this conversation as resolved.
Outdated
if (failedMessages[messageHash].timestamp == 0) {
// sourceChainId is needed to revert the message without having to recreate the digest from the message preimage.
failedMessages[messageHash] = FailedMessage({timestamp: block.timestamp, sourceChainId: _source});
}
emit FailedRelayedMessage(messageHash);
}
}
```

Expand All @@ -254,6 +288,77 @@ Note that the `relayMessage` function is `payable` to enable relayers to earn in
To enable cross chain authorization patterns, both the `_sender` and the `_source` MUST be exposed via `public`
getters.

#### Sending Back Messages Hashes

When sending back a message that failed to be relayed on the destination chain
to the source chain, it's crucial to ensure the message can only be sent back
to the `L2ToL2CrossDomainMessenger` contract in its source chain.

This function has no auth, which allows anyone to send back a given message hash.
The `RETURN_DELAY` variable is added to give the users enough time to replay their
failed messages and to prevent malicious actors from performing a griefing attack
by sending messages back immediately.

Once the message is returned to the source chain, the message on the local chain is set
as successful in the `successfulMessages` mapping to ensure non-replayability.

```solidity
function revert(bytes32 _messageHash) external nonReentrant {
if (successfulMessages[messageHash]) revert MessageAlreadyRelayed();

(uint256 messageTimestamp, uint256 messageSource) = failedMessages[messageHash];

// Return delay is necessary to avoid griefing attacks by providing users with time to
// replay their transactions give this function has no auth
if (block.timestamp < messageTimestamp + RETURN_DELAY) revert DelayHasNotEnsued();
Comment thread
tynes marked this conversation as resolved.
Outdated

delete failedMessages[messageHash];

// This is set to true to ensure non-replayability on this chain
successfulMessages[messageHash] = true;

bytes memory data = abi.encodeCall(
L2ToL2CrossDomainMessenger.receiveMessageHash,
(messageSource, block.chainid, Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, messageHash)
);
emit SentMessage(data);
}
```

#### Storing Sent-back Message Hashes

When receiving a sent-back message only message hashes
of actual failed messages should be stored, for this we must ensure the origin,
sender and caller are all the expected contracts.

It's also important to ensure only the hashes of messages that were initiated
in this chain are accepted and that they come from the correct destination
chain id.

If all checks have been successful, the message has is stored in the
`returnedMessages` mapping. This enables smart contracts to read from it and
check whether a message failed or not, and handle the failing case accordingly.

```solidity
function receiveMessageHash(uint256 _messageSource, uint256 _messageDestination, address _sender, bytes32 _messageHash) external {
Comment thread
tynes marked this conversation as resolved.
Outdated
Comment thread
tynes marked this conversation as resolved.
Outdated
if (_messageSource != block.chainid) revert IncorrectMessageSource();
Comment thread
tynes marked this conversation as resolved.
if (_sender != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert SenderIsNotCrossDomainMesenger();
if (msg.sender != Predeploys.CROSS_L2_INBOX) revert ReceiveMessageHashCallerNotCrossL2Inbox();

if (CrossL2Inbox(Predeploys.CROSS_L2_INBOX).origin() != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) {
revert CrossL2InboxOriginNotL2ToL2CrossDomainMessenger();
}

Comment thread
tynes marked this conversation as resolved.
if (CrossL2Inbox(Predeploys.CROSS_L2_INBOX).chainid() != _messageDestination) {
revert CrossL2InboxDestinationDoesntMatch();
}

returnedMessages[_messageHash] = block.timestamp;
Comment thread
tynes marked this conversation as resolved.
Outdated

emit MessageHashReceived(_messageHash);
Comment thread
tynes marked this conversation as resolved.
Outdated
}
```

## L1Block

| Constant | Value |
Expand Down