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

add ability to recover from failed migration #715

Merged
merged 17 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
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
75 changes: 43 additions & 32 deletions l1-contracts/contracts/bridgehub/Bridgehub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {EnumerableMap} from "@openzeppelin/contracts-v4/utils/structs/Enumerable
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol";

import {IBridgehub, L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter, L2TransactionRequestTwoBridgesInner} from "./IBridgehub.sol";
import {IBridgehub, L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter, L2TransactionRequestTwoBridgesInner, BridgehubMintSTMAssetData, BridgehubBurnSTMAssetData} from "./IBridgehub.sol";
import {IL1AssetRouter} from "../bridge/interfaces/IL1AssetRouter.sol";
import {IL1BaseTokenAssetHandler} from "../bridge/interfaces/IL1BaseTokenAssetHandler.sol";
import {IStateTransitionManager} from "../state-transition/IStateTransitionManager.sol";
Expand Down Expand Up @@ -673,71 +673,82 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus
) external payable override onlyAssetRouter onlyL1 returns (bytes memory bridgehubMintData) {
require(whitelistedSettlementLayers[_settlementChainId], "BH: SL not whitelisted");

(uint256 _chainId, bytes memory _stmData, bytes memory _chainData) = abi.decode(_data, (uint256, bytes, bytes));
require(_assetId == stmAssetIdFromChainId(_chainId), "BH: assetInfo 1");
require(settlementLayer[_chainId] == block.chainid, "BH: not current SL");
settlementLayer[_chainId] = _settlementChainId;
BridgehubBurnSTMAssetData memory bridgeData = abi.decode(_data, (BridgehubBurnSTMAssetData));
require(_assetId == stmAssetIdFromChainId(bridgeData.chainId), "BH: assetInfo 1");
require(settlementLayer[bridgeData.chainId] == block.chainid, "BH: not current SL");
settlementLayer[bridgeData.chainId] = _settlementChainId;

address hyperchain = hyperchainMap.get(_chainId);
address hyperchain = hyperchainMap.get(bridgeData.chainId);
require(hyperchain != address(0), "BH: hyperchain not registered");
require(_prevMsgSender == IZkSyncHyperchain(hyperchain).getAdmin(), "BH: incorrect sender");

bytes memory stmMintData = IStateTransitionManager(stateTransitionManager[_chainId]).forwardedBridgeBurn(
_chainId,
_stmData
);
bytes memory stmMintData = IStateTransitionManager(stateTransitionManager[bridgeData.chainId])
.forwardedBridgeBurn(bridgeData.chainId, bridgeData.stmData);
bytes memory chainMintData = IZkSyncHyperchain(hyperchain).forwardedBridgeBurn(
hyperchainMap.get(_settlementChainId),
_prevMsgSender,
_chainData
bridgeData.chainData
);
bridgehubMintData = abi.encode(_chainId, stmMintData, chainMintData);
bridgehubMintData = abi.encode(bridgeData.chainId, stmMintData, chainMintData);
koloz193 marked this conversation as resolved.
Show resolved Hide resolved

emit MigrationStarted(_chainId, _assetId, _settlementChainId);
emit MigrationStarted(bridgeData.chainId, _assetId, _settlementChainId);
}

/// @dev IL1AssetHandler interface, used to receive a chain on the settlement layer.
/// @param _assetId the assetId of the chain's STM
/// @param _bridgehubMintData the data for the mint
function bridgeMint(
uint256, // originChainId
bytes32 _assetId,
bytes calldata _bridgehubMintData
) external payable override onlyAssetRouter {
(uint256 _chainId, bytes memory _stmData, bytes memory _chainMintData) = abi.decode(
_bridgehubMintData,
(uint256, bytes, bytes)
);
BridgehubMintSTMAssetData memory bridgeData = abi.decode(_bridgehubMintData, (BridgehubMintSTMAssetData));

address stm = stmAssetIdToAddress[_assetId];
require(stm != address(0), "BH: assetInfo 2");
require(settlementLayer[_chainId] != block.chainid, "BH: already current SL");
require(settlementLayer[bridgeData.chainId] != block.chainid, "BH: already current SL");

settlementLayer[_chainId] = block.chainid;
stateTransitionManager[_chainId] = stm;
settlementLayer[bridgeData.chainId] = block.chainid;
stateTransitionManager[bridgeData.chainId] = stm;
address hyperchain;
if (hyperchainMap.contains(_chainId)) {
hyperchain = hyperchainMap.get(_chainId);
if (hyperchainMap.contains(bridgeData.chainId)) {
hyperchain = hyperchainMap.get(bridgeData.chainId);
} else {
hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData);
hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(bridgeData.chainId, bridgeData.stmData);
}

messageRoot.addNewChainIfNeeded(_chainId);
_registerNewHyperchain(_chainId, hyperchain);
IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData);
messageRoot.addNewChainIfNeeded(bridgeData.chainId);
_registerNewHyperchain(bridgeData.chainId, hyperchain);
IZkSyncHyperchain(hyperchain).forwardedBridgeMint(bridgeData.chainData);

emit MigrationFinalized(_chainId, _assetId, hyperchain);
emit MigrationFinalized(bridgeData.chainId, _assetId, hyperchain);
}

/// @dev IL1AssetHandler interface, used to undo a failed migration of a chain.
/// @param _chainId the chainId of the chain
/// @param _assetId the assetId of the chain's STM
/// @param _data the data for the recovery
/// @param _data the data for the recovery.
function bridgeRecoverFailedTransfer(
uint256 _chainId,
bytes32 _assetId,
address _depositSender,
bytes calldata _data
) external payable override onlyAssetRouter onlyL1 {}
) external payable override onlyAssetRouter onlyL1 {
BridgehubBurnSTMAssetData memory stmAssetData = abi.decode(_data, (BridgehubBurnSTMAssetData));

delete settlementLayer[_chainId];

IStateTransitionManager(stateTransitionManager[_chainId]).forwardedBridgeRecoverFailedTransfer({
_chainId: _chainId,
_assetInfo: _assetId,
_depositSender: _depositSender,
_stmData: stmAssetData.stmData
});

IZkSyncHyperchain(getHyperchain(_chainId)).forwardedBridgeRecoverFailedTransfer({
_chainId: _chainId,
_assetInfo: _assetId,
_prevMsgSender: _depositSender,
_chainData: stmAssetData.chainData
});
}

/*//////////////////////////////////////////////////////////////
PAUSE
Expand Down
12 changes: 12 additions & 0 deletions l1-contracts/contracts/bridgehub/IBridgehub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ struct L2TransactionRequestTwoBridgesInner {
bytes32 txDataHash;
}

struct BridgehubMintSTMAssetData {
uint256 chainId;
bytes stmData;
bytes chainData;
}

struct BridgehubBurnSTMAssetData {
uint256 chainId;
bytes stmData;
bytes chainData;
}

/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IBridgehub is IL1AssetHandler {
Expand Down
2 changes: 2 additions & 0 deletions l1-contracts/contracts/common/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,6 @@ struct HyperchainCommitment {
bytes32[] batchHashes;
/// @notice Commitment to the priority merkle tree.
PriorityTreeCommitment priorityTree;
/// @notice Current active protocol version
uint256 protocolVersion;
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ interface IStateTransitionManager {

function forwardedBridgeMint(uint256 _chainId, bytes calldata _data) external returns (address);

function bridgeClaimFailedBurn(
function forwardedBridgeRecoverFailedTransfer(
uint256 _chainId,
bytes32 _assetInfo,
address _prevMsgSender,
bytes calldata _data
address _depositSender,
bytes calldata _stmData
) external;
}
Original file line number Diff line number Diff line change
Expand Up @@ -501,16 +501,17 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
}

/// @notice Called by the bridgehub during the failed migration of a chain.
/// @param _chainId the chainId of the chain
/// @param _assetInfo the assetInfo of the chain
/// @param _prevMsgSender the previous message sender
/// @param _data the data of the migration
function bridgeClaimFailedBurn(
uint256 _chainId,
bytes32 _assetInfo,
address _prevMsgSender,
bytes calldata _data
/// param _chainId the chainId of the chain
/// param _assetInfo the assetInfo of the chain
/// param _depositSender the address of that sent the deposit
/// param _stmData the data of the migration
function forwardedBridgeRecoverFailedTransfer(
uint256 /* _chainId */,
bytes32 /* _assetInfo */,
address /* _depositSender */,
bytes calldata /* _stmData */
) external {
// todo
// Function is empty due to the fact that when calling `forwardedBridgeBurn` there are no
// state updates that occur.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,14 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin {
function forwardedBridgeBurn(
address _settlementLayer,
address _prevMsgSender,
bytes calldata
bytes calldata _data
) external payable override onlyBridgehub returns (bytes memory chainBridgeMintData) {
require(s.settlementLayer == address(0), "Af: already migrated");
require(_prevMsgSender == s.admin, "Af: not chainAdmin");
IStateTransitionManager stm = IStateTransitionManager(s.stateTransitionManager);
// As of now all we need in this function is the chainId so we encode it and pass it down in the _chainData field
uint256 protocolVersion = abi.decode(_data, (uint256));

uint256 currentProtocolVersion = s.protocolVersion;
uint256 protocolVersion = stm.protocolVersion();

require(currentProtocolVersion == protocolVersion, "STM: protocolVersion not up to date");

Expand Down Expand Up @@ -309,12 +309,26 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin {
}

/// @inheritdoc IAdmin
function forwardedBridgeClaimFailedBurn(
uint256 _chainId,
bytes32 _assetInfo,
address _prevMsgSender,
bytes calldata _data
) external payable override onlyBridgehub {}
/// @dev Note that this function does not check that the caller is the chain admin.
function forwardedBridgeRecoverFailedTransfer(
uint256 /* _chainId */,
bytes32 /* _assetInfo */,
address _depositSender,
bytes calldata _chainData
) external payable override onlyBridgehub {
// As of now all we need in this function is the chainId so we encode it and pass it down in the _chainData field
uint256 protocolVersion = abi.decode(_chainData, (uint256));

require(s.settlementLayer != address(0), "Af: not migrated");
// Sanity check that the _depositSender is the chain admin.
require(_depositSender == s.admin, "Af: not chainAdmin");

uint256 currentProtocolVersion = s.protocolVersion;

require(currentProtocolVersion == protocolVersion, "STM: protocolVersion not up to date");

s.settlementLayer = address(0);
}

/// @notice Returns the commitment for a chain.
/// @dev Note, that this is a getter method helpful for debugging and should not be relied upon by clients.
Expand All @@ -328,6 +342,7 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin {
commitment.l2SystemContractsUpgradeBatchNumber = s.l2SystemContractsUpgradeBatchNumber;
commitment.l2SystemContractsUpgradeTxHash = s.l2SystemContractsUpgradeTxHash;
commitment.priorityTree = s.priorityTree.getCommitment();
commitment.protocolVersion = s.protocolVersion;
koloz193 marked this conversation as resolved.
Show resolved Hide resolved

// just in case
require(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@ interface IAdmin is IZkSyncHyperchainBase {
) external payable returns (bytes memory _bridgeMintData);

/// @dev Similar to IL1AssetHandler interface, used to claim failed chain transfers.
function forwardedBridgeClaimFailedBurn(
function forwardedBridgeRecoverFailedTransfer(
uint256 _chainId,
bytes32 _assetInfo,
address _prevMsgSender,
bytes calldata _data
bytes calldata _chainData
) external payable;

/// @dev Similar to IL1AssetHandler interface, used to receive chains.
Expand Down
11 changes: 8 additions & 3 deletions l1-contracts/deploy-scripts/Gateway.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Script, console2 as console} from "forge-std/Script.sol";
import {stdToml} from "forge-std/StdToml.sol";

import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol";
import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol";
import {IBridgehub, BridgehubBurnSTMAssetData} from "contracts/bridgehub/IBridgehub.sol";
import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol";
// import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol";
// import {Governance} from "contracts/governance/Governance.sol";
Expand Down Expand Up @@ -154,8 +154,13 @@ contract GatewayScript is Script {
bytes32 stmAssetId = bridgehub.stmAssetIdFromChainId(config.chainChainId);
bytes memory diamondCutData = config.diamondCutData; // todo replace with config.zkDiamondCutData;
bytes memory stmData = abi.encode(newAdmin, diamondCutData);
bytes memory chainData = abi.encode(address(1));
bytes memory bridgehubData = abi.encode(config.chainChainId, stmData, chainData);
bytes memory chainData = abi.encode(chain.getProtocolVersion());
BridgehubBurnSTMAssetData memory stmAssetData = BridgehubBurnSTMAssetData({
chainId: config.chainChainId,
stmData: stmData,
chainData: chainData
});
bytes memory bridgehubData = abi.encode(stmAssetData);
bytes memory routerData = bytes.concat(bytes1(0x01), abi.encode(stmAssetId, bridgehubData));

vm.startBroadcast(chain.getAdmin());
Expand Down
8 changes: 5 additions & 3 deletions l1-contracts/src.ts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
compileInitialCutHash,
readBytecode,
applyL1ToL2Alias,
BRIDGEHUB_STM_ASSET_DATA_ABI_STRING,
// priorityTxMaxGasLimit,
encodeNTVAssetId,
ETH_ADDRESS_IN_CONTRACTS,
Expand Down Expand Up @@ -1143,6 +1144,8 @@ export class Deployer {

// Main function to move the current chain (that is hooked to l1), on top of the syncLayer chain.
public async moveChainToGateway(gatewayChainId: string, gasPrice: BigNumberish) {
const protocolVersion = packSemver(...unpackStringSemVer(process.env.CONTRACTS_GENESIS_PROTOCOL_SEMANTIC_VERSION));
const chainData = ethers.utils.defaultAbiCoder.encode(["uint256"], [protocolVersion]);
const bridgehub = this.bridgehubContract(this.deployWallet);
// Just some large gas limit that should always be enough
const l2GasLimit = ethers.BigNumber.from(72_000_000);
Expand All @@ -1156,10 +1159,9 @@ export class Deployer {
const initialDiamondCut = new ethers.utils.AbiCoder().encode([DIAMOND_CUT_DATA_ABI_STRING], [diamondCutData]);

const stmData = new ethers.utils.AbiCoder().encode(["uint256", "bytes"], [newAdmin, initialDiamondCut]);
const chainData = new ethers.utils.AbiCoder().encode(["uint256"], [ADDRESS_ONE]); // empty for now
const bridgehubData = new ethers.utils.AbiCoder().encode(
["uint256", "bytes", "bytes"],
[this.chainId, stmData, chainData]
[BRIDGEHUB_STM_ASSET_DATA_ABI_STRING],
[[this.chainId, stmData, chainData]]
);

// console.log("bridgehubData", bridgehubData)
Expand Down
3 changes: 1 addition & 2 deletions l1-contracts/src.ts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ export const DIAMOND_CUT_DATA_ABI_STRING =
"tuple(tuple(address facet, uint8 action, bool isFreezable, bytes4[] selectors)[] facetCuts, address initAddress, bytes initCalldata)";
export const FORCE_DEPLOYMENT_ABI_STRING =
"tuple(bytes32 bytecodeHash, address newAddress, bool callConstructor, uint256 value, bytes input)[]";
export const HYPERCHAIN_COMMITMENT_ABI_STRING =
"tuple(uint256 totalBatchesExecuted, uint256 totalBatchesVerified, uint256 totalBatchesCommitted, bytes32 l2SystemContractsUpgradeTxHash, uint256 l2SystemContractsUpgradeBatchNumber, bytes32[] batchHashes, tuple(uint256 nextLeafIndex, uint256 startIndex, uint256 unprocessedIndex, bytes32[] sides) priorityTree)";
export const BRIDGEHUB_STM_ASSET_DATA_ABI_STRING = "tuple(uint256 chainId, bytes stmData, bytes chainData)";

export function applyL1ToL2Alias(address: string): string {
return ethers.utils.hexlify(ethers.BigNumber.from(address).add(L1_TO_L2_ALIAS_OFFSET).mod(ADDRESS_MODULO));
Expand Down
Loading
Loading