Skip to content

Commit

Permalink
bridging article
Browse files Browse the repository at this point in the history
  • Loading branch information
RogerLamTd committed Jun 10, 2024
1 parent 8d341d0 commit 3bf3066
Showing 1 changed file with 18 additions and 16 deletions.
34 changes: 18 additions & 16 deletions packages/docs-site/src/content/docs/core-concepts/bridging.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ The Taiko protocol's design, specifically its Ethereum-equivalence enables secur

Taiko deploys two smart contracts which store the hashes of the other chain:

- TaikoL1 stores a blockNumber->blockHash mapping `l2Hashes` (deployed on Ethereum)
- TaikoL2 stores a blockNumber->blockHash mapping `l1Hashes` (deployed on Taiko)
- TaikoL1 stores the L2 world state root on L1 (deployed on Ethereum)
- TaikoL2 stores the L1 world state root on L2 (deployed on Taiko)

Every time an L2 block is created on Taiko, the hash of the enclosing block on L1 is stored in the TaikoL2 contract. And every time an L1 block is verified, the L2 hash is stored in the TaikoL1 contract (only the latest one, if multiple ones are verified at once).
Every time an L2 block is created on Taiko, the world state root of the enclosing block on L1 is stored in the [TaikoL2](https://github.com/taikoxyz/taiko-mono/blob/fbfcc7f3810d0122f46673944c39e5f4d759d4e0/packages/protocol/contracts/L2/TaikoL2.sol#L151) contract using the `anchor` transaction.

The L2 world state root is stored in the TaikoL1 contract using the `syncChainData` function call in
[`LibVerifying`](https://github.com/taikoxyz/taiko-mono/blob/fbfcc7f3810d0122f46673944c39e5f4d759d4e0/packages/protocol/contracts/L1/libs/LibVerifying.sol#L191).

Taiko by default synchronizes the world state roots cross-chain with the above mechanism.

### Merkle trees enable verifying values exist on the other chain

Expand All @@ -32,24 +37,24 @@ Merkle trees are a data storage structure that allows a lot of data to be finger
- The value, this is the value we are checking is inside the merkle root
- A list of intermediate sibling hashes, these are the hashes that enable the verifier to re-calculate the merkle root

You can get the latest known merkle root stored on the destination chain by calling `getCrossChainBlockHash(0)` on the TaikoL1/TaikoL2 contract. You can get the value / message to verify and the sibling hashes for that latest known merkle root by asking for it with the standard RPC call `eth_getProof` on the "source chain". Then you just need to send them to be verified against that latest known block hash that is stored in a list on the "destination chain".
The `signalForChainData` function is used to store and retrieve chain data in the `SignalService` contract. This is a multi-purpose storage function, we can sync the state root or signal service storage roots as needed for each chain respectively.

A verifier will take the value (a leaf in the merkle tree) and the sibling hashes to re-calculate the merkle root. If the calculated merkle root matches the one that is stored in the destination chain's list of block hashes (the block hashes of the source chain), then we have proved that the message was sent on the source chain, assuming the source chain block hashes stored on the destination chain were correct.
A verifier will take the value (a leaf in the merkle tree) and the sibling hashes to re-calculate the merkle root. If the calculated merkle root matches the one that is stored in the destination chains list of block hashes (the block hashes of the source chain), then we have proved that the message was sent on the source chain, assuming the source chain block hashes stored on the destination chain were correct.

## The signal service

Taiko's signal service is a smart contract available on both L1 and L2, available for any dapp developer to use. It's what uses the previously mentioned merkle proofs to provide a service for secure cross-chain messaging.
Taiko's signal service is a smart contract available on both L1 and L2, available for any dapp developer to use. It uses the previously mentioned merkle proofs to provide a service for secure cross-chain messaging.

You can store signals and check if a signal was sent from an address. It also exposes one more important function: `isSignalReceived`.
You can store signals and check if a signal was sent from an address. It also exposes an important function: `verifySignalReceived`.

What does this function do? The first thing to understand is that the Taiko protocol maintains two important contracts:

- `TaikoL1`
- `TaikoL2`

These contracts both keep track of the block hashes on the **other chain**. So TaikoL1, which is deployed on Ethereum, has access to the latest block hashes on Taiko. And TaikoL2, which is deployed on Taiko, has access to the latest block hashes on Ethereum.
These contracts both keep track of the world state roots on the **other chain**. So TaikoL1, which is deployed on Ethereum, has access to the latest world state roots on Taiko. And TaikoL2, which is deployed on Taiko, has access to the latest world state roots on Ethereum.

So, `isSignalReceived` can prove on either chain that you sent a signal to the Signal Service on the other chain. A user or dapp can call `eth_getProof`(https://eips.ethereum.org/EIPS/eip-1186) which generates a merkle proof.
So, `verifySignalReceived` can prove on either chain that you sent a signal to the Signal Service on the other chain. A user or dapp can call `eth_getProof` which generates a merkle proof.

You need to provide `eth_getProof` with:

Expand All @@ -65,7 +70,7 @@ Let's walk through an example:

1. First, we can send a message on some source chain, and store it on the signal service.
2. Next, we call `eth_getProof`, which will give us a proof that we did indeed send a message on the source chain.
3. Finally, we call `isSignalReceived` on the destination chain's SignalService which essentially just verifies the merkle proof. `isSignalReceived` will look up the block hash you are asserting you had stored a message on the source chain (where you originally sent the message), and with the sibling hashes inside the merkle proof it will rebuild the merkle root, which verifies the signal was included in that merkle root—meaning it was sent.
3. Finally, we call `verifySignalReceived` on the destination chain's SignalService which essentially just verifies the merkle proof. `verifySignalReceived` will look up the block hash you are asserting you had stored a message on the source chain (where you originally sent the message), and with the sibling hashes inside the merkle proof it will rebuild the merkle root, which verifies the signal was included in that merkle root—meaning it was sent.

And boom! We have sent a cross-chain message. If this is confusing, you can also find a simple dApp that was built during one of our workshops to demonstrate the fundamentals. You can find it [here](https://github.com/taikoxyz/MessageServiceShowCaseApp).

Expand All @@ -89,7 +94,7 @@ Taiko's bridge utilizes the Signal Service we described. Here is the general use
With the current design there are 2 ways to bridge `Ether`:

1. `Ether` only case: The user interacts directly with the Bridge contract by calling `sendMessage`
2. `ERC-XXX` + `Ether` + case: The user interacts with the `ERCXXXVault` (ERC20, ERC721, ERC1155) because he/she wants to bridge over some tokens, but in case he/she fills the `message.value` field, also `Ether` will be bridged
2. `ERC-XXX` + `Ether` case: The user interacts with the `ERCXXXVault` (ERC20, ERC721, ERC1155) because they want to bridge over some tokens, but if they fill the `message.value` field, `Ether` will also be bridged

### How does ERC-20 (or ERC-721, ERC-1155) bridging work?

Expand All @@ -100,18 +105,15 @@ ERC-20 tokens originate from a canonical chain. To send a token and bridge it to
Here are the overall steps for transferring canonical ERC-20 (the overall process is identical for ERC-721, and ERC-1155 token types as well!) from a source chain to the destination chain:

1. A contract for the ERC-20 (or ERC-721, ERC-1155) must first be deployed on the destination chain (will be done automatically by the ERC20Vault if not already deployed)

2. Call `sendToken` on the source chain ERC20Vault, this will **transfer** the amount by using the `safeTransferFrom` function on the canonical ERC-20 contract, on the source chain, to the ERC20Vault.

3. The vault contract (via the Bridge) sends a message to the Signal Service (on the source chain), this message will contain some metadata related to the bridge request, but most importantly it includes the calldata for the `receiveToken` method.

4. Process the message on the destination chain by submitting a merkle proof (generated from the source chain), proving that a message is included in the state of the source chain Signal Service. After verifying this occurred and doing some checks, it will attempt to invoke the `receiveToken` method encoded in the message. This will **mint** ERC-20 (or ERC-721, ERC-1155) on the BridgedERC20 contract to the `to` address on the destination chain!

#### Bridge from destination chain back to the canonical chain

Okay now let's do the reverse, how do we transfer a bridged token from a source chain to the destination chain? (Destination chain in this case is the canonical chain, where the original token lives.)
Okay now lets do the reverse, how do we transfer a bridged token from a source chain to the destination chain? (Destination chain in this case is the canonical chain, where the original token lives.)

1. A contract for the ERC-20 (or ERC-721, ERC-1155) already exists on the canonical chain, so no need to deploy a new one.
2. Call `sendToken` on the source chain token vault contract, this will **burn** the ERC-20 on the BridgedERC20 contract.
3. The vault contract (via the Bridge) sends a message to the Signal Service (on the source chain), this message will contain some metadata related to the bridge request, but most importantly it includes the calldata for the `receiveToken` method.
4. Process the message on the destination chain by submitting a merkle proof (generated from the source chain), proving that a message is included in the state of the source chain Signal Service. After verifying this occurred and doing some checks, it will attempt to invoke the `receiveToken` method encoded in this message. This will **transfer** the amount from the destination chain TokenVault to the `to` address on the destination chain.
4. Process the message on the destination chain by submitting a merkle proof (generated from the source chain), proving that a message is included in the state of the source chain SignalService. After verifying this occurred and doing some checks, it will attempt to invoke the `receiveToken` method encoded in this message. This will transfer the amount from the destination chain TokenVault to the `to` address on the destination chain.

0 comments on commit 3bf3066

Please sign in to comment.