-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
127 additions
and
126 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
40 changes: 40 additions & 0 deletions
40
docs/src/specs/contracts/bridging/interop/examples/cross_chain_message.md
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,40 @@ | ||
# Interop Message Simple Use Case | ||
|
||
Before we dive into the details of how the system works, let’s look at a simple use case for a DApp that decides to use | ||
InteropMessage. | ||
|
||
For this example, imagine a basic cross-chain contract where the `signup()` method can be called on chains B, C, and D | ||
only if someone has first 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 calls the `signup_open` method. After that, any user on other | ||
chains can retrieve the `signup_open_msg_hash`, obtain the necessary proof from the Gateway (or another source), and | ||
call the `openSignup` function on any destination chain. |
82 changes: 81 additions & 1 deletion
82
docs/src/specs/contracts/bridging/interop/examples/interop_request_two_bridges.md
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 |
---|---|---|
@@ -1 +1,81 @@ | ||
# InteropCenter requestL2TransactionTwoBridges | ||
# InteropCenter requestL2TransactionTwoBridges | ||
|
||
## Generic usage of `BridgeHub.requestL2TransactionTwoBridges` | ||
|
||
`L1AssetRouter` is the only bridge that can handle base tokens. However, the `BridgeHub.requestL2TransactionTwoBridges` could be used by `secondBridgeAddress` on L1. A notable example of how it is done is how our [CTMDeploymentTracker](../../../l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol) uses it to register the correct CTM address on Gateway. You can read more about how Gateway works in [its documentation](../../gateway/overview.md). | ||
|
||
Let’s do a quick recap on how it works: | ||
|
||
When calling `BridgeHub.requestL2TransactionTwoBridges` the following struct needs to be provided: | ||
|
||
```solidity | ||
struct L2TransactionRequestTwoBridgesOuter { | ||
uint256 chainId; | ||
uint256 mintValue; | ||
uint256 l2Value; | ||
uint256 l2GasLimit; | ||
uint256 l2GasPerPubdataByteLimit; | ||
address refundRecipient; | ||
address secondBridgeAddress; | ||
uint256 secondBridgeValue; | ||
bytes secondBridgeCalldata; | ||
} | ||
``` | ||
|
||
- `secondBridgeAddress` is the address of the L1 contract that needs to perform the L1->L2 transaction. | ||
- `secondBridgeValue` is the `msg.value` to be sent to the `secondBridgeAddress`. | ||
- `secondBridgeCalldata` is the data to pass to the `secondBridgeAddress`. This can be interpreted any way it wants. | ||
|
||
1. Firstly, the Bridgehub will deposit the `request.mintValue` the same way as during a simple L1→L2 transaction. These funds will be used for funding the `l2Value` and the fee to the operator. | ||
2. After that, the `secondBridgeAddress.bridgehubDeposit` with the following signature is called | ||
|
||
```solidity | ||
struct L2TransactionRequestTwoBridgesInner { | ||
// Should be equal to a constant `keccak256("TWO_BRIDGES_MAGIC_VALUE")) - 1` | ||
bytes32 magicValue; | ||
// The L2 contract to call | ||
address l2Contract; | ||
// The calldata to call it with | ||
bytes l2Calldata; | ||
// The factory deps to call it with | ||
bytes[] factoryDeps; | ||
// Just some 32-byte value that can be used for later processing | ||
// It is called `txDataHash` as it *should* be used as a way to facilitate | ||
// reclaiming failed deposits. | ||
bytes32 txDataHash; | ||
} | ||
function bridgehubDeposit( | ||
uint256 _chainId, | ||
// The actual user that does the deposit | ||
address _prevMsgSender, | ||
// The msg.value of the L1->L2 transaction to be created | ||
uint256 _l2Value, | ||
// Custom bridge-specific data | ||
bytes calldata _data | ||
) external payable returns (L2TransactionRequestTwoBridgesInner memory request); | ||
``` | ||
|
||
Now the job of the contract will be to “validate” whether they are okay with the transaction to come. For instance, the `CTMDeploymentTracker` checks that the `_prevMsgSender` is the owner of `CTMDeploymentTracker` and has the necessary rights to perform the transaction out of the name of it. | ||
|
||
Ultimately, the correctly processed `bridgehubDeposit` function basically grants `BridgeHub` the right to create an L1→L2 transaction out of the name of the `secondBridgeAddress`. Since it is so powerful, the first returned value must be a magical constant that is equal to `keccak256("TWO_BRIDGES_MAGIC_VALUE")) - 1`. The fact that it was a somewhat non standard signature and a struct with the magical value is the major defense against “accidental” approvals to start a transaction out of the name of an account. | ||
|
||
Aside from the magical constant, the method should also return the information an L1→L2 transaction will start its call with: the `l2Contract` , `l2Calldata`, `factoryDeps`. It also should return the `txDataHash` field. The meaning `txDataHash` will be needed in the next paragraphs. But generally it can be any 32-byte value the bridge wants. | ||
|
||
1. After that, an L1→L2 transaction is invoked. Note, that the “trusted” `L1AssetRouter` has enforced that the baseToken was deposited correctly (again, the step (1) can _only_ be handled by the `L1AssetRouter`), while the second bridge can provide any data to call its L2 counterpart with. | ||
2. As a final step, following function is called: | ||
|
||
```solidity | ||
function bridgehubConfirmL2Transaction( | ||
// `chainId` of the ZKChain | ||
uint256 _chainId, | ||
// the same value that was returned by `bridgehubDeposit` | ||
bytes32 _txDataHash, | ||
// the hash of the L1->L2 transaction | ||
bytes32 _txHash | ||
) external; | ||
``` | ||
|
||
This function is needed for whatever actions are needed to be done after the L1→L2 transaction has been invoked. | ||
|
||
On `L1AssetRouter` it is used to remember the hash of each deposit transaction, so that later on, the funds could be returned to user if the `L1->L2` transaction fails. The `_txDataHash` is stored so that the whenever the users will want to reclaim funds from a failed deposit, they would provide the token and the amount as well as the sender to send the money to. |
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
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