Skip to content

Commit

Permalink
trying to fix genesis upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
kelemeno committed Nov 15, 2024
1 parent 7a2a4bd commit 527db0d
Show file tree
Hide file tree
Showing 15 changed files with 137 additions and 79 deletions.
54 changes: 25 additions & 29 deletions docs/bridging/bridgehub/interop_1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## What is interop?

Interop is a way to communicate: observe messages, send assets, execute calls, bundle of calls and transactions between two ZKStack chains.
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.
Expand Down Expand Up @@ -42,36 +42,32 @@ While this looks very similar to ‘regular’ call, there are some caveats, esp
### 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).
- 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).
- 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.
- 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.
- 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.
- 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

Expand All @@ -97,7 +93,7 @@ The good news, is that third party bridges can use interop to improve the transf

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.
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).

Expand All @@ -112,4 +108,4 @@ When analysing interop, you can break it into 4 levels - which allows you to cho

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

We will be covering the details of each layer in the next articles.
We will be covering the details of each layer in the next articles.
16 changes: 7 additions & 9 deletions docs/bridging/bridgehub/interop_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ contract 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`.
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`.

Expand Down Expand Up @@ -84,16 +84,14 @@ contract SignupContract {
require(message.data == "We are open");
signupIsOpen = true;
}
function signup() {
require(signupIsOpen);
signedUpUser[msg.sender] = true;
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
Expand All @@ -112,7 +110,7 @@ function sendInteropMessage(bytes data) {

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.
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)
Expand All @@ -127,10 +125,10 @@ The settlment layer receives the messages and once the proof for the batch is su

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).
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.
Note the chain root across batches is missing from this image.

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

Expand Down
37 changes: 18 additions & 19 deletions docs/bridging/bridgehub/interop_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct InteropCall {
address destinationAddress,
uint256 destinationChainId,
calldata data,
uint256 value
uint256 value
}
contract InteropCenter {
// On source chain.
Expand All @@ -41,9 +41,9 @@ contract InteropCenter {
// Executes a given bundle.
// interopMessage is the message that contains your bundle as payload.
// If it fails, it can be called again.
function executeInteropBundle(interopMessage, proof);
// If the bundle didn't execute succesfully yet, it can be marked as cancelled.
// See details below.
function executeInteropBundle(interopMessage, proof);
// If the bundle didn't execute succesfully yet, it can be marked as cancelled.

Check warning on line 45 in docs/bridging/bridgehub/interop_3.md

View workflow job for this annotation

GitHub Actions / typos

"succesfully" should be "successfully".
// See details below.
function cancelInteropBundle(interopMessage, proof);
}
```
Expand All @@ -60,7 +60,7 @@ The msg.sender on the destination chain will be `AliasedAccount` - which is an a

(Normally we’d like to use `sourceAccount@sourceChain` - but as ethereum limits the size of addresses to 20 bytes, we compute the keccak of the string above, and use this as the address).

One way to think about it, is you (as account 0x5bFF1… on chain A) can send a call to some contract on a destination chain, and for that contract, it would look like it was a local call coming from the address `keccak(0x5bFF1 || A)` . This means that you are ‘controlling’ such account address on **every ZKChain** by sending interop messages from the `0x5bFF1..` account on chain A.
One way to think about it, is you (as account 0x5bFF1… on chain A) can send a call to some contract on a destination chain, and for that contract, it would look like it was a local call coming from the address `keccak(0x5bFF1 || A)` . This means that you are ‘controlling’ such account address on **every ZKChain** by sending interop messages from the `0x5bFF1..` account on chain A.

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

Expand Down Expand Up @@ -94,12 +94,12 @@ contract HQ {
// Adding aliased accounts.
shops[address(keccak(addressOnChain || chainId))] = true;
}
function reportSales(uint256 itemPrice) {
// only allow calls from our shops (their aliased accounts).
require(shops[msg.sender]);
sales[msg.sender] += itemPrice;
}
}
}
```

Expand Down Expand Up @@ -140,13 +140,13 @@ contract InteropCenter {
// Calls have to be done in this order.
InteropCall calls[];
uint256 destinationChain;
// If not set - anyone can execute it.
address executionAddresses[];
address executionAddresses[];
// Who can 'cancel' this bundle.
address cancellationAddress;
}
// Starts a new bundle.
// All the calls that will be added to this bundle (potentially by different contracts)
// will have a 'shared fate'.
Expand Down Expand Up @@ -182,8 +182,8 @@ bundleId = InteropCenter(INTEROP_CENTER).startBundle(chainD);
// when this call is executed on chainD, it will mint 1k USDC there.
// BUT - this interopCall is tied to this bundle id.
USDCBridge.transferWithBundle(
bundleId,
chainD,
bundleId,
chainD,
aliasedAccount(this(account), block.chain_id),
1000);
Expand All @@ -192,17 +192,17 @@ InteropCenter.addToBundle(bundleId,
USDCOnDestinationChain,
createCalldata("approve", 1000, poolOnDestinationChain),
0);
// This will create interopCall to do the swap.
InteropCenter.addToBundle(bundleId,
poolOnDestinationChain,
// This will create interopCall to do the swap.
InteropCenter.addToBundle(bundleId,
poolOnDestinationChain,
createCalldata("swap", "USDC_PEPE", 1000, ...),
0)
// And this will be the interopcall to transfer all the assets back.
InteropCenter.addToBundle(bundleId,
pepeBridgeOnDestinationChain,
createCalldata("transferAll", block.chain_id, this(account)),
0)
bundleHash = interopCenter.finishAndSendBundle(bundleId);
```
Expand All @@ -219,16 +219,15 @@ If bundle execution fails - either due to some contract error, or out of gas - n

This is equivalent to our ‘hitchhiker’ (or with a bundle - more like a group of hitchhikers) - if the car they travelled on doesn’t make it to the destination, they simply look for a new one rather than going back home ;-)

But there will be scenarios when the bundle should be cancelled - and it can be done by the `cancellationAddress` that is specified in the bundle itself.
But there will be scenarios when the bundle should be cancelled - and it can be done by the `cancellationAddress` that is specified in the bundle itself.

For our cross chain swap example:

- call `cancelInteropBundle(interopMessage, proof)` on the destination chain
- we will have a helper method for this - look in the next article.
- we will have a helper method for this - look in the next article.
- when this happens, the destination chain will create an `InteropMessage` with cancellation info.
- with the proof of this method, the user will be able to call USDC bridge to get their assets back:

```solidity
USDCBridge.recoverFailedTransfer(bundleId, cancellationMessage, proof);
```

8 changes: 4 additions & 4 deletions docs/bridging/bridgehub/interop_4.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ contract InteropCenter {
/// This function covers all the cases - we expect most users to use the helper
/// functions defined later.
function sendInteropTransaction(
destinationChain,
destinationChain,
bundleHash, // the main bundle that you want to execute on destination chain
gasLimit, // gasLimit & price for execution
gasPrice,
gasPrice,
feesBundleHash, // this is the bundle that contains the calls to pay for gas
destinationPaymaster, // optionally - you can use a paymaster on destination chain
destinationPaymasterInput); // with specific params
struct InteropTransaction {
address sourceChainSender
uint256 destinationChain
Expand Down Expand Up @@ -114,7 +114,7 @@ For this, we’ll offer a helper function:
// Before calling, you have to 'approve' InteropCenter to the ERC20/Bridge that holds the destination chain's base tokens.
// or if the destination chain's tokens are the same as yours, just attach value to this call.
function sendInteropTxMinimal(
destinationChain,
destinationChain,
bundleHash, // the main bundle that you want to execute on destination chain
gasLimit, // gasLimit & price for execution
gasPrice,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {UnsupportedEncodingVersion, AssetIdNotSupported, AssetHandlerDoesNotExis
import {NativeTokenVaultAlreadySet} from "../L1BridgeContractErrors.sol";
import {L2_ASSET_ROUTER_ADDR} from "../../common/l2-helpers/L2ContractAddresses.sol";


import {IBridgehub, L2TransactionRequestTwoBridgesInner, L2TransactionRequestDirect} from "../../bridgehub/IBridgehub.sol";
import {IInteropCenter} from "../../bridgehub/IInteropCenter.sol";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ address constant L2_NATIVE_TOKEN_VAULT_ADDR = address(0x10004);
address constant L2_MESSAGE_ROOT_ADDR = address(0x10005);

/// @dev the address of the L2 interop center
address constant L2_INTEROP_CENTER_ADDR = address(0x10006);
address constant L2_INTEROP_CENTER_ADDR = address(0x10008);

/// @dev the address of the L2 interop handler
address constant L2_INTEROP_HANDLER_ADDR = address(0x10007);
address constant L2_INTEROP_HANDLER_ADDR = address(0x10009);

/// @dev the address of the L2 interop center
address constant L2_INTEROP_ACCOUNT_ADDR = address(0x10008);
/// @dev the address of the L2 interop account
address constant L2_INTEROP_ACCOUNT_ADDR = address(0x1000a);

/// @dev the offset for the system contracts
uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000; // 2^15
Expand Down
1 change: 0 additions & 1 deletion l1-contracts/contracts/common/libraries/MessageHashing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {UncheckedMath} from "./UncheckedMath.sol";

import {NotL1, UnsupportedProofMetadataVersion, LocalRootIsZero, LocalRootMustBeZero, NotSettlementLayer, NotHyperchain} from "../../state-transition/L1StateTransitionErrors.sol";


bytes32 constant BATCH_LEAF_PADDING = keccak256("zkSync:BatchLeaf");
bytes32 constant CHAIN_ID_LEAF_PADDING = keccak256("zkSync:ChainIdLeaf");

Expand Down
2 changes: 1 addition & 1 deletion system-contracts/contracts/L2GatewayUpgrade.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pragma solidity 0.8.24;
import {SystemContractHelper} from "./libraries/SystemContractHelper.sol";
import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol";

import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol";
import {L2GenesisUpgradeHelper as L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol";
import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol";
import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol";
Expand Down
12 changes: 6 additions & 6 deletions system-contracts/contracts/L2GenesisUpgrade.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {ISystemContext} from "./interfaces/ISystemContext.sol";
import {InvalidChainId} from "contracts/SystemContractErrors.sol";
import {IL2GenesisUpgrade} from "./interfaces/IL2GenesisUpgrade.sol";

import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol";
import {L2GenesisUpgradeHelper as L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol";

/// @custom:security-contact [email protected]
/// @author Matter Labs
Expand All @@ -30,11 +30,11 @@ contract L2GenesisUpgrade is IL2GenesisUpgrade {
}
ISystemContext(SYSTEM_CONTEXT_CONTRACT).setChainId(_chainId);

L2GatewayUpgradeHelper.performForceDeployedContractsInit(
_ctmDeployer,
_fixedForceDeploymentsData,
_additionalForceDeploymentsData
);
// L2GatewayUpgradeHelper.performForceDeployedContractsInit(
// _ctmDeployer,
// _fixedForceDeploymentsData,
// _additionalForceDeploymentsData
// );

emit UpgradeComplete(_chainId);
}
Expand Down
6 changes: 6 additions & 0 deletions system-contracts/contracts/interfaces/IInteropAccount.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.20;

interface IInteropAccount {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

contract DummyInteropCenter {
constructor(address _bridgehub, uint256 _l1ChainId, address _owner) {}
}
Loading

0 comments on commit 527db0d

Please sign in to comment.