diff --git a/l1-contracts-foundry/script-config-template/config-deploy-l1.toml b/l1-contracts-foundry/script-config-template/config-deploy-l1.toml index 7e3f06e2124a..ad8982ffccbb 100644 --- a/l1-contracts-foundry/script-config-template/config-deploy-l1.toml +++ b/l1-contracts-foundry/script-config-template/config-deploy-l1.toml @@ -9,9 +9,9 @@ max_number_of_chains = 100 create2_factory_salt = "0x00000000000000000000000000000000000000000000000000000000000000ff" create2_factory_addr = "0x0000000000000000000000000000000000000000" validator_timelock_execution_delay = 0 -genesis_root = "0x0000000000000000000000000000000000000000000000000000000000000000" -genesis_rollup_leaf_index = 0 -genesis_batch_commitment = "0x0000000000000000000000000000000000000000000000000000000000000000" +genesis_root = "0x1000000000000000000000000000000000000000000000000000000000000000" +genesis_rollup_leaf_index = 1 +genesis_batch_commitment = "0x1000000000000000000000000000000000000000000000000000000000000000" latest_protocol_version = 0 recursion_node_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" recursion_leaf_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/l1-contracts-foundry/script/DeployL1.s.sol b/l1-contracts-foundry/script/DeployL1.s.sol index c89eb3abcc5d..6c2874e644ed 100644 --- a/l1-contracts-foundry/script/DeployL1.s.sol +++ b/l1-contracts-foundry/script/DeployL1.s.sol @@ -24,7 +24,7 @@ import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; import {StateTransitionManager} from "contracts/state-transition/StateTransitionManager.sol"; -import {StateTransitionManagerInitializeData} from "contracts/state-transition/IStateTransitionManager.sol"; +import {StateTransitionManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; import {IStateTransitionManager} from "contracts/state-transition/IStateTransitionManager.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {InitializeDataNewChain as DiamondInitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; @@ -428,14 +428,18 @@ contract DeployL1Script is Script { config.contracts.diamondCutData = abi.encode(diamondCut); - StateTransitionManagerInitializeData memory diamondInitData = StateTransitionManagerInitializeData({ - owner: config.ownerAddress, - validatorTimelock: addresses.validatorTimelock, + ChainCreationParams memory chainCreationParams = ChainCreationParams({ genesisUpgrade: addresses.stateTransition.genesisUpgrade, genesisBatchHash: config.contracts.genesisRoot, genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), genesisBatchCommitment: config.contracts.genesisBatchCommitment, - diamondCut: diamondCut, + diamondCut: diamondCut + }); + + StateTransitionManagerInitializeData memory diamondInitData = StateTransitionManagerInitializeData({ + owner: config.ownerAddress, + validatorTimelock: addresses.validatorTimelock, + chainCreationParams: chainCreationParams, protocolVersion: config.contracts.latestProtocolVersion }); diff --git a/l1-contracts/contracts/state-transition/IStateTransitionManager.sol b/l1-contracts/contracts/state-transition/IStateTransitionManager.sol index 4151a1454d39..4d7f7fc6bf2f 100644 --- a/l1-contracts/contracts/state-transition/IStateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/IStateTransitionManager.sol @@ -10,21 +10,28 @@ import {FeeParams} from "./chain-deps/ZkSyncHyperchainStorage.sol"; /// @dev We use struct instead of raw parameters in `initialize` function to prevent "Stack too deep" error /// @param owner The address who can manage non-critical updates in the contract /// @param validatorTimelock The address that serves as consensus, i.e. can submit blocks to be processed +/// @param chainCreationParams The struct that contains the fields that define how a new chain should be created +/// @param protocolVersion The initial protocol version on the newly deployed chain +struct StateTransitionManagerInitializeData { + address owner; + address validatorTimelock; + ChainCreationParams chainCreationParams; + uint256 protocolVersion; +} + +/// @notice The struct that contains the fields that define how a new chain should be created +/// within this STM. /// @param genesisUpgrade The address that is used in the diamond cut initialize address on chain creation /// @param genesisBatchHash Batch hash of the genesis (initial) batch /// @param genesisIndexRepeatedStorageChanges The serial number of the shortcut storage key for the genesis batch /// @param genesisBatchCommitment The zk-proof commitment for the genesis batch /// @param diamondCut The diamond cut for the first upgrade transaction on the newly deployed chain -/// @param protocolVersion The initial protocol version on the newly deployed chain -struct StateTransitionManagerInitializeData { - address owner; - address validatorTimelock; +struct ChainCreationParams { address genesisUpgrade; bytes32 genesisBatchHash; uint64 genesisIndexRepeatedStorageChanges; bytes32 genesisBatchCommitment; Diamond.DiamondCutData diamondCut; - uint256 protocolVersion; } interface IStateTransitionManager { @@ -48,8 +55,14 @@ interface IStateTransitionManager { /// @notice ValidatorTimelock changed event NewValidatorTimelock(address indexed oldValidatorTimelock, address indexed newValidatorTimelock); - /// @notice InitialCutHash changed - event NewInitialCutHash(bytes32 indexed oldInitialCutHash, bytes32 indexed newInitialCutHash); + /// @notice chain creation parameters changed + event NewChainCreationParams( + address genesisUpgrade, + bytes32 genesisBatchHash, + uint64 genesisIndexRepeatedStorageChanges, + bytes32 genesisBatchCommitment, + bytes32 newInitialCutHash + ); /// @notice new UpgradeCutHash event NewUpgradeCutHash(uint256 indexed protocolVersion, bytes32 indexed upgradeCutHash); @@ -85,10 +98,10 @@ interface IStateTransitionManager { function initialize(StateTransitionManagerInitializeData calldata _initializeData) external; - function setInitialCutHash(Diamond.DiamondCutData calldata _diamondCut) external; - function setValidatorTimelock(address _validatorTimelock) external; + function setChainCreationParams(ChainCreationParams calldata _chainCreationParams) external; + function getChainAdmin(uint256 _chainId) external view returns (address); function createNewChain( diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index 812e6fa11217..dc4ac9446aa3 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -11,7 +11,7 @@ import {IAdmin} from "./chain-interfaces/IAdmin.sol"; import {IDefaultUpgrade} from "../upgrades/IDefaultUpgrade.sol"; import {IDiamondInit} from "./chain-interfaces/IDiamondInit.sol"; import {IExecutor} from "./chain-interfaces/IExecutor.sol"; -import {IStateTransitionManager, StateTransitionManagerInitializeData} from "./IStateTransitionManager.sol"; +import {IStateTransitionManager, StateTransitionManagerInitializeData, ChainCreationParams} from "./IStateTransitionManager.sol"; import {ISystemContext} from "./l2-deps/ISystemContext.sol"; import {IZkSyncHyperchain} from "./chain-interfaces/IZkSyncHyperchain.sol"; import {FeeParams} from "./chain-deps/ZkSyncHyperchainStorage.sol"; @@ -72,6 +72,10 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own constructor(address _bridgehub, uint256 _maxNumberOfHyperchains) 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 + assert(L2_TO_L1_LOG_SERIALIZE_SIZE != 2 * 32); } /// @notice only the bridgehub can call @@ -124,28 +128,54 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own require(_initializeData.owner != address(0), "STM: owner zero"); _transferOwnership(_initializeData.owner); - genesisUpgrade = _initializeData.genesisUpgrade; protocolVersion = _initializeData.protocolVersion; protocolVersionDeadline[_initializeData.protocolVersion] = type(uint256).max; validatorTimelock = _initializeData.validatorTimelock; + _setChainCreationParams(_initializeData.chainCreationParams); + } + + /// @notice Updates the parameters with which a new chain is created + /// @param _chainCreationParams The new chain creation parameters + function _setChainCreationParams(ChainCreationParams calldata _chainCreationParams) internal { + require(_chainCreationParams.genesisUpgrade != address(0), "STM: genesisUpgrade zero"); + require(_chainCreationParams.genesisBatchHash != bytes32(0), "STM: genesisBatchHash zero"); + require( + _chainCreationParams.genesisIndexRepeatedStorageChanges != uint64(0), + "STM: genesisIndexRepeatedStorageChanges zero" + ); + require(_chainCreationParams.genesisBatchCommitment != bytes32(0), "STM: genesisBatchCommitment zero"); + + genesisUpgrade = _chainCreationParams.genesisUpgrade; + // We need to initialize the state hash because it is used in the commitment of the next batch IExecutor.StoredBatchInfo memory batchZero = IExecutor.StoredBatchInfo({ batchNumber: 0, - batchHash: _initializeData.genesisBatchHash, - indexRepeatedStorageChanges: _initializeData.genesisIndexRepeatedStorageChanges, + batchHash: _chainCreationParams.genesisBatchHash, + indexRepeatedStorageChanges: _chainCreationParams.genesisIndexRepeatedStorageChanges, numberOfLayer1Txs: 0, priorityOperationsHash: EMPTY_STRING_KECCAK, l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, timestamp: 0, - commitment: _initializeData.genesisBatchCommitment + commitment: _chainCreationParams.genesisBatchCommitment }); storedBatchZero = keccak256(abi.encode(batchZero)); - initialCutHash = keccak256(abi.encode(_initializeData.diamondCut)); + bytes32 newInitialCutHash = keccak256(abi.encode(_chainCreationParams.diamondCut)); + initialCutHash = newInitialCutHash; + + emit NewChainCreationParams({ + genesisUpgrade: _chainCreationParams.genesisUpgrade, + genesisBatchHash: _chainCreationParams.genesisBatchHash, + genesisIndexRepeatedStorageChanges: _chainCreationParams.genesisIndexRepeatedStorageChanges, + genesisBatchCommitment: _chainCreationParams.genesisBatchCommitment, + newInitialCutHash: newInitialCutHash + }); + } - // 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 - assert(L2_TO_L1_LOG_SERIALIZE_SIZE != 2 * 32); + /// @notice Updates the parameters with which a new chain is created + /// @param _chainCreationParams The new chain creation parameters + function setChainCreationParams(ChainCreationParams calldata _chainCreationParams) external onlyOwner { + _setChainCreationParams(_chainCreationParams); } /// @notice Starts the transfer of admin rights. Only the current admin can propose a new pending one. @@ -181,14 +211,6 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own emit NewValidatorTimelock(oldValidatorTimelock, _validatorTimelock); } - /// @dev set initial cutHash - function setInitialCutHash(Diamond.DiamondCutData calldata _diamondCut) external onlyOwner { - bytes32 oldInitialCutHash = initialCutHash; - bytes32 newCutHash = keccak256(abi.encode(_diamondCut)); - initialCutHash = newCutHash; - emit NewInitialCutHash(oldInitialCutHash, newCutHash); - } - /// @dev set New Version with upgrade from old version function setNewVersionUpgrade( Diamond.DiamondCutData calldata _cutData, diff --git a/l1-contracts/src.ts/deploy-test-process.ts b/l1-contracts/src.ts/deploy-test-process.ts index 269d8a7a8228..fe383e7558ed 100644 --- a/l1-contracts/src.ts/deploy-test-process.ts +++ b/l1-contracts/src.ts/deploy-test-process.ts @@ -39,9 +39,9 @@ const testnetTokenPath = `${testConfigPath}/hardhat.json`; export async function loadDefaultEnvVarsForTests(deployWallet: Wallet) { process.env.CONTRACTS_GENESIS_PROTOCOL_SEMANTIC_VERSION = "0.21.0"; - process.env.CONTRACTS_GENESIS_ROOT = ethers.constants.HashZero; - process.env.CONTRACTS_GENESIS_ROLLUP_LEAF_INDEX = "0"; - process.env.CONTRACTS_GENESIS_BATCH_COMMITMENT = ethers.constants.HashZero; + process.env.CONTRACTS_GENESIS_ROOT = "0x0000000000000000000000000000000000000000000000000000000000000001"; + process.env.CONTRACTS_GENESIS_ROLLUP_LEAF_INDEX = "1"; + process.env.CONTRACTS_GENESIS_BATCH_COMMITMENT = "0x0000000000000000000000000000000000000000000000000000000000000001"; // process.env.CONTRACTS_GENESIS_UPGRADE_ADDR = ADDRESS_ONE; process.env.CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT = "72000000"; process.env.CONTRACTS_FRI_RECURSION_NODE_LEVEL_VK_HASH = ethers.constants.HashZero; diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 77dfba303b89..4ff958560da6 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -10,7 +10,6 @@ import { packSemver, readBatchBootloaderBytecode, readSystemContractsBytecode, - SYSTEM_CONFIG, unpackStringSemVer, } from "../scripts/utils"; import { getTokens } from "./deploy-token"; @@ -22,6 +21,7 @@ import { PubdataPricingMode, hashL2Bytecode, DIAMOND_CUT_DATA_ABI_STRING, + compileInitialCutHash, } from "./utils"; import { IBridgehubFactory } from "../typechain/IBridgehubFactory"; import { IGovernanceFactory } from "../typechain/IGovernanceFactory"; @@ -35,7 +35,7 @@ import { L1SharedBridgeFactory } from "../typechain/L1SharedBridgeFactory"; import { SingletonFactoryFactory } from "../typechain/SingletonFactoryFactory"; import { ValidatorTimelockFactory } from "../typechain/ValidatorTimelockFactory"; import type { FacetCut } from "./diamondCut"; -import { diamondCut, getCurrentFacetCutsForAdd } from "./diamondCut"; +import { getCurrentFacetCutsForAdd } from "./diamondCut"; import { ERC20Factory } from "../typechain"; import type { Contract, Overrides } from "@ethersproject/contracts"; @@ -98,43 +98,17 @@ export class Deployer { recursionCircuitsSetVksHash: "0x0000000000000000000000000000000000000000000000000000000000000000", }; const priorityTxMaxGasLimit = getNumberFromEnv("CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT"); - const DiamondInit = new Interface(hardhat.artifacts.readArtifactSync("DiamondInit").abi); - - const feeParams = { - pubdataPricingMode: PubdataPricingMode.Rollup, - batchOverheadL1Gas: SYSTEM_CONFIG.priorityTxBatchOverheadL1Gas, - maxPubdataPerBatch: SYSTEM_CONFIG.priorityTxPubdataPerBatch, - priorityTxMaxPubdata: SYSTEM_CONFIG.priorityTxMaxPubdata, - maxL2GasPerBatch: SYSTEM_CONFIG.priorityTxMaxGasPerBatch, - minimalL2GasPrice: SYSTEM_CONFIG.priorityTxMinimalGasPrice, - }; - const diamondInitCalldata = DiamondInit.encodeFunctionData("initialize", [ - // these first values are set in the contract - { - chainId: "0x0000000000000000000000000000000000000000000000000000000000000001", - bridgehub: "0x0000000000000000000000000000000000001234", - stateTransitionManager: "0x0000000000000000000000000000000000002234", - protocolVersion: "0x0000000000000000000000000000000000002234", - admin: "0x0000000000000000000000000000000000003234", - validatorTimelock: "0x0000000000000000000000000000000000004234", - baseToken: "0x0000000000000000000000000000000000004234", - baseTokenBridge: "0x0000000000000000000000000000000000004234", - storedBatchZero: "0x0000000000000000000000000000000000000000000000000000000000005432", - verifier: this.addresses.StateTransition.Verifier, - verifierParams, - l2BootloaderBytecodeHash: L2_BOOTLOADER_BYTECODE_HASH, - l2DefaultAccountBytecodeHash: L2_DEFAULT_ACCOUNT_BYTECODE_HASH, - priorityTxMaxGasLimit, - feeParams, - blobVersionedHashRetriever: this.addresses.BlobVersionedHashRetriever, - }, - ]); - - return diamondCut( + return compileInitialCutHash( facetCuts, + verifierParams, + L2_BOOTLOADER_BYTECODE_HASH, + L2_DEFAULT_ACCOUNT_BYTECODE_HASH, + this.addresses.StateTransition.Verifier, + this.addresses.BlobVersionedHashRetriever, + +priorityTxMaxGasLimit, this.addresses.StateTransition.DiamondInit, - "0x" + diamondInitCalldata.slice(2 + (4 + 9 * 32) * 2) + false ); } @@ -317,15 +291,19 @@ export class Deployer { const stateTransitionManager = new Interface(hardhat.artifacts.readArtifactSync("StateTransitionManager").abi); + const chainCreationParams = { + genesisUpgrade: this.addresses.StateTransition.GenesisUpgrade, + genesisBatchHash, + genesisIndexRepeatedStorageChanges: genesisRollupLeafIndex, + genesisBatchCommitment, + diamondCut, + }; + const initCalldata = stateTransitionManager.encodeFunctionData("initialize", [ { owner: this.addresses.Governance, validatorTimelock: this.addresses.ValidatorTimeLock, - genesisUpgrade: this.addresses.StateTransition.GenesisUpgrade, - genesisBatchHash, - genesisIndexRepeatedStorageChanges: genesisRollupLeafIndex, - genesisBatchCommitment, - diamondCut, + chainCreationParams, protocolVersion, }, ]); diff --git a/l1-contracts/src.ts/utils.ts b/l1-contracts/src.ts/utils.ts index e370e4499879..ca18bc7e4840 100644 --- a/l1-contracts/src.ts/utils.ts +++ b/l1-contracts/src.ts/utils.ts @@ -6,6 +6,10 @@ import type { BytesLike, BigNumberish } from "ethers"; import { ethers } from "ethers"; import * as fs from "fs"; import * as path from "path"; +import { DiamondInitFactory } from "../typechain"; +import type { DiamondCut, FacetCut } from "./diamondCut"; +import { diamondCut } from "./diamondCut"; +import { SYSTEM_CONFIG } from "../scripts/utils"; export const testConfigPath = process.env.ZKSYNC_ENV ? path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant") @@ -174,3 +178,112 @@ export interface L2CanonicalTransaction { // But it is still here, just in case we want to enable some additional functionality. reservedDynamic: BytesLike; } + +// Checks that the initial cut hash params are valid. +// Sometimes it makes sense to allow dummy values for testing purposes, but in production +// these values should be set correctly. +function checkValidInitialCutHashParams( + facetCuts: FacetCut[], + verifierParams: VerifierParams, + l2BootloaderBytecodeHash: string, + l2DefaultAccountBytecodeHash: string, + verifier: string, + blobVersionedHashRetriever: string, + priorityTxMaxGasLimit: number +) { + // We do not fetch the following numbers from the environment because they are very rarely changed + // and we want to avoid the risk of accidentally changing them. + const EXPECTED_FACET_CUTS = 4; + const EXPECTED_PRIORITY_TX_MAX_GAS_LIMIT = 72_000_000; + + if (facetCuts.length != EXPECTED_FACET_CUTS) { + throw new Error(`Expected ${EXPECTED_FACET_CUTS} facet cuts, got ${facetCuts.length}`); + } + + if (verifierParams.recursionNodeLevelVkHash === ethers.constants.HashZero) { + throw new Error("Recursion node level vk hash is zero"); + } + if (verifierParams.recursionLeafLevelVkHash === ethers.constants.HashZero) { + throw new Error("Recursion leaf level vk hash is zero"); + } + if (verifierParams.recursionCircuitsSetVksHash !== ethers.constants.HashZero) { + throw new Error("Recursion circuits set vks hash must be zero"); + } + if (l2BootloaderBytecodeHash === ethers.constants.HashZero) { + throw new Error("L2 bootloader bytecode hash is zero"); + } + if (l2DefaultAccountBytecodeHash === ethers.constants.HashZero) { + throw new Error("L2 default account bytecode hash is zero"); + } + if (verifier === ethers.constants.AddressZero) { + throw new Error("Verifier address is zero"); + } + if (blobVersionedHashRetriever === ethers.constants.AddressZero) { + throw new Error("Blob versioned hash retriever address is zero"); + } + if (priorityTxMaxGasLimit !== EXPECTED_PRIORITY_TX_MAX_GAS_LIMIT) { + throw new Error( + `Expected priority tx max gas limit to be ${EXPECTED_PRIORITY_TX_MAX_GAS_LIMIT}, got ${priorityTxMaxGasLimit}` + ); + } +} + +// We should either reuse code or add a test for this function. +export function compileInitialCutHash( + facetCuts: FacetCut[], + verifierParams: VerifierParams, + l2BootloaderBytecodeHash: string, + l2DefaultAccountBytecodeHash: string, + verifier: string, + blobVersionedHashRetriever: string, + priorityTxMaxGasLimit: number, + diamondInit: string, + strictMode: boolean = true +): DiamondCut { + if (strictMode) { + checkValidInitialCutHashParams( + facetCuts, + verifierParams, + l2BootloaderBytecodeHash, + l2DefaultAccountBytecodeHash, + verifier, + blobVersionedHashRetriever, + priorityTxMaxGasLimit + ); + } + + const factory = new DiamondInitFactory(); + + const feeParams = { + pubdataPricingMode: PubdataPricingMode.Rollup, + batchOverheadL1Gas: SYSTEM_CONFIG.priorityTxBatchOverheadL1Gas, + maxPubdataPerBatch: SYSTEM_CONFIG.priorityTxPubdataPerBatch, + priorityTxMaxPubdata: SYSTEM_CONFIG.priorityTxMaxPubdata, + maxL2GasPerBatch: SYSTEM_CONFIG.priorityTxMaxGasPerBatch, + minimalL2GasPrice: SYSTEM_CONFIG.priorityTxMinimalGasPrice, + }; + + const diamondInitCalldata = factory.interface.encodeFunctionData("initialize", [ + // these first values are set in the contract + { + chainId: "0x0000000000000000000000000000000000000000000000000000000000000001", + bridgehub: "0x0000000000000000000000000000000000001234", + stateTransitionManager: "0x0000000000000000000000000000000000002234", + protocolVersion: "0x0000000000000000000000000000000000002234", + admin: "0x0000000000000000000000000000000000003234", + validatorTimelock: "0x0000000000000000000000000000000000004234", + baseToken: "0x0000000000000000000000000000000000004234", + baseTokenBridge: "0x0000000000000000000000000000000000004234", + storedBatchZero: "0x0000000000000000000000000000000000000000000000000000000000005432", + verifier, + verifierParams, + l2BootloaderBytecodeHash, + l2DefaultAccountBytecodeHash, + priorityTxMaxGasLimit, + feeParams, + blobVersionedHashRetriever, + }, + ]); + + return diamondCut(facetCuts, diamondInit, "0x" + diamondInitCalldata.slice(2 + (4 + 9 * 32) * 2)); +} 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 3dcc863fc2a5..9ef3ae0fbd32 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 @@ -7,6 +7,7 @@ import {stdStorage, StdStorage, Test} from "forge-std/Test.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; import {DummyStateTransitionManagerWBH} from "contracts/dev-contracts/test/DummyStateTransitionManagerWithBridgeHubAddress.sol"; import {DummyHyperchain} from "contracts/dev-contracts/test/DummyHyperchain.sol"; @@ -861,7 +862,16 @@ contract ExperimentalBridgeTest is Test { diamondCutData.initAddress = address(0); diamondCutData.initCalldata = ""; - mockSTM.setInitialCutHash(diamondCutData); + ChainCreationParams memory params = ChainCreationParams({ + diamondCut: diamondCutData, + // Just some dummy values: + genesisUpgrade: address(0x01), + genesisBatchHash: bytes32(uint256(0x01)), + genesisIndexRepeatedStorageChanges: uint64(0x01), + genesisBatchCommitment: bytes32(uint256(0x01)) + }); + + mockSTM.setChainCreationParams(params); return abi.encode(diamondCutData); } diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/RevertBatches.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/RevertBatches.t.sol index 7e277c1ec043..2113f34671a2 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/RevertBatches.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/RevertBatches.t.sol @@ -41,13 +41,13 @@ contract revertBatchesTest is StateTransitionManagerTest { genesisStoredBatchInfo = IExecutor.StoredBatchInfo({ batchNumber: 0, - batchHash: bytes32(""), - indexRepeatedStorageChanges: 0, + batchHash: bytes32(uint256(0x01)), + indexRepeatedStorageChanges: 1, numberOfLayer1Txs: 0, priorityOperationsHash: EMPTY_STRING_KECCAK, l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, timestamp: 0, - commitment: bytes32("") + commitment: bytes32(uint256(0x01)) }); adminFacet.setTokenMultiplier(1, 1); @@ -65,7 +65,7 @@ contract revertBatchesTest is StateTransitionManagerTest { newCommitBatchInfo = IExecutor.CommitBatchInfo({ batchNumber: 1, timestamp: uint64(currentTimestamp), - indexRepeatedStorageChanges: 0, + indexRepeatedStorageChanges: 1, newStateRoot: Utils.randomBytes32("newStateRoot"), numberOfLayer1Txs: 0, priorityOperationsHash: keccak256(""), @@ -91,6 +91,13 @@ contract revertBatchesTest is StateTransitionManagerTest { Utils.packBatchTimestampAndBlockTimestamp(currentTimestamp, currentTimestamp) ); + correctL2Logs[uint256(uint256(SystemLogKey.PREV_BATCH_HASH_KEY))] = Utils.constructL2Log( + true, + L2_SYSTEM_CONTEXT_ADDRESS, + uint256(SystemLogKey.PREV_BATCH_HASH_KEY), + bytes32(uint256(0x01)) + ); + l2Logs = Utils.encodePacked(correctL2Logs); newCommitBatchInfo.timestamp = uint64(currentTimestamp); newCommitBatchInfo.systemLogs = l2Logs; @@ -107,7 +114,7 @@ contract revertBatchesTest is StateTransitionManagerTest { newStoredBatchInfo = IExecutor.StoredBatchInfo({ batchNumber: 1, batchHash: entries[0].topics[2], - indexRepeatedStorageChanges: 0, + indexRepeatedStorageChanges: 1, numberOfLayer1Txs: 0, priorityOperationsHash: keccak256(""), l2LogsTreeRoot: 0, diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetChainCreationParams.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetChainCreationParams.t.sol new file mode 100644 index 000000000000..85fa1a3169ad --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetChainCreationParams.t.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; +import {ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; +import {IExecutor} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; +import {EMPTY_STRING_KECCAK, DEFAULT_L2_LOGS_TREE_ROOT_HASH} from "contracts/common/Config.sol"; + +contract SetChainCreationParamsTest is StateTransitionManagerTest { + function test_SettingInitialCutHash() public { + bytes32 initialCutHash = keccak256(abi.encode(getDiamondCutData(address(diamondInit)))); + address randomDiamondInit = address(0x303030303030303030303); + + assertEq(chainContractAddress.initialCutHash(), initialCutHash, "Initial cut hash is not correct"); + + Diamond.DiamondCutData memory newDiamondCutData = getDiamondCutData(address(randomDiamondInit)); + bytes32 newCutHash = keccak256(abi.encode(newDiamondCutData)); + + address newGenesisUpgrade = address(0x02); + bytes32 genesisBatchHash = bytes32(uint256(0x02)); + uint64 genesisIndexRepeatedStorageChanges = 2; + bytes32 genesisBatchCommitment = bytes32(uint256(0x02)); + + ChainCreationParams memory newChainCreationParams = ChainCreationParams({ + genesisUpgrade: newGenesisUpgrade, + genesisBatchHash: genesisBatchHash, + genesisIndexRepeatedStorageChanges: genesisIndexRepeatedStorageChanges, + genesisBatchCommitment: genesisBatchCommitment, + diamondCut: newDiamondCutData + }); + + chainContractAddress.setChainCreationParams(newChainCreationParams); + + assertEq(chainContractAddress.initialCutHash(), newCutHash, "Initial cut hash update was not successful"); + assertEq(chainContractAddress.genesisUpgrade(), newGenesisUpgrade, "Genesis upgrade was not set correctly"); + + // We need to initialize the state hash because it is used in the commitment of the next batch + IExecutor.StoredBatchInfo memory newBatchZero = IExecutor.StoredBatchInfo({ + batchNumber: 0, + batchHash: genesisBatchHash, + indexRepeatedStorageChanges: genesisIndexRepeatedStorageChanges, + numberOfLayer1Txs: 0, + priorityOperationsHash: EMPTY_STRING_KECCAK, + l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, + timestamp: 0, + commitment: genesisBatchCommitment + }); + bytes32 expectedStoredBatchZero = keccak256(abi.encode(newBatchZero)); + + assertEq( + chainContractAddress.storedBatchZero(), + expectedStoredBatchZero, + "Stored batch zero was not set correctly" + ); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetInitialCutHash.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetInitialCutHash.t.sol deleted file mode 100644 index f0fc3785818e..000000000000 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetInitialCutHash.t.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; -import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; - -contract setInitialCutHashTest is StateTransitionManagerTest { - function test_SettingInitialCutHash() public { - bytes32 initialCutHash = keccak256(abi.encode(getDiamondCutData(address(diamondInit)))); - address randomDiamondInit = address(0x303030303030303030303); - - assertEq(chainContractAddress.initialCutHash(), initialCutHash, "Initial cut hash is not correct"); - - Diamond.DiamondCutData memory newDiamondCutData = getDiamondCutData(address(randomDiamondInit)); - bytes32 newCutHash = keccak256(abi.encode(newDiamondCutData)); - - chainContractAddress.setInitialCutHash(newDiamondCutData); - - assertEq(chainContractAddress.initialCutHash(), newCutHash, "Initial cut hash update was not successful"); - } -} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/StateTransitionOwnerZero.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/StateTransitionOwnerZero.t.sol index 60c606a3d895..8fae0aa1ed7b 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/StateTransitionOwnerZero.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/StateTransitionOwnerZero.t.sol @@ -4,18 +4,22 @@ pragma solidity 0.8.24; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; import {StateTransitionManager} from "contracts/state-transition/StateTransitionManager.sol"; -import {StateTransitionManagerInitializeData} from "contracts/state-transition/IStateTransitionManager.sol"; +import {StateTransitionManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; contract initializingSTMOwnerZeroTest is StateTransitionManagerTest { function test_InitializingSTMWithGovernorZeroShouldRevert() public { + ChainCreationParams memory chainCreationParams = ChainCreationParams({ + genesisUpgrade: address(genesisUpgradeContract), + genesisBatchHash: bytes32(uint256(0x01)), + genesisIndexRepeatedStorageChanges: 1, + genesisBatchCommitment: bytes32(uint256(0x01)), + diamondCut: getDiamondCutData(address(diamondInit)) + }); + StateTransitionManagerInitializeData memory stmInitializeDataNoOwner = StateTransitionManagerInitializeData({ owner: address(0), validatorTimelock: validator, - genesisUpgrade: address(genesisUpgradeContract), - genesisBatchHash: bytes32(""), - genesisIndexRepeatedStorageChanges: 0, - genesisBatchCommitment: bytes32(""), - diamondCut: getDiamondCutData(address(diamondInit)), + chainCreationParams: chainCreationParams, 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 cb7251185241..2cdbd8e00897 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 @@ -16,7 +16,7 @@ import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol import {GenesisUpgrade} from "contracts/upgrades/GenesisUpgrade.sol"; import {InitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; import {StateTransitionManager} from "contracts/state-transition/StateTransitionManager.sol"; -import {StateTransitionManagerInitializeData} from "contracts/state-transition/IStateTransitionManager.sol"; +import {StateTransitionManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; contract StateTransitionManagerTest is Test { @@ -78,14 +78,18 @@ contract StateTransitionManagerTest is Test { }) ); + ChainCreationParams memory chainCreationParams = ChainCreationParams({ + genesisUpgrade: address(genesisUpgradeContract), + genesisBatchHash: bytes32(uint256(0x01)), + genesisIndexRepeatedStorageChanges: 0x01, + genesisBatchCommitment: bytes32(uint256(0x01)), + diamondCut: getDiamondCutData(address(diamondInit)) + }); + StateTransitionManagerInitializeData memory stmInitializeDataNoGovernor = StateTransitionManagerInitializeData({ owner: address(0), validatorTimelock: validator, - genesisUpgrade: address(genesisUpgradeContract), - genesisBatchHash: bytes32(""), - genesisIndexRepeatedStorageChanges: 0, - genesisBatchCommitment: bytes32(""), - diamondCut: getDiamondCutData(address(diamondInit)), + chainCreationParams: chainCreationParams, protocolVersion: 0 }); @@ -99,11 +103,7 @@ contract StateTransitionManagerTest is Test { StateTransitionManagerInitializeData memory stmInitializeData = StateTransitionManagerInitializeData({ owner: governor, validatorTimelock: validator, - genesisUpgrade: address(genesisUpgradeContract), - genesisBatchHash: bytes32(""), - genesisIndexRepeatedStorageChanges: 0, - genesisBatchCommitment: bytes32(""), - diamondCut: getDiamondCutData(address(diamondInit)), + chainCreationParams: chainCreationParams, protocolVersion: 0 }); diff --git a/l1-contracts/test/unit_tests/utils.ts b/l1-contracts/test/unit_tests/utils.ts index f1a2b49f243e..c42b55c19a2c 100644 --- a/l1-contracts/test/unit_tests/utils.ts +++ b/l1-contracts/test/unit_tests/utils.ts @@ -253,7 +253,8 @@ export function createSystemLogs( export function createSystemLogsWithUpgrade( chainedPriorityTxHashKey?: BytesLike, numberOfLayer1Txs?: BigNumberish, - upgradeTxHash?: string + upgradeTxHash?: string, + previousBatchHash?: string ) { return [ constructL2Log(true, L2_TO_L1_MESSENGER, SYSTEM_LOG_KEYS.L2_TO_L1_LOGS_TREE_ROOT_KEY, ethers.constants.HashZero), @@ -265,7 +266,12 @@ export function createSystemLogsWithUpgrade( SYSTEM_LOG_KEYS.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY, ethers.constants.HashZero ), - constructL2Log(true, L2_SYSTEM_CONTEXT_ADDRESS, SYSTEM_LOG_KEYS.PREV_BATCH_HASH_KEY, ethers.constants.HashZero), + constructL2Log( + true, + L2_SYSTEM_CONTEXT_ADDRESS, + SYSTEM_LOG_KEYS.PREV_BATCH_HASH_KEY, + previousBatchHash ? previousBatchHash : ethers.constants.HashZero + ), constructL2Log( true, L2_BOOTLOADER_ADDRESS, @@ -311,13 +317,13 @@ export function createSystemLogsWithUpgrade( export function genesisStoredBatchInfo(): StoredBatchInfo { return { batchNumber: 0, - batchHash: ethers.constants.HashZero, - indexRepeatedStorageChanges: 0, + batchHash: "0x0000000000000000000000000000000000000000000000000000000000000001", + indexRepeatedStorageChanges: 1, numberOfLayer1Txs: 0, priorityOperationsHash: EMPTY_STRING_KECCAK, l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, timestamp: 0, - commitment: ethers.constants.HashZero, + commitment: "0x0000000000000000000000000000000000000000000000000000000000000001", }; } @@ -427,7 +433,12 @@ export async function buildCommitBatchInfoWithUpgrade( upgradeTxHash: string ): Promise { const timestamp = info.timestamp || (await hardhat.ethers.provider.getBlock("latest")).timestamp; - const systemLogs = createSystemLogsWithUpgrade(info.priorityOperationsHash, info.numberOfLayer1Txs, upgradeTxHash); + const systemLogs = createSystemLogsWithUpgrade( + info.priorityOperationsHash, + info.numberOfLayer1Txs, + upgradeTxHash, + ethers.utils.hexlify(prevInfo.batchHash) + ); systemLogs[SYSTEM_LOG_KEYS.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY] = constructL2Log( true, L2_SYSTEM_CONTEXT_ADDRESS, @@ -437,7 +448,7 @@ export async function buildCommitBatchInfoWithUpgrade( return { timestamp, - indexRepeatedStorageChanges: 0, + indexRepeatedStorageChanges: 1, newStateRoot: ethers.utils.randomBytes(32), numberOfLayer1Txs: 0, priorityOperationsHash: EMPTY_STRING_KECCAK,