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

Kl/interop #676

Open
wants to merge 84 commits into
base: gateway-release-candidate
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
0ce97bf
started changing things
kelemeno Jul 10, 2024
e892425
script fixes,
kelemeno Jul 24, 2024
5a7ec59
Merge branch 'kl/sync-layer-reorg' of ssh://github.com/matter-labs/er…
kelemeno Aug 5, 2024
9858b16
bootloader interop tx
kelemeno Aug 5, 2024
29a8be7
isSystem flag
kelemeno Aug 5, 2024
008d453
some debugging
kelemeno Aug 14, 2024
82183c4
comments in bootloader
kelemeno Aug 21, 2024
ed1bbd2
Merge branch 'sync-layer-stable' of ssh://github.com/matter-labs/era-…
kelemeno Aug 22, 2024
5f42021
fix tests
kelemeno Aug 22, 2024
1b0f335
zk ip storage in bridgehub
kelemeno Aug 22, 2024
f36129d
some fixes
kelemeno Aug 27, 2024
17a8300
Merge branch 'sync-layer-stable' of ssh://github.com/matter-labs/era-…
kelemeno Sep 19, 2024
a4b8744
merging sls fixes
kelemeno Sep 19, 2024
d53e7c5
cleanup
kelemeno Sep 22, 2024
71dae6e
bootloader more changes,
kelemeno Sep 23, 2024
872386b
Merge branch 'sync-layer-stable' of ssh://github.com/matter-labs/era-…
kelemeno Sep 23, 2024
fbeeb5f
contract too large, we will need to slim it down
kelemeno Sep 23, 2024
c0397bf
added MessageHashing to sys contracts
kelemeno Sep 24, 2024
b92f325
message hashing
kelemeno Sep 24, 2024
ff2016b
fixes
kelemeno Sep 24, 2024
812b7ad
hardhat config isSystem
kelemeno Sep 24, 2024
5434786
preparing L2AR for interop and linting
kelemeno Sep 25, 2024
2476a4e
some more changes/fixes
kelemeno Sep 26, 2024
37e2290
Merge branch 'sync-layer-stable' of ssh://github.com/matter-labs/era-…
kelemeno Oct 3, 2024
5b4eff0
Merge branch 'kl/sync-layer-reorg' of ssh://github.com/matter-labs/er…
kelemeno Oct 3, 2024
a271fc5
Merge branch 'kl/l2-native-token' of ssh://github.com/matter-labs/era…
kelemeno Oct 3, 2024
12271b9
zks fmt
kelemeno Oct 3, 2024
d3c6abc
Merge branch 'kl/l2-native-token' of ssh://github.com/matter-labs/era…
kelemeno Oct 4, 2024
d95f4f3
compilation issue
kelemeno Oct 4, 2024
0aa04ce
compile fixes
kelemeno Oct 7, 2024
1746cc0
Merge branch 'kl/l2-native-token' of ssh://github.com/matter-labs/era…
kelemeno Oct 7, 2024
321ea23
Merge branch 'kl/l2-native-token' of ssh://github.com/matter-labs/era…
kelemeno Oct 7, 2024
f9ef373
error message fixes
kelemeno Oct 7, 2024
4466531
debugging process
kelemeno Oct 9, 2024
7f3aa62
shuffle originTokenaddress for ntv for xL2 and
kelemeno Oct 11, 2024
2e11c5f
small bootloader fix
kelemeno Oct 13, 2024
a96ebe4
split bridgehub
kelemeno Oct 21, 2024
db4f00c
did not include
kelemeno Oct 21, 2024
80bf8f5
lint and fix foundry
kelemeno Oct 21, 2024
cb05e9a
some more test fixes
kelemeno Oct 21, 2024
befaf79
some more fixes
kelemeno Oct 21, 2024
829a171
fixing compilation
kelemeno Oct 22, 2024
4f3cb9b
Update system-contracts/contracts/L2GenesisUpgradeHelper.sol
kelemeno Oct 22, 2024
826fa4c
more fixes, legacy routing
kelemeno Oct 22, 2024
55f98da
lint
kelemeno Oct 22, 2024
b70380b
ts lint
kelemeno Oct 22, 2024
cea3bdb
hashes fix
kelemeno Oct 22, 2024
18897c8
system contrac hashes
kelemeno Oct 22, 2024
b949f86
small changes in BH
kelemeno Oct 22, 2024
bba0d44
some more fixes
kelemeno Oct 23, 2024
47b0bbe
Merge branch 'kl/sync-layer-reorg' of ssh://github.com/matter-labs/er…
kelemeno Oct 23, 2024
c6bf125
bootloader issue
kelemeno Oct 23, 2024
f3061f3
interopHandler
kelemeno Oct 25, 2024
ef40deb
bootloader tx1
kelemeno Oct 25, 2024
c71950e
Merge branch 'kl/split-bh' of ssh://github.com/matter-labs/era-contra…
kelemeno Oct 25, 2024
39c601c
merge bridgehub splitting fixes
kelemeno Oct 27, 2024
5d1500b
new bridgehub interface = interopCenter
kelemeno Oct 28, 2024
c84d175
linting and startup fixes
kelemeno Oct 28, 2024
7e455a6
refund recipient
kelemeno Oct 28, 2024
c48dfc1
IC changes
kelemeno Oct 29, 2024
14756e9
changes for Vlad
kelemeno Oct 29, 2024
10f2a5b
got swap bridging back to work
kelemeno Oct 30, 2024
8b8cf1f
moving to l1-contracts, and renaming
kelemeno Oct 31, 2024
cd2cf27
default account test
kelemeno Oct 31, 2024
d44807a
interop handler contract address
kelemeno Oct 31, 2024
b38f587
lint
kelemeno Oct 31, 2024
b6b351e
preprocessing
kelemeno Oct 31, 2024
ee7b670
importing for compilation
kelemeno Nov 3, 2024
40a73a2
more paths for compilation
kelemeno Nov 3, 2024
c5ee56c
modified paths
kelemeno Nov 5, 2024
de5d28d
contracts path
kelemeno Nov 5, 2024
e20fa6c
some compilation issues, cleanup
kelemeno Nov 5, 2024
2515041
linting and add sendCall
kelemeno Nov 6, 2024
d40d58e
addresses as constants
kelemeno Nov 6, 2024
4d1d054
saving L2ARDev just in case
kelemeno Nov 13, 2024
0dcdefc
made l2 deploy scripts era_test_node compatible
kelemeno Nov 13, 2024
da26030
folders for l2 abstract tests
kelemeno Nov 13, 2024
48298f7
previous interop method cleanup
kelemeno Nov 13, 2024
a14d12a
linting
kelemeno Nov 13, 2024
ecfbd5b
reved function in test
kelemeno Nov 13, 2024
df00dcf
Merge branch 'gateway-release-candidate' of ssh://github.com/matter-l…
kelemeno Nov 13, 2024
a9a5a7d
interop docs
kelemeno Nov 14, 2024
7a2a4bd
compilation and tests
kelemeno Nov 15, 2024
527db0d
trying to fix genesis upgrade
kelemeno Nov 15, 2024
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
Binary file added docs/bridging/bridgehub/img/aliased_account.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/bridging/bridgehub/img/chain_root.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/bridging/bridgehub/img/gateway_chains.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/bridging/bridgehub/img/global_root.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/bridging/bridgehub/img/merkle_proof.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 111 additions & 0 deletions docs/bridging/bridgehub/interop_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# InteropExplained - ep 1 - Intro

# Basics

## What is interop?

Interop is a way to communicate: observe messages, send assets, execute calls, bundle of calls and transactions between two ZKStack chains.

**Observe messages**
Allows you to see that some interop message (think about it as special Event) was created on the source chain.

**Send assets**
Allows you to send different assets (ERC20) between chains.

**Execute calls**
Allows you to call a contract on a remote chain, with given calldata and value. With interop, you automatically get an account (a.k.a aliasedAccount) on each chain, that you can control from the source chain.

**Execute bundle of calls**
Allows you to have multiple remote calls **tied together** in a bundle - making sure that all of them execute at once.

**Execute transactions**
You are able to create a transaction on the source chain, that will be automatically executed on the destination chain - while selecting from different cross-chain Paymaster solutions to cover the gas fees.

## How do I use it?

For the simplest scenario - of executing a contract on a destination chain:

```solidity
cast send source-chain-rpc.com INTEROP_CENTER_ADDRESS sendInteropWithSingleCall(
0x1fa72e78 // destination_chain_id,
0xb4AB2FF34fa... // destination_contract,
0x29723511000000... // destination_calldata,
0, // value
100_000, // gasLimit
250_000_000, // gasPrice
..
)
```

While this looks very similar to ‘regular’ call, there are some caveats, especially around failures and error handling. Please read the FAQ below.

### Simple scenario FAQ

- Who pays for gas?
- when you use this method, your account must have `gasLimit * gasPrice` worth of destination chain tokens available on the source chain. (so if you send this request from Era and destination chain is Sophon with SOPH, you must have SOPH tokens on Era)
- There are of course far more payment options (but that’s in later sections).
- How does the destination contract know it is from me?
- destination contract will be called with `msg.sender` equal to `keccak(source_account, source_chain)[:20]` (in the perfect world, we would have used `source_account@source_chain` - similar to how email works - but as we have to fit into 20 bytes ethereum address, we do a keccak).
- Who executes it on the destination chain?
- This call will be ‘auto executed’ on the destination chain. You as a user don’t have to do anything.
- What if it fails out of gas? Or what if I set too low gasPrice?
- In either of these scenarios, you can ‘retry’ it, by using `retryInteropTransaction` (not implemented yet).
```solidity
cast send source-chain.com INTEROP_CENTER_ADDRESS retryInteropTransaction(
0x2654.. // previous interop transaction hash from above
200_000, // new gasLimit
300_000_000 // new gasPrice
)
```
- IMPORTANT: depending on your use case, it might be very important to retry rather than to create a new `sendInteropWithSingleCall` - for example if your call includes some larger asset transfer, creating the new `sendInteropWithSingleCall` would attempt to freeze/burn these assets again.
- If some of my assets were burned when I did the transaction, but it failed on destination chain, how do I get them back?
- If your transaction failed on destination chain, you can either try to retry it with more gas, higher gas limits (see above) or cancel it (not implemented yet):
```solidity
cast send source-chain INTEROP_CENTER_ADDRESS cancelInteropTransaction(
0x2654.. // previous interop transaction
100_000 // gasLimit (yes, cancellation needs gas too - but just to mark as cancelled)
300_000_000 // gasPrice
)
```
- after that, you’ll need to call the `claimFailedDeposit` methods on your source chain contracts to get the assets that were burned when you did the transaction back - details of those are contract specific.

### Complex scenario

What if I want to transfer some USDC to Sophon chain, then swap to PEPE coin, and then transfer the results back?

You’ll have to create a bunch of **InteropCalls** (transfer USDC, do a swap etc), then put them into a common **InteropBundle,** and then create the InteropTransaction to execute them on the destination chain.

The exact details will be in the next article.

# Technical details

### How does native bridging differ from a third party bridging?

There are roughly 2 types of bridges: Native and third party.

Normal native bridging allows you to bridge assets ‘up and down’ (so from L2 to L1 and from L1 to L2), interop (which is also a form of native bridging) allows you to move them between different L2s. So instead of doing the ‘round trip’ (from source L2 to L1, and then from L1 to destination L2), you can go between 2 L2s directly, saving you both the latency and cost.

Third party bridging can work between 2 different L2s, but it depends on its own liquidity. So while you as a user, get your assets on destination chain ‘immediately’, they are actually coming from the bridge’s own tokens, and liquidity providers have to rebalance them using native L1<>L2 bridges (which means that they need to have a reserve of tokens on both sides, which cost them money, which usually results in higher fees).

The good news, is that third party bridges can use interop to improve the transfers of their tokens too - by using the InteropMessage layer - more details below.

### How fast is it?

Interop speed depends on its lowest level - InteropMessage propagation speed - which boils down to the question, at which moment are you (as destination chain) sure that the message created by source chain is valid.

As security is our top priority, the default Interop will wait for the ZK proof - which might take around 10 minutes.

At the same time, we plan to release another INTEROP_CENTER contract (under a different address, but with same interface) - that would work within 1 second, but with additional risks (similar to optimistic chains).

### 4 Levels of interop

When analysing interop, you can break it into 4 levels - which allows you to choose the level to integrate on:

- InteropMessages - lowest level - directly used by 3rd party bridges and other protocols.
- Interop Call - medium level - used by ‘library’ contracts.
- InteropCallBundle - high level - used by ‘user-visible’ contracts.
- InteropTransaction - highest level - used by UX & frontends.

![image.png](./img/message_layers.png)

We will be covering the details of each layer in the next articles.
143 changes: 143 additions & 0 deletions docs/bridging/bridgehub/interop_2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# InteropExplained - ep 2 - Basic messages

In this article, we’re going to cover the lowest level of the interop stack - Interop Messages - the interface that all other things are build on.

We’ll look at the details of the interface, some use cases.

This is an ‘advanced’ document - as most users and app developers would usually interact with higher levels of interop, but it is still worth to see how the internals look like.

## Basics

![Interop Messages are the lowest level of our stack.](./img/message_layers_A.png)

Interop Messages are the lowest level of our stack.

InteropMessage contains ‘data’ and it offers two methods:

- send a message
- verify that a given message was sent on some chain

Notice, that the message itself doesn’t have any ‘destination chain’ or address - it is simply a payload that a user (or contract) is creating. Think about it as a ‘broadcast’.

The `InteropCenter` is a contract, that is pre-deployed on all the chains on a fixed address `0x00..010008`

```solidity
contract InteropCenter {
// Sends interop message. Can be called by anyone.
// Returns the unique interopHash.
function sendInteropMessage(bytes data) returns interopHash;

// Interop message - uniquely identified by the hash of the payload.
struct InteropMessage {
bytes data;
address sender; // filled by InteropCenter
uint256 sourceChainId; // filled by InteropCenter
uint256 messageNum; // a 'nonce' to guarantee different hashes.
}

// Verifies if such interop message was ever producted.

Check warning on line 38 in docs/bridging/bridgehub/interop_2.md

View workflow job for this annotation

GitHub Actions / typos

"producted" should be "produced".
function verifyInteropMessage(bytes32 interopHash, Proof merkleProof) return bool;
}
```

When you call the `sendInteropMessage`, the `InteropCenter` will add some additional fields (like your sender address, source chain id and messageNum - which acts as a nonce to guarantee that the hash of this struct is globally unique) - and return you the `interopHash`.

This is now a globally unique identifier, that you can use on any chain in the network, to call the `verifyInteropMessage`.

![A message created on one chain can be verified on any other chain.](./img/verify_message.png)

A message created on one chain can be verified on any other chain.

**How do I get the proof**

You can notice that `verifyInteropMessage` has a second argument - a proof, that you have to pass. This proof is a merkle tree proof (more details below) - and you can get it by querying the [chain](https://docs.zksync.io/build/api-reference/zks-rpc#zks_getl2tol1msgproof) (note image is incorrect), or you can build it yourself off-chain - by looking at the chain's state on L1.

**How does the interop message differ from other layers (InteropTransactions, InteropCalls)**
Interop message (as the most basic layer), doesn’t have any additional features - no support for picking destination chains, no support for nullifiers/replays, no cancellation etc.

If you need any of these, you might consider integrating on the higher layer of the interop (Call or Bundle) instead.

## Simple use case

Before we dive into the details of how the system works, let’s see a simple use case of the Dapp, that would decide to use InteropMessage.

For our example, let’s imagine the very trivial cross-chain contract - where you can call `signup()` method on chain B, C and D only if someone called `signup_open()` on chain A.

```solidity
// Contract deployed on chain A.
contract SignupManager {
public bytes32 sigup_open_msg_hash;
function signup_open() onlyOwner {
// We are open for business
signup_open_msg_hash = InteropCenter(INTEROP_CENTER_ADDRESS).sendInteropMessage("We are open");
}
}

// Contract deployed on all other chains.
contract SignupContract {
public bool signupIsOpen;
// Anyone can call it.
function openSignup(InteropMessage message, InteropProof proof) {
InteropCenter(INTEROP_CENTER_ADDRESS).verifyInteropMessage(keccak(message), proof);
require(message.sourceChainId == CHAIN_A_ID);
require(message.sender == SIGNUP_MANAGER_ON_CHAIN_A);
require(message.data == "We are open");
signupIsOpen = true;
}

function signup() {
require(signupIsOpen);
signedUpUser[msg.sender] = true;
}
}
```

In the example above, the signupManager on chain A, is calling the `signup_open` method. Then any user on other chains, can get the `signup_open_msg_hash` , get the necessary proof, and call the `openSignup` function on any destination chain.

## Deeper technical dive

Let’s see what’s happening inside InteropCenter when new interop message is created.

```solidity
function sendInteropMessage(bytes data) {
messageNum += 1;
msg = InteropMessage({data, msg.sender, block.chain_id, messageNum});
// Does L2->L1 Messaging.
sendToL1(abi.encode(msg));
return keccak(msg);
}
```

As you can see, it fills the necessary data, and then calls the `sendToL1` method.

The `sendToL1` is a system contract, that collects all these messages, creates a merkle tree out of them at the end of the batch, and sends them to the settlement layer (L1 or Gateway) when it commits the batch.
To see an exact description of the Merkle root structure read the [nested l3 l1 messaging doc](../../gateway/nested_l3_l1_messaging.md).

![image.png](./img/chain_root.png)

The settlment layer receives the messages and once the proof for the batch is submitted (or to be more correct the ‘execute’ step), it will add the root of this merkle tree to its `globalRoot`.

Check warning on line 118 in docs/bridging/bridgehub/interop_2.md

View workflow job for this annotation

GitHub Actions / typos

"settlment" should be "settlement".

![image.png](./img/global_root.png)

`globalRoot` is the root of the merkle tree, that contains all the messages from all the chains and each chain is regularly reading its value from settlment layer.

Check warning on line 122 in docs/bridging/bridgehub/interop_2.md

View workflow job for this annotation

GitHub Actions / typos

"settlment" should be "settlement".

![Each chain is regularly syncing data from the settlement layer, and fetches that globalRoot at this time.](./img/gateway_chains.png)

Each chain is regularly syncing data from its settlement layers, and fetches that globalRoot at this time.

If a user now wants to call the `verifyInteropMessage` on some chain, they first have to ask the chain for the merkle path from the batch that they are interested into, up to the `globalRoot`. Afterwards, they can simply provide this path when calling a method on the destination chain (in our case the `openSignup` method).

![image.png](./img/merkle_proof.png)
Note the chain root across batches is missing from this image.

**What if Chain doesn’t provide the proof?**

In such scenario, user can re-create the merkle proof, based on the L1 data. As each interopMessage is sent there too.

**Global roots change every second**

Yes, as new chains are proving their blocks, the global root keeps changing. The chains will keep some number of historical global roots (around 24h), so that the Merkle path that you just generated stays valid.

**Is this secure? could chain D operator just use a different global root?**

Yes. If chain D operator was malicious, and tried to use a different global root, they would not be able to submit the proof of their new batch to the settlment layer - as part of the proof’s public input is the global root.

Check warning on line 143 in docs/bridging/bridgehub/interop_2.md

View workflow job for this annotation

GitHub Actions / typos

"settlment" should be "settlement".
Loading
Loading