Skip to content

Commit

Permalink
Merge pull request #203 from defi-wonderland/feat/sent-back
Browse files Browse the repository at this point in the history
Adding returnedMessages primitive into L2ToL2CrossDomainMessenger
  • Loading branch information
tynes authored May 29, 2024
2 parents 6e0c912 + 310177e commit cd54ede
Showing 1 changed file with 102 additions and 2 deletions.
104 changes: 102 additions & 2 deletions 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)
- [`sendExpire` Invariants](#sendexpire-invariants)
- [`relayExpire` Invariants](#relayexpire-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 Expired Message Hashes](#sending-expired-message-hashes)
- [Relaying Expired Message Hashes](#relaying-expired-message-hashes)
- [L1Block](#l1block)
- [Static Configuration](#static-configuration)
- [Dependency Set](#dependency-set)
Expand Down Expand Up @@ -146,6 +150,7 @@ properties about the `_msg`.
|-------------------|----------------------------------------------|
| Address | `0x4200000000000000000000000000000000000023` |
| `MESSAGE_VERSION` | `uint256(0)` |
| `EXPIRY_WINDOW` | `uint256(7200)` |

The `L2ToL2CrossDomainMessenger` is a higher level abstraction on top of the `CrossL2Inbox` that
provides features necessary for secure transfers ERC20 tokens between L2 chains.
Expand All @@ -159,6 +164,21 @@ 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

### `sendExpire` Invariants

- The message MUST have not been successfully relayed
- The `EXPIRY_WINDOW` MUST have elapsed since the message first failed to be relayed
- The expired message MUST not have been previously sent back to source
- The expired message MUST not be relayable after being sent back

### `relayExpire` Invariants

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

### Message Versioning

Versioning is handled in the most significant bits of the nonce, similarly to how it is handled by
Expand Down Expand Up @@ -223,6 +243,11 @@ chain. The hash of the message is used for replay protection.
It is important to ensure that the source chain is in the dependency set of the destination chain, otherwise
it is possible to send a message that is not playable.

When a message fails to be relayed, only the timestamp at which it
first failed along with its source chain id are stored. This is
needed for calculation of the failed message's expiry. The source chain id
is also required to simplify the function signature of `sendExpire`.

```solidity
function relayMessage(uint256 _destination, uint256 _source, uint256 _nonce, address _sender, address _target, bytes memory _message) external payable {
require(msg.sender == address(CROSS_L2_INBOX));
Expand All @@ -245,15 +270,90 @@ function relayMessage(uint256 _destination, uint256 _source, uint256 _nonce, add
_calldata: _message
});
require(success);
}
if (!success) {
emit FailedRelayedMessage(messageHash);
if (failedMessages[messageHash].timestamp == 0) {
failedMessages[messageHash] = FailedMessage({timestamp: block.timestamp, sourceChainId: _source});
}
return;
}
successfulMessages[messageHash] = true;
delete failedMessages[messageHash];
emit RelayedMessage(messageHash);
};
```

Note that the `relayMessage` function is `payable` to enable relayers to earn in the gas paying asset.

To enable cross chain authorization patterns, both the `_sender` and the `_source` MUST be exposed via `public`
getters.

#### Sending Expired Message Hashes

When expiring 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 expire a given message hash.
The `EXPIRY_WINDOW` 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 expiring messages upon arrival.

Once the expired message is sent to the source chain, the message on the local chain is set
as successful in the `successfulMessages` mapping to ensure non-replayability and deleted
from `failedMessages`. An initiating message is then emitted to `relayExpire`

```solidity
function sendExpire(bytes32 _expiredHash) external nonReentrant {
if (successfulMessages[_expiredHash]) revert MessageAlreadyRelayed();
(uint256 messageTimestamp, uint256 messageSource) = failedMessages[_expiredHash];
if (block.timestamp < messageTimestamp + EXPIRY_WINDOW) revert ExpiryWindowHasNotEnsued();
delete failedMessages[_expiredHash];
successfulMessages[_expiredHash] = true;
bytes memory data = abi.encodeCall(
L2ToL2CrossDomainMessenger.expired,
(_expiredHash, messageSource)
);
emit SentMessage(data);
}
```

#### Relaying Expired Message Hashes

When relaying an expired message, only message hashes
of actual failed messages should be stored, for this we must ensure the origin
of the log, and caller are all expected contracts.

It's also important to ensure only the hashes of messages that were initiated
in this chain are accepted.

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

```solidity
function relayExpire(bytes32 _expiredHash, uint256 _messageSource) external {
if (_messageSource != block.chainid) revert IncorrectMessageSource();
if (expiredMessages[_expiredHash] != 0) revert ExpiredMessageAlreadyRelayed();
if (msg.sender != Predeploys.CROSS_L2_INBOX) revert ExpiredMessageCallerNotCrossL2Inbox();
if (CrossL2Inbox(Predeploys.CROSS_L2_INBOX).origin() != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) {
revert CrossL2InboxOriginNotL2ToL2CrossDomainMessenger();
}
expiredMessages[_expiredHash] = block.timestamp;
emit MessageHashExpired(_expiredHash);
}
```

## L1Block

| Constant | Value |
Expand Down

0 comments on commit cd54ede

Please sign in to comment.