-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bridge and cross chain messaging docs. (#1087)
* Added a couple of docs pages. * Addressed PR comments. * Fixed typo. * Fixed typo. --------- Co-authored-by: StefanIliev545 <[email protected]>
- Loading branch information
1 parent
dfc2b28
commit 96565a7
Showing
3 changed files
with
140 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
--- | ||
--- | ||
# The Standard Obscuro Bridge | ||
|
||
The standard Obscuro bridge is a trustless and decentralised asset bridge that uses a wrapped token mint and burn pattern. Under the hood it is based on the cross chain messaging protocol and exists entirely as a smart contract without the need of separate runnables or nodes. | ||
|
||
## Contract Addresses | ||
|
||
## Interacting with the contract | ||
|
||
Users interact with the bridge contract using either the `sendERC20` or `sendNative` functions. In order to send ERC20 tokens they need to be whitelisted. | ||
|
||
The pattern of interacting on both layers with `sendERC20` is as follows: | ||
1. Approve the bridge contract to act on your behalf for X amount of tokens using the standard ERC20 `approve()` method. | ||
2. Call `sendERC20` with the amount approved and the address of the recipient on the other side. | ||
3. Wait a tiny bit for the cross chain protocol to synchronize the state of the layers. | ||
4. Use your assets on the other layer. | ||
|
||
Interacting with `sendNative` does not require approving an allowance, but instead just add value to the transaction (or evm call). | ||
The value received by the bridge contract during the execution of `sendNative` is what will be logged as transfer to the other layer. | ||
|
||
## Layer 1 To Layer 2 Specifics | ||
|
||
The `ObscuroBridge.sol` contract is responsible for managing the layer 1 side of the bridge. It's the "bridge to Obscuro". | ||
The `EthereumBridge.sol` contract is responsible for managing the layer 2 side of the bridge. It's the "bridge to Ethereum". | ||
|
||
In order to bridge tokens over they need to be whitelisted. **Initially only accounts with the admin role can whitelist tokens!** When the protocol has matured this whitelisting functionality will change. If you want your token to be whitelisted, get in touch with us through our discord. | ||
|
||
When an asset is whitelisted, the bridge internally uses the `publishMessage` call on the `MessageBus` contract which is deployed and exposed by the `ManagementContract` on layer 1. In the message that is published the bridge "tells" the other side of it, which resides on layer 2 that a token has been whitelisted. This in turn creates a wrapped version of the token on the other side. This version of the token can only be minted and burned by the layer 2 bridge. Notice that when the bridge "tells" its counter part, the message is not automatically delivered by Obscuro. To automate the process one needs to have a relayer in place that will do it automatically and a system in place that funds the gas costs of said relayer. As the network grows general purpose relayers might pop up. If you are developing or have developed such a relayer, contact us on discord to get it listed. | ||
|
||
* Minting allows to create fresh funds on the L2 when they get locked on L1. | ||
* Burning allows to destroy supply on L2 in order to release it from the bridge on L1. | ||
|
||
The protocol to bridge assets using `mint`/`burn` is based on the `MessageBus`'s `publishMessage` too. The `ObscuroBridge` tells the layer 2 that it has locked a certain amount of tokens and because of that address Y should receive the same amount of the wrapped token counterpart. This is when `mint` comes into play. On the other hand, the `EthereumBridge` burns tokens when received and tells the L1 that it has in fact done so. This is interpreted by the L1 smart contract as evidence to release locked funds. This message once again needs to be relayed for the process to work, but by a separate L1 relayer. | ||
|
||
|
||
## Security | ||
|
||
The bridge inherits the `CrossChainEnabledObscuro` contract. It provides a modifier `onlyCrossChainSender` that can be attached to public smart contract functions in order to limit them. Normally you can limit a function to be called by `msg.sender`. With this new modifier you can limit a function to be called from the equivalent of `msg.crosschainsender`. In the `ObscuroBridge` we limit the cross chain sender to the remote bridge only like this `onlyCrossChainSender(remoteBridgeAddress)`. | ||
|
||
The bridge initialization phase links the two bridge contracts (ObscuroBridge, EthereumBridge) together. This linking is finalized when both contracts know the remote address on the opposite layer. Using those remote addresses, the bridges limit incoming `receiveAsset` messages to only be callable by the `CrossChainMessenger` contract sitting on the same layer. This `CrossChainMessenger` contract provides the necessary context to the bridge about the sender of the cross chain message. This allows to validate message is coming from the `remoteBridgeAddress`. The sender address is determined inside of `publishMessage` by taking the `msg.sender`. This `msg.sender` is put inside the metadata of each message. | ||
|
||
The result of this setup is that `receiveAsset` is only callable by having a valid cross chain message coming from the correct contract. Anything else will get rejected and cause the call to revert. | ||
|
||
The layer 2 bridge works much the same way as the L1 bridge and uses the `MessageBus` to facilitate communication. | ||
|
||
## Building Bridges | ||
|
||
The standard bridge was designed to work without requiring access to any private platform capabilities. This means that anyone can build a bridge using the same cross chain messaging API. There is no permissioning required to do so. We encourage you to build novel bridges and we'd love to assist you in any way possible. You are welcome at our discord if you have questions, need support or just want to discuss your bridge idea! | ||
|
||
## Interface | ||
|
||
This is the interface that can be used in order to interact with the bridge. | ||
|
||
`sendNative` is where you send raw ethereum as a call value. Said value received by the bridge will be forwarded to the `receiver`. | ||
`sendERC20 ` is the previously described function that allows for moving tokens. | ||
|
||
```solidity | ||
interface IBridge { | ||
function sendNative(address receiver) external payable; | ||
function sendERC20( | ||
address asset, | ||
uint256 amount, | ||
address receiver | ||
) external; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
--- | ||
--- | ||
# Obscuro Cross Chain Messaging | ||
|
||
Obscuro is equipped with a cross chain messaging protocol that enables sending and receiving messages across layers securely and in an entirely decentralized fashion. | ||
|
||
The core contract that provides this functionality is the `MessageBus`. It exists both on L1 and L2. In the L1 you can find it under the management contract whilst on the L2 it is created and managed by the protocol. | ||
|
||
## How It Works | ||
|
||
Users call the `publishMessage` function on the `MessageBus`. This function emits an event that records what message is being published, who is calling the method and some other bits. This event later on gets picked up by the protocol which ensures that the message is available on the counterpart `MessageBus`. | ||
|
||
When the message is made available, users can call the `verifyMessageFinalized` method in the `MessageBus` on the recipient layer. This method will return `true` if the message was indeed sent on the other layer as presented. Same `msg.sender`, same `payload`, same `nonce`. | ||
|
||
This allows to have a behaviour much like querying questions - **'Has the address 0xAAAA.. received 25WETH tokens on the bridge with address 0XAB0FF?'**. If the bridge on this address has called `publishMessage` saying **'I have received 25 WETH tokens with recipient 0xAAAA.`** the query will return true. | ||
|
||
When messages are published on the Obscuro layer (L2) the transport to L1 is done by the management contract upon rollup submission. Messages delivered however need to wait for the challenge period of the rollup before being considered final. This ensures that rollups along with the messages they carry can be challenged. This is the logical equivalent of challenge period for optimistic rollups. | ||
|
||
## Advanced capabilities | ||
|
||
The `MessageBus` contract provides a way to query non finalized delivered messages. Those are the messages that are still within the time window allowing for a rollup to be challenged. Furthermore the contract gives the time they will become final at. This is done using the `getMessageTimeOfFinality` method. | ||
|
||
Building on top of this message "preview" functionality allows for dApps that provide faster than challenge period finality. For example, a contract can provide the withdrawn amount from a bridge immediately with a fee. Doing this will transfer the rights for withdrawing the deposit to said party when the message is finalized. However providing such an early withdrawal at a fee exposes the provider to the risk of the rollup being challenged and proven invalid. This risk should be lower than average as it depends on submitting an invalid rollup which requires breaking SGX. | ||
|
||
## Security | ||
|
||
The protocol only listens to the events on the contract address that is bound to the management contract. Messages are bound to L1 blocks and in the event of reorganization the state will be recalculated. L2 messages are bound to L1 block hashes too, resulting in a system where rollups that update the state can only be applied to the correct fork. If there is a mismatch, the state update will be rejected by the management contract. | ||
|
||
The protocol controls the keys for the L2 contract. They are hidden within SGX and there are no codepaths that expose them externally. To create a transaction that publishes messages on the L2, one would need to break SGX in order to extract the private key controlling the `MessageBus` L2 contract. | ||
However if one were to produce such a transaction, the code path would reject it automatically as it scans every external transaction. The protocol rejects external transactions that are signed by the hidden signer. This means there is no way to get such a transaction to be processed and even if local SGX is bypassed, validators will immediately pick it up. | ||
|
||
|
||
## Interface | ||
|
||
Those are the current interfaces required to interact with the `MessageBus` contracts. If you encounter issues or have questions, please reach out on our discord. | ||
|
||
```solidity | ||
interface Structs { | ||
struct CrossChainMessage { | ||
address sender; // The contract/address which called the publishMessage on the message bus. | ||
uint64 sequence; // The sequential index of the message for the sending address. | ||
uint32 nonce; // Provided by the smart contract. | ||
uint32 topic; // Can be used to separate messages and provide basic versioning. | ||
bytes payload; // The actual encoded message. | ||
uint8 consistencyLevel; | ||
} | ||
} | ||
interface IMessageBus { | ||
function publishMessage( | ||
uint32 nonce, | ||
uint32 topic, | ||
bytes calldata payload, | ||
uint8 consistencyLevel | ||
) external returns (uint64 sequence); | ||
function verifyMessageFinalized( | ||
Structs.CrossChainMessage calldata crossChainMessage | ||
) external view returns (bool); | ||
function getMessageTimeOfFinality( | ||
Structs.CrossChainMessage calldata crossChainMessage | ||
) external view returns (uint256); | ||
} | ||
``` |