Skip to content

Commit

Permalink
Merge pull request #149 from ethereum-optimism/feat/interop-3074
Browse files Browse the repository at this point in the history
specs: interop 3074 updates
  • Loading branch information
tynes authored May 15, 2024
2 parents 813b2af + 09692b8 commit 08d3c71
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 51 deletions.
36 changes: 11 additions & 25 deletions specs/interop/messaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
- [Messaging Invariants](#messaging-invariants)
- [Timestamp Invariant](#timestamp-invariant)
- [ChainID Invariant](#chainid-invariant)
- [Only EOA Invariant](#only-eoa-invariant)
- [Message Expiry Invariant](#message-expiry-invariant)
- [Message Graph](#message-graph)
- [Invalid messages](#invalid-messages)
Expand Down Expand Up @@ -89,7 +88,7 @@ exact state of the block templates between multiple chains together.

[log]: https://github.com/ethereum/go-ethereum/blob/5c67066a050e3924e1c663317fd8051bc8d34f43/core/types/log.go#L29

Each Log (also known as `event` in solidity) forms an initiating message.
Each [Log][log] (also known as `event` in solidity) forms an initiating message.
The raw log data froms the [Message Payload](#message-payload).

Messages are *broadcast*: the protocol does not enshrine address-targeting within messages.
Expand All @@ -101,21 +100,26 @@ An initiating message may be executed many times: no replay-protection is enshri

### Executing Messages

All the information required to satisfy the invariants MUST be included in the calldata
of the function that is used to execute messages.
An executing message is represented by the [ExecutingMessage event][event] that is emitted by
the `CrossL2Inbox` predeploy. This event is coupled to a `CALL` with the payload that is emitted
within the event, allowing introspection of the data without needing to do call tracing.
All of the information required to satisfy the invariants MUST be included in this event.

[event]: ./predeploys.md#executingmessage-event

Both the block builder and the verifier use this information to ensure that all system invariants are held.

The executing message is verified by checking if there is an existing initiating-message
that originates at [`Identifier`] with matching [Message Payload](#message-payload).

Since an executing message is defined by a log, it means that reverting calls to the `CrossL2Inbox`
do not count as executing messages.

## Messaging Invariants

- [Timestamp Invariant](#timestamp-invariant): The timestamp at the time of inclusion of the executing message MUST
be greater than or equal to the timestamp of the initiating message.
- [ChainID Invariant](#chainid-invariant): The chain id of the initiating message MUST be in the dependency set
- [Only EOA Invariant](#only-eoa-invariant): The executing message MUST be initiated by an externally owned
account such that the top level EVM call frame enters the `CrossL2Inbox`
- [Message Expiry Invariant](#message-expiry-invariant): The timestamp at the time of inclusion of the executing
message MUST be lower than the initiating message timestamp (as defined in the [`Identifier`]) + `EXPIRY_TIME`.

Expand All @@ -131,24 +135,6 @@ Without a guarantee on the set of dependencies of a chain, it may be impossible
pipeline to know which chain to source the initiating message from. This also allows for chain operators
to explicitly define the set of chains that they depend on.

### Only EOA Invariant

The `onlyEOA` invariant on executing a cross chain message enables static analysis on executing messages.
This allows for the derivation pipeline and block builders to reject executing messages that do not
have a corresponding initiating message without needing to do any EVM execution.

It may be possible to relax this invariant in the future if the block building process is efficient
enough to do full simulations to gain the information required to verify the existence of the
initiating transaction. Instead of the [`Identifier`] being included in calldata, it would be emitted
in an event that can be used after the fact to verify the existence of the initiating message.
This adds complexity around mempool inclusion as it would require EVM execution and remote RPC
access to learn if a transaction can enter the mempool.

This feature could be added in a backwards compatible way by adding a new function to the `CrossL2Inbox`.

One possible way to handle explicit denial of service attacks is to utilize identity
in iterated games such that the block builder can ban identities that submit malicious transactions.

### Message Expiry Invariant

Note: Message Expiry as property of the protocol is in active discussion.
Expand Down Expand Up @@ -260,7 +246,7 @@ The graph is bounded in 4 ways:
as per the [ChainID invariant](#chainid-invariant).
- Every block cannot depend on future blocks, as per the [Timestamp invariant](#timestamp-invariant).
- Every block has a maximum gas limit, an intrinsic cost per transaction,
and thus a maximum inward degree of dependencies, as per the [Only-EOA invariant](#only-eoa-invariant)
and thus a maximum inward degree of dependencies.
- Every block cannot depend on expired messages, as per the [Message expiry invariant](#message-expiry-invariant).

The verifier is responsible for filtering out non-canonical parts of the graph.
Expand Down
4 changes: 2 additions & 2 deletions specs/interop/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ network upgrade will be included in this document in the future.
| Source Chain | A blockchain that includes an initiating message |
| Destination Chain | A blockchain that includes an executing message |
| Initiating Message | An event emitted from a source chain |
| Executing Message | A transaction submitted to a destination chain that corresponds to an initiating message |
| Executing Message | An event emitted from a destination chain's `CrossL2Inbox` that includes an initiating message |
| Cross Chain Message | The cumulative execution and side effects of the initiating message and executing message |
| Dependency Set | The set of chains that originate initiating transactions where the executing transactions are valid |

A total of two transactions are required to complete a cross chain message.
The first transaction is submitted to the source chain and emits an event that can be consumed on a destination chain.
The second transaction is submitted to a destination chain, where the block builder SHOULD only include it if they are
certain that the first transaction was included in the source chain.
There is no strict requirement that the execution message is ever submitted.
There is no strict requirement that the executing message is ever submitted.

The term "block builder" is used interchangeably with the term "sequencer" for the purposes of this document but
they need not be the same entity in practice.
Expand Down
25 changes: 22 additions & 3 deletions specs/interop/predeploys.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [`_msg`](#_msg)
- [`_id`](#_id)
- [`_target`](#_target)
- [`ExecutingMessage` Event](#executingmessage-event)
- [Reference implementation](#reference-implementation)
- [`Identifier` Getters](#identifier-getters)
- [L2ToL2CrossDomainMessenger](#l2tol2crossdomainmessenger)
Expand Down Expand Up @@ -83,13 +84,29 @@ In practice, the `_target` will be a contract that needs to know the schema of t
It MAY call back to the `CrossL2Inbox` to authenticate
properties about the `_msg` using the information in the `Identifier`.

### `ExecutingMessage` Event

The `ExecutingMessage` event represents an executing message. It MUST be emitted on every call
to `executeMessage`.

```solidity
event ExecutingMessage(bytes,bytes);
```

The data encoded in the event contains the `Identifier` and the `msg`.
The following pseudocode shows the serialization:

```solidity
(bytes memory identifier, bytes memory log) = abi.decode(receipt.data, (bytes, bytes));
Identifier id = abi.decode(identifier, (Identifier));
```

### Reference implementation

A simple implementation of the `executeMessage` function is included below.

```solidity
function executeMessage(Identifier calldata _id, address _target, bytes calldata _msg) public payable {
require(msg.sender == tx.origin);
require(_id.timestamp <= block.timestamp);
require(L1Block.isInDependencySet(_id.chainid));
Expand All @@ -108,6 +125,8 @@ function executeMessage(Identifier calldata _id, address _target, bytes calldata
});
require(success);
emit ExecutingMessage(abi.encode(_id), _msg);
}
```

Expand Down Expand Up @@ -215,15 +234,15 @@ function relayMessage(uint256 _destination, uint256 _source, uint256 _nonce, add
tstore(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT, _source)
}
sentMessages[messageHash] = true;
bool success = SafeCall.call({
_target: _target,
_value: msg.value,
_calldata: _message
});
require(success);
sentMessages[messageHash] = true;
}
```

Expand Down
48 changes: 28 additions & 20 deletions specs/interop/sequencer.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,15 @@ without adding additional state-transition complexity or cross-chain synchronici
## Block Building

The goal is to present information in a way where it is as efficient as possible for the block builder to only include
executing messages that have a corresponding initiating message.
executing messages that have a corresponding initiating message. It is not possible to enforce the ability to
statically analyze a transaction, so execution MAY be required to determine the information required to include
executing messages.

### Static analysis

The block builder SHOULD use static analysis on executing messages to determine the dependency of the message.

When a transaction has a top level [to][tx-to] field that is equal to the `CrossL2Inbox`
and the 4-byte selector in the calldata matches the entrypoint interface,
the block builder should use the chain-ID that is encoded in the `Identifier` to determine which chain includes
the initiating transaction.
Note that static analysis is never reliable because even if the top level `transaction.to`
is equal to the `CrossL2Inbox`, it is possible that there is a reentrant `CALL`. The block
builder SHOULD NOT rely on static analysis for building blocks.

### Dependency confirmations

Expand Down Expand Up @@ -94,27 +93,36 @@ The block builder MAY also trust a remote RPC and use the following algorithm
to verify the existence of the log.

The following pseudocode represents how to check existence of a log based on an `Identifier`.
If the value `True` is returned, then it is safe to include the transaction.

```python
target, message, id = abi.decode(calldata)
success, receipt = evm.apply_transaction(tx)

if not success:
return True

for log in receipt.logs:
if is_executing_message(log):
id, message = abi.decode(log.data)

eth = clients[id.chainid]
# assumes there is a client for each chain in the dependency set
eth = clients[id.chainid]

if eth is None:
return False
if eth is None:
return False

logs = eth.getLogs(id.origin, from=id.blocknumber, to=id.blocknumber)
log = filter(lambda x: x.index == id.logIndex && x.address == id.origin)
if len(log) == 0:
return False
logs = eth.getLogs(id.origin, from=id.blocknumber, to=id.blocknumber)
log = filter(lambda x: x.index == id.logIndex && x.address == id.origin)
if len(log) == 0:
return False

if message != encode(log[0]):
return False
if message != encode(log[0]):
return False

block = eth.getBlockByNumber(id.blocknumber)
block = eth.getBlockByNumber(id.blocknumber)

if id.timestamp != block.timestamp:
return False
if id.timestamp != block.timestamp:
return False

return True
```
Expand Down
4 changes: 3 additions & 1 deletion specs/interop/tx_pool.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ However, additional validation rules are applied to demote messages that cannot
## Message validation

Through [static-analysis](./sequencer.md#static-analysis) as performed in block building,
the [`Identifier`] of the message is read, and used for further validation.
the [`Identifier`] of the message is read, and used for further validation. Static analysis is
not always possible, therefore the mempool SHOULD delegate execution to another service that can
horizontally scale validation of executing messages.

The [messaging invariants](./messaging.md#messaging-invariants) should be enforced in the transaction pool,
with dependency validation adapted for guarantees of the in-flight transactions:
Expand Down

0 comments on commit 08d3c71

Please sign in to comment.