From ee85a0cf730a91fecb1b102d5d558addecf5dcc5 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Thu, 25 Jan 2024 17:44:41 -0600 Subject: [PATCH 1/4] interop: specs The interop spec after many wip commits and rounds of feedback, squashed into a single commit. Based off of https://github.com/ethereum-optimism/specs/pull/6/commits/3db04186ce50136cad9a0601492f900df4c12ea3 Co-authored-by: protolambda Co-authored-by: Matt Solomon Co-authored-by: Wyatt Barnes Co-authored-by: Liam Horne --- specs/SUMMARY.md | 10 ++ specs/interop/README.md | 39 +++++ specs/interop/dependency_set.md | 85 ++++++++++ specs/interop/execution.md | 52 ++++++ specs/interop/fault_proof.md | 24 +++ specs/interop/messaging.md | 281 +++++++++++++++++++++++++++++++ specs/interop/predeploys.md | 260 ++++++++++++++++++++++++++++ specs/interop/rollup_node_p2p.md | 21 +++ specs/interop/sequencer.md | 145 ++++++++++++++++ specs/interop/tx_pool.md | 60 +++++++ specs/interop/upgrade.md | 32 ++++ specs/interop/verifier.md | 159 +++++++++++++++++ specs/root.md | 1 + 13 files changed, 1169 insertions(+) create mode 100644 specs/interop/README.md create mode 100644 specs/interop/dependency_set.md create mode 100644 specs/interop/execution.md create mode 100644 specs/interop/fault_proof.md create mode 100644 specs/interop/messaging.md create mode 100644 specs/interop/predeploys.md create mode 100644 specs/interop/rollup_node_p2p.md create mode 100644 specs/interop/sequencer.md create mode 100644 specs/interop/tx_pool.md create mode 100644 specs/interop/upgrade.md create mode 100644 specs/interop/verifier.md diff --git a/specs/SUMMARY.md b/specs/SUMMARY.md index 949449899..572fa7fab 100644 --- a/specs/SUMMARY.md +++ b/specs/SUMMARY.md @@ -32,4 +32,14 @@ - [Honest Challenger](./experimental/fault-proof/honest-challenger-fdg.md) - [Bond Incentives](./experimental/fault-proof/bond-incentives.md) - [Plasma](./experimental/plasma.md) + - [Interoperability](./interop/README.md) + - [Dependency Set](./interop/dependency_set.md) + - [Messaging](./interop/messaging.md) + - [Predeploys](./interop/predeploys.md) + - [Execution](./interop/execution.md) + - [Sequencer](./interop/sequencer.md) + - [Verifier](./interop/verifier.md) + - [Rollup Node P2P](./interop/rollup_node_p2p.md) + - [Fault Proof](./interop/fault_proof.md) + - [Upgrade](./interop/upgrade.md) - [Glossary](./glossary.md) diff --git a/specs/interop/README.md b/specs/interop/README.md new file mode 100644 index 000000000..726175bcc --- /dev/null +++ b/specs/interop/README.md @@ -0,0 +1,39 @@ + + +# Interop + +The ability for a blockchain to easily read the state of another blockchain is called interoperability. +Low latency interoperability allows for horizontally scalable blockchains, a key feature of the superchain. + +Note: this document references an "interop network upgrade" as a temporary name. The actual name of the +network upgrade will be included in this document in the future. + +| Term | Definition | +|---------------------|-----------------------------------------------------------------------------------------------------| +| 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 | +| 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. + +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. + +## Specifications + +- [Dependency Set](./dependency_set.md): definition of chains and chain-dependencies in the Superchain. +- [Messaging](./messaging.md): messaging functionality, core of protocol-level interoperability. +- [Predeploys](./predeploys.md): system contracts to interface with other chains. +- [Execution](./execution.md): enhancements to the execution-layer. +- [Sequencer](./sequencer.md): Sequencer Policy and block-building information. +- [Verifier](./verifier.md): Verification of cross-L2 messaging. +- [Rollup Node P2P](./rollup_node_p2p.md): modifications to the rollup-node P2P layer to support fast interop. +- [Fault Proof](./fault_proof.md): modifications to prove interop functionality in the fault-proof. +- [Upgrade](./upgrade.md): Superchain upgrade process to activate Interop. diff --git a/specs/interop/dependency_set.md b/specs/interop/dependency_set.md new file mode 100644 index 000000000..738c66e02 --- /dev/null +++ b/specs/interop/dependency_set.md @@ -0,0 +1,85 @@ +# The Dependency Set + + + +**Table of Contents** + +- [Chain ID](#chain-id) +- [Updating the Dependency Set](#updating-the-dependency-set) + - [`DEPENDENCY_SET` UpdateType](#dependency_set-updatetype) +- [Security Considerations](#security-considerations) + - [Dynamic Size of L1 Attributes Transaction](#dynamic-size-of-l1-attributes-transaction) + - [Maximum Size of the Dependency Set](#maximum-size-of-the-dependency-set) + - [Layer 1 as Part of the Dependency Set](#layer-1-as-part-of-the-dependency-set) + + + +The dependency set defines the set of chains that a destination chains allows as source chains. Another way of +saying it is that the dependency set defines the set of initiating messages that are valid for an executing +message to be included. An executing message MUST have an initiating message that is included in a chain +in the dependency set. + +The dependency set is defined by a set of chain ids. Since it is impossible to enforce uniqueness of chain ids, +social consensus MUST be used to determine the chain that represents the canonical chain id. This +particularly impacts the block builder as they SHOULD use the chain id to assist in validation +of executing messages. + +The dependency set is configured on a per chain basis. + +The chainid of the local chain MUST be considered as part of its own dependency set. + +While the dependency set explicitly defines the set of chains that are depended on for incoming messages, +the full set of transitive dependencies must be known to allow for the progression of [safety](#safety). +This means that the `op-node` needs to be aware of all transitive dependencies. + +## Chain ID + +The concept of a chain id was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155) to prevent +replay attacks between chains. This EIP does not specify the max size of a chain id, although +[EIP-2294](https://eips.ethereum.org/EIPS/eip-2294) attempts to add a maximum size. Since this EIP is +stagnant, all representations of chain ids MUST be the `uint256` type. + +In the future, OP Stack chains reserve the right to use up to 32 bytes to represent a chain id. The +configuration of the chain should deterministically map to a chain id and with careful architecture +changes, all possible OP Stack chains in the superchain will be able to exist counterfactually. + +It is a known issue that not all software in the Ethereum can handle 32 byte chain ids. + +## Updating the Dependency Set + +The `SystemConfig` is updated to manage the dependency set. The chain operator can add or remove +chains from the dependency set through the `SystemConfig`. A new `ConfigUpdate` event `UpdateType` +enum is added that corresponds to a change in the dependency set. + +The `SystemConfig` MUST enforce that the maximum size of the dependency set is `type(uint8).max` or 255. + +### `DEPENDENCY_SET` UpdateType + +When a `ConfigUpdate` event is emitted where the `UpdateType` is `DEPENDENCY_SET`, the L2 network will +update its dependency set. The chain operator SHOULD be able to add or remove chains from the dependency set. + +## Security Considerations + +### Dynamic Size of L1 Attributes Transaction + +The L1 Attributes transaction includes the dependency set which is dynamically sized. This means that +the worst case (largest size) transaction must be accounted for when ensuring that it is not possible +to create a block that has force inclusion transactions that go over the L2 block gas limit. +It MUST be impossible to produce an L2 block that consumes more than the L2 block gas limit. +Limiting the dependency set size is an easy way to ensure this. + +### Maximum Size of the Dependency Set + +The maximum size of the dependency set is constrained by the L2 block gas limit. The larger the dependency set, +the more costly it is to fully verify the network. It also makes the block building role more centralized +as it requires more hardware to verify executing transactions before inclusion. + +### Layer 1 as Part of the Dependency Set + +The layer one MAY be part of the dependency set if the fault proof implementation is set up +to support it. It is known that it is possible but it is not known if this is going to be +a feature of the first release. This section should be clarified when the decision is made. + +If layer one is part of the dependency set, then it means that any event on L1 can be pulled +into any L2. This is a very powerful abstraction as a minimal amount of execution can happen +on L1 which triggers additional exeuction across all L2s in the OP Stack. diff --git a/specs/interop/execution.md b/specs/interop/execution.md new file mode 100644 index 000000000..20424cef8 --- /dev/null +++ b/specs/interop/execution.md @@ -0,0 +1,52 @@ +# Execution + + + +**Table of Contents** + +- [Overview](#overview) +- [Security Considerations](#security-considerations) + + + +## Overview + +This is an experimental section and may be changed in the future. It is not required +for the initial release. + +After the full execution of a block, the set of [logs][log] that are emitted MUST be merklized and +included in the block header. This commitment MUST be included as the block header's +[extra data field][block-extra-data]. The events are serialized with using [Simple Serialize][ssz] aka SSZ. + +The goal of committing to data in the extra data field is to make commitments to data required to +verify executing messages easier. The commitment could also commit to the set of executing messages +instead of just the logs which represent all possible initiating messages that originate in the block. +This would reduce rounds of communication for nodes that use proofs to verify executing messages. + +[log]: https://github.com/ethereum/go-ethereum/blob/5c67066a050e3924e1c663317fd8051bc8d34f43/core/types/log.go#L29 +[block-extra-data]: https://github.com/ethereum/execution-specs/blob/1fed0c0074f9d6aab3861057e1924411948dc50b/src/ethereum/frontier/fork_types.py#L115 +[ssz]: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md + +| Name | Value | +|--------------------------|-------------------------------| +| `MAX_LOG_DATA_SIZE` | `uint64(2**24)` or 16,777,216 | +| `MAX_MESSAGES_PER_BLOCK` | `uint64(2**20)` or 1,048,576 | + +```python +class Log(Container): + address: ExecutionAddress + topics: List[bytes32, 4] + data: ByteList[MAX_LOG_DATA_SIZE] + transaction_hash: bytes32 + transaction_index: uint64 + log_index: uint64 + +logs = List[Log, MAX_MESSAGES_PER_BLOCK]( + log_0, log_1, log_2, ...) + +block.extra_data = logs.hash_tree_root() +``` + +## Security Considerations + +TODO diff --git a/specs/interop/fault_proof.md b/specs/interop/fault_proof.md new file mode 100644 index 000000000..d3a6667bd --- /dev/null +++ b/specs/interop/fault_proof.md @@ -0,0 +1,24 @@ +# Fault Proof + + + +**Table of Contents** + +- [Security Considerations](#security-considerations) + - [Cascading dependencies](#cascading-dependencies) + + + +TODO + +No changes to the dispute game bisection are required. The only changes required are to the fault proof program itself. +The insight is that the fault proof program can be a superset of the state transition function. + +## Security Considerations + +TODO + +### Cascading dependencies + +Deposits are a special case, synchronous with derivation, at enforced cross-L2 delay. +Thus deposits cannot reference each others events intra-block. diff --git a/specs/interop/messaging.md b/specs/interop/messaging.md new file mode 100644 index 000000000..2b228b990 --- /dev/null +++ b/specs/interop/messaging.md @@ -0,0 +1,281 @@ +# Messaging + + + +**Table of Contents** + +- [Message](#message) + - [Message payload](#message-payload) + - [Message Identifier](#message-identifier) +- [Messaging ends](#messaging-ends) + - [Initiating Messages](#initiating-messages) + - [Executing Messages](#executing-messages) +- [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) + - [Block reorgs](#block-reorgs) + - [Block Recommits](#block-recommits) + - [Intra-block messaging: cycles](#intra-block-messaging-cycles) + - [Resolving cross-chain safety](#resolving-cross-chain-safety) + - [Horizon timestamp](#horizon-timestamp) + - [Pruning the graph](#pruning-the-graph) + - [Bounding the graph](#bounding-the-graph) +- [Security Considerations](#security-considerations) + - [Cyclic dependencies](#cyclic-dependencies) + - [Transitive dependencies](#transitive-dependencies) + + + +## Message + +A message is a [broadcast payload](#message-payload) emitted from an [identified source](#message-identifier). + +### Message payload + +Opaque `bytes` that represent a [Log][log]. + +It is serialized by first concatenating the topics and then with the data. + +```go +msg := make([]byte, 0) +for _, topic := range log.Topics { + msg = append(msg, topic.Bytes()...) +} +msg = append(msg, log.Data...) +``` + +The `_msg` can easily be decoded into a Solidity `struct` with `abi.decode` since each topic is always 32 bytes and +the data generally ABI encoded. + +### Message Identifier + +[`Identifier`]: #message-identifier + +The [`Identifier`] that uniquely represents a log that is emitted from a chain. It can be considered to be a +unique pointer to a particular log. The derivation pipeline and fault proof program MUST ensure that the +`_msg` corresponds exactly to the log that the [`Identifier`] points to. + +```solidity +struct Identifier { + address origin; + uint256 blocknumber; + uint256 logIndex; + uint256 timestamp; + uint256 chainid; +} +``` + +| Name | Type | Description | +|---------------|-----------|---------------------------------------------------------------------------------| +| `origin` | `address` | Account that emits the log | +| `blocknumber` | `uint256` | Block number in which the log was emitted | +| `logIndex` | `uint256` | The index of the log in the array of all logs emitted in the block | +| `timestamp` | `uint256` | The timestamp that the log was emitted. Used to enforce the timestamp invariant | +| `chainid` | `uint256` | The chainid of the chain that emitted the log | + +The [`Identifier`] includes the set of information to uniquely identify a log. When using an absolute +log index within a particular block, it makes ahead of time coordination more complex. Ideally there +is a better way to uniquely identify a log that does not add ordering constraints when building +a block. This would make building atomic cross chain messages more simple by not coupling the +exact state of the block templates between multiple chains together. + +## Messaging ends + +### Initiating Messages + +[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. +The raw log data froms the [Message Payload](#message-payload). + +Messages are *broadcast*: the protocol does not enshrine address-targeting within messages. + +The initiating message is uniquely identifiable with an [`Identifier`](#message-identifier), +such that it can be distinguished from other duplicate messages within the same transaction or block. + +An initiating message may be executed many times: no replay-protection is enshrined in the protocol. + +### 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. + +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). + +## 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`. + +### Timestamp Invariant + +The timestamp invariant ensures that initiating messages cannot come from a future block. Note that since +all transactions in a block have the same timestamp, it is possible for an executing transaction to be +ordered before the initiating message in the same block. + +### ChainID Invariant + +Without a guarantee on the set of dependencies of a chain, it may be impossible for the derivation +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. +It helps set a strict bound on total messaging activity to support, but also limits use-cases. +This trade-off is in review. This invariant may be ignored in initial interop testnets. + +The expiry invariant invalidates inclusion of any executing message with +`id.timestamp + EXPIRY_TIME < executing_block.timestamp` where: + +- `id` is the [`Identifier`] encoded in the executing message, matching the block attributes of the initiating message. +- `executing_block` is the block where the executing message was included in. +- `EXPIRY_TIME = 180 * 24 * 60 * 60 = 15552000` seconds, i.e. 180 days. + +## Message Graph + +The dependencies of messages can be modeled as a directed graph: + +- **vertex**: a block +- **edge**: a dependency: + - parent-block (source) to block (target) + - message relay, from initiation (source) to execution (target) + +If the source of an edge is invalidated, the target is invalidated. + +### Invalid messages + +A message is said to be "invalid" when the executing message does not have a valid dependency. + +Dependencies are invalid when: + +- The dependency is unknown. +- The dependency is known but not part of the canonical chain. +- The dependency is known but does not match all message attributes ([`Identifier`] and payload). + +#### Block reorgs + +The [`Identifier`] used by an executing message does not cryptographically +commit to the block it may reference. + +Messages may be executed before the block that initiates them is sealed. + +When tracking message dependencies, edges are maintained for *all* identified source blocks. + +Reorgs are resolved by filtering the view of the solver to only canonical blocks. +If the source block is not canonical, the dependency is invalid. + +The canonical L2 block at the identified block-height is the source of truth. + +#### Block Recommits + +Blocks may be partially built, or sealed but not published, and then rebuilt. + +Any optimistically assumed messages have to be revalidated, +and the original partial block can be removed from the dependency graph. + +### Intra-block messaging: cycles + +While messages cannot be initiated by future blocks, +they can be initiated by any transactions within the same timestamp, +as per the [Timestamp Invariant](#timestamp-invariant). + +This property allows messages to form cycles in the graph: +blocks with equal timestamp, of chains in the same dependency set, +may have dependencies on one another. + +### Resolving cross-chain safety + +To determine cross-chain safety, the graph is inspected for valid graph components that have no invalid dependencies, +while applying the respective safety-view on the blocks in the graph. + +I.e. the graph must not have any inward edges towards invalid blocks within the safety-view. + +A safety-view is the subset of canonical blocks of all chains with the specified safety label or a higher safety label. +Dependencies on blocks outside of the safety-view are invalid, +but may turn valid once the safety-view changes (e.g. a reorg of unsafe blocks). + +By resolving in terms of graph-components, cyclic dependencies and transitive dependencies are addressed. + +### Horizon timestamp + +The maximum seen timestamp in the graph is the `horizon_timestamp`. + +The verifier can defer growth of the graph past this `horizon_timestamp`, +until the graph has been resolved and pruned. + +### Pruning the graph + +Edges between blocks can be de-duplicated, when the blocks are complete and sealed. + +The same initiating or executing messages, but attached to different blocks, +may have to be added back to the graph if the set of blocks changes. + +Blocks with cross-chain safety in the finalized-blocks safety-view can be optimized: +outward edges for initiating messages may be attached +when relevant to resolution of newer lower-safety blocks. +No inward edges are required anymore however, as the maximum safety has been ensured. + +Blocks older than `horizon_timestamp - (2 * EXPIRY_TIME)` cannot be depended +on from any valid block within the graph, and can thus be safely pruned entirely. + +### Bounding the graph + +With many events, and transitive dependencies, resolving the cross-chain safety may be an intensive task for a verifier. +It is thus important for the graph to be reasonably bounded, such that it can be resolved. + +The graph is bounded in 4 ways: + +- Every block can only depend on blocks of chains in the dependency set, + 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) +- 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. + +## Security Considerations + +### Cyclic dependencies + +If there is a cycle in the dependency set, chains MUST still be able to promote unsafe blocks +to safe blocks. A cycle in the dependency set happens anytime that two chains are in each other's +dependency set. This means that they are able to send cross chain messages to each other. + +### Transitive dependencies + +The safety of a chain is only ensured when the inputs are safe: +dependencies are thus said to be transitive. +Without validating the transitive dependencies, +the verifier relies on the verification work of the nodes that it sources its direct dependencies from. diff --git a/specs/interop/predeploys.md b/specs/interop/predeploys.md new file mode 100644 index 000000000..942fa422f --- /dev/null +++ b/specs/interop/predeploys.md @@ -0,0 +1,260 @@ +# Predeploys + + + +**Table of Contents** + +- [CrossL2Inbox](#crossl2inbox) + - [Message execution arguments](#message-execution-arguments) + - [`_msg`](#_msg) + - [`_id`](#_id) + - [`_target`](#_target) + - [Reference implementation](#reference-implementation) + - [`Identifier` Getters](#identifier-getters) +- [L2ToL2CrossDomainMessenger](#l2tol2crossdomainmessenger) + - [`relayMessage` Invariants](#relaymessage-invariants) + - [Message Versioning](#message-versioning) + - [Transferring Ether in a Cross Chain Message](#transferring-ether-in-a-cross-chain-message) + - [Interfaces](#interfaces) + - [Sending Messages](#sending-messages) + - [Relaying Messages](#relaying-messages) +- [L1Block](#l1block) + - [L1Attributes](#l1attributes) +- [Security Considerations](#security-considerations) + + + +Two new system level predeploys are introduced for managing cross chain messaging along with +an update to the `L1Block` contract with additional functionality. + +## CrossL2Inbox + +| Constant | Value | +|----------|----------------------------------------------| +| Address | `0x4200000000000000000000000000000000000022` | + +The `CrossL2Inbox` is responsible for executing a cross chain message on the destination chain. +It is permissionless to execute a cross chain message on behalf of any user. + +To ensure safety of the protocol, the [Message Invariants](./messaging.md#messaging-invariants) must be enforced. + +### Message execution arguments + +The following fields are required for executing a cross chain message: + +[message payload]: ./messaging.md#message-payload +[`Identifier`]: ./messaging.md#message-identifier + +| Name | Type | Description | +|-----------|--------------|---------------------------------------------------------| +| `_msg` | `bytes` | The [message payload], matching the initiating message. | +| `_id` | `Identifier` | A [`Identifier`] pointing to the initiating message. | +| `_target` | `address` | Account that is called with `_msg`. | + +#### `_msg` + +The [message payload] of the executing message. + +This must match the emitted payload of the initiating message identified by `_id`. + +#### `_id` + +A pointer to the `_msg` in a remote (or local) chain. + +The message [`Identifier`] of the executing message. +This is required to enforce the message executes an existing and valid initiating message. + +By including the [`Identifier`] in the calldata, it makes static analysis much easier for block builders. +It is impossible to check that the [`Identifier`] matches the cross chain message on chain. If the block +builder includes a message that does not correspond to the [`Identifier`], their block will be reorganized +by the derivation pipeline. + +A possible upgrade path to this contract would involve adding a new function. If any fields in the [`Identifier`] +change, then a new 4byte selector will be generated by solc. + +#### `_target` + +Messages are broadcast, not directed. Upon execution the caller can specify which `address` to target: +there is no protocol enforcement on what this value is. + +The `_target` is called with the `_msg` as input. +In practice, the `_target` will be a contract that needs to know the schema of the `_msg` so that it can be decoded. +It MAY call back to the `CrossL2Inbox` to authenticate +properties about the `_msg` using the information in the `Identifier`. + +### Reference implementation + +A simple implementation of the `executeMessage` function is included below. + +```solidity +function executeMessage(address _target, bytes calldata _msg, Identifier calldata _id) public payable { + require(msg.sender == tx.origin); + require(_id.timestamp <= block.timestamp); + require(L1Block.isInDependencySet(_id.chainid)); + + assembly { + tstore(ORIGIN_SLOT, _id.origin) + tstore(BLOCKNUMBER_SLOT, _id.blocknumber) + tstore(LOG_INDEX_SLOT, _id.logIndex) + tstore(TIMESTAMP_SLOT, _id.timestamp) + tstore(CHAINID_SLOT, _id.chainid) + } + + bool success = SafeCall.call({ + _target: _target, + _value: msg.value, + _calldata: _msg + }); + + require(success); +} +``` + +### `Identifier` Getters + +The `Identifier` MUST be exposed via `public` getters so that contracts can call back to authenticate +properties about the `_msg`. + +## L2ToL2CrossDomainMessenger + +| Constant | Value | +|-------------------|----------------------------------------------| +| Address | `0x4200000000000000000000000000000000000024` | +| `MESSAGE_VERSION` | `uint256(0)` | +| `INITIAL_BALANCE` | `type(uint248).max` | + +The `L2ToL2CrossDomainMessenger` is a higher level abstraction on top of the `CrossL2Inbox` that +provides features necessary for secure transfers of `ether` and ERC20 tokens between L2 chains. +Messages sent through the `L2ToL2CrossDomainMessenger` on the source chain receive both replay protection +as well as domain binding, ie the executing transaction can only be valid on a single chain. + +### `relayMessage` Invariants + +- Only callable by the `CrossL2Inbox` +- The `Identifier.origin` MUST be `address(L2ToL2CrossDomainMessenger)` +- The `_destination` chainid MUST be equal to the local chainid +- The `CrossL2Inbox` cannot call itself + +### Message Versioning + +Versioning is handled in the most significant bits of the nonce, similarly to how it is handled by +the `CrossDomainMessenger`. + +```solidity +function messageNonce() public view returns (uint256) { + return Encoding.encodeVersionedNonce(nonce, MESSAGE_VERSION); +} +``` + +### Transferring Ether in a Cross Chain Message + +The `L2ToL2CrossDomainMessenger` MUST be initially set in state with an ether balance of `INITIAL_BALANCE`. +This initial balance exists to provide liquidity for cross chain transfers. It is large enough to always have +ether present to dispurse while still being able to accept inbound transfers of ether without overflowing. +The `L2ToL2CrossDomainMessenger` MUST only ensure all invariants are held before transferring out any ether. + +The `L2CrossDomainMessenger` is not updated to include the `L2ToL2CrossDomainMessenger` functionality because +there is no need to introduce complexity between the L1 to L2 messages and the L2 to L2 liquidity. + +### Interfaces + +The `L2ToL2CrossDomainMessenger` uses a similar interface to the `L2CrossDomainMessenger` but +the `_minGasLimit` is removed to prevent complexity around EVM gas introspection and the `_destination` +chain is included instead. + +#### Sending Messages + +The initiating message is represented by the following event: + +```solidity +event SentMessage(bytes message) anonymous; +``` + +The `bytes` are an ABI encoded call to `relayMessage`. The event is defined as `anonymous` so that no topics +are prefixed to the abi encoded call. + +An explicit `_destination` chain and `nonce` are used to ensure that the message can only be played on a single remote +chain a single time. The `_destination` is enforced to not be the local chain to avoid edge cases. + +```solidity +function sendMessage(uint256 _destination, address _target, bytes calldata _message) external payable { + require(_destination != block.chainid); + + bytes memory data = abi.encodeCall(L2ToL2CrossDomainMessenger.relayMessage, (_destination, messageNonce(), msg.sender, _target, msg.value, _message)); + emit SentMessage(data); + nonce++; +} +``` + +#### Relaying Messages + +When relaying a message through the `L2ToL2CrossDomainMessenger`, it is important to require that +the `_destination` equal to `block.chainid` to ensure that the message is only valid on a single +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. + +```solidity +function relayMessage(uint256 _destination, uint256 _nonce, address _sender, address _target, uint256 _value, bytes memory _message) external { + require(msg.sender == address(CROSS_L2_INBOX)); + require(_destination == block.chainid); + require(CROSS_L2_INBOX.origin() == address(this)); + require(_target != address(this)); + + bytes32 messageHash = keccak256(abi.encode(_destination, _nonce, _sender, _target, _value, _message)); + require(sentMessages[messageHash] == false); + + assembly { + tstore(CROSS_DOMAIN_MESSAGE_SENDER_SLOT, _sender) + } + + bool success = SafeCall.call({ + _target: _target, + _value: _value, + _calldata: _message + }); + + require(success); + + sentMessages[messageHash] = true; +} +``` + +## L1Block + +| Constant | Value | +|----------|----------------------------------------------| +| Address | `0x4200000000000000000000000000000000000015` | + +The `L1Block` contract is updated to include the set of allowed chains. The L1 Attributes transaction +sets the set of allowed chains. The `L1Block` contract MUST provide a public getter to check if a particular +chain is in the dependency set called `isInDependencySet(uint256)`. This function MUST return true when +the chain's chain id is passed in as an argument. + +The `setL1BlockValuesInterop()` function MUST be called on every block after the interop upgrade block. +The interop upgrade block itself MUST include a call to `setL1BlockValuesEcotone`. + +### L1Attributes + +The L1 Atrributes transaction is updated to include the dependency set. Since the dependency set is dynamically sized, +a `uint8` "interopSetSize" parameter prefixes tightly packed `uint256` values that represent each chain id. + +| Input arg | Type | Calldata bytes | Segment | +|-------------------|-------------------------|-------------------------|---------| +| {0x760ee04d} | bytes4 | 0-3 | n/a | +| baseFeeScalar | uint32 | 4-7 | 1 | +| blobBaseFeeScalar | uint32 | 8-11 | | +| sequenceNumber | uint64 | 12-19 | | +| l1BlockTimestamp | uint64 | 20-27 | | +| l1BlockNumber | uint64 | 28-35 | | +| basefee | uint256 | 36-67 | 2 | +| blobBaseFee | uint256 | 68-99 | 3 | +| l1BlockHash | bytes32 | 100-131 | 4 | +| batcherHash | bytes32 | 132-163 | 5 | +| interopSetSize | uint8 | 164-165 | 6 | +| chainIds | [interopSetSize]uint256 | 165-(32*interopSetSize) | 6+ | + +## Security Considerations + +TODO diff --git a/specs/interop/rollup_node_p2p.md b/specs/interop/rollup_node_p2p.md new file mode 100644 index 000000000..ea36b3fa0 --- /dev/null +++ b/specs/interop/rollup_node_p2p.md @@ -0,0 +1,21 @@ +# P2P Networking + + + +**Table of Contents** + +- [Security Considerations](#security-considerations) + + + +A node will optimistically accept a block as `unsafe` with only a signature by the sequencer. +To promote this block to a higher level of safety, it must be sure that the initiating messages +exist for all executing messages. A future release may add specific p2p networking components +to decrease the latency of this process. This could look like the sequencer signing and gossiping +sets of executing messages to nodes of remote chains so that they know exactly what initiating +messages to look for. An optimization on this would involve working with commitments to this data +so that less data is sent around via p2p. + +## Security Considerations + +TODO diff --git a/specs/interop/sequencer.md b/specs/interop/sequencer.md new file mode 100644 index 000000000..31a1d1c6f --- /dev/null +++ b/specs/interop/sequencer.md @@ -0,0 +1,145 @@ +# Sequencer + + + +**Table of Contents** + +- [Sequencer Policy](#sequencer-policy) +- [Block Building](#block-building) + - [Static analysis](#static-analysis) + - [Dependency confirmations](#dependency-confirmations) + - [Pre-confirmations](#pre-confirmations) + - [Streaming pre-confirmations: "shreds"](#streaming-pre-confirmations-shreds) + - [Direct-dependency confirmation](#direct-dependency-confirmation) + - [Transitive-dependency confirmation](#transitive-dependency-confirmation) +- [Sponsorship](#sponsorship) +- [Security Considerations](#security-considerations) + - [Cross Chain Message Latency](#cross-chain-message-latency) + + + +## Sequencer Policy + +Sequencer Policy is the process of optimistically enacting rules outside of consensus +(the state-transition function in this context), and the choices can then be asynchronously validated +by [verifiers](./verifier.md) and [the fault-proof](./fault_proof.md). + +In the context of superchain interopability, sequencer policy is utilized to enable cross-chain message relay +without adding additional state-transition complexity or cross-chain synchronicity to the protocol. + +## 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. + +### 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. + +### Dependency confirmations + +The sequencer MAY include an executing message in a block with any desired level +of confirmation safety around the dependency of the message. + +Confirmation levels: + +- pre-confirmation: through direct signaling by the block builder of the initiating message. +- direct-dependency confirmation: verify the inclusion of the initiating message, but not the transitive dependencies. +- transitive-dependency confirmation: verify the message and all transitive dependencies + are included in canonical blocks within the specified safety-view (unsafe/safe/finalized). + +When operating at lower-safety confirmation levels, the block builder SHOULD re-validate included +executing messages at an increased safety level, before the block is published. + +#### Pre-confirmations + +The block builder can include executing messages that have corresponding initiating messages +that only have pre-confirmation levels of security if they trust the block builder that initiates. + +Using an allowlist and identity turns sequencing into an integrated game which increases the ability for +block builders to trust each other. Better pre-confirmation technology will help to scale the block builder +set to untrusted actors. + +Without pre-confirmations, the block builder cannot include messages of other concurrent block building work. + +Pre-confirmations MAY be invalidated, and assumptions around message-validity SHOULD be treated with care. + +##### Streaming pre-confirmations: "shreds" + +In the context of low-latency block building, each pre-confirmation may be communicated in the form of a "shred": +the most minimal atomic state-change that can be communicated between actors in the system. + +In the context of cross-chain block-building, shreds may be used to communicate initiated cross-chain messages, +to validate executing messages against. + +Shreds may be streamed, and potentially signal rewinds in case of invalidated dependencies. + +This block builder feature is in ongoing research, +and may be developed and experimented with between block builders without further protocol rules changes. + +#### Direct-dependency confirmation + +By verifying the direct dependency the block-builder does not have to implement pre-confirmations, +and can rely on its direct view of the remote chain. + +The block builder MAY require cryptographic proof of the existence of the log +that the identifier points to, if it trusts the remote canonical chain but not its RPC server. + +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`. + +```python +target, message, id = abi.decode(calldata) + +eth = clients[id.chainid] + +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 + +if message != encode(log[0]): + return False + +block = eth.getBlockByNumber(id.blocknumber) + +if id.timestamp != block.timestamp: + return False + +return True +``` + +[tx-to]: https://github.com/ethereum/execution-specs/blob/1fed0c0074f9d6aab3861057e1924411948dc50b/src/ethereum/frontier/fork_types.py#L52 + +#### Transitive-dependency confirmation + +When operating pessimistically, the direct-dependency validation may be applied recursively, +to harden against unstable message guarantees at the cost of increased cross-chain latency. + +The transitive dependencies may also be enforced with `safe` or even `finalized` safety-views +over the blocks of the chains in the dependency set, to further ensure increased cross-chain safety. + +## Sponsorship + +If a user does not have ether to pay for the gas of an executing message, application layer sponsorship +solutions can be created. It is possible to create an MEV incentive by paying `tx.origin` in the executing +message. This can be done by wrapping the `L2ToL2CrossDomainMessenger` with a pair of relaying contracts. + +## Security Considerations + +### Cross Chain Message Latency + +The latency at which a cross chain message is relayed from the moment at which it was initiated is bottlenecked by +the security of the preconfirmations. An initiating transaction and a executing transaction MAY have the same timestamp, +meaning that a secure preconfirmation scheme enables atomic cross chain composability. Any sort of equivocation on +behalf of the sequencer will result in the production of invalid blocks. diff --git a/specs/interop/tx_pool.md b/specs/interop/tx_pool.md new file mode 100644 index 000000000..0aee2b52d --- /dev/null +++ b/specs/interop/tx_pool.md @@ -0,0 +1,60 @@ +# Transaction pool + + + +**Table of Contents** + +- [Message validation](#message-validation) +- [Security Considerations](#security-considerations) + - [Mempool Denial of Service](#mempool-denial-of-service) + + + +The transaction-pool is part of the execution-engine, +and generally replicated by the sequencer and its surrounding infrastructure. + +Not all messages may be included by the sequencer during [block building](./sequencer.md#block-building), +additional transaction-pool validation is thus required to prevent non-includable +transactions from overwhelming the network. + +Transactions with cross-chain messages are subject to the same transaction-pool +validation rules as regular transactions: +nonce, balance and fee changes need to be applied to validation so the cross-chain transaction +is possible to include in a block in the first place. + +However, additional validation rules are applied to demote messages that cannot be included in a block. + +## 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 [messaging invariants](./messaging.md#messaging-invariants) should be enforced in the transaction pool, +with dependency validation adapted for guarantees of the in-flight transactions: +the executing message is not included in the chain yet, +but the validity of the message and its initiating source is tracked. + +A Message with a definitively invalid message-dependency SHOULD be "demoted" from the transaction pool. + +Irreversible invalidation conditions: + +- The block at the initiating message source is finalized and does not pass the initiating-message checks. +- The message is expired. + +The sequencer MAY choose to demote messages which are invalid but can still technically become valid: + +- The block at the initiating message source is known, but another conflicting unsafe block is canonical. +- The block at the initiating message has invalidated message dependencies. + +Transactions with invalid message-dependencies MUST NOT be included in block-building, +and should thus be dropped from the transaction-pool. + +## Security Considerations + +### Mempool Denial of Service + +Since the validation of the execuing message relies on a remote RPC request, this introduces a denial of +service attack vector. The cost of network access is magnitudes larger than in memory validity checks. +The mempool SHOULD perform low-cost checks before any sort of network access is performed. +The results of the check SHOULD be cached such that another request does not need to be performed +when building the block although consistency is not guaranteed. diff --git a/specs/interop/upgrade.md b/specs/interop/upgrade.md new file mode 100644 index 000000000..d26ba51de --- /dev/null +++ b/specs/interop/upgrade.md @@ -0,0 +1,32 @@ +# Interop Network Upgrade + + + +**Table of Contents** + +- [Security Considerations](#security-considerations) + + + +The interop network upgrade timestamp defines the timestamp at which all functionality in this document is considered +the consensus rules for an OP Stack based network. On the interop network upgrade block, a set of deposit transaction +based upgrade transactions are deterministically generated by the derivation pipeline in the following order: + +- L1 Attributes Transaction calling `setL1BlockValuesEcotone` +- User deposits from L1 +- Network Upgrade Transactions + - L1Block deployment + - CrossL2Inbox deployment + - L2ToL2CrossDomainMessenger deployment + - Update L1Block Proxy ERC-1967 Implementation Slot + - Update CrossL2Inbox Proxy ERC-1967 Implementation Slot + - Update L2ToL2CrossDomainMessenger Proxy ERC-1967 Implementation Slot + - Mint Cross Chain Ether Liquidity in L2ToL2CrossDomainMessenger + +The execution payload MUST set `noTxPool` to `true` for this block. + +The exact definitions for these upgrade transactions are still to be defined. + +## Security Considerations + +TODO diff --git a/specs/interop/verifier.md b/specs/interop/verifier.md new file mode 100644 index 000000000..1c3e20066 --- /dev/null +++ b/specs/interop/verifier.md @@ -0,0 +1,159 @@ +# Verifier + + + +**Table of Contents** + +- [Derivation Pipeline](#derivation-pipeline) + - [Depositing an Executing Message](#depositing-an-executing-message) + - [Safety](#safety) + - [`unsafe` Inputs](#unsafe-inputs) + - [`cross-unsafe` Inputs](#cross-unsafe-inputs) + - [`safe` Inputs](#safe-inputs) + - [`finalized` Inputs](#finalized-inputs) +- [Security Considerations](#security-considerations) + - [Forced Inclusion of Cross Chain Messages](#forced-inclusion-of-cross-chain-messages) + - [Reliance on History](#reliance-on-history) + + + +## Derivation Pipeline + +The derivation pipeline enforces invariants on safe blocks that include executing messages. + +- The executing message MUST have a corresponding initiating message +- The initiating message that corresponds to an executing message MUST come from a chain in its dependency set +- A block MUST be considered invalid if it is built with any invalid executing messages + +Blocks that contain transactions that relay cross domain messages to the destination chain where the +initiating transaction does not exist MUST be considered invalid and MUST not be allowed by the +derivation pipeline to be considered safe. + +There is no concept of replay protection at the lowest level of abstractions within the protocol because +there is no replay protection mechanism that fits well for all applications. Users MAY submit an +arbitrary number of executing messages per initiating message. Applications MUST build their own replay +protection mechanisms if they are interacting with the lowest level abstractions. + +Blocks that contain invalid executing messages are considered invalid by the protocol. The derivation +pipeline will never promote them from being `unsafe`. A block that contains invalid executing messages +MUST be replaced by a deposits only block at the same block number. + +### Depositing an Executing Message + +Deposit transactions (force inclusion transactions) give censorship resistance to layer two networks. +The derivation pipeline must gracefully handle the case in which a user uses a deposit transaction to +relay a cross chain message. To not couple preconfirmation security to consensus, deposit transactions +that execute cross chain messages MUST have an initiating message that is considered [safe](#safety) by the remote +chain's derivation pipeline. This means that the remote chain's data including the initiating message +MUST be posted to the data availability layer. This relaxes a strict synchrony assumption on the sequencer +that it MUST have all unsafe blocks of destination chains as fast as possible to ensure that it is building +correct blocks. + +If a deposit transaction references an initiating transaction that is not yet safe or does not exist, +it MUST be dropped by the derivation pipeline. + +This inclusion property prevents a class of attacks where the user can trick the derivation pipeline +into reorganizing the sequencer. + +### Safety + +Safety is an abstraction that is useful for reasoning about security. It should be thought about +as a spectrum from `unsafe` to `finalized`. Users can choose to operate on information based on its +level of safety depending on their risk profile and personal preferences. + +The following labels are used to describe both inputs and outputs: + +- `unsafe` +- `cross-unsafe` +- `safe` +- `finalized` + +Inputs correspond to the inputs to the state transition function while outputs correspond to the side +effects of the state transition function. + +Anything before `safe` technically uses a "preconfirmation" based security model which is not part +of consensus. While useful to have definitions of the default meanings of these terms, they are +technically policy and may be changed in the future. + +The `unsafe` label has the lowest latency while the `finalized` label has the highest latency. +A set of invariants must be held true before an input or an output can be promoted to the next +label. + +The initiating messages for all dependent executing messages MUST be resolved as safe before an L2 block can transition +from being unsafe to safe. Users MAY optimistically accept unsafe blocks without any verification of the +executing messages. They SHOULD optimistically verify the initiating messages exist in destination unsafe blocks +to more quickly reorganize out invalid blocks. + +#### `unsafe` Inputs + +- MUST be signed by the p2p sequencer key +- MAY be reorganized +- MUST be promoted to a higher level of safety or reorganized out to ensure liveness + +`unsafe` inputs are currently gossiped around the p2p network. To prevent denial of service, they MUST +be signed by the sequencer. This signature represents the sequencer's claim that it +built a block that conforms to the protocol. `unsafe` blocks exist to give low latency access to the +latest information. To keep the latency as low as possible, cross chain messages are assumed valid +at this stage. This means that the remote unsafe inputs are trusted. + +An alternative approach to `unsafe` inputs would be to include an SGX proof that the sequencer ran +particular software when building the block. + +#### `cross-unsafe` Inputs + +- MUST have valid cross chain messages + +`cross-unsafe` represents the `unsafe` blocks that had their cross chain messages fully verified. +The network can be represented as a graph where each block across all chains are represented as +a node and then a directed edge between two blocks represents the source block of the initiating +message and the block that included the executing message. + +An input can be promoted from `safe` to `cross-unsafe` when the full dependency graph is resolved +such that all cross chain messages are verified to be valid and at least one message in the dependency +graph is still `unsafe`. + +#### `safe` Inputs + +- MUST be available +- MAY be reorganized +- Safe block MUST be invalidated if a reorg occurs + +`safe` represents the state in which the `cross-unsafe` dependency graph has been fully resolved +in a way where all of the data has been published to the data availability layer. + +#### `finalized` Inputs + +- MUST NOT be reorganized based on Ethereum economic security + +`finalized` represents full Proof of Stake economic security on top of the data. This means that +if the data is reorganized, then validators will be slashed. + +## Security Considerations + +### Forced Inclusion of Cross Chain Messages + +The design is particular to not introduce any sort of "forced inclusion" between L2s. This design space introduces +risky synchrony assumptions and forces the introduction of a message queue to prevent denial of service attacks where +all chains in the network decide to send cross chain messages to the same chain at the same time. + +"Forced inclusion" transactions are good for censorship resistance. In the worst case of censoring sequencers, it will +take at most 2 sequencing windows for the cross chain message to be processed. The initiating transaction can be sent +via a deposit which MUST be included in the source chain or the sequencer will be reorganized at the end of the +sequencing window that includes the deposit transaction. If the executing transaction is censored, it will take +another sequencing window of time to force the inclusion of the executing message per the +[spec][depositing-an-executing-message]. + +TODO: verify exact timing of when reorg happens with deposits that are skipped + +[depositing-an-executing-message]: #depositing-an-executing-message + +### Reliance on History + +When fully executing historical blocks, a dependency on historical receipts from remote chains is present. +[EIP-4444][eip-4444] will eventually provide a solution for making historical receipts available without +needing to require increasingly large execution client databases. + +It is also possible to introduce a form of expiry on historical receipts by enforcing that the timestamp +is recent enough in the `CrossL2Inbox`. + +[eip-4444]: https://eips.ethereum.org/EIPS/eip-4444 diff --git a/specs/root.md b/specs/root.md index b3f8a7db5..c5d60e9c1 100644 --- a/specs/root.md +++ b/specs/root.md @@ -43,6 +43,7 @@ Specifications of new features in active development. - [Honest Challenger Behavior](./experimental/fault-proof/honest-challenger-fdg.md) - [Cannon VM](./experimental/fault-proof/cannon-fault-proof-vm.md) - [Plasma](./experimental/plasma.md) +- [Interoperability](./interop/README.md) ## Design Goals From 4664ca46e4f2a15bef5d4f9dbe2b3523286cb249 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Tue, 13 Feb 2024 03:08:41 +0800 Subject: [PATCH 2/4] interop: verifier pseudocode --- specs/interop/predeploys.md | 28 ++++++------- specs/interop/verifier.md | 81 ++++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/specs/interop/predeploys.md b/specs/interop/predeploys.md index 942fa422f..c727652bd 100644 --- a/specs/interop/predeploys.md +++ b/specs/interop/predeploys.md @@ -240,20 +240,20 @@ The interop upgrade block itself MUST include a call to `setL1BlockValuesEcotone The L1 Atrributes transaction is updated to include the dependency set. Since the dependency set is dynamically sized, a `uint8` "interopSetSize" parameter prefixes tightly packed `uint256` values that represent each chain id. -| Input arg | Type | Calldata bytes | Segment | -|-------------------|-------------------------|-------------------------|---------| -| {0x760ee04d} | bytes4 | 0-3 | n/a | -| baseFeeScalar | uint32 | 4-7 | 1 | -| blobBaseFeeScalar | uint32 | 8-11 | | -| sequenceNumber | uint64 | 12-19 | | -| l1BlockTimestamp | uint64 | 20-27 | | -| l1BlockNumber | uint64 | 28-35 | | -| basefee | uint256 | 36-67 | 2 | -| blobBaseFee | uint256 | 68-99 | 3 | -| l1BlockHash | bytes32 | 100-131 | 4 | -| batcherHash | bytes32 | 132-163 | 5 | -| interopSetSize | uint8 | 164-165 | 6 | -| chainIds | [interopSetSize]uint256 | 165-(32*interopSetSize) | 6+ | +| Input arg | Type | Calldata bytes | Segment | +|-------------------|--------------------------|-------------------------|---------| +| {0x760ee04d} | bytes4 | 0-3 | n/a | +| baseFeeScalar | uint32 | 4-7 | 1 | +| blobBaseFeeScalar | uint32 | 8-11 | | +| sequenceNumber | uint64 | 12-19 | | +| l1BlockTimestamp | uint64 | 20-27 | | +| l1BlockNumber | uint64 | 28-35 | | +| basefee | uint256 | 36-67 | 2 | +| blobBaseFee | uint256 | 68-99 | 3 | +| l1BlockHash | bytes32 | 100-131 | 4 | +| batcherHash | bytes32 | 132-163 | 5 | +| interopSetSize | uint8 | 164-165 | 6 | +| chainIds | uint256\[interopSetSize] | 165-(32*interopSetSize) | 6+ | ## Security Considerations diff --git a/specs/interop/verifier.md b/specs/interop/verifier.md index 1c3e20066..832efe050 100644 --- a/specs/interop/verifier.md +++ b/specs/interop/verifier.md @@ -44,8 +44,7 @@ Deposit transactions (force inclusion transactions) give censorship resistance t The derivation pipeline must gracefully handle the case in which a user uses a deposit transaction to relay a cross chain message. To not couple preconfirmation security to consensus, deposit transactions that execute cross chain messages MUST have an initiating message that is considered [safe](#safety) by the remote -chain's derivation pipeline. This means that the remote chain's data including the initiating message -MUST be posted to the data availability layer. This relaxes a strict synchrony assumption on the sequencer +chain's derivation pipeline. This relaxes a strict synchrony assumption on the sequencer that it MUST have all unsafe blocks of destination chains as fast as possible to ensure that it is building correct blocks. @@ -77,7 +76,7 @@ technically policy and may be changed in the future. The `unsafe` label has the lowest latency while the `finalized` label has the highest latency. A set of invariants must be held true before an input or an output can be promoted to the next -label. +label, starting at `unsafe`. The initiating messages for all dependent executing messages MUST be resolved as safe before an L2 block can transition from being unsafe to safe. Users MAY optimistically accept unsafe blocks without any verification of the @@ -94,7 +93,8 @@ to more quickly reorganize out invalid blocks. be signed by the sequencer. This signature represents the sequencer's claim that it built a block that conforms to the protocol. `unsafe` blocks exist to give low latency access to the latest information. To keep the latency as low as possible, cross chain messages are assumed valid -at this stage. This means that the remote unsafe inputs are trusted. +at this stage. This means that the remote unsafe inputs are trusted soley because they were +included in a block by the sequencer. An alternative approach to `unsafe` inputs would be to include an SGX proof that the sequencer ran particular software when building the block. @@ -112,6 +112,10 @@ An input can be promoted from `safe` to `cross-unsafe` when the full dependency such that all cross chain messages are verified to be valid and at least one message in the dependency graph is still `unsafe`. +Note that the `cross-unsafe` is not meant to be exposed to the end user via the RPC label. It is +meant for internal usage. All `cross-unsafe` inputs are still considered `unsafe` by the execution +layer RPC. + #### `safe` Inputs - MUST be available @@ -128,6 +132,61 @@ in a way where all of the data has been published to the data availability layer `finalized` represents full Proof of Stake economic security on top of the data. This means that if the data is reorganized, then validators will be slashed. +### Honest Verifier + +The honest verifier follows a naive verification algorithm. That is similar +to the block building code that the [sequencer](./sequencer.md#direct-dependency-confirmation) +follows. The main difference is that the validity of included executing +messages is verified instead of verifying possible executing messages before +inclusion. + +```python +deps = get_transitive_dependency_set() + +blocks = {} + +while True: + for dep in deps: + if dep.has_new_block(): + block = dep.get_earliest_unseen_block() + is_valid_block = True + for tx in block.transactions: + if is_executing_message(tx): + if not is_valid_executing_message(tx): + is_valid_block = False + if is_valid_block: + blocks[dep.chainid][block.number] = block + + block = chain.next_unsafe_block(): + can_promote = True + for tx in block.txs: + if is_executing_message(tx): + if not is_valid_executing_message(tx): + can_promote = False + break + + target, message, id = abi.decode(tx.calldata) + + if id.chainid not in get_transitive_dependency_set(): + can_promote = False + break + + remote_block = blocks[id.chainid][id.blocknumber] + if remote_block is None: + can_promote = False + break + + messages = [] + for receipt in remote_block.receipts: + for log in receipt: + messages.append(encode(log)) + if message not in messages: + can_promote = False + + if can_promote: + promote(block) +``` + ## Security Considerations ### Forced Inclusion of Cross Chain Messages @@ -143,10 +202,20 @@ sequencing window that includes the deposit transaction. If the executing transa another sequencing window of time to force the inclusion of the executing message per the [spec][depositing-an-executing-message]. -TODO: verify exact timing of when reorg happens with deposits that are skipped - [depositing-an-executing-message]: #depositing-an-executing-message +#### What if Safety isn't Enough? + +It is possible that small latency differences may impact the allowance of deposited executing messages +if the rule is that the initiating message is safe. A more strict invariant may be introduced: + +```text +identifier.timestamp + sequencer_window <= block.timestamp +``` + +This says that a sequencer window must elapse before the initiating message can be referenced +in an executing message. + ### Reliance on History When fully executing historical blocks, a dependency on historical receipts from remote chains is present. From 347d6435be9f7cc4655ac7cefee4578f588fa261 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Thu, 15 Feb 2024 04:02:53 +0800 Subject: [PATCH 3/4] specs: remove naive verifier spec --- specs/interop/verifier.md | 47 --------------------------------------- 1 file changed, 47 deletions(-) diff --git a/specs/interop/verifier.md b/specs/interop/verifier.md index 832efe050..ad6e28155 100644 --- a/specs/interop/verifier.md +++ b/specs/interop/verifier.md @@ -140,53 +140,6 @@ follows. The main difference is that the validity of included executing messages is verified instead of verifying possible executing messages before inclusion. -```python -deps = get_transitive_dependency_set() - -blocks = {} - -while True: - for dep in deps: - if dep.has_new_block(): - block = dep.get_earliest_unseen_block() - is_valid_block = True - for tx in block.transactions: - if is_executing_message(tx): - if not is_valid_executing_message(tx): - is_valid_block = False - if is_valid_block: - blocks[dep.chainid][block.number] = block - - block = chain.next_unsafe_block(): - can_promote = True - for tx in block.txs: - if is_executing_message(tx): - if not is_valid_executing_message(tx): - can_promote = False - break - - target, message, id = abi.decode(tx.calldata) - - if id.chainid not in get_transitive_dependency_set(): - can_promote = False - break - - remote_block = blocks[id.chainid][id.blocknumber] - if remote_block is None: - can_promote = False - break - - messages = [] - for receipt in remote_block.receipts: - for log in receipt: - messages.append(encode(log)) - if message not in messages: - can_promote = False - - if can_promote: - promote(block) -``` - ## Security Considerations ### Forced Inclusion of Cross Chain Messages From 2145255c62c374a2c7e98ba4d021c7baee2462f7 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Thu, 15 Feb 2024 04:03:31 +0800 Subject: [PATCH 4/4] specs: update toc --- specs/interop/verifier.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/interop/verifier.md b/specs/interop/verifier.md index ad6e28155..39f7b89ae 100644 --- a/specs/interop/verifier.md +++ b/specs/interop/verifier.md @@ -11,8 +11,10 @@ - [`cross-unsafe` Inputs](#cross-unsafe-inputs) - [`safe` Inputs](#safe-inputs) - [`finalized` Inputs](#finalized-inputs) + - [Honest Verifier](#honest-verifier) - [Security Considerations](#security-considerations) - [Forced Inclusion of Cross Chain Messages](#forced-inclusion-of-cross-chain-messages) + - [What if Safety isn't Enough?](#what-if-safety-isnt-enough) - [Reliance on History](#reliance-on-history)