diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index c5440395c904..b88704733874 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -518,12 +518,15 @@ jobs: # # ci_run ./bin/run_on_all_chains.sh "zkstack dev test revert --no-deps --external-node --no-kill --ignore-prerequisites" ${{ env.CHAINS }} ${{ env.INTEGRATION_TESTS_LOGS_DIR }} # -# # Upgrade tests should run last, because as soon as they -# # finish the bootloader will be different -# # TODO make upgrade tests safe to run multiple times -# - name: Run upgrade test -# run: | -# ci_run zkstack dev test upgrade --no-deps --chain era + # Upgrade tests should run last, because as soon as they + # finish the bootloader will be different + # TODO make upgrade tests safe to run multiple times + - name: Run upgrade test + run: | + ci_run killall -INT zksync_server || true + ci_run zkstack server --ignore-prerequisites --chain gateway &> ${{ env.SERVER_LOGS_DIR }}/gateway.log & + ci_run sleep 5 + ci_run zkstack dev test upgrade --no-deps --chain era - name: Upload logs uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 diff --git a/contracts b/contracts index e6573f6e9e7d..e8648270f38f 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit e6573f6e9e7d52cda2f5c0aa3e772ad10413934d +Subproject commit e8648270f38f170af1dd4db3d0f089e49d74a1ad diff --git a/core/lib/config/src/configs/gateway.rs b/core/lib/config/src/configs/gateway.rs index cc0cdcc1d6a2..1fa23ef3ff07 100644 --- a/core/lib/config/src/configs/gateway.rs +++ b/core/lib/config/src/configs/gateway.rs @@ -28,6 +28,8 @@ pub struct GatewayChainConfig { pub validator_timelock_addr: Address, pub multicall3_addr: Address, pub diamond_proxy_addr: Address, + // FIXME: there is no "governnace" for a chain, only an admin, we + // need to figure out what we mean here pub chain_admin_addr: Option
, pub governance_addr: Address, pub settlement_layer: u64, @@ -37,19 +39,16 @@ impl GatewayChainConfig { pub fn from_gateway_and_chain_data( gateway_config: &GatewayConfig, diamond_proxy_addr: Address, - chain_admin_addr: Address, + l2_chain_admin_addr: Address, settlement_layer: u64, ) -> Self { - // FIXME: there is no "governnace" for a chain, only an admin, we - // need to figure out what we mean here - Self { state_transition_proxy_addr: gateway_config.state_transition_proxy_addr, validator_timelock_addr: gateway_config.validator_timelock_addr, multicall3_addr: gateway_config.multicall3_addr, diamond_proxy_addr, - chain_admin_addr: Some(chain_admin_addr), - governance_addr: chain_admin_addr, + chain_admin_addr: Some(l2_chain_admin_addr), + governance_addr: l2_chain_admin_addr, settlement_layer, } } diff --git a/core/node/consistency_checker/src/lib.rs b/core/node/consistency_checker/src/lib.rs index 368881f127b4..e3f57af4cd7d 100644 --- a/core/node/consistency_checker/src/lib.rs +++ b/core/node/consistency_checker/src/lib.rs @@ -14,12 +14,9 @@ use zksync_eth_client::{ }; use zksync_health_check::{Health, HealthStatus, HealthUpdater, ReactiveHealthCheck}; use zksync_l1_contract_interface::{ - i_executor::{ - commit::kzg::ZK_SYNC_BYTES_PER_BLOB, - structures::{ - CommitBatchInfo, StoredBatchInfo, PUBDATA_SOURCE_BLOBS, PUBDATA_SOURCE_CALLDATA, - PUBDATA_SOURCE_CUSTOM_PRE_GATEWAY, SUPPORTED_ENCODING_VERSION, - }, + i_executor::structures::{ + CommitBatchInfo, StoredBatchInfo, PUBDATA_SOURCE_BLOBS, PUBDATA_SOURCE_CALLDATA, + PUBDATA_SOURCE_CUSTOM_PRE_GATEWAY, SUPPORTED_ENCODING_VERSION, }, Tokenizable, }; @@ -240,23 +237,6 @@ impl LocalL1BatchCommitData { let da = detect_da(protocol_version, reference, self.commitment_mode) .context("cannot detect DA source from reference commitment token")?; - // For rollups with `PubdataSendingMode::Calldata`, it's required that the pubdata fits into a single blob. - if matches!(self.commitment_mode, L1BatchCommitmentMode::Rollup) - && matches!(da, PubdataSendingMode::Calldata) - { - let pubdata_len = self - .l1_batch - .header - .pubdata_input - .as_ref() - .map_or_else(|| self.l1_batch.construct_pubdata().len(), Vec::len); - anyhow::ensure!( - pubdata_len <= ZK_SYNC_BYTES_PER_BLOB, - "pubdata size is too large when using calldata DA source: expected <={ZK_SYNC_BYTES_PER_BLOB} bytes, \ - got {pubdata_len} bytes" - ); - } - let local_token = CommitBatchInfo::new(self.commitment_mode, &self.l1_batch, da).into_token(); anyhow::ensure!( diff --git a/core/tests/ts-integration/src/env.ts b/core/tests/ts-integration/src/env.ts index de0dcac2358c..03d8e1ff8d5c 100644 --- a/core/tests/ts-integration/src/env.ts +++ b/core/tests/ts-integration/src/env.ts @@ -7,7 +7,7 @@ import { Reporter } from './reporter'; import * as yaml from 'yaml'; import { L2_BASE_TOKEN_ADDRESS } from 'zksync-ethers/build/utils'; import { FileConfig, loadConfig, loadEcosystem, shouldLoadConfigFromFile } from 'utils/build/file-configs'; -import { NodeSpawner } from './utils'; +import { NodeSpawner } from 'utils'; import { logsTestPath } from 'utils/build/logs'; import * as nodefs from 'node:fs/promises'; import { exec } from 'utils'; diff --git a/core/tests/ts-integration/tests/fees.test.ts b/core/tests/ts-integration/tests/fees.test.ts index fc156e03f16d..765dc8b73a81 100644 --- a/core/tests/ts-integration/tests/fees.test.ts +++ b/core/tests/ts-integration/tests/fees.test.ts @@ -21,7 +21,7 @@ import { logsTestPath } from 'utils/build/logs'; import { sleep } from 'utils/build'; import { killPidWithAllChilds } from 'utils/build/kill'; import path from 'path'; -import { NodeSpawner } from '../src/utils'; +import { NodeSpawner } from 'utils'; import { sendTransfers } from '../src/context-owner'; import { Reporter } from '../src/reporter'; diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 4065480b121b..8bc8904b6148 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -3,52 +3,74 @@ import { Tester } from './tester'; import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; import { expect } from 'chai'; -import fs from 'fs'; +import fs from 'node:fs/promises'; +import { existsSync, readFileSync } from 'node:fs'; import { BytesLike } from '@ethersproject/bytes'; -import { IZkSyncHyperchain } from 'zksync-ethers/build/typechain'; import { BigNumberish } from 'ethers'; import { loadConfig, shouldLoadConfigFromFile } from 'utils/build/file-configs'; import { Contracts, initContracts, - runServerInBackground, setAggregatedBlockExecuteDeadline, setAggregatedBlockProveDeadline, setBlockCommitDeadlineMs, setEthSenderSenderAggregatedBlockCommitDeadline } from './utils'; import path from 'path'; +import { ZKSYNC_MAIN_ABI } from 'zksync-ethers/build/utils'; const pathToHome = path.join(__dirname, '../../../..'); const fileConfig = shouldLoadConfigFromFile(); const contracts: Contracts = initContracts(pathToHome, fileConfig.loadFromFile); -let serverComponents = [ - 'api', - 'tree', - 'eth', - 'state_keeper', - 'commitment_generator', - 'da_dispatcher', - 'vm_runner_protective_reads' -]; +const ZK_CHAIN_INTERFACE = JSON.parse( + readFileSync(pathToHome + '/contracts/l1-contracts/out/IZKChain.sol/IZKChain.json').toString() +).abi; const depositAmount = ethers.parseEther('0.001'); +interface GatewayInfo { + gatewayChainId: string; + gatewayProvider: zksync.Provider; + gatewayCTM: string; + l2ChainAdmin: string; + l2DiamondProxyAddress: string; +} + +interface Call { + target: string; + value: BigNumberish; + data: BytesLike; +} + describe('Upgrade test', function () { + // Utility wallets for facilitating testing let tester: Tester; let alice: zksync.Wallet; + + // The wallet that controls the ecosystem governance on L1. let ecosystemGovWallet: ethers.Wallet; - let adminGovWallet: ethers.Wallet; - let mainContract: IZkSyncHyperchain; - let governanceContract: ethers.Contract; - let chainAdminContract: ethers.Contract; + // The wallet that controls the chain admin on the settlement layer. + // The settlement layer can be either Gateway or L1. Depending on this, + // the provider changes. + let slAdminGovWallet: ethers.Wallet; + + // The address of the ecosystem governance. It is present on L1. + let ecosystemGovernance: string; + + // The chain admin contract on the settlement layer. + let slChainAdminContract: ethers.Contract; + // The diamond proxy contract on the settlement layer. + let slMainContract: ethers.Contract; + let bootloaderHash: string; + let defaultAccountHash: string; + let bytecodeSupplier: string; let executeOperation: string; let forceDeployAddress: string; let forceDeployBytecode: string; - let logs: fs.WriteStream; + let logs: fs.FileHandle; let ethProviderAddress: string | undefined; let web3JsonRpc: string | undefined; @@ -58,89 +80,130 @@ describe('Upgrade test', function () { let upgradeAddress: string | undefined; let contractsPriorityTxMaxGasLimit: string; + // Information about Gateway. Note, that it is non-null only if the chain settles + // on top of Gateway, i.e. checking whether this variable is null allows to also know + // whetheer the chain settles on top of Gateway. + let gatewayInfo: GatewayInfo | null = null; + + let mainNodeSpawner: utils.NodeSpawner; + before('Create test wallet', async () => { forceDeployAddress = '0xf04ce00000000000000000000000000000000000'; deployerAddress = '0x0000000000000000000000000000000000008007'; complexUpgraderAddress = '0x000000000000000000000000000000000000800f'; + logs = await fs.open('upgrade.log', 'a'); - if (fileConfig.loadFromFile) { - const generalConfig = loadConfig({ - pathToHome, - chain: fileConfig.chain, - config: 'general.yaml' - }); - const contractsConfig = loadConfig({ - pathToHome, - chain: fileConfig.chain, - config: 'contracts.yaml' - }); - const secretsConfig = loadConfig({ + if (!fileConfig.loadFromFile) { + throw new Error('Non file based not supported'); + } + + const generalConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + config: 'general.yaml' + }); + const contractsConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + config: 'contracts.yaml' + }); + const secretsConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + config: 'secrets.yaml' + }); + const genesisConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + config: 'genesis.yaml' + }); + + ethProviderAddress = secretsConfig.l1.l1_rpc_url; + web3JsonRpc = generalConfig.api.web3_json_rpc.http_url; + contractsL2DefaultUpgradeAddr = contractsConfig.l2.default_l2_upgrader; + bytecodeSupplier = contractsConfig.ecosystem_contracts.l1_bytecodes_supplier_addr; + contractsPriorityTxMaxGasLimit = '72000000'; + + const slChainId = genesisConfig.sl_chain_id; + const l1ChainId = genesisConfig.l1_chain_id; + + if (slChainId && l1ChainId != slChainId) { + const gatewayChainConfig = loadConfig({ pathToHome, chain: fileConfig.chain, - config: 'secrets.yaml' + config: 'gateway_chain.yaml' }); - ethProviderAddress = secretsConfig.l1.l1_rpc_url; - web3JsonRpc = generalConfig.api.web3_json_rpc.http_url; - contractsL2DefaultUpgradeAddr = contractsConfig.l2.default_l2_upgrader; - upgradeAddress = contractsConfig.l1.default_upgrade_addr; - contractsPriorityTxMaxGasLimit = '72000000'; - } else { - ethProviderAddress = process.env.L1_RPC_ADDRESS || process.env.ETH_CLIENT_WEB3_URL; - web3JsonRpc = process.env.ZKSYNC_WEB3_API_URL || process.env.API_WEB3_JSON_RPC_HTTP_URL; - contractsL2DefaultUpgradeAddr = process.env.CONTRACTS_L2_DEFAULT_UPGRADE_ADDR!; - - upgradeAddress = process.env.CONTRACTS_DEFAULT_UPGRADE_ADDR; - if (!upgradeAddress) { - throw new Error('CONTRACTS_DEFAULT_UPGRADE_ADDR not set'); - } - contractsPriorityTxMaxGasLimit = process.env.CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT!; + gatewayInfo = { + gatewayChainId: slChainId, + gatewayProvider: new zksync.Provider(secretsConfig.l1.gateway_url), + gatewayCTM: gatewayChainConfig.state_transition_proxy_addr, + l2ChainAdmin: gatewayChainConfig.chain_admin_addr, + l2DiamondProxyAddress: gatewayChainConfig.diamond_proxy_addr + }; } + mainNodeSpawner = new utils.NodeSpawner(pathToHome, logs, fileConfig, { + enableConsensus: false, + ethClientWeb3Url: ethProviderAddress!, + apiWeb3JsonRpcHttpUrl: web3JsonRpc!, + baseTokenAddress: contractsConfig.l1.base_token_addr + }); + tester = await Tester.init(ethProviderAddress!, web3JsonRpc!); alice = tester.emptyWallet(); - if (fileConfig.loadFromFile) { - const chainWalletConfig = loadConfig({ - pathToHome, - chain: fileConfig.chain, - config: 'wallets.yaml' - }); + const chainWalletConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + config: 'wallets.yaml' + }); - adminGovWallet = new ethers.Wallet(chainWalletConfig.governor.private_key, alice._providerL1()); + slAdminGovWallet = gatewayInfo + ? new zksync.Wallet(chainWalletConfig.governor.private_key, gatewayInfo.gatewayProvider) + : new ethers.Wallet(chainWalletConfig.governor.private_key, alice._providerL1()); - const ecosystemWalletConfig = loadConfig({ - pathToHome, - chain: fileConfig.chain, - configsFolder: '../../configs/', - config: 'wallets.yaml' - }); + const ecosystemWalletConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + configsFolder: '../../configs/', + config: 'wallets.yaml' + }); - if (ecosystemWalletConfig.governor.private_key == chainWalletConfig.governor.private_key) { - ecosystemGovWallet = adminGovWallet; - } else { - ecosystemGovWallet = new ethers.Wallet(ecosystemWalletConfig.governor.private_key, alice._providerL1()); - } + // Note, that the following check is needed to reduce flackiness. In case the + // `ecosystemGovWallet` and `slAdminGovWallet` refer to the same account, then + // sending transactions from the same account while using different `Wallet` objects + // could lead to flacky issues. + if (chainWalletConfig.governor.private_key == ecosystemWalletConfig.governor.private_key && !gatewayInfo) { + ecosystemGovWallet = slAdminGovWallet; } else { - let govMnemonic = ethers.Mnemonic.fromPhrase( - require('../../../../etc/test_config/constant/eth.json').mnemonic - ); - let govWalletHD = ethers.HDNodeWallet.fromMnemonic(govMnemonic, "m/44'/60'/0'/0/1"); - adminGovWallet = new ethers.Wallet(govWalletHD.privateKey, alice._providerL1()); - ecosystemGovWallet = adminGovWallet; + ecosystemGovWallet = new ethers.Wallet(ecosystemWalletConfig.governor.private_key, alice._providerL1()); } - logs = fs.createWriteStream('upgrade.log', { flags: 'a' }); + upgradeAddress = await deployDefaultUpgradeImpl(slAdminGovWallet); + forceDeployBytecode = contracts.counterBytecode; + + slChainAdminContract = gatewayInfo + ? new ethers.Contract(gatewayInfo.l2ChainAdmin, contracts.chainAdminAbi, gatewayInfo.gatewayProvider) + : new ethers.Contract( + contractsConfig.l1.chain_admin_addr, + contracts.chainAdminAbi, + tester.syncWallet.providerL1 + ); + + slMainContract = gatewayInfo + ? new ethers.Contract(gatewayInfo.l2DiamondProxyAddress, ZKSYNC_MAIN_ABI, gatewayInfo.gatewayProvider) + : new ethers.Contract(contractsConfig.l1.diamond_proxy_addr, ZKSYNC_MAIN_ABI, tester.syncWallet.providerL1); + + const l1CtmContract = new ethers.Contract( + contractsConfig.ecosystem_contracts.state_transition_proxy_addr, + contracts.chainTypeManager, + tester.syncWallet.providerL1 + ); + ecosystemGovernance = await l1CtmContract.owner(); }); step('Run server and execute some transactions', async () => { - // Make sure server isn't running. - try { - await utils.exec('pkill zksync_server'); - // It may take some time for witness generator to stop. - await utils.sleep(10); - } catch (_) {} - // Set small timeouts. process.env.ETH_SENDER_SENDER_AGGREGATED_BLOCK_COMMIT_DEADLINE = '1'; process.env.ETH_SENDER_SENDER_AGGREGATED_BLOCK_PROVE_DEADLINE = '1'; @@ -154,36 +217,9 @@ describe('Upgrade test', function () { setAggregatedBlockExecuteDeadline(pathToHome, fileConfig, 1); setBlockCommitDeadlineMs(pathToHome, fileConfig, 2000); } + await mainNodeSpawner.killAndSpawnMainNode(); - // Run server in background. - runServerInBackground({ - components: serverComponents, - stdio: ['ignore', logs, logs], - cwd: pathToHome, - useZkStack: fileConfig.loadFromFile, - chain: fileConfig.chain - }); - // Server may need some time to recompile if it's a cold run, so wait for it. - let iter = 0; - while (iter < 30 && !mainContract) { - try { - mainContract = await tester.syncWallet.getMainContract(); - } catch (_) { - await utils.sleep(1); - } - iter += 1; - } - if (!mainContract) { - throw new Error('Server did not start'); - } - - const stmAddr = await mainContract.getStateTransitionManager(); - const stmContract = new ethers.Contract(stmAddr, contracts.stateTransitonManager, tester.syncWallet.providerL1); - const governanceAddr = await stmContract.owner(); - governanceContract = new ethers.Contract(governanceAddr, contracts.governanceAbi, tester.syncWallet.providerL1); - const chainAdminAddr = await mainContract.getAdmin(); - chainAdminContract = new ethers.Contract(chainAdminAddr, contracts.chainAdminAbi, tester.syncWallet.providerL1); - let blocksCommitted = await mainContract.getTotalBatchesCommitted(); + let blocksCommitted = await slMainContract.getTotalBatchesCommitted(); const initialL1BatchNumber = await tester.web3Provider.getL1BatchNumber(); @@ -226,42 +262,35 @@ describe('Upgrade test', function () { } // Wait for at least one new committed block - let newBlocksCommitted = await mainContract.getTotalBatchesCommitted(); + let newBlocksCommitted = await slMainContract.getTotalBatchesCommitted(); let tryCount = 0; while (blocksCommitted === newBlocksCommitted && tryCount < 30) { - newBlocksCommitted = await mainContract.getTotalBatchesCommitted(); + newBlocksCommitted = await slMainContract.getTotalBatchesCommitted(); tryCount += 1; await utils.sleep(1); } }); - step('Send l1 tx for saving new bootloader', async () => { - const path = `${pathToHome}/contracts/system-contracts/zkout/playground_batch.yul/contracts-preprocessed/bootloader/playground_batch.yul.json`; - let bootloaderCode; - if (fs.existsSync(path)) { - bootloaderCode = '0x'.concat(require(path).bytecode.object); - } else { - const legacyPath = `${pathToHome}/contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul.zbin`; - bootloaderCode = ethers.hexlify(fs.readFileSync(legacyPath)); - } + step('Publish bytecodes', async () => { + const bootloaderCode = readCode( + 'contracts/system-contracts/zkout/playground_batch.yul/contracts-preprocessed/bootloader/playground_batch.yul.json', + 'contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul.zbin' + ); + + const defaultAACode = readCode( + 'contracts/system-contracts/zkout/DefaultAccount.sol/DefaultAccount.json', + 'contracts/system-contracts/artifacts-zk/contracts-preprocessed/DefaultAccount.sol/DefaultAccount.json' + ); bootloaderHash = ethers.hexlify(zksync.utils.hashBytecode(bootloaderCode)); - const txHandle = await tester.syncWallet.requestExecute({ - contractAddress: ethers.ZeroAddress, - calldata: '0x', - l2GasLimit: 20000000, - factoryDeps: [bootloaderCode], - overrides: { - gasLimit: 3000000 - } - }); - await txHandle.wait(); - await waitForNewL1Batch(alice); + defaultAccountHash = ethers.hexlify(zksync.utils.hashBytecode(defaultAACode)); + + await publishBytecode(tester.ethWallet, bytecodeSupplier, bootloaderCode); + await publishBytecode(tester.ethWallet, bytecodeSupplier, defaultAACode); + await publishBytecode(tester.ethWallet, bytecodeSupplier, forceDeployBytecode); }); step('Schedule governance call', async () => { - forceDeployBytecode = contracts.counterBytecode; - const forceDeployment: ForceDeployment = { bytecodeHash: ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode)), newAddress: forceDeployAddress, @@ -279,7 +308,7 @@ describe('Upgrade test', function () { ]); const { stmUpgradeData, chainUpgradeCalldata, setTimestampCalldata } = await prepareUpgradeCalldata( - adminGovWallet, + alice._providerL1(), alice._providerL2(), upgradeAddress!, { @@ -296,23 +325,37 @@ describe('Upgrade test', function () { reserved: [0, 0, 0, 0], data, signature: '0x', - factoryDeps: [ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode))], + factoryDeps: [ + bootloaderHash, + defaultAccountHash, + ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode)) + ], paymasterInput: '0x', reservedDynamic: '0x' }, - factoryDeps: [forceDeployBytecode], bootloaderHash, upgradeTimestamp: 0 - } + }, + gatewayInfo ); executeOperation = chainUpgradeCalldata; console.log('Sending scheduleTransparentOperation'); - await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation); + await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation, 0, null); + console.log('Sending executeOperation'); - await sendGovernanceOperation(stmUpgradeData.executeOperation); + await sendGovernanceOperation( + stmUpgradeData.executeOperation, + stmUpgradeData.executeOperationValue, + gatewayInfo ? gatewayInfo.gatewayProvider : null + ); + console.log('Sending chain admin operation'); - await sendChainAdminOperation(setTimestampCalldata); + await sendChainAdminOperation({ + target: await slChainAdminContract.getAddress(), + data: setTimestampCalldata, + value: 0 + }); // Wait for server to process L1 event. await utils.sleep(2); @@ -334,10 +377,10 @@ describe('Upgrade test', function () { l1BatchNumber -= 1; } - let lastBatchExecuted = await mainContract.getTotalBatchesExecuted(); + let lastBatchExecuted = await slMainContract.getTotalBatchesExecuted(); let tryCount = 0; while (lastBatchExecuted < l1BatchNumber && tryCount < 40) { - lastBatchExecuted = await mainContract.getTotalBatchesExecuted(); + lastBatchExecuted = await slMainContract.getTotalBatchesExecuted(); tryCount += 1; await utils.sleep(2); } @@ -346,13 +389,13 @@ describe('Upgrade test', function () { } // Execute the upgrade - const executeMulticallData = chainAdminContract.interface.encodeFunctionData('multicall', [ - [[await mainContract.getAddress(), 0, executeOperation]], - true - ]); - await sendChainAdminOperation(executeMulticallData); + await sendChainAdminOperation({ + target: await slMainContract.getAddress(), + data: executeOperation, + value: 0 + }); - let bootloaderHashL1 = await mainContract.getL2BootloaderBytecodeHash(); + let bootloaderHashL1 = await slMainContract.getL2BootloaderBytecodeHash(); expect(bootloaderHashL1).eq(bootloaderHash); }); @@ -369,18 +412,7 @@ describe('Upgrade test', function () { step('Execute transactions after simple restart', async () => { // Stop server. - await utils.exec('pkill zksync_server'); - await utils.sleep(10); - - // Run again. - runServerInBackground({ - components: serverComponents, - stdio: ['ignore', logs, logs], - cwd: pathToHome, - useZkStack: fileConfig.loadFromFile, - chain: fileConfig.chain - }); - await utils.sleep(10); + await mainNodeSpawner.killAndSpawnMainNode(); // Trying to send a transaction from the same address again await checkedRandomTransfer(alice, 1n); @@ -399,29 +431,93 @@ describe('Upgrade test', function () { } catch (_) {} }); - async function sendGovernanceOperation(data: string) { + async function sendGovernanceOperation( + data: string, + value: BigNumberish, + providerForPriorityOp: zksync.Provider | null + ) { const transaction = await ecosystemGovWallet.sendTransaction({ - to: await governanceContract.getAddress(), + to: ecosystemGovernance, + value: value, data: data, type: 0 }); console.log(`Sent governance operation, tx_hash=${transaction.hash}, nonce=${transaction.nonce}`); - await transaction.wait(); + const receipt = await transaction.wait(); console.log(`Governance operation succeeded, tx_hash=${transaction.hash}`); + + // The governance operations may trigger additional L1->L2 transactions to gateway, which we should wait. + if (!providerForPriorityOp) { + // No further waiting is needed. + return; + } + + const contract = await providerForPriorityOp.getMainContractAddress(); + const hash = zksync.utils.getL2HashFromPriorityOp(receipt!, contract); + console.log(`Gateway L1->L2 transaction with hash ${hash} detected`); + + await providerForPriorityOp.waitForTransaction(hash); + console.log('Transaction complete!'); } - async function sendChainAdminOperation(data: string) { - const transaction = await adminGovWallet.sendTransaction({ - to: await chainAdminContract.getAddress(), - data: data, + async function sendChainAdminOperation(call: Call) { + const executeMulticallData = slChainAdminContract.interface.encodeFunctionData('multicall', [[call], true]); + + const transaction = await slAdminGovWallet.sendTransaction({ + to: await slChainAdminContract.getAddress(), + data: executeMulticallData, type: 0 }); console.log(`Sent chain admin operation, tx_hash=${transaction.hash}, nonce=${transaction.nonce}`); await transaction.wait(); console.log(`Chain admin operation succeeded, tx_hash=${transaction.hash}`); } + + async function deployDefaultUpgradeImpl(runner: ethers.Wallet): Promise { + const bytecodePath = gatewayInfo + ? pathToHome + '/contracts/l1-contracts/zkout/DefaultUpgrade.sol/DefaultUpgrade.json' + : pathToHome + '/contracts/l1-contracts/out/DefaultUpgrade.sol/DefaultUpgrade.json'; + + const bytecode = '0x' + JSON.parse(readFileSync(bytecodePath).toString()).bytecode.object; + + if (gatewayInfo) { + const factory = new zksync.ContractFactory([], bytecode, runner); + return (await factory.deploy()).getAddress(); + } else { + const factory = new ethers.ContractFactory([], bytecode, runner); + return (await factory.deploy()).getAddress(); + } + } }); +function readCode(newPath: string, legacyPath: string): string { + let path = `${pathToHome}/${newPath}`; + if (existsSync(path)) { + return '0x'.concat(require(path).bytecode.object); + } else { + path = `${pathToHome}/${legacyPath}`; + if (path.endsWith('.zbin')) { + return ethers.hexlify(readFileSync(path)); + } else { + return require(path).bytecode; + } + } +} + +async function publishBytecode(wallet: ethers.Wallet, bytecodeSupplierAddr: string, bytecode: string) { + const hash = zksync.utils.hashBytecode(bytecode); + const abi = [ + 'function publishBytecode(bytes calldata _bytecode) public', + 'function publishingBlock(bytes32 _hash) public view returns (uint256)' + ]; + + const contract = new ethers.Contract(bytecodeSupplierAddr, abi, wallet); + const block = await contract.publishingBlock(hash); + if (block == BigInt(0)) { + await (await contract.publishBytecode(bytecode)).wait(); + } +} + async function checkedRandomTransfer(sender: zksync.Wallet, amount: bigint): Promise { const senderBalanceBefore = await sender.getBalance(); const receiverHD = zksync.Wallet.createRandom(); @@ -481,7 +577,7 @@ async function waitForNewL1Batch(wallet: zksync.Wallet): Promise { + let call; + if (gatewayInfo) { + // We will have to perform an L1->L2 transaction to the gateway + call = await composeL1ToL2Call( + gatewayInfo.gatewayChainId, + to, + data, + bridgehubAddr, + l1Provider, + // It does not matter who is the refund recipient in this test + gatewayInfo.l2ChainAdmin + ); + } else { + call = { + target: to, + value: 0, + data + }; + } + const governanceOperation = { calls: [call], predecessor: ethers.ZeroHash, - salt: ethers.ZeroHash + // Use random salt for easier testing + salt: ethers.randomBytes(32) }; // Get transaction data of the `scheduleTransparent` @@ -604,7 +740,48 @@ function prepareGovernanceCalldata(to: string, data: BytesLike): UpgradeCalldata return { scheduleTransparentOperation, - executeOperation + executeOperation, + executeOperationValue: call.value + }; +} + +async function composeL1ToL2Call( + chainId: string, + to: string, + data: BytesLike, + bridgehubAddr: string, + l1Provider: ethers.Provider, + refundRecipient: string +): Promise { + const gasPerPubdata = zksync.utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT; + // Just a constant that needs to be large enough to handle upgrade-related things + const gasLimit = 40_000_000; + + const gasPrice = (await l1Provider.getFeeData()).gasPrice! * BigInt(5); + + const bridgehub = new ethers.Contract(bridgehubAddr, zksync.utils.BRIDGEHUB_ABI, l1Provider); + + const baseCost = await bridgehub.l2TransactionBaseCost(chainId, gasPrice, gasLimit, gasPerPubdata); + + const encodedData = zksync.utils.BRIDGEHUB_ABI.encodeFunctionData('requestL2TransactionDirect', [ + { + chainId: chainId, + mintValue: baseCost, + l2Contract: to, + l2Value: 0, + l2Calldata: data, + l2GasLimit: gasLimit, + l2GasPerPubdataByteLimit: gasPerPubdata, + factoryDeps: [], + refundRecipient + } + ]); + + return { + target: bridgehubAddr, + data: encodedData, + // Works when ETH is the base token + value: baseCost }; } diff --git a/core/tests/upgrade-test/tests/utils.ts b/core/tests/upgrade-test/tests/utils.ts index 93f5b9bb1eee..914dddaa2019 100644 --- a/core/tests/upgrade-test/tests/utils.ts +++ b/core/tests/upgrade-test/tests/utils.ts @@ -1,35 +1,7 @@ import { ethers } from 'ethers'; import * as fs from 'fs'; -import { background } from 'utils'; import { getConfigPath } from 'utils/build/file-configs'; -export function runServerInBackground({ - components, - stdio, - cwd, - useZkStack, - chain -}: { - components?: string[]; - stdio: any; - cwd?: Parameters[0]['cwd']; - useZkStack?: boolean; - chain?: string; -}) { - let command = ''; - - if (useZkStack) { - command = 'zkstack server'; - command += chain ? ` --chain ${chain}` : ''; - } else { - command = 'cd $ZKSYNC_HOME && cargo run --bin zksync_server --release --'; - } - if (components && components.length > 0) { - command += ` --components=${components.join(',')}`; - } - background({ command, stdio, cwd }); -} - export function setEthSenderSenderAggregatedBlockCommitDeadline(pathToHome: string, fileConfig: any, value: number) { setPropertyInGeneralConfig(pathToHome, fileConfig, 'aggregated_block_commit_deadline', value); } @@ -68,7 +40,7 @@ export interface Contracts { l2ForceDeployUpgraderAbi: any; complexUpgraderAbi: any; counterBytecode: any; - stateTransitonManager: any; + chainTypeManager: any; } export function initContracts(pathToHome: string, zkStack: boolean): Contracts { @@ -96,8 +68,8 @@ export function initContracts(pathToHome: string, zkStack: boolean): Contracts { counterBytecode: require(`${pathToHome}/core/tests/ts-integration/artifacts-zk/contracts/counter/counter.sol/Counter.json`) .deployedBytecode, - stateTransitonManager: new ethers.Interface( - require(`${CONTRACTS_FOLDER}/l1-contracts/out/StateTransitionManager.sol/StateTransitionManager.json`).abi + chainTypeManager: new ethers.Interface( + require(`${CONTRACTS_FOLDER}/l1-contracts/out/ChainTypeManager.sol/ChainTypeManager.json`).abi ) }; } else { @@ -123,8 +95,8 @@ export function initContracts(pathToHome: string, zkStack: boolean): Contracts { ), counterBytecode: require(`${pathToHome}/core/tests/ts-integration/zkout/counter.sol/Counter.json`) .deployedBytecode, - stateTransitonManager: new ethers.Interface( - require(`${L1_CONTRACTS_FOLDER}/state-transition/StateTransitionManager.sol/StateTransitionManager.json`).abi + chainTypeManager: new ethers.Interface( + require(`${L1_CONTRACTS_FOLDER}/state-transition/ChainTypeManager.sol/ChainTypeManager.json`).abi ) }; } diff --git a/etc/utils/src/file-configs.ts b/etc/utils/src/file-configs.ts index 374bf53f6be9..01b0014b19c8 100644 --- a/etc/utils/src/file-configs.ts +++ b/etc/utils/src/file-configs.ts @@ -15,7 +15,8 @@ export const configNames = [ 'genesis.yaml', 'secrets.yaml', 'wallets.yaml', - 'external_node.yaml' + 'external_node.yaml', + 'gateway_chain.yaml' ] as const; export type ConfigName = (typeof configNames)[number]; diff --git a/etc/utils/src/index.ts b/etc/utils/src/index.ts index 6246a209c845..9b541cee8906 100644 --- a/etc/utils/src/index.ts +++ b/etc/utils/src/index.ts @@ -3,6 +3,7 @@ import { promisify } from 'util'; import fs from 'fs'; import readline from 'readline'; import chalk from 'chalk'; +export * from './node-spawner'; export type { ChildProcess } from 'child_process'; diff --git a/core/tests/ts-integration/src/utils.ts b/etc/utils/src/node-spawner.ts similarity index 95% rename from core/tests/ts-integration/src/utils.ts rename to etc/utils/src/node-spawner.ts index 65a6b7566b9a..cdb5f1b80fca 100644 --- a/core/tests/ts-integration/src/utils.ts +++ b/etc/utils/src/node-spawner.ts @@ -1,12 +1,11 @@ +import { killPidWithAllChilds } from './kill'; import { spawn as _spawn, ChildProcessWithoutNullStreams, type ProcessEnvOptions } from 'child_process'; -import { assert } from 'chai'; -import { FileConfig, getConfigPath } from 'utils/build/file-configs'; -import { killPidWithAllChilds } from 'utils/build/kill'; -import * as utils from 'utils'; +import { exec, sleep } from './index'; import fs from 'node:fs/promises'; import * as zksync from 'zksync-ethers'; import * as fsSync from 'fs'; import YAML from 'yaml'; +import { FileConfig, getConfigPath } from './file-configs'; // executes a command in background and returns a child process handle // by default pipes data to parent's stdio but this can be overridden @@ -23,8 +22,6 @@ export function runServerInBackground({ cwd?: ProcessEnvOptions['cwd']; env?: ProcessEnvOptions['env']; useZkStack?: boolean; - newL1GasPrice?: string; - newPubdataPrice?: string; chain?: string; }): ChildProcessWithoutNullStreams { let command = ''; @@ -74,7 +71,7 @@ export class Node { */ public static async killAll(type: NodeType) { try { - await utils.exec(`killall -KILL ${type}`); + await exec(`killall -KILL ${type}`); } catch (err) { console.log(`ignored error: ${err}`); } @@ -88,7 +85,7 @@ export class Node { try { let provider = new zksync.Provider(this.l2NodeUrl); await provider.getBlockNumber(); - await utils.sleep(2); + await sleep(2); iter += 1; } catch (_) { // When exception happens, we assume that server died. @@ -157,7 +154,7 @@ export class NodeSpawner { if (overrides != null) { if (overrides.newL1GasPrice) { - config['eth']['gas_adjuster']['internal_enforced_sl_gas_price'] = overrides.newL1GasPrice; + config['eth']['gas_adjuster']['internal_enforced_l1_gas_price'] = overrides.newL1GasPrice; } if (overrides.newPubdataPrice) { @@ -251,7 +248,7 @@ export class NodeSpawner { } } - let components = 'api,tree,eth,state_keeper,da_dispatcher,vm_runner_protective_reads'; + let components = 'api,tree,eth,state_keeper,da_dispatcher,commitment_generator,vm_runner_protective_reads'; if (options.enableConsensus) { components += ',consensus'; } @@ -304,10 +301,10 @@ async function waitForNodeToStart(proc: ChildProcessWithoutNullStreams, l2Url: s } } catch (err) { if (proc.exitCode != null) { - assert.fail(`server failed to start, exitCode = ${proc.exitCode}`); + throw new Error(`server failed to start, exitCode = ${proc.exitCode}`); } console.log(`Node waiting for API on ${l2Url}`); - await utils.sleep(1); + await sleep(1); } } } diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs index 5335398f7817..d459a29eee4f 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs @@ -60,26 +60,28 @@ impl DeployGatewayCTMInput { chain_config: &ChainConfig, ecosystem_config: &EcosystemConfig, genesis_config: &GenesisConfig, - contracts_config: &ContractsConfig, + ecosystem_contracts_config: &ContractsConfig, initial_deployment_config: &InitialDeploymentConfig, ) -> Self { Self { - bridgehub_proxy_addr: contracts_config.ecosystem_contracts.bridgehub_proxy_addr, - ctm_deployment_tracker_proxy_addr: contracts_config + bridgehub_proxy_addr: ecosystem_contracts_config + .ecosystem_contracts + .bridgehub_proxy_addr, + ctm_deployment_tracker_proxy_addr: ecosystem_contracts_config .ecosystem_contracts .stm_deployment_tracker_proxy_addr .expect("stm_deployment_tracker_proxy_addr"), - native_token_vault_addr: contracts_config + native_token_vault_addr: ecosystem_contracts_config .ecosystem_contracts .native_token_vault_addr .expect("native_token_vault_addr"), - chain_type_manager_proxy_addr: contracts_config + chain_type_manager_proxy_addr: ecosystem_contracts_config .ecosystem_contracts .state_transition_proxy_addr, - shared_bridge_proxy_addr: contracts_config.bridges.shared.l1_address, - governance: contracts_config.l1.governance_addr, + shared_bridge_proxy_addr: ecosystem_contracts_config.bridges.shared.l1_address, + governance: ecosystem_contracts_config.l1.governance_addr, - base_token: contracts_config.l1.base_token_addr, + base_token: chain_config.base_token.address, chain_chain_id: U256::from(chain_config.chain_id.0), era_chain_id: U256::from(ecosystem_config.era_chain_id.0), @@ -117,12 +119,12 @@ impl DeployGatewayCTMInput { latest_protocol_version: genesis_config.protocol_version.unwrap().pack(), - expected_rollup_l2_da_validator: contracts_config + expected_rollup_l2_da_validator: ecosystem_contracts_config .ecosystem_contracts .expected_rollup_l2_da_validator .unwrap(), - force_deployments_data: contracts_config + force_deployments_data: ecosystem_contracts_config .ecosystem_contracts .force_deployments_data .clone() diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs index 7160a0af4c8c..c201625be28b 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs @@ -6,6 +6,7 @@ use crate::traits::ZkStackConfig; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GatewayPreparationOutput { pub governance_l2_tx_hash: H256, + pub l2_chain_admin_address: Address, pub gateway_transaction_filterer_implementation: Address, pub gateway_transaction_filterer_proxy: Address, } diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs index e3637d3b834d..76b615a42b2d 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs @@ -33,6 +33,7 @@ lazy_static! { "function governanceWhitelistGatewayCTM(address gatewaySTMAddress, bytes32 governanoceOperationSalt) public", "function governanceSetCTMAssetHandler(bytes32 governanoceOperationSalt)", "function registerAssetIdInBridgehub(address gatewaySTMAddress, bytes32 governanoceOperationSalt)", + "function grantWhitelist(address filtererProxy, address[] memory addr) public" ]) .unwrap(), ); @@ -111,6 +112,32 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { // We could've deployed the CTM at the beginning however, to be closer to how the actual upgrade // looks like we'll do it as the last step + call_script( + shell, + args.clone(), + &GATEWAY_PREPARATION_INTERFACE + .encode( + "grantWhitelist", + ( + output.gateway_transaction_filterer_proxy, + vec![ + ecosystem_config.get_contracts_config()?.l1.governance_addr, + ecosystem_config + .get_wallets()? + .deployer + .context("no deployer addr")? + .address, + ], + ), + ) + .unwrap(), + &ecosystem_config, + &chain_config, + &chain_config.get_wallets_config()?.governor, + l1_url.clone(), + ) + .await?; + deploy_gateway_ctm( shell, args, @@ -136,7 +163,7 @@ async fn calculate_gateway_ctm( initial_deployemnt_config: &InitialDeploymentConfig, l1_rpc_url: String, ) -> anyhow::Result { - let contracts_config = chain_config.get_contracts_config()?; + let contracts_config = config.get_contracts_config()?; let deploy_config_path = DEPLOY_GATEWAY_CTM.input(&config.link_to_code); let deploy_config = DeployGatewayCTMInput::new( @@ -149,7 +176,7 @@ async fn calculate_gateway_ctm( deploy_config.save(shell, deploy_config_path)?; let calldata = DEPLOY_GATEWAY_CTM_INTERFACE - .encode("deployCTM", ()) + .encode("prepareAddresses", ()) .unwrap(); let mut forge = Forge::new(&config.path_to_l1_foundry()) @@ -183,7 +210,7 @@ async fn deploy_gateway_ctm( initial_deployemnt_config: &InitialDeploymentConfig, l1_rpc_url: String, ) -> anyhow::Result<()> { - let contracts_config = chain_config.get_contracts_config()?; + let contracts_config = config.get_contracts_config()?; let deploy_config_path = DEPLOY_GATEWAY_CTM.input(&config.link_to_code); let deploy_config = DeployGatewayCTMInput::new( @@ -196,7 +223,7 @@ async fn deploy_gateway_ctm( deploy_config.save(shell, deploy_config_path)?; let calldata = DEPLOY_GATEWAY_CTM_INTERFACE - .encode("prepareAddresses", ()) + .encode("deployCTM", ()) .unwrap(); let mut forge = Forge::new(&config.path_to_l1_foundry()) diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs index e56829babc42..a2c0a72010fe 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs @@ -51,7 +51,7 @@ pub struct MigrateFromGatewayArgs { lazy_static! { static ref GATEWAY_PREPARATION_INTERFACE: BaseContract = BaseContract::from( parse_abi(&[ - "function startMigrateChainFromGateway(address chainAdmin,address accessControlRestriction,uint256 chainId) public", + "function startMigrateChainFromGateway(address chainAdmin,address accessControlRestriction,address l2ChainAdmin,uint256 chainId) public", "function finishMigrateChainFromGateway(uint256 migratingChainId,uint256 gatewayChainId,uint256 l2BatchNumber,uint256 l2MessageIndex,uint16 l2TxNumberInBatch,bytes memory message,bytes32[] memory merkleProof) public", ]) .unwrap(), @@ -114,6 +114,9 @@ pub async fn run(args: MigrateFromGatewayArgs, shell: &Shell) -> anyhow::Result< ( chain_admin_addr, chain_access_control_restriction.context("chain_access_control_restriction")?, + gateway_chain_chain_config + .chain_admin_addr + .context("l2 chain admin missing")?, U256::from(chain_config.chain_id.0), ), ) diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs index 282e658759d8..61c01dd3613e 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs @@ -50,11 +50,12 @@ pub struct MigrateToGatewayArgs { lazy_static! { static ref GATEWAY_PREPARATION_INTERFACE: BaseContract = BaseContract::from( parse_abi(&[ - "function migrateChainToGateway(address chainAdmin,address accessControlRestriction,uint256 chainId) public", - "function setDAValidatorPair(address chainAdmin,address accessControlRestriction,uint256 chainId,address l1DAValidator,address l2DAValidator,address chainDiamondProxyOnGateway)", + "function migrateChainToGateway(address chainAdmin,address l2ChainAdmin,address accessControlRestriction,uint256 chainId) public", + "function setDAValidatorPair(address chainAdmin,address accessControlRestriction,uint256 chainId,address l1DAValidator,address l2DAValidator,address chainDiamondProxyOnGateway,address chainAdminOnGateway)", "function supplyGatewayWallet(address addr, uint256 addr) public", - "function enableValidator(address chainAdmin,address accessControlRestriction,uint256 chainId,address validatorAddress,address gatewayValidatorTimelock) public", - "function grantWhitelist(address filtererProxy, address[] memory addr) public" + "function enableValidator(address chainAdmin,address accessControlRestriction,uint256 chainId,address validatorAddress,address gatewayValidatorTimelock,address chainAdminOnGateway) public", + "function grantWhitelist(address filtererProxy, address[] memory addr) public", + "function deployL2ChainAdmin() public" ]) .unwrap(), ); @@ -137,6 +138,23 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() println!("Migrating the chain to the Gateway..."); + let l2_chain_admin = call_script( + shell, + args.forge_args.clone(), + &GATEWAY_PREPARATION_INTERFACE + .encode("deployL2ChainAdmin", ()) + .unwrap(), + &ecosystem_config, + &chain_config.get_wallets_config()?.governor, + l1_url.clone(), + ) + .await? + .l2_chain_admin_address; + println!( + "L2 chain admin deployed! Its address: {:#?}", + l2_chain_admin + ); + let hash = call_script( shell, args.forge_args.clone(), @@ -145,6 +163,8 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() "migrateChainToGateway", ( chain_admin_addr, + // TODO(EVM-746): Use L2-based chain admin contract + l2_chain_admin, chain_access_control_restriction, U256::from(chain_config.chain_id.0), ), @@ -154,7 +174,8 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() &chain_config.get_wallets_config()?.governor, l1_url.clone(), ) - .await?; + .await? + .governance_l2_tx_hash; let gateway_provider = Provider::::try_from( gateway_chain_config @@ -221,6 +242,7 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() .da_validator_addr .context("da_validator_addr")?, new_diamond_proxy_address, + l2_chain_admin, ), ) .unwrap(), @@ -228,7 +250,8 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() &chain_config.get_wallets_config()?.governor, l1_url.clone(), ) - .await?; + .await? + .governance_l2_tx_hash; println!( "DA validator pair set! Hash: {}", hex::encode(hash.as_bytes()) @@ -249,6 +272,7 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() U256::from(chain_config.chain_id.0), chain_secrets_config.blob_operator.address, gateway_gateway_config.validator_timelock_addr, + l2_chain_admin, ), ) .unwrap(), @@ -256,7 +280,8 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() &chain_config.get_wallets_config()?.governor, l1_url.clone(), ) - .await?; + .await? + .governance_l2_tx_hash; println!( "blob_operator enabled! Hash: {}", hex::encode(hash.as_bytes()) @@ -278,7 +303,8 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() &chain_config.get_wallets_config()?.governor, l1_url.clone(), ) - .await?; + .await? + .governance_l2_tx_hash; println!( "blob_operator supplied with 10 ETH! Hash: {}", hex::encode(hash.as_bytes()) @@ -296,6 +322,7 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() U256::from(chain_config.chain_id.0), chain_secrets_config.operator.address, gateway_gateway_config.validator_timelock_addr, + l2_chain_admin, ), ) .unwrap(), @@ -303,7 +330,8 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() &chain_config.get_wallets_config()?.governor, l1_url.clone(), ) - .await?; + .await? + .governance_l2_tx_hash; println!("operator enabled! Hash: {}", hex::encode(hash.as_bytes())); let hash = call_script( @@ -322,7 +350,8 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() &chain_config.get_wallets_config()?.governor, l1_url.clone(), ) - .await?; + .await? + .governance_l2_tx_hash; println!( "operator supplied with 10 ETH! Hash: {}", hex::encode(hash.as_bytes()) @@ -345,8 +374,7 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() let gateway_chain_config = GatewayChainConfig::from_gateway_and_chain_data( &gateway_gateway_config, new_diamond_proxy_address, - // TODO: for now we do not use a noraml chain admin - Address::zero(), + l2_chain_admin, gateway_chain_id, ); gateway_chain_config.save_with_base_path(shell, chain_config.configs.clone())?; @@ -431,7 +459,7 @@ async fn call_script( config: &EcosystemConfig, governor: &Wallet, l1_rpc_url: String, -) -> anyhow::Result { +) -> anyhow::Result { let mut forge = Forge::new(&config.path_to_l1_foundry()) .script(&GATEWAY_PREPARATION.script(), forge_args.clone()) .with_ffi() @@ -447,5 +475,5 @@ async fn call_script( let gateway_preparation_script_output = GatewayPreparationOutput::read(shell, GATEWAY_PREPARATION.output(&config.link_to_code))?; - Ok(gateway_preparation_script_output.governance_l2_tx_hash) + Ok(gateway_preparation_script_output) }