From 2f73bda3c995147305ba619fbee471a23402d825 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 13 Nov 2024 22:57:17 +0100 Subject: [PATCH 01/13] use l2 chain admin --- .github/workflows/ci-core-reusable.yml | 12 +-- contracts | 2 +- core/lib/config/src/configs/gateway.rs | 6 +- core/tests/upgrade-test/tests/upgrade.test.ts | 99 ++++++++++++++----- .../gateway_preparation/output.rs | 1 + .../src/commands/chain/migrate_to_gateway.rs | 82 ++++++++++++--- 6 files changed, 155 insertions(+), 47 deletions(-) diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 16a6e7662dc9..b00aa965bb03 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -486,12 +486,12 @@ 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 zkstack dev test upgrade --no-deps --chain era - name: Upload logs diff --git a/contracts b/contracts index 563dfef011c5..c3467e2e0413 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 563dfef011c52a46a45944f76b4568a088745fce +Subproject commit c3467e2e041358cde84acdaa1f51a2b7b0748837 diff --git a/core/lib/config/src/configs/gateway.rs b/core/lib/config/src/configs/gateway.rs index cc0cdcc1d6a2..e139535b8d3f 100644 --- a/core/lib/config/src/configs/gateway.rs +++ b/core/lib/config/src/configs/gateway.rs @@ -37,7 +37,7 @@ 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 @@ -48,8 +48,8 @@ impl GatewayChainConfig { 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/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 4065480b121b..9d11d086bf11 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -18,6 +18,7 @@ import { setEthSenderSenderAggregatedBlockCommitDeadline } from './utils'; import path from 'path'; +import internal from 'stream'; const pathToHome = path.join(__dirname, '../../../..'); const fileConfig = shouldLoadConfigFromFile(); @@ -45,6 +46,9 @@ describe('Upgrade test', function () { let governanceContract: ethers.Contract; let chainAdminContract: ethers.Contract; let bootloaderHash: string; + let defaultAccountHash: string; + let bootloaderCode: string; + let bytecodeSupplier: string; let executeOperation: string; let forceDeployAddress: string; let forceDeployBytecode: string; @@ -84,6 +88,7 @@ describe('Upgrade test', function () { web3JsonRpc = generalConfig.api.web3_json_rpc.http_url; contractsL2DefaultUpgradeAddr = contractsConfig.l2.default_l2_upgrader; upgradeAddress = contractsConfig.l1.default_upgrade_addr; + bytecodeSupplier = contractsConfig.ecosystem_contracts.l1_bytecodes_supplier_addr; contractsPriorityTxMaxGasLimit = '72000000'; } else { ethProviderAddress = process.env.L1_RPC_ADDRESS || process.env.ETH_CLIENT_WEB3_URL; @@ -94,6 +99,10 @@ describe('Upgrade test', function () { if (!upgradeAddress) { throw new Error('CONTRACTS_DEFAULT_UPGRADE_ADDR not set'); } + bytecodeSupplier = process.env.CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR as string; + if (!bytecodeSupplier) { + throw new Error('CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR not set'); + } contractsPriorityTxMaxGasLimit = process.env.CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT!; } @@ -236,27 +245,34 @@ describe('Upgrade test', function () { }); 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)); - } + 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 () => { @@ -296,11 +312,10 @@ 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 } @@ -422,6 +437,46 @@ describe('Upgrade test', function () { } }); +function readCode( + newPath: string, + legacyPath: string, +): string { + let path = `${pathToHome}/${newPath}`; + if (fs.existsSync(path)) { + return '0x'.concat(require(path).bytecode.object); + } else { + path = `${pathToHome}/${legacyPath}`; + if (path.endsWith('.zbin')) { + return ethers.hexlify(fs.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 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(); @@ -503,7 +558,6 @@ async function prepareUpgradeCalldata( paymasterInput: BytesLike; reservedDynamic: BytesLike; }; - factoryDeps: BytesLike[]; bootloaderHash?: BytesLike; defaultAAHash?: BytesLike; verifier?: string; @@ -528,7 +582,6 @@ async function prepareUpgradeCalldata( const upgradeInitData = contracts.l1DefaultUpgradeAbi.encodeFunctionData('upgrade', [ [ params.l2ProtocolUpgradeTx, - params.factoryDeps, params.bootloaderHash ?? ethers.ZeroHash, params.defaultAAHash ?? ethers.ZeroHash, params.verifier ?? ethers.ZeroAddress, 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/migrate_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs index 5de821770a5c..ccddaaaa2443 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,24 @@ 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 +164,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 +175,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 +243,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 +251,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 +273,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 +281,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 +304,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 +323,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 +331,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 +351,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 +375,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())?; @@ -432,7 +461,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() @@ -448,5 +477,30 @@ 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) +} + +/// Applies the L1 to L2 address aliasing by adding a constant offset. +/// +/// # Arguments +/// +/// * `l1_address` - The Layer 1 Ethereum address to be aliased. +/// +/// # Returns +/// +/// * `Address` - The aliased Layer 2 Ethereum address. +fn apply_l1_to_l2_alias(l1_address: Address) -> Address { + // Define the constant offset as a U256 number. + let offset = U256::from_str_radix("1111000000000000000000000000000000001111", 16).unwrap(); + + // Convert the l1_address (which is 20 bytes) into a U256 number. + let l1_address_u256 = U256::from_big_endian(&l1_address.0); + + // Perform the addition with wrapping to mimic Solidity's unchecked arithmetic. + let l2_address_u256 = l1_address_u256 + offset; + + // Convert the resulting U256 back into a 20-byte address. + let mut l2_address_bytes = [0u8; 32]; + l2_address_u256.to_big_endian(&mut l2_address_bytes); + Address::from_slice(&l2_address_bytes[12..32]) // Take the last 20 bytes. } From f9defa9298965e7bc3a8bc3af2af6626185baaea Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 14 Nov 2024 16:52:10 +0100 Subject: [PATCH 02/13] make upgrade test work for gateway chain --- core/tests/upgrade-test/tests/upgrade.test.ts | 429 +++++++++++++----- etc/utils/src/file-configs.ts | 3 +- etc/utils/src/index.ts | 1 + etc/utils/src/node-spawner.ts | 310 +++++++++++++ .../deploy_gateway_ctm/input.rs | 20 +- .../src/commands/chain/convert_to_gateway.rs | 26 +- 6 files changed, 668 insertions(+), 121 deletions(-) create mode 100644 etc/utils/src/node-spawner.ts diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 9d11d086bf11..1ed79e771cea 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -3,9 +3,9 @@ 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 { @@ -19,12 +19,17 @@ import { } from './utils'; import path from 'path'; import internal from 'stream'; +import { ZKSYNC_MAIN_ABI } from 'zksync-ethers/build/utils'; const pathToHome = path.join(__dirname, '../../../..'); const fileConfig = shouldLoadConfigFromFile(); const contracts: Contracts = initContracts(pathToHome, fileConfig.loadFromFile); +const ZK_CHAIN_INTERFACE = JSON.parse(readFileSync( + pathToHome + '/contracts/l1-contracts/out/IZKChain.sol/IZKChain.json' +).toString()).abi; + let serverComponents = [ 'api', 'tree', @@ -37,22 +42,36 @@ let serverComponents = [ 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 () { let tester: Tester; let alice: zksync.Wallet; let ecosystemGovWallet: ethers.Wallet; - let adminGovWallet: ethers.Wallet; - let mainContract: IZkSyncHyperchain; + let slAdminGovWallet: ethers.Wallet; + let mainContract: ethers.Contract; let governanceContract: ethers.Contract; - let chainAdminContract: ethers.Contract; + let slChainAdminContract: ethers.Contract; + let slMainContract: ethers.Contract; let bootloaderHash: string; let defaultAccountHash: string; - let bootloaderCode: 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; @@ -62,10 +81,17 @@ describe('Upgrade test', function () { let upgradeAddress: string | undefined; let contractsPriorityTxMaxGasLimit: string; + let isGateway: boolean; + 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({ @@ -83,22 +109,59 @@ describe('Upgrade test', function () { 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; - upgradeAddress = contractsConfig.l1.default_upgrade_addr; 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) { + isGateway = true; + + const gatewayChainConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + config: 'gateway_chain.yaml' + }); + + 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 + }); } else { + // Since gateway config can not be imported + // FIXME: potentially delete the non-file-based tests is enough + throw new Error('Non file based not supported'); + 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'); - } + // upgradeAddress = process.env.CONTRACTS_DEFAULT_UPGRADE_ADDR; + // if (!upgradeAddress) { + // throw new Error('CONTRACTS_DEFAULT_UPGRADE_ADDR not set'); + // } bytecodeSupplier = process.env.CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR as string; if (!bytecodeSupplier) { throw new Error('CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR not set'); @@ -116,7 +179,9 @@ describe('Upgrade test', function () { 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, @@ -125,31 +190,24 @@ describe('Upgrade test', function () { 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()); - } + + ecosystemGovWallet = new ethers.Wallet(ecosystemWalletConfig.governor.private_key, alice._providerL1()); } 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; + throw new Error('Not loading from file not supported'); + // 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; } - logs = fs.createWriteStream('upgrade.log', { flags: 'a' }); + upgradeAddress = await deployDefaultUpgradeImpl(slAdminGovWallet); + forceDeployBytecode = contracts.counterBytecode; }); 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 (_) {} - + console.log('hi!'); // Set small timeouts. process.env.ETH_SENDER_SENDER_AGGREGATED_BLOCK_COMMIT_DEADLINE = '1'; process.env.ETH_SENDER_SENDER_AGGREGATED_BLOCK_PROVE_DEADLINE = '1'; @@ -157,6 +215,8 @@ describe('Upgrade test', function () { // Must be > 1s, because bootloader requires l1 batch timestamps to be incremental. process.env.CHAIN_STATE_KEEPER_BLOCK_COMMIT_DEADLINE_MS = '2000'; + console.log('hi2!'); + if (fileConfig.loadFromFile) { setEthSenderSenderAggregatedBlockCommitDeadline(pathToHome, fileConfig, 1); setAggregatedBlockProveDeadline(pathToHome, fileConfig, 1); @@ -164,34 +224,32 @@ describe('Upgrade test', function () { setBlockCommitDeadlineMs(pathToHome, fileConfig, 2000); } - // 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'); - } + console.log('hi4!'); + await mainNodeSpawner.killAndSpawnMainNode(); - const stmAddr = await mainContract.getStateTransitionManager(); + console.log('hi5!'); + mainContract = new ethers.Contract( + await tester.web3Provider.getMainContractAddress(), + ZK_CHAIN_INTERFACE, + tester.ethProvider + ); + + console.log('hi3!'); + + const stmAddr = await mainContract.getChainTypeManager(); 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); + + slChainAdminContract = gatewayInfo ? + new ethers.Contract(gatewayInfo.l2ChainAdmin, contracts.chainAdminAbi, gatewayInfo.gatewayProvider) : + new ethers.Contract(chainAdminAddr, contracts.chainAdminAbi, tester.syncWallet.providerL1); + + slMainContract = gatewayInfo ? + new ethers.Contract(gatewayInfo.l2DiamondProxyAddress, ZKSYNC_MAIN_ABI, gatewayInfo.gatewayProvider) : + mainContract; + let blocksCommitted = await mainContract.getTotalBatchesCommitted(); const initialL1BatchNumber = await tester.web3Provider.getL1BatchNumber(); @@ -244,7 +302,7 @@ describe('Upgrade test', function () { } }); - step('Send l1 tx for saving new bootloader', async () => { + 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' @@ -276,8 +334,6 @@ describe('Upgrade test', function () { }); step('Schedule governance call', async () => { - forceDeployBytecode = contracts.counterBytecode; - const forceDeployment: ForceDeployment = { bytecodeHash: ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode)), newAddress: forceDeployAddress, @@ -294,8 +350,9 @@ describe('Upgrade test', function () { delegateCalldata ]); + console.log('hey'); const { stmUpgradeData, chainUpgradeCalldata, setTimestampCalldata } = await prepareUpgradeCalldata( - adminGovWallet, + alice._providerL1(), alice._providerL2(), upgradeAddress!, { @@ -318,16 +375,27 @@ describe('Upgrade test', function () { }, bootloaderHash, upgradeTimestamp: 0 - } + }, + isGateway ? gatewayInfo : null ); executeOperation = chainUpgradeCalldata; + console.log('hey2'); console.log('Sending scheduleTransparentOperation'); - await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation); + await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation, 0, gatewayInfo); + console.log('hey3'); + console.log('Sending executeOperation'); - await sendGovernanceOperation(stmUpgradeData.executeOperation); + await sendGovernanceOperation(stmUpgradeData.executeOperation, stmUpgradeData.executeOperationValue, gatewayInfo); + console.log('hey4'); + + 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); @@ -349,25 +417,27 @@ 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(); + console.log(lastBatchExecuted); tryCount += 1; await utils.sleep(2); } if (lastBatchExecuted < l1BatchNumber) { + console.log(l1BatchNumber); throw new Error('Server did not execute old blocks'); } // Execute the upgrade - const executeMulticallData = chainAdminContract.interface.encodeFunctionData('multicall', [ - [[await mainContract.getAddress(), 0, executeOperation]], - true - ]); - await sendChainAdminOperation(executeMulticallData); + await sendChainAdminOperation({ + target: gatewayInfo ? gatewayInfo.l2DiamondProxyAddress : await mainContract.getAddress(), + data: executeOperation, + value: 0 + }); - let bootloaderHashL1 = await mainContract.getL2BootloaderBytecodeHash(); + let bootloaderHashL1 = await slMainContract.getL2BootloaderBytecodeHash(); expect(bootloaderHashL1).eq(bootloaderHash); }); @@ -384,18 +454,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); @@ -414,26 +473,75 @@ describe('Upgrade test', function () { } catch (_) {} }); - async function sendGovernanceOperation(data: string) { + async function sendGovernanceOperation( + data: string, + value: BigNumberish, + gatewayInfo: GatewayInfo | null + ) { const transaction = await ecosystemGovWallet.sendTransaction({ to: await governanceContract.getAddress(), + 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(!gatewayInfo) { + return; + } + + // `try/catch` is needed since SDK will throw an error if the priority operation is not found. + // So it is not possible to disntiguish the case when it was not emitted on purpose or not + let hash; + try { + const contract = await gatewayInfo.gatewayProvider.getMainContractAddress(); + hash = zksync.utils.getL2HashFromPriorityOp(receipt!, contract); + console.log(`Gateway L1->L2 transaction with hash ${hash} detected`); + } catch { + return; + } + + await gatewayInfo.gatewayProvider.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}`); + + // return receipt; + } + + async function deployDefaultUpgradeImpl(runner: ethers.Wallet): Promise { + const bytecodePath = isGateway ? + 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(isGateway) { + 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(); + } } }); @@ -442,12 +550,12 @@ function readCode( legacyPath: string, ): string { let path = `${pathToHome}/${newPath}`; - if (fs.existsSync(path)) { + if (existsSync(path)) { return '0x'.concat(require(path).bytecode.object); } else { path = `${pathToHome}/${legacyPath}`; if (path.endsWith('.zbin')) { - return ethers.hexlify(fs.readFileSync(path)); + return ethers.hexlify(readFileSync(path)); } else { return require(path).bytecode; } @@ -462,10 +570,9 @@ async function publishBytecode( const hash = zksync.utils.hashBytecode(bytecode); const abi = [ 'function publishBytecode(bytes calldata _bytecode) public', - 'function publishingBlock(bytes32 _hash) public returns (uint256)' + 'function publishingBlock(bytes32 _hash) public view returns (uint256)' ]; - const contract = new ethers.Contract( bytecodeSupplierAddr, abi, @@ -536,7 +643,7 @@ async function waitForNewL1Batch(wallet: zksync.Wallet): Promise { + let call; + console.log('t1'); + 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 + }; + } + + console.log('t2'); 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` @@ -657,10 +816,64 @@ function prepareGovernanceCalldata(to: string, data: BytesLike): UpgradeCalldata return { scheduleTransparentOperation, - executeOperation + executeOperation, + executeOperationValue: call.value }; } +async function prepareAdminCalldata() { + +} + +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 + } +} + async function mintToAddress( baseTokenAddress: zksync.types.Address, ethersWallet: ethers.Wallet, 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/etc/utils/src/node-spawner.ts b/etc/utils/src/node-spawner.ts new file mode 100644 index 000000000000..1eaf8409f525 --- /dev/null +++ b/etc/utils/src/node-spawner.ts @@ -0,0 +1,310 @@ +import { killPidWithAllChilds } from "./kill"; +import { spawn as _spawn, ChildProcessWithoutNullStreams, type ProcessEnvOptions } from 'child_process'; +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 +export function runServerInBackground({ + components, + stdio, + cwd, + env, + useZkStack, + chain +}: { + components?: string[]; + stdio: any; + cwd?: ProcessEnvOptions['cwd']; + env?: ProcessEnvOptions['env']; + useZkStack?: boolean; + chain?: string; +}): ChildProcessWithoutNullStreams { + let command = ''; + if (useZkStack) { + command = 'zkstack server'; + if (chain) { + command += ` --chain ${chain}`; + } + } else { + command = 'zk server'; + } + if (components && components.length > 0) { + command += ` --components=${components.join(',')}`; + } + command = command.replace(/\n/g, ' '); + console.log(`Run command ${command}`); + return _spawn(command, { stdio: stdio, shell: true, detached: true, cwd, env }); +} + +export interface MainNodeSpawnOptions { + enableConsensus: boolean; + ethClientWeb3Url: string; + apiWeb3JsonRpcHttpUrl: string; + baseTokenAddress: string; +} + +export enum NodeType { + MAIN = 'zksync_server', + EXT = 'zksync_external_node' +} + +export class Node { + constructor(public proc: ChildProcessWithoutNullStreams, public l2NodeUrl: string, private readonly type: TYPE) {} + + public async terminate() { + try { + await killPidWithAllChilds(this.proc.pid!, 9); + } catch (err) { + console.log(`ignored error: ${err}`); + } + } + + /** + * Terminates all main node processes running. + * + * WARNING: This is not safe to use when running nodes on multiple chains. + */ + public static async killAll(type: NodeType) { + try { + await exec(`killall -KILL ${type}`); + } catch (err) { + console.log(`ignored error: ${err}`); + } + } + + public async killAndWaitForShutdown() { + await this.terminate(); + // Wait until it's really stopped. + let iter = 0; + while (iter < 30) { + try { + let provider = new zksync.Provider(this.l2NodeUrl); + await provider.getBlockNumber(); + await sleep(2); + iter += 1; + } catch (_) { + // When exception happens, we assume that server died. + return; + } + } + // It's going to panic anyway, since the server is a singleton entity, so better to exit early. + throw new Error(`${this.type} didn't stop after a kill request`); + } +} + +interface MainNodeOptions { + newL1GasPrice?: bigint; + newPubdataPrice?: bigint; + customBaseToken?: boolean; + externalPriceApiClientForcedNumerator?: number; + externalPriceApiClientForcedDenominator?: number; + externalPriceApiClientForcedFluctuation?: number; + baseTokenPricePollingIntervalMs?: number; + baseTokenAdjusterL1UpdateDeviationPercentage?: number; +} +export class NodeSpawner { + private readonly generalConfigPath: string | undefined; + private readonly originalConfig: string | undefined; + public mainNode: Node | null; + + public constructor( + private readonly pathToHome: string, + private readonly logs: fs.FileHandle, + private readonly fileConfig: FileConfig, + private readonly options: MainNodeSpawnOptions, + private env?: ProcessEnvOptions['env'] + ) { + this.mainNode = null; + if (fileConfig.loadFromFile) { + this.generalConfigPath = getConfigPath({ + pathToHome, + chain: fileConfig.chain, + configsFolder: 'configs', + config: 'general.yaml' + }); + this.originalConfig = fsSync.readFileSync(this.generalConfigPath, 'utf8'); + } + } + + public async killAndSpawnMainNode(configOverrides: MainNodeOptions | null = null): Promise { + if (this.mainNode != null) { + await this.mainNode.killAndWaitForShutdown(); + this.mainNode = null; + } + this.mainNode = await this.spawnMainNode(configOverrides); + } + + private async spawnMainNode(overrides: MainNodeOptions | null): Promise> { + const env = this.env ?? process.env; + const { fileConfig, pathToHome, options, logs } = this; + + const testMode = overrides?.newPubdataPrice != null || overrides?.newL1GasPrice != null; + + console.log('Overrides: ', overrides); + + if (fileConfig.loadFromFile) { + this.restoreConfig(); + const config = this.readFileConfig(); + config['state_keeper']['transaction_slots'] = testMode ? 1 : 8192; + + if (overrides != null) { + if (overrides.newL1GasPrice) { + config['eth']['gas_adjuster']['internal_enforced_l1_gas_price'] = overrides.newL1GasPrice; + } + + if (overrides.newPubdataPrice) { + config['eth']['gas_adjuster']['internal_enforced_pubdata_price'] = overrides.newPubdataPrice; + } + + if (overrides.externalPriceApiClientForcedNumerator !== undefined) { + config['external_price_api_client']['forced_numerator'] = + overrides.externalPriceApiClientForcedNumerator; + } + + if (overrides.externalPriceApiClientForcedDenominator !== undefined) { + config['external_price_api_client']['forced_denominator'] = + overrides.externalPriceApiClientForcedDenominator; + } + + if (overrides.externalPriceApiClientForcedFluctuation !== undefined) { + config['external_price_api_client']['forced_fluctuation'] = + overrides.externalPriceApiClientForcedFluctuation; + } + + if (overrides.baseTokenPricePollingIntervalMs !== undefined) { + const cacheUpdateInterval = overrides.baseTokenPricePollingIntervalMs / 2; + // To reduce price polling interval we also need to reduce base token receipt checking and tx sending sleeps as they are blocking the poller. Also cache update needs to be reduced appropriately. + + config['base_token_adjuster']['l1_receipt_checking_sleep_ms'] = + overrides.baseTokenPricePollingIntervalMs; + config['base_token_adjuster']['l1_tx_sending_sleep_ms'] = overrides.baseTokenPricePollingIntervalMs; + config['base_token_adjuster']['price_polling_interval_ms'] = + overrides.baseTokenPricePollingIntervalMs; + config['base_token_adjuster']['price_cache_update_interval_ms'] = cacheUpdateInterval; + } + + if (overrides.baseTokenAdjusterL1UpdateDeviationPercentage !== undefined) { + config['base_token_adjuster']['l1_update_deviation_percentage'] = + overrides.baseTokenAdjusterL1UpdateDeviationPercentage; + } + } + + this.writeFileConfig(config); + } else { + env['DATABASE_MERKLE_TREE_MODE'] = 'full'; + + if (overrides != null) { + if (overrides.newPubdataPrice) { + env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_PUBDATA_PRICE'] = + overrides.newPubdataPrice.toString(); + } + + if (overrides.newL1GasPrice) { + // We need to ensure that each transaction gets into its own batch for more fair comparison. + env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_L1_GAS_PRICE'] = overrides.newL1GasPrice.toString(); + } + + if (overrides.externalPriceApiClientForcedNumerator !== undefined) { + env['EXTERNAL_PRICE_API_CLIENT_FORCED_NUMERATOR'] = + overrides.externalPriceApiClientForcedNumerator.toString(); + } + + if (overrides.externalPriceApiClientForcedDenominator !== undefined) { + env['EXTERNAL_PRICE_API_CLIENT_FORCED_DENOMINATOR'] = + overrides.externalPriceApiClientForcedDenominator.toString(); + } + + if (overrides.externalPriceApiClientForcedFluctuation !== undefined) { + env['EXTERNAL_PRICE_API_CLIENT_FORCED_FLUCTUATION'] = + overrides.externalPriceApiClientForcedFluctuation.toString(); + } + + if (overrides.baseTokenPricePollingIntervalMs !== undefined) { + const cacheUpdateInterval = overrides.baseTokenPricePollingIntervalMs / 2; + // To reduce price polling interval we also need to reduce base token receipt checking and tx sending sleeps as they are blocking the poller. Also cache update needs to be reduced appropriately. + env['BASE_TOKEN_ADJUSTER_L1_RECEIPT_CHECKING_SLEEP_MS'] = + overrides.baseTokenPricePollingIntervalMs.toString(); + env['BASE_TOKEN_ADJUSTER_L1_TX_SENDING_SLEEP_MS'] = + overrides.baseTokenPricePollingIntervalMs.toString(); + env['BASE_TOKEN_ADJUSTER_PRICE_POLLING_INTERVAL_MS'] = + overrides.baseTokenPricePollingIntervalMs.toString(); + env['BASE_TOKEN_ADJUSTER_PRICE_CACHE_UPDATE_INTERVAL_MS'] = cacheUpdateInterval.toString(); + } + + if (overrides.baseTokenAdjusterL1UpdateDeviationPercentage !== undefined) { + env['BASE_TOKEN_ADJUSTER_L1_UPDATE_DEVIATION_PERCENTAGE'] = + overrides.baseTokenAdjusterL1UpdateDeviationPercentage.toString(); + } + } + + if (testMode) { + // We need to ensure that each transaction gets into its own batch for more fair comparison. + env['CHAIN_STATE_KEEPER_TRANSACTION_SLOTS'] = '1'; + } + } + + let components = 'api,tree,eth,state_keeper,da_dispatcher,commitment_generator,vm_runner_protective_reads'; + if (options.enableConsensus) { + components += ',consensus'; + } + if (options.baseTokenAddress != zksync.utils.LEGACY_ETH_ADDRESS) { + components += ',base_token_ratio_persister'; + } + let proc = runServerInBackground({ + components: [components], + stdio: ['ignore', logs, logs], + cwd: pathToHome, + env: env, + useZkStack: fileConfig.loadFromFile, + chain: fileConfig.chain + }); + + // Wait until the main node starts responding. + await waitForNodeToStart(proc, options.apiWeb3JsonRpcHttpUrl); + return new Node(proc, options.apiWeb3JsonRpcHttpUrl, NodeType.MAIN); + } + + public restoreConfig() { + if (this.generalConfigPath != void 0 && this.originalConfig != void 0) + fsSync.writeFileSync(this.generalConfigPath, this.originalConfig, 'utf8'); + } + + private readFileConfig() { + if (this.generalConfigPath == void 0) + throw new Error('Trying to set property in config while not in file mode'); + const generalConfig = fsSync.readFileSync(this.generalConfigPath, 'utf8'); + return YAML.parse(generalConfig); + } + + private writeFileConfig(config: any) { + if (this.generalConfigPath == void 0) + throw new Error('Trying to set property in config while not in file mode'); + + const newGeneralConfig = YAML.stringify(config); + fsSync.writeFileSync(this.generalConfigPath, newGeneralConfig, 'utf8'); + } +} + +async function waitForNodeToStart(proc: ChildProcessWithoutNullStreams, l2Url: string) { + while (true) { + try { + const l2Provider = new zksync.Provider(l2Url); + const blockNumber = await l2Provider.getBlockNumber(); + if (blockNumber != 0) { + console.log(`Initialized node API on ${l2Url}; latest block: ${blockNumber}`); + break; + } + } catch (err) { + if (proc.exitCode != null) { + throw new Error(`server failed to start, exitCode = ${proc.exitCode}`); + } + console.log(`Node waiting for API on ${l2Url}`); + 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..925846180aef 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,26 @@ 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 +117,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/zkstack/src/commands/chain/convert_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs index e3637d3b834d..94641527724a 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(), ); @@ -110,6 +111,27 @@ 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 + ], + ), + ) + .unwrap(), + &ecosystem_config, + &chain_config, + &chain_config.get_wallets_config()?.governor, + l1_url.clone(), + ) + .await?; deploy_gateway_ctm( shell, @@ -136,7 +158,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( @@ -183,7 +205,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( From 3df82c62c0d4e74d50f93d03ea7ca198ae4f7da9 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 14 Nov 2024 16:53:55 +0100 Subject: [PATCH 03/13] fmt --- contracts | 2 +- core/tests/upgrade-test/tests/upgrade.test.ts | 166 +++++++----------- etc/utils/src/node-spawner.ts | 6 +- .../deploy_gateway_ctm/input.rs | 4 +- .../src/commands/chain/convert_to_gateway.rs | 6 +- .../src/commands/chain/migrate_to_gateway.rs | 17 +- 6 files changed, 80 insertions(+), 121 deletions(-) diff --git a/contracts b/contracts index c3467e2e0413..e9fe166659ce 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit c3467e2e041358cde84acdaa1f51a2b7b0748837 +Subproject commit e9fe166659ce67cdeb785c7eb2b11ec180a615f3 diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 1ed79e771cea..789bfa543fc7 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -26,9 +26,9 @@ const fileConfig = shouldLoadConfigFromFile(); const contracts: Contracts = initContracts(pathToHome, fileConfig.loadFromFile); -const ZK_CHAIN_INTERFACE = JSON.parse(readFileSync( - pathToHome + '/contracts/l1-contracts/out/IZKChain.sol/IZKChain.json' -).toString()).abi; +const ZK_CHAIN_INTERFACE = JSON.parse( + readFileSync(pathToHome + '/contracts/l1-contracts/out/IZKChain.sol/IZKChain.json').toString() +).abi; let serverComponents = [ 'api', @@ -44,14 +44,14 @@ const depositAmount = ethers.parseEther('0.001'); interface GatewayInfo { gatewayChainId: string; - gatewayProvider: zksync.Provider, - gatewayCTM: string, - l2ChainAdmin: string, - l2DiamondProxyAddress: string + gatewayProvider: zksync.Provider; + gatewayCTM: string; + l2ChainAdmin: string; + l2DiamondProxyAddress: string; } interface Call { - target: string, + target: string; value: BigNumberish; data: BytesLike; } @@ -92,7 +92,6 @@ describe('Upgrade test', function () { complexUpgraderAddress = '0x000000000000000000000000000000000000800f'; logs = await fs.open('upgrade.log', 'a'); - if (fileConfig.loadFromFile) { const generalConfig = loadConfig({ pathToHome, @@ -142,13 +141,12 @@ describe('Upgrade test', function () { }; } - mainNodeSpawner = new utils.NodeSpawner(pathToHome, logs, fileConfig, { enableConsensus: false, ethClientWeb3Url: ethProviderAddress!, apiWeb3JsonRpcHttpUrl: web3JsonRpc!, baseTokenAddress: contractsConfig.l1.base_token_addr - }); + }); } else { // Since gateway config can not be imported // FIXME: potentially delete the non-file-based tests is enough @@ -179,9 +177,9 @@ describe('Upgrade test', function () { config: 'wallets.yaml' }); - slAdminGovWallet = gatewayInfo ? - new zksync.Wallet(chainWalletConfig.governor.private_key, gatewayInfo.gatewayProvider): - 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, @@ -190,7 +188,6 @@ describe('Upgrade test', function () { config: 'wallets.yaml' }); - ecosystemGovWallet = new ethers.Wallet(ecosystemWalletConfig.governor.private_key, alice._providerL1()); } else { throw new Error('Not loading from file not supported'); @@ -241,14 +238,14 @@ describe('Upgrade test', function () { const governanceAddr = await stmContract.owner(); governanceContract = new ethers.Contract(governanceAddr, contracts.governanceAbi, tester.syncWallet.providerL1); const chainAdminAddr = await mainContract.getAdmin(); - - slChainAdminContract = gatewayInfo ? - new ethers.Contract(gatewayInfo.l2ChainAdmin, contracts.chainAdminAbi, gatewayInfo.gatewayProvider) : - new ethers.Contract(chainAdminAddr, contracts.chainAdminAbi, tester.syncWallet.providerL1); - slMainContract = gatewayInfo ? - new ethers.Contract(gatewayInfo.l2DiamondProxyAddress, ZKSYNC_MAIN_ABI, gatewayInfo.gatewayProvider) : - mainContract; + slChainAdminContract = gatewayInfo + ? new ethers.Contract(gatewayInfo.l2ChainAdmin, contracts.chainAdminAbi, gatewayInfo.gatewayProvider) + : new ethers.Contract(chainAdminAddr, contracts.chainAdminAbi, tester.syncWallet.providerL1); + + slMainContract = gatewayInfo + ? new ethers.Contract(gatewayInfo.l2DiamondProxyAddress, ZKSYNC_MAIN_ABI, gatewayInfo.gatewayProvider) + : mainContract; let blocksCommitted = await mainContract.getTotalBatchesCommitted(); @@ -316,21 +313,9 @@ describe('Upgrade test', function () { bootloaderHash = ethers.hexlify(zksync.utils.hashBytecode(bootloaderCode)); 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 - ); + await publishBytecode(tester.ethWallet, bytecodeSupplier, bootloaderCode); + await publishBytecode(tester.ethWallet, bytecodeSupplier, defaultAACode); + await publishBytecode(tester.ethWallet, bytecodeSupplier, forceDeployBytecode); }); step('Schedule governance call', async () => { @@ -369,7 +354,11 @@ describe('Upgrade test', function () { reserved: [0, 0, 0, 0], data, signature: '0x', - factoryDeps: [bootloaderHash, defaultAccountHash, ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode))], + factoryDeps: [ + bootloaderHash, + defaultAccountHash, + ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode)) + ], paymasterInput: '0x', reservedDynamic: '0x' }, @@ -386,10 +375,13 @@ describe('Upgrade test', function () { console.log('hey3'); console.log('Sending executeOperation'); - await sendGovernanceOperation(stmUpgradeData.executeOperation, stmUpgradeData.executeOperationValue, gatewayInfo); + await sendGovernanceOperation( + stmUpgradeData.executeOperation, + stmUpgradeData.executeOperationValue, + gatewayInfo + ); console.log('hey4'); - console.log('Sending chain admin operation'); await sendChainAdminOperation({ target: await slChainAdminContract.getAddress(), @@ -473,11 +465,7 @@ describe('Upgrade test', function () { } catch (_) {} }); - async function sendGovernanceOperation( - data: string, - value: BigNumberish, - gatewayInfo: GatewayInfo | null - ) { + async function sendGovernanceOperation(data: string, value: BigNumberish, gatewayInfo: GatewayInfo | null) { const transaction = await ecosystemGovWallet.sendTransaction({ to: await governanceContract.getAddress(), value: value, @@ -487,13 +475,13 @@ describe('Upgrade test', function () { console.log(`Sent governance operation, tx_hash=${transaction.hash}, nonce=${transaction.nonce}`); 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(!gatewayInfo) { + if (!gatewayInfo) { return; } - // `try/catch` is needed since SDK will throw an error if the priority operation is not found. + // `try/catch` is needed since SDK will throw an error if the priority operation is not found. // So it is not possible to disntiguish the case when it was not emitted on purpose or not let hash; try { @@ -508,13 +496,8 @@ describe('Upgrade test', function () { console.log('Transaction complete!'); } - async function sendChainAdminOperation( - call: Call, - ) { - const executeMulticallData = slChainAdminContract.interface.encodeFunctionData('multicall', [ - [call], - true - ]); + async function sendChainAdminOperation(call: Call) { + const executeMulticallData = slChainAdminContract.interface.encodeFunctionData('multicall', [[call], true]); const transaction = await slAdminGovWallet.sendTransaction({ to: await slChainAdminContract.getAddress(), @@ -524,18 +507,16 @@ describe('Upgrade test', function () { 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}`); - - // return receipt; } async function deployDefaultUpgradeImpl(runner: ethers.Wallet): Promise { - const bytecodePath = isGateway ? - pathToHome + '/contracts/l1-contracts/zkout/DefaultUpgrade.sol/DefaultUpgrade.json' : - pathToHome + '/contracts/l1-contracts/out/DefaultUpgrade.sol/DefaultUpgrade.json'; + const bytecodePath = isGateway + ? 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(isGateway) { + if (isGateway) { const factory = new zksync.ContractFactory([], bytecode, runner); return (await factory.deploy()).getAddress(); } else { @@ -545,10 +526,7 @@ describe('Upgrade test', function () { } }); -function readCode( - newPath: string, - legacyPath: string, -): string { +function readCode(newPath: string, legacyPath: string): string { let path = `${pathToHome}/${newPath}`; if (existsSync(path)) { return '0x'.concat(require(path).bytecode.object); @@ -562,26 +540,18 @@ function readCode( } } -async function publishBytecode( - wallet: ethers.Wallet, - bytecodeSupplierAddr: string, - bytecode: string -) { +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 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 { @@ -683,7 +653,11 @@ async function prepareUpgradeCalldata( let settlementLayerDiamondProxy: ethers.Contract; if (gatewayInfo) { - settlementLayerDiamondProxy = new ethers.Contract(gatewayInfo.l2DiamondProxyAddress, ZK_CHAIN_INTERFACE, gatewayInfo.gatewayProvider); + settlementLayerDiamondProxy = new ethers.Contract( + gatewayInfo.l2DiamondProxyAddress, + ZK_CHAIN_INTERFACE, + gatewayInfo.gatewayProvider + ); } else { const zksyncAddress = await l2Provider.getMainContractAddress(); settlementLayerDiamondProxy = new ethers.Contract(zksyncAddress, ZK_CHAIN_INTERFACE, l1Provider); @@ -694,7 +668,6 @@ async function prepareUpgradeCalldata( const newProtocolVersion = addToProtocolVersion(oldProtocolVersion, 1, 1); console.log('hey1122'); - params.l2ProtocolUpgradeTx.nonce ??= BigInt(unpackNumberSemVer(newProtocolVersion)[1]); const upgradeInitData = contracts.l1DefaultUpgradeAbi.encodeFunctionData('upgrade', [ [ @@ -743,13 +716,11 @@ async function prepareUpgradeCalldata( ]); console.log('hey10'); - const bridgehubAddr = await l2Provider.getBridgehubContractAddress(); console.log('hey11'); - const stmUpgradeData = await prepareGovernanceCalldata( - settlementLayerCTMAddress, + settlementLayerCTMAddress, stmUpgradeCalldata, bridgehubAddr, l1Provider!, @@ -774,9 +745,9 @@ async function prepareGovernanceCalldata( data: BytesLike, bridgehubAddr: string, l1Provider: ethers.Provider, - gatewayInfo: GatewayInfo | null, + gatewayInfo: GatewayInfo | null ): Promise { - let call; + let call; console.log('t1'); if (gatewayInfo) { // We will have to perform an L1->L2 transaction to the gateway @@ -795,7 +766,7 @@ async function prepareGovernanceCalldata( value: 0, data }; - } + } console.log('t2'); const governanceOperation = { @@ -821,9 +792,7 @@ async function prepareGovernanceCalldata( }; } -async function prepareAdminCalldata() { - -} +async function prepareAdminCalldata() {} async function composeL1ToL2Call( chainId: string, @@ -831,7 +800,7 @@ async function composeL1ToL2Call( data: BytesLike, bridgehubAddr: string, l1Provider: ethers.Provider, - refundRecipient: string, + 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 @@ -839,18 +808,9 @@ async function composeL1ToL2Call( const gasPrice = (await l1Provider.getFeeData()).gasPrice! * BigInt(5); - const bridgehub = new ethers.Contract( - bridgehubAddr, - zksync.utils.BRIDGEHUB_ABI, - l1Provider - ); + const bridgehub = new ethers.Contract(bridgehubAddr, zksync.utils.BRIDGEHUB_ABI, l1Provider); - const baseCost = await bridgehub.l2TransactionBaseCost( - chainId, - gasPrice, - gasLimit, - gasPerPubdata - ) + const baseCost = await bridgehub.l2TransactionBaseCost(chainId, gasPrice, gasLimit, gasPerPubdata); const encodedData = zksync.utils.BRIDGEHUB_ABI.encodeFunctionData('requestL2TransactionDirect', [ { @@ -860,18 +820,18 @@ async function composeL1ToL2Call( l2Value: 0, l2Calldata: data, l2GasLimit: gasLimit, - l2GasPerPubdataByteLimit: gasPerPubdata, + l2GasPerPubdataByteLimit: gasPerPubdata, factoryDeps: [], - refundRecipient + refundRecipient } - ]); + ]); return { target: bridgehubAddr, data: encodedData, // Works when ETH is the base token value: baseCost - } + }; } async function mintToAddress( diff --git a/etc/utils/src/node-spawner.ts b/etc/utils/src/node-spawner.ts index 1eaf8409f525..cdb5f1b80fca 100644 --- a/etc/utils/src/node-spawner.ts +++ b/etc/utils/src/node-spawner.ts @@ -1,11 +1,11 @@ -import { killPidWithAllChilds } from "./kill"; +import { killPidWithAllChilds } from './kill'; import { spawn as _spawn, ChildProcessWithoutNullStreams, type ProcessEnvOptions } from 'child_process'; -import {exec, sleep} from "./index"; +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"; +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 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 925846180aef..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 @@ -64,7 +64,9 @@ impl DeployGatewayCTMInput { initial_deployment_config: &InitialDeploymentConfig, ) -> Self { Self { - bridgehub_proxy_addr: ecosystem_contracts_config.ecosystem_contracts.bridgehub_proxy_addr, + 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 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 94641527724a..59a03d76864e 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 @@ -111,7 +111,7 @@ 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(), @@ -120,9 +120,7 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { "grantWhitelist", ( output.gateway_transaction_filterer_proxy, - vec![ - ecosystem_config.get_contracts_config()?.l1.governance_addr - ], + vec![ecosystem_config.get_contracts_config()?.l1.governance_addr], ), ) .unwrap(), 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 ccddaaaa2443..3e1498d62b69 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 @@ -142,10 +142,7 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() shell, args.forge_args.clone(), &GATEWAY_PREPARATION_INTERFACE - .encode( - "deployL2ChainAdmin", - (), - ) + .encode("deployL2ChainAdmin", ()) .unwrap(), &ecosystem_config, &chain_config.get_wallets_config()?.governor, @@ -153,8 +150,10 @@ pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<() ) .await? .l2_chain_admin_address; - println!("L2 chain admin deployed! Its address: {:#?}", l2_chain_admin); - + println!( + "L2 chain admin deployed! Its address: {:#?}", + l2_chain_admin + ); let hash = call_script( shell, @@ -243,7 +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 + l2_chain_admin, ), ) .unwrap(), @@ -273,7 +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 + l2_chain_admin, ), ) .unwrap(), @@ -323,7 +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 + l2_chain_admin, ), ) .unwrap(), From 8b417d65e891b381cd138cbe068b79fcabd9af4b Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 14 Nov 2024 21:36:13 +0100 Subject: [PATCH 04/13] wip --- core/node/eth_watch/src/tests/client.rs | 3 +- core/tests/upgrade-test/tests/upgrade.test.ts | 46 +++++-------------- .../src/commands/chain/migrate_to_gateway.rs | 25 ---------- 3 files changed, 14 insertions(+), 60 deletions(-) diff --git a/core/node/eth_watch/src/tests/client.rs b/core/node/eth_watch/src/tests/client.rs index d01f3bf502ef..ed2b1fc4c6d7 100644 --- a/core/node/eth_watch/src/tests/client.rs +++ b/core/node/eth_watch/src/tests/client.rs @@ -12,10 +12,11 @@ use zksync_types::{ l1::L1Tx, protocol_upgrade::ProtocolUpgradeTx, tokens::TokenMetadata, + u256_to_h256, web3::{contract::Tokenizable, BlockNumber}, Address, L1BatchNumber, L2ChainId, ProtocolUpgrade, SLChainId, Transaction, H256, U256, U64, }; -use zksync_utils::{bytecode::hash_bytecode, u256_to_h256}; +use zksync_utils::bytecode::hash_bytecode; use crate::client::{EthClient, L2EthClient, RETRY_LIMIT}; diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 789bfa543fc7..336ea72903e5 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -30,16 +30,6 @@ const ZK_CHAIN_INTERFACE = JSON.parse( readFileSync(pathToHome + '/contracts/l1-contracts/out/IZKChain.sol/IZKChain.json').toString() ).abi; -let serverComponents = [ - 'api', - 'tree', - 'eth', - 'state_keeper', - 'commitment_generator', - 'da_dispatcher', - 'vm_runner_protective_reads' -]; - const depositAmount = ethers.parseEther('0.001'); interface GatewayInfo { @@ -152,19 +142,19 @@ describe('Upgrade test', function () { // FIXME: potentially delete the non-file-based tests is enough throw new Error('Non file based not supported'); - 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'); + // 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'); + // // } + // bytecodeSupplier = process.env.CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR as string; + // if (!bytecodeSupplier) { + // throw new Error('CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR not set'); // } - bytecodeSupplier = process.env.CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR as string; - if (!bytecodeSupplier) { - throw new Error('CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR not set'); - } - contractsPriorityTxMaxGasLimit = process.env.CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT!; + // contractsPriorityTxMaxGasLimit = process.env.CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT!; } tester = await Tester.init(ethProviderAddress!, web3JsonRpc!); @@ -204,7 +194,6 @@ describe('Upgrade test', function () { }); step('Run server and execute some transactions', async () => { - console.log('hi!'); // Set small timeouts. process.env.ETH_SENDER_SENDER_AGGREGATED_BLOCK_COMMIT_DEADLINE = '1'; process.env.ETH_SENDER_SENDER_AGGREGATED_BLOCK_PROVE_DEADLINE = '1'; @@ -212,27 +201,20 @@ describe('Upgrade test', function () { // Must be > 1s, because bootloader requires l1 batch timestamps to be incremental. process.env.CHAIN_STATE_KEEPER_BLOCK_COMMIT_DEADLINE_MS = '2000'; - console.log('hi2!'); - if (fileConfig.loadFromFile) { setEthSenderSenderAggregatedBlockCommitDeadline(pathToHome, fileConfig, 1); setAggregatedBlockProveDeadline(pathToHome, fileConfig, 1); setAggregatedBlockExecuteDeadline(pathToHome, fileConfig, 1); setBlockCommitDeadlineMs(pathToHome, fileConfig, 2000); } - - console.log('hi4!'); await mainNodeSpawner.killAndSpawnMainNode(); - console.log('hi5!'); mainContract = new ethers.Contract( await tester.web3Provider.getMainContractAddress(), ZK_CHAIN_INTERFACE, tester.ethProvider ); - console.log('hi3!'); - const stmAddr = await mainContract.getChainTypeManager(); const stmContract = new ethers.Contract(stmAddr, contracts.stateTransitonManager, tester.syncWallet.providerL1); const governanceAddr = await stmContract.owner(); @@ -335,7 +317,6 @@ describe('Upgrade test', function () { delegateCalldata ]); - console.log('hey'); const { stmUpgradeData, chainUpgradeCalldata, setTimestampCalldata } = await prepareUpgradeCalldata( alice._providerL1(), alice._providerL2(), @@ -369,10 +350,8 @@ describe('Upgrade test', function () { ); executeOperation = chainUpgradeCalldata; - console.log('hey2'); console.log('Sending scheduleTransparentOperation'); await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation, 0, gatewayInfo); - console.log('hey3'); console.log('Sending executeOperation'); await sendGovernanceOperation( @@ -380,7 +359,6 @@ describe('Upgrade test', function () { stmUpgradeData.executeOperationValue, gatewayInfo ); - console.log('hey4'); console.log('Sending chain admin operation'); await sendChainAdminOperation({ 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 3e1498d62b69..2cf7eb710280 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 @@ -478,28 +478,3 @@ async fn call_script( Ok(gateway_preparation_script_output) } - -/// Applies the L1 to L2 address aliasing by adding a constant offset. -/// -/// # Arguments -/// -/// * `l1_address` - The Layer 1 Ethereum address to be aliased. -/// -/// # Returns -/// -/// * `Address` - The aliased Layer 2 Ethereum address. -fn apply_l1_to_l2_alias(l1_address: Address) -> Address { - // Define the constant offset as a U256 number. - let offset = U256::from_str_radix("1111000000000000000000000000000000001111", 16).unwrap(); - - // Convert the l1_address (which is 20 bytes) into a U256 number. - let l1_address_u256 = U256::from_big_endian(&l1_address.0); - - // Perform the addition with wrapping to mimic Solidity's unchecked arithmetic. - let l2_address_u256 = l1_address_u256 + offset; - - // Convert the resulting U256 back into a 20-byte address. - let mut l2_address_bytes = [0u8; 32]; - l2_address_u256.to_big_endian(&mut l2_address_bytes); - Address::from_slice(&l2_address_bytes[12..32]) // Take the last 20 bytes. -} From c77f9f7ccaae37533f8e0e768bc6b58a4994db0a Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 18 Nov 2024 23:42:03 +0100 Subject: [PATCH 05/13] upd contracts --- contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts b/contracts index 35e5dd381766..eed140aa3f17 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 35e5dd38176642c64a4eef0f0b19f1b6e7f5706a +Subproject commit eed140aa3f17a113691a9f6cbfa16e45de78f20f From f1951b5abafa2b800159bfecc6695862e39454c5 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 18 Nov 2024 23:44:10 +0100 Subject: [PATCH 06/13] upgrade test in ci --- .github/workflows/ci-core-reusable.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 7201608ecd0a..333b0c97fc2b 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -519,6 +519,9 @@ jobs: # 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 From 18c823800a5f2025eec8914677a28f2fe8038f8f Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 19 Nov 2024 14:29:50 +0100 Subject: [PATCH 07/13] gateway migration --- contracts | 2 +- core/lib/config/src/configs/gateway.rs | 5 ++-- core/tests/upgrade-test/tests/upgrade.test.ts | 17 ----------- core/tests/upgrade-test/tests/utils.ts | 28 ------------------- .../src/commands/chain/convert_to_gateway.rs | 13 +++++++-- .../commands/chain/migrate_from_gateway.rs | 5 +++- zkstack_cli/zkstackup/zkstackup | 2 +- 7 files changed, 18 insertions(+), 54 deletions(-) diff --git a/contracts b/contracts index eed140aa3f17..128ad4f6f70c 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit eed140aa3f17a113691a9f6cbfa16e45de78f20f +Subproject commit 128ad4f6f70c4bf6931631a489af438b2c943e10 diff --git a/core/lib/config/src/configs/gateway.rs b/core/lib/config/src/configs/gateway.rs index e139535b8d3f..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, @@ -40,9 +42,6 @@ impl GatewayChainConfig { 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, diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 336ea72903e5..a0d15db130b3 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -11,14 +11,12 @@ import { loadConfig, shouldLoadConfigFromFile } from 'utils/build/file-configs'; import { Contracts, initContracts, - runServerInBackground, setAggregatedBlockExecuteDeadline, setAggregatedBlockProveDeadline, setBlockCommitDeadlineMs, setEthSenderSenderAggregatedBlockCommitDeadline } from './utils'; import path from 'path'; -import internal from 'stream'; import { ZKSYNC_MAIN_ABI } from 'zksync-ethers/build/utils'; const pathToHome = path.join(__dirname, '../../../..'); @@ -391,12 +389,10 @@ describe('Upgrade test', function () { let tryCount = 0; while (lastBatchExecuted < l1BatchNumber && tryCount < 40) { lastBatchExecuted = await slMainContract.getTotalBatchesExecuted(); - console.log(lastBatchExecuted); tryCount += 1; await utils.sleep(2); } if (lastBatchExecuted < l1BatchNumber) { - console.log(l1BatchNumber); throw new Error('Server did not execute old blocks'); } @@ -627,8 +623,6 @@ async function prepareUpgradeCalldata( }, gatewayInfo: GatewayInfo | null ) { - console.log('hey5'); - let settlementLayerDiamondProxy: ethers.Contract; if (gatewayInfo) { settlementLayerDiamondProxy = new ethers.Contract( @@ -644,7 +638,6 @@ async function prepareUpgradeCalldata( const oldProtocolVersion = Number(await settlementLayerDiamondProxy.getProtocolVersion()); const newProtocolVersion = addToProtocolVersion(oldProtocolVersion, 1, 1); - console.log('hey1122'); params.l2ProtocolUpgradeTx.nonce ??= BigInt(unpackNumberSemVer(newProtocolVersion)[1]); const upgradeInitData = contracts.l1DefaultUpgradeAbi.encodeFunctionData('upgrade', [ @@ -661,7 +654,6 @@ async function prepareUpgradeCalldata( ] ]); - console.log('hey6'); // Prepare the diamond cut data const upgradeParam = { facetCuts: [], @@ -669,7 +661,6 @@ async function prepareUpgradeCalldata( initCalldata: upgradeInitData }; - console.log('hey7'); // Prepare calldata for upgrading STM const stmUpgradeCalldata = contracts.stateTransitonManager.encodeFunctionData('setNewVersionUpgrade', [ upgradeParam, @@ -678,24 +669,20 @@ async function prepareUpgradeCalldata( ethers.MaxUint256, newProtocolVersion ]); - console.log('hey8'); // Execute this upgrade on a specific chain under this STM. const chainUpgradeCalldata = contracts.adminFacetAbi.encodeFunctionData('upgradeChainFromVersion', [ oldProtocolVersion, upgradeParam ]); - console.log('hey9'); // Set timestamp for upgrade on a specific chain under this STM. const setTimestampCalldata = contracts.chainAdminAbi.encodeFunctionData('setUpgradeTimestamp', [ newProtocolVersion, params.upgradeTimestamp ]); - console.log('hey10'); const bridgehubAddr = await l2Provider.getBridgehubContractAddress(); - console.log('hey11'); const stmUpgradeData = await prepareGovernanceCalldata( settlementLayerCTMAddress, @@ -726,7 +713,6 @@ async function prepareGovernanceCalldata( gatewayInfo: GatewayInfo | null ): Promise { let call; - console.log('t1'); if (gatewayInfo) { // We will have to perform an L1->L2 transaction to the gateway call = await composeL1ToL2Call( @@ -746,7 +732,6 @@ async function prepareGovernanceCalldata( }; } - console.log('t2'); const governanceOperation = { calls: [call], predecessor: ethers.ZeroHash, @@ -770,8 +755,6 @@ async function prepareGovernanceCalldata( }; } -async function prepareAdminCalldata() {} - async function composeL1ToL2Call( chainId: string, to: string, diff --git a/core/tests/upgrade-test/tests/utils.ts b/core/tests/upgrade-test/tests/utils.ts index 93f5b9bb1eee..e5d0fc553b55 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); } 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 59a03d76864e..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 @@ -120,7 +120,14 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { "grantWhitelist", ( output.gateway_transaction_filterer_proxy, - vec![ecosystem_config.get_contracts_config()?.l1.governance_addr], + vec![ + ecosystem_config.get_contracts_config()?.l1.governance_addr, + ecosystem_config + .get_wallets()? + .deployer + .context("no deployer addr")? + .address, + ], ), ) .unwrap(), @@ -169,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()) @@ -216,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/zkstackup/zkstackup b/zkstack_cli/zkstackup/zkstackup index e91bbc17905c..07002e216659 100755 --- a/zkstack_cli/zkstackup/zkstackup +++ b/zkstack_cli/zkstackup/zkstackup @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eo pipefail -LOCAL_DIR="$HOME/.local/" +LOCAL_DIR="$/Users/stas/.local/" BIN_DIR="$LOCAL_DIR/bin" BINS=() From 1ed04b1764ff257fcd3ec1210f196fcb64046e21 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 19 Nov 2024 14:51:44 +0100 Subject: [PATCH 08/13] fix file --- zkstack_cli/zkstackup/zkstackup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zkstack_cli/zkstackup/zkstackup b/zkstack_cli/zkstackup/zkstackup index 07002e216659..e91bbc17905c 100755 --- a/zkstack_cli/zkstackup/zkstackup +++ b/zkstack_cli/zkstackup/zkstackup @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eo pipefail -LOCAL_DIR="$/Users/stas/.local/" +LOCAL_DIR="$HOME/.local/" BIN_DIR="$LOCAL_DIR/bin" BINS=() From bfeaab6668f8945ddac9dba8d2a3c23ec74ee494 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 19 Nov 2024 16:35:37 +0100 Subject: [PATCH 09/13] remove consistency checker requirement --- core/node/consistency_checker/src/lib.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/core/node/consistency_checker/src/lib.rs b/core/node/consistency_checker/src/lib.rs index 368881f127b4..290196b49a96 100644 --- a/core/node/consistency_checker/src/lib.rs +++ b/core/node/consistency_checker/src/lib.rs @@ -240,23 +240,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!( From 9ad421cf730e090271a0d86583a34f21601675a7 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 19 Nov 2024 17:24:23 +0100 Subject: [PATCH 10/13] fix contracts used --- core/node/consistency_checker/src/lib.rs | 9 +++------ core/tests/upgrade-test/tests/upgrade.test.ts | 4 ++-- core/tests/upgrade-test/tests/utils.ts | 10 +++++----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/core/node/consistency_checker/src/lib.rs b/core/node/consistency_checker/src/lib.rs index 290196b49a96..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, }; diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index a0d15db130b3..d62cb4ec6615 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -214,7 +214,7 @@ describe('Upgrade test', function () { ); const stmAddr = await mainContract.getChainTypeManager(); - const stmContract = new ethers.Contract(stmAddr, contracts.stateTransitonManager, tester.syncWallet.providerL1); + const stmContract = new ethers.Contract(stmAddr, contracts.chainTypeManager, tester.syncWallet.providerL1); const governanceAddr = await stmContract.owner(); governanceContract = new ethers.Contract(governanceAddr, contracts.governanceAbi, tester.syncWallet.providerL1); const chainAdminAddr = await mainContract.getAdmin(); @@ -662,7 +662,7 @@ async function prepareUpgradeCalldata( }; // Prepare calldata for upgrading STM - const stmUpgradeCalldata = contracts.stateTransitonManager.encodeFunctionData('setNewVersionUpgrade', [ + const stmUpgradeCalldata = contracts.chainTypeManager.encodeFunctionData('setNewVersionUpgrade', [ upgradeParam, oldProtocolVersion, // The protocol version will not have any deadline in this upgrade diff --git a/core/tests/upgrade-test/tests/utils.ts b/core/tests/upgrade-test/tests/utils.ts index e5d0fc553b55..914dddaa2019 100644 --- a/core/tests/upgrade-test/tests/utils.ts +++ b/core/tests/upgrade-test/tests/utils.ts @@ -40,7 +40,7 @@ export interface Contracts { l2ForceDeployUpgraderAbi: any; complexUpgraderAbi: any; counterBytecode: any; - stateTransitonManager: any; + chainTypeManager: any; } export function initContracts(pathToHome: string, zkStack: boolean): Contracts { @@ -68,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 { @@ -95,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 ) }; } From 834fb13ad0e547923132d0db057f51cf9fe11b9f Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 20 Nov 2024 14:22:05 +0100 Subject: [PATCH 11/13] review --- core/tests/ts-integration/src/env.ts | 2 +- core/tests/ts-integration/src/utils.ts | 313 ------------------ core/tests/ts-integration/tests/fees.test.ts | 2 +- core/tests/upgrade-test/tests/upgrade.test.ts | 261 ++++++++------- 4 files changed, 132 insertions(+), 446 deletions(-) delete mode 100644 core/tests/ts-integration/src/utils.ts 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/src/utils.ts b/core/tests/ts-integration/src/utils.ts deleted file mode 100644 index 65a6b7566b9a..000000000000 --- a/core/tests/ts-integration/src/utils.ts +++ /dev/null @@ -1,313 +0,0 @@ -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 fs from 'node:fs/promises'; -import * as zksync from 'zksync-ethers'; -import * as fsSync from 'fs'; -import YAML from 'yaml'; - -// executes a command in background and returns a child process handle -// by default pipes data to parent's stdio but this can be overridden -export function runServerInBackground({ - components, - stdio, - cwd, - env, - useZkStack, - chain -}: { - components?: string[]; - stdio: any; - cwd?: ProcessEnvOptions['cwd']; - env?: ProcessEnvOptions['env']; - useZkStack?: boolean; - newL1GasPrice?: string; - newPubdataPrice?: string; - chain?: string; -}): ChildProcessWithoutNullStreams { - let command = ''; - if (useZkStack) { - command = 'zkstack server'; - if (chain) { - command += ` --chain ${chain}`; - } - } else { - command = 'zk server'; - } - if (components && components.length > 0) { - command += ` --components=${components.join(',')}`; - } - command = command.replace(/\n/g, ' '); - console.log(`Run command ${command}`); - return _spawn(command, { stdio: stdio, shell: true, detached: true, cwd, env }); -} - -export interface MainNodeSpawnOptions { - enableConsensus: boolean; - ethClientWeb3Url: string; - apiWeb3JsonRpcHttpUrl: string; - baseTokenAddress: string; -} - -export enum NodeType { - MAIN = 'zksync_server', - EXT = 'zksync_external_node' -} - -export class Node { - constructor(public proc: ChildProcessWithoutNullStreams, public l2NodeUrl: string, private readonly type: TYPE) {} - - public async terminate() { - try { - await killPidWithAllChilds(this.proc.pid!, 9); - } catch (err) { - console.log(`ignored error: ${err}`); - } - } - - /** - * Terminates all main node processes running. - * - * WARNING: This is not safe to use when running nodes on multiple chains. - */ - public static async killAll(type: NodeType) { - try { - await utils.exec(`killall -KILL ${type}`); - } catch (err) { - console.log(`ignored error: ${err}`); - } - } - - public async killAndWaitForShutdown() { - await this.terminate(); - // Wait until it's really stopped. - let iter = 0; - while (iter < 30) { - try { - let provider = new zksync.Provider(this.l2NodeUrl); - await provider.getBlockNumber(); - await utils.sleep(2); - iter += 1; - } catch (_) { - // When exception happens, we assume that server died. - return; - } - } - // It's going to panic anyway, since the server is a singleton entity, so better to exit early. - throw new Error(`${this.type} didn't stop after a kill request`); - } -} - -interface MainNodeOptions { - newL1GasPrice?: bigint; - newPubdataPrice?: bigint; - customBaseToken?: boolean; - externalPriceApiClientForcedNumerator?: number; - externalPriceApiClientForcedDenominator?: number; - externalPriceApiClientForcedFluctuation?: number; - baseTokenPricePollingIntervalMs?: number; - baseTokenAdjusterL1UpdateDeviationPercentage?: number; -} -export class NodeSpawner { - private readonly generalConfigPath: string | undefined; - private readonly originalConfig: string | undefined; - public mainNode: Node | null; - - public constructor( - private readonly pathToHome: string, - private readonly logs: fs.FileHandle, - private readonly fileConfig: FileConfig, - private readonly options: MainNodeSpawnOptions, - private env?: ProcessEnvOptions['env'] - ) { - this.mainNode = null; - if (fileConfig.loadFromFile) { - this.generalConfigPath = getConfigPath({ - pathToHome, - chain: fileConfig.chain, - configsFolder: 'configs', - config: 'general.yaml' - }); - this.originalConfig = fsSync.readFileSync(this.generalConfigPath, 'utf8'); - } - } - - public async killAndSpawnMainNode(configOverrides: MainNodeOptions | null = null): Promise { - if (this.mainNode != null) { - await this.mainNode.killAndWaitForShutdown(); - this.mainNode = null; - } - this.mainNode = await this.spawnMainNode(configOverrides); - } - - private async spawnMainNode(overrides: MainNodeOptions | null): Promise> { - const env = this.env ?? process.env; - const { fileConfig, pathToHome, options, logs } = this; - - const testMode = overrides?.newPubdataPrice != null || overrides?.newL1GasPrice != null; - - console.log('Overrides: ', overrides); - - if (fileConfig.loadFromFile) { - this.restoreConfig(); - const config = this.readFileConfig(); - config['state_keeper']['transaction_slots'] = testMode ? 1 : 8192; - - if (overrides != null) { - if (overrides.newL1GasPrice) { - config['eth']['gas_adjuster']['internal_enforced_sl_gas_price'] = overrides.newL1GasPrice; - } - - if (overrides.newPubdataPrice) { - config['eth']['gas_adjuster']['internal_enforced_pubdata_price'] = overrides.newPubdataPrice; - } - - if (overrides.externalPriceApiClientForcedNumerator !== undefined) { - config['external_price_api_client']['forced_numerator'] = - overrides.externalPriceApiClientForcedNumerator; - } - - if (overrides.externalPriceApiClientForcedDenominator !== undefined) { - config['external_price_api_client']['forced_denominator'] = - overrides.externalPriceApiClientForcedDenominator; - } - - if (overrides.externalPriceApiClientForcedFluctuation !== undefined) { - config['external_price_api_client']['forced_fluctuation'] = - overrides.externalPriceApiClientForcedFluctuation; - } - - if (overrides.baseTokenPricePollingIntervalMs !== undefined) { - const cacheUpdateInterval = overrides.baseTokenPricePollingIntervalMs / 2; - // To reduce price polling interval we also need to reduce base token receipt checking and tx sending sleeps as they are blocking the poller. Also cache update needs to be reduced appropriately. - - config['base_token_adjuster']['l1_receipt_checking_sleep_ms'] = - overrides.baseTokenPricePollingIntervalMs; - config['base_token_adjuster']['l1_tx_sending_sleep_ms'] = overrides.baseTokenPricePollingIntervalMs; - config['base_token_adjuster']['price_polling_interval_ms'] = - overrides.baseTokenPricePollingIntervalMs; - config['base_token_adjuster']['price_cache_update_interval_ms'] = cacheUpdateInterval; - } - - if (overrides.baseTokenAdjusterL1UpdateDeviationPercentage !== undefined) { - config['base_token_adjuster']['l1_update_deviation_percentage'] = - overrides.baseTokenAdjusterL1UpdateDeviationPercentage; - } - } - - this.writeFileConfig(config); - } else { - env['DATABASE_MERKLE_TREE_MODE'] = 'full'; - - if (overrides != null) { - if (overrides.newPubdataPrice) { - env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_PUBDATA_PRICE'] = - overrides.newPubdataPrice.toString(); - } - - if (overrides.newL1GasPrice) { - // We need to ensure that each transaction gets into its own batch for more fair comparison. - env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_L1_GAS_PRICE'] = overrides.newL1GasPrice.toString(); - } - - if (overrides.externalPriceApiClientForcedNumerator !== undefined) { - env['EXTERNAL_PRICE_API_CLIENT_FORCED_NUMERATOR'] = - overrides.externalPriceApiClientForcedNumerator.toString(); - } - - if (overrides.externalPriceApiClientForcedDenominator !== undefined) { - env['EXTERNAL_PRICE_API_CLIENT_FORCED_DENOMINATOR'] = - overrides.externalPriceApiClientForcedDenominator.toString(); - } - - if (overrides.externalPriceApiClientForcedFluctuation !== undefined) { - env['EXTERNAL_PRICE_API_CLIENT_FORCED_FLUCTUATION'] = - overrides.externalPriceApiClientForcedFluctuation.toString(); - } - - if (overrides.baseTokenPricePollingIntervalMs !== undefined) { - const cacheUpdateInterval = overrides.baseTokenPricePollingIntervalMs / 2; - // To reduce price polling interval we also need to reduce base token receipt checking and tx sending sleeps as they are blocking the poller. Also cache update needs to be reduced appropriately. - env['BASE_TOKEN_ADJUSTER_L1_RECEIPT_CHECKING_SLEEP_MS'] = - overrides.baseTokenPricePollingIntervalMs.toString(); - env['BASE_TOKEN_ADJUSTER_L1_TX_SENDING_SLEEP_MS'] = - overrides.baseTokenPricePollingIntervalMs.toString(); - env['BASE_TOKEN_ADJUSTER_PRICE_POLLING_INTERVAL_MS'] = - overrides.baseTokenPricePollingIntervalMs.toString(); - env['BASE_TOKEN_ADJUSTER_PRICE_CACHE_UPDATE_INTERVAL_MS'] = cacheUpdateInterval.toString(); - } - - if (overrides.baseTokenAdjusterL1UpdateDeviationPercentage !== undefined) { - env['BASE_TOKEN_ADJUSTER_L1_UPDATE_DEVIATION_PERCENTAGE'] = - overrides.baseTokenAdjusterL1UpdateDeviationPercentage.toString(); - } - } - - if (testMode) { - // We need to ensure that each transaction gets into its own batch for more fair comparison. - env['CHAIN_STATE_KEEPER_TRANSACTION_SLOTS'] = '1'; - } - } - - let components = 'api,tree,eth,state_keeper,da_dispatcher,vm_runner_protective_reads'; - if (options.enableConsensus) { - components += ',consensus'; - } - if (options.baseTokenAddress != zksync.utils.LEGACY_ETH_ADDRESS) { - components += ',base_token_ratio_persister'; - } - let proc = runServerInBackground({ - components: [components], - stdio: ['ignore', logs, logs], - cwd: pathToHome, - env: env, - useZkStack: fileConfig.loadFromFile, - chain: fileConfig.chain - }); - - // Wait until the main node starts responding. - await waitForNodeToStart(proc, options.apiWeb3JsonRpcHttpUrl); - return new Node(proc, options.apiWeb3JsonRpcHttpUrl, NodeType.MAIN); - } - - public restoreConfig() { - if (this.generalConfigPath != void 0 && this.originalConfig != void 0) - fsSync.writeFileSync(this.generalConfigPath, this.originalConfig, 'utf8'); - } - - private readFileConfig() { - if (this.generalConfigPath == void 0) - throw new Error('Trying to set property in config while not in file mode'); - const generalConfig = fsSync.readFileSync(this.generalConfigPath, 'utf8'); - return YAML.parse(generalConfig); - } - - private writeFileConfig(config: any) { - if (this.generalConfigPath == void 0) - throw new Error('Trying to set property in config while not in file mode'); - - const newGeneralConfig = YAML.stringify(config); - fsSync.writeFileSync(this.generalConfigPath, newGeneralConfig, 'utf8'); - } -} - -async function waitForNodeToStart(proc: ChildProcessWithoutNullStreams, l2Url: string) { - while (true) { - try { - const l2Provider = new zksync.Provider(l2Url); - const blockNumber = await l2Provider.getBlockNumber(); - if (blockNumber != 0) { - console.log(`Initialized node API on ${l2Url}; latest block: ${blockNumber}`); - break; - } - } catch (err) { - if (proc.exitCode != null) { - assert.fail(`server failed to start, exitCode = ${proc.exitCode}`); - } - console.log(`Node waiting for API on ${l2Url}`); - await utils.sleep(1); - } - } -} 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 d62cb4ec6615..b268112e6050 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -45,14 +45,25 @@ interface Call { } 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; + // 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; - let mainContract: ethers.Contract; - let governanceContract: ethers.Contract; + + // 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; @@ -69,7 +80,9 @@ describe('Upgrade test', function () { let upgradeAddress: string | undefined; let contractsPriorityTxMaxGasLimit: string; - let isGateway: boolean; + // 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; @@ -80,115 +93,114 @@ describe('Upgrade test', function () { 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({ - pathToHome, - chain: fileConfig.chain, - config: 'secrets.yaml' - }); - const genesisConfig = 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: 'genesis.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; - 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) { - isGateway = true; - - const gatewayChainConfig = loadConfig({ - pathToHome, - chain: fileConfig.chain, - config: 'gateway_chain.yaml' - }); - - 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 - }); - } else { - // Since gateway config can not be imported - // FIXME: potentially delete the non-file-based tests is enough - throw new Error('Non file based not supported'); - - // 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'); - // // } - // bytecodeSupplier = process.env.CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR as string; - // if (!bytecodeSupplier) { - // throw new Error('CONTRACTS_L1_BYTECODE_SUPPLIER_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' + }); - slAdminGovWallet = gatewayInfo - ? new zksync.Wallet(chainWalletConfig.governor.private_key, gatewayInfo.gatewayProvider) - : 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' + }); - 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 { - throw new Error('Not loading from file not supported'); - // 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()); } 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 () => { @@ -207,27 +219,16 @@ describe('Upgrade test', function () { } await mainNodeSpawner.killAndSpawnMainNode(); - mainContract = new ethers.Contract( + const l1MainContract = new ethers.Contract( await tester.web3Provider.getMainContractAddress(), ZK_CHAIN_INTERFACE, tester.ethProvider ); - const stmAddr = await mainContract.getChainTypeManager(); + const stmAddr = await l1MainContract.getChainTypeManager(); const stmContract = new ethers.Contract(stmAddr, contracts.chainTypeManager, tester.syncWallet.providerL1); - const governanceAddr = await stmContract.owner(); - governanceContract = new ethers.Contract(governanceAddr, contracts.governanceAbi, tester.syncWallet.providerL1); - const chainAdminAddr = await mainContract.getAdmin(); - slChainAdminContract = gatewayInfo - ? new ethers.Contract(gatewayInfo.l2ChainAdmin, contracts.chainAdminAbi, gatewayInfo.gatewayProvider) - : new ethers.Contract(chainAdminAddr, contracts.chainAdminAbi, tester.syncWallet.providerL1); - - slMainContract = gatewayInfo - ? new ethers.Contract(gatewayInfo.l2DiamondProxyAddress, ZKSYNC_MAIN_ABI, gatewayInfo.gatewayProvider) - : mainContract; - - let blocksCommitted = await mainContract.getTotalBatchesCommitted(); + let blocksCommitted = await slMainContract.getTotalBatchesCommitted(); const initialL1BatchNumber = await tester.web3Provider.getL1BatchNumber(); @@ -270,10 +271,10 @@ 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); } @@ -344,18 +345,18 @@ describe('Upgrade test', function () { bootloaderHash, upgradeTimestamp: 0 }, - isGateway ? gatewayInfo : null + gatewayInfo ); executeOperation = chainUpgradeCalldata; console.log('Sending scheduleTransparentOperation'); - await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation, 0, gatewayInfo); + await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation, 0, null); console.log('Sending executeOperation'); await sendGovernanceOperation( stmUpgradeData.executeOperation, stmUpgradeData.executeOperationValue, - gatewayInfo + gatewayInfo ? gatewayInfo.gatewayProvider : null ); console.log('Sending chain admin operation'); @@ -398,7 +399,7 @@ describe('Upgrade test', function () { // Execute the upgrade await sendChainAdminOperation({ - target: gatewayInfo ? gatewayInfo.l2DiamondProxyAddress : await mainContract.getAddress(), + target: await slMainContract.getAddress(), data: executeOperation, value: 0 }); @@ -439,9 +440,13 @@ describe('Upgrade test', function () { } catch (_) {} }); - async function sendGovernanceOperation(data: string, value: BigNumberish, gatewayInfo: GatewayInfo | null) { + 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 @@ -451,22 +456,16 @@ describe('Upgrade test', function () { 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 (!gatewayInfo) { + if (!providerForPriorityOp) { + // No further waiting is needed. return; } - // `try/catch` is needed since SDK will throw an error if the priority operation is not found. - // So it is not possible to disntiguish the case when it was not emitted on purpose or not - let hash; - try { - const contract = await gatewayInfo.gatewayProvider.getMainContractAddress(); - hash = zksync.utils.getL2HashFromPriorityOp(receipt!, contract); - console.log(`Gateway L1->L2 transaction with hash ${hash} detected`); - } catch { - return; - } + const contract = await providerForPriorityOp.getMainContractAddress(); + const hash = zksync.utils.getL2HashFromPriorityOp(receipt!, contract); + console.log(`Gateway L1->L2 transaction with hash ${hash} detected`); - await gatewayInfo.gatewayProvider.waitForTransaction(hash); + await providerForPriorityOp.waitForTransaction(hash); console.log('Transaction complete!'); } @@ -484,13 +483,13 @@ describe('Upgrade test', function () { } async function deployDefaultUpgradeImpl(runner: ethers.Wallet): Promise { - const bytecodePath = isGateway + 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 (isGateway) { + if (gatewayInfo) { const factory = new zksync.ContractFactory([], bytecode, runner); return (await factory.deploy()).getAddress(); } else { From 5cbc6e74946098a4e840043daa3e6fc835e5f53b Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 20 Nov 2024 14:27:34 +0100 Subject: [PATCH 12/13] fix lint --- core/tests/upgrade-test/tests/upgrade.test.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index b268112e6050..8bc8904b6148 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -219,15 +219,6 @@ describe('Upgrade test', function () { } await mainNodeSpawner.killAndSpawnMainNode(); - const l1MainContract = new ethers.Contract( - await tester.web3Provider.getMainContractAddress(), - ZK_CHAIN_INTERFACE, - tester.ethProvider - ); - - const stmAddr = await l1MainContract.getChainTypeManager(); - const stmContract = new ethers.Contract(stmAddr, contracts.chainTypeManager, tester.syncWallet.providerL1); - let blocksCommitted = await slMainContract.getTotalBatchesCommitted(); const initialL1BatchNumber = await tester.web3Provider.getL1BatchNumber(); From 706861941a0604a9697abd9833057d776b8272ef Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 20 Nov 2024 14:30:03 +0100 Subject: [PATCH 13/13] upd contracts --- contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts b/contracts index 128ad4f6f70c..e8648270f38f 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 128ad4f6f70c4bf6931631a489af438b2c943e10 +Subproject commit e8648270f38f170af1dd4db3d0f089e49d74a1ad