From 40353616f278800dc80fcbe5f2a6483019033b20 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 10 Oct 2024 15:38:21 +0300 Subject: [PATCH] feat: Handle new yul compilation flow (#3038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ New versions of `zksolc` have several breaking changes. The following changes require updates to the zksync-era: 1. New output path for yul and zasm contracts (`.yul/.yul.zbin` instead of `.yul.zbin`) 2. `.zbin` files now contain utf8 encoded bytecode instead of binary This pull request adds support for these changes while maintaining backwards compatibility. ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk_supervisor fmt` and `zk_supervisor lint`. --- core/lib/contracts/src/lib.rs | 54 +++++++++++++++---- .../multivm/src/versions/vm_1_3_2/utils.rs | 9 +--- .../src/versions/vm_fast/tests/utils.rs | 8 ++- .../src/versions/vm_latest/tests/utils.rs | 9 ++-- core/lib/multivm/src/versions/vm_m5/utils.rs | 9 +--- core/lib/multivm/src/versions/vm_m6/utils.rs | 9 +--- core/tests/upgrade-test/tests/upgrade.test.ts | 35 +++++++++--- 7 files changed, 84 insertions(+), 49 deletions(-) diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index fb28693887a9..7e133f8dee31 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -259,19 +259,19 @@ impl SystemContractsRepo { "artifacts-zk/contracts-preprocessed/{0}{1}.sol/{1}.json", directory, name ))), - ContractLanguage::Yul => read_zbin_bytecode_from_path(self.root.join(format!( - "contracts-preprocessed/{0}artifacts/{1}.yul.zbin", - directory, name - ))), + ContractLanguage::Yul => { + let artifacts_path = self + .root + .join(format!("contracts-preprocessed/{}artifacts/", directory)); + read_yul_bytecode_by_path(artifacts_path, name) + } } } } pub fn read_bootloader_code(bootloader_type: &str) -> Vec { - read_zbin_bytecode(format!( - "contracts/system-contracts/bootloader/build/artifacts/{}.yul.zbin", - bootloader_type - )) + let artifacts_path = "contracts/system-contracts/bootloader/build/artifacts/"; + read_yul_bytecode(artifacts_path, bootloader_type) } fn read_proved_batch_bootloader_bytecode() -> Vec { @@ -288,10 +288,46 @@ pub fn read_zbin_bytecode(relative_zbin_path: impl AsRef) -> Vec { read_zbin_bytecode_from_path(bytecode_path) } +pub fn read_yul_bytecode(relative_artifacts_path: &str, name: &str) -> Vec { + let artifacts_path = Path::new(&home_path()).join(relative_artifacts_path); + read_yul_bytecode_by_path(artifacts_path, name) +} + +pub fn read_yul_bytecode_by_path(artifacts_path: PathBuf, name: &str) -> Vec { + let bytecode_path = artifacts_path.join(format!("{name}.yul/{name}.yul.zbin")); + + // Legacy versions of zksolc use the following path for output data if a yul file is being compiled: .yul.zbin + // New zksolc versions use .yul/.yul.zbin, for consistency with solidity files compilation. + // In addition, the output of the legacy zksolc in this case is a binary file, while in new versions it is hex encoded. + if fs::exists(&bytecode_path) + .unwrap_or_else(|err| panic!("Invalid path: {bytecode_path:?}, {err}")) + { + read_zbin_bytecode_from_hex_file(bytecode_path) + } else { + let bytecode_path_legacy = artifacts_path.join(format!("{name}.yul.zbin")); + + if fs::exists(&bytecode_path_legacy) + .unwrap_or_else(|err| panic!("Invalid path: {bytecode_path_legacy:?}, {err}")) + { + read_zbin_bytecode_from_path(bytecode_path_legacy) + } else { + panic!("Can't find bytecode for '{name}' yul contract at {artifacts_path:?}") + } + } +} + /// Reads zbin bytecode from a given path. fn read_zbin_bytecode_from_path(bytecode_path: PathBuf) -> Vec { fs::read(&bytecode_path) - .unwrap_or_else(|err| panic!("Can't read .zbin bytecode at {:?}: {}", bytecode_path, err)) + .unwrap_or_else(|err| panic!("Can't read .zbin bytecode at {bytecode_path:?}: {err}")) +} + +/// Reads zbin bytecode from a given path as utf8 text file. +fn read_zbin_bytecode_from_hex_file(bytecode_path: PathBuf) -> Vec { + let bytes = fs::read(&bytecode_path) + .unwrap_or_else(|err| panic!("Can't read .zbin bytecode at {bytecode_path:?}: {err}")); + + hex::decode(bytes).unwrap_or_else(|err| panic!("Invalid input file: {bytecode_path:?}, {err}")) } /// Hash of code and code which consists of 32 bytes words diff --git a/core/lib/multivm/src/versions/vm_1_3_2/utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/utils.rs index da4e2f5350f9..7870b1ff7443 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/utils.rs @@ -5,7 +5,7 @@ use zk_evm_1_3_3::{ vm_state::PrimitiveValue, zkevm_opcode_defs::FatPointer, }; -use zksync_contracts::{read_zbin_bytecode, BaseSystemContracts}; +use zksync_contracts::BaseSystemContracts; use zksync_system_constants::ZKPORTER_IS_AVAILABLE; use zksync_types::{Address, StorageLogKind, H160, MAX_L2_TX_GAS_LIMIT, U256}; use zksync_utils::h256_to_u256; @@ -221,13 +221,6 @@ pub fn create_test_block_params() -> (BlockContext, BlockProperties) { ) } -pub fn read_bootloader_test_code(test: &str) -> Vec { - read_zbin_bytecode(format!( - "contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin", - test - )) -} - pub(crate) fn calculate_computational_gas_used< S: WriteStorage, T: PubdataSpentTracer, diff --git a/core/lib/multivm/src/versions/vm_fast/tests/utils.rs b/core/lib/multivm/src/versions/vm_fast/tests/utils.rs index 5ab5aa0dec92..76ca9bc5dd38 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/utils.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/utils.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use ethabi::Contract; use once_cell::sync::Lazy; use zksync_contracts::{ - load_contract, read_bytecode, read_zbin_bytecode, BaseSystemContracts, SystemContractCode, + load_contract, read_bytecode, read_yul_bytecode, BaseSystemContracts, SystemContractCode, }; use zksync_types::{ utils::storage_key_for_standard_token_balance, AccountTreeId, Address, StorageKey, H160, H256, @@ -64,10 +64,8 @@ pub(crate) fn read_test_contract() -> Vec { } pub(crate) fn get_bootloader(test: &str) -> SystemContractCode { - let bootloader_code = read_zbin_bytecode(format!( - "contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin", - test - )); + let artifacts_path = "contracts/system-contracts/bootloader/tests/artifacts/"; + let bootloader_code = read_yul_bytecode(artifacts_path, test); let bootloader_hash = hash_bytecode(&bootloader_code); SystemContractCode { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/utils.rs b/core/lib/multivm/src/versions/vm_latest/tests/utils.rs index c5487379ce31..4d728962febf 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/utils.rs @@ -1,7 +1,8 @@ use ethabi::Contract; use once_cell::sync::Lazy; use zksync_contracts::{ - load_contract, read_bytecode, read_zbin_bytecode, BaseSystemContracts, SystemContractCode, + load_contract, read_bytecode, read_yul_bytecode, read_zbin_bytecode, BaseSystemContracts, + SystemContractCode, }; use zksync_types::{ utils::storage_key_for_standard_token_balance, AccountTreeId, Address, StorageKey, H256, U256, @@ -59,10 +60,8 @@ pub(crate) fn read_test_contract() -> Vec { } pub(crate) fn get_bootloader(test: &str) -> SystemContractCode { - let bootloader_code = read_zbin_bytecode(format!( - "contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin", - test - )); + let artifacts_path = "contracts/system-contracts/bootloader/tests/artifacts/"; + let bootloader_code = read_yul_bytecode(artifacts_path, test); let bootloader_hash = hash_bytecode(&bootloader_code); SystemContractCode { diff --git a/core/lib/multivm/src/versions/vm_m5/utils.rs b/core/lib/multivm/src/versions/vm_m5/utils.rs index 8c5bca674c69..a38618395b1f 100644 --- a/core/lib/multivm/src/versions/vm_m5/utils.rs +++ b/core/lib/multivm/src/versions/vm_m5/utils.rs @@ -5,7 +5,7 @@ use zk_evm_1_3_1::{ vm_state::PrimitiveValue, zkevm_opcode_defs::FatPointer, }; -use zksync_contracts::{read_zbin_bytecode, BaseSystemContracts}; +use zksync_contracts::BaseSystemContracts; use zksync_system_constants::ZKPORTER_IS_AVAILABLE; use zksync_types::{Address, StorageLogKind, H160, MAX_L2_TX_GAS_LIMIT, U256}; use zksync_utils::h256_to_u256; @@ -253,13 +253,6 @@ pub fn create_test_block_params() -> (BlockContext, BlockProperties) { ) } -pub fn read_bootloader_test_code(test: &str) -> Vec { - read_zbin_bytecode(format!( - "contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin", - test - )) -} - /// Log query, which handle initial and repeated writes to the storage #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct StorageLogQuery { diff --git a/core/lib/multivm/src/versions/vm_m6/utils.rs b/core/lib/multivm/src/versions/vm_m6/utils.rs index d9709022fe3c..912a30a4eafc 100644 --- a/core/lib/multivm/src/versions/vm_m6/utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/utils.rs @@ -5,7 +5,7 @@ use zk_evm_1_3_1::{ vm_state::PrimitiveValue, zkevm_opcode_defs::FatPointer, }; -use zksync_contracts::{read_zbin_bytecode, BaseSystemContracts}; +use zksync_contracts::BaseSystemContracts; use zksync_system_constants::ZKPORTER_IS_AVAILABLE; use zksync_types::{Address, StorageLogKind, H160, MAX_L2_TX_GAS_LIMIT, U256}; use zksync_utils::h256_to_u256; @@ -256,13 +256,6 @@ pub fn create_test_block_params() -> (BlockContext, BlockProperties) { ) } -pub fn read_bootloader_test_code(test: &str) -> Vec { - read_zbin_bytecode(format!( - "contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin", - test - )) -} - pub(crate) fn calculate_computational_gas_used< S: Storage, T: PubdataSpentTracer, diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 2e223b9d7441..665b570ede74 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -64,9 +64,21 @@ describe('Upgrade test', function () { complexUpgraderAddress = '0x000000000000000000000000000000000000800f'; 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 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' + }); ethProviderAddress = secretsConfig.l1.l1_rpc_url; web3JsonRpc = generalConfig.api.web3_json_rpc.http_url; @@ -89,7 +101,11 @@ describe('Upgrade test', function () { alice = tester.emptyWallet(); if (fileConfig.loadFromFile) { - const chainWalletConfig = loadConfig({ pathToHome, chain: fileConfig.chain, config: 'wallets.yaml' }); + const chainWalletConfig = loadConfig({ + pathToHome, + chain: fileConfig.chain, + config: 'wallets.yaml' + }); adminGovWallet = new ethers.Wallet(chainWalletConfig.governor.private_key, alice._providerL1()); @@ -220,8 +236,15 @@ describe('Upgrade test', function () { }); step('Send l1 tx for saving new bootloader', async () => { - const path = `${pathToHome}/contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul.zbin`; - const bootloaderCode = ethers.hexlify(fs.readFileSync(path)); + const path = `${pathToHome}/contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul/playground_batch.yul.zbin`; + let bootloaderCode; + if (fs.existsSync(path)) { + bootloaderCode = '0x'.concat(fs.readFileSync(path).toString()); + } else { + const legacyPath = `${pathToHome}/contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul.zbin`; + bootloaderCode = ethers.hexlify(fs.readFileSync(legacyPath)); + } + bootloaderHash = ethers.hexlify(zksync.utils.hashBytecode(bootloaderCode)); const txHandle = await tester.syncWallet.requestExecute({ contractAddress: ethers.ZeroAddress,