Skip to content

Commit

Permalink
add spec docs
Browse files Browse the repository at this point in the history
  • Loading branch information
beer-1 committed Nov 14, 2023
1 parent 89efdc5 commit 08e64bb
Show file tree
Hide file tree
Showing 7 changed files with 519 additions and 4 deletions.
65 changes: 61 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,66 @@
# OPinit CosmosSDK Modules

This repository provides CosmosSDK modules for OPinit. Any app chain can use these modules to integrate OPinit.
Initia Layer 2 solution with Optimistic Rollup.

## How to Integrate
## Optimistic Rollup Architecture

### Host(L1) Chain
![architecture](./specs/architecture.png)

### Child(L2) Chain
### L1 Components

#### [Bridge Module](./specs/l1_bridge.md)

The bridge module triggers the deposit event for the bridge executor, which acts as a relayer between L1 and L2. It has two interfaces: `initialize_deposit` for users and `finalize_withdrawal` for the bridge executor. Both interfaces can be executed by anyone who wants to move the tokens between L1 and L2.

A deposit does not require any proving or confirmation period, but a withdrawal requires [withdrawal proving](./specs/withdrawal_proving.md) and a finalized output root which contains the withdrawal transaction.

#### BatchInbox Module

The batch inbox is the data availability (DA) layer, which can be replaced by other solutions like `Celestia`. The rollup chain can be deterministically derived using the data from the DA layer. This ability to derive the entire rollup chain based on the DA layer is what makes Minitia a rollup.

To reduce gas costs, the batch inbox only exposes an empty function interface that receives arbitrary bytes as an argument. This trick ensures that L2 data is not a part of the state but instead resides in tx db (= calldata).

#### [L2OutputOracle Module](./specs/l2_output_oracle.md)

The L2 output oracle is the component to store the L2 output root for block finalization. The users who withdraw the tokens from L2 to L1 also need to use this output root to prove the withdraw transaction is in the finalized L2 output root.

The challenger always monitor the oracle output and do challenge when the output is different from the value computed from challenger side.

### L2 Components

#### BridgeExecutor

The bridge executor is the core component in minitia rollup, which is charge of following operations via [L2 Bridge Module](./specs/l2_bridge.md):

* Finalize L1 deposit transaction to L2.
* Construct withdraw tx storage Merkle Tree.
* Compute L2 output root.
* Provide the withdrawal proofs (Merkle Proofs) to users.

#### [Minitia](./specs/minitia.md)

The L2 app chain implementation provides rollup-specific interfaces for a bridge executor. The minitia is a minimized version of the initia app chain, so it does not include staking-related modules such as `staking`, `distribution`, `crisis`, and `evidence`. Instead, it has a new module called `rollup`, which provides a permissioned interface for adding and removing validators, as well as executing [bridge messages](./specs/l2_bridge.md) that can be executed by the bridge executor.

#### BatchSubmitter

A background process that submits transaction batches to the `BatchInbox` module of L1.

#### Challenger

A challenger is an entity capable of deleting invalid output proposals from the output oracle. It mimics the output root generation process that a bridge executor does to check the validity of the proposed output root on the oracle module. This process confirms that the proposed output root contains a valid app hash, and all withdrawal transactions are properly relayed to L1.

Additionally, a challenger monitors deposit transactions from L1 to L2 to ensure censorship resistance. If the transactions are not properly relayed to L2 within the timeout (L2 block numbers), the challenger deletes the output root.

In the initia optimistic rollup spec, a challenger is supposed to run an IBC relayer between L1 and L2 to support instant bridge operation. It is the entity that can monitor an invalid state first, so it can prevent invalid IBC operation by stopping the relayer process. To accomplish this, initia is using [a new ibc middleware](https://github.com/initia-labs/initia/pull/86) on the L1 side to restrict the relayer permission to a specific address for each channel.

### Dispute Process

Initia's optimistic rollup uses a simplified version of the dispute mechanism with L1 governance security. This approach is very similar to Cosmos's shared security, but it does not require all validators to run a whole L2 node. Instead, the validators are only required to run an L2 node to decide the valid entity between the `proposer` and `challenger` when a dispute is opened. They do not need to run whole L2 blocks but only need to run a dispute block with the last L2 state on L1.

The dispute process works as follows:

1. A `challenger` deletes the invalid output root from the output oracle module.
2. Both a `challenger` and a `proposer` make a governance proposal to update invalid operator addresses:
* The `challenger` make a governance proposal to change the `proposer` to another address if the `proposer` keeps submitting an invalid output root.
* The `proposer` make a governance proposal to change the `challenger` to another address if the `challenger` keeps deleting a valid output root.
3. L1 validators make a validity decision by running an L2 node with L2 state and data inputs.
Binary file added specs/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions specs/l1_bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# L1 Bridge Module

## Events

### `TokenRegisteredEvent`

The event is emitted when a new token support is added to bridge contract.

* In v1 spec, the bridge executor should add a new token support manually.

```rust
/// Emitted when deposit store is registered.
struct TokenRegisteredEvent has drop, store {
l2_id: String,
l1_token: String,
l2_token: vector<u8>, // sha3_256(type_name(`L2ID`) || type_name(`l1_token`))
}
```

### `TokenBridgeInitiatedEvent`

The event is emitted when a user executes `deposit_token` function to move a token from L1 to L2.

* The bridge module maintain `sequence` number to give an unique identifier for each relaying operation.
* In v1, `l2_id` + `l1_sequence` is the unique identifier.

```rust
/// Emitted when a token bridge is initiated to the l2 chain.
struct TokenBridgeInitiatedEvent has drop, store {
from: address, // l1 address
to: address, // l2 address
l2_id: String,
l1_token: String,
l2_token: vector<u8>,
amount: u64,
l1_sequence: u64,
}
```

### `TokenBridgeFinalizedEvent`

The event is emitted when a withdrawal transaction is proved and finalized.

* In v1, `sha3(bcs(l2_sequence) + bcs(from) + bcs(to) + bcs(amount) + bytes(l1_token))` is the unique identifier for each withdrawal operation.

```rust
/// Emitted when a token bridge is finalized on l1 chain.
struct TokenBridgeFinalizedEvent has drop, store {
from: address, // l2 address
to: address, // l1 address
l2_id: String,
l1_token: String,
l2_token: vector<u8>,
amount: u64,
l2_sequence: u64, // the sequence number which is assigned from the l2 bridge
}
```

## Operations

### Register Token

This function is for the bridge executor, who controls the bridge relaying operations. In version 1, only the bridge executor can add support for a new token type. After registration, the `TokenRegisteredEvent` event is emitted to deploy a new coin type module on L2.

The bridge executor should monitor the `TokenRegisteredEvent` and deploy a new coin type module on L2. They should also execute the L2 bridge's `/minitia.rollup.v1.MsgCreateToken` function for initialization.

### Initiate Token Bridge

This function enables a user to transfer their asset from L1 to L2. The deposited token will be locked in the bridge's `DepositStore` and can only be released using the `withdraw` operation in L2. When executed, this operation emits a `TokenBridgeInitiatedEvent`. A bridge executor should subscribe to this event and transfer the token to L2 by executing the `finalize_token_bridge` function in the L2 bridge.

### Finalize Token Bridge

This function is used to prove and finalize the withdrawal transaction from L2. The proving process is described [here](https://www.notion.so/Withdrawal-Proving-a49f7c26467044489731048f68ed584b?pvs=21). Once the proving is complete, the deposited tokens are withdrawn to the recipient address, and the `TokenBridgeFinalizedEvent` event is emitted. To prevent duplicate withdrawal attempts, the bridge uses a unique identifier calculated as `sha3(bcs(l2_sequence) + bcs(from) + bcs(to) + bcs(amount) + bytes(l1_token))`.
52 changes: 52 additions & 0 deletions specs/l2_bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# L2 Bridge

## Events

### `TokenBridgeFinalizedEvent`

The event is emitted when a executor finalized the token transfer from the L1 to L2.

```rust
// Emitted when a token bridge is finalized on l2 chain.
struct TokenBridgeFinalizedEvent has drop, store {
from: address, // l1 address
to: address, // l2 address
l2_token: vector<u8>,
amount: u64,
l1_sequence: u64, // the sequence number which is assigned from the l1 bridge
}
```

### `TokenBridgeInitiatedEvent`

The event is emitted when a user executes `withdraw_token` function to move token from L2 to L1.

- The bridge module maintain `sequence` number to give unique identifier to each relay operation.
- In v1, `l2_sequence` is the unique identifier.

```rust
// Emitted when a token bridge is initiated to the l1 chain.
struct TokenBridgeInitiatedEvent has drop, store {
from: address, // l2 address
to: address, // l1 address
l2_token: vector<u8>,
amount: u64,
l2_sequence: u64, // the operation sequence number
}
```

## Operations

### Register Token

This function allows the block executor to initialize a new token type with registration on the bridge module. The name of the newly deployed module should follow the L1 bridge contract’s event message `l2_token`, such as `01::l2_${l2_token}::Coin`.

### Finalize Token Bridge

This function finalizes the token transfer from L1 to L2. Only the block executor is allowed to execute this operation.

### Initiate Token Bridge

This function initiates the token bridge from L2 to L1. Users can execute `withdraw_token` to send tokens from L2 to L1. This operation emits the `TokenBridgeInitiatedEvent` with an `l2_sequence` number to prevent duplicate execution on L1.

The block executor should monitor this event to build withdraw storage for withdrawal proofs.
32 changes: 32 additions & 0 deletions specs/l2_output_oracle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# L2 Output Oracle

In version 1, output oracle maintain `proposer` and `challenger` addresses on its config store. The `proposer` can submit the `output_proposal` and the `challenger` can delete the output when the proposed output state is wrong.

The first version of the implementation does not include a dispute system, but uses permissioned propose and challenge mechanisms. In version 2, anyone can propose the output with a certain amount of `stake`, and disputes will be resolved based on the governance of L1.

## Operations

### Propose L2 Output

L2 output oracle receives `output_root` with L2 block number to check the checkpoint of L2. The checkpoints are the multiple of `submission_interval`. A proposer must submit the `output_root` at the every checkpoints.

The followings are the components of `output_root`.

- `version`: the version of output root
- `state_root`: l2 state root
- `storage_root`: withdrawal storage root
- `latest_block_hash`: l2 latest block hash

To build the `output_root`, concatenate all the components in sequence and apply `sha3_256`.

### Delete L2 Output

A challenger can delete the output without dispute in version 1 with output index.

### Update Proposer

The operation is to update proposer to another address when a proposer keeps submitting a invalid output root. The operation is supposed to be executed by `0x1` via L1 governance.

### Update Challenger

The operation is to update challenger to another address when a challenger keeps deleting a valid output root. The operation is supposed to be executed by `0x1` via L1 governance.
Loading

0 comments on commit 08e64bb

Please sign in to comment.