diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 2f9ef12c9..82a4a1c90 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.24; // solhint-disable reason-string, gas-custom-errors +import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; + import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; @@ -29,6 +31,8 @@ import {L2CanonicalTransaction} from "../common/Messaging.sol"; /// Bridgehub is also an IL1AssetHandler for the chains themselves, which is used to migrate the chains /// between different settlement layers (for example from L1 to Gateway). contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, PausableUpgradeable { + using EnumerableMap for EnumerableMap.UintToAddressMap; + /// @notice the asset id of Eth bytes32 internal immutable ETH_TOKEN_ASSET_ID; @@ -36,6 +40,10 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// L1 that is at the most base layer. uint256 public immutable L1_CHAIN_ID; + /// @notice The total number of hyperchains can be created/connected to this STM. + /// This is the temporary security measure. + uint256 public immutable MAX_NUMBER_OF_HYPERCHAINS; + /// @notice all the ether and ERC20 tokens are held by NativeVaultToken managed by this shared Bridge. IL1AssetRouter public sharedBridge; @@ -56,6 +64,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @dev used to accept the admin role address private pendingAdmin; + /// @notice The map from chainId => hyperchain contract + EnumerableMap.UintToAddressMap internal hyperchainMap; + /// @notice The contract that stores the cross-chain message root for each chain and the aggregated root. /// @dev Note that the message root does not contain messages from the chain it is deployed on. It may /// be added later on if needed. @@ -105,9 +116,10 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus } /// @notice to avoid parity hack - constructor(uint256 _l1ChainId, address _owner) reentrancyGuardInitializer { + constructor(uint256 _l1ChainId, address _owner, uint256 _maxNumberOfHyperchains) reentrancyGuardInitializer { _disableInitializers(); L1_CHAIN_ID = _l1ChainId; + MAX_NUMBER_OF_HYPERCHAINS = _maxNumberOfHyperchains; // Note that this assumes that the bridgehub only accepts transactions on chains with ETH base token only. // This is indeed true, since the only methods where this immutable is used are the ones with `onlyL1` modifier. @@ -163,6 +175,19 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus messageRoot = _messageRoot; } + /// @notice Used to set the legacy chain address for the upgrade. + /// @notice This has to be used after the BH but before the STM is upgraded. + /// @param _chainId The chainId of the legacy chain we are migrating. + function setLegacyChainAddress(uint256 _chainId) external { + address stm = stateTransitionManager[_chainId]; + require(stm != address(0), "BH: chain not legacy"); + require(!hyperchainMap.contains(_chainId), "BH: chain already migrated"); + /// Note we have to do this before STM is upgraded. + address chainAddress = IStateTransitionManager(stm).getHyperchainLegacy(_chainId); + require(chainAddress != address(0), "BH: chain not legacy 2"); + _registerNewHyperchain(_chainId, chainAddress); + } + //// Registry /// @notice State Transition can be any contract with the appropriate interface/functionality @@ -269,7 +294,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus baseTokenAssetId[_chainId] = DataEncoding.encodeNTVAssetId(block.chainid, _baseToken); settlementLayer[_chainId] = block.chainid; - IStateTransitionManager(_stateTransitionManager).createNewChain({ + address chainAddress = IStateTransitionManager(_stateTransitionManager).createNewChain({ _chainId: _chainId, _baseToken: _baseToken, _sharedBridge: address(sharedBridge), @@ -277,19 +302,45 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus _initData: _initData, _factoryDeps: _factoryDeps }); + _registerNewHyperchain(_chainId, chainAddress); messageRoot.addNewChain(_chainId); emit NewChain(_chainId, _stateTransitionManager, _admin); return _chainId; } + /// @dev This internal function is used to register a new hyperchain in the system. + function _registerNewHyperchain(uint256 _chainId, address _hyperchain) internal { + // slither-disable-next-line unused-return + hyperchainMap.set(_chainId, _hyperchain); + require(hyperchainMap.length() <= MAX_NUMBER_OF_HYPERCHAINS, "STM: Hyperchain limit reached"); + } + /*////////////////////////////////////////////////////////////// Getters //////////////////////////////////////////////////////////////*/ - /// @notice return the state transition chain contract for a chainId - function getHyperchain(uint256 _chainId) public view returns (address) { - return IStateTransitionManager(stateTransitionManager[_chainId]).getHyperchain(_chainId); + /// @notice Returns all the registered hyperchain addresses + function getAllHyperchains() public view override returns (address[] memory chainAddresses) { + uint256[] memory keys = hyperchainMap.keys(); + chainAddresses = new address[](keys.length); + uint256 keysLength = keys.length; + for (uint256 i = 0; i < keysLength; ++i) { + chainAddresses[i] = hyperchainMap.get(keys[i]); + } + } + + /// @notice Returns all the registered hyperchain chainIDs + function getAllHyperchainChainIDs() public view override returns (uint256[] memory) { + return hyperchainMap.keys(); + } + + /// @notice Returns the address of the hyperchain with the corresponding chainID + /// @param _chainId the chainId of the chain + /// @return chainAddress the address of the hyperchain + function getHyperchain(uint256 _chainId) public view override returns (address chainAddress) { + // slither-disable-next-line unused-return + (, chainAddress) = hyperchainMap.tryGet(_chainId); } function stmAssetIdFromChainId(uint256 _chainId) public view override returns (bytes32) { @@ -331,7 +382,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus ); } - address hyperchain = getHyperchain(_request.chainId); + address hyperchain = hyperchainMap.get(_request.chainId); address refundRecipient = AddressAliasHelper.actualRefundRecipient(_request.refundRecipient, msg.sender); canonicalTxHash = IZkSyncHyperchain(hyperchain).bridgehubRequestL2Transaction( BridgehubL2TransactionRequest({ @@ -386,7 +437,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus ); } - address hyperchain = getHyperchain(_request.chainId); + address hyperchain = hyperchainMap.get(_request.chainId); // slither-disable-next-line arbitrary-send-eth L2TransactionRequestTwoBridgesInner memory outputRequest = IL1AssetRouter(_request.secondBridgeAddress) @@ -436,7 +487,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus uint64 _expirationTimestamp ) external override onlySettlementLayerRelayedSender { require(L1_CHAIN_ID != block.chainid, "BH: not in sync layer mode"); - address hyperchain = getHyperchain(_chainId); + address hyperchain = hyperchainMap.get(_chainId); IZkSyncHyperchain(hyperchain).bridgehubRequestL2TransactionOnGateway( _transaction, _factoryDeps, @@ -459,7 +510,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus L2Message calldata _message, bytes32[] calldata _proof ) external view override returns (bool) { - address hyperchain = getHyperchain(_chainId); + address hyperchain = hyperchainMap.get(_chainId); return IZkSyncHyperchain(hyperchain).proveL2MessageInclusion(_batchNumber, _index, _message, _proof); } @@ -477,7 +528,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus L2Log calldata _log, bytes32[] calldata _proof ) external view override returns (bool) { - address hyperchain = getHyperchain(_chainId); + address hyperchain = hyperchainMap.get(_chainId); return IZkSyncHyperchain(hyperchain).proveL2LogInclusion(_batchNumber, _index, _log, _proof); } @@ -500,7 +551,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32[] calldata _merkleProof, TxStatus _status ) external view override returns (bool) { - address hyperchain = getHyperchain(_chainId); + address hyperchain = hyperchainMap.get(_chainId); return IZkSyncHyperchain(hyperchain).proveL1ToL2TransactionStatus({ _l2TxHash: _l2TxHash, @@ -519,7 +570,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus uint256 _l2GasLimit, uint256 _l2GasPerPubdataByteLimit ) external view returns (uint256) { - address hyperchain = getHyperchain(_chainId); + address hyperchain = hyperchainMap.get(_chainId); return IZkSyncHyperchain(hyperchain).l2TransactionBaseCost(_gasPrice, _l2GasLimit, _l2GasPerPubdataByteLimit); } @@ -546,7 +597,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus require(settlementLayer[_chainId] == block.chainid, "BH: not current SL"); settlementLayer[_chainId] = _settlementChainId; - address hyperchain = getHyperchain(_chainId); + address hyperchain = hyperchainMap.get(_chainId); require(hyperchain != address(0), "BH: hyperchain not registered"); require(_prevMsgSender == IZkSyncHyperchain(hyperchain).getAdmin(), "BH: incorrect sender"); @@ -555,7 +606,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus _stmData ); bytes memory chainMintData = IZkSyncHyperchain(hyperchain).forwardedBridgeBurn( - getHyperchain(_settlementChainId), + hyperchainMap.get(_settlementChainId), _prevMsgSender, _chainData ); @@ -566,7 +617,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @param _assetId the assetId of the chain's STM /// @param _bridgehubMintData the data for the mint function bridgeMint( - uint256, // chainId + uint256, // originChainId bytes32 _assetId, bytes calldata _bridgehubMintData ) external payable override onlyAssetRouter returns (address l1Receiver) { @@ -580,12 +631,15 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus settlementLayer[_chainId] = block.chainid; stateTransitionManager[_chainId] = stm; - address hyperchain = getHyperchain(_chainId); - if (hyperchain == address(0)) { + address hyperchain; + if (hyperchainMap.contains(_chainId)) { + hyperchain = hyperchainMap.get(_chainId); + } else { hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData); } messageRoot.addNewChainIfNeeded(_chainId); + _registerNewHyperchain(_chainId, hyperchain); IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData); return address(0); } diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index 37efceadd..30e9e1fa0 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -85,6 +85,10 @@ interface IBridgehub is IL1AssetHandler { function getHyperchain(uint256 _chainId) external view returns (address); + function getAllHyperchains() external view returns (address[] memory); + + function getAllHyperchainChainIDs() external view returns (uint256[] memory); + /// Mailbox forwarder function proveL2MessageInclusion( diff --git a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol index fb9ee074e..25c85148f 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol @@ -11,6 +11,8 @@ import {IGetters} from "../../state-transition/chain-interfaces/IGetters.sol"; contract DummyBridgehub { IMessageRoot public messageRoot; + address public hyperchain; + // add this to be excluded from coverage report function test() internal virtual {} @@ -29,4 +31,12 @@ contract DummyBridgehub { function setMessageRoot(address _messageRoot) public { messageRoot = IMessageRoot(_messageRoot); } + + function setHyperchain(uint256, address _hyperchain) external { + hyperchain = _hyperchain; + } + + function getHyperchain(uint256) external view returns (address) { + return address(0); + } } diff --git a/l1-contracts/contracts/dev-contracts/test/DummyBridgehubSetter.sol b/l1-contracts/contracts/dev-contracts/test/DummyBridgehubSetter.sol new file mode 100644 index 000000000..0f053956c --- /dev/null +++ b/l1-contracts/contracts/dev-contracts/test/DummyBridgehubSetter.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {Bridgehub} from "../../bridgehub/Bridgehub.sol"; + +contract DummyBridgehubSetter is Bridgehub { + // add this to be excluded from coverage report + function test() internal virtual {} + + /// @notice Constructor + constructor( + uint256 _l1ChainId, + address _owner, + uint256 _maxNumberOfHyperchains + ) Bridgehub(_l1ChainId, _owner, _maxNumberOfHyperchains) {} + + function setHyperchain(uint256 _chainId, address _hyperchain) external { + _registerNewHyperchain(_chainId, _hyperchain); + } + + function setSTM(uint256 _chainId, address _stm) external { + stateTransitionManager[_chainId] = _stm; + } +} diff --git a/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManager.sol b/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManager.sol index b13050318..0769e39ba 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManager.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManager.sol @@ -14,10 +14,12 @@ contract DummyStateTransitionManager is StateTransitionManager { // add this to be excluded from coverage report function test() internal virtual {} + address hyperchain; + /// @notice Constructor - constructor() StateTransitionManager(address(0), type(uint256).max) {} + constructor() StateTransitionManager(address(0)) {} function setHyperchain(uint256 _chainId, address _hyperchain) external { - hyperchainMap.set(_chainId, _hyperchain); + hyperchain = _hyperchain; } } diff --git a/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerForValidatorTimelock.sol b/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerForValidatorTimelock.sol index f2944d3a8..1543fd66e 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerForValidatorTimelock.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerForValidatorTimelock.sol @@ -23,4 +23,8 @@ contract DummyStateTransitionManagerForValidatorTimelock { function getHyperchain(uint256) external view returns (address) { return hyperchainAddress; } + + function setHyperchain(uint256, address _hyperchain) external { + hyperchainAddress = _hyperchain; + } } diff --git a/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerWithBridgeHubAddress.sol b/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerWithBridgeHubAddress.sol index 3ee0f1a64..1d870d876 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerWithBridgeHubAddress.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyStateTransitionManagerWithBridgeHubAddress.sol @@ -11,11 +11,12 @@ import {StateTransitionManager} from "../../state-transition/StateTransitionMana contract DummyStateTransitionManagerWBH is StateTransitionManager { using EnumerableMap for EnumerableMap.UintToAddressMap; + address hyperchain; /// @notice Constructor - constructor(address bridgeHub) StateTransitionManager(bridgeHub, type(uint256).max) {} + constructor(address bridgeHub) StateTransitionManager(bridgeHub) {} function setHyperchain(uint256 _chainId, address _hyperchain) external { - hyperchainMap.set(_chainId, _hyperchain); + hyperchain = _hyperchain; } // add this to be excluded from coverage report diff --git a/l1-contracts/contracts/state-transition/IStateTransitionManager.sol b/l1-contracts/contracts/state-transition/IStateTransitionManager.sol index 46771f1dc..3f8231641 100644 --- a/l1-contracts/contracts/state-transition/IStateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/IStateTransitionManager.sol @@ -84,12 +84,10 @@ interface IStateTransitionManager { function acceptAdmin() external; - function getAllHyperchains() external view returns (address[] memory); - - function getAllHyperchainChainIDs() external view returns (uint256[] memory); - function getHyperchain(uint256 _chainId) external view returns (address); + function getHyperchainLegacy(uint256 _chainId) external view returns (address); + function storedBatchZero() external view returns (bytes32); function initialCutHash() external view returns (bytes32); @@ -104,6 +102,8 @@ interface IStateTransitionManager { function protocolVersionIsActive(uint256 _protocolVersion) external view returns (bool); + function getProtocolVersion(uint256 _chainId) external view returns (uint256); + function initialize(StateTransitionManagerInitializeData calldata _initializeData) external; function setValidatorTimelock(address _validatorTimelock) external; @@ -119,7 +119,7 @@ interface IStateTransitionManager { address _admin, bytes calldata _initData, bytes[] calldata _factoryDeps - ) external; + ) external returns (address); function registerAlreadyDeployedHyperchain(uint256 _chainId, address _hyperchain) external; diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index f8baf7a98..087a1fc50 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -30,12 +30,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @notice Address of the bridgehub address public immutable BRIDGE_HUB; - /// @notice The total number of hyperchains can be created/connected to this STM. - /// This is the temporary security measure. - uint256 public immutable MAX_NUMBER_OF_HYPERCHAINS; - /// @notice The map from chainId => hyperchain contract - EnumerableMap.UintToAddressMap internal hyperchainMap; + EnumerableMap.UintToAddressMap internal __DEPRECATED_hyperchainMap; /// @dev The batch zero hash, calculated at initialization bytes32 public storedBatchZero; @@ -69,9 +65,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @dev Contract is expected to be used as proxy implementation. /// @dev Initialize the implementation to prevent Parity hack. - constructor(address _bridgehub, uint256 _maxNumberOfHyperchains) reentrancyGuardInitializer { + constructor(address _bridgehub) reentrancyGuardInitializer { BRIDGE_HUB = _bridgehub; - MAX_NUMBER_OF_HYPERCHAINS = _maxNumberOfHyperchains; // While this does not provide a protection in the production, it is needed for local testing // Length of the L2Log encoding should not be equal to the length of other L2Logs' tree nodes preimages @@ -96,33 +91,23 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own return SemVer.unpackSemVer(SafeCast.toUint96(protocolVersion)); } - /// @notice Returns all the registered hyperchain addresses - function getAllHyperchains() public view override returns (address[] memory chainAddresses) { - uint256[] memory keys = hyperchainMap.keys(); - chainAddresses = new address[](keys.length); - uint256 keysLength = keys.length; - for (uint256 i = 0; i < keysLength; ++i) { - chainAddresses[i] = hyperchainMap.get(keys[i]); - } - } - - /// @notice Returns all the registered hyperchain chainIDs - function getAllHyperchainChainIDs() public view override returns (uint256[] memory) { - return hyperchainMap.keys(); + /// @notice return the chain contract address for a chainId + function getHyperchain(uint256 _chainId) public view returns (address) { + return IBridgehub(BRIDGE_HUB).getHyperchain(_chainId); } - /// @notice Returns the address of the hyperchain with the corresponding chainID - /// @param _chainId the chainId of the chain - /// @return chainAddress the address of the hyperchain - function getHyperchain(uint256 _chainId) public view override returns (address chainAddress) { + /// @notice return the chain contract address for a chainId + /// @notice Do not use! use getHyperchain instead. This will be removed. + function getHyperchainLegacy(uint256 _chainId) public view returns (address chainAddress) { // slither-disable-next-line unused-return - (, chainAddress) = hyperchainMap.tryGet(_chainId); + (, chainAddress) = __DEPRECATED_hyperchainMap.tryGet(_chainId); } - /// @notice Returns the address of the hyperchain admin with the corresponding chainID + /// @notice Returns the address of the hyperchain admin with the corresponding chainID. + /// @notice Not related to the STM, but it is here for legacy reasons. /// @param _chainId the chainId of the chain function getChainAdmin(uint256 _chainId) external view override returns (address) { - return IZkSyncHyperchain(hyperchainMap.get(_chainId)).getAdmin(); + return IZkSyncHyperchain(getHyperchain(_chainId)).getAdmin(); } /// @dev initialize @@ -269,20 +254,20 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @dev freezes the specified chain /// @param _chainId the chainId of the chain function freezeChain(uint256 _chainId) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).freezeDiamond(); + IZkSyncHyperchain(getHyperchain(_chainId)).freezeDiamond(); } /// @dev freezes the specified chain /// @param _chainId the chainId of the chain function unfreezeChain(uint256 _chainId) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).unfreezeDiamond(); + IZkSyncHyperchain(getHyperchain(_chainId)).unfreezeDiamond(); } /// @dev reverts batches on the specified chain /// @param _chainId the chainId of the chain /// @param _newLastBatch the new last batch function revertBatches(uint256 _chainId, uint256 _newLastBatch) external onlyOwnerOrAdmin { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).revertBatches(_newLastBatch); + IZkSyncHyperchain(getHyperchain(_chainId)).revertBatches(_newLastBatch); } /// @dev execute predefined upgrade @@ -294,21 +279,21 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own uint256 _oldProtocolVersion, Diamond.DiamondCutData calldata _diamondCut ) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).upgradeChainFromVersion(_oldProtocolVersion, _diamondCut); + IZkSyncHyperchain(getHyperchain(_chainId)).upgradeChainFromVersion(_oldProtocolVersion, _diamondCut); } /// @dev executes upgrade on chain /// @param _chainId the chainId of the chain /// @param _diamondCut the diamond cut data function executeUpgrade(uint256 _chainId, Diamond.DiamondCutData calldata _diamondCut) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).executeUpgrade(_diamondCut); + IZkSyncHyperchain(getHyperchain(_chainId)).executeUpgrade(_diamondCut); } /// @dev setPriorityTxMaxGasLimit for the specified chain /// @param _chainId the chainId of the chain /// @param _maxGasLimit the new max gas limit function setPriorityTxMaxGasLimit(uint256 _chainId, uint256 _maxGasLimit) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).setPriorityTxMaxGasLimit(_maxGasLimit); + IZkSyncHyperchain(getHyperchain(_chainId)).setPriorityTxMaxGasLimit(_maxGasLimit); } /// @dev setTokenMultiplier for the specified chain @@ -316,14 +301,14 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @param _nominator the new nominator of the token multiplier /// @param _denominator the new denominator of the token multiplier function setTokenMultiplier(uint256 _chainId, uint128 _nominator, uint128 _denominator) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).setTokenMultiplier(_nominator, _denominator); + IZkSyncHyperchain(getHyperchain(_chainId)).setTokenMultiplier(_nominator, _denominator); } /// @dev changeFeeParams for the specified chain /// @param _chainId the chainId of the chain /// @param _newFeeParams the new fee params function changeFeeParams(uint256 _chainId, FeeParams calldata _newFeeParams) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).changeFeeParams(_newFeeParams); + IZkSyncHyperchain(getHyperchain(_chainId)).changeFeeParams(_newFeeParams); } /// @dev setValidator for the specified chain @@ -331,14 +316,14 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @param _validator the new validator /// @param _active whether the validator is active function setValidator(uint256 _chainId, address _validator, bool _active) external onlyOwnerOrAdmin { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).setValidator(_validator, _active); + IZkSyncHyperchain(getHyperchain(_chainId)).setValidator(_validator, _active); } /// @dev setPorterAvailability for the specified chain /// @param _chainId the chainId of the chain /// @param _zkPorterIsAvailable whether the zkPorter mode is available function setPorterAvailability(uint256 _chainId, bool _zkPorterIsAvailable) external onlyOwner { - IZkSyncHyperchain(hyperchainMap.get(_chainId)).setPorterAvailability(_zkPorterIsAvailable); + IZkSyncHyperchain(getHyperchain(_chainId)).setPorterAvailability(_zkPorterIsAvailable); } /// registration @@ -348,8 +333,9 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @param _hyperchain the chain's contract address function registerAlreadyDeployedHyperchain(uint256 _chainId, address _hyperchain) external onlyOwner { require(_hyperchain != address(0), "STM: hyperchain zero"); + emit NewHyperchain(_chainId, _hyperchain); - _registerNewHyperchain(_chainId, _hyperchain); + // _registerNewHyperchain(_chainId, _hyperchain); } /// @dev deploys a full set of chains contracts @@ -401,8 +387,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own DiamondProxy hyperchainContract = new DiamondProxy{salt: bytes32(0)}(block.chainid, diamondCut); // save data hyperchainAddress = address(hyperchainContract); - - _registerNewHyperchain(_chainId, hyperchainAddress); + emit NewHyperchain(_chainId, hyperchainAddress); } /// @notice called by Bridgehub when a chain registers @@ -420,11 +405,11 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own address _admin, bytes calldata _initData, bytes[] calldata _factoryDeps - ) external onlyBridgehub { + ) external onlyBridgehub returns (address hyperchainAddress) { (bytes memory _diamondCut, bytes memory _forceDeploymentData) = abi.decode(_initData, (bytes, bytes)); // solhint-disable-next-line func-named-parameters - address hyperchainAddress = _deployNewChain(_chainId, _baseToken, _sharedBridge, _admin, _diamondCut); + hyperchainAddress = _deployNewChain(_chainId, _baseToken, _sharedBridge, _admin, _diamondCut); { // check input @@ -437,7 +422,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @param _chainId the chainId of the chain function getProtocolVersion(uint256 _chainId) public view returns (uint256) { - return IZkSyncHyperchain(hyperchainMap.get(_chainId)).getProtocolVersion(); + return IZkSyncHyperchain(getHyperchain(_chainId)).getProtocolVersion(); } /// @param _newSettlementLayerChainId the chainId of the chain @@ -446,7 +431,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own require(_newSettlementLayerChainId != 0, "Bad chain id"); // Currently, we require that the sync layer is deployed by the same STM. - require(hyperchainMap.contains(_newSettlementLayerChainId), "STM: sync layer not registered"); + require(getHyperchain(_newSettlementLayerChainId) != address(0), "STM: sync layer not registered"); IBridgehub(BRIDGE_HUB).registerSettlementLayer(_newSettlementLayerChainId, _isWhitelisted); } @@ -465,7 +450,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own // We ensure that the chain has the latest protocol version to avoid edge cases // related to different protocol version support. - address hyperchain = hyperchainMap.get(_chainId); + address hyperchain = getHyperchain(_chainId); require(IZkSyncHyperchain(hyperchain).getProtocolVersion() == protocolVersion, "STM: outdated pv"); return abi.encode(IBridgehub(BRIDGE_HUB).baseToken(_chainId), _newGatewayAdmin, protocolVersion, _diamondCut); @@ -508,12 +493,4 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own ) external { // todo } - - /// @dev This internal function is used to register a new hyperchain in the system. - function _registerNewHyperchain(uint256 _chainId, address _hyperchain) internal { - // slither-disable-next-line unused-return - hyperchainMap.set(_chainId, _hyperchain); - require(hyperchainMap.length() <= MAX_NUMBER_OF_HYPERCHAINS, "STM: Hyperchain limit reached"); - emit NewHyperchain(_chainId, _hyperchain); - } } diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 0af406abb..4e71bb02f 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -369,7 +369,7 @@ contract DeployL1Script is Script { function deployBridgehubContract() internal { bytes memory bridgeHubBytecode = abi.encodePacked( type(Bridgehub).creationCode, - abi.encode(config.l1ChainId, config.ownerAddress) + abi.encode(config.l1ChainId, config.ownerAddress, (config.contracts.maxNumberOfChains)) ); address bridgehubImplementation = deployViaCreate2(bridgeHubBytecode); console.log("Bridgehub Implementation deployed at:", bridgehubImplementation); @@ -474,8 +474,7 @@ contract DeployL1Script is Script { function deployStateTransitionManagerImplementation() internal { bytes memory bytecode = abi.encodePacked( type(StateTransitionManager).creationCode, - abi.encode(addresses.bridgehub.bridgehubProxy), - abi.encode(config.contracts.maxNumberOfChains) + abi.encode(addresses.bridgehub.bridgehubProxy) ); address contractAddress = deployViaCreate2(bytecode); console.log("StateTransitionManagerImplementation deployed at:", contractAddress); diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 49be82021..806b59962 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -196,8 +196,12 @@ export class Deployer { callConstructor: true, value: 0, input: ethers.utils.defaultAbiCoder.encode( - ["uint256", "address"], - [getNumberFromEnv("ETH_CLIENT_CHAIN_ID"), applyL1ToL2Alias(this.addresses.Governance)] + ["uint256", "address", "uint256"], + [ + getNumberFromEnv("ETH_CLIENT_CHAIN_ID"), + applyL1ToL2Alias(this.addresses.Governance), + getNumberFromEnv("CONTRACTS_MAX_NUMBER_OF_HYPERCHAINS"), + ] ), }; const eraChainId = getNumberFromEnv("CONTRACTS_ERA_CHAIN_ID"); @@ -419,7 +423,7 @@ export class Deployer { const l1ChainId = this.isZkMode() ? getNumberFromEnv("ETH_CLIENT_CHAIN_ID") : await this.deployWallet.getChainId(); const contractAddress = await this.deployViaCreate2( "Bridgehub", - [l1ChainId, this.addresses.Governance], + [l1ChainId, this.addresses.Governance, getNumberFromEnv("CONTRACTS_MAX_NUMBER_OF_HYPERCHAINS")], create2Salt, ethTxOptions ); @@ -490,7 +494,7 @@ export class Deployer { ) { const contractAddress = await this.deployViaCreate2( "StateTransitionManager", - [this.addresses.Bridgehub.BridgehubProxy, getNumberFromEnv("CONTRACTS_MAX_NUMBER_OF_HYPERCHAINS")], + [this.addresses.Bridgehub.BridgehubProxy], create2Salt, { ...ethTxOptions, diff --git a/l1-contracts/test/foundry/integration/DeploymentTest.t.sol b/l1-contracts/test/foundry/integration/DeploymentTest.t.sol index 3421dc361..ef28640fe 100644 --- a/l1-contracts/test/foundry/integration/DeploymentTest.t.sol +++ b/l1-contracts/test/foundry/integration/DeploymentTest.t.sol @@ -20,6 +20,8 @@ import {L2CanonicalTransaction, L2Message} from "contracts/common/Messaging.sol" import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IL1ERC20Bridge} from "contracts/bridge/interfaces/IL1ERC20Bridge.sol"; +import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol"; +import {IStateTransitionManager} from "contracts/state-transition/IStateTransitionManager.sol"; contract DeploymentTests is L1ContractDeployer, HyperchainDeployer, TokenDeployer, L2TxMocker { uint256 constant TEST_USERS_COUNT = 10; @@ -66,7 +68,25 @@ contract DeploymentTests is L1ContractDeployer, HyperchainDeployer, TokenDeploye // Check whether the sum of ETH deposits from tests, updated on each deposit and withdrawal, // equals the balance of L1Shared bridge. function test_initialDeployment() public { - require(1 == 1); + uint256 chainId = hyperchainIds[0]; + IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); + address newChainAddress = bridgehub.getHyperchain(chainId); + address admin = IZkSyncHyperchain(bridgehub.getHyperchain(chainId)).getAdmin(); + IStateTransitionManager stm = IStateTransitionManager(bridgehub.stateTransitionManager(chainId)); + + assertNotEq(admin, address(0)); + assertNotEq(newChainAddress, address(0)); + + address[] memory chainAddresses = bridgehub.getAllHyperchains(); + assertEq(chainAddresses.length, 1); + assertEq(chainAddresses[0], newChainAddress); + + uint256[] memory chainIds = bridgehub.getAllHyperchainChainIDs(); + assertEq(chainIds.length, 1); + assertEq(chainIds[0], chainId); + + uint256 protocolVersion = stm.getProtocolVersion(chainId); + assertEq(protocolVersion, 0); } // add this to be excluded from coverage report diff --git a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol index 8ae349636..9c3777bde 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -13,6 +13,7 @@ import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "c import {DummyStateTransitionManagerWBH} from "contracts/dev-contracts/test/DummyStateTransitionManagerWithBridgeHubAddress.sol"; import {DummyHyperchain} from "contracts/dev-contracts/test/DummyHyperchain.sol"; import {DummySharedBridge} from "contracts/dev-contracts/test/DummySharedBridge.sol"; +import {DummyBridgehubSetter} from "contracts/dev-contracts/test/DummyBridgehubSetter.sol"; import {IL1AssetRouter} from "contracts/bridge/interfaces/IL1AssetRouter.sol"; import {L1NativeTokenVault} from "contracts/bridge/L1NativeTokenVault.sol"; @@ -28,6 +29,7 @@ contract ExperimentalBridgeTest is Test { using stdStorage for StdStorage; Bridgehub bridgeHub; + DummyBridgehubSetter dummyBridgehub; address public bridgeOwner; address public testTokenAddress; DummyStateTransitionManagerWBH mockSTM; @@ -40,6 +42,8 @@ contract ExperimentalBridgeTest is Test { uint256 eraChainId; + bytes32 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; + bytes32 ETH_TOKEN_ASSET_ID = keccak256( abi.encode(block.chainid, L2_NATIVE_TOKEN_VAULT_ADDRESS, bytes32(uint256(uint160(ETH_TOKEN_ADDRESS)))) @@ -49,7 +53,8 @@ contract ExperimentalBridgeTest is Test { eraChainId = 9; uint256 l1ChainId = 1; bridgeOwner = makeAddr("BRIDGE_OWNER"); - bridgeHub = new Bridgehub(l1ChainId, bridgeOwner); + dummyBridgehub = new DummyBridgehubSetter(l1ChainId, bridgeOwner, type(uint256).max); + bridgeHub = Bridgehub(address(dummyBridgehub)); address weth = makeAddr("WETH"); mockSTM = new DummyStateTransitionManagerWBH(address(bridgeHub)); mockChainContract = new DummyHyperchain(address(bridgeHub), eraChainId); @@ -72,11 +77,7 @@ contract ExperimentalBridgeTest is Test { vm.expectRevert(bytes("1B")); bridgeHub.initialize(bridgeOwner); - vm.store( - address(mockChainContract), - 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4, - bytes32(uint256(1)) - ); + vm.store(address(mockChainContract), LOCK_FLAG_ADDRESS, bytes32(uint256(1))); bytes32 bridgehubLocation = bytes32(uint256(36)); vm.store(address(mockChainContract), bridgehubLocation, bytes32(uint256(uint160(address(bridgeHub))))); bytes32 baseTokenGasPriceNominatorLocation = bytes32(uint256(40)); @@ -946,12 +947,8 @@ contract ExperimentalBridgeTest is Test { // So, perhaps we will have to manually set the values in the stateTransitionManager mapping via a foundry cheatcode assertTrue(!(bridgeHub.stateTransitionManager(mockChainId) == address(mockSTM))); - stdstore.target(address(bridgeHub)).sig("stateTransitionManager(uint256)").with_key(mockChainId).checked_write( - address(mockSTM) - ); - - // Now in the StateTransitionManager that has been set for our mockChainId, we set the hyperchain contract as our mockChainContract - mockSTM.setHyperchain(mockChainId, address(mockChainContract)); + dummyBridgehub.setSTM(mockChainId, address(mockSTM)); + dummyBridgehub.setHyperchain(mockChainId, address(mockChainContract)); } function _setUpBaseTokenForChainId(uint256 mockChainId, bool tokenIsETH) internal { diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol index 7eef43b79..4a2f2753f 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol @@ -27,25 +27,4 @@ contract createNewChainTest is StateTransitionManagerTest { _factoryDeps: new bytes[](0) }); } - - function test_SuccessfulCreationOfNewChain() public { - createNewChain(getDiamondCutData(diamondInit)); - - address admin = chainContractAddress.getChainAdmin(chainId); - address newChainAddress = chainContractAddress.getHyperchain(chainId); - - assertEq(newChainAdmin, admin); - assertNotEq(newChainAddress, address(0)); - - address[] memory chainAddresses = chainContractAddress.getAllHyperchains(); - assertEq(chainAddresses.length, 1); - assertEq(chainAddresses[0], newChainAddress); - - uint256[] memory chainIds = chainContractAddress.getAllHyperchainChainIDs(); - assertEq(chainIds.length, 1); - assertEq(chainIds[0], chainId); - - uint256 protocolVersion = chainContractAddress.getProtocolVersion(chainId); - assertEq(protocolVersion, 0); - } } diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol index 56dd03f84..a202e29bf 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol @@ -44,7 +44,7 @@ contract StateTransitionManagerTest is Test { newChainAdmin = makeAddr("chainadmin"); vm.startPrank(bridgehub); - stateTransitionManager = new StateTransitionManager(address(IBridgehub(address(bridgehub))), type(uint256).max); + stateTransitionManager = new StateTransitionManager(address(IBridgehub(address(bridgehub)))); diamondInit = address(new DiamondInit()); genesisUpgradeContract = new GenesisUpgrade(); @@ -130,18 +130,19 @@ contract StateTransitionManagerTest is Test { return Diamond.DiamondCutData({facetCuts: facetCuts, initAddress: _diamondInit, initCalldata: initCalldata}); } - function createNewChain(Diamond.DiamondCutData memory _diamondCut) internal { + function createNewChain(Diamond.DiamondCutData memory _diamondCut) internal returns (address) { vm.stopPrank(); vm.startPrank(bridgehub); - chainContractAddress.createNewChain({ - _chainId: chainId, - _baseToken: baseToken, - _sharedBridge: sharedBridge, - _admin: newChainAdmin, - _initData: abi.encode(abi.encode(_diamondCut), bytes("")), - _factoryDeps: new bytes[](0) - }); + return + chainContractAddress.createNewChain({ + _chainId: chainId, + _baseToken: baseToken, + _sharedBridge: sharedBridge, + _admin: newChainAdmin, + _initData: abi.encode(abi.encode(_diamondCut), bytes("")), + _factoryDeps: new bytes[](0) + }); } // add this to be excluded from coverage report diff --git a/l1-contracts/test/unit_tests/validator_timelock_test.spec.ts b/l1-contracts/test/unit_tests/validator_timelock_test.spec.ts index 3b66c0f2c..1ce079342 100644 --- a/l1-contracts/test/unit_tests/validator_timelock_test.spec.ts +++ b/l1-contracts/test/unit_tests/validator_timelock_test.spec.ts @@ -55,16 +55,18 @@ describe("ValidatorTimelock tests", function () { const dummyExecutorContract = await dummyExecutorFactory.deploy(); dummyExecutor = DummyExecutorFactory.connect(dummyExecutorContract.address, dummyExecutorContract.signer); - const dummyStateTransitionManagerFactory = await hardhat.ethers.getContractFactory("DummyStateTransitionManager"); - const dummyStateTransitionManagerContract = await dummyStateTransitionManagerFactory.deploy(); + const dummyStateTransitionManagerFactory = await hardhat.ethers.getContractFactory( + "DummyStateTransitionManagerForValidatorTimelock" + ); + const dummyStateTransitionManagerContract = await dummyStateTransitionManagerFactory.deploy( + await owner.getAddress(), + dummyExecutor.address + ); dummyStateTransitionManager = DummyStateTransitionManagerFactory.connect( dummyStateTransitionManagerContract.address, dummyStateTransitionManagerContract.signer ); - const setSTtx = await dummyStateTransitionManager.setHyperchain(chainId, dummyExecutor.address); - await setSTtx.wait(); - const validatorTimelockFactory = await hardhat.ethers.getContractFactory("ValidatorTimelock"); const validatorTimelockContract = await validatorTimelockFactory.deploy(await owner.getAddress(), 0, chainId); validatorTimelock = ValidatorTimelockFactory.connect(