Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(spec): update bridge spec #1705

Merged
merged 5 commits into from
Nov 5, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 201 additions & 85 deletions specs/bridge.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,210 @@
# Astria rollup bridging protocol
# Astria Rollup Bridging Protocol

The Astria sequencer implements a native bridging protocol which allows for
bridging assets from the sequencer to rollups which decide to use this protocol.

At a high level, the sequencer-to-rollup protocol works as follows:

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like having the high level summary first, before delving into the code details. can you re-add?

- a bridge account is initialized on the sequencer which has an associated
rollup ID, which is the rollup ID that the rollup reads its block data from
- the rollup enshrines the sequencer bridge account into its consensus,
authorizing transfers (locks) into this bridge account of a specific asset
to result in mints of the synthetic token on the rollup
- users then send transfers to the sequencer bridge account, which result in
`Deposit` events being included as part of the rollup's block data
- when the rollup sees a `Deposit`, it mints the corresponding tokens to the
user's account

This is similar to how existing L1-to-rollup bridges work, where some lock
event happening on the L1 results in a deposit transaction being derived
automatically on the rollup.

The rollup-to-sequencer protocol works as follows:

- the bridge account has an associated private key. this could potentially be
backed by the threshold signature scheme/multisig.
- a bridge withdrawer is able to sign transaction which move funds out of the
bridge account to other sequencer accounts.
- there is a withdrawal contract deployed on the rollup which emits `Withdrawal`
events when funds are locked in it (and effectively burned, as the contract
should not be able to transfer its own funds).
- the bridge withdrawer watches for these `Withdrawal` events. when it sees
one, it sends a sequencer transaction which transfers funds from the bridge
account to the account specified in the withdrawal event.
The protocol is similar to how existing L1-to-rollup bridges work, where a lock
event on the L1 results in a deposit transaction being derived automatically
on the rollup.

Since the sequencer can support multiple assets via IBC, the bridging protocol
also has support for deposits and withdrawals via IBC, as well as support for
various assets to be bridged to the rollup.

The one-step deposit flow from another IBC chain is as follows:

- a user initiates an ICS20 withdrawal from some IBC chain to Astria.
- the user sets the destination chain address as the sequencer bridge address
which corresponds to the rollup they wish to deposit to.
- the user sets the withdrawal memo to their rollup address.
- when the ICS20 transfer is executed on Astria, the funds are locked into the
## High-Level Overview

### Sequencer-to-Rollup

1. The bridge account is initialized on the sequencer with an associated
rollup ID.
2. The rollup modifies its consensus to register the sequencer-side bridge account
for use in its state transition logic.
3. Users then send sequencer-side transfers to the bridge account (either natively
or using IBC), which are included in the rollup's block data as `Deposit` events.
4. Mints to the user's rollup-side account are derived from the `Deposit`s included
in the rollup block data.

### Rollup-to-Sequencer

1. The bridge withdrawer is registered with the sequencer-side bridge account,
allowing it to make withdrawals from the bridge account.
2. The withdrawal contract is deployed on the rollup, emitting `SequencerWithdrawal`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit confusing, maybe rephrase to "the bridge account has an associated withdrawer address which is authorized to withdraw from the bridge account"

and `Ics20Withdrawal` events when its `withdrawToSequencer` and `withdrawToIbcChain`
functions are called, respectively.
3. The bridge withdrawer watches for these events, converting them to the appropriate
sequencer-side native or IBC withdrawals and batching them by rollup block. Batches
are then submitted to the sequencer sequentially.

## Bridge Account Initialization

1. A bridge account is initialized on the sequencer with an associated rollup
ID, which is the rollup ID that the rollup reads its block data from.

2. The rollup modifies its consensus to register the sequencer bridge account
for use in its state transition logic. `Deposit`s of the correct asset to the
bridge account will be used to derive a mint of the corresponding tokens on the
rollup, according to the metadata provided.

- An example for "registering" the bridge account: [`astria-geth`](https://github.com/astriaorg/astria-geth/blob/09c27dc320570d9e1f58ea60325158f36d6a0309/genesis.json#L24)
- An example for the mint derivation logic: [`astria-geth`](https://github.com/astriaorg/astria-geth/blob/09c27dc320570d9e1f58ea60325158f36d6a0309/grpc/execution/validation.go#L27)

The bridge account is initialized with a sequencer-side [`InitBridgeAccount`](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/proto/protocolapis/astria/protocol/transaction/v1/action.proto#L146)
action.

After being initialized, the bridge account's rollup ID and asset ID cannot
be changed. The account also cannot be re-initialized as a bridge account, or
converted back into a non-bridge account.

Only the `sudo_address` is authorized to change the bridge account's `sudo_address`
and `withdrawer_address`.

## Withdrawer Account

The sequencer-side bridge account has an associated withdrawer account. This is a
sequencer-side account, which uses `Ed25519`, and could potentially be backed
by a threshold signature scheme.

The withdrawer account is set with a sequencer-side [`BridgeSudoChange`](https://github.com/astriaorg/astria/blob/main/proto/protocolapis/astria/protocol/transaction/v1/action.proto#L211)
action.

The sequencer-side bridge withdrawer is the only account able to make actions that
transfer funds out of the bridge account to other sequencer accounts, either via
[`BridgeUnlock` actions](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/proto/protocolapis/astria/protocol/transaction/v1/action.proto#L186)
or [`Ics20Withdrawal` actions](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/proto/protocolapis/astria/protocol/transaction/v1/action.proto#L78)

## Sequencer-To-Rollup Deposits

After the bridge account is initialized, the deposit flow works as follows:

1. When the user makes sequencer-side transfers, such as `BridgeLock`, to the
bridge account, `Deposit` events are included by the sequencer as part of the
rollup's block data.

2. When the rollup receives a `Deposit` in an `ExecuteBlockRequest`, it derives
a mint transactions to the user's account according to the metadata provided.

[`BridgeLock`](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/proto/protocolapis/astria/protocol/transaction/v1/action.proto#L167)
actions have the following structure:

```proto
// `BridgeLock` represents a transaction that transfers
// funds from a sequencer account to a bridge account.
//
// It's the same as a `Transfer` but with the added
// `destination_chain_address` field.
message BridgeLock {
// the address of the bridge account to transfer to
astria.primitive.v1.Address to = 1;
// the amount to transfer
astria.primitive.v1.Uint128 amount = 2;
// the asset to be transferred
string asset = 3;
// the asset used to pay the transaction fee
string fee_asset = 4;
// the address on the destination chain which
// will receive the bridged funds
string destination_chain_address = 5;
}
```

- [`Ics20Transfer`](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/crates/astria-sequencer/src/ibc/ics20_transfer.rs#L335)
packets are received when an IBC transfer to Astria from another chain occurs.
If the recipient of the packet is a bridge account and the asset transferred is
valid, the funds are locked in the bridge account and a `Deposit` is created.
- `Deposit` events are defined in the `sequencerblockapis`: [`block.proto`](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/proto/sequencerblockapis/astria/sequencerblock/v1/block.proto#L76)
- An example for the derived rollup side transaction: [`astria-geth`'s `DepositTx`](https://github.com/astriaorg/astria-geth/blob/09c27dc320570d9e1f58ea60325158f36d6a0309/core/types/deposit_tx.go#L14)

## Rollup-To-Sequencer Withdrawals

Tokens that have been deposited into the rollup can be withdrawn out by the user
in the following way:

1. There is a withdrawal smart contract deployed on the rollup. Calling its
payable withdrawal function burns the transferred tokens and emits a
`Withdrawal` event.

2. The bridge withdrawer watches for withdrawal events. For each event emitted
by the smart contract, the withdrawer creates a sequencer-side action which
transfers the funds from the bridge account to the destination specified in
the withdrawal event. These actions are batched by rollup block into one sequencer
transaction, and the transaction is submitted to the sequencer.

[`BridgeUnlock` actions](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/proto/protocolapis/astria/protocol/transaction/v1/action.proto#L186)
have the following structure:

```proto
// `BridgeUnlock` represents a transaction that transfers
// funds from a bridge account to a sequencer account.
//
// It's the same as a `Transfer` but without the `asset` field
// and with the `memo` field.
message BridgeUnlock {
// the to withdraw funds to
astria.primitive.v1.Address to = 1;
// the amount to transfer
astria.primitive.v1.Uint128 amount = 2;
// the asset used to pay the transaction fee
string fee_asset = 3;
// The memo field can be used to provide unique identifying additional
// information about the bridge unlock transaction.
string memo = 4;
// the address of the bridge account to transfer from
astria.primitive.v1.Address bridge_address = 5;
// The block number on the rollup that triggered the transaction underlying
// this bridge unlock memo.
uint64 rollup_block_number = 6;
// An identifier of the original rollup event, such as a transaction hash which
// triggered a bridge unlock and is underlying event that led to this bridge
// unlock. This can be utilized for tracing from the bridge back to
// distinct rollup events.
//
// This field is of type `string` so that it can be formatted in the preferred
// format of the rollup when targeting plain text encoding.
string rollup_withdrawal_event_id = 7;
}
```

- [`Ics20Withdrawal`](https://github.com/astriaorg/astria/blob/d03059977c3a40590d66591c520bfda3a9b9de1c/proto/protocolapis/astria/protocol/transaction/v1/action.proto#L78)
represents a withdrawal from Astria to another IBC chain.

- If the `bridge_address` field is set, the funds are transferred out of a
bridge account. Alternatively, if `bridge_address` is unset but the signer
of the action is a bridge address, and the withdrawer address is the same
as the bridge address, the funds are transferred out of the bridge account.

- Examples for a withdrawal smart contracts: [`astria-bridge-contracts`](https://github.com/astriaorg/astria-bridge-contracts/tree/main)
- The events emitted by the smart contracts are defined in [`IAstriaWithdrawer`](https://github.com/astriaorg/astria-bridge-contracts/blob/038002e156c667419434204f9e5be43460da7995/src/IAstriaWithdrawer.sol#L22)

## One-Step IBC Bridging

### Deposit Into A Rollup

The user initiates an ICS20 withdrawal from some IBC chain to the sequencer.
The following metadata is provided:

- The destination chain address is the sequencer-side bridge address correspodning
to the destination rollup.
- The correct asset denomination.
- The rollup-side destination address is provided in the memo field.

When the ICS20 transfer is executed on the sequencer, the funds are locked into the
bridge address, and a `Deposit` event is created for that rollup's ID with the
user's rollup address.

The one-step withdrawal flow from a rollup to another IBC chain is as follows:
### Withdrawal From A Rollup

The User initiates a withdrawal from the rollup to an IBC destination using the
`withdrawToIbcChain` function of an `IAstriaWithdrawer`-compatible smart contract.
This contract calls burns the funds, emitting an `Ics20Withdrawal` event for the
withdrawer to process (as described above).

- the user withdraws the asset by burning it on the rollup, which emits a
`Withdrawal` event that includes the asset, the amount, and a memo.
- the memo contains the needed information for creating an Ics20 withdrawal on
In addition to the metadata in a sequencer-native withdrawal, the `Ics20Withdrawal`
event also provides the needed information for creating an Ics20 withdrawal on
the sequencer, such as the recipient destination chain address.
- the bridge withdrawer sees the `Withdrawal` event and submits a sequencer
transaction which withdraws the funds to the destination IBC chain.
- in the case that the transfer to the destination IBC chain fails, the
sequencer is notified of this, and emits a `Deposit` refunding the funds back
to the origin rollup address.

In the case that the transfer to the destination IBC chain fails, the sequencer
is notified of this via IBC, and emits a `Deposit` refunding the funds back to
the origin rollup address via the mint derivation process described above.

## Implementation

Expand All @@ -66,43 +214,11 @@ module, and the IBC-bridging logic is in the
[`ibc`](https://github.com/astriaorg/astria/tree/main/crates/astria-sequencer/src/ibc)
module.

The bridge related actions are:

- [`InitBridgeAccountAction`](https://github.com/astriaorg/astria/blob/6902ef35370e5980a76302fc756e1a9a56af21b5/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto#L167):
initializes the signer of the action as a bridge account. The associated rollup
ID and asset ID which this account accepts are provided. Optional `sudo_address`
and `withdrawer_address` fields can be provided, which are set to the action
sender if unset.
- the account's rollup ID and asset ID cannot be changed once initialized.
- the account cannot be re-initialized as a bridge account, and it cannot be
converted back into a non-bridge account.
- `sudo_address` is authorized to change the rollup account's `sudo_address`
and `withdrawer_address`.
- to make withdrawals from the bridge account, the `withdrawer_address` must
be the transaction signer.
- withdrawals are allowed using either `BridgeUnlockAction` or `Ics20Withdrawal`.
- [`BridgeLockAction`](https://github.com/astriaorg/astria/blob/6902ef35370e5980a76302fc756e1a9a56af21b5/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto#L188):
transfers funds to a bridge account, locking them and emitting a
[`Deposit`](https://github.com/astriaorg/astria/blob/6902ef35370e5980a76302fc756e1a9a56af21b5/proto/sequencerblockapis/astria/sequencerblock/v1alpha1/block.proto#L76).
The `destination_chain_address` is the rollup account funds are minted to.
- [`BridgeUnlockAction`](https://github.com/astriaorg/astria/blob/main/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto#L207):
transfers funds from a bridge account to another account. The asset transferred
is the one for the bridge account (ie. the asset ID specified in `InitBridgeAccountAction`).
The signer of this action must be the bridge account's `withdrawer_address`.
- [`BridgeSudoChangeAction`](https://github.com/astriaorg/astria/blob/6902ef35370e5980a76302fc756e1a9a56af21b5/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto#L222)
changes the bridge account's sudo and/or withdrawer addresses. The signer of
this action must be the bridge account's `sudo_address`.

The two IBC actions which can also perform bridging actions are an `IbcRelay`
which contains an `Ics20Transfer` packet, and `Ics20Withdrawal`.

- [`Ics20Transfer`](https://github.com/astriaorg/astria/blob/6902ef35370e5980a76302fc756e1a9a56af21b5/crates/astria-sequencer/src/ibc/ics20_transfer.rs#L370)
packets are received when an IBC transfer to Astria from another chain occurs.
If the recipient of the packet is a bridge account and the asset transferred is
valid, the funds are locked in the bridge account and a `Deposit` is created.
- [`Ics20Withdrawal`](https://github.com/astriaorg/astria/blob/6902ef35370e5980a76302fc756e1a9a56af21b5/proto/protocolapis/astria/protocol/transactions/v1alpha1/types.proto#L102)
represents a withdrawal from Astria to another IBC chain. If the `bridge_address`
field is set, the funds are transferred out of a bridge account. Alternatively,
if `bridge_address` is unset but the signer of the action is a bridge address,
and the withdrawer address is the same as the bridge address, the funds are
transferred out of the bridge account.
The rollup-side smart contracts can be found in the
[`astria-bridge-contracts`](https://github.com/astriaorg/astria-bridge-contracts/tree/4580ffc0747f463e304214bb29848e21e4e93e32)
repository.

The bridge-withdrawer service is responsible for watching for `Withdrawal` events
emitted by the rollup and submitting the corresponding sequencer transactions. The
bridge-withdrawer service is implemented in the [`astria-bridge-withdrawer`](https://github.com/astriaorg/astria/tree/main/crates/astria-bridge-withdrawer)
crate.
Loading