From dd4a2508b67c7d203f5d0f9d05a31317bf2dd521 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Wed, 18 Sep 2024 18:45:13 +0800 Subject: [PATCH 01/31] feat: PoC MLIR codegen integration (#103) --- .gitignore | 39 ++-- .../src/compiler_tester/arguments.rs | 11 +- compiler_tester/src/compiler_tester/main.rs | 28 +-- compiler_tester/src/compilers/solidity/mod.rs | 4 +- .../src/compilers/solidity/upstream/mod.rs | 167 ++++++++++++++++-- .../src/compilers/solidity/upstream/mode.rs | 8 + .../compilers/solidity/upstream/solc/mod.rs | 2 +- .../upstream/solc/standard_json/input/mod.rs | 2 + .../solc/standard_json/input/settings/mod.rs | 9 + compiler_tester/src/compilers/vyper/mod.rs | 4 +- compiler_tester/src/compilers/yul/mod.rs | 3 +- .../src/compilers/yul/mode_upstream.rs | 9 +- compiler_tester/src/lib.rs | 20 +-- compiler_tester/src/vm/eravm/mod.rs | 11 +- compiler_tester/src/vm/evm/mod.rs | 13 +- configs/solc-bin-llvm.json | 16 ++ 16 files changed, 259 insertions(+), 87 deletions(-) create mode 100644 configs/solc-bin-llvm.json diff --git a/.gitignore b/.gitignore index b79c463a..12dcfdf4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,28 @@ +# IDE +/.idea/ +/.vscode/ + +# MacOS +/**/.DS_Store + +# Backup files generated by rustfmt +/**/*.rs.bk + # Compiled files and executables /target*/ -# These are backup files generated by rustfmt -**/*.rs.bk +# Dependency locks +# /Cargo.lock +# /LLVM.lock -# The LLVM framework source +# LLVM framework source /llvm/ # External compilers -/solc-bin*/* -/vyper-bin/* +/solc-bin*/ +/vyper-bin*/ -# The debug, trace, benchmark artifacts +# Debug and benchmark artifacts /debug/ -/trace/ -/**/*.json -/**/*.txt - -# The dependency locks -# /Cargo.lock -# /LLVM.lock - -# IDE -/.idea/ -/.vscode/ - -# MacOS -/**/.DS_Store +/*.json +/*.txt \ No newline at end of file diff --git a/compiler_tester/src/compiler_tester/arguments.rs b/compiler_tester/src/compiler_tester/arguments.rs index 93c77ebd..13359500 100644 --- a/compiler_tester/src/compiler_tester/arguments.rs +++ b/compiler_tester/src/compiler_tester/arguments.rs @@ -59,12 +59,12 @@ pub struct Arguments { #[structopt(long = "disable-value-simulator")] pub disable_value_simulator: bool, - /// Path to the `zksolc` binary. + /// Path to the `zksolc` executable. /// Is set to `zksolc` by default. #[structopt(long = "zksolc")] pub zksolc: Option, - /// Path to the `zkvyper` binary. + /// Path to the `zkvyper` executable. /// Is set to `zkvyper` by default. #[structopt(long = "zkvyper")] pub zkvyper: Option, @@ -78,8 +78,7 @@ pub struct Arguments { /// Specify the target architecture. /// Available arguments: `eravm`, `evm`. - /// The default is `eravm`. - #[structopt(long = "target", default_value = "eravm")] + #[structopt(long = "target")] pub target: era_compiler_common::Target, /// Specify the environment to run tests on. @@ -93,11 +92,11 @@ pub struct Arguments { #[structopt(long = "workflow", default_value = "run")] pub workflow: compiler_tester::Workflow, - /// Path to the default `solc` binaries download configuration file. + /// Path to the default `solc` executables download configuration file. #[structopt(long = "solc-bin-config-path")] pub solc_bin_config_path: Option, - /// Path to the default `vyper` binaries download configuration file. + /// Path to the default `vyper` executables download configuration file. #[structopt(long = "vyper-bin-config-path")] pub vyper_bin_config_path: Option, diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index 3a540880..7770a510 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -1,5 +1,5 @@ //! -//! The compiler tester binary. +//! The compiler tester executable. //! pub(crate) mod arguments; @@ -106,12 +106,12 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { (era_compiler_common::Target::EVM, Some(toolchain)) => toolchain, (era_compiler_common::Target::EVM, None) => compiler_tester::Toolchain::Solc, }; - let binary_download_config_paths = vec![ + let executable_download_config_paths = vec![ arguments.solc_bin_config_path.unwrap_or_else(|| { PathBuf::from(match toolchain { compiler_tester::Toolchain::IrLLVM => "./configs/solc-bin-default.json", compiler_tester::Toolchain::Solc => "./configs/solc-bin-upstream.json", - compiler_tester::Toolchain::SolcLLVM => todo!(), + compiler_tester::Toolchain::SolcLLVM => "./configs/solc-bin-llvm.json", }) }), arguments @@ -156,7 +156,7 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { None }; let vm = compiler_tester::EraVM::new( - binary_download_config_paths, + executable_download_config_paths, PathBuf::from("./configs/solc-bin-system-contracts.json"), system_contracts_debug_config, arguments.system_contracts_load_path, @@ -168,16 +168,16 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { arguments.disable_deployer, arguments.disable_value_simulator, ) { - (true, true) => { - compiler_tester.run_eravm::(vm) - } - (true, false) => { - compiler_tester.run_eravm::(vm) - } + (true, true) => compiler_tester + .run_eravm::(vm, toolchain), + (true, false) => compiler_tester + .run_eravm::(vm, toolchain), (false, true) => compiler_tester - .run_eravm::(vm), + .run_eravm::( + vm, toolchain, + ), (false, false) => compiler_tester - .run_eravm::(vm), + .run_eravm::(vm, toolchain), } } compiler_tester::Environment::FastVM => todo!(), @@ -188,7 +188,7 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { None }; let vm = compiler_tester::EraVM::new( - binary_download_config_paths, + executable_download_config_paths, PathBuf::from("./configs/solc-bin-system-contracts.json"), system_contract_debug_config, arguments.system_contracts_load_path, @@ -202,7 +202,7 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { ) } compiler_tester::Environment::REVM => { - compiler_tester::EVM::download(binary_download_config_paths)?; + compiler_tester::EVM::download(executable_download_config_paths)?; compiler_tester.run_revm(toolchain) } }?; diff --git a/compiler_tester/src/compilers/solidity/mod.rs b/compiler_tester/src/compilers/solidity/mod.rs index 3fe13f15..f2fd25a8 100644 --- a/compiler_tester/src/compilers/solidity/mod.rs +++ b/compiler_tester/src/compilers/solidity/mod.rs @@ -81,7 +81,7 @@ impl Default for SolidityCompiler { } impl SolidityCompiler { - /// The compiler binaries directory. + /// The compiler executables directory. const DIRECTORY: &'static str = "solc-bin/"; /// The solc allow paths argument value. @@ -136,7 +136,7 @@ impl SolidityCompiler { })?; if !entry_type.is_file() { anyhow::bail!( - "Invalid `solc` binary file type: {}", + "Invalid `solc` executable file type: {}", path.to_string_lossy() ); } diff --git a/compiler_tester/src/compilers/solidity/upstream/mod.rs b/compiler_tester/src/compilers/solidity/upstream/mod.rs index b86c9c7d..b19832c1 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mod.rs @@ -14,6 +14,7 @@ use crate::compilers::mode::Mode; use crate::compilers::solidity::cache_key::CacheKey; use crate::compilers::yul::mode_upstream::Mode as YulUpstreamMode; use crate::compilers::Compiler; +use crate::toolchain::Toolchain; use crate::vm::eravm::input::Input as EraVMInput; use crate::vm::evm::input::build::Build as EVMBuild; use crate::vm::evm::input::Input as EVMInput; @@ -33,6 +34,9 @@ use self::solc::Compiler as SolcUpstreamCompiler; pub struct SolidityCompiler { /// The language the compiler will compile. language: SolcStandardJsonInputLanguage, + /// The toolchain identifier. + /// Only `solc` and `solc-llvm` are supported. + toolchain: Toolchain, /// The `solc` process output cache. cache: Cache, } @@ -54,7 +58,7 @@ lazy_static::lazy_static! { (era_compiler_solidity::SolcPipeline::Yul, true, true), ] { for version in SolidityCompiler::all_versions(pipeline, via_ir).expect("`solc` versions analysis error") { - modes.push(SolidityUpstreamMode::new(version, pipeline, via_ir, optimize).into()); + modes.push(SolidityUpstreamMode::new(version, pipeline, via_ir, false, optimize).into()); } } modes @@ -71,16 +75,37 @@ lazy_static::lazy_static! { false, true ] { for version in SolidityCompiler::all_versions(era_compiler_solidity::SolcPipeline::Yul, true).expect("`solc` versions analysis error") { - modes.push(YulUpstreamMode::new(version, optimize).into()); + modes.push(YulUpstreamMode::new(version, false, optimize).into()); } } modes }; + + /// + /// The supported Solidity modes for MLIR codegen. + /// + /// All compilers must be downloaded before initialization. + /// + static ref SOLIDITY_MLIR_MODES: Vec = { + vec![SolidityUpstreamMode::new(semver::Version::new(0, 8, 26), era_compiler_solidity::SolcPipeline::Yul, false, true, false).into()] + }; + + /// + /// The supported Yul modes for MLIR codegen. + /// + /// All compilers must be downloaded before initialization. + /// + static ref YUL_MLIR_MODES: Vec = { + vec![YulUpstreamMode::new(semver::Version::new(0, 8, 26), true, false).into()] + }; } impl SolidityCompiler { - /// The compiler binaries directory. - const DIRECTORY: &'static str = "solc-bin-upstream/"; + /// The upstream compiler executables directory. + const DIRECTORY_UPSTREAM: &'static str = "solc-bin-upstream/"; + + /// The LLVM-fork compiler executables directory. + const DIRECTORY_LLVM: &'static str = "solc-bin-llvm/"; /// The solc allow paths argument value. const SOLC_ALLOW_PATHS: &'static str = "tests"; @@ -88,9 +113,10 @@ impl SolidityCompiler { /// /// A shortcut constructor. /// - pub fn new(language: SolcStandardJsonInputLanguage) -> Self { + pub fn new(language: SolcStandardJsonInputLanguage, toolchain: Toolchain) -> Self { Self { language, + toolchain, cache: Cache::new(), } } @@ -98,8 +124,16 @@ impl SolidityCompiler { /// /// Returns the `solc` executable by its version. /// - pub fn executable(version: &semver::Version) -> anyhow::Result { - SolcUpstreamCompiler::new(format!("{}/solc-{}", Self::DIRECTORY, version)) + pub fn executable( + toolchain: Toolchain, + version: &semver::Version, + ) -> anyhow::Result { + let directory = match toolchain { + Toolchain::Solc => Self::DIRECTORY_UPSTREAM, + Toolchain::SolcLLVM => Self::DIRECTORY_LLVM, + toolchain => panic!("Unsupported toolchain: {toolchain}"), + }; + SolcUpstreamCompiler::new(format!("{}/solc-{}", directory, version)) } /// @@ -110,7 +144,7 @@ impl SolidityCompiler { via_ir: bool, ) -> anyhow::Result> { let mut versions = Vec::new(); - for entry in std::fs::read_dir(Self::DIRECTORY)? { + for entry in std::fs::read_dir(Self::DIRECTORY_UPSTREAM)? { let entry = entry?; let path = entry.path(); let entry_type = entry.file_type().map_err(|error| { @@ -122,7 +156,7 @@ impl SolidityCompiler { })?; if !entry_type.is_file() { anyhow::bail!( - "Invalid `solc` binary file type: {}", + "Invalid `solc` executable file type: {}", path.to_string_lossy() ); } @@ -158,6 +192,7 @@ impl SolidityCompiler { /// pub fn standard_json_output( language: SolcStandardJsonInputLanguage, + toolchain: Toolchain, sources: &[(String, String)], libraries: &BTreeMap>, mode: &Mode, @@ -168,7 +203,7 @@ impl SolidityCompiler { Mode::YulUpstream(mode) => &mode.solc_version, mode => anyhow::bail!("Unsupported mode: {mode}"), }; - let mut solc = Self::executable(solc_version)?; + let mut solc = Self::executable(toolchain, solc_version)?; let output_selection = SolcStandardJsonInputSettingsSelection::new_required(match mode { Mode::SolidityUpstream(mode) => mode.solc_pipeline, @@ -199,6 +234,12 @@ impl SolidityCompiler { mode => anyhow::bail!("Unsupported mode: {mode}"), }; + let via_mlir = match mode { + Mode::SolidityUpstream(mode) => mode.via_mlir, + Mode::YulUpstream(mode) => mode.via_mlir, + mode => anyhow::bail!("Unsupported mode: {mode}"), + }; + let debug = if solc_version >= &semver::Version::new(0, 6, 3) { test_params.map(|test_params| { SolcStandardJsonInputSettingsDebug::new(Some( @@ -217,6 +258,7 @@ impl SolidityCompiler { None, output_selection, via_ir, + via_mlir, optimizer, debug, ) @@ -263,7 +305,14 @@ impl SolidityCompiler { if !self.cache.contains(&cache_key) { self.cache.evaluate(cache_key.clone(), || { - Self::standard_json_output(language, sources, libraries, mode, test_params) + Self::standard_json_output( + language, + self.toolchain, + sources, + libraries, + mode, + test_params, + ) }); } @@ -374,14 +423,90 @@ impl SolidityCompiler { impl Compiler for SolidityCompiler { fn compile_for_eravm( &self, - _test_path: String, - _sources: Vec<(String, String)>, - _libraries: BTreeMap>, - _mode: &Mode, + test_path: String, + sources: Vec<(String, String)>, + libraries: BTreeMap>, + mode: &Mode, _llvm_options: Vec, _debug_config: Option, ) -> anyhow::Result { - anyhow::bail!("The upstream Solidity compiler cannot compile for EraVM"); + let solc_output = self.standard_json_output_cached( + test_path, + self.language, + &sources, + &libraries, + mode, + None, + )?; + + if let Some(errors) = solc_output.errors.as_deref() { + let mut has_errors = false; + let mut error_messages = Vec::with_capacity(errors.len()); + + for error in errors.iter() { + if error.severity.as_str() == "error" { + has_errors = true; + error_messages.push(error.formatted_message.to_owned()); + } + } + + if has_errors { + anyhow::bail!("`solc` errors found: {:?}", error_messages); + } + } + + let method_identifiers = match self.language { + SolcStandardJsonInputLanguage::Solidity => { + Some(Self::get_method_identifiers(&solc_output)?) + } + SolcStandardJsonInputLanguage::Yul => None, + }; + + let last_contract = Self::get_last_contract(self.language, &solc_output, &sources)?; + + let contracts = solc_output + .contracts + .ok_or_else(|| anyhow::anyhow!("Solidity contracts not found in the output"))?; + + let mut builds = HashMap::with_capacity(contracts.len()); + for (file, contracts) in contracts.into_iter() { + for (name, contract) in contracts.into_iter() { + let path = format!("{file}:{name}"); + let bytecode_string = contract + .evm + .as_ref() + .ok_or_else(|| { + anyhow::anyhow!("EraVM object of the contract `{path}` not found") + })? + .bytecode + .as_ref() + .ok_or_else(|| { + anyhow::anyhow!("EraVM bytecode of the contract `{path}` not found") + })? + .object + .as_str(); + let bytecode = hex::decode(bytecode_string).map_err(|error| { + anyhow::anyhow!("EraVM bytecode of the contract `{path}` is invalid: {error}") + })?; + let bytecode_words: Vec<[u8; era_compiler_common::BYTE_LENGTH_FIELD]> = bytecode + .as_slice() + .chunks(era_compiler_common::BYTE_LENGTH_FIELD) + .map(|word| word.try_into().expect("Always valid")) + .collect(); + let bytecode_hash = zkevm_opcode_defs::utils::bytecode_to_code_hash_for_mode::< + { era_compiler_common::BYTE_LENGTH_X64 }, + zkevm_opcode_defs::decoding::EncodingModeProduction, + >(bytecode_words.as_slice()) + .map_err(|_| { + anyhow::anyhow!("EraVM bytecode of the contract `{path}` hashing error") + })?; + let build = + era_compiler_llvm_context::EraVMBuild::new(bytecode, bytecode_hash, None, None); + builds.insert(path, build); + } + } + + Ok(EraVMInput::new(builds, method_identifiers, last_contract)) } fn compile_for_evm( @@ -468,9 +593,13 @@ impl Compiler for SolidityCompiler { } fn all_modes(&self) -> Vec { - match self.language { - SolcStandardJsonInputLanguage::Solidity => SOLIDITY_MODES.clone(), - SolcStandardJsonInputLanguage::Yul => YUL_MODES.clone(), + match (self.language, self.toolchain) { + (SolcStandardJsonInputLanguage::Solidity, Toolchain::SolcLLVM) => { + SOLIDITY_MLIR_MODES.clone() + } + (SolcStandardJsonInputLanguage::Solidity, _) => SOLIDITY_MODES.clone(), + (SolcStandardJsonInputLanguage::Yul, Toolchain::SolcLLVM) => YUL_MLIR_MODES.clone(), + (SolcStandardJsonInputLanguage::Yul, _) => YUL_MODES.clone(), } } diff --git a/compiler_tester/src/compilers/solidity/upstream/mode.rs b/compiler_tester/src/compilers/solidity/upstream/mode.rs index 16734c00..6c9157d6 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mode.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mode.rs @@ -17,6 +17,8 @@ pub struct Mode { pub solc_pipeline: era_compiler_solidity::SolcPipeline, /// Whether to enable the EVMLA codegen via Yul IR. pub via_ir: bool, + /// Whether to enable the MLIR codegen. + pub via_mlir: bool, /// Whether to run the Solidity compiler optimizer. pub solc_optimize: bool, } @@ -29,12 +31,14 @@ impl Mode { solc_version: semver::Version, solc_pipeline: era_compiler_solidity::SolcPipeline, via_ir: bool, + via_mlir: bool, solc_optimize: bool, ) -> Self { Self { solc_version, solc_pipeline, via_ir, + via_mlir, solc_optimize, } } @@ -111,6 +115,10 @@ impl Mode { impl std::fmt::Display for Mode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.via_mlir { + return write!(f, "L {}", self.solc_version); + } + write!( f, "{}{} {}", diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/mod.rs index 7f4f0e23..1b4ab09f 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/mod.rs @@ -13,7 +13,7 @@ use self::standard_json::output::Output as StandardJsonOutput; /// The Solidity compiler. /// pub struct Compiler { - /// The binary executable name. + /// The executable name. pub executable: String, } diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs index 7a9c3533..993d26f7 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs @@ -49,6 +49,7 @@ impl Input { remappings: Option>, output_selection: SolcStandardJsonInputSettingsSelection, via_ir: bool, + via_mlir: bool, optimizer: SolcStandardJsonInputSettingsOptimizer, debug: Option, ) -> anyhow::Result { @@ -66,6 +67,7 @@ impl Input { remappings, output_selection, via_ir, + via_mlir, optimizer, debug, ), diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs index e97c4c6b..16581b11 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs @@ -40,6 +40,13 @@ pub struct Settings { skip_deserializing )] pub via_ir: Option, + /// Whether to compile via MLIR. + #[serde( + rename = "viaMLIR", + skip_serializing_if = "Option::is_none", + skip_deserializing + )] + pub via_mlir: Option, /// The optimizer settings. pub optimizer: Optimizer, /// The debug settings. @@ -57,6 +64,7 @@ impl Settings { remappings: Option>, output_selection: Selection, via_ir: bool, + via_mlir: bool, optimizer: Optimizer, debug: Option, ) -> Self { @@ -66,6 +74,7 @@ impl Settings { remappings, output_selection: Some(output_selection), via_ir: if via_ir { Some(true) } else { None }, + via_mlir: if via_mlir { Some(true) } else { None }, optimizer, debug, } diff --git a/compiler_tester/src/compilers/vyper/mod.rs b/compiler_tester/src/compilers/vyper/mod.rs index 678d1565..bae7e2b6 100644 --- a/compiler_tester/src/compilers/vyper/mod.rs +++ b/compiler_tester/src/compilers/vyper/mod.rs @@ -55,7 +55,7 @@ impl Default for VyperCompiler { } impl VyperCompiler { - /// The compiler binaries directory. + /// The compiler executables directory. pub const DIRECTORY: &'static str = "vyper-bin/"; /// @@ -88,7 +88,7 @@ impl VyperCompiler { .file_type() .map_err(|error| anyhow::anyhow!("File {path:?} type getting error: {error}"))?; if !entry_type.is_file() { - anyhow::bail!("Invalid `vyper` binary file type: {path:?}"); + anyhow::bail!("Invalid `vyper` executable file type: {path:?}"); } let file_name = entry.file_name().to_string_lossy().to_string(); diff --git a/compiler_tester/src/compilers/yul/mod.rs b/compiler_tester/src/compilers/yul/mod.rs index 015553bb..2c635fee 100644 --- a/compiler_tester/src/compilers/yul/mod.rs +++ b/compiler_tester/src/compilers/yul/mod.rs @@ -26,7 +26,6 @@ use self::mode::Mode as YulMode; /// pub struct YulCompiler { /// The compiler toolchain to use. - #[allow(dead_code)] toolchain: Toolchain, } @@ -134,7 +133,7 @@ impl Compiler for YulCompiler { ) -> anyhow::Result { let language = SolcStandardJsonInputLanguage::Yul; - let solc_compiler = SolidityUpstreamCompiler::new(language); + let solc_compiler = SolidityUpstreamCompiler::new(language, self.toolchain); let solc_output = solc_compiler.standard_json_output_cached( test_path, diff --git a/compiler_tester/src/compilers/yul/mode_upstream.rs b/compiler_tester/src/compilers/yul/mode_upstream.rs index 46b58c01..0dcc3ae6 100644 --- a/compiler_tester/src/compilers/yul/mode_upstream.rs +++ b/compiler_tester/src/compilers/yul/mode_upstream.rs @@ -11,6 +11,8 @@ use crate::compilers::mode::Mode as ModeWrapper; pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, + /// Whether to enable the MLIR codegen. + pub via_mlir: bool, /// Whether to run the Solidity compiler optimizer. pub solc_optimize: bool, } @@ -19,9 +21,10 @@ impl Mode { /// /// A shortcut constructor. /// - pub fn new(solc_version: semver::Version, solc_optimize: bool) -> Self { + pub fn new(solc_version: semver::Version, via_mlir: bool, solc_optimize: bool) -> Self { Self { solc_version, + via_mlir, solc_optimize, } } @@ -43,6 +46,10 @@ impl Mode { impl std::fmt::Display for Mode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.via_mlir { + return write!(f, "L {}", self.solc_version); + } + write!( f, "Y{} {}", diff --git a/compiler_tester/src/lib.rs b/compiler_tester/src/lib.rs index 4793eacc..ff5c2a57 100644 --- a/compiler_tester/src/lib.rs +++ b/compiler_tester/src/lib.rs @@ -123,11 +123,11 @@ impl CompilerTester { /// /// Runs all tests on EraVM. /// - pub fn run_eravm(self, vm: EraVM) -> anyhow::Result<()> + pub fn run_eravm(self, vm: EraVM, toolchain: Toolchain) -> anyhow::Result<()> where D: EraVMDeployer, { - let tests = self.all_tests(era_compiler_common::Target::EraVM, Toolchain::IrLLVM)?; + let tests = self.all_tests(era_compiler_common::Target::EraVM, toolchain)?; let vm = Arc::new(vm); let _: Vec<()> = tests @@ -266,12 +266,14 @@ impl CompilerTester { let solidity_compiler = Arc::new(SolidityCompiler::new()); let solidity_upstream_compiler = Arc::new(SolidityUpstreamCompiler::new( SolcStandardJsonInputLanguage::Solidity, + toolchain, )); let solidity_upstream_yul_compiler = Arc::new(SolidityUpstreamCompiler::new( SolcStandardJsonInputLanguage::Yul, + toolchain, )); - let vyper_compiler = Arc::new(VyperCompiler::new()); let yul_compiler = Arc::new(YulCompiler::new(toolchain)); + let vyper_compiler = Arc::new(VyperCompiler::new()); let llvm_compiler = Arc::new(LLVMCompiler); let eravm_compiler = Arc::new(EraVMCompiler); @@ -283,8 +285,7 @@ impl CompilerTester { era_compiler_common::EXTENSION_SOLIDITY, match toolchain { Toolchain::IrLLVM => solidity_compiler.clone(), - Toolchain::Solc => solidity_upstream_compiler.clone(), - Toolchain::SolcLLVM => todo!(), + Toolchain::Solc | Toolchain::SolcLLVM => solidity_upstream_compiler.clone(), }, )?); if let era_compiler_common::Target::EraVM = target { @@ -301,8 +302,7 @@ impl CompilerTester { era_compiler_common::EXTENSION_YUL, match toolchain { Toolchain::IrLLVM => yul_compiler.clone(), - Toolchain::Solc => solidity_upstream_yul_compiler.clone(), - Toolchain::SolcLLVM => todo!(), + Toolchain::Solc | Toolchain::SolcLLVM => solidity_upstream_yul_compiler.clone(), }, )?); tests.extend(self.directory::( @@ -324,8 +324,7 @@ impl CompilerTester { era_compiler_common::EXTENSION_JSON, match toolchain { Toolchain::IrLLVM => solidity_compiler.clone(), - Toolchain::Solc => solidity_upstream_compiler.clone(), - Toolchain::SolcLLVM => todo!(), + Toolchain::Solc | Toolchain::SolcLLVM => solidity_upstream_compiler.clone(), }, )?); if let era_compiler_common::Target::EraVM = target { @@ -346,8 +345,7 @@ impl CompilerTester { era_compiler_common::EXTENSION_SOLIDITY, match toolchain { Toolchain::IrLLVM => solidity_compiler.clone(), - Toolchain::Solc => solidity_upstream_compiler.clone(), - Toolchain::SolcLLVM => todo!(), + Toolchain::Solc | Toolchain::SolcLLVM => solidity_upstream_compiler.clone(), }, )?); if let era_compiler_common::Target::EraVM = target { diff --git a/compiler_tester/src/vm/eravm/mod.rs b/compiler_tester/src/vm/eravm/mod.rs index c7eabc06..a9b6c1f9 100644 --- a/compiler_tester/src/vm/eravm/mod.rs +++ b/compiler_tester/src/vm/eravm/mod.rs @@ -74,7 +74,7 @@ impl EraVM { /// Creates and initializes a new EraVM instance. /// pub fn new( - binary_download_config_paths: Vec, + executable_download_config_paths: Vec, system_contracts_solc_downloader_config_path: PathBuf, system_contracts_debug_config: Option, system_contracts_load_path: Option, @@ -88,16 +88,19 @@ impl EraVM { let http_client = http_client_builder.build()?; let download_time_start = Instant::now(); - println!(" {} compiler binaries", "Downloading".bright_green().bold()); + println!( + " {} compiler executables", + "Downloading".bright_green().bold() + ); let system_contracts_solc_downloader_config = era_compiler_downloader::Downloader::new(http_client.clone()) .download(system_contracts_solc_downloader_config_path.as_path())?; - for config_path in binary_download_config_paths.into_iter() { + for config_path in executable_download_config_paths.into_iter() { era_compiler_downloader::Downloader::new(http_client.clone()) .download(config_path.as_path())?; } println!( - " {} downloading compiler binaries in {}m{:02}s", + " {} downloading compiler executables in {}m{:02}s", "Finished".bright_green().bold(), download_time_start.elapsed().as_secs() / 60, download_time_start.elapsed().as_secs() % 60, diff --git a/compiler_tester/src/vm/evm/mod.rs b/compiler_tester/src/vm/evm/mod.rs index 248cd9b7..4f3d8085 100644 --- a/compiler_tester/src/vm/evm/mod.rs +++ b/compiler_tester/src/vm/evm/mod.rs @@ -49,9 +49,9 @@ impl<'evm> EVM<'evm> { } /// - /// Downloads the necessary compiler binaries. + /// Downloads the necessary compiler executables. /// - pub fn download(binary_download_config_paths: Vec) -> anyhow::Result<()> { + pub fn download(executable_download_config_paths: Vec) -> anyhow::Result<()> { let mut http_client_builder = reqwest::blocking::ClientBuilder::new(); http_client_builder = http_client_builder.connect_timeout(Duration::from_secs(60)); http_client_builder = http_client_builder.pool_idle_timeout(Duration::from_secs(60)); @@ -59,13 +59,16 @@ impl<'evm> EVM<'evm> { let http_client = http_client_builder.build()?; let download_time_start = Instant::now(); - println!(" {} compiler binaries", "Downloading".bright_green().bold()); - for config_path in binary_download_config_paths.into_iter() { + println!( + " {} compiler executables", + "Downloading".bright_green().bold() + ); + for config_path in executable_download_config_paths.into_iter() { era_compiler_downloader::Downloader::new(http_client.clone()) .download(config_path.as_path())?; } println!( - " {} downloading compiler binaries in {}m{:02}s", + " {} downloading compiler executables in {}m{:02}s", "Finished".bright_green().bold(), download_time_start.elapsed().as_secs() / 60, download_time_start.elapsed().as_secs() % 60, diff --git a/configs/solc-bin-llvm.json b/configs/solc-bin-llvm.json new file mode 100644 index 00000000..04646898 --- /dev/null +++ b/configs/solc-bin-llvm.json @@ -0,0 +1,16 @@ +{ + "binaries": { + "0.8.26": { + "is_enabled": true, + "protocol": "https", + "source": "https://github.com/matter-labs/era-solidity/releases/download/mlir-${VERSION}-tag/solc-${PLATFORM}-${VERSION}-mlir", + "destination": "./solc-bin-llvm/solc-${VERSION}" + } + }, + "platforms": { + "linux-amd64": "linux-amd64", + "linux-arm64": "linux-arm64", + "macos-amd64": "macosx-amd64", + "macos-arm64": "macosx-arm64" + } +} From 715055a1b4553cdbfc031689ad57a54243c52976 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Tue, 24 Sep 2024 01:21:20 +0800 Subject: [PATCH 02/31] feat: test deploy-time linking (#104) --- Cargo.lock | 88 +++++++++--------- README.md | 10 +- .../src/compilers/solidity/upstream/mod.rs | 8 +- .../src/directories/ethereum/test.rs | 4 +- .../src/directories/matter_labs/test/mod.rs | 4 +- compiler_tester/src/vm/eravm/input.rs | 16 +++- compiler_tester/src/vm/eravm/mod.rs | 28 +++++- system-contracts-stable-build | Bin 965238 -> 965580 bytes 8 files changed, 96 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa2695a1..5a0cb99c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c35df7b972b06f1b2f4e8b7a53328522fa788054a9d3e556faf2411c5a51d5a" +checksum = "f923dd5fca5f67a43d81ed3ebad0880bd41f6dd0ada930030353ac356c54cd0f" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arbitrary" @@ -263,9 +263,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -501,9 +501,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "c-kzg" @@ -522,9 +522,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.18" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", @@ -944,7 +944,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era-compiler-common" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#666de17f795f20530423bb88c912efe45d5a092e" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#20f20ca069c9bfea46e97d2cdadeac10ff198947" dependencies = [ "anyhow", "base58", @@ -960,7 +960,7 @@ dependencies = [ [[package]] name = "era-compiler-downloader" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#666de17f795f20530423bb88c912efe45d5a092e" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#20f20ca069c9bfea46e97d2cdadeac10ff198947" dependencies = [ "anyhow", "colored", @@ -973,7 +973,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#7c1ba758b7f68c9391c16db766f69df47834f4ea" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#8ee09a90e1969f98b175c49442b08805a8ce474e" dependencies = [ "anyhow", "era-compiler-common", @@ -988,7 +988,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.4" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#62830743e1dbdeea99798e35d700f6a792c151f1" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#eacc6130a8e5aa815320b9f34707ff8f62c47130" dependencies = [ "anyhow", "era-compiler-common", @@ -1017,7 +1017,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#f353939d6b056a5f456af7ed5ec6e6d785031d15" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#0d2782ad10645ffb6212f8010ce574b947cb8202" dependencies = [ "anyhow", "era-compiler-common", @@ -1040,7 +1040,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.4" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#62830743e1dbdeea99798e35d700f6a792c151f1" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#eacc6130a8e5aa815320b9f34707ff8f62c47130" dependencies = [ "anyhow", "regex", @@ -1607,7 +1607,7 @@ dependencies = [ [[package]] name = "inkwell" version = "0.4.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#fd415e819f185f5a4b5ed60cff4e67814b9c9b53" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#993d3832f97b5e4127ab9c9cd2fc00b06d24d9fc" dependencies = [ "either", "inkwell_internals", @@ -1622,7 +1622,7 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.9.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#fd415e819f185f5a4b5ed60cff4e67814b9c9b53" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#993d3832f97b5e4127ab9c9cd2fc00b06d24d9fc" dependencies = [ "proc-macro2", "quote", @@ -1717,9 +1717,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -1810,7 +1810,7 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "llvm-sys" version = "170.0.1" -source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#a0eca916803d6123b22d9a67d1e7d2cd07227d26" +source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#a011658a7da7f977dff29cfb8d012dfbccc4470c" dependencies = [ "anyhow", "cc", @@ -2206,9 +2206,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", "thiserror", @@ -2259,9 +2259,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "ppv-lite86" @@ -2310,7 +2310,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.22.20", + "toml_edit 0.22.21", ] [[package]] @@ -2918,9 +2918,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -3324,18 +3324,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -3442,9 +3442,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "toml_datetime", @@ -3538,30 +3538,30 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unsafe-libyaml" @@ -4086,7 +4086,7 @@ dependencies = [ [[package]] name = "zksync_vm2" version = "0.1.0" -source = "git+https://github.com/matter-labs/vm2#cd6136c42ec56856e0abcf2a98d1a9e120161482" +source = "git+https://github.com/matter-labs/vm2#5e7f91d875a3cf9c7859e19ce5b709329bc7bd4d" dependencies = [ "enum_dispatch", "primitive-types", @@ -4098,7 +4098,7 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" version = "0.1.0" -source = "git+https://github.com/matter-labs/vm2#cd6136c42ec56856e0abcf2a98d1a9e120161482" +source = "git+https://github.com/matter-labs/vm2#5e7f91d875a3cf9c7859e19ce5b709329bc7bd4d" dependencies = [ "primitive-types", ] diff --git a/README.md b/README.md index 4e8c1a05..a802f68e 100644 --- a/README.md +++ b/README.md @@ -105,16 +105,16 @@ made, and there is no point in running tests in all LLVM optimization modes.
-5. Build zksolc and zkvyper compilers. +5. Build compiler executables. * Build [zksolc](https://github.com/matter-labs/era-compiler-solidity) and [zkvyper](https://github.com/matter-labs/era-compiler-vyper) compilers and add the binaries to `$PATH`, or use the `--zksolc` or `--zkvyper` options to specify their paths.
-6. Build era-compiler-tester. +6. Build the main application. - * Build the Tester with `cargo`: + * Build era-compiler-tester with `cargo`: ```shell cargo build --release ``` @@ -130,9 +130,9 @@ The `era-compiler-tester` is integrated into the GitHub Actions workflows of the * [era-compiler-llvm](https://github.com/matter-labs/era-compiler-llvm) * [era-solidity](https://github.com/matter-labs/era-solidity/) -To allow testing custom FE and VM changes in the Pull Requests (PRs) of these repositories, two additional tags are supported: -* `era-solidity-test` +To allow testing custom FE and VM changes in Pull Requests (PRs) of these repositories, two additional tags are supported: * `era-compiler-llvm-test` +* `era-solidity-test` If these tags exist, the tester from these tags will be used by the workflows instead of the default `main` branch. diff --git a/compiler_tester/src/compilers/solidity/upstream/mod.rs b/compiler_tester/src/compilers/solidity/upstream/mod.rs index b19832c1..4c0cf29a 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mod.rs @@ -500,8 +500,12 @@ impl Compiler for SolidityCompiler { .map_err(|_| { anyhow::anyhow!("EraVM bytecode of the contract `{path}` hashing error") })?; - let build = - era_compiler_llvm_context::EraVMBuild::new(bytecode, bytecode_hash, None, None); + let build = era_compiler_llvm_context::EraVMBuild::new( + bytecode, + Some(bytecode_hash), + None, + None, + ); builds.insert(path, build); } } diff --git a/compiler_tester/src/directories/ethereum/test.rs b/compiler_tester/src/directories/ethereum/test.rs index 04d8fa40..abbcb664 100644 --- a/compiler_tester/src/directories/ethereum/test.rs +++ b/compiler_tester/src/directories/ethereum/test.rs @@ -272,7 +272,9 @@ impl Buildable for EthereumTest { .into_values() .map(|build| { ( - web3::types::U256::from_big_endian(build.bytecode_hash.as_slice()), + web3::types::U256::from_big_endian( + build.bytecode_hash.expect("Always exists").as_slice(), + ), build.bytecode, ) }) diff --git a/compiler_tester/src/directories/matter_labs/test/mod.rs b/compiler_tester/src/directories/matter_labs/test/mod.rs index b9154bac..00cdc2e3 100644 --- a/compiler_tester/src/directories/matter_labs/test/mod.rs +++ b/compiler_tester/src/directories/matter_labs/test/mod.rs @@ -505,7 +505,9 @@ impl Buildable for MatterLabsTest { .into_values() .map(|build| { ( - web3::types::U256::from_big_endian(build.bytecode_hash.as_slice()), + web3::types::U256::from_big_endian( + build.bytecode_hash.expect("Always exists").as_slice(), + ), build.bytecode, ) }) diff --git a/compiler_tester/src/vm/eravm/input.rs b/compiler_tester/src/vm/eravm/input.rs index a53f0829..ec19126b 100644 --- a/compiler_tester/src/vm/eravm/input.rs +++ b/compiler_tester/src/vm/eravm/input.rs @@ -51,7 +51,9 @@ impl Input { let build = self.builds.get(name.as_str()).ok_or_else(|| { anyhow::anyhow!("Library `{}` not found in the build artifacts", name) })?; - let code_hash = web3::types::U256::from_big_endian(build.bytecode_hash.as_slice()); + let code_hash = web3::types::U256::from_big_endian( + build.bytecode_hash.expect("Always exists").as_slice(), + ); instances.insert( name.clone(), @@ -66,8 +68,12 @@ impl Input { .ok_or_else(|| { anyhow::anyhow!("Main contract not found in the compiler build artifacts") })?; - let code_hash = - web3::types::U256::from_big_endian(main_contract_build.bytecode_hash.as_slice()); + let code_hash = web3::types::U256::from_big_endian( + main_contract_build + .bytecode_hash + .expect("Always exists") + .as_slice(), + ); instances.insert( "Test".to_owned(), @@ -84,7 +90,9 @@ impl Input { let build = self.builds.get(path.as_str()).ok_or_else(|| { anyhow::anyhow!("{} not found in the compiler build artifacts", path) })?; - let code_hash = web3::types::U256::from_big_endian(build.bytecode_hash.as_slice()); + let code_hash = web3::types::U256::from_big_endian( + build.bytecode_hash.expect("Always exists").as_slice(), + ); instances.insert( instance.to_owned(), diff --git a/compiler_tester/src/vm/eravm/mod.rs b/compiler_tester/src/vm/eravm/mod.rs index a9b6c1f9..e175df19 100644 --- a/compiler_tester/src/vm/eravm/mod.rs +++ b/compiler_tester/src/vm/eravm/mod.rs @@ -130,10 +130,18 @@ impl EraVM { let mut vm = Self { known_contracts: HashMap::new(), default_aa_code_hash: web3::types::U256::from_big_endian( - system_contracts.default_aa.bytecode_hash.as_slice(), + system_contracts + .default_aa + .bytecode_hash + .expect("Always exists") + .as_slice(), ), evm_interpreter_code_hash: web3::types::U256::from_big_endian( - system_contracts.evm_interpreter.bytecode_hash.as_slice(), + system_contracts + .evm_interpreter + .bytecode_hash + .expect("Always exists") + .as_slice(), ), deployed_contracts: HashMap::new(), storage, @@ -145,13 +153,21 @@ impl EraVM { vm.add_known_contract( system_contracts.default_aa.bytecode, web3::types::U256::from_big_endian( - system_contracts.default_aa.bytecode_hash.as_slice(), + system_contracts + .default_aa + .bytecode_hash + .expect("Always exists") + .as_slice(), ), ); vm.add_known_contract( system_contracts.evm_interpreter.bytecode, web3::types::U256::from_big_endian( - system_contracts.evm_interpreter.bytecode_hash.as_slice(), + system_contracts + .evm_interpreter + .bytecode_hash + .expect("Always exists") + .as_slice(), ), ); vm.add_known_contract( @@ -164,7 +180,9 @@ impl EraVM { for (address, build) in system_contracts.deployed_contracts { vm.add_deployed_contract( address, - web3::types::U256::from_big_endian(build.bytecode_hash.as_slice()), + web3::types::U256::from_big_endian( + build.bytecode_hash.expect("Always exists").as_slice(), + ), Some(build.bytecode), ); } diff --git a/system-contracts-stable-build b/system-contracts-stable-build index eda61935527a401ec396b777b1e7227edb10eb76..16b7e15445e0563be2f0f84750f63b7fa652f4fe 100644 GIT binary patch delta 88367 zcmeFa2Y3}#8aF;OwK`o{7Ijbj3uDkT|v01V1zvUb=@a|pfuB*Fi&YTOc?AkVU)UA^yyU+UN z@u^As8%BJ!OX@GnlBB*krdO}$+Lms7|LcE0^4xJVZn}8!pg9+O(faoG{EM!V%zyG+ zg=#OcCv3W3PK!OgdyIee|J%pEzwqhWKDo=w`+s#HQocEIRC>(`br*ZT%NUsyJ5-Eu z{pgn;ZF=MJM{;g`=$pC&BL}}+>AK+5h*71L&5`}aY;wfUAMDp<>A=&h*T-5uF8b_& zvF}NzM(i4S+_9gYyKvzC>5ac%HavZ^=yLBpldeBs+1&TJA@e^N`u2!{559Qtk*tJs zE|l(jX7ml4{y943EWa*4{ou3G69iTrKIrpV9s?ept0Dfyk&Q`gM6yWpo2_D8OnHu~maEqm9^|9kGKvv+i61@gx$^l_d0w_owCa>|mL(dvCci=m74SK*il}-?48|H7CgZFRwhSpR<1eejNU`XKK}6E z=4?G>{#)y>-Px49XG?US>zi61by{C}*{{#*e%_S2Cc62OJF^z8&pdxwT-wjw)e$_=T@p3hId?ffl{^wy+&+dX&s_4&qkw;wlZY(}4V%6>iL;yyJGNN0Zb zm3(Gr!$D>D!k8J`o)Gi=?&N>XJ>}qsd$OM&URrzm`iy>s5x1VAet!456~Er}R_oyx zZ9|7C&8c*v%YIJGX2mTNM8hh%rJE&5{){%b>`B90C69CuUQd>!%;QmRGoJq;N#oh` z)8bi9F`l0>o>hkTFocG$5BTSRUF*Qla%^_MS$@*^_|B_C3~&;ZFi6Oi|e*$<}<-*<#>n zfYExZ@qC5x{8SJghhj8AmeuW+M2#V^o#U6<+b#JDhRKM>XxphQ-kuY;LyQJ|xdgss zH_eZ+C;SPFZHTvL$bJa3C)k_iv5gJC<)^a21*$9ts$`FesswwP`hrC~?xcc5dxGUP z>Jn%oDz5FA^yl5$i`PM+8B-y1? zK`Xd&z~iKkibMJ@<@iY{8$;NYG*K+ zXgl9PoAbYbwwVlViS%WPJw=58vvK4uE=S?GsUay)MaOzr4xFSEU>c;puw z#}DsS5l%n8X6Q$ksUM;CqtTr#x#b2FpV7!Pdyaf1&0)nHU+-!5#8_phi3Ttk8@z)) zNwcTND#MKT6QF`C(Ai7$*c}m2x-U0f0~LExx*a;1yuVtKsPY;)nyyc`t0}LDi6|Y= ziXODduzBQVD4!+ot5Dd=#gzHJB|lY(6n!ff8}bycUi=@b7pu5~>b*#3XV?qmU9wk`*^MD;A5JLnc`)oGgmfcvh_ZS+*Huk;X6!fPWT7Hclpn5!oWOiBdr6whBy1 zQn9s!)sr$=d686Q*s(pvL`8WD&Q5F zJ%>Zv8A0gixn8U?PSitBHqMWAjh^U<>Sl=>LRi;PUooO}(8x^t$uKp>Yeg96Dp_DL z7^n`~kqOJSgx*4{l)XV9W9EFQJE$xR25A>(rWsj*%v2}^bGnJaU{adht)aJAcSq*Y zd11Q^=38dMz`9F-Ig>iu?S}R%?^|jBjoGW!NYEJ0=_brQ`diJ=c1)ILV$^fS6{WZ;6C9nzR|Dk{;l6WbHZgYGu!U@c-pImJ}k&uH}QvNoQ#(?u6&T4U_x6`DG2 zLX#sht#MwSjczWmpQ(5~G;5JHj=n3fS11);@_6hXt-`D24;kVa=dB=ltnC;K)aZS5 zDk)w&D)iT_H1u6LmOM>%8+ER++ZtD<#L)P6=Cr9&>r1-VYPjmtP~g1xX4DtOB!SLCb9`fMR3BEmeAxX`|zalB2Aq#dwQK(#a~wx zis7j1W@Vs(){#niDM0}x2&6V{e?EdHd@lD<$~+9K!x{@-aMJdF*}hdOz-NB|frA=n z?TVwCDtm0A|JdbaV(hYV8b92g0z6zh++H1lDS;=ipbck6<~E)?RfC}H-Qny-u3k&Gg3>z-5Wb{SmN*uJ+f)z;X{6|a{vpHXs^iW0i5zj9JaMM(hmy3V z$sTF^l&E;BD^eV^y#QibFj;ZBgc9)-W61F{Ca7Mjek(GLhOczQQs={<(+Okj`BZy` zeSlm-n@8A30Hji~jkM<~&^!er?P+?Usfj%0Js<$^F+}(FdOba(&Fl4sG-Eiw?JlK| zTC$Zy-D@Zf;EXm7QL`*wFZ4yf$c=C}DUoXz2Bp52CN1R^n;q zS2mT}`dFi=ZM6Mmi??DB#ry;&dEg9tky7ZTf1Y74v=n*=HLlK%BhM0OPxoXcl02iK zve)mCqsU`dG`jU1NMPL<`$lKu-Y8Tsom*v%@USc2MhA$J7{!D{YAOG*`wscOp zI{Dz0Pi;H@qP@?~pD|+k69sEp$6R8Mi9qd3r-|ALDWhhseZ5cAw6{L}?3S_DW~_Md znD>gBb`SY3djC%!wQihdzabK}OJv<3OxYUqR{XQ+#UtOp@Y2@W zTkI=_q4rO2*ar;2ytmCRc=^`Ib5E)4)fL^V{BLvGBlb1X%pWRpb|3!gGrnb&_)2^^SW~E1P*ppsp*?-EwFCV#dUDC4b?=y~Foqj<3=FD5|r&K)oN@45M z=@Fl(sQo~$sNL_EZKrPD^wOO_54LHQpWJX@&11tad~kc^?vJjSIPB-vL%9(J8~mb0 zI}TiYqcZw$&rMph?w2vgE+69Acj)m)M*Vw2|Jzr*et*ltCT{#~5f`4Zxp&Dek!POO zwAWRdb=%W-r+ji;;){;G8Be^}f61v+5p4>B3%3Cja+MfkVkwu)y-5Z<#KA7sg|Xzx478X8`~rg4{c=N8A}GohNe38$yCQ7sey-t zE-+nH;h8yFsH09TkjtB9sri)NOYB*TX*TjoDkCQ7`OQ+Jy)oQtwuDTqP+wzA|(*QhyE zFh|`|VUA#U%Z_k;Z8jzv%a$*EqVB8IGVogCR@(j{4h96KQH(pz@;^WX*OlSln`#APY z-`LT7>{r1|2xP|L{d~;H@i=u|r_NLjMt5u?j~9%(@Om`{vuB9O9B%d?iym45I!4An z9-ehPNuAdNf1IC-gG9aaK{bhH&Q&8rLKOU^i_;H{lMPYpl3#SlvT}BGme1JEG<3D*Elj9%BbxmEuE>1&Xct4Sca;0PCl?QR`_vC$X&0N z&GS(e%Q!~WL(ollC%Kg_v2M31>^=UK@MidyDY$^+5$_8F-&;np5R2ngusEX->d)u( z?U}~=TL1U4QijFDBNR4^N2%kaY^#UPy3`y;x|lDfCpXPk(+4@atX!3%l~AFZ5Ep@z zI5x>OLRzFuR^3@|4l7xC^J{7Be05~rY#$PMn8OLlVMuW{8HDmFQtFtmX2}1buK8+G z%sP{dCaS5eNlnT-3R6OIEgYqf)7Za3Rj#7Ne^V>-3?7xLqB(*c1_a8>#vry}UUAQc z3U(|c_YIhePs;w&sVu$$Q+c7ssYpM91YluvN(h;7+B;q+_l;<^glcZY6bwb>XJkA% zT0T8=qZ$(-fOFDgSF4$sHAfS{M;#jB|DO&Cn zQ8H;STiS| z{%Vh^(z0vSNX9~?J!7zH2}f{Sn9nWZv!|@**6yXeH5b~q0)=*hPg4H89dI(2&j>Wf zL{s%uY9dTJHwVjZR+Eon4iJU!yjjgjX*PHi4vLf}#e!3#P)d&X$bE~NQ4!9i*b(ge z1V+5$hLXEeO{80IQN2FeH`YMpk7qrPj<$Nu?t+qDwHAM0<4ea1u7_mhHy4P1Si>ftsuY5YG-tSTl}xkqC)ER=~&V2f{A zSm&8o^#ozH$}O;LtzuZl5~NuAC&zB>am>ePFnPh}(l@^ZMFQNd=4RP6`{cSE2Ctet zDA?CbMncnwS5xinP}Pr9M=@Lq>*}Vt+FAY0)rO(!82Fx;GOXJC&K^aUd~^VMWV z5T8F9o&W@I*OZg+eBV~hyzpz)7?Tt|iQEb-R7^V)dsI8qvO+!CSKV^d>h3%Js%uBB z-hk@h3vNgD1=x3Db`&kVn-Im3%Jh=Sdc6$m02z%Qr`u1@>z zI+A*J-wlc{qpG{HR&yMpniI9b{e8;<-*gS-70N|zXdX`2^5@Cs7X&)<=Q1ZQgoFQC0^h%T@le7w{L!J ztAOh5)QR?C!wDhwnmo-IZYYgyRx{LF3~Tu;?<6Ix(Wka%HNH2CAOOf{fE$4wTEqhY zHs9!m+3#vbjKl12C%IN1VNzxQ#R4NO;u8Fn1XlXB4`6avQS)jnwyrQWv#o|{X8QuF zo1@NwHmN-o;fa~|sky3Zsp4UY$Eo=~wNkxLpdAbEgZHV+<{66^Of!}_(2Ocfqc{%9 zReUw6nn4XL_IOZ3iu120Magd7`-_HWVzdX;C-7Bc;CmJ0Sil9q41##z2F0=UkD)>7 z84Yg;H0%vR!vPZwRR{p6yA2DxjcOlM%Q)8uF%feNRTZ-Xz2eK89>P06wq9P#X>0o!<0_ znnssAtftD(QvJhf&QU!~&BThqa^0-f`7mg0j0alhu7Q}=Q0*G^Y{=)9HELgAApSJ$ zHNp}-Nk6VpGrZdbXM{B-ogrGTel!+27p~>8n!2@cm@(jLhNUV|73eZz6ZA*nANjul z{Sor4Ho8igC$ei{!wi(K4B6Y3<)pv5LJEaK-^xgr2VHNZQ5LQF(^@6PB z>(z{u8bMZwp9mWql54r_)Hgk>CRO{s$|pi19Ka6OgASAG-QTnEok>X0ciOdH9jQL4 z@c4KBaaIZ!^1u?^t7la_auf)m-_;@QShH3j&P%^n6N}YU<#xN zL`H*MgCx@M(%VLB`QgoJb0`;Y1m~I_matZmqkE;*hR={0=Cj3&HTYYy8I&g14|^mTAT|Pc&ki>v2Nn9?1z9Y*HGd2-Tx@%qH2zBVRsCLM`WM8R~l2EUeB@%ln2U zG1g(6CaLG=rg&ib$H(Cz8sr7={PYIs=h+0=-bi)lYh|=*lj<>HSs)=S2e=&Hh@P=( zJh-VF)P=Pnn5eq|@;vwnF3+6Nhts4d)O6;fEPeu57)~uu2)^GX9=o1^fixL;I3;fm zc&yqC9c1$DR$9FI516}9J>P|9?4$|fFlLR~nS#j>_D;qAsIYe(PePFnH#n@7x}M}R z5@z@3kI6{FK&<dsWS#Sp9mHup(>>Gqq^NK zPx*Q22}iB&-o|Ys6Vx6SvJKnRq|9ocGWBu?;#H}ZiA5aV&u4hRQubLjUs0cLdQLrw z20tyB^=TMOh}~+2j4G=UpEX>TDw_z7z9zyPjy*PhMyz{Bh+3Xj3;g8I^*m&{qv;v7 zw{R2O&j91)RP#)?@R))cqL$d71?@Ryq=-TTj5-bm-ZLU03@pKpe_o9U;P?ji_Lk?>*lv>&0&4CH-Kbp&J<+`>P{GxuL2Fty(n{x6TU%a?xr%plp>SGuLjE(UbEi7eYxoJ+w)$)G+5Dm@pB*nU?b5Uh${jm< zyL#j~9$T8Yi-(_hh~s@8;>cUqvp6pZ<;dFIFc-$;rPQP0y=k`}+N(ln-i*$CED<_Z z=k9K*bZZ)OR~cKRy#~(oJF^hP6$_g~#;beDufs?)$Ve<6id_@F&E|ZEI$lyw8Welb z;4Mk=vh&T|VN|o3_})Xzrsiet=o!5tgr0;$4r^Y9Y4}iBj_6YAd>Pw^@J4%5q6S1B_@AX+2Qx4d6^WWh{nJO1y7UqBWqKomS7!cxtiS*i1RZS6& z6T2A~Eu}Hv#F)do)bRH|u+61o2w*8H-4Kf)(!Uu<`k=4>iCu9q#vPvnCtv9IZT{VaK zhB#u$7MykO0>;VY`Vz%@YQ{(T8S1!43v;fL@2*$ArzT^qH1<98R4*y3L0{J|ZH&gO{b)@NRNKTNI1%)JwVok*)v5~Pq zAf(&l;XPwlpbBvi*lrxGs(BykT%~#xhKhA*xsa{f(dDz$_WoZAy<&4eDQ}Abrc>q< zR-XrrFt(u~x<}hewVnS_Sj2MxRKI9JYkF4q^K2eC~dSj}< zw_`hbKKcXH^M#q9a1aT_w9P%5hL6;75bUmxu;SuH5{>&84#0>#q-vk61p#K-lQ12d zheNRm)bTIyUR%@0i0!PGcu)YM=KdHSwTewLFn2TprJS6)5!LCU+K<)5RM+pOHGptH zWl7NpC!v1SPt@?SIzoy1H+6mt)FcXe;|v)89OL+Jq2{Vj5J_l*)?f#wrN~%mI10O9 za4d*;RMQ;Qu}S0S`=Fprj;J@3)WJ`n%KDfvif|bY$+{0>M-?(XUzh`~;Y<*ty;O4$ z>hl(q( zz8~V&C*~hLm1;i2fFDZDY+BKmiGdv-izD696RG|)s4&A|J3geA&)7o1UdtQLrIzIy zYuvyaFYqMyqEra+O zA`BUQ5MjtUN4G79zEYDjVNluuyAjC(k_@7+m#PFf%NuY5xbl!1=?5W4-NZv`vigvL zy4?(QMtpD0App3ZcA-ewV6Ef+gCUDBG{cygirLcC1+|iHFrLuvHow6Q3v6fwgNPs= zr!@q;wo9z{JE)|Dq!~X^e+1?XGIOZP#q?Dv4 z@NWe+qTiwNZ?Q~$oa(=Yjccbh-@?YVi)jH4t{Gw%P}lEjcC71ZV!jLt2B9fkuZRs`v~oQ1>0qD4a?y-vQ*E)QQR}d=kb}t{0i3Qs#U7g=)Ts zidb)IJXi_9&O8`i`xmPJ9uqShnxs14?A!2K#)=n>QAGX#dfO?N6$;nTaw1LoL7gYR zLR}AHm;Ue%FfV<`^$;i@lfgBdTtqcLs_(0Fga;LKKh^wVcu=viI}LF+(Y2GV`U&Vd z*F;y>!#F+`GdT!fQw6?aCIsPY8rA*`d|i8Fe4R*LKLe;4fG=bEK78$L`ju0{^DA_f30pUJ{~_U|mR|wG6SR+YwVNV;Q{RfY zN#HK_I0Ic#)BvY6=HVdhJxsa3gEBACu-~z*5c4%%^*d@IsS7{u;4sY7%hd8ayt-G+ zRm2GvYcaUWZ7>~Vk;x4H3FaJSAp5r!^?MR1t~@y-hbdcM$_v9;pyO5^bw@rd=}cs8Lss9Dy|uC6tJHj#tp`3yO-Qp>g@WA8U8U`i~; zsb=3z?H=A%|?AA!9eW&Vi@9~l|jS2ZVYp$F$|O*uWv&07P+k22+i?tjBpR!f-yA7 zs*RH)X_plN2Ap)XX(M9IuTrE?opCiZ$)>RbZ-Jlf#^>SWvWw4K5$H2xppH*K-D->x zP4#vyDeMgrNt|p2P(e3DY$7_bQL5onohZ28@i>G8-GY96Pm68N^5}-mUZe0ezpa$} zt5O7C^2Nb!t_I40cxi%I^jj_#Ye7qiv=(nwaiawN)riG@1Yfk{!_}-KUznx)@DkU| zae#h2qXkC^0JhMS${6zwYo)4D90D#Im=pQy!g_n0IO@Q#8D)Wl`X^^Uy(=GGUkQ( z%(`csBJhanf0p$Y`{sW{&O0TP^(=z*9yP=tkoJH84nI%Mfn|~cc60nEH&AmOT0!P& zL+=1$WC-iV>>&~CiF<~wa%dS|(_otEU}VVVREWsqqPck^C~mV*JB}x)*`Xz7buTLd zgG1d5cWQqLr%+5pGsN?SeG7FupqKWO+o^>=QE47t^sR<{w?B~+%}urE$WcmMr#RCQ z_pS-4Nc0l-A!aNFku#T#A+ESSWq*)dSk6zV;n-_OQeL$C^8NQ9vt6NIWNDXNxb}?R>T3VPtdM7 zz_g9J;&7aa9Z(KDs_74y#10jM+2da{0R4fQ}kb_6Q^*FbSea58b3 zNfVQ_**b;`)!DL9+$rPD_PT)T>-tbN92vKE5Z+RxQrD%^SdGAtP zs+Q4%sSMJ^)RGDvHHo@VTfPN}Pxw;sSmiLO?Q`g0U%^2U+TcksA1w`hGAm z{XPn=Atr6^*yS}4?&9ZXCBT8;JsBHYd94QH8~ZG%SKb?wK6*85MW)C4x(l3GW`k+C z0`G9cyz?|*xRce{TRST@bk-(Z=AEo6@I}y_th@sXzs#L-g#pS0vCUzE!b5A2Gl0^W z2H~7Rk?AP*k|$j=E)m?5t_55ofQxC|x$~^la_A5~r%Vb`M^%s#DB`?H;PDR2 zz@@CLt(?Q4fblO4thCgmgV_d-yPmv8PF^J>BkJM z(g#t{9p1zzO~`$N)Vj5t%-QB3;qIOr7h&ttwcxAg?o24bV36HDkXw!iPn^Rs5{qqD zeh0SC1tzE_{!QZXTj$|viz5~3M53 zL^&UGg{~wXptkF7p>0nt^di3HhI40XkmKSAJe-`zosda+z{xq( zkO%%bi(1&z#ngEPA~FAQj*zx~=;T^z=#QDrVdyD9kg}`4HYdiAC@wNnB`N7!jwmhZ zTSK|*rxnNiM~*2U@-aSS%p(T-8>ftu;EiDH3UlnES<}!>u7+7qf|Ua`7sAYeNDXvK zp$m~$|ii}9HbnX>0<|< z*iuVAX!TF($k&o8E)G^YhH(zMTHgb&xeK&1U+3MdX-E?8()($h?$5Z1pTx)*#JISK zUmDx3?0p*0?dh?)I1%U3T-70JB*;!5#ZcgI4>FMCE6KyieZ4wZGfjj@MbfP)x&f&5 zhM&>HUM*Q(N^Phz&u6mDo4Q9%(22uX1yVvv)=ESz5I+pZIPB! z5u)J?E)2?o&;u8?6(J_-V*-_j`As6?dRnJ((1z!S=%G1s;rTJKIPn67S#>>EnBAv* zGFZ62SUY>3;omvJobfTq6@13We2+y|z!@KtTzDUF#>adgbjC-b{cos|O7h3lRsuVA zz(A96awQArLUH&fHfw({)>-<7p2QB>8+sHwq;u%0I4t~{x^v|1I z|HOpoAHx*mtXzheDF=PT0kxx^@iGpm^_duq6oI@2ZL+v9VBGnE zn~UrUe!45fNpM$aN$2DXivzluc>wqZB-@Q`hgb$@S76@Tc>ey6WmgFA?U|muJJgd8 z%&w5#?ZI?wFQixKfl|n>P})CK`w9dtbcY}a=@!znd{guhR@C=%N5&qj@mwS_Q&hf5 z7~R%~bj#8*bWb`M?@d=~N#iq3vGe=hw(zkeCb66pK= zsTG0$t?ECrAPQU^$kVCgc&u2l1_9r&03bWv5RdY|k%S~~I@K)25z@JXwaigrwC94V zU;+g$hwz&RTyPSg`vVC~U4wh#GU8UdtWrx3J7n`whU^SGWOHTsAwR*8n3p4)4y{FW9zZl{K-@H2a>N=pv)p_;10w>Y^vJS+>PBco5Q`opjw14PC-VXX3v zoq@w7ED$aQvsrjP#4b)ep7wfs>YlCvBwYGu=s_(>j->=ws0mWa#TDT7m?92;~WdqnWqInOAu) zP@H!?)!wTj$NFWyn4ftExYzA`bq-=6=LI`8 zgftg}WAUQ}eCgZ^egWevEc1RmRa-W>d&`*TyU4pIo8a!bfFY7lYSkbd|7(MuvEE`7m3gCJv!dRYBC#uVr(T^j$w~C;S#h{We zYlNo;1{FI=nSk$SNo?HZU53=)fXBFYj>q~MgpdE)t`&2K#$Y$l7z^A|I|`j%%ra)2 zr9g7KM)8dqq|B%}6En|7vT5wT9wV45tljGj(N9R3krWeRkId*JOI_gee}k~a=Em8e zwh7>y`)fU<+W2EgYd!HeOB?^@`0nsgRU2bUtH*N_j3xhoNFi}e0{vF2ouZx>C|BEP#dvLE561;~w4J#I7io!F z)(0kYQ~gE2YZlH6Tm&hgn8S4kOPVGeVXYCAiX*f&)uFk$LnqHs0^^{DZ;&^II_81lV7^xiPBQZ|bxi;T z@LDjKJs|f+9^TJqbTo2rq`xCFI9#H|K+CsWg77T_mv>lY6`?fKefke`Z&2-}nsGS4 z{!&nA617~~z5i(5^f%pd)APxjOI?@3IM4OZ#ld%q1yq5lIJ$R={Znxi$XP5C$Jt&p z%==d_`XdoX0!^Q!C5dAl*a<2aZmObMkx9C5MHrR|%E}Q+^rp%Gw`iU*ozpyj3a5G1 zl)pst9%~Zq=#TqIkb{HIi$ZJ3nkM!^h#rhCEjT9ZG;`QsDCH?4mUv=95 zHB7$_Omm;JZu(!sa@b5Cfs%zJ9~tw)pm~85j5nX+5PMX?@uc8YerK5WZoFq)?-o%4 zn)`wpIVjw+T?)z*5-JDy(GIOF9Iy0Xb&)ruJA8cvb>gbil7@G0ARaN12-7Eq_6$fQ zV(J-a&$>G3vZ2&mr;Yrt>6hABnsN4zsTR|Snn~(IRSR{_0^=LDuG{#C&}`0SegUHH z$al?UT22l;Xs+VT>;OGSE~3rI#ea4rX80WLGQ^rc#Lx!^j%8-4f*30}g-H)A(QbD4;XXZ$8w@1%=9~SW+XW=HzKhk zH5-PlugGzdhr3|a+gYCC^jq#BGhpb6CU+nHu;8TIOU^?xiMiS;MxRMY^yw8!Ikr*u*Uli@n~stZ@nd zo?&x|TOj6l9FE26679|w?Ny!=!*t>t^UQ4Ian`D!lnp$zmI(zko^!q3%Ce42(nPxz zt6k`Z%dL{?fz-yz2tEZI+`59Pio2}=68G{|q$)A=pqd>T^SoRsA=o#A`I51|~P z7heJxACbDx*CGuaLR@pny#Sn=N979;%X@?tFVJ#&8|j6BZ=L|A&j7K)W|Aq>wv}ox zC993rEac}i4=zNE++?tz%rIGdsT2LhylUt?C_~1UkkI6X+Dw)GjrB_=^M}%rgvmlB z4rwIGbu?)y_GnrOsZY`erF`lmHqU+HLzO}0u#?$JolF1om|veesP1OO{GcJ*nW8Wb z$M#tr5cBKvxsY1N{=YU@yBO@5puv#S^x8&bDP*nNKR&2MBAwgT@TDk*gtqIeiz|*xnoq zf;Aw(pC(WOsq1KfoUJpZ&UG6?nmMnM=Qg};ps}}sTbHsY{6^JnIC9_d`Fv4b0s6(+(^IA&HDyCM`&_^dEnZigCs%pkXU?H)g!csrzdyKxM} z&~8!;_IDt4eUXl!5ju4e)irAAh1rG@%zn$zdeVt~xrZuACvZG>a@%g6r21odV){ND zkSC@uW;ma5f{`bt@BJ)KOt#kde#7qyOcc}isenW=ecOGl>zPO17r$M&95li0lT6kr z&l6Lc>RW5+A!@s}2?jqzZPVhWfCS>f30*px{Izuxfl>N?Zt@MZHI98m(^!I1?)!aU zEZlA}=+<*Ayq_du;eCHK%z5{AI3;U~g1jLmMl&`Z`;DfmyTE0qQ3Ku%Z!{R+;OLy23}!R) zAO#Clm66XU+UPtWB}}3wvas=E6UldLJPWm9Q)}*q9A9Ztso`$SXa%*Pjy#{!h+WL! zT+^MXWh`4*^}C7O_du3#CaWDx1;4e5DW85#e}p7u6$Jqlv7-Z#R4EKxa49_eE+m|Q zl_C<=K^-eG+4aZu-3>Qrz_0Nj3K}?CujeZeF-ot5CAvZd!$X8Z$)_mhSFV zVC-&{^*><_i%4XJBP<4Um;n8_N-M~Ng63#+n9O1L1<3f$ehL`>W;5lI^AY2gB%~gi z)C^{c;mnfvTM)DSI~=n#t%hdzT@NLmuc&M_G7qdYY{M?9L6wwMyaOmxA659FV{mf) zYOVszxg+0D0zHE!Y}=vx$9EY?S44^*yR7Q#%T*?F`l5m3u$%ZJV$(W*%T_!J& zlJCdP^(-^H%;Nj8-kL9157Xf8X6_r*aX)B%oq;PO7e?-zBHc-LBY7UsCSrqU7KHe&`x>O4I4nAarFSDz+52)_i6GMbx-O)&8_c%5R@ zGV_v2kuuM0g3n04ldfbV=MPJ~ll>ewB1-mKEc4DK^vpxrHR^4`HO(1DRV=4Y?)QPX z$^9O`(xRQDWdCj=2dQ_;Vz6zUiHZ1-bDu~ClcNS<=YFbN1N6HEa%MKIg=pjF%-2F! z=ZdO1@l?B3E7jP*3^kZY^IC90P=*)3&mC+1=4qk=^K?A|<&LuI5iK#*fCCH-o=;=0 zk8z(FDIXsRFeIE0H$0-bRam0&n1N$Avy4 z?Q>g}VgGL@a z${95FQDE@NBPz$7?Myjl$L8Qzcssf_wPVi9e?&Xxr!qsu<}F!-X_n*&{&Tju1qK{n zQq=~qQ_iOXkl$$W25nwUw!t_)0DSon0&!Q@0zjgt)%yh_=HhoL zH^HAnCQVn29>|P&Ivv~y48BB>j{^;-1?Dkwieny*Bj+*7y&#Bnw^7UEkZa@gmzm=x z_h~xF#;6OM#i(i%R<3tb{U+49m|8Z0T2~niF@b?lregLC|A45coKbn*pKG3gY#Xqe z3=LQt@LJZX?Fncuo$-{LetbfU(sGdygApkAC30=nMpm~NbaqQT{*#O6`HX(D!jlo@ zeq&(KR9Ra^6oFBgsrB5iSo)rfl+DI;4;j^s>^-^Pup~ie4at2eXc#>E50i&js?3Hp zhj~44m^_OZW{>D557gq-jbZX41TY^4je_TZQThdq@mrg<{h6vs;VLIk3%=11eJI*wt!-%lgvvUEX@FOb@DE1dJ3DD@NQN!!+ffR~;|Yt%Yav+`5ki+55fcGuZly|Gb3;#n4F7ogAzxBX zH4lm?uJ$y8*mI%=Y#`e*)kiHh=|a0E{u znY9W-dCS5R73IOeF`i-n0uvQc+cWU7^V$s_6A6I|^PUAl7>|W4E(2qNej&u51VLcA z0`Tf0@aCpEg{K1%H8jWVqFFWWV@ zJOF>DE0!EyDEINg8pR98wc=?aFZA0)v7Q&sOL<{i$&0utVGDY@P0N%oWZx;Z9e?KW zs?PIJWYD$kn8G~%@u_yLTINGh>a(bK0k3EIm4Cd3y$Kj3@@oG52qP>1)o<2ud?G(T zkBR)uhqfN%Z_a%IZ>HkU47&RTtx7c)iu$hWhj5LoyrUA)1vsD7wSE{u%w8Wf9Hk1 zU%Xr{o@#mFNDxn7@IuSwg?cM5td+d5cvvAv@;PYN@OO?rywKm^g{@E&cZin)Kl*n5 zWEFsGpNJ}gnAX$zlf%LbO-$N)7Jt$OOW1^bYVY%x)=p78A*wteim~G5OT4fNj*7TQ zJPD4noxq-e%=r&xD3M}Dnvmzn$)e)TywD?fp$a^xkMJk0R1|me;!iW%|7-By)kOba z!e>Mr=Lx&jpEY8?#54ZQ%__`7q$_btco8c)ixJWuvxvWp5i%Te zns_=z6nBear6`v1B9NCx)W05$9f9ricFA`qjMal$TWC`7rAQYp!jlWEsB#LpOikMf{T=B#y zOR0x2Xu^~HRp6j$3wwAOSmnR@L~nnMnIsNmgVHtMz(fh9n{_3hMsfo$5*`xYS=}t5 z*^)Q&FIk+c5CZb%*izsO*_EVd(d02+Wb&3uim)Rog2Y)|^(&bIqe(xAo&?2Gg(^?! zEnW&rXK`zIL@M=<iJElLbtPvAoa36{2{a7YTxsQ@OE_6S*}?y^wvP)Gq;U<_El5 zR-!0+@gnnkULvD`DwUh1{mv*-JTCG3uBzzk9UyvGA}X&)-pN6)6d_aRq8Y`!biMtx3U79 z@bzmEf)i4z#0LRPs?ZK8i`h#$_aNdOgZ__^4Z#iAH5icO*r znmk)bRi0Q4-jdl-rjn?LvOP* zI_3CtS5KH+cj@J0vtr$;Z#}POFa7=Sn4z_o-(>mn!u-zZ^S(NV3clFePkwgdPj5>7 zWm$5{Uvv#TyK~w{Tc11b`r)nD&U>cygvZJ=o<1>QT4L3)JQl$IPhRqXg+{$;k86F` zrQc^!ycHDlls=$!`@@djvQk`1Utg&AZCdN-&Hips?D6DI)UU@w>#9Wk2E|fZb^-0} zADK+Yr|4@~os5qhsTBK>BaY%z^&#}@WT%5JPSvkv-#SzEs}yfBo!d(vsCdh1E>Po;)al&D5m zlAWeUIeOEk-lF>6?;NF~dg0T08clPlDpjTFaf-K;j{jFgG7<(kt(0bQBI$XWZe=5Q zZ*eA2@9!L$bm%Te1bz6To{UR?dI}AGQFqXz7H1i~yT_4)8^O-F){D~hI12*=NU37w z{O==^s3TK<7+tkqou&7;vA6E4qT*?5j-DhJ)7~82gJS#Xw)9qauI^>c8|Pn#JeSpW zmF`j$t@X8I^a74*`8>tbSeG7U^?J}t^+0_s>#e;2!~e7b@VN8!zDj91os_R{L7(mM z&UG~KZKr1Qj4SaJ)7ApLfPoZqvz|)tR_JFdo)X$PLhnVc7>9$t_|4&>r;C+HYW~_5 zLGRrV6+^v$$GcU7^e7tiyQ6nw^Hn2g!(BS|Z6-S$t>=05c7@FVFyt#`CG>&Q*^8#u z*qpRqcIvceovzZ7V*NBWs1NnFX%nXBPip)iFWFX7e8B)WZM@Ews+1Jdm=gU>Mu-8W zg804`1Y#A^t(T%VSAmki>Sk_NlC6NMnC6!%sdW2ijuH4#?GHY4BmvgK!cw}qOmxB- zq-C&fC3g~F?3#|hW~MshsPkPgP315>iQYe;d+Drl{Vg`lf(re%U>vn>JYOFsQ`MzP zNmIOzUl+esKe_2RJ(q!Q68eL$BI2q3IIvks8MUv|6KThBda_zlq2&)5;u%*#d)GR% zsAi;KssPZcX+~mX6gieTbgkT5YBp$(bYur0j-eq9d1S$%p6?wA02H<9)WJPqD~%?8 zszlTAgLMbP=r@Bw9{|@AGS02k%NZb_k8+;T*mbTIXmo!U(Z}LJ{f$345~=H4plm>u zemS+Auj_Qm0X;#3P!Dil=EZ*`{|reU_pTn**qZ93-WwfBvX{JfIf|V{nmT^M1eI4S zq4B3%lWF7*-A=Dg2hz_&=60Ij+nPo-J9Hau_!Pq6CHX-;la5Pwx~Ss;jJ2v-zZAc2 zvOV3I5#<@@^%e`X}MhQfaw4p<;|WV}e@5Cg*L+*Lzb<1!%Tr z5oEe~rXC@eQ1w4;X>@3at}2D)^z~{x=Kn-wl*J2(-7~C7^ks(A3+#HmWi<5!{UWW* z9JZY1N2w`|9jRJs5ulqgdwQK(#pzpCWY7&08uhs!rN@^$fx-#^yXrjsxW@J4Rmz(m znM!pBo%l()dflp)8@(4Y7Kx)Te{w|IF*w72<_}O;M`T{bQ%?0iJJJNc>WAqG^u&q! z2)UGYbU7BdL_g*LrGw~$HcaQSll1;RpbE<kkgdk!7#;z^wqD9d?)8KqmQEh z$|NuDaehVS{FV*ZXUb)Cc(`6lt7q$63Mv&R7;`Sf2fWJ60{CL%A!jN*r8tLDy~Ckd zyqIu%fl&0TFSI05TRJp`>d{Aor@^~Zn~GJ4&hHMb_0d84wWjgndfF7w_@6u5SFAg;9|u^p81h+-89uqnwEHIW3k>(^_B}+@KR=;vwtug@*8>ueLWJ! z(3JV}YV;C6Ku68h%jvyjXDJ6%@s{||5}-)j^vozdMKe_h1mCB0G;;<;g*56XXpC+w zqVpnJIa)7)WNbWJ=bX@OR;?d9lj!&}^gg~J2lsY*dxYShy^RT4yk(pc@w8(Bv#oy0 zpI#lKU(5#b$4XfbQ>vg7vYaIbykOCTj&AK~&Q7I)KY>`Q&VnHx^}Sz(by;DiIOA*} zdDaNQQr%p1JXMWFAHG3+^y&#&6%=)jUQOTR`msc6Ug&_FXiwxBIDv8-HCXUwTK`a1 z<}A?K-$Y=c$J|!C5a;U!g@De?qmR+o;GNm=Y1TNZF`)oRM6x!dGQ}VQ1}^pCg{t3qJ<7K ze-d02Ac5UpN(8O+Ivt{EsIxMJ?!8oxj%Du8Wgh%jXyCt)sy@LFew|afDscV-B@d=O&=ZQ#O5hE%T?%@G@r>NHnd?=|Qow%y|~vV|#I}J7YqodhyH&mz-C1Ld|gS zy>L}%&%3rns_BFlfm@P~pTV7#uP31|xQ|&_vb@#l%%%EuR;y{L3rouBf>cMJ0dO^# zV^ybKRs_~5!GA%%h>yOd!u&89eE)c7F5n4}EwN-ug-cdB81lB^8i;NwXKbGW^wRuu znJeJ4xm;28tu(b-A=UmMwAy=@1#?e#_uF4}M$!6%SQ{0Vi7EN)QDIRJ%Dk$@*la3z+XNpZo~!gMrNT=;!8(OHb!}K;Qi}yT^`%s-Y2Evs5#)Xo?sQ!aR3ZY3ony-Tk5r3MW&HVhj0K{^}at${aODTe@ zWN6r~>-Azk4uoC3#-|RiJtb5f@}6#;rw@T0GUsd>HQ|QSn0nn~T2gpK=19k#)?;sj zXy0ijXP~yJiXRSqK(EwBla zoeD+?Eukqz#mxIi@cDNpdb7x}P%kEVjI$qewp%AJ)JL1%h%ZPqDjzIf6Ya3ms;?Yw z=-Z;=a;iGsnvW<_OjDUnwXhf5RateDem&h5Z;dC<66nZ68n*;d3#hZN*CB*)&36bL z?O&oNuvkX{?0e^9Skp%@)#F)}&qh1*D7_Y%VG!1uO=)_f;whusu2)iMTz@MTgJ(F? zX~$AMpH_V+tF7NJ)%TiHowXiF;^J1^}^^?MYZ>R#55X>7Rc0v2Y8Z*1#z8_g?ql+ZnQz-M#*(~$xlUW7PV zspDivk-0!ADlMU!`A&{XSh3DQjuvzZP578&Iuo4s<@(u}(rpJE$+T=B*sZcj_t40v z6dPU9q_4qJt9q~vF&)wuDrH`pwHeWi*GT^%5sA9ht7cvnV7*|cI}a!cRQI|=rQer3 z?R0pBek?;RC>BQ}@6=1#!=gL&YE~j?M*^ikf`wRRmmEd6%(lcdzWYiXwQL3ATL$Tx z3GBNx;2t=Z3`WKhP~;z!I!@B@hwzP<*P$a6w`Y(e%2EV{F2H1(-2jtCp)6$;p4R%6 z`YM?!--@(R-Fl#0{RFEJ;9>kcL>}ADL=^8(ZWPXe?!uzKoH@`dm2_IMN-vBk7E3-_ zHq+V9=NJ9DDp>zi(E3S+Lcmli=*vmYex`aKbqrS69v0o0d^1pe>{1}*+KqS|v07)2 z);MfQB8x_=Se?V`76Yr!aF`7x z&{YM)Idmo_JMVb}O*;xfqUkf85ov5a0J{4HHxP?3Oh_?xto2hC$94s)v~FdwvI`)d zJ{aP6=waOf3GEq4<+s4z+&T+_bKOSFfFX%N!yUBFSfxaAj12Y9z!WRA#}?+0mp_5_2eW!jFgodalK+JM@-ezn`xbm zBjH)z^Squ58hI_iZp{Xvre|)@Pgh_?w{Orhn6LT%27Ls3E@;umvht=D7LP5W5AM{{ zS`WAA3j&qV1JHL@e~d6;*CzdANG!;mN&{Zg=PHF|t&rFpg(|jSru)x=K62fmCs>N% z6)+KU;13}}#jr+(o+ySL;{wF~@E0JuekFnt&9|d>_a4Yj?;D&MRC}{-YqdY6kC8#g zwsqj1%eU$M03ia>X^DP#DKGSzL-~wQzyj$!pWP`Av^zVV4$n#%&%k#uZS<69f-80| zbq=KF=ivP@H+6xt2T!YVp{{3gZ@DLhW_Zz%p@*@KIR(){DCi2>xLtpm_AXKSQr9>| zrHqX*E-Nl~#*llCHIbGsat`1D#nxYU=r_v|SgokjCe+dDF(5jRUbeI&$ON9d$vFiQ zSzJ;?s|P~%yVfBbdEFA{>EyWt3+CD^IDxL`M4aT4CC(hGzrh+wZ`9bZQs0RM_~CU> z9w#rwdK)l0x}B|?!+RfgYQ7n!8iI#4Fjmt(+jz?R4wuRFK2Bx30NCYl)5p(ax8<}9 zRih!Qqc6Sx0G71&&^HYBY>j7ghPwt`M(^sO5U+33Tt5HV5^3R*zQ7U`Gv^n+F#RBG;dqx9a`GE=TPi zY!EFYSi`6lAny)`s#(xI6{NCXuzV3D?(CB7tyk~S&lfv61K?|a)q(9A*fnl{p12dx z-+GcYg1*XgBs3)<{N3Ci>~hbWy6Pw^GGZmgWfhci7no%GUj0<4`>+3jDoe*YhU(^O zxfo%dS%24uv3;Q9??%nbUxzBoe@3xK@%+`dL2-F8{dPeVa~_Ie8#>Q{ z^T7U?<%uex+JERyt;kd#*g!C%5Uc(X?7LqL2O}W%khwC{m*yd{E zfnR(uU@1QSfL_7Z+Fh%#W@y6#oj-kWK)+65!Lep8R3-S+z1lgN@7BTHZ%>2w`TIVw zrI%vfg$~>Bk-nEY?nBJg(;-(<*LV7fH1~e+`pl2@M}0VBn`1Tk4qTvm03OE51Q;!> zt8{w(5yfHNJPUf7oHwj6|OcG`YLxwYkLBJVMpyJGsIFAhC ztQfF$W^693Y8{}1wWTW5UQ=5O*!EWYSp{zIZN0s%1)plI6JV`hs{-HeU3>3y&fX_l z+pqWP^GMFwXYIAuUh7@&{QlRRu|;UJ_ZFa=Y%a*_Tl#W%W~fd~`V2NI;XQ|sM$=}{ zk$t{7XD*Za-u>Ze2!nrkE?0;b-JMD-ai(&VZg|vc@y+MpEqDmJejkpwliJ6mUTLJ0 zr^y%UMcWYL-o&XCousYNw25R)ki(Z^_4+K#Xojh@`W@e^8iQ`+0i3RX!(WxwJ|2oP z9;1CM`4A?^Ci{oy=;lx)JxP2tll0p+2+6wiX6EOq**x&_Us#;|O0ZE#G(~@X7rzzW zVRn6bz8?P`&1b)Z3H7-jWZ1+T4g`f;`jyuH#UhqQ)Ufk@V-OqTc zd^DbRa(EVPiQb*BCh2|OB+5Kzuf-G*v-9FRQDWW_$&i&;`O{C5$NPSYJQh;abC1hV zpjZ6c$1usNpL&iEynepe#uWuB;37|x$cqKa_}MD-h9wzK>2GMp_rRze-$R;K5-CrT zPj73`Z$4+$WV#v9D}4MuTY-a2j!9EUGNK`YbK`)XYL5^NOv;3L(j=Nt*I3?-s^ zbG@oB@jSRZDz%m})!Cku^c3Ju8LIC;>^P(X4@xwucF0^Y9_tH~)^jS)ohrX(FCtks)J)cB0qa({w}RO<<0bxvyM zY!9nUhouo;GnH8pRPl6F)Qe-f$x9EthT%^jvP6d%bISeT+lPbo`@gr!P~nN5b?O~& z6b;HWzv8W~osL7uw_^fNy8l(ng5)JNYgLh|VYlI$kFlJAw>EK|DX$<<){w!F7mE>y zd1gPkT&)9CSazZ(&_tHSq{ZA;lHFn@8s9oxP!=^!($5 zS~NC8EE;mzR;rF;w|MzVzbaOyb(M{%9h=~FAzD7`8{-pH7@EwQI!iMCFCXa;g`!DOpQADE=d^y0Vi zzIe`qus&L$$oEc7lJB7tdyn*4dRwJ>uQnOb16#d^n}9_5Cq}V;JT*K}A0294CQ+%` z7zf0pmheX+QI#SBvs`)lg=#fJxp_JrkybJWR=&=u_N%{+6Qk=sm5+M3e>e-#S)-ct z{8kL$Yid-d9oP`CS(=DWw~r=_*lJmq;)zs z#5+KFtdpBkMT&xlt9Ho-CVY8GQ#U9VbGQUX&EPWs7H zC0C7KUMx{Mu`(mt?y+mJANRij*SNYaT;BKzF+y`gb5+qWUH8|}P<`Jo3AJy26BTXs zvQTlm^w;jt%lHeId5K6QNx9KMd8P!HQ^V_!$YPsb(G*hRW>;~~trsWdQbLOj>CmD# zV3(o9+X$<&gm;&8xNFNE^<<-Oai;0b2d%2ifN`8$h0Fk0Ktb#nu11C;Nj-l!@(Fp? zJ6zqO+GXaP3C9WgAU@RA5vnLeVqfP7m@9vmeTe0IYK+2MZxbSaLk%CdtilBe?lsQGLuPH3&a^r#M&2_2r{fmDnNpfa>}l8l{GXN?f}d z<6duhIy9uW^vycG=R!3@kH1b8<;445JV$*u$4V`&N&0NQ4+hjS%RWi38mB75ac?yu zB*KkTSCn|E9Pvo+)aNU7%Xmd#$#tQ}Bg6^DtCK}j{_=P=F5`!|-uwrv-mIFtHRCMF z;qfYlgC{aU&4BX~Kd9Hc|BR1g&qr3R>qhD$`*f`5s!WA=+S*l12)n}<)roV#M0Iwx zlOjNzJKf>SA=~RvAK`O2w!mG%XtH-zOL_0nJ|zs&)^o1KDY>#kJr-&uRprY3lf@Ua{UWKWjI&tpI}$4N z1uA}>8yA=Tf2rY!&^WdV9;_3?F>jago9knZ6KuzewOtZgGM!lZx1Fj;|M-;r8co7) z&EI$yQqp@C<_XVAd2ZtTjCALCpb~u!X^c^}u4HZZH zw5jS}aue(TFXHgIu*Xjl@lFjbSL=n-iLMgdD;)6>_$(YWu z$=cr6P2|W#OmRtC!AFGy6(w>?d*@t9^H^{cWBcjV{nRHAR&w!I`>lmoT$BJLdg3 zL)8`|C!OE)v|~tofAM@gM^`^mRGFI~6!^Q@YEeBQMW=-$EXmYOr^e{V>+NgY?gkvl z<5_KPVpIbE*^CA|!V>p1*tLdFxtxzSdfFdyDxHV@>D59WM(bZU*yoyXd9lk1aEnT& zaF`cCCMNiw#G|i4OxF*#X90DB$JV9i1k;ljw?~U39_{puVmloGBVnvl@2e)XpCIxB zGi}!E<2F+#X&CZ|jcCVBlXA;Rm2dCe_R}ie^a?Zk{LhM}lMrr?)CVn=s&<|lDw%3s z^9mYzANW(D-Ze4ADo!fO)3XP_ZXWtq(m@61RS$c=tA(Yq#|_y%~07%z-mi zS!!%P`63hTsQ=v)L-l&yzl)p7@h=NiB4%fwt*Qw{ ziEH_UaH8E?<#zqi0=th4$mp8lj7GIblAw_3b%FJ)P4)Jk2k{QxEMRF%M#` z?AtM4)#m8AZ*ZL2<9Uc+-%P#d?Dq>-sv%N8a?Bz|(-GBYFH$GgqikmOo%GVk6C#_X zYrSzEb-IHIs=^a9CFV4h%==w8|K?|c4icFqj*Y&OJLKcRwAvY|r zE1mb+TSZ#_%@^a`n()0M)!Q|$SS37veS($)rrJ#@zBV9)q{hkKpqD(9*PEN*+vxMv zUX}26RfP9lF3H_HG~4aonx7MndV2;1pl3eRIR7FwEkv?~;Xl4GLV~N^cjcAKGH>o?o4h_N zcYrJz9%*@X^AU|(Qq_3(v7QPgzq{dt1Q9RZbbE-ucVD6=l8z~>{b-Kfc|MN8-K$~H zYp=75M=bURoD^Nyr^%(r0No*cirqlAYSL425>eKVM35@|!DZ_15RS56pTeeJcbh8D zONe>=OfIg+Q~p>~C1Q6050|NGOk%PnJi^ElGM9>3C_toN)? zd3FAjzl5g;CgP|?COKy8MdY{)xLZxE{n(~PWt^(V-@@{F{DsWzLrYb2I5o*A9Y%xN zdW$+Q-HMd>ZACS@wFl|*vt_C}qkUArwM@+ldwClPRFb>SvIpz#t5i!k?y5-%@;myM zr{$ZLt9m4`F*j;;a=E&z*t4t1p&o(%wth`gMQKKX;Vx^R2-E)oFkSp4W$>v!^a5Qu0d%2;5fVflgP8c5t91L$J^)A{OIxaD4voe zECyM&*YUx(FO#jVQ009OZb7~F-gl^>&`mQ5xW0I!s$sYBj>Gk?F?j>^`Y`!X$AtLy74N7j9KI>B=5es>k1l@H7PR*ypj#yr^lCg2}xfSeFV*iMvacM2ZVHljTca)7@Xl zC*68<9toJQVy^7Do-p>2t&sJ$FQ{8Zf**gW!`~|SvS}P*f7fklofI)aB11$>z|TgC zaqP0QBp*F?=B73+O48h8XOmTjw~*~@IK$17i08k_&0IAviM{M{vn1IjE;mb(f#{G; zph$|YovoPc(0e{0x-RTYh$XD8VqY&N`+lQ#xccqtfLA%WyIX$eU!0 zZHItT7ZS2->olyan~=IUmSksd+1^3Aa2}S9;bp0xA|yAQX@5?(0;cl|cd2S*hFsB0 zZ&!J>flU#0d0P_AP8#7KNX7y~_85AS#cJMwk`h6LuThVxh^sO2APk->80CO3s$vOL z3|^}m!*N%BB)Rb5GJ6P`2Q~^u!jRk~j;{^taV`)REW6rnC4nm$@Q5Vi-ZDq^q4vUZ zJ#}_LSS3@PMsZBaU=Y*{=CQE%B2h$KdMt$0oUwRA<${bwriVz0p+N#-G8X-pwP?Nd zSKGs75-5W7t(zwV=N(^C6_NvHl$zgdQV*v4OLzk$>fsJB;SLZ%H}Okm zfKFGpd`UG(=FPOzbDQ;!FR9*eYE(ql=HIKvrD`LRy{)4w2x*j+6jiCDXY3{uSavJW zD@}OL^O=smPfaR-oGCrf`=hxH`mr*SRd#)`AWz@>2$`_!eu`24?AMUblx^CW>hCq- zG_I)3E7S8HB#&v|eX4vU{z})aC(bgbkin^Ai1D4?ue|08e5`uGZT3hBwR}wtChwzN zPwYX5mg-qHU^;`sRCW&ib73QGqI%z#)!Ff6(r*E$Qz>ZTx@V17RNR2xuE4loe!of@ zC%F`lrcF{@`}&#t)e*gGa(=OX?g6lqCw+*L|EHZTdh3I#6}jA97a|$^ztL!)uLvI& z-`d`*^DDKzSrwR+l{K5y%<@)IVXnIHT8HOALj(+Pe>6EszwdnBF4j$7SL0>bU{O)` zOS?X1i<&7EVN|dm9$}g@+8P1coZBic5*|VZPWM;Tta49Vx~k3d07uf**IJgdGF`vR zl$(0&V$9)PH|5srZO`QtJ8St$l&|SU{@$WcnOMTEN-8$7F8C_LLw?Trx@yrqQ^=LN z|6zPva~?)5{PNfNXXgW|xVLseiC%e!JxHI_icU(4xw`dX=BxCPq7prMITL&8!}v^Q zwxTif>k55f3wM40NwT3|qhRY}$*D}w-VrVs_IDB#?vJ?s#%*>}t>hQ9r(Y+*5;=Y0 zNZO4@VdYoUxW4t@5QC3ZrJye5-yHl+d{6EAgm3ct6lZ~SFjP-@gdM!|o5~UsfF$uc zDa&~Co9YZ=6vpKt59`!(*N|t_`VDjcmd6Teobu%7PYA2rRz!2zH&6o49$OUFUEflz zy(@oF-+S;siE#Z24Wnsg-jq$}GleU+7Y)v*J0#btn;UfNCbEg9_OcN#tRX3>X{*|t z)7tmUR<#jJm259umBw3(Bg|ymt^20RFLsB-@7t#r4}q;=E}NV?KIyp|!;5)mzYkG^ zLqK6vMiu;-GA~)61Vi)-+tfvaFx{jQJ5c*1-mPl=>dQF&mTwowf4jbayBfFY7gYFv zxE({n-IMIwc63YNB&0T*;UOpw|25R2mwX5PI}+*p%Xid$-nx)4@OqB8X*?+^yS{hY z`{R1&{4zg}s}w%F_4y@zk$+X+p_usJ?ywZFQ4{`expw{A$G#w zQ%wT{y~f!MlevzdT=+fJ8k`P-aQ;!mqPSBsht=LjbS3kGsA-wsKg}p3LM?JrPih_h z;kW(!pPn(o9*+z@o5vT|Ij)FU1WwqelR{4|?^ig^A+lY!qUgY(}I ziv*TKvM46?sm6*}G%&4F6hHijs;wt5XKmv9%v_)jEK}cw_C#P0b|eB%jm83fw8aB0 zVyUG{QFt=2%pF0#*3ljq2-!{kk&(3NztENlzO|SlnQs^E!C~RM(k5Q7%qAVJfwyTF zGb*!1dm=#Pk>E3(2nQc_uP)wz|uthd!k2bL4r-ls_=CZ2|l+YFpG;wx%J-?Z4JCk1bfcc zY19yWPlmD+4X$3c-1(myz3UWZe^Sq>{r^dg{s*;?6#xBp4 z9zoXfzx4R=J%Y#ezxA+wp4Ic&K7Km<=QDi!>8(AV?c*2q{BOQJ)xmxq+!5^wu=?!b z;D7Vq6cG<~RAL=qq=k}Mh2ONgHNZ%G98Kf*`;7D-R3O!5CDg4ztRJvc-{hQ2Y!BLShG2f5+H^o zc@4fHCp)5j>i?)CHR1_UzgLIk@6q3@>xxj>Ve}n~boVRP2yuy#B{~uBZR^wF#uV^o zX?_PW_o!}sO^uT_&{-i&si!!bm064Pphv+@8l2+67 zIu0fGNky!O2Q)b-uLJx`{qXB*LP4u!|Bytrn3ydEN#6?b^auXvJUuR!3unX^qt z!sEkO}f_w!M0W}`hb|%fvpyyp@e{gBuIDPP)!hF52260Q(Sg{ej5>TVma72oxNbS#BXandum3I*8lSKohGi%Yn z=ZqOzfEVmNj;aq(`$$2ARk(nC1dPb9o62A)bb1PjMUB(t^6~`1sdJiV1V@EXoUho4 zDb_!)CaD5$*{a;-^A|ixY6rDW0vfis2908BSz)2>8evseNb}5~u&kGqK1~dxxEhX( z3s>r%POxPlCO4B093_;)|Y01Zy;N z-q4E@8ko^za@$EO_a+yN(M%p}_B8OK(_kLYyzR_Cyy0AZQ;XGF;_(Kul0HWGHVJ5F zy*GT?Eh|;&5uw_AmQw_`G7l>r|c z1$T_~&b_r-@2N8Ta_)F*T4vk5QJ*@w|3YL4$e&fNtniyb8m_|K{uQOD41WhL)}G!*0D@+b16ZJj za*E+W+fQV(bWL}*5eR)*6TJPB@DvEw%{Aq*@M(`mT!!l5dp4Wxp4(2S*dsJRL$i9E zdl=*mU_(cy2TX|*^`RUo>euUhtA~mX6CNsgOdx|+vAG*B4-aBpVukI`MbsB4+<0RnE$5p<=O?+CY~Re-;t_InDAUvXxq>_cH(H_@7$11Wv=1qN0*leoQ?5snZkuLNLXzgs=`mJlU^d6Q10^lr zeP_T`TG-%QE~C)6_yo(X=75UXYpzWcTu1hLjUVE(*thk`vmt5%-(&>SpjWK4eqMeAB2 zs9~7~GWk835}-d3%kHM6f3DRerE(um0o=&re`!tp)MLU^y=CF3h-`b;cUHt0J>fMA zWJ}n78hi|3&QiVT20I_+4sg!T=^HBS0s6t4DSdizD+M^Et6?%g&6Q}tbi#dWPlLSm zk4kd~`aBY?*XB3soYSp|KDWew!0SGxZMb1(XoOxegp9kxXIP8-BB#^=cEsq4DB zEi_O+z5s^WahBC4ywj8g+{l-lj*Yk234Qcdex%56cz-=rIq08XINQ2T?iI*S(Z9pU zwsGfJmuKCA$z28L=lMoHh-nHGD)?>Z;uTBi{dx8Y3TRY^-^&kFe-4o{r$gsk-w9E8 zv!&GX06$w~)MG=q3XG_jRW8S0?8b!Lwif4*T4=qkOK-6NZEvUE>=Yfo2>PWb62B_- zmFq$!`i>eBH~*mFJBJoo+iE52C#X^zvq})&itF^_s;Ef6++`(YRZFGZDPNE6wqhl6 zqcNg7dUtKF)wg$BjZztGa6xUs#r)Y|^5%E#&%uN`Ket%F+TD-fT9h`{si=lYookH} zW6PNm!skW=$}i58#B|%3@Z);(1(rZ@d((oyqCy`jL>hy>L&Yl_W;`VZh6Ng2nnXK4FA_F)ByofG#>(H-9BOQg6P>63|-Xgqi&#?781w zW<8rlkw#w!!uDi+@N5f&MWZ}C!hOahZ@ALBnehOtc_l!o(G=w9WEmZBqoeff7J9z! z4tD<1M=7Ovbg{Km@>!cdU}Q@MvcrHN?WptBmtY^Yrk2#;5v9#h@UF6$BcsqrC#SxVJq3&ecK`$c8? zFROAb{a=q#X8h`Wa?6c!$NU;3Q>FJzH@4JFQ3-BqQfa_-5RhUd|`m!6W zvs11TTmsuDll(txAv$({2skYPh3vgz#{m8E4%k5AHR@hq2-OZu@|@3649g*m1LSlq zr60J_njMO9lGu!bDww136gKFFBWSNzz6D)Pxd|YxOh5If7YKb&@_-zv6S&2?mp=yn zxJOO~`>28J}?4sEsfK|vEz3`YEdqKmA$3%A#{FIde{(v&|fJ8Acp6xA@(lLJ6ke_N+Y z1Fu?T_D^mEeFy+mvw_4DE+7q+w_8mm4yT88r7==#_0Fxpm;UfFDDaWh)ZjdxoADswaIE^7;`1-j~D{*W|pu&oWU>ZQVcfKrt+g7ylI zs<;y5aIzY~Q5~+URy%%1pk%XV>Hlio5fueK`WvFYvN zq5m4ircPh6#sWEEfnL%|jozGM!cPWY6=!$8{@TF2%1tL&#YJtck{ox%1-k7;Rn0U~jPDOTRoC9ZHR>6D0JQdFZ8=sH@UB?a zF8t~MkI$PZB|YH0>J%rdIL>v~E8&Er^~W_ptkjBAw(I)hyK^JBIKgbZGQ?j{F^yPC z#dP%IM_FV2D(3BuEqEMJR}CWO(Jj`EnIxyj-V_?7=ez~!g3~J0=-cpvbk`%6!EAlO zSZ8u}04S}~q}{t2o*ITfR{!8p2)pGsocXr&dUiZti}im!0$>hctrpCLc;;`AnhxXo z4R;&Q{qC<@RXNd3zvXBmW8;=SY<1@fvaAQ+dJ4-U-$8Kw9QD6Wd~aDJ8cgX|P^QoP zhIOHD*q7de(a^fn5>!L~%k=Rw$C~(&hY6&_hvEJ@J*T ze-~B(knTUelIIB^2n%^dZj=um2&{=+`<8Ro-&n-;k69OFy|S}@vtJ`B;s%Wa9O7O?JZBC{(8QR{?v9H z`?Ug8J1o9%npHoSx3Q@&PE@<3%(0 zhZu&ldqBHADmwxjZ%9dsSvc;}l*=+}k_@W=-FB*@sHDBx-hpu(SbA6;D~VPkAo{EC!O?+BDv|?Ji}b{Qvr0>x!escNTuR znwE0M0&?qP3x*N_xNX*3pLJ*zjM6N14`-!gJL-#`6PH3rhsenFLrXr;Hh&+!z zaJoSmVY&fQ*HOpx^n#3e=#Th}GdXB+1; zHGlcKb)tUn+)x>I(cb&i-~lK;k`3tA$-*{HEh^U+HLH`V+>9cAmZSvYqYiTAre+AK%;r9*}TsxcT(uu-Xj7jYqc@AD0JA zDMKLRfJbm_os@p3P#EPG3@HzoQtx@PpjdIqF*CT!MA64MF?;uBu_ygbG@xnS8zNag z|M~j4Ut1H>Ylyu=yg5w{{bef{Y++ZehWdS5Jt*@G$szO7;m#P!Zht|8eZ^{0rf(h- z>V0Oq-y{cFYD_+kBs@j5Zq6f=c;2!w#pXt9eduNKP2>mnEW$hGqz~^D!n5Q{Dj!1l zQ()!Lxc7-ig%pyPECE~CH3Ud1&KztBJGe(I3qMK%LrrSx)5i@O6A%sUJi9?J*l#U$ z&xnwl1Xnk0JZA>601_U;lROgzl;og~(};DDl3nGNVVhINX20e{uf;^6gY8?`9;Khh zjC4~3o}U+7|-6A-TwB;OOd3CpX*gzg)No%i@ivX$ z^xONMi#M!)&>NC?U>tX>GcSmajNa$#bKl6^NMy03+2y&q>P>SH=dM3k#&j?&^grK3 zs^ag>skh~nHvfYcyoJ}Sl_~AM+L|e@W#bio>n*HjHc&NrD!HwcZhOGWg?ix;0Qdn` zo{N#qk<5luyA2tievP~IA7%`z0*=O=;5;F>zn_KqD5*MxVf!^Oj;+v zzC%$ZH?FYzjk=mxDb)p1G!Pf0d5ws zhU^nn)C~=S#cMEXhi)vl2kOm&$!U;`fwvtjjb&^bZvy`958ZgJnr_d}lam;W^{yw~Cf6Y-2EOvDo#Zrwz6rIUmX?fp}-lnHloB3#(6tB_LH zERU&|qT~Sy%R9l!l|3Bvx=wxM45}WO#%R_QB6mzS5-7p78SbBBE(9rtOetIMv22i7 z<`pzEPYD)@NNh|W{1R(?a21gmgUq~gH!hj2w*8d1BR?iMi^q3pWkTwGvt9s~Z-bsw zYNz0SJAnJ*MBPjVaH!P2z&Qv&+;v3Ny#Qv%#_=e>6Q+}Y|4_A+@4%Nx8^ju}=4$injlKi#S$ObN5t*MN$>|w!W^U*b;B^GW$etA<4ID_~Zsp+ViPh-hEbcoQ2&tz> z*d=vRif;sA{5$34~KS8KM7-qakqTS$!$@2_8aa@MbMQM=?4+wkbCCTGN z{TU=D%jatVib0F2w*|-wjN(Nj-JMMH?7VQ^mS_60iZ2Lzr8400N&uoxe|uG)Bc@X7 zl_Lk6{sSR%urxPMpHhim_0;z*(ZSLlX(I|>zN1LVi512AdoSfkwNqw`zn-p^>901~ z=W?IO$#N6UYiCeybytEMJirjU1cVTLVJU4wR-Ud~hS*jZ)xn(s%qfAJ9se?kC07rz z6Xh+tv!UCGO&X4F)~q&U{;xiz|Rlzj&+0z2!S zNo*xDK_&{XH}5#1lX(1+$wigAd<3~nNB@HcD@NNx;nmG&PUcP|#}>m07ISjThzysG_(2T3|R& zL-m0l7wh|5L?_?WmFIGXKh7*hyY2#+>7y3=NA%zSibrBVPnvWlcfsL}=P9XobO&h` z`%(!_q}JHQkmg;Yt#CrT2&`|;1YtP1Ul-Wb5@-|)^w|Y=OBm!7rz?ImON(aXEiE6- z(&BFIZ)sQRwbPK|OWW;%4N?Wr{{lx@VE9d?Uh|6_4(W#E)9WmABnZ%QIeTtS*7tST z*D^O{5&J>C_YAu_Hz7_r7X~>60bMBTM%AwK9xXze11%|KH^?oi<~SByX!5FDQIkFu zHD9V{C+)$R6mf4Avf{wo$O^<~rOqu4Rp?P)(vVPW#u+yF?QBse}J*RJi-%?aAsLna11PDDc2Rg)=Q*^FR zN9Kq*KieZbH%xz2!ijVSrJp(~z}REDVzNEUh2doF;vR|YI6dDXk!_W^)%u-V0SW!~ zHMvE4^MB+te5%cK*JZ%kE%-fLcIgybs<$AWTlL~&?1)Q005|ndKXH$Kz_K*-BZ!Zg z^pj}cG|hf4Os<(TCooH3Wt+ne&MI#~Ix)zB?wPZLr`w+w{{$%6i}Mn4EJGR~n>>OG z1x~a{Z$6U*P0|`~Fq&%u^%OAKcHoqg0wp`b0m-LpaCAKw*-lK@1PEu&Ad;y4bBb(m zvGGlSB>SIX2Mpb`5xCfHerXr5*b#!GT)ITHVP__MxQ+7N<7fCFFrlUkSnTv#$~ZE% zbkei45*&AJAQBDg^+V5+(|1V;D>eT_d&tM%V}N-LYV0U|aGIU(S!qcMX>yj>L(|{Q z;U@+-cA=j0kd>F3VMd@2o(9D6ZGpWePPRGjIo7-PCsGca1lJKT>XVr$J~|Uc z`FW?Y$&r?Lrh=?L)`Y8eWqsLQJ;Huup> z(o>I;?a<5AxhTLSM>*u;ZgH9jxtu2mi%^RIF1o}5uCbpE0ID*?m&8kAUJ>kVgGD{9 zj`#GDOlmgJSj>CQ{~XYwmzCH`sHOQ6+M9HbMuGJog5zL9VlUO-o1TJ2GeQJ1=wChZdERb0R1o?_M(9b$lPY0OE`(A9%SQr^+2-v zXVu&N00c;jGwb}b4jhCln*eRrW!?6yuvc)H#LELd{0y(Sj2ul&zDhZ0#no}j^_&Hv zyxv9I3-p|`?SW*ex!CYMXWMs*SEl$JyIT^FyxnlBp819RV%ck+|0EfKe!u;`HP+E1 z1x1+?6^Q&jOy$e&#|vz|^jv$MtMWKGYevZxW2m8b(bYwgQB|VH&bLnrM_jHfhs2=O zUIJFVtLaI4P)gI`s!!8F;W6q02(`|vqw0*|fEka+!*h(T14Hq29X_J#bmju*+^@1@ zJWt&pg=+MhU3OinK0FDWvrMr#gcfzPv2YME!za7PKQJx);FGXRU&#aZ|Ba ztC`-riUkx^+<$F)+5kciW1qv`fcp&Zedvi2Gs7M?ivmWv{@ua4DNHu_z9&L%nX@bF zGRcm-?SAE5(o6ak3Ps+HbJ9WZ2N0Mwc~U9ODam)vIXczBXJ7mgCrPZh$S#rs36BUG z^z#?lvpChTQ(K=$1d>`>q&qLR8;nAf_Sfya*e-WZ-w=YqM=9J>zwv=JFYJKB%H^cP zBfXaeavSC2N20!f$-YGX8aeyorS=y^@!xxm-E6>rT`>wAw8IzqYow^hY-wA0xqZIp zT1duuzA!@qZ#g%qA!33vTbJVp*>*cBJLgHP340Z$Nz?+vA94N&e+;1>fV$bbcM zEOQ5Yssx8;xG(tqacZ>4R5+yHlbm#pB9TrwAxGf*TNmRqOL55hk8h8c!%QB1W9;3c zJQnFq8_I{UNR^sbIcLG#@y%OQmm^uzzaa)Cy>3r436SA=D4fXHn)I3^lAD&=m(&HV zIIoyBiN|8qRl?@bR6sQ+>9U?vrJh`B-)<#S>`IgsgDQStdRU68`96&&4#YEE(x^iQG!hCi!$CO8a0Hh;CgX`T2>x8g+Jh79N=KTI zP{DXId*d_!8Bo$cM|WBt!{%);+5Drzcd-oMcTX3jiGF|~iMHU3$(bU?goB4MMKJ&l z&9bZH0^w&yV70sR%N1zAeFHg0`FeY@Xb%9TpJ6f&NvfWHojo=bae>m0`BhQA?z)r{ z8P;8grAJmT=Vf$}hPU%N`##GHg7DI;tlKS$h3OW3BHe9N*WF;c1=W3!;~|GFj_nE6 z_VwIgcjshwl^!8VgVR;U2oe0g5q5FXg?yYw%&*>kgE=K1%Pg+IU+{?Gx1KkT1oOR?*fFJQ);Fctfpu)Hj#X(?_%gr&SX|#6->w*(tWNI8_ zf3x=AUBuc;ggu;^PGr%wpSK&G=Z@7~w=$5O8`zJnvpDg^IOSLFrjiegdiUK;1zPh# zz|@!AE*pX*wTGE>0f_HC_ncx#Y5!at13h_(~yM4MIAcXE=$(Q3}59=#l z#^(==y!eW|hBNfr4x%&r%&DUx+?m6vEm)#B&h+s_+k1od9Aqq)orOM z2qjQ@UK*q)-f543ryGhq{3EoHTkfpsKPC(q}jIGoH^Dd63 zv_DEbxdT1W_gdY0mpw`6-A{$x&3Ez7FW1;*`hmObAw9_OG|C!UgCBQ}qF_IA7o-?$ zL7teu4SqhJ-A5 z=5D)*l#W;Kwp#}Vh7#w9zlrPV;R%_6dH2`@d;a!wDR23>@qhAQKh0e|pQh3OV4D*@ zW@Y}KHxuZGZ>`20et(_aR1x<>=&`mOhH-?ShZ4@oBm+_>{1*g^{K++TBdjZ~&tGFt zDEEV5J4g#xe9;Cb+mE>nE%1bXZJj-}$B%YMrD*E(0Jn~> z-H!(-wWI-3k7jL;V2hX^(@>@wX)f#RA{NSX10(D}OY}`qEJ!n^axIfs0?6bnj~_!E z&g7j1;E}F=Wxb8G^*^cI53tB^IdRT67Ac$=y}@qoae0*29SNcShHc_|%p7AhIGr&+ zMm|Iq<`=Ako8A9K{`nnDQLkCg zKR;w2;80JxtG}y6?TqZYuYmEx|-^e9sE@mi{kko40{~E|o5S*vmgB z$=4Qn%<8+b>Q0eQ@2!UmpCwKHbT9urC{Mlf9{xG;i||!HTsy)k_&myq{O=XbCIW9R z+^P7qJBfbNaHaoWJJ~<+pHfK4Yo*}AKbyy2=)q-wMvqT#{=Z|*QV8S!gh4xS+5d!& z{(jOkuVLV_KfA|I6}arr@bLnd{aHSKde3M3_~|{L>Dy255V-6t*`0Nz!+(+iqB^MW z_t%S@jGQTuBK|`a=p`BXAJS_99PmH8zwya?_hb%``snQ2;)0RPDq#fs+e+vs^9lj5 zzy<%I3b_ApO9gK6!*$cdf4~P9g8B=mVg@>CH(1z6=ByzG?e{bTFpjpO|uz@B) zMUta${VfP+1}_ALC;`;BzHC?b1a%ZV;(=SUI`^LyN%`pB_$A$w_qzxEg zYP3nY@thtKJ)ApY84qo9d&5O!o)VKpH)o*Z2!)!rAUD?iF-@TSNY1SDJMIhl&<;MH l*HBVbyk0tzP{6TsY~~}*lby#z`a<8cf0d(eeIf6z{|AW%mtOz? delta 95142 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%0AsZN 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 Date: Tue, 24 Sep 2024 17:25:34 +0800 Subject: [PATCH 03/31] release: zksolc v1.5.4 (#105) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a0cb99c..9163f289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -988,7 +988,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.4" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#eacc6130a8e5aa815320b9f34707ff8f62c47130" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#6519ed7753a5836337eaec93ba9b513a5821427e" dependencies = [ "anyhow", "era-compiler-common", @@ -1040,7 +1040,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.4" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#eacc6130a8e5aa815320b9f34707ff8f62c47130" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#6519ed7753a5836337eaec93ba9b513a5821427e" dependencies = [ "anyhow", "regex", From 16298826d147fb29ed772ceda7ecba21fba7d741 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic <129192835+vladimirradosavljevic@users.noreply.github.com> Date: Wed, 25 Sep 2024 12:49:51 +0200 Subject: [PATCH 04/31] feat: tweak LLVM options to optimize EVMInterpreter (#74) Signed-off-by: Vladimir Radosavljevic --- compiler_tester/src/vm/eravm/system_contracts.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index 7260a3e1..2f1a25dc 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -254,10 +254,16 @@ impl SystemContracts { yul_file_paths.push(Self::PATH_EVM_INTERPRETER.to_owned()); let yul_optimizer_settings = era_compiler_llvm_context::OptimizerSettings::cycles(); let yul_mode = YulMode::new(yul_optimizer_settings, true).into(); - let yul_llvm_options = vec!["-eravm-jump-table-density-threshold", "10"] - .into_iter() - .map(|option| option.to_owned()) - .collect(); + let yul_llvm_options = vec![ + "-eravm-jump-table-density-threshold", + "10", + "-tail-dup-size", + "4", + "-tail-merge-only-bbs-without-succ", + ] + .into_iter() + .map(|option| option.to_owned()) + .collect(); let mut builds = Self::compile( YulCompiler::new(Toolchain::IrLLVM), yul_file_paths, From bd3601ef1288fe4aad6ad6abbb3f85b1c15fb660 Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Wed, 25 Sep 2024 13:04:32 +0100 Subject: [PATCH 05/31] ci: add mandatory target parameter to sanitizers workflow (#106) --- .github/workflows/sanitizers.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index c34670e9..1561101d 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -32,6 +32,11 @@ on: type: string default: 'Y+M3B3 0.8.26' description: 'Mode filter for the era-compiler-tester. For example: Y+M3B3 0.8.26' + target: + required: false + type: string + default: 'eravm' + description: 'Target filter for the era-compiler-tester. Possible values are: `eravm` or `evm`' jobs: run-with-sanitizers: @@ -88,6 +93,7 @@ jobs: run: | set -x ./target/${TARGET}/debug/compiler-tester \ + --target "${{ inputs.target }}" \ --zksolc "./target-zksolc/${TARGET}/debug/zksolc" \ --zkvyper "./target-zkvyper/${TARGET}/debug/zkvyper" \ --path '${{ inputs.path }}' \ From 27c18e2a061b6e3f3a8722a4bfb2878171230b4e Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Wed, 25 Sep 2024 16:01:56 +0100 Subject: [PATCH 06/31] ci: fix sanitizers mode to 0.8.27 after solc update (#107) --- .github/workflows/sanitizers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 1561101d..1dd6eac6 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -30,8 +30,8 @@ on: mode: required: false type: string - default: 'Y+M3B3 0.8.26' - description: 'Mode filter for the era-compiler-tester. For example: Y+M3B3 0.8.26' + default: 'Y+M3B3 0.8.27' + description: 'Mode filter for the era-compiler-tester. For example: Y+M3B3 0.8.27' target: required: false type: string From eedb5a2ab71801706210dca5c738936355a8ae8d Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 27 Sep 2024 13:12:22 +0200 Subject: [PATCH 07/31] chore: Update contracts and adapt for new EvmGasManager layout (#109) --- .gitmodules | 4 +- compiler_tester/src/vm/eravm/mod.rs | 59 +++++------------------------ era-contracts | 2 +- 3 files changed, 13 insertions(+), 52 deletions(-) diff --git a/.gitmodules b/.gitmodules index dc4a4a44..64ae8f6b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,5 +8,5 @@ branch = develop [submodule "era-contracts"] path = era-contracts - url = https://github.com/lambdaclass/era-contracts - branch = evm-equivalence-yul-new + url = https://github.com/matter-labs/era-contracts + branch = stable/evm-emulator diff --git a/compiler_tester/src/vm/eravm/mod.rs b/compiler_tester/src/vm/eravm/mod.rs index e175df19..86ba368f 100644 --- a/compiler_tester/src/vm/eravm/mod.rs +++ b/compiler_tester/src/vm/eravm/mod.rs @@ -59,13 +59,11 @@ impl EraVM { /// The extra amount of gas consumed by every call to the EVM interpreter. pub const EVM_INTERPRETER_GAS_OVERHEAD: u64 = 2500; - /// The `evmStackFrames` variable storage slot in the `EvmGasManager` contract. - pub const EVM_GAS_MANAGER_STACK_FRAME_SLOT: u64 = 2; + /// The `passGas` variable transient storage slot in the `EvmGasManager` contract. + pub const EVM_GAS_MANAGER_GAS_TRANSIENT_SLOT: u64 = 4; - /// The first `evmStackFrames` array element storage slot in the `EvmGasManager` contract. - /// (keccak256(uit256(2))) - pub const EVM_GAS_MANAGER_FIRST_STACK_FRAME: &'static str = - "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; + /// The `auxData` variable transient storage slot in the `EvmGasManager` contract. + pub const EVM_GAS_MANAGER_AUX_DATA_TRANSIENT_SLOT: u64 = 5; /// The EVM call gas limit. pub const EVM_CALL_GAS_LIMIT: u64 = u32::MAX as u64; @@ -420,60 +418,23 @@ impl EraVM { calldata: Vec, vm_launch_option: Option, ) -> anyhow::Result { - // legacy EvmGasManager.sol compatibility - // set `evmStackFrames` size to 1 - self.storage.insert( - zkevm_tester::compiler_tests::StorageKey { - address: web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), - key: web3::types::U256::from(Self::EVM_GAS_MANAGER_STACK_FRAME_SLOT), - }, - web3::types::H256::from_low_u64_be(1), - ); - - // set `evmStackFrames[0].isStatic` to false - self.storage.insert( - zkevm_tester::compiler_tests::StorageKey { - address: web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), - key: web3::types::U256::from(Self::EVM_GAS_MANAGER_FIRST_STACK_FRAME), - }, - web3::types::H256::zero(), - ); - - // set `evmStackFrames[0].passGas` to `EVM_CALL_GAS_LIMIT` - self.storage.insert( - zkevm_tester::compiler_tests::StorageKey { - address: web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), - key: web3::types::U256::from(Self::EVM_GAS_MANAGER_FIRST_STACK_FRAME).add(1), - }, - web3::types::H256::from_low_u64_be(Self::EVM_CALL_GAS_LIMIT), - ); - - // updated EvmGasManager.sol compatibility - // set `evmStackFrames` size to 1 - self.storage_transient.insert( - zkevm_tester::compiler_tests::StorageKey { - address: web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), - key: web3::types::U256::from(Self::EVM_GAS_MANAGER_STACK_FRAME_SLOT), - }, - web3::types::H256::from_low_u64_be(1), - ); - - // set `evmStackFrames[0].passGas` to `EVM_CALL_GAS_LIMIT` + // add initial frame data in EvmGasManager + // set `passGas` to `EVM_CALL_GAS_LIMIT` self.storage_transient.insert( zkevm_tester::compiler_tests::StorageKey { address: web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), - key: web3::types::U256::from(Self::EVM_GAS_MANAGER_STACK_FRAME_SLOT).add(2), + key: web3::types::U256::from(Self::EVM_GAS_MANAGER_GAS_TRANSIENT_SLOT), }, web3::types::H256::from_low_u64_be(Self::EVM_CALL_GAS_LIMIT), ); - // set `evmStackFrames[0].isStatic` to false + // set `isActiveFrame` to true self.storage_transient.insert( zkevm_tester::compiler_tests::StorageKey { address: web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), - key: web3::types::U256::from(Self::EVM_GAS_MANAGER_STACK_FRAME_SLOT).add(3), + key: web3::types::U256::from(Self::EVM_GAS_MANAGER_AUX_DATA_TRANSIENT_SLOT), }, - web3::types::H256::zero(), + web3::types::H256::from_low_u64_be(2), // "activeFrame flag" ); let mut result = self.execute::( diff --git a/era-contracts b/era-contracts index 220e983c..d76a8d15 160000 --- a/era-contracts +++ b/era-contracts @@ -1 +1 @@ -Subproject commit 220e983c9a873e52bfc7e52e3576f67922a132cb +Subproject commit d76a8d15b3800e85fc02b7f1dc8ec44ddc68e24c From b3195859e184e9a2ede0d53416a0909e8c9cfcc6 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic <129192835+vladimirradosavljevic@users.noreply.github.com> Date: Wed, 2 Oct 2024 14:02:52 +0200 Subject: [PATCH 08/31] =?UTF-8?q?feat:=20enable=20splitting=20loop=20phi?= =?UTF-8?q?=20live=20ranges=20LLVM=20optimization=20for=20EVM=E2=80=A6=20(?= =?UTF-8?q?#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compiler_tester/src/vm/eravm/system_contracts.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index 2f1a25dc..176d9fbc 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -259,6 +259,7 @@ impl SystemContracts { "10", "-tail-dup-size", "4", + "-eravm-enable-split-loop-phi-live-ranges", "-tail-merge-only-bbs-without-succ", ] .into_iter() From 9c94faae468ac19c65a6253dd857ae9e82e86275 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Thu, 10 Oct 2024 17:54:24 +0800 Subject: [PATCH 09/31] feat: add EVM modules linking function (#14) --- Cargo.lock | 274 ++++++++++-------- compiler_tester/src/compiler_tester/main.rs | 2 +- compiler_tester/src/compilers/llvm/mod.rs | 5 +- .../src/compilers/solidity/upstream/mod.rs | 13 +- compiler_tester/src/compilers/yul/mod.rs | 9 +- .../matter_labs/test/metadata/evm_contract.rs | 4 +- .../matter_labs/test/metadata/mod.rs | 2 +- .../src/directories/matter_labs/test/mod.rs | 2 +- .../src/test/case/input/deploy_evm.rs | 48 ++- compiler_tester/src/test/case/input/mod.rs | 22 +- compiler_tester/src/test/case/mod.rs | 5 - compiler_tester/src/test/instance/evm.rs | 8 +- compiler_tester/src/test/instance/mod.rs | 8 +- compiler_tester/src/test/mod.rs | 1 - .../src/vm/eravm/deployers/dummy_deployer.rs | 2 +- compiler_tester/src/vm/eravm/deployers/mod.rs | 2 +- .../deployers/system_contract_deployer.rs | 8 +- compiler_tester/src/vm/eravm/mod.rs | 6 +- .../src/vm/eravm/system_contracts.rs | 18 +- compiler_tester/src/vm/evm/input/build.rs | 9 +- compiler_tester/src/vm/evm/input/mod.rs | 29 +- compiler_tester/src/vm/evm/mod.rs | 2 +- era-contracts | 2 +- system-contracts-stable-build | Bin 965580 -> 994031 bytes 24 files changed, 233 insertions(+), 248 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9163f289..91f9f649 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" +checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -83,23 +83,28 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +checksum = "8ecb848c43f6b06ae3de2e4a67496cbbabd78ae87db0f1248934f15d76192c6a" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more 1.0.0", + "foldhash", "hex-literal", + "indexmap", "itoa", "k256", "keccak-asm", + "paste", "proptest", "rand", "ruint", + "rustc-hash", "serde", + "sha3 0.10.8", "tiny-keccak", ] @@ -122,7 +127,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -297,14 +302,14 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -522,9 +527,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "jobserver", "libc", @@ -627,9 +632,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" dependencies = [ "cfg-if", "cpufeatures", @@ -812,7 +817,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -832,7 +837,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "unicode-xid", ] @@ -921,7 +926,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -932,7 +937,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -944,7 +949,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era-compiler-common" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#20f20ca069c9bfea46e97d2cdadeac10ff198947" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#3207c451486e49df6e241167d8c6577222ecbd8e" dependencies = [ "anyhow", "base58", @@ -960,7 +965,7 @@ dependencies = [ [[package]] name = "era-compiler-downloader" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#20f20ca069c9bfea46e97d2cdadeac10ff198947" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#3207c451486e49df6e241167d8c6577222ecbd8e" dependencies = [ "anyhow", "colored", @@ -973,7 +978,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#8ee09a90e1969f98b175c49442b08805a8ce474e" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#736a2011b8de90887b429ded48556dd49333d365" dependencies = [ "anyhow", "era-compiler-common", @@ -987,8 +992,8 @@ dependencies = [ [[package]] name = "era-compiler-solidity" -version = "1.5.4" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#6519ed7753a5836337eaec93ba9b513a5821427e" +version = "1.5.5" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2de7a1a6d8f6c766a785d022fecb12b67fa2142e" dependencies = [ "anyhow", "era-compiler-common", @@ -1012,12 +1017,13 @@ dependencies = [ "structopt", "thiserror", "which", + "zkevm_opcode_defs 0.150.0 (git+https://github.com/matter-labs/era-zkevm_opcode_defs?branch=v1.5.0)", ] [[package]] name = "era-compiler-vyper" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#0d2782ad10645ffb6212f8010ce574b947cb8202" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#ff8f1dd7c1102c0c003cdb19b2a04e726c666abd" dependencies = [ "anyhow", "era-compiler-common", @@ -1039,8 +1045,8 @@ dependencies = [ [[package]] name = "era-yul" -version = "1.5.4" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#6519ed7753a5836337eaec93ba9b513a5821427e" +version = "1.5.5" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2de7a1a6d8f6c766a785d022fecb12b67fa2142e" dependencies = [ "anyhow", "regex", @@ -1183,6 +1189,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1215,9 +1227,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1230,9 +1242,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1240,15 +1252,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1257,32 +1269,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -1292,9 +1304,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1332,9 +1344,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -1382,6 +1394,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "headers" version = "0.3.9" @@ -1475,9 +1493,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1596,18 +1614,18 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] name = "inkwell" version = "0.4.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#993d3832f97b5e4127ab9c9cd2fc00b06d24d9fc" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c506921a593e3d1e11c3e84142d2a32a38d483fc" dependencies = [ "either", "inkwell_internals", @@ -1622,11 +1640,11 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.9.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#993d3832f97b5e4127ab9c9cd2fc00b06d24d9fc" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c506921a593e3d1e11c3e84142d2a32a38d483fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1654,9 +1672,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "itertools" @@ -1693,9 +1711,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "0cb94a0ffd3f3ee755c20f7d8752f45cac88605a4dcf808abcff72873296ec7b" dependencies = [ "wasm-bindgen", ] @@ -1759,9 +1777,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libfuzzer-sys" @@ -1810,7 +1828,7 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "llvm-sys" version = "170.0.1" -source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#a011658a7da7f977dff29cfb8d012dfbccc4470c" +source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#4b0421ff90757ed7a701ce5b81926515605dd4bd" dependencies = [ "anyhow", "cc", @@ -2038,23 +2056,23 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -2085,7 +2103,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2217,22 +2235,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2310,7 +2328,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.22.21", + "toml_edit 0.22.22", ] [[package]] @@ -2339,9 +2357,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -2466,18 +2484,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -2487,9 +2505,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -2504,9 +2522,9 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -2607,7 +2625,7 @@ dependencies = [ "cfg-if", "dyn-clone", "enumn", - "hashbrown", + "hashbrown 0.14.5", "hex", "serde", ] @@ -2704,6 +2722,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -2829,9 +2853,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -2979,7 +3003,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3258,9 +3282,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -3302,9 +3326,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -3339,7 +3363,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3442,13 +3466,13 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.6.18", + "winnow 0.6.20", ] [[package]] @@ -3476,7 +3500,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3502,9 +3526,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -3526,9 +3550,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -3648,9 +3672,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "ef073ced962d62984fb38a36e5fdc1a2b23c9e0e1fa0689bb97afa4202ef6887" dependencies = [ "cfg-if", "once_cell", @@ -3659,24 +3683,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "c4bfab14ef75323f4eb75fa52ee0a3fb59611977fd3240da19b2cf36ff85030e" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "65471f79c1022ffa5291d33520cbbb53b7687b01c2f8e83b57d102eed7ed479d" dependencies = [ "cfg-if", "js-sys", @@ -3686,9 +3710,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "a7bec9830f60924d9ceb3ef99d55c155be8afa76954edffbb5936ff4509474e7" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3696,28 +3720,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "4c74f6e152a76a2ad448e223b0fc0b6b5747649c3d769cc6bf45737bf97d0ed6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "a42f6c679374623f295a8623adfe63d9284091245c3504bde47c17a3ce2777d9" [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "44188d185b5bdcae1052d08bcbcf9091a5524038d4572cc4f4f2bb9d5554ddd9" dependencies = [ "js-sys", "wasm-bindgen", @@ -3931,9 +3955,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -3981,7 +4005,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4001,7 +4025,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4085,8 +4109,8 @@ dependencies = [ [[package]] name = "zksync_vm2" -version = "0.1.0" -source = "git+https://github.com/matter-labs/vm2#5e7f91d875a3cf9c7859e19ce5b709329bc7bd4d" +version = "0.2.1" +source = "git+https://github.com/matter-labs/vm2#a233d44bbe61dc6a758a754c3b78fe4f83e56699" dependencies = [ "enum_dispatch", "primitive-types", @@ -4097,8 +4121,8 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" -version = "0.1.0" -source = "git+https://github.com/matter-labs/vm2#5e7f91d875a3cf9c7859e19ce5b709329bc7bd4d" +version = "0.2.1" +source = "git+https://github.com/matter-labs/vm2#a233d44bbe61dc6a758a754c3b78fe4f83e56699" dependencies = [ "primitive-types", ] diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index 7770a510..a8452cc3 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -242,7 +242,7 @@ mod tests { verbosity: false, quiet: false, debug: false, - modes: vec!["Y+M3B3 0.8.26".to_owned()], + modes: vec!["Y+M3B3 0.8.27".to_owned()], paths: vec!["tests/solidity/simple/default.sol".to_owned()], groups: vec![], benchmark: None, diff --git a/compiler_tester/src/compilers/llvm/mod.rs b/compiler_tester/src/compilers/llvm/mod.rs index e635d0c3..607f6163 100644 --- a/compiler_tester/src/compilers/llvm/mod.rs +++ b/compiler_tester/src/compilers/llvm/mod.rs @@ -131,10 +131,7 @@ impl Compiler for LLVMCompiler { .into_iter() .map(|(path, build)| { let build = build.expect("Always valid"); - let build = EVMBuild::new( - era_compiler_llvm_context::EVMBuild::default(), - build.runtime_build, - ); + let build = EVMBuild::new(vec![], build.runtime_build); (path, build) }) .collect(); diff --git a/compiler_tester/src/compilers/solidity/upstream/mod.rs b/compiler_tester/src/compilers/solidity/upstream/mod.rs index 4c0cf29a..42b3854c 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mod.rs @@ -579,15 +579,10 @@ impl Compiler for SolidityCompiler { .object .as_str(); let build = EVMBuild::new( - era_compiler_llvm_context::EVMBuild::new( - hex::decode(bytecode_string).map_err(|error| { - anyhow::anyhow!( - "EVM bytecode of the contract `{path}` is invalid: {error}" - ) - })?, - None, - ), - era_compiler_llvm_context::EVMBuild::default(), + hex::decode(bytecode_string).map_err(|error| { + anyhow::anyhow!("EVM bytecode of the contract `{path}` is invalid: {error}") + })?, + vec![], ); builds.insert(path, build); } diff --git a/compiler_tester/src/compilers/yul/mod.rs b/compiler_tester/src/compilers/yul/mod.rs index 2c635fee..b8ddbfbd 100644 --- a/compiler_tester/src/compilers/yul/mod.rs +++ b/compiler_tester/src/compilers/yul/mod.rs @@ -187,13 +187,8 @@ impl Compiler for YulCompiler { })? .object .as_str(); - let build = EVMBuild::new( - era_compiler_llvm_context::EVMBuild::new( - hex::decode(bytecode_string).expect("Always valid"), - None, - ), - era_compiler_llvm_context::EVMBuild::default(), - ); + let build = + EVMBuild::new(hex::decode(bytecode_string).expect("Always valid"), vec![]); builds.insert(path, build); } } diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/evm_contract.rs b/compiler_tester/src/directories/matter_labs/test/metadata/evm_contract.rs index 0bcf1005..1bef195e 100644 --- a/compiler_tester/src/directories/matter_labs/test/metadata/evm_contract.rs +++ b/compiler_tester/src/directories/matter_labs/test/metadata/evm_contract.rs @@ -18,9 +18,9 @@ impl EVMContract { pub const RUNTIME_CODE_REPEATS: usize = 32; /// - /// Returns the init code. + /// Returns the deploy code. /// - pub fn init_code(&self, size: usize) -> String { + pub fn deploy_code(&self, size: usize) -> String { if size > 0xffff { panic!("The bytecode is too large"); } diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/mod.rs b/compiler_tester/src/directories/matter_labs/test/metadata/mod.rs index 44c7a198..940a4d23 100644 --- a/compiler_tester/src/directories/matter_labs/test/metadata/mod.rs +++ b/compiler_tester/src/directories/matter_labs/test/metadata/mod.rs @@ -27,7 +27,7 @@ pub struct Metadata { /// The test contracts as `instance -> path`. #[serde(default)] pub contracts: BTreeMap, - /// The EVM auxiliary contracts as `instance -> init code`. + /// The EVM auxiliary contracts as `instance -> deploy code`. #[serde(default)] pub evm_contracts: BTreeMap, /// The test libraries for linking. diff --git a/compiler_tester/src/directories/matter_labs/test/mod.rs b/compiler_tester/src/directories/matter_labs/test/mod.rs index 00cdc2e3..3cf54aac 100644 --- a/compiler_tester/src/directories/matter_labs/test/mod.rs +++ b/compiler_tester/src/directories/matter_labs/test/mod.rs @@ -274,7 +274,7 @@ impl MatterLabsTest { for (instance, evm_contract) in self.metadata.evm_contracts.iter() { let instruction_name = instance.split('_').next().expect("Always exists"); let runtime_code = evm_contract.runtime_code(instruction_name); - let mut bytecode = evm_contract.init_code(runtime_code.len()); + let mut bytecode = evm_contract.deploy_code(runtime_code.len()); bytecode.push_str(runtime_code.as_str()); let bytecode = hex::decode(bytecode.as_str()).map_err(|error| { diff --git a/compiler_tester/src/test/case/input/deploy_evm.rs b/compiler_tester/src/test/case/input/deploy_evm.rs index b6d9ca30..45f56422 100644 --- a/compiler_tester/src/test/case/input/deploy_evm.rs +++ b/compiler_tester/src/test/case/input/deploy_evm.rs @@ -2,8 +2,6 @@ //! The EVM deploy contract call input variant. //! -use std::collections::HashMap; -use std::hash::RandomState; use std::sync::Arc; use std::sync::Mutex; @@ -18,7 +16,6 @@ use crate::test::case::input::output::Output; use crate::test::case::input::storage::Storage; use crate::vm::eravm::deployers::EraVMDeployer; use crate::vm::eravm::EraVM; -use crate::vm::evm::input::build::Build; use crate::vm::evm::EVM; use crate::vm::revm::revm_type_conversions::revm_bytes_to_vec_value; @@ -32,8 +29,8 @@ use crate::vm::revm::Revm; pub struct DeployEVM { /// The contract identifier. identifier: String, - /// The contract init code. - init_code: Vec, + /// The contract deploy code. + deploy_code: Vec, /// The calldata. calldata: Calldata, /// The caller. @@ -52,7 +49,7 @@ impl DeployEVM { /// pub fn new( identifier: String, - init_code: Vec, + deploy_code: Vec, calldata: Calldata, caller: web3::types::Address, value: Option, @@ -61,7 +58,7 @@ impl DeployEVM { ) -> Self { Self { identifier, - init_code, + deploy_code, calldata, caller, value, @@ -131,20 +128,15 @@ impl DeployEVM { mode: Mode, test_group: Option, name_prefix: String, - evm_builds: &HashMap, evm_version: Option, ) -> Revm<'a> { let name = format!("{}[#deployer:{}]", name_prefix, self.identifier); - let build = evm_builds - .get(self.identifier.as_str()) - .expect("Always valid"); - let mut deploy_code = build.deploy_build.bytecode.to_owned(); - deploy_code.extend(self.calldata.inner.clone()); + let size = self.deploy_code.len(); let vm = vm.update_deploy_balance(&self.caller); let mut vm = - vm.fill_deploy_new_transaction(self.caller, self.value, evm_version, deploy_code); + vm.fill_deploy_new_transaction(self.caller, self.value, evm_version, self.deploy_code); let result = match vm.state.transact_commit() { Ok(res) => res, @@ -195,29 +187,27 @@ impl DeployEVM { } }; - let output = match result { + let (output, gas, error) = match result { ExecutionResult::Success { reason: _, - gas_used: _, + gas_used, gas_refunded: _, logs, output, - } => transform_success_output(output, logs), - ExecutionResult::Revert { - gas_used: _, - output, - } => { + } => (transform_success_output(output, logs), gas_used, None), + ExecutionResult::Revert { gas_used, output } => { let return_data_value = revm_bytes_to_vec_value(output); - Output::new(return_data_value, true, vec![]) + (Output::new(return_data_value, true, vec![]), gas_used, None) + } + ExecutionResult::Halt { reason, gas_used } => { + (Output::new(vec![], true, vec![]), gas_used, Some(reason)) } - ExecutionResult::Halt { - reason: _, - gas_used: _, - } => Output::new(vec![], true, vec![]), }; if output == self.expected { - Summary::passed_deploy(summary, mode, name, test_group, 0, 0, 0, 0); + Summary::passed_deploy(summary, mode, name, test_group, size, 0, 0, gas); + } else if let Some(error) = error { + Summary::invalid(summary, Some(mode), name, format!("{error:?}")); } else { Summary::failed( summary, @@ -248,13 +238,13 @@ impl DeployEVM { { let name = format!("{}[#deployer:{}]", name_prefix, self.identifier); - let size = self.init_code.len(); + let size = self.deploy_code.len(); vm.populate_storage(self.storage.inner); let result = match deployer.deploy_evm::( name.clone(), self.caller, - self.init_code, + self.deploy_code, self.calldata.inner.clone(), self.value, vm, diff --git a/compiler_tester/src/test/case/input/mod.rs b/compiler_tester/src/test/case/input/mod.rs index e254cca4..f1f6764d 100644 --- a/compiler_tester/src/test/case/input/mod.rs +++ b/compiler_tester/src/test/case/input/mod.rs @@ -13,8 +13,6 @@ pub mod storage_empty; pub mod value; use std::collections::BTreeMap; -use std::collections::HashMap; -use std::hash::RandomState; use std::str::FromStr; use std::sync::Arc; use std::sync::Mutex; @@ -27,7 +25,6 @@ use crate::summary::Summary; use crate::test::instance::Instance; use crate::vm::eravm::deployers::EraVMDeployer; use crate::vm::eravm::EraVM; -use crate::vm::evm::input::build::Build; use crate::vm::evm::EVM; use crate::vm::revm::Revm; @@ -124,7 +121,7 @@ impl Input { )), Instance::EVM(instance) => Input::DeployEVM(DeployEVM::new( instance.path.to_owned(), - instance.init_code.to_owned(), + instance.deploy_code.to_owned(), calldata, caller, value, @@ -263,7 +260,7 @@ impl Input { ))), Instance::EVM(instance) => Some(Input::DeployEVM(DeployEVM::new( instance.path.to_owned(), - instance.init_code.to_owned(), + instance.deploy_code.to_owned(), calldata.clone().into(), *caller, value, @@ -307,7 +304,7 @@ impl Input { ))), Instance::EVM(instance) => Some(Input::DeployEVM(DeployEVM::new( instance.path.to_owned(), - instance.init_code.to_owned(), + instance.deploy_code.to_owned(), Calldata::default(), *caller, None, @@ -444,20 +441,13 @@ impl Input { test_group: Option, name_prefix: String, index: usize, - evm_builds: &HashMap, evm_version: Option, ) -> Revm<'a> { match self { Self::DeployEraVM { .. } => panic!("EraVM deploy transaction cannot be run on REVM"), - Self::DeployEVM(deploy) => deploy.run_revm( - summary, - vm, - mode, - test_group, - name_prefix, - evm_builds, - evm_version, - ), + Self::DeployEVM(deploy) => { + deploy.run_revm(summary, vm, mode, test_group, name_prefix, evm_version) + } Self::Runtime(runtime) => runtime.run_revm( summary, vm, diff --git a/compiler_tester/src/test/case/mod.rs b/compiler_tester/src/test/case/mod.rs index 901e0bbd..6f8f58ad 100644 --- a/compiler_tester/src/test/case/mod.rs +++ b/compiler_tester/src/test/case/mod.rs @@ -6,8 +6,6 @@ pub mod input; use solidity_adapter::test::params::evm_version; use std::collections::BTreeMap; -use std::collections::HashMap; -use std::hash::RandomState; use std::sync::Arc; use std::sync::Mutex; @@ -17,7 +15,6 @@ use crate::summary::Summary; use crate::test::instance::Instance; use crate::vm::eravm::deployers::EraVMDeployer; use crate::vm::eravm::EraVM; -use crate::vm::evm::input::build::Build; use crate::vm::evm::EVM; use crate::vm::revm::Revm; @@ -167,7 +164,6 @@ impl Case { mode: &Mode, test_name: String, test_group: Option, - evm_builds: HashMap, evm_version: Option, ) { let name = if let Some(case_name) = self.name { @@ -185,7 +181,6 @@ impl Case { test_group.clone(), name.clone(), index, - &evm_builds, evm_version, ) } diff --git a/compiler_tester/src/test/instance/evm.rs b/compiler_tester/src/test/instance/evm.rs index d285d46c..618b6aab 100644 --- a/compiler_tester/src/test/instance/evm.rs +++ b/compiler_tester/src/test/instance/evm.rs @@ -15,8 +15,8 @@ pub struct Instance { pub is_main: bool, /// Whether the instance is a library. pub is_library: bool, - /// The init bytecode. - pub init_code: Vec, + /// The deploy bytecode. + pub deploy_code: Vec, } impl Instance { @@ -28,14 +28,14 @@ impl Instance { address: Option, is_main: bool, is_library: bool, - init_code: Vec, + deploy_code: Vec, ) -> Self { Self { path, address, is_main, is_library, - init_code, + deploy_code, } } } diff --git a/compiler_tester/src/test/instance/mod.rs b/compiler_tester/src/test/instance/mod.rs index 751124c5..d51e5610 100644 --- a/compiler_tester/src/test/instance/mod.rs +++ b/compiler_tester/src/test/instance/mod.rs @@ -44,10 +44,14 @@ impl Instance { address: Option, is_main: bool, is_library: bool, - init_code: Vec, + deploy_code: Vec, ) -> Self { Self::EVM(EVMInstance::new( - path, address, is_main, is_library, init_code, + path, + address, + is_main, + is_library, + deploy_code, )) } diff --git a/compiler_tester/src/test/mod.rs b/compiler_tester/src/test/mod.rs index a128f539..3e77b457 100644 --- a/compiler_tester/src/test/mod.rs +++ b/compiler_tester/src/test/mod.rs @@ -117,7 +117,6 @@ impl Test { &self.mode, self.name.clone(), self.group.clone(), - self.evm_builds.clone(), self.evm_version, ); } diff --git a/compiler_tester/src/vm/eravm/deployers/dummy_deployer.rs b/compiler_tester/src/vm/eravm/deployers/dummy_deployer.rs index efd947bd..b7fda707 100644 --- a/compiler_tester/src/vm/eravm/deployers/dummy_deployer.rs +++ b/compiler_tester/src/vm/eravm/deployers/dummy_deployer.rs @@ -100,7 +100,7 @@ impl EraVMDeployer for DummyDeployer { &mut self, _test_name: String, _caller: web3::types::Address, - _init_code: Vec, + _deploy_code: Vec, _constructor_calldata: Vec, _value: Option, _vm: &mut EraVM, diff --git a/compiler_tester/src/vm/eravm/deployers/mod.rs b/compiler_tester/src/vm/eravm/deployers/mod.rs index 8a45f7fb..338892f2 100644 --- a/compiler_tester/src/vm/eravm/deployers/mod.rs +++ b/compiler_tester/src/vm/eravm/deployers/mod.rs @@ -37,7 +37,7 @@ pub trait EraVMDeployer { &mut self, test_name: String, caller: web3::types::Address, - init_code: Vec, + deploy_code: Vec, constructor_calldata: Vec, value: Option, vm: &mut EraVM, diff --git a/compiler_tester/src/vm/eravm/deployers/system_contract_deployer.rs b/compiler_tester/src/vm/eravm/deployers/system_contract_deployer.rs index f185b3e9..dabbf3cf 100644 --- a/compiler_tester/src/vm/eravm/deployers/system_contract_deployer.rs +++ b/compiler_tester/src/vm/eravm/deployers/system_contract_deployer.rs @@ -122,7 +122,7 @@ impl EraVMDeployer for SystemContractDeployer { &mut self, test_name: String, caller: web3::types::Address, - init_code: Vec, + deploy_code: Vec, constructor_calldata: Vec, value: Option, vm: &mut EraVM, @@ -192,7 +192,7 @@ impl EraVMDeployer for SystemContractDeployer { let mut calldata = Vec::with_capacity( era_compiler_common::BYTE_LENGTH_X32 + era_compiler_common::BYTE_LENGTH_FIELD * 2 - + init_code.len() + + deploy_code.len() + constructor_calldata.len(), ); calldata.extend(crate::utils::selector(Self::EVM_CREATE_METHOD_SIGNATURE)); @@ -202,11 +202,11 @@ impl EraVMDeployer for SystemContractDeployer { ); calldata.extend( web3::types::H256::from_low_u64_be( - (init_code.len() + constructor_calldata.len()) as u64, + (deploy_code.len() + constructor_calldata.len()) as u64, ) .as_bytes(), ); - calldata.extend(init_code); + calldata.extend(deploy_code); calldata.extend(constructor_calldata); vm.execute::( diff --git a/compiler_tester/src/vm/eravm/mod.rs b/compiler_tester/src/vm/eravm/mod.rs index 86ba368f..5cd6c8f9 100644 --- a/compiler_tester/src/vm/eravm/mod.rs +++ b/compiler_tester/src/vm/eravm/mod.rs @@ -136,7 +136,7 @@ impl EraVM { ), evm_interpreter_code_hash: web3::types::U256::from_big_endian( system_contracts - .evm_interpreter + .evm_emulator .bytecode_hash .expect("Always exists") .as_slice(), @@ -159,10 +159,10 @@ impl EraVM { ), ); vm.add_known_contract( - system_contracts.evm_interpreter.bytecode, + system_contracts.evm_emulator.bytecode, web3::types::U256::from_big_endian( system_contracts - .evm_interpreter + .evm_emulator .bytecode_hash .expect("Always exists") .as_slice(), diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index 176d9fbc..c412f35a 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -31,8 +31,8 @@ pub struct SystemContracts { pub deployed_contracts: Vec<(web3::types::Address, era_compiler_llvm_context::EraVMBuild)>, /// The default account abstraction contract build. pub default_aa: era_compiler_llvm_context::EraVMBuild, - /// The EVM interpreter contract build. - pub evm_interpreter: era_compiler_llvm_context::EraVMBuild, + /// The EVM emulator contract build. + pub evm_emulator: era_compiler_llvm_context::EraVMBuild, } impl SystemContracts { @@ -44,9 +44,9 @@ impl SystemContracts { const PATH_DEFAULT_AA: &'static str = "era-contracts/system-contracts/contracts/DefaultAccount.sol:DefaultAccount"; - /// The EVM interpreter system contract implementation path. - const PATH_EVM_INTERPRETER: &'static str = - "era-contracts/system-contracts/contracts/EvmInterpreter.yul"; + /// The EVM emulator system contract implementation path. + const PATH_EVM_EMULATOR: &'static str = + "era-contracts/system-contracts/contracts/EvmEmulator.yul"; /// The `keccak256` system contract implementation path. const PATH_KECCAK256: &'static str = @@ -251,7 +251,7 @@ impl SystemContracts { for (_, path) in yul_system_contracts.into_iter() { yul_file_paths.push(path.to_owned()); } - yul_file_paths.push(Self::PATH_EVM_INTERPRETER.to_owned()); + yul_file_paths.push(Self::PATH_EVM_EMULATOR.to_owned()); let yul_optimizer_settings = era_compiler_llvm_context::OptimizerSettings::cycles(); let yul_mode = YulMode::new(yul_optimizer_settings, true).into(); let yul_llvm_options = vec![ @@ -311,8 +311,8 @@ impl SystemContracts { let default_aa = builds.remove(Self::PATH_DEFAULT_AA).ok_or_else(|| { anyhow::anyhow!("The default AA code not found in the compiler build artifacts") })?; - let evm_interpreter = builds.remove(Self::PATH_EVM_INTERPRETER).ok_or_else(|| { - anyhow::anyhow!("The EVM interpreter code not found in the compiler build artifacts") + let evm_emulator = builds.remove(Self::PATH_EVM_EMULATOR).ok_or_else(|| { + anyhow::anyhow!("The EVM emulator code not found in the compiler build artifacts") })?; let mut system_contracts = @@ -338,7 +338,7 @@ impl SystemContracts { Ok(Self { deployed_contracts, default_aa, - evm_interpreter, + evm_emulator, }) } diff --git a/compiler_tester/src/vm/evm/input/build.rs b/compiler_tester/src/vm/evm/input/build.rs index 8579fa2d..3c9aa198 100644 --- a/compiler_tester/src/vm/evm/input/build.rs +++ b/compiler_tester/src/vm/evm/input/build.rs @@ -8,19 +8,16 @@ #[derive(Debug, Clone)] pub struct Build { /// The contract deploy build. - pub deploy_build: era_compiler_llvm_context::EVMBuild, + pub deploy_build: Vec, /// The contract runtime build. - pub runtime_build: era_compiler_llvm_context::EVMBuild, + pub runtime_build: Vec, } impl Build { /// /// A shortcut constructor. /// - pub fn new( - deploy_build: era_compiler_llvm_context::EVMBuild, - runtime_build: era_compiler_llvm_context::EVMBuild, - ) -> Self { + pub fn new(deploy_build: Vec, runtime_build: Vec) -> Self { Self { deploy_build, runtime_build, diff --git a/compiler_tester/src/vm/evm/input/mod.rs b/compiler_tester/src/vm/evm/input/mod.rs index c9435653..abe25522 100644 --- a/compiler_tester/src/vm/evm/input/mod.rs +++ b/compiler_tester/src/vm/evm/input/mod.rs @@ -56,15 +56,12 @@ impl Input { anyhow::anyhow!("Library `{}` not found in the build artifacts", name) })?; + let mut deploy_code = build.deploy_build.to_owned(); + deploy_code.extend_from_slice(build.runtime_build.as_slice()); + instances.insert( name.clone(), - Instance::evm( - name, - Some(address), - false, - true, - build.deploy_build.bytecode.to_owned(), - ), + Instance::evm(name, Some(address), false, true, deploy_code), ); } @@ -75,6 +72,10 @@ impl Input { .ok_or_else(|| { anyhow::anyhow!("Main contract not found in the compiler build artifacts") })?; + + let mut deploy_code = main_contract_build.deploy_build.to_owned(); + deploy_code.extend_from_slice(main_contract_build.runtime_build.as_slice()); + instances.insert( "Test".to_owned(), Instance::evm( @@ -82,7 +83,7 @@ impl Input { main_address, true, false, - main_contract_build.deploy_build.bytecode.to_owned(), + deploy_code, ), ); } else { @@ -91,15 +92,13 @@ impl Input { anyhow::anyhow!("{} not found in the compiler build artifacts", path) })?; let is_main = path.as_str() == self.last_contract.as_str(); + + let mut deploy_code = build.deploy_build.to_owned(); + deploy_code.extend_from_slice(build.runtime_build.as_slice()); + instances.insert( instance.to_owned(), - Instance::evm( - path.to_owned(), - None, - is_main, - false, - build.deploy_build.bytecode.to_owned(), - ), + Instance::evm(path.to_owned(), None, is_main, false, deploy_code), ); } } diff --git a/compiler_tester/src/vm/evm/mod.rs b/compiler_tester/src/vm/evm/mod.rs index 4f3d8085..162cda66 100644 --- a/compiler_tester/src/vm/evm/mod.rs +++ b/compiler_tester/src/vm/evm/mod.rs @@ -89,7 +89,7 @@ impl<'evm> EVM<'evm> { constructor_args: Vec, ) -> anyhow::Result { let build = self.builds.get(path).expect("Always valid"); - let mut deploy_code = build.deploy_build.bytecode.to_owned(); + let mut deploy_code = build.deploy_build.to_owned(); deploy_code.extend(constructor_args); self.runtime diff --git a/era-contracts b/era-contracts index d76a8d15..7827bf3c 160000 --- a/era-contracts +++ b/era-contracts @@ -1 +1 @@ -Subproject commit d76a8d15b3800e85fc02b7f1dc8ec44ddc68e24c +Subproject commit 7827bf3c74be6490e0218aa5aeb606ba6919401b diff --git a/system-contracts-stable-build b/system-contracts-stable-build index 16b7e15445e0563be2f0f84750f63b7fa652f4fe..28bc182ba83102f2b208edbde4534f3dca6b90f1 100644 GIT binary patch delta 162769 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 vvAt^_`AC1hQdGZ}*rmp@U^1~6q delta 136363 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 From 237ff47399f62f5dafe4342c1fbd40af2a8413d7 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 18 Oct 2024 17:09:27 +0200 Subject: [PATCH 14/31] chore: Update contracts (#115) --- compiler_tester/src/vm/eravm/system_contracts.rs | 10 +++++----- era-contracts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index c412f35a..3ca04df4 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -114,7 +114,7 @@ impl SystemContracts { /// The EVM gas manager system contract implementation path. const PATH_EVM_GAS_MANAGER: &'static str = - "era-contracts/system-contracts/contracts/EvmGasManager.sol:EvmGasManager"; + "era-contracts/system-contracts/contracts/EvmGasManager.yul"; /// /// Loads or builds the system contracts. @@ -187,6 +187,10 @@ impl SystemContracts { web3::types::Address::from_low_u64_be(0x8012), Self::PATH_CODE_ORACLE, ), + ( + web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), + Self::PATH_EVM_GAS_MANAGER, + ), ]; let solidity_system_contracts = vec![ @@ -241,10 +245,6 @@ impl SystemContracts { web3::types::Address::from_low_u64_be(zkevm_opcode_defs::ADDRESS_ETH_TOKEN.into()), Self::PATH_BASE_TOKEN, ), - ( - web3::types::Address::from_low_u64_be(ADDRESS_EVM_GAS_MANAGER.into()), - Self::PATH_EVM_GAS_MANAGER, - ), ]; let mut yul_file_paths = Vec::with_capacity(yul_system_contracts.len() + 1); diff --git a/era-contracts b/era-contracts index 7827bf3c..a3fc4b02 160000 --- a/era-contracts +++ b/era-contracts @@ -1 +1 @@ -Subproject commit 7827bf3c74be6490e0218aa5aeb606ba6919401b +Subproject commit a3fc4b02c19bd5f2c2c3c004342f094e2c6a83b9 From 5c4da35578b89abdbce8f26da8f988e0fc6e8106 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic <129192835+vladimirradosavljevic@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:05:38 +0200 Subject: [PATCH 15/31] chore: update contracts and tweak options for lazy stack implementation (#98) Signed-off-by: Vladimir Radosavljevic --- compiler_tester/src/vm/eravm/system_contracts.rs | 4 +++- era-contracts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index 3ca04df4..a6842843 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -258,9 +258,11 @@ impl SystemContracts { "-eravm-jump-table-density-threshold", "10", "-tail-dup-size", - "4", + "6", "-eravm-enable-split-loop-phi-live-ranges", "-tail-merge-only-bbs-without-succ", + "-join-globalcopies", + "-disable-early-taildup", ] .into_iter() .map(|option| option.to_owned()) diff --git a/era-contracts b/era-contracts index a3fc4b02..31465df1 160000 --- a/era-contracts +++ b/era-contracts @@ -1 +1 @@ -Subproject commit a3fc4b02c19bd5f2c2c3c004342f094e2c6a83b9 +Subproject commit 31465df18b212c420c288bf8c05af7b47046a369 From 1939677d1d942163f06ab0639fb2b762a4f7961c Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Thu, 31 Oct 2024 11:51:31 +0800 Subject: [PATCH 16/31] fix: test zksolc interface changes after unification (#117) --- Cargo.lock | 143 ++++++++-------- LLVM.lock | 2 +- README.md | 2 +- compiler_tester/src/compilers/eravm/mod.rs | 5 +- compiler_tester/src/compilers/llvm/mod.rs | 10 +- compiler_tester/src/compilers/mod.rs | 6 +- .../src/compilers/solidity/cache_key.rs | 6 +- compiler_tester/src/compilers/solidity/mod.rs | 158 +++++++++--------- .../src/compilers/solidity/mode.rs | 24 +-- .../src/compilers/solidity/upstream/mod.rs | 48 +++--- .../src/compilers/solidity/upstream/mode.rs | 24 +-- .../upstream/solc/standard_json/input/mod.rs | 2 +- .../solc/standard_json/input/settings/mod.rs | 12 +- .../input/settings/selection/file/flag.rs | 10 +- .../input/settings/selection/file/mod.rs | 6 +- .../input/settings/selection/mod.rs | 6 +- compiler_tester/src/compilers/vyper/mod.rs | 4 +- compiler_tester/src/compilers/yul/mod.rs | 34 ++-- .../src/directories/ethereum/test.rs | 4 +- .../src/directories/matter_labs/test/mod.rs | 4 +- compiler_tester/src/vm/eravm/mod.rs | 2 +- .../src/vm/eravm/system_contracts.rs | 5 +- era-contracts | 2 +- fuzzer/fuzz_targets/common.rs | 4 +- system-contracts-stable-build | Bin 979219 -> 946341 bytes 25 files changed, 271 insertions(+), 252 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1709d34..7597d0fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f35429a652765189c1c5092870d8360ee7b7769b09b06d89ebaefd34676446" +checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" dependencies = [ "alloy-rlp", "bytes", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" dependencies = [ "alloy-rlp-derive", "arrayvec 0.7.6", @@ -121,13 +121,13 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" +checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -302,7 +302,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -486,6 +486,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + [[package]] name = "bumpalo" version = "3.16.0" @@ -506,9 +512,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "c-kzg" @@ -527,9 +533,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.30" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", @@ -817,7 +823,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -837,7 +843,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", "unicode-xid", ] @@ -910,9 +916,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -926,7 +932,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -937,7 +943,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -949,7 +955,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era-compiler-common" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ce91855778bd5e1821f9b46751f92bafafd88435" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#11cc7ed039fb96ef04d45d71c009e8dc8fffda9a" dependencies = [ "anyhow", "base58", @@ -964,8 +970,8 @@ dependencies = [ [[package]] name = "era-compiler-downloader" -version = "0.1.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ce91855778bd5e1821f9b46751f92bafafd88435" +version = "1.5.0" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#11cc7ed039fb96ef04d45d71c009e8dc8fffda9a" dependencies = [ "anyhow", "colored", @@ -978,7 +984,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#23bd5c502ab9d246c44ed61802153de47a7d9dfd" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#67bb2349e221627f15b09893e59f807094ab81ca" dependencies = [ "anyhow", "era-compiler-common", @@ -993,9 +999,10 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.6" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#12befad5d65a40166a8ffc6fd4f2008cb92a173b" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2b32d2de6039165bb91569375673fd750c8a10b0" dependencies = [ "anyhow", + "boolinator", "era-compiler-common", "era-compiler-llvm-context", "era-yul", @@ -1022,8 +1029,8 @@ dependencies = [ [[package]] name = "era-compiler-vyper" -version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#be758e3a119f45e960f796f177db2de5d56c72ab" +version = "1.5.6" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#90a858bc42d1a83be3fe95beb858f60c14f910cb" dependencies = [ "anyhow", "era-compiler-common", @@ -1046,7 +1053,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.6" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#12befad5d65a40166a8ffc6fd4f2008cb92a173b" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2b32d2de6039165bb91569375673fd750c8a10b0" dependencies = [ "anyhow", "regex", @@ -1281,7 +1288,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -1644,7 +1651,7 @@ source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c50692 dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -1777,9 +1784,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libfuzzer-sys" @@ -1794,9 +1801,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libmimalloc-sys" @@ -2056,7 +2063,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -2082,9 +2089,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.67" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8cefcf97f41316955f9294cd61f639bdcfa9f2f230faac6cb896aa8ab64704" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -2103,7 +2110,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -2114,9 +2121,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.3.2+3.3.2" +version = "300.4.0+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" dependencies = [ "cc", ] @@ -2235,29 +2242,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2357,9 +2364,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -2754,9 +2761,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags 2.6.0", "errno", @@ -2828,27 +2835,27 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" dependencies = [ "bitvec", "cfg-if", - "derive_more 0.99.18", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", ] [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.85", ] [[package]] @@ -3003,7 +3010,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -3282,9 +3289,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -3363,7 +3370,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -3401,9 +3408,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -3500,7 +3507,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -3692,7 +3699,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", "wasm-bindgen-shared", ] @@ -3726,7 +3733,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4005,7 +4012,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -4025,7 +4032,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -4095,7 +4102,7 @@ dependencies = [ [[package]] name = "zksync_vm2" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2#a233d44bbe61dc6a758a754c3b78fe4f83e56699" +source = "git+https://github.com/matter-labs/vm2#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" dependencies = [ "enum_dispatch", "primitive-types", @@ -4107,7 +4114,7 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2#a233d44bbe61dc6a758a754c3b78fe4f83e56699" +source = "git+https://github.com/matter-labs/vm2#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" dependencies = [ "primitive-types", ] diff --git a/LLVM.lock b/LLVM.lock index 9b393bc3..15b374ab 100644 --- a/LLVM.lock +++ b/LLVM.lock @@ -1,2 +1,2 @@ url = "https://github.com/matter-labs/era-compiler-llvm" -branch = "main" +branch = "kpv-eravm-fix-linker-duplicating-symbols" diff --git a/README.md b/README.md index a802f68e..e9834f0f 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ When testing is done, these tags should be removed. - [0.4.10; latest] for compiling Solidity via EVM assembly - [0.3.3, 0.3.9] for compiling Vyper via LLL IR -### Compiler pipelines +### Compiler codegens Currently only relevant for the Solidity compiler, where you can choose the IR: diff --git a/compiler_tester/src/compilers/eravm/mod.rs b/compiler_tester/src/compilers/eravm/mod.rs index e47a77e3..b9a6d598 100644 --- a/compiler_tester/src/compilers/eravm/mod.rs +++ b/compiler_tester/src/compilers/eravm/mod.rs @@ -27,7 +27,7 @@ impl Compiler for EraVMCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - _libraries: BTreeMap>, + _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, _mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -54,6 +54,7 @@ impl Compiler for EraVMCompiler { let build = project.compile_to_eravm( &mut vec![], true, + BTreeMap::new(), era_compiler_common::HashType::Ipfs, era_compiler_llvm_context::OptimizerSettings::none(), llvm_options, @@ -75,7 +76,7 @@ impl Compiler for EraVMCompiler { &self, _test_path: String, _sources: Vec<(String, String)>, - _libraries: BTreeMap>, + _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, _mode: &Mode, _test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/compilers/llvm/mod.rs b/compiler_tester/src/compilers/llvm/mod.rs index 607f6163..333cca86 100644 --- a/compiler_tester/src/compilers/llvm/mod.rs +++ b/compiler_tester/src/compilers/llvm/mod.rs @@ -4,7 +4,6 @@ pub mod mode; -use std::collections::BTreeMap; use std::collections::HashMap; use era_compiler_solidity::CollectableError; @@ -40,7 +39,7 @@ impl Compiler for LLVMCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - _libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -53,6 +52,8 @@ impl Compiler for LLVMCompiler { .0 .clone(); + let linker_symbols = libraries.as_linker_symbols()?; + let project = era_compiler_solidity::Project::try_from_llvm_ir_sources( sources .into_iter() @@ -63,12 +64,14 @@ impl Compiler for LLVMCompiler { ) }) .collect(), + libraries, None, )?; let build = project.compile_to_eravm( &mut vec![], true, + linker_symbols, era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, @@ -90,7 +93,7 @@ impl Compiler for LLVMCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - _libraries: BTreeMap>, + _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, _test_params: Option<&solidity_adapter::Params>, llvm_options: Vec, @@ -114,6 +117,7 @@ impl Compiler for LLVMCompiler { ) }) .collect(), + era_compiler_solidity::SolcStandardJsonInputSettingsLibraries::default(), None, )?; diff --git a/compiler_tester/src/compilers/mod.rs b/compiler_tester/src/compilers/mod.rs index c29d7ee9..54162604 100644 --- a/compiler_tester/src/compilers/mod.rs +++ b/compiler_tester/src/compilers/mod.rs @@ -10,8 +10,6 @@ pub mod solidity; pub mod vyper; pub mod yul; -use std::collections::BTreeMap; - use crate::vm::eravm::input::Input as EraVMInput; use crate::vm::evm::input::Input as EVMInput; @@ -28,7 +26,7 @@ pub trait Compiler: Send + Sync + 'static { &self, test_path: String, sources: Vec<(String, String)>, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -41,7 +39,7 @@ pub trait Compiler: Send + Sync + 'static { &self, test_path: String, sources: Vec<(String, String)>, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, llvm_options: Vec, diff --git a/compiler_tester/src/compilers/solidity/cache_key.rs b/compiler_tester/src/compilers/solidity/cache_key.rs index 2bb9e654..3968f716 100644 --- a/compiler_tester/src/compilers/solidity/cache_key.rs +++ b/compiler_tester/src/compilers/solidity/cache_key.rs @@ -12,7 +12,7 @@ pub struct CacheKey { /// The Solidity compiler version. pub version: semver::Version, /// The Solidity compiler output type. - pub pipeline: era_compiler_solidity::SolcPipeline, + pub codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, /// Whether to enable the EVMLA codegen via Yul IR. pub via_ir: bool, /// Whether to run the Solidity compiler optimizer. @@ -26,14 +26,14 @@ impl CacheKey { pub fn new( test_path: String, version: semver::Version, - pipeline: era_compiler_solidity::SolcPipeline, + codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, via_ir: bool, optimize: bool, ) -> Self { Self { test_path, version, - pipeline, + codegen, via_ir, optimize, } diff --git a/compiler_tester/src/compilers/solidity/mod.rs b/compiler_tester/src/compilers/solidity/mod.rs index f2fd25a8..c534d105 100644 --- a/compiler_tester/src/compilers/solidity/mod.rs +++ b/compiler_tester/src/compilers/solidity/mod.rs @@ -7,6 +7,7 @@ pub mod mode; pub mod upstream; use std::collections::BTreeMap; +use std::collections::BTreeSet; use std::collections::HashMap; use std::path::Path; @@ -39,28 +40,26 @@ lazy_static::lazy_static! { /// All compilers must be downloaded before initialization. /// static ref MODES: Vec = { - let mut solc_pipeline_versions = Vec::new(); - for (pipeline, optimize, via_ir) in [ - (era_compiler_solidity::SolcPipeline::EVMLA, false, false), - (era_compiler_solidity::SolcPipeline::EVMLA, true, false), - (era_compiler_solidity::SolcPipeline::EVMLA, true, true), - (era_compiler_solidity::SolcPipeline::Yul, false, true), - (era_compiler_solidity::SolcPipeline::Yul, true, true), + let mut solc_codegen_versions = Vec::new(); + for (codegen, optimize, via_ir) in [ + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, false), + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, true), + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true, true), ] { - for version in SolidityCompiler::all_versions(pipeline, via_ir).expect("`solc` versions analysis error") { - solc_pipeline_versions.push((pipeline, optimize, via_ir, version)); + for version in SolidityCompiler::all_versions(codegen, via_ir).expect("`solc` versions analysis error") { + solc_codegen_versions.push((codegen, optimize, via_ir, version)); } } era_compiler_llvm_context::OptimizerSettings::combinations() .into_iter() - .cartesian_product(solc_pipeline_versions) + .cartesian_product(solc_codegen_versions) .map( - |(mut llvm_optimizer_settings, (pipeline, optimize, via_ir, version))| { + |(mut llvm_optimizer_settings, (codegen, optimize, via_ir, version))| { llvm_optimizer_settings.enable_fallback_to_size(); SolidityMode::new( version, - pipeline, + codegen, via_ir, optimize, llvm_optimizer_settings, @@ -102,7 +101,7 @@ impl SolidityCompiler { pub fn executable( version: &semver::Version, ) -> anyhow::Result { - era_compiler_solidity::SolcCompiler::new( + era_compiler_solidity::SolcCompiler::try_from_path( format!("{}/solc-{}", Self::DIRECTORY, version).as_str(), ) } @@ -111,16 +110,16 @@ impl SolidityCompiler { /// Returns the `solc` executable used to compile system contracts. /// pub fn system_contract_executable() -> anyhow::Result { - era_compiler_solidity::SolcCompiler::new( + era_compiler_solidity::SolcCompiler::try_from_path( format!("{}/solc-system-contracts", Self::DIRECTORY).as_str(), ) } /// - /// Returns the compiler versions downloaded for the specified compilation pipeline. + /// Returns the compiler versions downloaded for the specified compilation codegen. /// pub fn all_versions( - pipeline: era_compiler_solidity::SolcPipeline, + codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, via_ir: bool, ) -> anyhow::Result> { let mut versions = Vec::new(); @@ -150,12 +149,12 @@ impl SolidityCompiler { Ok(version) => version, Err(_) => continue, }; - if era_compiler_solidity::SolcPipeline::Yul == pipeline + if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul == codegen && version < era_compiler_solidity::SolcCompiler::FIRST_YUL_VERSION { continue; } - if era_compiler_solidity::SolcPipeline::EVMLA == pipeline + if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA == codegen && via_ir && version < era_compiler_solidity::SolcCompiler::FIRST_VIA_IR_VERSION { @@ -172,7 +171,7 @@ impl SolidityCompiler { /// fn standard_json_output( sources: &[(String, String)], - libraries: &BTreeMap>, + libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &SolidityMode, ) -> anyhow::Result { let solc_compiler = if mode.is_system_contracts_mode { @@ -182,42 +181,48 @@ impl SolidityCompiler { }?; let mut output_selection = - era_compiler_solidity::SolcStandardJsonInputSettingsSelection::new_required(Some( - mode.solc_pipeline, - )); - output_selection.extend_with_eravm_assembly(); - - let optimizer = era_compiler_solidity::SolcStandardJsonInputSettingsOptimizer::new( - mode.solc_optimize, - None, - &mode.solc_version, - false, + era_compiler_solidity::SolcStandardJsonInputSettingsSelection::new_required( + mode.solc_codegen, + ); + output_selection.extend( + era_compiler_solidity::SolcStandardJsonInputSettingsSelection::new(vec![ + era_compiler_solidity::SolcStandardJsonInputSettingsSelectionFlag::EraVMAssembly, + ]), ); - let evm_version = if mode.solc_version >= semver::Version::new(0, 8, 24) - /* TODO */ - { - Some(era_compiler_common::EVMVersion::Cancun) - } else { - None - }; + let evm_version = + if mode.solc_version >= era_compiler_solidity::SolcCompiler::FIRST_CANCUN_VERSION { + Some(era_compiler_common::EVMVersion::Cancun) + } else { + None + }; + + let sources: BTreeMap = sources + .iter() + .map(|(path, source)| { + ( + path.to_owned(), + era_compiler_solidity::SolcStandardJsonInputSource::from(source.to_owned()), + ) + }) + .collect(); let mut solc_input = era_compiler_solidity::SolcStandardJsonInput::try_from_solidity_sources( + sources, + libraries.to_owned(), + BTreeSet::new(), + era_compiler_solidity::SolcStandardJsonInputSettingsOptimizer::default(), + Some(mode.solc_codegen), evm_version, - sources.iter().cloned().collect(), - libraries.clone(), - None, - output_selection, - optimizer, - None, - mode.solc_pipeline == era_compiler_solidity::SolcPipeline::EVMLA, - mode.via_ir, mode.enable_eravm_extensions, - false, + output_selection, + era_compiler_solidity::SolcStandardJsonInputSettingsMetadata::default(), vec![], - vec![era_compiler_solidity::MessageType::SendTransfer], + vec![era_compiler_solidity::ErrorType::SendTransfer], vec![], + false, + mode.via_ir, ) .map_err(|error| anyhow::anyhow!("Solidity standard JSON I/O error: {}", error))?; @@ -229,7 +234,7 @@ impl SolidityCompiler { solc_compiler.standard_json( &mut solc_input, - Some(mode.solc_pipeline), + Some(mode.solc_codegen), &mut vec![], None, vec![], @@ -244,13 +249,13 @@ impl SolidityCompiler { &self, test_path: String, sources: &[(String, String)], - libraries: &BTreeMap>, + libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &SolidityMode, ) -> anyhow::Result { let cache_key = CacheKey::new( test_path, mode.solc_version.clone(), - mode.solc_pipeline, + mode.solc_codegen, mode.via_ir, mode.solc_optimize, ); @@ -270,14 +275,9 @@ impl SolidityCompiler { fn get_method_identifiers( solc_output: &era_compiler_solidity::SolcStandardJsonOutput, ) -> anyhow::Result>> { - let files = solc_output - .contracts - .as_ref() - .ok_or_else(|| anyhow::anyhow!("Solidity contracts not found in the output"))?; - let mut method_identifiers = BTreeMap::new(); - for (path, contracts) in files.iter() { - for (name, contract) in contracts.iter() { + for (path, file) in solc_output.contracts.iter() { + for (name, contract) in file.iter() { let mut contract_identifiers = BTreeMap::new(); for (entry, selector) in contract .evm @@ -320,28 +320,18 @@ impl SolidityCompiler { solc_output: &era_compiler_solidity::SolcStandardJsonOutput, sources: &[(String, String)], ) -> anyhow::Result { - solc_output - .sources - .as_ref() - .ok_or_else(|| { - anyhow::anyhow!( - "The Solidity sources are empty. Found errors: {:?}", - solc_output.errors - ) - }) - .and_then(|output_sources| { - for (path, _source) in sources.iter().rev() { - match output_sources - .get(path) - .ok_or_else(|| anyhow::anyhow!("The last source not found in the output"))? - .last_contract_name() - { - Ok(name) => return Ok(format!("{path}:{name}")), - Err(_error) => continue, - } - } - anyhow::bail!("The last source not found in the output") - }) + for (path, _source) in sources.iter().rev() { + match solc_output + .sources + .get(path) + .ok_or_else(|| anyhow::anyhow!("The last source not found in the output"))? + .last_contract_name() + { + Ok(name) => return Ok(format!("{path}:{name}")), + Err(_error) => continue, + } + } + anyhow::bail!("The last source not found in the output") } } @@ -350,7 +340,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -374,9 +364,11 @@ impl Compiler for SolidityCompiler { SolidityCompiler::executable(&mode.solc_version) }?; + let linker_symbols = libraries.as_linker_symbols()?; + let project = era_compiler_solidity::Project::try_from_solc_output( libraries, - mode.solc_pipeline, + mode.solc_codegen, &mut solc_output, &solc_compiler, debug_config.as_ref(), @@ -385,6 +377,7 @@ impl Compiler for SolidityCompiler { let build = project.compile_to_eravm( &mut vec![], mode.enable_eravm_extensions, + linker_symbols, era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, @@ -415,7 +408,6 @@ impl Compiler for SolidityCompiler { mode.solc_version.to_owned(), None, )), - &semver::Version::new(0, 0, 0), )?; solc_output.collect_errors()?; @@ -430,7 +422,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, _test_params: Option<&solidity_adapter::Params>, llvm_options: Vec, @@ -450,7 +442,7 @@ impl Compiler for SolidityCompiler { let project = era_compiler_solidity::Project::try_from_solc_output( libraries, - mode.solc_pipeline, + mode.solc_codegen, &mut solc_output, &solc_compiler, debug_config.as_ref(), diff --git a/compiler_tester/src/compilers/solidity/mode.rs b/compiler_tester/src/compilers/solidity/mode.rs index 296abf7f..45d3d0c2 100644 --- a/compiler_tester/src/compilers/solidity/mode.rs +++ b/compiler_tester/src/compilers/solidity/mode.rs @@ -16,7 +16,7 @@ pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, /// The Solidity compiler output type. - pub solc_pipeline: era_compiler_solidity::SolcPipeline, + pub solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, /// Whether to enable the EVMLA codegen via Yul IR. pub via_ir: bool, /// Whether to run the Solidity compiler optimizer. @@ -35,7 +35,7 @@ impl Mode { /// pub fn new( solc_version: semver::Version, - solc_pipeline: era_compiler_solidity::SolcPipeline, + solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, via_ir: bool, solc_optimize: bool, mut llvm_optimizer_settings: era_compiler_llvm_context::OptimizerSettings, @@ -49,7 +49,7 @@ impl Mode { Self { solc_version, - solc_pipeline, + solc_codegen, via_ir, solc_optimize, llvm_optimizer_settings, @@ -112,16 +112,16 @@ impl Mode { return false; } - match self.solc_pipeline { - era_compiler_solidity::SolcPipeline::Yul => { + match self.solc_codegen { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcPipeline::EVMLA if self.via_ir => { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA if self.via_ir => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcPipeline::EVMLA => { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => { params.compile_via_yul != solidity_adapter::CompileViaYul::True } } @@ -133,10 +133,12 @@ impl std::fmt::Display for Mode { write!( f, "{}{}{} {}", - match self.solc_pipeline { - era_compiler_solidity::SolcPipeline::Yul => "Y", - era_compiler_solidity::SolcPipeline::EVMLA if self.via_ir => "I", - era_compiler_solidity::SolcPipeline::EVMLA => "E", + match self.solc_codegen { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => "Y", + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA + if self.via_ir => + "I", + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => "E", }, if self.solc_optimize { '+' } else { '-' }, self.llvm_optimizer_settings, diff --git a/compiler_tester/src/compilers/solidity/upstream/mod.rs b/compiler_tester/src/compilers/solidity/upstream/mod.rs index 42b3854c..10078d40 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mod.rs @@ -49,16 +49,16 @@ lazy_static::lazy_static! { /// static ref SOLIDITY_MODES: Vec = { let mut modes = Vec::new(); - for (pipeline, optimize, via_ir) in [ - (era_compiler_solidity::SolcPipeline::EVMLA, false, false), - (era_compiler_solidity::SolcPipeline::EVMLA, false, true), - (era_compiler_solidity::SolcPipeline::EVMLA, true, false), - (era_compiler_solidity::SolcPipeline::EVMLA, true, true), - (era_compiler_solidity::SolcPipeline::Yul, false, true), - (era_compiler_solidity::SolcPipeline::Yul, true, true), + for (codegen, optimize, via_ir) in [ + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, false, false), + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, false, true), + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, false), + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, true), + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, false, true), + (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true, true), ] { - for version in SolidityCompiler::all_versions(pipeline, via_ir).expect("`solc` versions analysis error") { - modes.push(SolidityUpstreamMode::new(version, pipeline, via_ir, false, optimize).into()); + for version in SolidityCompiler::all_versions(codegen, via_ir).expect("`solc` versions analysis error") { + modes.push(SolidityUpstreamMode::new(version, codegen, via_ir, false, optimize).into()); } } modes @@ -74,7 +74,7 @@ lazy_static::lazy_static! { for optimize in [ false, true ] { - for version in SolidityCompiler::all_versions(era_compiler_solidity::SolcPipeline::Yul, true).expect("`solc` versions analysis error") { + for version in SolidityCompiler::all_versions(era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true).expect("`solc` versions analysis error") { modes.push(YulUpstreamMode::new(version, false, optimize).into()); } } @@ -87,7 +87,7 @@ lazy_static::lazy_static! { /// All compilers must be downloaded before initialization. /// static ref SOLIDITY_MLIR_MODES: Vec = { - vec![SolidityUpstreamMode::new(semver::Version::new(0, 8, 26), era_compiler_solidity::SolcPipeline::Yul, false, true, false).into()] + vec![SolidityUpstreamMode::new(semver::Version::new(0, 8, 26), era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, false, true, false).into()] }; /// @@ -137,10 +137,10 @@ impl SolidityCompiler { } /// - /// Returns the compiler versions downloaded for the specified compilation pipeline. + /// Returns the compiler versions downloaded for the specified compilation codegen. /// pub fn all_versions( - pipeline: era_compiler_solidity::SolcPipeline, + codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, via_ir: bool, ) -> anyhow::Result> { let mut versions = Vec::new(); @@ -170,12 +170,12 @@ impl SolidityCompiler { Ok(version) => version, Err(_) => continue, }; - if era_compiler_solidity::SolcPipeline::Yul == pipeline + if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul == codegen && version < SolcUpstreamCompiler::FIRST_YUL_VERSION { continue; } - if era_compiler_solidity::SolcPipeline::EVMLA == pipeline + if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA == codegen && via_ir && version < SolcUpstreamCompiler::FIRST_VIA_IR_VERSION { @@ -194,7 +194,7 @@ impl SolidityCompiler { language: SolcStandardJsonInputLanguage, toolchain: Toolchain, sources: &[(String, String)], - libraries: &BTreeMap>, + libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, ) -> anyhow::Result { @@ -206,8 +206,10 @@ impl SolidityCompiler { let mut solc = Self::executable(toolchain, solc_version)?; let output_selection = SolcStandardJsonInputSettingsSelection::new_required(match mode { - Mode::SolidityUpstream(mode) => mode.solc_pipeline, - Mode::YulUpstream(_mode) => era_compiler_solidity::SolcPipeline::Yul, + Mode::SolidityUpstream(mode) => mode.solc_codegen, + Mode::YulUpstream(_mode) => { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul + } mode => anyhow::bail!("Unsupported mode: {mode}"), }); @@ -281,7 +283,7 @@ impl SolidityCompiler { test_path: String, language: SolcStandardJsonInputLanguage, sources: &[(String, String)], - libraries: &BTreeMap>, + libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, ) -> anyhow::Result { @@ -289,14 +291,14 @@ impl SolidityCompiler { Mode::SolidityUpstream(mode) => CacheKey::new( test_path, mode.solc_version.to_owned(), - mode.solc_pipeline, + mode.solc_codegen, mode.via_ir, mode.solc_optimize, ), Mode::YulUpstream(mode) => CacheKey::new( test_path, mode.solc_version.to_owned(), - era_compiler_solidity::SolcPipeline::Yul, + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true, mode.solc_optimize, ), @@ -425,7 +427,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, _llvm_options: Vec, _debug_config: Option, @@ -517,7 +519,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/compilers/solidity/upstream/mode.rs b/compiler_tester/src/compilers/solidity/upstream/mode.rs index 6c9157d6..3a37b435 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mode.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mode.rs @@ -14,7 +14,7 @@ pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, /// The Solidity compiler output type. - pub solc_pipeline: era_compiler_solidity::SolcPipeline, + pub solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, /// Whether to enable the EVMLA codegen via Yul IR. pub via_ir: bool, /// Whether to enable the MLIR codegen. @@ -29,14 +29,14 @@ impl Mode { /// pub fn new( solc_version: semver::Version, - solc_pipeline: era_compiler_solidity::SolcPipeline, + solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, via_ir: bool, via_mlir: bool, solc_optimize: bool, ) -> Self { Self { solc_version, - solc_pipeline, + solc_codegen, via_ir, via_mlir, solc_optimize, @@ -97,16 +97,16 @@ impl Mode { return false; } - match self.solc_pipeline { - era_compiler_solidity::SolcPipeline::Yul => { + match self.solc_codegen { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcPipeline::EVMLA if self.via_ir => { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA if self.via_ir => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcPipeline::EVMLA => { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => { params.compile_via_yul != solidity_adapter::CompileViaYul::True } } @@ -122,10 +122,12 @@ impl std::fmt::Display for Mode { write!( f, "{}{} {}", - match self.solc_pipeline { - era_compiler_solidity::SolcPipeline::Yul => "Y", - era_compiler_solidity::SolcPipeline::EVMLA if self.via_ir => "I", - era_compiler_solidity::SolcPipeline::EVMLA => "E", + match self.solc_codegen { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => "Y", + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA + if self.via_ir => + "I", + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => "E", }, if self.solc_optimize { '+' } else { '-' }, self.solc_version, diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs index 993d26f7..a1feb86d 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs @@ -45,7 +45,7 @@ impl Input { language: Language, evm_version: Option, sources: BTreeMap, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, remappings: Option>, output_selection: SolcStandardJsonInputSettingsSelection, via_ir: bool, diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs index 16581b11..1ffd5b12 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs @@ -6,7 +6,6 @@ pub mod debug; pub mod optimizer; pub mod selection; -use std::collections::BTreeMap; use std::collections::BTreeSet; use serde::Serialize; @@ -25,8 +24,11 @@ pub struct Settings { #[serde(skip_serializing_if = "Option::is_none")] pub evm_version: Option, /// The linker library addresses. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub libraries: Option>>, + #[serde( + default, + skip_serializing_if = "era_compiler_solidity::SolcStandardJsonInputSettingsLibraries::is_empty" + )] + pub libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, /// The sorted list of remappings. #[serde(skip_serializing_if = "Option::is_none")] pub remappings: Option>, @@ -60,7 +62,7 @@ impl Settings { /// pub fn new( evm_version: Option, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, remappings: Option>, output_selection: Selection, via_ir: bool, @@ -70,7 +72,7 @@ impl Settings { ) -> Self { Self { evm_version, - libraries: Some(libraries), + libraries, remappings, output_selection: Some(output_selection), via_ir: if via_ir { Some(true) } else { None }, diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs index dc86bc50..e5eb3fce 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs @@ -28,11 +28,11 @@ pub enum Flag { EVMLA, } -impl From for Flag { - fn from(pipeline: era_compiler_solidity::SolcPipeline) -> Self { - match pipeline { - era_compiler_solidity::SolcPipeline::Yul => Self::Yul, - era_compiler_solidity::SolcPipeline::EVMLA => Self::EVMLA, +impl From for Flag { + fn from(codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen) -> Self { + match codegen { + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => Self::Yul, + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => Self::EVMLA, } } } diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs index 70b82559..d49a842c 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs @@ -27,13 +27,15 @@ impl File { /// /// Creates the selection required by EVM compilation process. /// - pub fn new_required(pipeline: era_compiler_solidity::SolcPipeline) -> Self { + pub fn new_required( + codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + ) -> Self { Self { per_file: Some(HashSet::from_iter([SelectionFlag::AST])), per_contract: Some(HashSet::from_iter([ SelectionFlag::Bytecode, SelectionFlag::MethodIdentifiers, - SelectionFlag::from(pipeline), + SelectionFlag::from(codegen), ])), } } diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs index c8f84ecd..4e8ffcf5 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs @@ -22,9 +22,11 @@ impl Selection { /// /// Creates the selection required by EVM compilation process. /// - pub fn new_required(pipeline: era_compiler_solidity::SolcPipeline) -> Self { + pub fn new_required( + codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + ) -> Self { Self { - all: Some(FileSelection::new_required(pipeline)), + all: Some(FileSelection::new_required(codegen)), } } } diff --git a/compiler_tester/src/compilers/vyper/mod.rs b/compiler_tester/src/compilers/vyper/mod.rs index bae7e2b6..b89f2a61 100644 --- a/compiler_tester/src/compilers/vyper/mod.rs +++ b/compiler_tester/src/compilers/vyper/mod.rs @@ -195,7 +195,7 @@ impl Compiler for VyperCompiler { &self, test_path: String, sources: Vec<(String, String)>, - _libraries: BTreeMap>, + _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -258,7 +258,7 @@ impl Compiler for VyperCompiler { &self, _test_path: String, _sources: Vec<(String, String)>, - _libraries: BTreeMap>, + _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, _mode: &Mode, _test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/compilers/yul/mod.rs b/compiler_tester/src/compilers/yul/mod.rs index b8ddbfbd..824bf22b 100644 --- a/compiler_tester/src/compilers/yul/mod.rs +++ b/compiler_tester/src/compilers/yul/mod.rs @@ -5,7 +5,6 @@ pub mod mode; pub mod mode_upstream; -use std::collections::BTreeMap; use std::collections::HashMap; use era_compiler_solidity::CollectableError; @@ -55,7 +54,7 @@ impl Compiler for YulCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - _libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -65,8 +64,10 @@ impl Compiler for YulCompiler { let solc_version = if mode.enable_eravm_extensions { None } else { - Some(era_compiler_solidity::SolcVersion::new_simple( + Some(era_compiler_solidity::SolcVersion::new( + era_compiler_solidity::SolcCompiler::LAST_SUPPORTED_VERSION.to_string(), era_compiler_solidity::SolcCompiler::LAST_SUPPORTED_VERSION, + None, )) }; @@ -76,17 +77,21 @@ impl Compiler for YulCompiler { .0 .clone(); + let linker_symbols = libraries.as_linker_symbols()?; + + let sources = sources + .into_iter() + .map(|(path, source)| { + ( + path, + era_compiler_solidity::SolcStandardJsonInputSource::from(source), + ) + }) + .collect(); + let project = era_compiler_solidity::Project::try_from_yul_sources( - sources - .into_iter() - .map(|(path, source)| { - ( - path, - era_compiler_solidity::SolcStandardJsonInputSource::from(source), - ) - }) - .collect(), - BTreeMap::new(), + sources, + libraries, None, solc_version.as_ref(), debug_config.as_ref(), @@ -95,6 +100,7 @@ impl Compiler for YulCompiler { let build = project.compile_to_eravm( &mut vec![], mode.enable_eravm_extensions, + linker_symbols, era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, @@ -125,7 +131,7 @@ impl Compiler for YulCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: BTreeMap>, + libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/directories/ethereum/test.rs b/compiler_tester/src/directories/ethereum/test.rs index abbcb664..99172fc7 100644 --- a/compiler_tester/src/directories/ethereum/test.rs +++ b/compiler_tester/src/directories/ethereum/test.rs @@ -124,7 +124,7 @@ impl EthereumTest { ) -> anyhow::Result<( web3::types::Address, BTreeMap, - BTreeMap>, + era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, )> { let mut caller = solidity_adapter::account_address(solidity_adapter::DEFAULT_ACCOUNT_INDEX); @@ -163,7 +163,7 @@ impl EthereumTest { } let contract_address = contract_address.expect("Always valid"); - Ok((contract_address, libraries_addresses, libraries)) + Ok((contract_address, libraries_addresses, libraries.into())) } /// diff --git a/compiler_tester/src/directories/matter_labs/test/mod.rs b/compiler_tester/src/directories/matter_labs/test/mod.rs index 3cf54aac..313e7863 100644 --- a/compiler_tester/src/directories/matter_labs/test/mod.rs +++ b/compiler_tester/src/directories/matter_labs/test/mod.rs @@ -230,7 +230,7 @@ impl MatterLabsTest { &self, address_iterator: &mut API, ) -> ( - BTreeMap>, + era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, BTreeMap, ) where @@ -262,7 +262,7 @@ impl MatterLabsTest { libraries.insert(file_path.to_string_lossy().to_string(), file_libraries); } - (libraries, library_addresses) + (libraries.into(), library_addresses) } /// diff --git a/compiler_tester/src/vm/eravm/mod.rs b/compiler_tester/src/vm/eravm/mod.rs index 5cd6c8f9..1ebc6c56 100644 --- a/compiler_tester/src/vm/eravm/mod.rs +++ b/compiler_tester/src/vm/eravm/mod.rs @@ -105,7 +105,7 @@ impl EraVM { ); let solc_version = system_contracts_solc_downloader_config - .binaries + .executables .keys() .next() .ok_or_else(|| { diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index a6842843..8dc79694 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -2,7 +2,6 @@ //! The EraVM system contracts. //! -use std::collections::BTreeMap; use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; @@ -294,7 +293,7 @@ impl SystemContracts { let solidity_optimizer_settings = era_compiler_llvm_context::OptimizerSettings::cycles(); let solidity_mode = SolidityMode::new( solc_version, - era_compiler_solidity::SolcPipeline::Yul, + era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true, true, solidity_optimizer_settings, @@ -431,7 +430,7 @@ impl SystemContracts { .compile_for_eravm( "system-contracts".to_owned(), sources, - BTreeMap::new(), + era_compiler_solidity::SolcStandardJsonInputSettingsLibraries::default(), mode, llvm_options, debug_config, diff --git a/era-contracts b/era-contracts index 31465df1..daba783e 160000 --- a/era-contracts +++ b/era-contracts @@ -1 +1 @@ -Subproject commit 31465df18b212c420c288bf8c05af7b47046a369 +Subproject commit daba783e8ee9ad578a69a1a15716b5fd3680a5bd diff --git a/fuzzer/fuzz_targets/common.rs b/fuzzer/fuzz_targets/common.rs index d6213294..6e3044b5 100644 --- a/fuzzer/fuzz_targets/common.rs +++ b/fuzzer/fuzz_targets/common.rs @@ -9,7 +9,7 @@ use std::{path::PathBuf, sync::Arc}; use compiler_tester::{ Buildable, EthereumTest, Mode, SolidityCompiler, SolidityMode, Summary, Workflow, }; -use era_compiler_solidity::SolcPipeline; +use era_compiler_solidity::SolcStandardJsonInputSettingsCodegen; pub use solidity_adapter::{ test::function_call::parser::{ @@ -160,7 +160,7 @@ pub fn build_and_run(test: EthereumTest) -> anyhow::Result { let solc_version = semver::Version::new(0, 8, 26); let mode = Mode::Solidity(SolidityMode::new( solc_version, - SolcPipeline::Yul, + SolcStandardJsonInputSettingsCodegen::Yul, true, true, era_compiler_llvm_context::OptimizerSettings::try_from_cli('3') diff --git a/system-contracts-stable-build b/system-contracts-stable-build index 69366327deb3dee7e2b88ce432b7ed5f5fa22e12..a8c8ada23d3312d19b19f4b0dc66ebae6f0047cf 100644 GIT binary patch delta 219862 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 z z`q~kt>q^{{Fw|F-@0bkDo4P_Tpxg5u-BIxpML;UvsrihXP`2H>LBFwfgU@+-^^nm! z&(;1kYTJ|J|C*HYr!W6>(|6ttuU~SP=iz%gx^{K_xuTnle!A+Ib;JM1-n+orRn_<6 zYwz>i=ggit^GY(4WagYaW+pQ{W+o3ZAmj`&h>b`fs0dXj3L*tz1Vs?-o+wCtv;?Cp z1%X6sHT7Z#YcE=~O{~w-R_$RO1@NDKcK6QWbRri1Q;I;>T@yK1tf4=mucHdUIKl5Pe z{_NxLU;DdchT=zin2|MAFjr*{AoKHj;%raP=GCNg4||HP8b%@OKHxkDa(MhsV^3JK z9tGIZGHwXg`u}97cTzC%ris@YA5%6Ic6_OsoHB>cS~E5G9u)`J&%+A*Ae;v{iPRGO zXk$9T;lNjZx68gJ8DDHdr4q%xgFF!NoG>Vdw^zY;=aJ0?hve`Kg*ed1)v60uMOI|* zeRx0(P}czmzXRqhIkRy?@_9%X9m9~GD_{vQX(M48)&MTAHc* z{YU65m*fxBBmDea+1eEK`@BiL%0IyEC{r&wcn|}m0dYs$tB=5FD@0cIsknVsEpl`T zN3s-&4r~KTI!=(NX()9J9Y192rS_v0w%2ofXgi*NzySf`ClFpreF12ouMV_or9qX@ zf|KWigN~_zruDms_5j5GHdmEr>6%z14)M2H@saE`Bje+8Fk)s+yyufS{cWnudw(m( zJuIk_2#^Bt&Y-*K_+h%nLnc@v(bYp{u2}+puzD@_=8l3kE`;} z!UzEg{0hnKbP-(uNzZKUBlfXC@2zf_0;J&QFXYK7hByVsRiD|?hnn|*D^vk(gKZ&u zgqy(Fd+B+g@*>52G)#Q0{M9&68C;JNbl(NJKvz12ir>c&*^t{DlAdMX1hcQd1K#`< z9PJa3_Mt%OOa=`FCeT_fWB`fZ2dx0H00}{AjXM%OYPBawHVKbEuu1 z(;VomNV3D=^vw5|Eh8^WBs&`TQ zP7mWwxu0VzfbQ?3=MXudT4HCez;-mw8mN`{6@d##>}56#{4cP8MZFxUwG~FSc5OxA z;;8DVm5=`Oth{TXIf4_VI*w6u1a~+k_>>4ZKLONqd`UcwXj>3nIZNNPO64z z%?xXHSnX=fk>;MS$aWHm>iuTam+Y=D2`Fe8|8EvQPn?N>zx#u6RNqCHWO>7+yUlNEN0&y+qT90&c2_tPh z&|qoC?{SQ@@j!upBCb$US@mapS%`4syT%Y(b4dsycC1eK5w|S^b=b=?KFC0B|MDVAQqKDc^rBUv%f`Jx~~sVGz~Z*;;!%qy@n0AR4?? z!T*qse6hMCI0URH4gam1+=P0cb&EVRe5nYoK74-!-{Z$VE?LpR9(oWZE(d`Z9zhI| zMIp1UO~@qcAQOlsV?pzsD$q27Otj%OZ8q%IHEh}q3{HO3qTyOWRF89IO)>*9mG5F5 z5=Ct-d#Yqm7n{oG&D3#Qk(iJ+tegmq&Ahm1q3hKcl>v@hueHC+Nh=`AY?FYKiRf@l zbCQV4-;pYi*9{+8PI^>oHxZ$6zVSRYMI%oh2oFrYy!t(yZgR4@{N6LwD~R~YLTova zY{N-I-KjnXVJfxD1-tmY=*cWKob`mQGPhF2bhHqaOT5N*RJ~lh;MB$Xyq&qBT>5gZ zXBbNx?my%$4_?Z*s_R1TmkkywFGtV)b1**GW*8nPr1+;N%jE2Xn=j&K-e`T@bRuy+ zJ-;HId>&nsZxo|kt)qGq=rS`Kf}HFNzu$frBzE(M35h1)Ln9`Ved0bO`^0^aOc=Nq z1#vGDy~eHRm2%FkaW8sgydmjvbhJ*YSl$Wjh{sT|h>_;hIc;Of_H4grrkho zYO~?K77btAqT#Y@Ntq2d6Qm@OKg$Brr>s7vd?a`>$!n()t&`2sX7^8|g#MHNu!s?XUaR1KCw1O7i+-xA?0 zqTau@)S8x1kxH#2>Q}|oE@swZdQO-&vzec(>=tu1$E&N`m?(>hNs=N71W}k9&_nUw zLv~1U2+03wjM6=$Ow$ltptq)YpsNDZEBuYW%q0+h&M5+3xIHFQwI@$1%YT1 z5G~`ZC(6_e+MaDNR6C>=35VnlF;^rU365nT+5Q+K+u;`6VS+$nfCW?-{d3hEXr@Yl z4Upgh{X0gI| z)4kewq`Bu7!wC3yHIJm%S{Wxg9D;ziOujB91Y*5!>y5`+>xx4oU8flw`hX?8fv(dV z$cKPsY{a1n4Iu>2H!|`cSdx0Q+VOB)z3K~G-%-C1i26hrKpJXH#S!(VAPCx0yWK4$ z+I&8!_z|QeqPAwV;WOK8cy)`03s#f{HGEZJt6EW7XDs`@R&=sUiJlwdIh8M;RGQA%Or6*u(mK(g+0WnU#A)K)9Pkd+js#YLkL*oBRy4Gkpii z=kWLWp6cm_`Z#11;AkIft4UlUWMiq7(*N+e7CD{(>uoRH_ac1AVSqQqNw#ei;=BmjROC;xH@;tR&-m44P zlc}1EzJQd2f2vay*J^2Yfvc7bv7ou5Z!MWvR2WO9k!Y=Ev!jipn{*X5rLGOS-z9(rOC8(p*ab^ECvZk}Q3&Yh-?B?i3e`9M0I8+ZCjr8-$f1<(tmc z7`()Ju62lU>JXEzMoj2{E)g$eur@U9>ku;+!d ztF>f6L&C~?n?GnO1|66gODwFc`7SFV2&C^65sH8$7wPBs0NNi$`=p<^0gC%bpkweZJv$6_9eAs zQ)Vi$17izZh8_S`xiLpl4AUT6suy8pzACu#^Vdf80_c^Zx3IDv@|m6)thu!Mq7DK*K) zh`9$cdm%wQG{6(*ATM^``sBSOTf#aiU!yEJcZm{$iSZ@>w+M*L;*N5ew$sJY-}|(; zEKc_O?M5w?nI^aM^VA9es|p6J@s-HwJP;evCz$TiL|lMGj_tS$n_@qJ?Wm*%gl!t zGbXGVXl4-66gLRv;i!R$H}$;>L_r9T({idy(p{C8ka~d`f(Vc}hmPYX%8t!ZYCqzw zy-j5r)7D%>6!sYi6s=&eR*7Ult6C9B4T}WBcOj{9n!b4enple-17Q&fCGRZ_R?{4W ze@JWe8O_y_$Rt_}Y0p^hM4R-SE@VEj$bgRf^W)=t9+ z8Pkb0F+>#^h2$b+()B(gL``Na0 zNZE>IVV4P8tC(Ii@7Fw& zL>aE~SLqL5k{JnN{WoizNAvijGSqZ9`wMR$g9i(-XaIvJC5s`rXg?-G1DUASr_C^7 z2CA^l8Y93ZKJ0!iN)jd2%FepET-a2VwJ6J|{>A|wRhw#|Y|5$RFM>QOLEcC*2Py@` zXc-R2`Lq9BJXC)NtR;B7q$Sygc)u-IC^#okd8cp~29m$6xG48-k?xg}IbN!d^Zb|1VK-BJXou(seGB8Xq)4-tRRt8I&kDCJvRGW8}>6MA+FSM98J8Do{ znT`wCI(A|!ZaNE9=hn1KGq*gJr?4e{aft+#9&!iOX^3-zt?)ZSDC#{b;_#Me2KI8- zR2(y#RgTPN1rf(Ax{};E90&5HL8i`9FCugW2wVa-kYE6a`5+SoHl(A8gB@7de+7@Y zX66BE(qh5h0JN8<;NV( z=6Mz--|c|lrN&Q&+&jX&0y=>-Ab)%h-8<+Yin5)O0{~v_z4Gg90*1Vrv)YQAow%+^8U@+?bYHCrr)75EGO! zyX8t9TQ=VoMgjAGbJek|1q#kxA_{6CRb%HEZ5;!umU7}$OH?grs;g>10p}@3Qacuw zIHHYhHr!j)uxVGETD#b1Zix+VZnNS3`i9w=b~>@Rk<5o(OKX@?y-1+cXL=dQNs^Au zk`@^sZcr;aqjt+JN2^zYqNCQBj>%_;3NW?41%$%LVk#CyGLlD@2BBzTRK=J#L_Q}F z0{yoDQAE&)dr)mehcaEP{#M$wSmPiE4crXud~R1U4M2Ql*vQtEFC^wEWwf_v+y7TQrPR%$rVY-FH z8fY=X$tELgq}D4s1^iC|_J|gA7MkI#8fB66p|V)-b;kQ}(ik)sBqR+vEi2iI7y8CJ zH%r}ABE_IhrRE5ft(Lgu4utCaSeCfirF;JAu36HiB!&)n5aw@HiAiIK$&xX@Ls@Bs6bW^DOnqr`@9{;W&;gJ4@!EY8-4p9=B$$@0ixiiGQT9Ho-{x<|K@GN(&mkacMNHrrT>+^>Djn z=ft?auWQk8+wq=ef|%NmikYJ=l&ET#32SwzI=KO4NY2&CF~0>$_O*aaWI%QGhmkP# z7qY~yg(|5HfmMqo8v)C&tX^$UXA#z>P1>k79Ag#K>EFM|o19FMoTtf|6m`z*j)U8VF?F*Ftl1V0OAVVN?}DQ`c0)+Wdrq#yN<(^1 z`{c|KG&(VneU*6pvuBd9DdIzN`E#I9y7%M*M6(L&YK-qrqi{S4ivxAe> zhtQK?wAf58?5IErD1V{=e%>@AXcd z`gCm?Rx5_df0fg;2FzkP%RiLoKybpo=CqH`t2<6Me04@d07QAD(OREoqqAsIdQeP=-I~fyx-dtP2Eut# zG*`vl{}sbUO1)FkHxtC7g&t*B?FwVu0iYz}lhRcB(dRkwjpm>~ZDI zPy>ju&|I(GEw0`DSX<44sfjjmoR7yb6X$`(DCVu-9KvaooyOa2IM<@#Pc&|r~dMjh3f$Ue`qs*t?htX+JZpr#m>}i6U(uIkZ8e#*&?N@ zdahOWW}%ISV9{e+TZEa`B#Q~m^}>o+lm&z?f9P+r-VaWyIrI70z=%_>Fic}aM1mwL zvFW+pUnq###k`hz>K$;a+pJ)v>@6}Mq7u7RLPcd3%R0&ssXL(;kSIN#9PM+8km4|u~jC?i95s|u{*pv z-W-jDrq9t4@;?sCeN_TD9!EzS;lj{SHvvk2M}E=F{#UD_pb|vxs|kiwhX9&K0JcWt zRuY@`HPuZn~bMN>qYQ$*NWgvoGnZc*wyiF(YSo3Ahw<#V57Jn`vZnYctn=4 zk!Lxd+(s408O28M#6=!vC?yuIw@N4Wi8@cra1VVo$u7H(Or;H+>MM~Qp>c#3X#?aD zw{uDszI7rra)itdmk__L^W2HNJ;10k#!+EMs zBA^p!iY~8^E^GxW^Ejs*+4j(kBZZ16L_)_;f>Ju)V9KQsv!sTKsX3-V9HlEN5-(7W zI#eQxJ14zMLM6#x9Fs|$BKs`fn2`o^?>3>kU!jUR-xNXIJN2VC=B^FQ01KbP zSQxK~rW`=a{SHz;!tEnkQGQdxSEqng_>cy>O2{uc6(LB3kamh7SW?*t+3K!8^aj?F zFG+D+Kr4ST7@+O!t3yg~>tkA)Bzznwp9LI{=)NC9{L(h@>WL=N_~sv!s)GqVKo zRZ~9K9zm##bj#P@LO{uJ)M6ZA%O2Q0qimRd4k~zw5c&H4vra< zMBNKS6JD4w%o4G>nCF~G9-AqRlt|vz1LZiCQO^8W7WgZLVSz?$WQYt!_!LbS4LTle z%G*b%mZP0qJ6BS~5nm8Yh#6|bc^2RVZ_JrezmdUDLo+^(aE6Y5mB2CBM+X4_5*E&x(Bqj$YWxj!&*cfZ2AhDFL5q1R zK>b%G)pQeZGtedE_qHkC?B?GEyVkO(Sl`izW*N1#sF{h_NZb}Xh+r%hvkjeBe-&tC z7Nuy;Y&-t7`kriEx`%+Tn`7m*W_FIH-4!3lhs&B3AOUefxZ)$S9F=@p=AszWg0gA$ zGAPw-JGN*Ygz}EQ|B_7q37~GXKpoRw1Y#Ajaa7ibfsC+y;bn%sQ~dmV*dQ4(eulBE z`gT;{Wko(H?7s~DFJKnYj5Nb?!a~HZ$Nh?xBgp|Zpzr%LZ?$(lgHEO2sL#4Dvn1vZ6KlElxgTs$yj%eDrG*8WFD<5ay`7ZF+2(n{JTJ&|e7=i1j0N#hJ}b{*1Iu%j zPVoSf={c$g=HwigR~gpP%FWF4a~F_Adtfwgo;S006+?nxyg(w_R+uym*@|$eId@b! z04d5no01%GmDo*t$=Z}AJm1AAbJAtfjxa-CDw}1ecS|terL%d^{sLAsaDfkQkwrHt z*>!tC!hwF7)vxM$#)96P;f9?oF9-I^Y9E2TfQfUW1%JC*p!=?;#EylQIF~)6h5SCZ zKpn~7EFDmPsCJ@#ThK3O7i6w0J0>5bq1ow!^bPVs`kkVtZ{v>oWtkf*z(sSI-LhMy zW1uO379Hb`6jp>Co9B+gs4Q`L?*%G2g!&h93(`>T64SFoOx>oSUrx+R$KdO6`Gn~h zKz<)}(E5ORAu=lwqrt5VsAmH@R<%4hSeV84WouapKcc`S^|Z1b6-;Y}1j2A=P%JFy zhO3NaWqg_VwI8t-)~U@+ECgSfzcF5-5tw+7zBe~qs&yscIDyP%^{6bCDe@Y=_&r#x zM30SL>|eNNc~H9|k2EWTuY26c1iKW`tn@SD8pbrl-D(Ci6Q&QNMxdNz0*r@layz3F z4lU>m~`(9w^}S z>DyE3WsaviK9oDlBhLstRrs9V{h{1QF8_U%ICvHeff4=C|MbSn;6c(Uon1vLRp&R| zNwK&l?{oTLG~yP%qw{y=M)|B)4uhI`-yEn7WQYBKCb=55T~a}diVd{F<5p||ojYNI zZq?V{l^a=A_*(-dSd^q`deMbhhFTuyRo|6ccS-@0*x@G`v0D;e5krBh$nA57(c-g^ z;P}$t0dkwwU3ceBce{U8@4Oq^v=#nI-+VWG%5IE}a8Y=|)Roo0kLPQ;i{{?u2bp{U zttqaKzJ?4gp|oy3*Xxuu-Jl^RWD`mY7g1Zu zbpeOg%;{pwOR@x6b-%{&i4lC(@$P=DKI7h8ahI28>IaoGJ#3fKz2_N>Bt%@^{8z~` z(~}XWmHnc4CmI?qZwl5uudg}{MBk0@Q3;stew%*J=RDlM&BlTBL!a|Fx6Ij&hCA`$ zil&iFyX{d6u+~A9S9WuNQnvd5uW|f<1Dl)BZ1cz_gef>=HU5pF<35ROcHdo#j_&`~ zR%FwUi)?oPZ-IPWK+7iY6>I4#9Po1#+4M!8lSj7)h>TkOJ!@t7ejYnGk{Y999c^S& zs<|KBRdJC6(T;Kc!qx&NXBW8edsPJpl zLW>brtw zhQ@7pl+CX&z|#k~neb;#syN_m*wm6*q!}gb4#G9x7ZdD>cK@}IYOXSD{ph#6p)Ho# zKqsR_clb4?WiaR-rdu-8^bhXM4F)|h6PnYqw}I;nO)`^d36)JI8L4jZLgOtmDdz~z zVb5>~MP!t|@guqJODrTsu!Rt;lK01m-%ksEKh-9FzsUG?()yu~=0-0-hPE?^0U|I! z)*(T;#L$CrK~Tol!pwF~QdT@=Rhf*g{AUyD0N67BxJr_(**hs@qEh;*y}6OW-aE|w zo%kN*!uy=Q``g|&=g0cdZ+oNNHOb%Lp?n|v%P?E1KHF23Vp`kLJ;@- zn;ByXOiV`OEA`Fa@kYjJLqdmp4VIu@Cvwcw$h$aTOvM9YD(Z*7NCEJYI~6jLgdl&OgMD^7+V^Q~Ete zy-_{>H(pmy2#74QjIJcW=$r3_{nGn7A=}=0z3~&dm#!jvh)LGi(R9C;hUmC+`lp9- z>!xGvNNZ8Q)M`VKti8XkpZ<7mXj$)tQgbgH9R`{MBz#>zdnh;9Q(vck$3J-J&3xD! z0X*)dH;H-e|JYl%CLEVV%}rtJg2H#{n}6t`b;xTPS1(+(s@8+Yy+IeS_{&4Na~k#H zCqMGep86%%yfqMOAsCTw9a+_&`7THIudk;`0Po;jVg@)fNNvnu_7bySv z=r|g(pjKalIQcr=C$lkZT8W&6`$i<9T;8|_ zJDwGp228eO-Byw+$!kz4s6sT+2b=XE^Pt)yvkW$?GO>xHlpf46&<3(R%QB!0xo!A$ zRZeLz=S6VL+)i6yUU$LdXR*E7_h!poKnXRB|5@N1^T^HFfIX@_LUFUc%fgX7*$Bk` zIlA{JIAm-ah@XjGvF4;u{oxsmr#&g`Gz^;U1=let8KWmWyD(K|OsFL1sltI3Kq4c= zU~so|%$_#jt`iJmRS07+K^P=jNlyz36W1~=twMW50zeFl^S277>}ExM_=GWq9AnnE zYei1^>ma8vB68RE;CqWvzuz@^wrbiyh9M|Si^{^yipplaN|ehD$k41n#>g%1A;`wa zu>!gaq^J)3OTGqmpLWxQ%>mHJV!#xxRL%yL6Y9*>MEUqkwt8m8q7sfuT!E)^HbBie z0Kh|S3czvhvfKaL0w|0_77`TDx2w^J2^IH$Ob~I)g2Tq0e}PCpdVY`)gl&;YN(L(J z2uf8-&dursDd2_w0%s09=b@;KiyDH3q&nn7z+o-PZU0=C$`RlQ4s;>Rrtk*=ihj%k z{tLOjJnpOO0}9K~BQq#m`CuKRdd{Wi3M-JP0hobMAzUGwYUl;u6JSlliqBY$#5^z> zbcVhws$Im()ILu=9wuf&v;H6_?7XPXY>!)3R0{s!v`3l` zC3-X*-v1sU0%$8X{QGayH~-Qba{J$-@BXECYCxM3fw=n*8IcabCgf}cfpwj1ELf@S z%(oJ;LOaIs3!^(C2vjOkPe;fmlE-Q!7@j5*ddD-~robATs8dWTYvRS5UX3gxxPthJ zkS9@seuyl(i1 z=V7WnPkNNdpaTu4{x2I4+rYD(x3i0<(~dFiFmDHKqzaM%HvvBm4bu-3UL;%1YRd6{ z(>r(IK$HBF%5~tI+f`WM2eo_YsEM*w=yn-KUQ9dVR#68Jq9GcoLk*gB9WCmE)Rx`l zaRmr>Dz)zIhUZo7d9HO0nB^3<0lS={(oujA^)Vy~!jZGlr9QMD_Smdwu@%A%c!Y3A z74Bsa+Raa^l7glLH8mq|QBU$f)xJ->3e$R~1~sil8r@2wGGepTcbl^>41U9d#tIqs zSBPQ{6Ky=J7HMGW*}MuC02<64E^?j*PHZxftj|d#%dU8_plrO-<=8vy*j1QnrQiy} z3O)3VreS6ihCkBSvp{saZli0h^@_K+$~nNEPIh<+s3Q7aj07J zW!$#s?XGO8gMeAtV(KA%MoEs{xYELw?IcS1B$JSTM-4<|WlI;{8#Av~@r3~nIi2{j zimEqiH`eHagk}9hjK2qr4He3#5HEwzas>BwWlL3;xd$+`&q!mcHtHA{>#Eh!Diae{ zu$dLRoe;+xFuWe90=jlX+S9dwG1E09(pnk~mM*F-cjZObKoXW~xS+Oy7WD^eqHKvO z1=@+(hzSucGHI%}NA5CqZ;OzwY9Vl<@^IaK zp}?x%h$*Niz9@ei$z|E7-jZr>lfgRB^TtaUf5`W95x!r?#6wDkJb9~RAP3-qyc@?P z$#c3;XaRDU%gK9|i2OVgK8J0LauA!Q9JX*#`jr=XJ==45Mq0rO-#4HjXmk!;L6MThRO8xyOo9IVZ&m0JF5hLNkYrx51Bk9T2Oeg%6bge!8PP!H-u zgLa#o<<}ePONfJxVrEm|6TA!4@^|Yqp2jXr?=+%3Ok`@Vo|oTE&heW|Cj4b5)Gd6O zXaKMNFUoFAR@FqNizrmZlLACdP3LiJURLyM2w{2{F_M?H{0lsq?$6bZX8E7Wv{Bf- zR?Wpt8`-=8You7C#TqeI#x#~iJ876jXP>CpJ)}p>JgF)wjvY`3XGwz+>(Ra@AT$oP zQQQV(MP#x-WRj6xnW%h;%9|iUt6iCv|GVtUwESN!fMnAz;OQNgc&`E>+Z7+2zgZ`O zYV|Bxr}jL)YO^*}0{&*O*DOjAKX0c?b2JBji~V3iE7AZwl&vtE_cGj!bs0uB{M9@w zW)Wi%W)WCxWd;>@I4aa5+bdbXhaD)J4#@UO7^%tqv?h$*Ybdh2QZ0l@kYr`qQ7Gig zHM{z%2{d$&M>@@?6j4iLw5(4P1Ln+RO@U>bj zNqL~2R+!cH2=Akih-^(A3yJuxsW~_l-I z?5D2*lLG}*ZZDTJ(`9SKZ=Waw|NNrpn|Uu|xDQJRA9Kn@cOD%Gr=!x679CDU8RHN! zaq0kukUlVgWX^@KV`tOz74NV#r1%~rl%#Sr5ycxf_~&4Rlt4I)DNY#teqWNOkNiGN zsBtn1xEd9$U1moDEk+f8z!G7ac2~qNC7a=5LXS~WOwS2wGG?EctT|>d%ZKa{{of|@ zQwf%bE71560Vz|t)g!1FD^SsalBJbe!##8WVgd|x=E*k9kIm5dQyB&^;?I2WAsHsr z(27rmAbTTsP$kTACmfVWY*d+(H3xI1%$%&OvkcjpHu!uYAbP4(6i8NY5{~w(sIf5@ zAnC6yE=afFg0IzXUc~Lvq;V|!VMo-q8eZbwv(*C*9Kv;k??YUc4GMalMV=Y7s{(6f zc>Dk&$DA|}T!FCPlFm7417%y=gxrdt1n%A8ov$dn~&KRzgQk!f2qAM3OD>B#hXGO}jyBVzW_ma0!Tv=A?@7o(ik_W2J+P4lC>Pr%B$H|o%tAg5(M$gKYSehTd)C#oG;|m(1 zl=)xIFF;RX{FK+V5W&Y9iA9iY1lD~QTBaBt{c2XnMdp@*CCn{ZH`~TTBJMW9Vh^;4 zopV|TpO5Zs7S+11S#!Qhtud_ROe^YY%@jo)H7xN+t2$)XY9FRH-m0@=I@l=*kQ+r< z+afoVTHBstMm}FuF9$LQVgm?ATTn#}oulE%5mpV!kxe|5%s3jU9dBeU zvEmjZuU$yWwwj^(6wX0vTgZZ!51armDYnb^K5JH;cv4To-sP z8jft=T&D}nfCI~ZDzY_qtSDXL*4%Lon|9$V9*)D|()j*uDq9^Ns7w%Iu7_v0CQY=> z#+*3^4EUeGi&++ly&QDL(uYY|`VeLmlqmj32AlG9|4Guu1#?G*Y=(<^rFm}V)^f7O#PNhp|- z+<<$#qm`MRvv3G}T22S^7ID8EXLIKeCZrEAea5tzO6_vz>3#YGTrnlM!;D^uT*HG5 zNSuR=WgZAG^8O#VzEr}9=DP#}rC+D_nak-r)LZ0wll-1Ua_VLLLZ$fJijYQ;H!IJj zL0Yv)Yk~XTg;)exeAXhf7Miu#OlBqpGUa<>QZwm>EL*bwpdMQ>dxt zQ$rga1HtyDhH7Spg8wYPoluu`o)nYNXywptHnHYgmAh!<(-#SZR!q>;t^~jTmZuTa zvzU9k9Ti>*W_T}{0pA}H+F;NVO83xxG*`PumDKCn6qv~t0`ogo zQkyf(qYc|5ze8fQas_cm9$8eo+IZvwX4hGwOLzAz-; znrHce7klZ&T-H>O9mI=as ziPR)ugJ0@nR6g}R!{*AnkUMBd!7k5H<;W6(a0Nk%)9%Utq5fC4fpSg#?s}>pY%jC@ zLD^XlRHx-Z(cY)zeu3|M@P%>ZSRh+L%8#4PtK+jCYA`&LoRZFG!qgEQVo3s^I@_k* z8E_L#qsSL6L=7)^xvGlzi!~wZ-OLpS8TXPB3fd@|Rd+%g;F=P@Pkow3 zg3PR4Od5E;m!8L*62J1_?J4n;9fMzZN<5zyW=R+`Ub1kiET_r)V~I(cx51*30%LGI zVKFLeL}-GZ6vLcj>NOeZP<#dvHRS?z*d};59K#MV0gZ_95N1ggT3t@0W%ZfQ1^pW85pWWY?|yAo|W z6B-NrfGTj-V&oqEfsvB2z@U~7LL(xN_Xz_DT4H834t+*Rew0*1-xCDcb}(s@1VOGB z7bod&B+YCZD(Z(njn#<9o#7f|T-dEij4^?fA+{N4T(bb6--|ATbRd#EmSW1&Kfz94 zS<&dLUaor=yvZOfGpBY@-f-Mw;Tl$NJ#}{JGWvC1#3W9gTfdX>oF4N%n&OC_39K6r4B^$8cs4I znEVwcmn2I#kWrb*XimsTzv129ieLhzOGI?4-x{R5L@L-cDcSlcESGo67&bs_P;F@{ ztj8g+TAHcQoV+IzAzA}0RSH)=g^<_~lGApV1uvZ=NHgbE0ys9dcIjKZsQ{F@gz%bK zc|^i=4^fOs-LClSoUn)4e^3@+m>V;j_u3^!*`y#JTlW%@*PaUY+e=^^hEyYck6(w8MzRL=)k%FW(M%~6YU*vo5c!qKPZqD9xg_amaW2FO9~gVNZyB5o=jqq0_BQvS z>XQ&8x;>BXCtS0;OoAO#`pzLlwVj|`k_2UlICd%4A=b5rF+I6bGLEf7Sne_R<{+tk z<{nn+pi)Cq7!SUqPy%;A;vT3=e+zK(}BwKC z!RKp&-73i|QtEwgy%Mt-?(iCY#?IWXAkAk#*%)CZ#+?#_Wemaq;-nJej+?b`%&dje z&qAf_(gYXrw($X2>yQOy@5NI_gtaCJ9vcI9>XYU{2q43OHpKF8;P6AHR#V%Ay>*D>e^R zDBEj0yZ{dk7Y@G*?NhX$rTsDPL9)J@@y;PhMvr|TRapx={0FkH z)$jwgPU-L$Yn;*Cy8ms6^o$eJ>|>_c{~2lai4(L2w^dlIxo1q)aD+MYW=HrUq|$qZ zL}x)5h%wtWxQnvk-SMg~(u>so(Rvq`n7huyjhY_i6QCCxX4B1`fQkuXkq)b)_4S%; zy5qBH8l~wGFS8H4k*?|iF@gnUlC{DRmi;?I1rX_ReCnpyG_E=})Kwq>L)6X}NRN%N zbgUIeqaI~zHCm08f55qxAtKWS0cJp`;W!8&YGF;2h8;z!fbmn7wuFKw%3$&YC)x~E z6Ad&B23V(H@~mKp|6-is11&U6gjUpPZ8p5TTC-96QD?Zkw1&l^Z)DILS~}1KMa2KH zxId%K5v{BqQG)>$;|C;5JeQg*c9vtD;bTY^tBs{$dS@RD@d*=)dT#a+h=*`FXu)1{ zO>ox?b*Hz9x?r#c?#AMA=b!@!3)Y5R%LywlCRwoAfQXvqL$2(aPJGEOOi&4gq<9O( zFkuHD?xhC8HK=yiD%nA%;?qcgq>^fxHK~@FbHzkXvF%-g?_ebCJ4F{HZ-Grt{Fi)z z1Uk)X`9x}7Vk4=G*o_%ZiiAtq_em27P43|Ls_Sfa#x9h&B(`oZu+83^m`q#&=I%>L z%8+uoe5u#a8jjXUoUdPYiPscEvm1BMf4VLzVRDLWgu3<3cs2cyAl+bX((D3P?d4vFBi zG+1S;)*5QH%vo>WSgWE^PZy!qcomgIzd9=&6HgL!YB4>Mu>**)pAI+E93*#ZU3A*p zSE6ENLo~%W-DuL9iqMT5mb02cO+b{>k8Rsn$dTa|z_YUfuezy1bU;*vHJZH#cs2e~ zS6W9bM3}mdSYx049vrr_C~0ajt|TSNfsLr6dq^`xHd3tf5F-$+Zcql4&nM3z0I8s1 zwoqi_L@1;dS^K9Qqr}e#n3eobl=y1O3IfsEtw(x-l0lEtz^u$$#L1&tvCw*4o0fz@ zgew12x8#D@2)&hB43Y}9Iy>YtA$lVohSVZ)9|d7c#7qfi5fY4Y>kG~wL+9cA7Izr; zDlx$ODulU*IE)m6lg+AtgReLz=NnYeF`ISddyHLmVwh?&9wh-;4Spo0>^~4Cfd2*> zd5pY@$e)NrCt%km{YE9ciNV6mpxMDZ%F9ea5h{si5-tWBR#W@Ybq#JliQE7rO;jAi zZB$HAtS%Q;R5JVL;YH=J3Vb&~XH_)XNYWR76sFOwrV;(Mu#V_I zmA`BEd#fF369WfJ@3HY1evR2LqJ`t-gx!BV;fi2TPKo*aXP#uh42uAvfN>E5!8RCc zXS5i=Ek~=izURF_%{r>s3fq==)@}i^VLq%w-WwC0nVwqngcw-b`P;Vh^c0APcbxaPCCf5R<*nmXooA6F)I$Jq?~tr%yH&p9k?6#)t?8=DL|pqg zvl}>@E%PBWX3$)iCmur+jUmaYIT7U%0p;C>Usc{DEyXmTQYLu`X;whNT6@ThN;_nQ zv%`eF1K}cChv_1<^Ar?>6g$HF57CDY?UW&g#2zI)RkM&Nk2 zEew?kK_o3fFrL?;4@U?ujR?Zh13A&_V6)9Kq9O!ANgdS$VGWyndjx?*Hc5Ffi@U21j8-g~qmB=rlq?FGLmz^5lCp~~ z%z5pj5S@DnMFm>?snpnKh&Tf+HE`s5NjX%bfht)RCORi3AOjPFv=swJEEu^BCHOMTpw> z3RGqpJ^K0=dEMt*95#f#_azy+-D@q>Pf%E-SJ(Z3iGvI{^4XO?bLR`3Asmew5u1n2 zs)lxH)@IRI{bco(6`wYpr?EH3#A(MBL!00}7#86%>~<>_Ger{u4ZGcnAtL418K($e zrs#R@NjwLapZ9r3xSipmued(fGhGF%P05w5>D*t{-dRbl`2F}`hjVR@PcpSHn8M&i zPQol=gaLWR+@xJ)*ec9=d>#3K(cNiz?w^wpGjtf62HXw+=R`u`uXho0Mmvmhgn7tF zwA5s&$QXg^H(M(*IiLpgeShYy_O55@Q0X`7v+m0*3o`S>I_pn09Uvux2JA6Zqw8%2 zi7b6L3T(3kjI4tv2bb%$9f^?jL^;jFzFROh7s`U$kLw~#CmCozmWG$TB zx$FxsL+cZ%A)jeF{sF+%DX zz$R)pyHz>{qLq$GNA`yuo9B-HhV7UqnGFlM1?gDs64S9m+%c33N>CVnIWaFigLi)j z{aHxB>ZFbVrE{gsz5(lhH*~T9|5<9FRhXfvmqlGY#wf#F?dt5< zFvGD&8740DM%?v(Dq)88U#WFc)_?o>!VDwlihT0_e-dW6L5APZtE>q#{AP?-83{9t zUMqtp)=

xIO( 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%0AE1k*o*bf?`Eq6ls+_T<10t<|r+b=0!>Nj- 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 delta 230334 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 From cba5ffddac56fad297275bcefd6b3e7bd57c23ed Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Fri, 1 Nov 2024 23:20:29 +0800 Subject: [PATCH 17/31] feat: move the solc client to another crate (#118) --- Cargo.lock | 89 +++++++------ LLVM.lock | 2 +- compiler_tester/Cargo.toml | 1 + compiler_tester/src/compilers/eravm/mod.rs | 13 +- compiler_tester/src/compilers/llvm/mod.rs | 22 +--- compiler_tester/src/compilers/mod.rs | 4 +- .../src/compilers/solidity/cache_key.rs | 4 +- compiler_tester/src/compilers/solidity/mod.rs | 120 ++++++++---------- .../src/compilers/solidity/mode.rs | 18 ++- .../src/compilers/solidity/upstream/mod.rs | 50 ++++---- .../src/compilers/solidity/upstream/mode.rs | 18 ++- .../upstream/solc/standard_json/input/mod.rs | 14 +- .../solc/standard_json/input/settings/mod.rs | 6 +- .../input/settings/selection/file/flag.rs | 8 +- .../input/settings/selection/file/mod.rs | 4 +- .../input/settings/selection/mod.rs | 4 +- compiler_tester/src/compilers/vyper/mod.rs | 4 +- compiler_tester/src/compilers/yul/mod.rs | 19 +-- .../src/directories/ethereum/test.rs | 2 +- .../src/directories/matter_labs/test/mod.rs | 2 +- .../src/vm/eravm/system_contracts.rs | 4 +- fuzzer/Cargo.toml | 1 + fuzzer/fuzz_targets/common.rs | 3 +- 23 files changed, 189 insertions(+), 223 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7597d0fb..95a63758 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,7 +127,7 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -302,7 +302,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -593,6 +593,7 @@ dependencies = [ "era-compiler-llvm-context", "era-compiler-solidity", "era-compiler-vyper", + "era-solc", "evm", "glob", "hex", @@ -630,6 +631,7 @@ dependencies = [ "era-compiler-common", "era-compiler-llvm-context", "era-compiler-solidity", + "era-solc", "libfuzzer-sys", "semver 1.0.23", "solidity-adapter", @@ -823,7 +825,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -843,7 +845,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "unicode-xid", ] @@ -932,7 +934,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -943,7 +945,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -955,7 +957,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era-compiler-common" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#11cc7ed039fb96ef04d45d71c009e8dc8fffda9a" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#32637b83bf23b7b177c7d711992a6f6f101ac399" dependencies = [ "anyhow", "base58", @@ -971,7 +973,7 @@ dependencies = [ [[package]] name = "era-compiler-downloader" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#11cc7ed039fb96ef04d45d71c009e8dc8fffda9a" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#32637b83bf23b7b177c7d711992a6f6f101ac399" dependencies = [ "anyhow", "colored", @@ -984,7 +986,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#67bb2349e221627f15b09893e59f807094ab81ca" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#5350b284c14ff19309d02c9c38a0fe88c93e037b" dependencies = [ "anyhow", "era-compiler-common", @@ -998,13 +1000,13 @@ dependencies = [ [[package]] name = "era-compiler-solidity" -version = "1.5.6" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2b32d2de6039165bb91569375673fd750c8a10b0" +version = "1.5.7" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#d965e139c2f5dcaddf25d8c568103c840ac38c44" dependencies = [ "anyhow", - "boolinator", "era-compiler-common", "era-compiler-llvm-context", + "era-solc", "era-yul", "hex", "inkwell", @@ -1013,24 +1015,20 @@ dependencies = [ "num", "num_cpus", "path-slash", - "rand", "rayon", - "regex", "rusty_pool", "semver 1.0.23", "serde", "serde_json", - "sha3 0.10.8", "structopt", "thiserror", - "which", "zkevm_opcode_defs", ] [[package]] name = "era-compiler-vyper" -version = "1.5.6" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#90a858bc42d1a83be3fe95beb858f60c14f910cb" +version = "1.5.7" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#9c8fd92cf1f2b1330750d18e67ce007642de82ef" dependencies = [ "anyhow", "era-compiler-common", @@ -1050,10 +1048,27 @@ dependencies = [ "zkevm_opcode_defs", ] +[[package]] +name = "era-solc" +version = "1.5.7" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#d965e139c2f5dcaddf25d8c568103c840ac38c44" +dependencies = [ + "anyhow", + "boolinator", + "era-compiler-common", + "hex", + "num", + "rayon", + "semver 1.0.23", + "serde", + "serde_json", + "which", +] + [[package]] name = "era-yul" -version = "1.5.6" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2b32d2de6039165bb91569375673fd750c8a10b0" +version = "1.5.7" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#d965e139c2f5dcaddf25d8c568103c840ac38c44" dependencies = [ "anyhow", "regex", @@ -1288,7 +1303,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1651,7 +1666,7 @@ source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c50692 dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -2063,7 +2078,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -2110,7 +2125,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -2257,7 +2272,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -2855,7 +2870,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3010,7 +3025,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3289,9 +3304,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -3370,7 +3385,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3507,7 +3522,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3699,7 +3714,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "wasm-bindgen-shared", ] @@ -3733,7 +3748,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4012,7 +4027,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4032,7 +4047,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4102,7 +4117,7 @@ dependencies = [ [[package]] name = "zksync_vm2" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" +source = "git+https://github.com/matter-labs/vm2#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "enum_dispatch", "primitive-types", @@ -4114,7 +4129,7 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" +source = "git+https://github.com/matter-labs/vm2#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "primitive-types", ] diff --git a/LLVM.lock b/LLVM.lock index 15b374ab..9b393bc3 100644 --- a/LLVM.lock +++ b/LLVM.lock @@ -1,2 +1,2 @@ url = "https://github.com/matter-labs/era-compiler-llvm" -branch = "kpv-eravm-fix-linker-duplicating-symbols" +branch = "main" diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 8c601faf..69b3ca82 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -50,6 +50,7 @@ era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-commo era-compiler-downloader = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", branch = "main" } era-compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } +era-solc = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } era-compiler-vyper = { git = "https://github.com/matter-labs/era-compiler-vyper", branch = "main" } solidity-adapter = { path = "../solidity_adapter" } diff --git a/compiler_tester/src/compilers/eravm/mod.rs b/compiler_tester/src/compilers/eravm/mod.rs index b9a6d598..60dc9513 100644 --- a/compiler_tester/src/compilers/eravm/mod.rs +++ b/compiler_tester/src/compilers/eravm/mod.rs @@ -7,7 +7,7 @@ pub mod mode; use std::collections::BTreeMap; use std::collections::HashMap; -use era_compiler_solidity::CollectableError; +use era_solc::CollectableError; use crate::compilers::mode::Mode; use crate::compilers::Compiler; @@ -27,7 +27,7 @@ impl Compiler for EraVMCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + _libraries: era_solc::StandardJsonInputLibraries, _mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -41,12 +41,7 @@ impl Compiler for EraVMCompiler { let project = era_compiler_solidity::Project::try_from_eravm_assembly_sources( sources .into_iter() - .map(|(path, source)| { - ( - path, - era_compiler_solidity::SolcStandardJsonInputSource::from(source), - ) - }) + .map(|(path, source)| (path, era_solc::StandardJsonInputSource::from(source))) .collect(), None, )?; @@ -76,7 +71,7 @@ impl Compiler for EraVMCompiler { &self, _test_path: String, _sources: Vec<(String, String)>, - _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + _libraries: era_solc::StandardJsonInputLibraries, _mode: &Mode, _test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/compilers/llvm/mod.rs b/compiler_tester/src/compilers/llvm/mod.rs index 333cca86..c0edfef8 100644 --- a/compiler_tester/src/compilers/llvm/mod.rs +++ b/compiler_tester/src/compilers/llvm/mod.rs @@ -6,7 +6,7 @@ pub mod mode; use std::collections::HashMap; -use era_compiler_solidity::CollectableError; +use era_solc::CollectableError; use crate::compilers::mode::Mode; use crate::compilers::Compiler; @@ -39,7 +39,7 @@ impl Compiler for LLVMCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -57,12 +57,7 @@ impl Compiler for LLVMCompiler { let project = era_compiler_solidity::Project::try_from_llvm_ir_sources( sources .into_iter() - .map(|(path, source)| { - ( - path, - era_compiler_solidity::SolcStandardJsonInputSource::from(source), - ) - }) + .map(|(path, source)| (path, era_solc::StandardJsonInputSource::from(source))) .collect(), libraries, None, @@ -93,7 +88,7 @@ impl Compiler for LLVMCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + _libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, _test_params: Option<&solidity_adapter::Params>, llvm_options: Vec, @@ -110,14 +105,9 @@ impl Compiler for LLVMCompiler { let project = era_compiler_solidity::Project::try_from_llvm_ir_sources( sources .into_iter() - .map(|(path, source)| { - ( - path, - era_compiler_solidity::SolcStandardJsonInputSource::from(source), - ) - }) + .map(|(path, source)| (path, era_solc::StandardJsonInputSource::from(source))) .collect(), - era_compiler_solidity::SolcStandardJsonInputSettingsLibraries::default(), + era_solc::StandardJsonInputLibraries::default(), None, )?; diff --git a/compiler_tester/src/compilers/mod.rs b/compiler_tester/src/compilers/mod.rs index 54162604..08566f4f 100644 --- a/compiler_tester/src/compilers/mod.rs +++ b/compiler_tester/src/compilers/mod.rs @@ -26,7 +26,7 @@ pub trait Compiler: Send + Sync + 'static { &self, test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -39,7 +39,7 @@ pub trait Compiler: Send + Sync + 'static { &self, test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, llvm_options: Vec, diff --git a/compiler_tester/src/compilers/solidity/cache_key.rs b/compiler_tester/src/compilers/solidity/cache_key.rs index 3968f716..7145b414 100644 --- a/compiler_tester/src/compilers/solidity/cache_key.rs +++ b/compiler_tester/src/compilers/solidity/cache_key.rs @@ -12,7 +12,7 @@ pub struct CacheKey { /// The Solidity compiler version. pub version: semver::Version, /// The Solidity compiler output type. - pub codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + pub codegen: era_solc::StandardJsonInputCodegen, /// Whether to enable the EVMLA codegen via Yul IR. pub via_ir: bool, /// Whether to run the Solidity compiler optimizer. @@ -26,7 +26,7 @@ impl CacheKey { pub fn new( test_path: String, version: semver::Version, - codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + codegen: era_solc::StandardJsonInputCodegen, via_ir: bool, optimize: bool, ) -> Self { diff --git a/compiler_tester/src/compilers/solidity/mod.rs b/compiler_tester/src/compilers/solidity/mod.rs index c534d105..85660ae3 100644 --- a/compiler_tester/src/compilers/solidity/mod.rs +++ b/compiler_tester/src/compilers/solidity/mod.rs @@ -13,7 +13,7 @@ use std::path::Path; use itertools::Itertools; -use era_compiler_solidity::CollectableError; +use era_solc::CollectableError; use crate::compilers::cache::Cache; use crate::compilers::mode::Mode; @@ -30,7 +30,7 @@ use self::mode::Mode as SolidityMode; /// pub struct SolidityCompiler { /// The `solc` process output cache. - cache: Cache, + cache: Cache, } lazy_static::lazy_static! { @@ -42,9 +42,9 @@ lazy_static::lazy_static! { static ref MODES: Vec = { let mut solc_codegen_versions = Vec::new(); for (codegen, optimize, via_ir) in [ - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, false), - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, true), - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true, true), + (era_solc::StandardJsonInputCodegen::EVMLA, true, false), + (era_solc::StandardJsonInputCodegen::EVMLA, true, true), + (era_solc::StandardJsonInputCodegen::Yul, true, true), ] { for version in SolidityCompiler::all_versions(codegen, via_ir).expect("`solc` versions analysis error") { solc_codegen_versions.push((codegen, optimize, via_ir, version)); @@ -98,19 +98,15 @@ impl SolidityCompiler { /// /// Returns the `solc` executable by its version. /// - pub fn executable( - version: &semver::Version, - ) -> anyhow::Result { - era_compiler_solidity::SolcCompiler::try_from_path( - format!("{}/solc-{}", Self::DIRECTORY, version).as_str(), - ) + pub fn executable(version: &semver::Version) -> anyhow::Result { + era_solc::Compiler::try_from_path(format!("{}/solc-{}", Self::DIRECTORY, version).as_str()) } /// /// Returns the `solc` executable used to compile system contracts. /// - pub fn system_contract_executable() -> anyhow::Result { - era_compiler_solidity::SolcCompiler::try_from_path( + pub fn system_contract_executable() -> anyhow::Result { + era_solc::Compiler::try_from_path( format!("{}/solc-system-contracts", Self::DIRECTORY).as_str(), ) } @@ -119,7 +115,7 @@ impl SolidityCompiler { /// Returns the compiler versions downloaded for the specified compilation codegen. /// pub fn all_versions( - codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + codegen: era_solc::StandardJsonInputCodegen, via_ir: bool, ) -> anyhow::Result> { let mut versions = Vec::new(); @@ -149,14 +145,14 @@ impl SolidityCompiler { Ok(version) => version, Err(_) => continue, }; - if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul == codegen - && version < era_compiler_solidity::SolcCompiler::FIRST_YUL_VERSION + if era_solc::StandardJsonInputCodegen::Yul == codegen + && version < era_solc::Compiler::FIRST_YUL_VERSION { continue; } - if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA == codegen + if era_solc::StandardJsonInputCodegen::EVMLA == codegen && via_ir - && version < era_compiler_solidity::SolcCompiler::FIRST_VIA_IR_VERSION + && version < era_solc::Compiler::FIRST_VIA_IR_VERSION { continue; } @@ -171,9 +167,9 @@ impl SolidityCompiler { /// fn standard_json_output( sources: &[(String, String)], - libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: &era_solc::StandardJsonInputLibraries, mode: &SolidityMode, - ) -> anyhow::Result { + ) -> anyhow::Result { let solc_compiler = if mode.is_system_contracts_mode { Self::system_contract_executable() } else { @@ -181,50 +177,44 @@ impl SolidityCompiler { }?; let mut output_selection = - era_compiler_solidity::SolcStandardJsonInputSettingsSelection::new_required( - mode.solc_codegen, - ); - output_selection.extend( - era_compiler_solidity::SolcStandardJsonInputSettingsSelection::new(vec![ - era_compiler_solidity::SolcStandardJsonInputSettingsSelectionFlag::EraVMAssembly, - ]), - ); + era_solc::StandardJsonInputSelection::new_required(mode.solc_codegen); + output_selection.extend(era_solc::StandardJsonInputSelection::new(vec![ + era_solc::StandardJsonInputSelectionFlag::EraVMAssembly, + ])); - let evm_version = - if mode.solc_version >= era_compiler_solidity::SolcCompiler::FIRST_CANCUN_VERSION { - Some(era_compiler_common::EVMVersion::Cancun) - } else { - None - }; + let evm_version = if mode.solc_version >= era_solc::Compiler::FIRST_CANCUN_VERSION { + Some(era_compiler_common::EVMVersion::Cancun) + } else { + None + }; - let sources: BTreeMap = sources + let sources: BTreeMap = sources .iter() .map(|(path, source)| { ( path.to_owned(), - era_compiler_solidity::SolcStandardJsonInputSource::from(source.to_owned()), + era_solc::StandardJsonInputSource::from(source.to_owned()), ) }) .collect(); - let mut solc_input = - era_compiler_solidity::SolcStandardJsonInput::try_from_solidity_sources( - sources, - libraries.to_owned(), - BTreeSet::new(), - era_compiler_solidity::SolcStandardJsonInputSettingsOptimizer::default(), - Some(mode.solc_codegen), - evm_version, - mode.enable_eravm_extensions, - output_selection, - era_compiler_solidity::SolcStandardJsonInputSettingsMetadata::default(), - vec![], - vec![era_compiler_solidity::ErrorType::SendTransfer], - vec![], - false, - mode.via_ir, - ) - .map_err(|error| anyhow::anyhow!("Solidity standard JSON I/O error: {}", error))?; + let mut solc_input = era_solc::StandardJsonInput::try_from_solidity_sources( + sources, + libraries.to_owned(), + BTreeSet::new(), + era_solc::StandardJsonInputOptimizer::default(), + Some(mode.solc_codegen), + evm_version, + mode.enable_eravm_extensions, + output_selection, + era_solc::StandardJsonInputMetadata::default(), + vec![], + vec![era_solc::StandardJsonInputErrorType::SendTransfer], + vec![], + false, + mode.via_ir, + ) + .map_err(|error| anyhow::anyhow!("Solidity standard JSON I/O error: {}", error))?; let allow_paths = Path::new(Self::SOLC_ALLOW_PATHS) .canonicalize() @@ -249,9 +239,9 @@ impl SolidityCompiler { &self, test_path: String, sources: &[(String, String)], - libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: &era_solc::StandardJsonInputLibraries, mode: &SolidityMode, - ) -> anyhow::Result { + ) -> anyhow::Result { let cache_key = CacheKey::new( test_path, mode.solc_version.clone(), @@ -273,7 +263,7 @@ impl SolidityCompiler { /// Get the method identifiers from the solc output. /// fn get_method_identifiers( - solc_output: &era_compiler_solidity::SolcStandardJsonOutput, + solc_output: &era_solc::StandardJsonOutput, ) -> anyhow::Result>> { let mut method_identifiers = BTreeMap::new(); for (path, file) in solc_output.contracts.iter() { @@ -286,14 +276,6 @@ impl SolidityCompiler { anyhow::anyhow!("EVM object of the contract `{}:{}` not found", path, name) })? .method_identifiers - .as_ref() - .ok_or_else(|| { - anyhow::anyhow!( - "Method identifiers of the contract `{}:{}` not found", - path, - name - ) - })? .iter() { let selector = @@ -317,7 +299,7 @@ impl SolidityCompiler { /// Get the last contract from the solc output. /// fn get_last_contract( - solc_output: &era_compiler_solidity::SolcStandardJsonOutput, + solc_output: &era_solc::StandardJsonOutput, sources: &[(String, String)], ) -> anyhow::Result { for (path, _source) in sources.iter().rev() { @@ -340,7 +322,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -403,7 +385,7 @@ impl Compiler for SolidityCompiler { build.write_to_standard_json( &mut solc_output, - Some(&era_compiler_solidity::SolcVersion::new( + Some(&era_solc::Version::new( mode.solc_version.to_string(), mode.solc_version.to_owned(), None, @@ -422,7 +404,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, _test_params: Option<&solidity_adapter::Params>, llvm_options: Vec, diff --git a/compiler_tester/src/compilers/solidity/mode.rs b/compiler_tester/src/compilers/solidity/mode.rs index 45d3d0c2..3b4c7ecd 100644 --- a/compiler_tester/src/compilers/solidity/mode.rs +++ b/compiler_tester/src/compilers/solidity/mode.rs @@ -16,7 +16,7 @@ pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, /// The Solidity compiler output type. - pub solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + pub solc_codegen: era_solc::StandardJsonInputCodegen, /// Whether to enable the EVMLA codegen via Yul IR. pub via_ir: bool, /// Whether to run the Solidity compiler optimizer. @@ -35,7 +35,7 @@ impl Mode { /// pub fn new( solc_version: semver::Version, - solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + solc_codegen: era_solc::StandardJsonInputCodegen, via_ir: bool, solc_optimize: bool, mut llvm_optimizer_settings: era_compiler_llvm_context::OptimizerSettings, @@ -113,15 +113,15 @@ impl Mode { } match self.solc_codegen { - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => { + era_solc::StandardJsonInputCodegen::Yul => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA if self.via_ir => { + era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => { + era_solc::StandardJsonInputCodegen::EVMLA => { params.compile_via_yul != solidity_adapter::CompileViaYul::True } } @@ -134,11 +134,9 @@ impl std::fmt::Display for Mode { f, "{}{}{} {}", match self.solc_codegen { - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => "Y", - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA - if self.via_ir => - "I", - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => "E", + era_solc::StandardJsonInputCodegen::Yul => "Y", + era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => "I", + era_solc::StandardJsonInputCodegen::EVMLA => "E", }, if self.solc_optimize { '+' } else { '-' }, self.llvm_optimizer_settings, diff --git a/compiler_tester/src/compilers/solidity/upstream/mod.rs b/compiler_tester/src/compilers/solidity/upstream/mod.rs index 10078d40..283356fc 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mod.rs @@ -21,9 +21,9 @@ use crate::vm::evm::input::Input as EVMInput; use self::mode::Mode as SolidityUpstreamMode; use self::solc::standard_json::input::language::Language as SolcStandardJsonInputLanguage; -use self::solc::standard_json::input::settings::debug::Debug as SolcStandardJsonInputSettingsDebug; -use self::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; -use self::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; +use self::solc::standard_json::input::settings::debug::Debug as SolcStandardJsonInputDebug; +use self::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputOptimizer; +use self::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSelection; use self::solc::standard_json::input::Input as SolcStandardJsonInput; use self::solc::standard_json::output::Output as SolcStandardJsonOutput; use self::solc::Compiler as SolcUpstreamCompiler; @@ -50,12 +50,12 @@ lazy_static::lazy_static! { static ref SOLIDITY_MODES: Vec = { let mut modes = Vec::new(); for (codegen, optimize, via_ir) in [ - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, false, false), - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, false, true), - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, false), - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA, true, true), - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, false, true), - (era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true, true), + (era_solc::StandardJsonInputCodegen::EVMLA, false, false), + (era_solc::StandardJsonInputCodegen::EVMLA, false, true), + (era_solc::StandardJsonInputCodegen::EVMLA, true, false), + (era_solc::StandardJsonInputCodegen::EVMLA, true, true), + (era_solc::StandardJsonInputCodegen::Yul, false, true), + (era_solc::StandardJsonInputCodegen::Yul, true, true), ] { for version in SolidityCompiler::all_versions(codegen, via_ir).expect("`solc` versions analysis error") { modes.push(SolidityUpstreamMode::new(version, codegen, via_ir, false, optimize).into()); @@ -74,7 +74,7 @@ lazy_static::lazy_static! { for optimize in [ false, true ] { - for version in SolidityCompiler::all_versions(era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, true).expect("`solc` versions analysis error") { + for version in SolidityCompiler::all_versions(era_solc::StandardJsonInputCodegen::Yul, true).expect("`solc` versions analysis error") { modes.push(YulUpstreamMode::new(version, false, optimize).into()); } } @@ -87,7 +87,7 @@ lazy_static::lazy_static! { /// All compilers must be downloaded before initialization. /// static ref SOLIDITY_MLIR_MODES: Vec = { - vec![SolidityUpstreamMode::new(semver::Version::new(0, 8, 26), era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, false, true, false).into()] + vec![SolidityUpstreamMode::new(semver::Version::new(0, 8, 26), era_solc::StandardJsonInputCodegen::Yul, false, true, false).into()] }; /// @@ -140,7 +140,7 @@ impl SolidityCompiler { /// Returns the compiler versions downloaded for the specified compilation codegen. /// pub fn all_versions( - codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + codegen: era_solc::StandardJsonInputCodegen, via_ir: bool, ) -> anyhow::Result> { let mut versions = Vec::new(); @@ -170,12 +170,12 @@ impl SolidityCompiler { Ok(version) => version, Err(_) => continue, }; - if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul == codegen + if era_solc::StandardJsonInputCodegen::Yul == codegen && version < SolcUpstreamCompiler::FIRST_YUL_VERSION { continue; } - if era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA == codegen + if era_solc::StandardJsonInputCodegen::EVMLA == codegen && via_ir && version < SolcUpstreamCompiler::FIRST_VIA_IR_VERSION { @@ -194,7 +194,7 @@ impl SolidityCompiler { language: SolcStandardJsonInputLanguage, toolchain: Toolchain, sources: &[(String, String)], - libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: &era_solc::StandardJsonInputLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, ) -> anyhow::Result { @@ -205,15 +205,13 @@ impl SolidityCompiler { }; let mut solc = Self::executable(toolchain, solc_version)?; - let output_selection = SolcStandardJsonInputSettingsSelection::new_required(match mode { + let output_selection = SolcStandardJsonInputSelection::new_required(match mode { Mode::SolidityUpstream(mode) => mode.solc_codegen, - Mode::YulUpstream(_mode) => { - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul - } + Mode::YulUpstream(_mode) => era_solc::StandardJsonInputCodegen::Yul, mode => anyhow::bail!("Unsupported mode: {mode}"), }); - let optimizer = SolcStandardJsonInputSettingsOptimizer::new(match mode { + let optimizer = SolcStandardJsonInputOptimizer::new(match mode { Mode::SolidityUpstream(mode) => mode.solc_optimize, Mode::YulUpstream(mode) => mode.solc_optimize, mode => anyhow::bail!("Unsupported mode: {mode}"), @@ -244,9 +242,7 @@ impl SolidityCompiler { let debug = if solc_version >= &semver::Version::new(0, 6, 3) { test_params.map(|test_params| { - SolcStandardJsonInputSettingsDebug::new(Some( - test_params.revert_strings.to_string(), - )) + SolcStandardJsonInputDebug::new(Some(test_params.revert_strings.to_string())) }) } else { None @@ -283,7 +279,7 @@ impl SolidityCompiler { test_path: String, language: SolcStandardJsonInputLanguage, sources: &[(String, String)], - libraries: &era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: &era_solc::StandardJsonInputLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, ) -> anyhow::Result { @@ -298,7 +294,7 @@ impl SolidityCompiler { Mode::YulUpstream(mode) => CacheKey::new( test_path, mode.solc_version.to_owned(), - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, + era_solc::StandardJsonInputCodegen::Yul, true, mode.solc_optimize, ), @@ -427,7 +423,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, _llvm_options: Vec, _debug_config: Option, @@ -519,7 +515,7 @@ impl Compiler for SolidityCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/compilers/solidity/upstream/mode.rs b/compiler_tester/src/compilers/solidity/upstream/mode.rs index 3a37b435..41fc264a 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mode.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mode.rs @@ -14,7 +14,7 @@ pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, /// The Solidity compiler output type. - pub solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + pub solc_codegen: era_solc::StandardJsonInputCodegen, /// Whether to enable the EVMLA codegen via Yul IR. pub via_ir: bool, /// Whether to enable the MLIR codegen. @@ -29,7 +29,7 @@ impl Mode { /// pub fn new( solc_version: semver::Version, - solc_codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, + solc_codegen: era_solc::StandardJsonInputCodegen, via_ir: bool, via_mlir: bool, solc_optimize: bool, @@ -98,15 +98,15 @@ impl Mode { } match self.solc_codegen { - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => { + era_solc::StandardJsonInputCodegen::Yul => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA if self.via_ir => { + era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => { params.compile_via_yul != solidity_adapter::CompileViaYul::False && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True } - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => { + era_solc::StandardJsonInputCodegen::EVMLA => { params.compile_via_yul != solidity_adapter::CompileViaYul::True } } @@ -123,11 +123,9 @@ impl std::fmt::Display for Mode { f, "{}{} {}", match self.solc_codegen { - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => "Y", - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA - if self.via_ir => - "I", - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => "E", + era_solc::StandardJsonInputCodegen::Yul => "Y", + era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => "I", + era_solc::StandardJsonInputCodegen::EVMLA => "E", }, if self.solc_optimize { '+' } else { '-' }, self.solc_version, diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs index a1feb86d..b051e233 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/mod.rs @@ -13,9 +13,9 @@ use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; use serde::Serialize; -use self::settings::debug::Debug as SolcStandardJsonInputSettingsDebug; -use self::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; -use self::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; +use self::settings::debug::Debug as SolcStandardJsonInputDebug; +use self::settings::optimizer::Optimizer as SolcStandardJsonInputOptimizer; +use self::settings::selection::Selection as SolcStandardJsonInputSelection; use self::language::Language; use self::settings::Settings; @@ -45,13 +45,13 @@ impl Input { language: Language, evm_version: Option, sources: BTreeMap, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, remappings: Option>, - output_selection: SolcStandardJsonInputSettingsSelection, + output_selection: SolcStandardJsonInputSelection, via_ir: bool, via_mlir: bool, - optimizer: SolcStandardJsonInputSettingsOptimizer, - debug: Option, + optimizer: SolcStandardJsonInputOptimizer, + debug: Option, ) -> anyhow::Result { let sources = sources .into_par_iter() diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs index 1ffd5b12..8daf0889 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/mod.rs @@ -26,9 +26,9 @@ pub struct Settings { /// The linker library addresses. #[serde( default, - skip_serializing_if = "era_compiler_solidity::SolcStandardJsonInputSettingsLibraries::is_empty" + skip_serializing_if = "era_solc::StandardJsonInputLibraries::is_empty" )] - pub libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + pub libraries: era_solc::StandardJsonInputLibraries, /// The sorted list of remappings. #[serde(skip_serializing_if = "Option::is_none")] pub remappings: Option>, @@ -62,7 +62,7 @@ impl Settings { /// pub fn new( evm_version: Option, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, remappings: Option>, output_selection: Selection, via_ir: bool, diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs index e5eb3fce..20ef485b 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/flag.rs @@ -28,11 +28,11 @@ pub enum Flag { EVMLA, } -impl From for Flag { - fn from(codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen) -> Self { +impl From for Flag { + fn from(codegen: era_solc::StandardJsonInputCodegen) -> Self { match codegen { - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul => Self::Yul, - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::EVMLA => Self::EVMLA, + era_solc::StandardJsonInputCodegen::Yul => Self::Yul, + era_solc::StandardJsonInputCodegen::EVMLA => Self::EVMLA, } } } diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs index d49a842c..263a60a0 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/file/mod.rs @@ -27,9 +27,7 @@ impl File { /// /// Creates the selection required by EVM compilation process. /// - pub fn new_required( - codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, - ) -> Self { + pub fn new_required(codegen: era_solc::StandardJsonInputCodegen) -> Self { Self { per_file: Some(HashSet::from_iter([SelectionFlag::AST])), per_contract: Some(HashSet::from_iter([ diff --git a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs index 4e8ffcf5..db6cb4ee 100644 --- a/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/solc/standard_json/input/settings/selection/mod.rs @@ -22,9 +22,7 @@ impl Selection { /// /// Creates the selection required by EVM compilation process. /// - pub fn new_required( - codegen: era_compiler_solidity::SolcStandardJsonInputSettingsCodegen, - ) -> Self { + pub fn new_required(codegen: era_solc::StandardJsonInputCodegen) -> Self { Self { all: Some(FileSelection::new_required(codegen)), } diff --git a/compiler_tester/src/compilers/vyper/mod.rs b/compiler_tester/src/compilers/vyper/mod.rs index b89f2a61..5d892239 100644 --- a/compiler_tester/src/compilers/vyper/mod.rs +++ b/compiler_tester/src/compilers/vyper/mod.rs @@ -195,7 +195,7 @@ impl Compiler for VyperCompiler { &self, test_path: String, sources: Vec<(String, String)>, - _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + _libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -258,7 +258,7 @@ impl Compiler for VyperCompiler { &self, _test_path: String, _sources: Vec<(String, String)>, - _libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + _libraries: era_solc::StandardJsonInputLibraries, _mode: &Mode, _test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/compilers/yul/mod.rs b/compiler_tester/src/compilers/yul/mod.rs index 824bf22b..5268c179 100644 --- a/compiler_tester/src/compilers/yul/mod.rs +++ b/compiler_tester/src/compilers/yul/mod.rs @@ -7,7 +7,7 @@ pub mod mode_upstream; use std::collections::HashMap; -use era_compiler_solidity::CollectableError; +use era_solc::CollectableError; use crate::compilers::mode::Mode; use crate::compilers::solidity::upstream::solc::standard_json::input::language::Language as SolcStandardJsonInputLanguage; @@ -54,7 +54,7 @@ impl Compiler for YulCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -64,9 +64,9 @@ impl Compiler for YulCompiler { let solc_version = if mode.enable_eravm_extensions { None } else { - Some(era_compiler_solidity::SolcVersion::new( - era_compiler_solidity::SolcCompiler::LAST_SUPPORTED_VERSION.to_string(), - era_compiler_solidity::SolcCompiler::LAST_SUPPORTED_VERSION, + Some(era_solc::Version::new( + era_solc::Compiler::LAST_SUPPORTED_VERSION.to_string(), + era_solc::Compiler::LAST_SUPPORTED_VERSION, None, )) }; @@ -81,12 +81,7 @@ impl Compiler for YulCompiler { let sources = sources .into_iter() - .map(|(path, source)| { - ( - path, - era_compiler_solidity::SolcStandardJsonInputSource::from(source), - ) - }) + .map(|(path, source)| (path, era_solc::StandardJsonInputSource::from(source))) .collect(); let project = era_compiler_solidity::Project::try_from_yul_sources( @@ -131,7 +126,7 @@ impl Compiler for YulCompiler { &self, test_path: String, sources: Vec<(String, String)>, - libraries: era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, test_params: Option<&solidity_adapter::Params>, _llvm_options: Vec, diff --git a/compiler_tester/src/directories/ethereum/test.rs b/compiler_tester/src/directories/ethereum/test.rs index 99172fc7..31a43242 100644 --- a/compiler_tester/src/directories/ethereum/test.rs +++ b/compiler_tester/src/directories/ethereum/test.rs @@ -124,7 +124,7 @@ impl EthereumTest { ) -> anyhow::Result<( web3::types::Address, BTreeMap, - era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + era_solc::StandardJsonInputLibraries, )> { let mut caller = solidity_adapter::account_address(solidity_adapter::DEFAULT_ACCOUNT_INDEX); diff --git a/compiler_tester/src/directories/matter_labs/test/mod.rs b/compiler_tester/src/directories/matter_labs/test/mod.rs index 313e7863..8a32951e 100644 --- a/compiler_tester/src/directories/matter_labs/test/mod.rs +++ b/compiler_tester/src/directories/matter_labs/test/mod.rs @@ -230,7 +230,7 @@ impl MatterLabsTest { &self, address_iterator: &mut API, ) -> ( - era_compiler_solidity::SolcStandardJsonInputSettingsLibraries, + era_solc::StandardJsonInputLibraries, BTreeMap, ) where diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index 8dc79694..fae0d6b2 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -293,7 +293,7 @@ impl SystemContracts { let solidity_optimizer_settings = era_compiler_llvm_context::OptimizerSettings::cycles(); let solidity_mode = SolidityMode::new( solc_version, - era_compiler_solidity::SolcStandardJsonInputSettingsCodegen::Yul, + era_solc::StandardJsonInputCodegen::Yul, true, true, solidity_optimizer_settings, @@ -430,7 +430,7 @@ impl SystemContracts { .compile_for_eravm( "system-contracts".to_owned(), sources, - era_compiler_solidity::SolcStandardJsonInputSettingsLibraries::default(), + era_solc::StandardJsonInputLibraries::default(), mode, llvm_options, debug_config, diff --git a/fuzzer/Cargo.toml b/fuzzer/Cargo.toml index 85a1434d..d5138c51 100644 --- a/fuzzer/Cargo.toml +++ b/fuzzer/Cargo.toml @@ -36,6 +36,7 @@ zkevm_tester = { git = "https://github.com/matter-labs/era-zkevm_tester", branch era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", branch = "main" } era-compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } +era-solc = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } compiler-tester = { path = "../compiler_tester" } solidity-adapter = { path = "../solidity_adapter" } diff --git a/fuzzer/fuzz_targets/common.rs b/fuzzer/fuzz_targets/common.rs index 6e3044b5..630ec565 100644 --- a/fuzzer/fuzz_targets/common.rs +++ b/fuzzer/fuzz_targets/common.rs @@ -9,7 +9,6 @@ use std::{path::PathBuf, sync::Arc}; use compiler_tester::{ Buildable, EthereumTest, Mode, SolidityCompiler, SolidityMode, Summary, Workflow, }; -use era_compiler_solidity::SolcStandardJsonInputSettingsCodegen; pub use solidity_adapter::{ test::function_call::parser::{ @@ -160,7 +159,7 @@ pub fn build_and_run(test: EthereumTest) -> anyhow::Result

{ let solc_version = semver::Version::new(0, 8, 26); let mode = Mode::Solidity(SolidityMode::new( solc_version, - SolcStandardJsonInputSettingsCodegen::Yul, + era_solc::StandardJsonInputCodegen::Yul, true, true, era_compiler_llvm_context::OptimizerSettings::try_from_cli('3') From c4758cd82eb6af2116ac993303b2b612448a2934 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Thu, 21 Nov 2024 13:07:45 +0800 Subject: [PATCH 18/31] feat(evm): EVMLA (E, I) pipelines --- Cargo.lock | 563 +++++++++++++++--- compiler_tester/src/compilers/solidity/mod.rs | 6 +- compiler_tester/src/compilers/yul/mod.rs | 3 +- 3 files changed, 479 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95a63758..e266a910 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "alloy-eip2930" @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" +checksum = "9fce5dbd6a4f118eecc4719eaa9c7ffc31c315e6c5ccde3642db927802312425" dependencies = [ "alloy-rlp", "bytes", @@ -127,7 +127,56 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", ] [[package]] @@ -138,9 +187,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "ark-ff" @@ -302,7 +351,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -533,9 +582,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "jobserver", "libc", @@ -570,6 +619,52 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "clap" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "clap_lex" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "colored" version = "2.1.0" @@ -698,9 +793,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -825,7 +920,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -845,7 +940,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", "unicode-xid", ] @@ -870,6 +965,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -934,7 +1040,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -945,7 +1051,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -986,7 +1092,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#5350b284c14ff19309d02c9c38a0fe88c93e037b" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#47d3a2143a6b063af9eed5a801e9a4ae90bbc5da" dependencies = [ "anyhow", "era-compiler-common", @@ -1000,10 +1106,11 @@ dependencies = [ [[package]] name = "era-compiler-solidity" -version = "1.5.7" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#d965e139c2f5dcaddf25d8c568103c840ac38c44" +version = "1.5.8" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#1843a4431485fb8e534ab3d17b3e21cb05a276d9" dependencies = [ "anyhow", + "clap 4.5.21", "era-compiler-common", "era-compiler-llvm-context", "era-solc", @@ -1020,7 +1127,6 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "structopt", "thiserror", "zkevm_opcode_defs", ] @@ -1028,7 +1134,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.7" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#9c8fd92cf1f2b1330750d18e67ce007642de82ef" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#145d7301ee383a2295b1354b75c7ac289206ca9a" dependencies = [ "anyhow", "era-compiler-common", @@ -1050,8 +1156,8 @@ dependencies = [ [[package]] name = "era-solc" -version = "1.5.7" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#d965e139c2f5dcaddf25d8c568103c840ac38c44" +version = "1.5.8" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#1843a4431485fb8e534ab3d17b3e21cb05a276d9" dependencies = [ "anyhow", "boolinator", @@ -1067,8 +1173,8 @@ dependencies = [ [[package]] name = "era-yul" -version = "1.5.7" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#d965e139c2f5dcaddf25d8c568103c840ac38c44" +version = "1.5.8" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#1843a4431485fb8e534ab3d17b3e21cb05a276d9" dependencies = [ "anyhow", "regex", @@ -1156,9 +1262,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fastrlp" @@ -1303,7 +1409,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -1418,9 +1524,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "headers" @@ -1455,6 +1561,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1576,6 +1688,124 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "idna" version = "0.4.0" @@ -1588,12 +1818,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1641,13 +1882,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", ] [[package]] name = "inkwell" version = "0.4.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c506921a593e3d1e11c3e84142d2a32a38d483fc" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#2b02c986508b68a38f9eb0f46919d5d1a1b0bfc1" dependencies = [ "either", "inkwell_internals", @@ -1662,11 +1903,11 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.9.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c506921a593e3d1e11c3e84142d2a32a38d483fc" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#2b02c986508b68a38f9eb0f46919d5d1a1b0bfc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -1698,6 +1939,12 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -1718,9 +1965,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "jobserver" @@ -1799,9 +2046,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libfuzzer-sys" @@ -1847,10 +2094,16 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "llvm-sys" version = "170.0.1" -source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#4b0421ff90757ed7a701ce5b81926515605dd4bd" +source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#b06c3d1989fa5d77e491e6aea709dd5c76621aff" dependencies = [ "anyhow", "cc", @@ -2078,7 +2331,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -2125,7 +2378,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -2136,9 +2389,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.4.0+3.4.0" +version = "300.4.1+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" dependencies = [ "cc", ] @@ -2184,14 +2437,14 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "8781a75c6205af67215f382092b6e0a4ff3734798523e69073d4bcd294ec767b" dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] @@ -2272,7 +2525,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -2379,9 +2632,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "307e3004becf10f5a6e0d59d20f3cd28231b0e0827a96cd3e0ce6d14bc1e4bb3" dependencies = [ "unicode-ident", ] @@ -2408,9 +2661,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] @@ -2527,9 +2780,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2776,9 +3029,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -2850,9 +3103,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scale-info" -version = "2.11.5" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "bitvec", "cfg-if", @@ -2863,21 +3116,21 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.5" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -2964,9 +3217,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -2992,9 +3245,9 @@ dependencies = [ [[package]] name = "semver-parser" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" dependencies = [ "pest", ] @@ -3025,7 +3278,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -3229,6 +3482,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "stacker" version = "0.1.17" @@ -3248,13 +3507,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "structopt" version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "clap", + "clap 2.34.0", "lazy_static", "structopt-derive", ] @@ -3265,7 +3530,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro-error", "proc-macro2", "quote", @@ -3304,9 +3569,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.86" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -3319,6 +3584,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -3348,9 +3624,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -3385,7 +3661,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -3406,6 +3682,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -3423,9 +3709,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -3522,7 +3808,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", ] [[package]] @@ -3578,9 +3864,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -3635,15 +3921,33 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.0" @@ -3714,7 +4018,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", "wasm-bindgen-shared", ] @@ -3748,7 +4052,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4000,6 +4304,18 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -4009,6 +4325,30 @@ dependencies = [ "tap", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -4027,7 +4367,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", ] [[package]] @@ -4047,7 +4408,29 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.89", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", ] [[package]] diff --git a/compiler_tester/src/compilers/solidity/mod.rs b/compiler_tester/src/compilers/solidity/mod.rs index 85660ae3..07dbdc9a 100644 --- a/compiler_tester/src/compilers/solidity/mod.rs +++ b/compiler_tester/src/compilers/solidity/mod.rs @@ -80,6 +80,9 @@ impl Default for SolidityCompiler { } impl SolidityCompiler { + /// The last ZKsync `solc` revision. + pub const LAST_ZKSYNC_SOLC_REVISION: semver::Version = semver::Version::new(1, 0, 1); + /// The compiler executables directory. const DIRECTORY: &'static str = "solc-bin/"; @@ -224,7 +227,6 @@ impl SolidityCompiler { solc_compiler.standard_json( &mut solc_input, - Some(mode.solc_codegen), &mut vec![], None, vec![], @@ -388,7 +390,7 @@ impl Compiler for SolidityCompiler { Some(&era_solc::Version::new( mode.solc_version.to_string(), mode.solc_version.to_owned(), - None, + Self::LAST_ZKSYNC_SOLC_REVISION, )), )?; solc_output.collect_errors()?; diff --git a/compiler_tester/src/compilers/yul/mod.rs b/compiler_tester/src/compilers/yul/mod.rs index 5268c179..687c7d3e 100644 --- a/compiler_tester/src/compilers/yul/mod.rs +++ b/compiler_tester/src/compilers/yul/mod.rs @@ -12,6 +12,7 @@ use era_solc::CollectableError; use crate::compilers::mode::Mode; use crate::compilers::solidity::upstream::solc::standard_json::input::language::Language as SolcStandardJsonInputLanguage; use crate::compilers::solidity::upstream::SolidityCompiler as SolidityUpstreamCompiler; +use crate::compilers::solidity::SolidityCompiler; use crate::compilers::Compiler; use crate::toolchain::Toolchain; use crate::vm::eravm::input::Input as EraVMInput; @@ -67,7 +68,7 @@ impl Compiler for YulCompiler { Some(era_solc::Version::new( era_solc::Compiler::LAST_SUPPORTED_VERSION.to_string(), era_solc::Compiler::LAST_SUPPORTED_VERSION, - None, + SolidityCompiler::LAST_ZKSYNC_SOLC_REVISION, )) }; From bdcbec5ae3a24f00da866c69ffd4ea276df11ea8 Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Fri, 22 Nov 2024 06:12:49 +0000 Subject: [PATCH 19/31] ci: update cargo deny config (#121) --- deny.toml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/deny.toml b/deny.toml index 7eeccc63..cb4c6042 100644 --- a/deny.toml +++ b/deny.toml @@ -1,17 +1,10 @@ -all-features = false -no-default-features = false - [advisories] -vulnerability = "deny" -unmaintained = "warn" yanked = "warn" -notice = "warn" ignore = [ #"RUSTSEC-0000-0000", ] [licenses] -unlicensed = "deny" allow = [ #"Apache-2.0 WITH LLVM-exception", "MIT", @@ -20,17 +13,12 @@ allow = [ "Unlicense", "MPL-2.0", "Unicode-DFS-2016", + "Unicode-3.0", "CC0-1.0", "BSD-2-Clause", "BSD-3-Clause", "Zlib", ] -deny = [ - #"Nokia", -] -copyleft = "warn" -allow-osi-fsf-free = "neither" -default = "deny" confidence-threshold = 0.93 exceptions = [ { allow = ["OpenSSL"], name = "ring" }, From a0c56d99c5d473383472b51cda45784140d16e6d Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Fri, 22 Nov 2024 14:19:10 +0800 Subject: [PATCH 20/31] fix: replace structopt with clap (#122) --- Cargo.lock | 175 +++--------------- Cargo.toml | 2 +- benchmark_analyzer/Cargo.toml | 2 +- .../src/benchmark_analyzer/arguments.rs | 28 +-- .../src/benchmark_analyzer/main.rs | 6 +- compiler_tester/Cargo.toml | 2 +- .../src/compiler_tester/arguments.rs | 76 ++++---- compiler_tester/src/compiler_tester/main.rs | 30 +-- compiler_tester/src/workflow.rs | 2 +- coverage_watcher/Cargo.toml | 2 +- .../src/coverage_watcher/arguments.rs | 20 +- coverage_watcher/src/coverage_watcher/main.rs | 4 +- rust-toolchain.toml | 2 +- solidity_adapter/Cargo.toml | 2 +- .../src/tests_updater/arguments.rs | 30 +-- solidity_adapter/src/tests_updater/main.rs | 3 +- 16 files changed, 105 insertions(+), 281 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e266a910..3fda2003 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,12 +185,6 @@ version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" - [[package]] name = "ark-ff" version = "0.3.0" @@ -410,10 +404,10 @@ name = "benchmark-analyzer" version = "1.5.0" dependencies = [ "anyhow", + "clap", "colored", "serde", "serde_json", - "structopt", ] [[package]] @@ -586,8 +580,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ - "jobserver", - "libc", "shlex", ] @@ -608,17 +600,6 @@ dependencies = [ "unsigned-varint 0.3.3", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "bitflags 1.3.2", - "textwrap", - "unicode-width", -] - [[package]] name = "clap" version = "4.5.21" @@ -647,7 +628,7 @@ version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.89", @@ -682,6 +663,7 @@ dependencies = [ "anyhow", "benchmark-analyzer", "bincode", + "clap", "colored", "era-compiler-common", "era-compiler-downloader", @@ -709,7 +691,6 @@ dependencies = [ "serde_yaml", "sha3 0.10.8", "solidity-adapter", - "structopt", "web3", "which", "zkevm_opcode_defs", @@ -717,27 +698,11 @@ dependencies = [ "zksync_vm2", ] -[[package]] -name = "compiler-tester-fuzz" -version = "1.5.0" -dependencies = [ - "anyhow", - "compiler-tester", - "era-compiler-common", - "era-compiler-llvm-context", - "era-compiler-solidity", - "era-solc", - "libfuzzer-sys", - "semver 1.0.23", - "solidity-adapter", - "zkevm_tester", -] - [[package]] name = "const-hex" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +checksum = "487981fa1af147182687064d0a2c336586d337a606595ced9ffb0c685c250c73" dependencies = [ "cfg-if", "cpufeatures", @@ -785,17 +750,17 @@ name = "coverage-watcher" version = "1.5.0" dependencies = [ "anyhow", + "clap", "era-compiler-common", "serde", "serde_yaml", - "structopt", ] [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -1107,10 +1072,10 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#1843a4431485fb8e534ab3d17b3e21cb05a276d9" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#712957b06725d8673d759c151a2413a927a6cb09" dependencies = [ "anyhow", - "clap 4.5.21", + "clap", "era-compiler-common", "era-compiler-llvm-context", "era-solc", @@ -1133,10 +1098,11 @@ dependencies = [ [[package]] name = "era-compiler-vyper" -version = "1.5.7" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#145d7301ee383a2295b1354b75c7ac289206ca9a" +version = "1.5.8" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#870ed37ea2150f03272ded47271d06fd2349dd7a" dependencies = [ "anyhow", + "clap", "era-compiler-common", "era-compiler-llvm-context", "hex", @@ -1149,7 +1115,6 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "structopt", "which", "zkevm_opcode_defs", ] @@ -1157,7 +1122,7 @@ dependencies = [ [[package]] name = "era-solc" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#1843a4431485fb8e534ab3d17b3e21cb05a276d9" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#712957b06725d8673d759c151a2413a927a6cb09" dependencies = [ "anyhow", "boolinator", @@ -1174,7 +1139,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#1843a4431485fb8e534ab3d17b3e21cb05a276d9" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#712957b06725d8673d759c151a2413a927a6cb09" dependencies = [ "anyhow", "regex", @@ -1552,15 +1517,6 @@ dependencies = [ "http", ] -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.5.0" @@ -1866,13 +1822,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] @@ -1969,15 +1925,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.72" @@ -2050,17 +1997,6 @@ version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" -[[package]] -name = "libfuzzer-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - [[package]] name = "libm" version = "0.2.11" @@ -2606,35 +2542,11 @@ dependencies = [ "toml_edit 0.22.22", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307e3004becf10f5a6e0d59d20f3cd28231b0e0827a96cd3e0ce6d14bc1e4bb3" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3455,6 +3367,7 @@ name = "solidity-adapter" version = "1.5.0" dependencies = [ "anyhow", + "clap", "colored", "era-compiler-common", "md5", @@ -3462,7 +3375,6 @@ dependencies = [ "semver 1.0.23", "serde", "serde_yaml", - "structopt", "web3", ] @@ -3513,30 +3425,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "substrate-bn" version = "0.6.0" @@ -3635,15 +3523,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.64" @@ -3877,18 +3756,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index 7a574496..02fe53d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,6 @@ members = [ "solidity_adapter", "coverage_watcher", "benchmark_analyzer", - "fuzzer" + # "fuzzer" ] resolver = "2" diff --git a/benchmark_analyzer/Cargo.toml b/benchmark_analyzer/Cargo.toml index 92ae8982..4e23ff99 100644 --- a/benchmark_analyzer/Cargo.toml +++ b/benchmark_analyzer/Cargo.toml @@ -13,7 +13,7 @@ name = "benchmark-analyzer" path = "src/benchmark_analyzer/main.rs" [dependencies] -structopt = { version = "=0.3.26", default-features = false } +clap = { version = "=4.5.21", features = ["derive"] } anyhow = "=1.0.89" colored = "=2.1.0" diff --git a/benchmark_analyzer/src/benchmark_analyzer/arguments.rs b/benchmark_analyzer/src/benchmark_analyzer/arguments.rs index ffc96623..f86f5077 100644 --- a/benchmark_analyzer/src/benchmark_analyzer/arguments.rs +++ b/benchmark_analyzer/src/benchmark_analyzer/arguments.rs @@ -4,39 +4,27 @@ use std::path::PathBuf; -use structopt::StructOpt; +use clap::Parser; /// /// The benchmark analyzer arguments. /// -#[derive(Debug, StructOpt)] -#[structopt( - name = "benchmark-analyzer", - about = "ZKsync toolchain benchmark analyzer" -)] +#[derive(Debug, Parser)] +#[command(about, long_about = None)] pub struct Arguments { /// The reference build benchmark. - #[structopt(long = "reference", default_value = "reference.json")] + #[structopt(long, default_value = "reference.json")] pub reference: PathBuf, /// The candidate build benchmark. - #[structopt(long = "candidate", default_value = "candidate.json")] + #[structopt(long, default_value = "candidate.json")] pub candidate: PathBuf, /// The output file. If unset, the result is printed to `stdout`. - #[structopt(short = "o", long = "output-file")] - pub output_path: Option, + #[structopt(short = 'o', long)] + pub output_file: Option, /// Maximum number of results displayed in a group. - #[structopt(long = "group-max", default_value = "100")] + #[structopt(long, default_value_t = 100)] pub group_max: usize, } - -impl Arguments { - /// - /// A shortcut constructor. - /// - pub fn new() -> Self { - Self::from_args() - } -} diff --git a/benchmark_analyzer/src/benchmark_analyzer/main.rs b/benchmark_analyzer/src/benchmark_analyzer/main.rs index e0a0d8a6..5c3cdaf9 100644 --- a/benchmark_analyzer/src/benchmark_analyzer/main.rs +++ b/benchmark_analyzer/src/benchmark_analyzer/main.rs @@ -6,20 +6,22 @@ pub(crate) mod arguments; use std::io::Write; +use clap::Parser; + use self::arguments::Arguments; /// /// The application entry point. /// fn main() -> anyhow::Result<()> { - let arguments = Arguments::new(); + let arguments = Arguments::try_parse()?; let reference = benchmark_analyzer::Benchmark::try_from(arguments.reference)?; let candidate = benchmark_analyzer::Benchmark::try_from(arguments.candidate)?; let groups_results = benchmark_analyzer::Benchmark::compare(&reference, &candidate); - match arguments.output_path { + match arguments.output_file { Some(output_path) => { let mut file = std::fs::File::create(output_path)?; for (group_name, mut results) in groups_results.into_iter() { diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 69b3ca82..9fec6205 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -17,7 +17,7 @@ path = "src/compiler_tester/main.rs" doctest = false [dependencies] -structopt = { version = "=0.3.26", default-features = false } +clap = { version = "=4.5.21", features = ["derive"] } anyhow = "=1.0.89" which = "=6.0.3" colored = "=2.1.0" diff --git a/compiler_tester/src/compiler_tester/arguments.rs b/compiler_tester/src/compiler_tester/arguments.rs index 13359500..efe394e0 100644 --- a/compiler_tester/src/compiler_tester/arguments.rs +++ b/compiler_tester/src/compiler_tester/arguments.rs @@ -4,124 +4,112 @@ use std::path::PathBuf; -use structopt::StructOpt; +use clap::Parser; /// /// The compiler tester arguments. /// -#[derive(Debug, StructOpt)] -#[structopt( - name = "compiler-tester", - about = "EraVM Compiler Integration Testing Framework" -)] +#[derive(Debug, Parser)] +#[command(about, long_about = None)] pub struct Arguments { /// The logging level. - #[structopt(short = "v", long = "verbose")] - pub verbosity: bool, + #[arg(short, long)] + pub verbose: bool, /// Suppresses the output completely. - #[structopt(short = "q", long = "quiet")] + #[arg(short, long)] pub quiet: bool, /// Saves all IRs produced by compilers to `./debug/` directory. - #[structopt(short = "D", long = "debug")] + #[arg(short = 'D', long)] pub debug: bool, /// Runs tests only in modes that contain any string from the specified ones. - #[structopt(short = "m", long = "mode")] - pub modes: Vec, + #[arg(short, long)] + pub mode: Vec, /// Runs only tests whose name contains any string from the specified ones. - #[structopt(short = "p", long = "path")] - pub paths: Vec, + #[arg(short, long)] + pub path: Vec, /// Runs only tests from the specified groups. - #[structopt(short = "g", long = "group")] - pub groups: Vec, + #[structopt(short, long)] + pub group: Vec, /// The benchmark output path, if requested. - #[structopt(short = "b", long = "benchmark")] + #[structopt(short, long)] pub benchmark: Option, /// Sets the number of threads, which execute the tests concurrently. - #[structopt(short = "t", long = "threads")] + #[structopt(short, long)] pub threads: Option, /// Whether to dump the debug data for system contracts. - #[structopt(long = "dump-system")] + #[structopt(long)] pub dump_system: bool, /// Whether the deployer should be disabled. - #[structopt(long = "disable-deployer")] + #[structopt(long)] pub disable_deployer: bool, /// Whether the msg.value simulator should be disabled. - #[structopt(long = "disable-value-simulator")] + #[structopt(long)] pub disable_value_simulator: bool, /// Path to the `zksolc` executable. /// Is set to `zksolc` by default. - #[structopt(long = "zksolc")] + #[structopt(long)] pub zksolc: Option, /// Path to the `zkvyper` executable. /// Is set to `zkvyper` by default. - #[structopt(long = "zkvyper")] + #[structopt(long)] pub zkvyper: Option, /// Specify the compiler toolchain. /// Available arguments: `ir-llvm`, `solc`, `solc-llvm`. /// The default for `EraVM` target is `ir-llvm`. /// The default for `EVM` target is `solc`. - #[structopt(long = "toolchain")] + #[structopt(long)] pub toolchain: Option, /// Specify the target architecture. /// Available arguments: `eravm`, `evm`. - #[structopt(long = "target")] + #[structopt(long)] pub target: era_compiler_common::Target, /// Specify the environment to run tests on. /// Available arguments: `zk_evm`, `FastVM`, `EVMInterpreter`, `REVM`. /// The default for `EraVM` target is `zk_evm`. /// The default for `EVM` target is `EVMInterpreter`. - #[structopt(long = "environment")] + #[structopt(long)] pub environment: Option, /// Choose between `build` to compile tests only without running, and `run` to compile and run. - #[structopt(long = "workflow", default_value = "run")] + #[structopt(long, default_value_t = compiler_tester::Workflow::BuildAndRun)] pub workflow: compiler_tester::Workflow, /// Path to the default `solc` executables download configuration file. - #[structopt(long = "solc-bin-config-path")] + #[structopt(long)] pub solc_bin_config_path: Option, /// Path to the default `vyper` executables download configuration file. - #[structopt(long = "vyper-bin-config-path")] + #[structopt(long)] pub vyper_bin_config_path: Option, /// Whether to load the system contracts builds from the specified file. - #[structopt(long = "load-system-contracts")] - pub system_contracts_load_path: Option, + #[structopt(long)] + pub load_system_contracts: Option, /// Whether to save the system contracts builds to the specified file. - #[structopt(long = "save-system-contracts")] - pub system_contracts_save_path: Option, + #[structopt(long)] + pub save_system_contracts: Option, /// Sets the `verify each` option in LLVM. - #[structopt(long = "llvm-verify-each")] + #[structopt(long)] pub llvm_verify_each: bool, /// Sets the `debug logging` option in LLVM. - #[structopt(long = "llvm-debug-logging")] + #[structopt(long)] pub llvm_debug_logging: bool, } - -impl Arguments { - /// - /// A shortcut constructor. - /// - pub fn new() -> Self { - Self::from_args() - } -} diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index ab0433be..6ddd067a 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::Instant; +use clap::Parser; use colored::Colorize; use self::arguments::Arguments; @@ -19,7 +20,10 @@ const RAYON_WORKER_STACK_SIZE: usize = 16 * 1024 * 1024; /// The application entry point. /// fn main() { - let exit_code = match main_inner(Arguments::new()) { + let exit_code = match Arguments::try_parse() + .map_err(|error| anyhow::anyhow!(error)) + .and_then(main_inner) + { Ok(()) => era_compiler_common::EXIT_CODE_SUCCESS, Err(error) => { eprintln!("{error:?}"); @@ -89,9 +93,9 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { .build_global() .expect("Thread pool configuration failure"); - let summary = compiler_tester::Summary::new(arguments.verbosity, arguments.quiet).wrap(); + let summary = compiler_tester::Summary::new(arguments.verbose, arguments.quiet).wrap(); - let filters = compiler_tester::Filters::new(arguments.paths, arguments.modes, arguments.groups); + let filters = compiler_tester::Filters::new(arguments.path, arguments.mode, arguments.group); let compiler_tester = compiler_tester::CompilerTester::new( summary.clone(), @@ -159,8 +163,8 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { executable_download_config_paths, PathBuf::from("./configs/solc-bin-system-contracts.json"), system_contracts_debug_config, - arguments.system_contracts_load_path, - arguments.system_contracts_save_path, + arguments.load_system_contracts, + arguments.save_system_contracts, arguments.target, )?; @@ -191,8 +195,8 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { executable_download_config_paths, PathBuf::from("./configs/solc-bin-system-contracts.json"), system_contract_debug_config, - arguments.system_contracts_load_path, - arguments.system_contracts_save_path, + arguments.load_system_contracts, + arguments.save_system_contracts, arguments.target, )?; @@ -239,12 +243,12 @@ mod tests { std::env::set_current_dir("..").expect("Change directory failed"); let arguments = Arguments { - verbosity: false, + verbose: false, quiet: false, debug: false, - modes: vec!["Y+M3B3 0.8.28".to_owned()], - paths: vec!["tests/solidity/simple/default.sol".to_owned()], - groups: vec![], + mode: vec!["Y+M3B3 0.8.28".to_owned()], + path: vec!["tests/solidity/simple/default.sol".to_owned()], + group: vec![], benchmark: None, threads: Some(1), dump_system: false, @@ -260,8 +264,8 @@ mod tests { workflow: compiler_tester::Workflow::BuildAndRun, solc_bin_config_path: Some(PathBuf::from("./configs/solc-bin-default.json")), vyper_bin_config_path: Some(PathBuf::from("./configs/vyper-bin-default.json")), - system_contracts_load_path: Some(PathBuf::from("system-contracts-stable-build")), - system_contracts_save_path: None, + load_system_contracts: Some(PathBuf::from("system-contracts-stable-build")), + save_system_contracts: None, llvm_verify_each: false, llvm_debug_logging: false, }; diff --git a/compiler_tester/src/workflow.rs b/compiler_tester/src/workflow.rs index 0ef54114..ff37156c 100644 --- a/compiler_tester/src/workflow.rs +++ b/compiler_tester/src/workflow.rs @@ -7,7 +7,7 @@ use std::str::FromStr; /// /// Describes sets of actions that compiler tester is able to perform. /// -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Workflow { /// Only build tests but not execute them. BuildOnly, diff --git a/coverage_watcher/Cargo.toml b/coverage_watcher/Cargo.toml index 930e120b..39a7b33b 100644 --- a/coverage_watcher/Cargo.toml +++ b/coverage_watcher/Cargo.toml @@ -14,7 +14,7 @@ name = "coverage-watcher" path = "src/coverage_watcher/main.rs" [dependencies] -structopt = { version = "=0.3.26", default-features = false } +clap = { version = "=4.5.21", features = ["derive"] } anyhow = "=1.0.89" serde = { version = "=1.0.210", features = [ "derive" ] } diff --git a/coverage_watcher/src/coverage_watcher/arguments.rs b/coverage_watcher/src/coverage_watcher/arguments.rs index 6e55123d..d7889d32 100644 --- a/coverage_watcher/src/coverage_watcher/arguments.rs +++ b/coverage_watcher/src/coverage_watcher/arguments.rs @@ -4,27 +4,15 @@ use std::path::PathBuf; -use structopt::StructOpt; +use clap::Parser; /// /// The coverage watcher arguments. /// -#[derive(Debug, StructOpt)] -#[structopt( - name = "coverage-watcher", - about = "ZKsync toolchain test coverage watcher" -)] +#[derive(Debug, Parser)] +#[command(about, long_about = None)] pub struct Arguments { /// The missed tests output file path. - #[structopt(short = "o", long = "output")] + #[arg(short, long)] pub output: Option, } - -impl Arguments { - /// - /// A shortcut constructor. - /// - pub fn new() -> Self { - Self::from_args() - } -} diff --git a/coverage_watcher/src/coverage_watcher/main.rs b/coverage_watcher/src/coverage_watcher/main.rs index 8eeab2ec..61e9cb8a 100644 --- a/coverage_watcher/src/coverage_watcher/main.rs +++ b/coverage_watcher/src/coverage_watcher/main.rs @@ -9,6 +9,8 @@ use std::io::Read; use std::io::Write; use std::path::PathBuf; +use clap::Parser; + use self::arguments::Arguments; use coverage_watcher::TestsDirectory; @@ -18,7 +20,7 @@ use coverage_watcher::TestsSet; /// The application entry point. /// fn main() -> anyhow::Result<()> { - let arguments = Arguments::new(); + let arguments = Arguments::try_parse()?; let mut data = String::new(); File::open("coverage.yaml") diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2cb44b1c..1a07338a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] profile = "default" -channel = "1.80.1" +channel = "1.82.0" diff --git a/solidity_adapter/Cargo.toml b/solidity_adapter/Cargo.toml index fb47b94c..6d35c09d 100644 --- a/solidity_adapter/Cargo.toml +++ b/solidity_adapter/Cargo.toml @@ -17,7 +17,7 @@ path = "src/tests_updater/main.rs" doctest = false [dependencies] -structopt = { version = "=0.3.26", default-features = false } +clap = { version = "=4.5.21", features = ["derive"] } anyhow = "=1.0.89" colored = "=2.1.0" diff --git a/solidity_adapter/src/tests_updater/arguments.rs b/solidity_adapter/src/tests_updater/arguments.rs index 380ecd12..cafa90d9 100644 --- a/solidity_adapter/src/tests_updater/arguments.rs +++ b/solidity_adapter/src/tests_updater/arguments.rs @@ -4,43 +4,27 @@ use std::path::PathBuf; -use structopt::StructOpt; +use clap::Parser; /// /// The tests updater's arguments. /// -#[derive(Debug, StructOpt)] -#[structopt( - name = "tests-updater", - about = "ZKsync toolchain test updater for Ethereum Solidity tests" -)] +#[derive(Debug, Parser)] +#[command(about, long_about = None)] pub struct Arguments { /// Source directory of changed tests. - #[structopt( - default_value = "solidity/test/libsolidity/semanticTests", - short = "s", - long = "source" - )] + #[arg(short, long, default_value = "solidity/test/libsolidity/semanticTests")] pub source: PathBuf, /// Path of the tests' index. - #[structopt(short = "i", long = "index")] + #[arg(short, long)] pub index: PathBuf, /// Destination directory for tests to be updated. - #[structopt(short = "d", long = "destination")] + #[arg(short, long)] pub destination: PathBuf, /// Whether to only update the index, and do not touch the files. - #[structopt(long = "index-only")] + #[arg(long)] pub index_only: bool, } - -impl Arguments { - /// - /// A shortcut constructor. - /// - pub fn new() -> Self { - Self::from_args() - } -} diff --git a/solidity_adapter/src/tests_updater/main.rs b/solidity_adapter/src/tests_updater/main.rs index e5e224d8..d4715668 100644 --- a/solidity_adapter/src/tests_updater/main.rs +++ b/solidity_adapter/src/tests_updater/main.rs @@ -8,6 +8,7 @@ use std::fs::OpenOptions; use std::io::BufReader; use std::io::Write; +use clap::Parser; use colored::Colorize; use self::arguments::Arguments; @@ -16,7 +17,7 @@ use self::arguments::Arguments; /// Run updating /// fn main() { - let arguments = Arguments::new(); + let arguments = Arguments::parse(); let file = OpenOptions::new() .read(true) From 9170ee46a0b8e34715e42f7b8900062a95fd6ca7 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Thu, 21 Nov 2024 13:07:45 +0800 Subject: [PATCH 21/31] fix(eravm): resets return data after calling CREATE --- .gitmodules | 18 +++++++++--------- Cargo.lock | 10 +++++----- tests | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.gitmodules b/.gitmodules index 64ae8f6b..72622394 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,12 @@ [submodule "tests"] - path = tests - url = https://github.com/matter-labs/era-compiler-tests - branch = main + path = tests + url = https://github.com/matter-labs/era-compiler-tests + branch = main [submodule "solidity"] - path = solidity - url = https://github.com/ethereum/solidity - branch = develop + path = solidity + url = https://github.com/ethereum/solidity + branch = develop [submodule "era-contracts"] - path = era-contracts - url = https://github.com/matter-labs/era-contracts - branch = stable/evm-emulator + path = era-contracts + url = https://github.com/matter-labs/era-contracts + branch = stable/evm-emulator diff --git a/Cargo.lock b/Cargo.lock index 3fda2003..7c36222f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1057,7 +1057,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#47d3a2143a6b063af9eed5a801e9a4ae90bbc5da" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#5f88ca511f972dae2d67fb91598b9f9b434c1731" dependencies = [ "anyhow", "era-compiler-common", @@ -1072,7 +1072,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#712957b06725d8673d759c151a2413a927a6cb09" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c5f34a12f2b991bb0d08ffdadd3b7c56f4ed5548" dependencies = [ "anyhow", "clap", @@ -1099,7 +1099,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#870ed37ea2150f03272ded47271d06fd2349dd7a" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#59a41bb41fb76a0cc04679c772406258323ec780" dependencies = [ "anyhow", "clap", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "era-solc" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#712957b06725d8673d759c151a2413a927a6cb09" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c5f34a12f2b991bb0d08ffdadd3b7c56f4ed5548" dependencies = [ "anyhow", "boolinator", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#712957b06725d8673d759c151a2413a927a6cb09" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c5f34a12f2b991bb0d08ffdadd3b7c56f4ed5548" dependencies = [ "anyhow", "regex", diff --git a/tests b/tests index 22539413..9c809589 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit 2253941334797eb2a997941845fb9eb0d436558b +Subproject commit 9c80958998132ae8f1a6df1ba9f9db4f6c66c48f From 18b68ea7563407e3fcb15a4777429bfb7fceb889 Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Fri, 22 Nov 2024 18:04:27 +0000 Subject: [PATCH 22/31] ci: fix mandatory checks to be included in terraform (#124) --- .github/workflows/secrets-scanner.yaml | 19 +++++++++++++++++++ .github/workflows/tests.yaml | 6 ------ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/secrets-scanner.yaml diff --git a/.github/workflows/secrets-scanner.yaml b/.github/workflows/secrets-scanner.yaml new file mode 100644 index 00000000..205477f5 --- /dev/null +++ b/.github/workflows/secrets-scanner.yaml @@ -0,0 +1,19 @@ +name: Leaked Secrets Scan +on: + pull_request: + merge_group: +jobs: + TruffleHog: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 + - name: TruffleHog OSS + uses: trufflesecurity/trufflehog@7e78ca385fb82c19568c7a4b341c97d57d9aa5e1 # v3.82.2 + with: + path: ./ + base: ${{ github.event.repository.default_branch }} + head: HEAD + extra_args: --debug --only-verified diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4095cd04..310bd15d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -36,11 +36,6 @@ concurrency: jobs: - # Check for secrets leak in the repository - secrets-scanner: - uses: matter-labs/era-compiler-ci/.github/workflows/secrets-scanner.yaml@v1 - secrets: inherit - # Check for cargo issues cargo-check: runs-on: matterlabs-ci-runner-high-performance @@ -133,7 +128,6 @@ jobs: runs-on: ubuntu-latest if: always() needs: - - secrets-scanner - cargo-check - integration-tests - benchmarks From 2b7bdb1da4b98a9df1cf583275d93c20ecd20ce8 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Mon, 2 Dec 2024 22:42:27 +0800 Subject: [PATCH 23/31] feat: exclusive dependency resolution by the linker (#125) --- Cargo.lock | 207 +++++++++--------- compiler_tester/src/compilers/eravm/mod.rs | 6 +- compiler_tester/src/compilers/llvm/mod.rs | 8 +- compiler_tester/src/compilers/solidity/mod.rs | 12 +- .../src/compilers/solidity/upstream/mod.rs | 4 +- compiler_tester/src/compilers/vyper/mod.rs | 7 +- compiler_tester/src/compilers/yul/mod.rs | 10 +- compiler_tester/src/vm/eravm/mod.rs | 7 +- system-contracts-stable-build | Bin 946341 -> 946341 bytes 9 files changed, 133 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c36222f..6ece9db6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-eip2930" @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fce5dbd6a4f118eecc4719eaa9c7ffc31c315e6c5ccde3642db927802312425" +checksum = "9db948902dfbae96a73c2fbf1f7abec62af034ab883e4c777c3fd29702bd6e2c" dependencies = [ "alloy-rlp", "bytes", @@ -127,7 +127,7 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -345,7 +345,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -555,9 +555,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "c-kzg" @@ -576,9 +576,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ "shlex", ] @@ -631,7 +631,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -700,9 +700,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487981fa1af147182687064d0a2c336586d337a606595ced9ffb0c685c250c73" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" dependencies = [ "cfg-if", "cpufeatures", @@ -885,7 +885,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -905,7 +905,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "unicode-xid", ] @@ -938,7 +938,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1005,7 +1005,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1016,7 +1016,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1028,7 +1028,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era-compiler-common" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#32637b83bf23b7b177c7d711992a6f6f101ac399" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ada30723f8315764ff965b7703159187afd668bf" dependencies = [ "anyhow", "base58", @@ -1044,7 +1044,7 @@ dependencies = [ [[package]] name = "era-compiler-downloader" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#32637b83bf23b7b177c7d711992a6f6f101ac399" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ada30723f8315764ff965b7703159187afd668bf" dependencies = [ "anyhow", "colored", @@ -1057,7 +1057,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#5f88ca511f972dae2d67fb91598b9f9b434c1731" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#050e09791561286b1faf116eb5ee4cd6a010dd9a" dependencies = [ "anyhow", "era-compiler-common", @@ -1072,7 +1072,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c5f34a12f2b991bb0d08ffdadd3b7c56f4ed5548" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c0cb2c6764f8ecbf209f75e518a4261375fb5eed" dependencies = [ "anyhow", "clap", @@ -1099,7 +1099,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#59a41bb41fb76a0cc04679c772406258323ec780" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#1bf4baeafcacf72f9e11adae106c6c6b6339df51" dependencies = [ "anyhow", "clap", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "era-solc" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c5f34a12f2b991bb0d08ffdadd3b7c56f4ed5548" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c0cb2c6764f8ecbf209f75e518a4261375fb5eed" dependencies = [ "anyhow", "boolinator", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c5f34a12f2b991bb0d08ffdadd3b7c56f4ed5548" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c0cb2c6764f8ecbf209f75e518a4261375fb5eed" dependencies = [ "anyhow", "regex", @@ -1149,12 +1149,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1374,7 +1374,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1489,9 +1489,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "headers" @@ -1759,7 +1759,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1828,23 +1828,23 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", ] [[package]] name = "inkwell" version = "0.4.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#2b02c986508b68a38f9eb0f46919d5d1a1b0bfc1" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c5d783f52b755f5382e99e3f1179039b9435e3cc" dependencies = [ "either", "inkwell_internals", @@ -1859,11 +1859,11 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.9.0" -source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#2b02c986508b68a38f9eb0f46919d5d1a1b0bfc1" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c5d783f52b755f5382e99e3f1179039b9435e3cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1921,16 +1921,17 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1993,9 +1994,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.164" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libm" @@ -2032,14 +2033,14 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "llvm-sys" version = "170.0.1" -source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#b06c3d1989fa5d77e491e6aea709dd5c76621aff" +source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-17.0#b65826dfdb19676637dbddb9f01bcc52ae2c4308" dependencies = [ "anyhow", "cc", @@ -2103,11 +2104,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", "wasi", "windows-sys 0.52.0", @@ -2267,7 +2267,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2314,7 +2314,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2380,7 +2380,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2461,7 +2461,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2911,9 +2911,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc-hex" @@ -3035,7 +3035,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3190,7 +3190,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3354,9 +3354,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3457,9 +3457,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.89" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -3480,7 +3480,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3540,7 +3540,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3670,9 +3670,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3681,20 +3681,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -3788,9 +3788,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna 1.0.3", @@ -3865,9 +3865,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" dependencies = [ "cfg-if", "once_cell", @@ -3876,36 +3876,37 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3913,28 +3914,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" dependencies = [ "js-sys", "wasm-bindgen", @@ -4194,9 +4195,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -4206,13 +4207,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -4234,27 +4235,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -4275,7 +4276,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4297,7 +4298,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] diff --git a/compiler_tester/src/compilers/eravm/mod.rs b/compiler_tester/src/compilers/eravm/mod.rs index 60dc9513..57eb6897 100644 --- a/compiler_tester/src/compilers/eravm/mod.rs +++ b/compiler_tester/src/compilers/eravm/mod.rs @@ -49,17 +49,17 @@ impl Compiler for EraVMCompiler { let build = project.compile_to_eravm( &mut vec![], true, - BTreeMap::new(), era_compiler_common::HashType::Ipfs, era_compiler_llvm_context::OptimizerSettings::none(), llvm_options, true, - None, debug_config.clone(), )?; build.collect_errors()?; + let build = build.link(BTreeMap::new()); + build.collect_errors()?; let builds = build - .contracts + .results .into_iter() .map(|(path, result)| Ok((path, result.expect("Always valid").build))) .collect::>>()?; diff --git a/compiler_tester/src/compilers/llvm/mod.rs b/compiler_tester/src/compilers/llvm/mod.rs index c0edfef8..d55d3c44 100644 --- a/compiler_tester/src/compilers/llvm/mod.rs +++ b/compiler_tester/src/compilers/llvm/mod.rs @@ -66,17 +66,17 @@ impl Compiler for LLVMCompiler { let build = project.compile_to_eravm( &mut vec![], true, - linker_symbols, era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, true, - None, debug_config.clone(), )?; build.collect_errors()?; + let build = build.link(linker_symbols); + build.collect_errors()?; let builds = build - .contracts + .results .into_iter() .map(|(path, result)| Ok((path, result.expect("Always valid").build))) .collect::>>()?; @@ -121,7 +121,7 @@ impl Compiler for LLVMCompiler { )?; build.collect_errors()?; let builds: HashMap = build - .contracts + .results .into_iter() .map(|(path, build)| { let build = build.expect("Always valid"); diff --git a/compiler_tester/src/compilers/solidity/mod.rs b/compiler_tester/src/compilers/solidity/mod.rs index 07dbdc9a..d6a98f06 100644 --- a/compiler_tester/src/compilers/solidity/mod.rs +++ b/compiler_tester/src/compilers/solidity/mod.rs @@ -361,23 +361,23 @@ impl Compiler for SolidityCompiler { let build = project.compile_to_eravm( &mut vec![], mode.enable_eravm_extensions, - linker_symbols, era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, true, - None, debug_config, )?; build.collect_errors()?; + let build = build.link(linker_symbols); + build.collect_errors()?; let builds = build - .contracts + .results .iter() .map(|(path, build)| { let build = build.to_owned().expect("Always valid"); - let build = era_compiler_llvm_context::EraVMBuild::new( + let build = era_compiler_llvm_context::EraVMBuild::new_with_bytecode_hash( build.build.bytecode, - build.build.bytecode_hash, + build.build.bytecode_hash.expect("Always valid"), None, build.build.assembly, ); @@ -442,7 +442,7 @@ impl Compiler for SolidityCompiler { )?; build.collect_errors()?; let builds: HashMap = build - .contracts + .results .into_iter() .map(|(path, result)| { let contract = result.expect("Always valid"); diff --git a/compiler_tester/src/compilers/solidity/upstream/mod.rs b/compiler_tester/src/compilers/solidity/upstream/mod.rs index 283356fc..cf1ae610 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mod.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mod.rs @@ -498,9 +498,9 @@ impl Compiler for SolidityCompiler { .map_err(|_| { anyhow::anyhow!("EraVM bytecode of the contract `{path}` hashing error") })?; - let build = era_compiler_llvm_context::EraVMBuild::new( + let build = era_compiler_llvm_context::EraVMBuild::new_with_bytecode_hash( bytecode, - Some(bytecode_hash), + bytecode_hash, None, None, ); diff --git a/compiler_tester/src/compilers/vyper/mod.rs b/compiler_tester/src/compilers/vyper/mod.rs index 5d892239..f1ae1747 100644 --- a/compiler_tester/src/compilers/vyper/mod.rs +++ b/compiler_tester/src/compilers/vyper/mod.rs @@ -225,7 +225,7 @@ impl Compiler for VyperCompiler { let method_identifiers = Self::get_method_identifiers(&project) .map_err(|error| anyhow::anyhow!("Failed to get method identifiers: {error}"))?; - let build = project.compile( + let mut build = project.compile( None, era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), @@ -233,13 +233,14 @@ impl Compiler for VyperCompiler { vec![], debug_config, )?; + build.link(BTreeMap::new())?; let builds = build .contracts .into_iter() .map(|(path, contract)| { - let build = era_compiler_llvm_context::EraVMBuild::new( + let build = era_compiler_llvm_context::EraVMBuild::new_with_bytecode_hash( contract.build.bytecode, - contract.build.bytecode_hash, + contract.build.bytecode_hash.expect("Always exists"), None, contract.build.assembly, ); diff --git a/compiler_tester/src/compilers/yul/mod.rs b/compiler_tester/src/compilers/yul/mod.rs index 687c7d3e..f8b45d66 100644 --- a/compiler_tester/src/compilers/yul/mod.rs +++ b/compiler_tester/src/compilers/yul/mod.rs @@ -96,23 +96,23 @@ impl Compiler for YulCompiler { let build = project.compile_to_eravm( &mut vec![], mode.enable_eravm_extensions, - linker_symbols, era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, true, - None, debug_config.clone(), )?; build.collect_errors()?; + let build = build.link(linker_symbols); + build.collect_errors()?; let builds = build - .contracts + .results .into_iter() .map(|(path, result)| { let contract = result.expect("Always valid"); - let build = era_compiler_llvm_context::EraVMBuild::new( + let build = era_compiler_llvm_context::EraVMBuild::new_with_bytecode_hash( contract.build.bytecode, - contract.build.bytecode_hash, + contract.build.bytecode_hash.expect("Always exists"), None, contract.build.assembly, ); diff --git a/compiler_tester/src/vm/eravm/mod.rs b/compiler_tester/src/vm/eravm/mod.rs index 1ebc6c56..fef2e60c 100644 --- a/compiler_tester/src/vm/eravm/mod.rs +++ b/compiler_tester/src/vm/eravm/mod.rs @@ -169,9 +169,12 @@ impl EraVM { ), ); vm.add_known_contract( - era_compiler_vyper::MINIMAL_PROXY_CONTRACT_BYTECODE.to_owned(), + era_compiler_vyper::MINIMAL_PROXY_BUILD.bytecode.clone(), web3::types::U256::from_big_endian( - era_compiler_vyper::MINIMAL_PROXY_CONTRACT_HASH.as_slice(), + era_compiler_vyper::MINIMAL_PROXY_BUILD + .bytecode_hash + .expect("Always exists") + .as_slice(), ), ); diff --git a/system-contracts-stable-build b/system-contracts-stable-build index a8c8ada23d3312d19b19f4b0dc66ebae6f0047cf..d6f4aaa3529dd281193915016e440d5b7ae18b5f 100644 GIT binary patch delta 1555 zcmXxic|6k%9Ki9d*)TblmNQLAmK^oW)hhfV#PpC@hbyUQ*yGq19#8J(X-tV`-8-dF)bJ|Ei(rLXGi~;L3MQxstyT+ z_@Z4AhHCi~ZKyvtBtyw3e7JqJWCP8CkOh0i1GFeoiF}kLuK+EEqF~$(`6IWUQCM|+u>ok z1uzCkUrs(F8>&K=7^Mx(topa%LZr^P&Ba8QIP%eXA4L#`sqXqL#QNTQo=gNcT6^#v zZT+RpXpuxUZ6BAqd@tFHaVqA}; zD~HI4ME{m25+|TlD zY!=VO>M<)jl2b9>gS)X2pDaMX2HdTdp1UB1Oz-W0z&Qh1Vxi7JVJa)}_%?JK>TN4^ zS_z>KD9XCgVR`HOv%?Q}hnu|J^uYxeMypZyy!$$!k!}}*iUh4m55Wq@UfCB>Lz3mB zEr^}I3oNNz#k!IsFx_Lt=BJ&!mNYZG%L&$}LCBnxWS9mVsC%?-nJtw=cVdohT`!>JJ9*-7r$sHV4fGU=m7uw{ zB1yxeWp>ZNgT_Pww>&DN(mHDxb*QM$LORmu38{+n?Ji78$+0!Pts;`FFlx)5T3o5a z$*3dTS9ZYfW~_!>@S;-m!$guxS}45iyBgoF`OQIPl+jwyv~t+*ajr1q>>TFfUylz0 z2P*yd`D&a1HY|$^sK?*@B2WC!%G|Qn z()=co$Yn_}3x$(=E-AM{Sssr)eb0IS`#PV`IiD|c$c#B;_Sg~%kX@qWJGlmuh6-6V z?+7zVvN*@Wdy@9o`$eXn9pQy(5Iraq0+G^5s!ruz!!Ybhw+l(*3)V#n=QHp^6!Mr# zqEBTkG;uRv^UANt%d@M75ne>PlZ%^JDD@F^Ju4Tq6v7p4-?8_Ip@{y&^`2>i9y*_N z9SGLru2IAA8AQ6xr@{ILQDh}S(F6}5=cHb)dGhuSHDvfiuh?HbzEl4wihUszLsPTZ zrxj-;hB^ZV-OF|ta4E%n^M%L?Me&`U!CQIqy!cH6wofKnVQ@0Tyx|0d zB^)aN=6UejcRxGR2gLTh-7>+4jIYI64lBG({qD1*-E?Q9RSX%+BHT1~pN0RHo*FWQDky1&Iq`Bb6@sT+UpTCZm-NWq=EQrs z7`0pt0f1!V`$%xdW_sY6T%?7XFA4JJLAy1w3g_TTSsw)wXmB789_9*68Ge+r&engH z#x3FZC3^hOD?X-S_~^FoDN%!!c2$byh7V=7;O1+afjwAmG|tl7ZON<{v~I}MvDKI; zHXR4=%)#ArfUA7@A)m!fj|8WkRfoQe(UtCJ+>8j?A@rU<-$r`8x?4QyLLX?$n%3xr zJ3K#uMWS@_JE#ft4>3FjC7-?>G@v6?j{^GD14%BwJtz^;73@i0hvX;z)TW5f?b^Ir z?_C)4{q|yMv+iDVK1XdrRGdr3Z%$oQd{f}s`=h%;k)K632I=n+YAC?OUAX@jk29TA zER3Ppd+H@HS|*F^#%iCZn$-(Z%38#d(qH#(%|KPVy@wstSL*k~rge1SC)`{>rvI^M zJ~-KrNRnHZkXKa%_iKddv8`TDr{8165+<8bK;TaUf^uqt zSB)aEpo*a<*!!9QpJI)2-kX(jr7*YJSK>^5M<8 z%&KLvM4MbooyWB`{FI#;aPMWs>1)sbY$`kA$QqG5jRTWKh+%smk)jVz%*Am(IeSFYbj`oFJnNe>$+m= zVfa-%llH4Rs#i?ec!iGMQFDW@hl4$|B3kPfC|Iu&Xbkux2Pus?k%6tXt6+>WuDuSs zoNR!1jPQI+oU+Y*yIP_@yy Date: Wed, 4 Dec 2024 06:36:25 +0100 Subject: [PATCH 24/31] feat: support CSV output (#119) Co-authored-by: Oleksandr Zarudnyi --- README.md | 25 ++- .../src/benchmark/format/csv.rs | 71 +++++++ .../src/benchmark/format/json.rs | 18 ++ .../src/benchmark/format/mod.rs | 15 ++ .../src/benchmark/group/element/input.rs | 54 +++++ .../group/{element.rs => element/mod.rs} | 16 +- .../src/benchmark/group/element/selector.rs | 39 ++++ benchmark_analyzer/src/benchmark/metadata.rs | 29 +++ benchmark_analyzer/src/benchmark/mod.rs | 15 +- benchmark_analyzer/src/lib.rs | 6 +- .../arguments/benchmark_format.rs | 36 ++++ .../{arguments.rs => arguments/mod.rs} | 7 + compiler_tester/src/compiler_tester/main.rs | 13 +- compiler_tester/src/compilers/eravm/mode.rs | 2 +- compiler_tester/src/compilers/llvm/mode.rs | 2 +- compiler_tester/src/compilers/mode/mod.rs | 2 +- .../src/compilers/solidity/mode.rs | 20 +- .../src/compilers/solidity/upstream/mode.rs | 24 ++- compiler_tester/src/compilers/vyper/mode.rs | 21 +- compiler_tester/src/compilers/yul/mode.rs | 2 +- .../src/compilers/yul/mode_upstream.rs | 24 ++- .../src/directories/ethereum/test.rs | 73 ++++--- .../src/directories/matter_labs/test/mod.rs | 87 +++++--- .../src/summary/benchmark_adapters/input.rs | 29 +++ .../summary/benchmark_adapters/metadata.rs | 54 +++++ .../src/summary/benchmark_adapters/mod.rs | 8 + .../summary/benchmark_adapters/selector.rs | 13 ++ compiler_tester/src/summary/element/mod.rs | 18 +- compiler_tester/src/summary/mod.rs | 59 +++--- .../src/test/case/input/balance.rs | 58 ++---- .../src/test/case/input/deploy_eravm.rs | 28 +-- .../src/test/case/input/deploy_evm.rs | 147 +++++--------- .../src/test/case/input/identifier.rs | 37 ++++ compiler_tester/src/test/case/input/mod.rs | 104 +++------- .../src/test/case/input/runtime.rs | 190 +++++++----------- .../src/test/case/input/storage_empty.rs | 82 ++------ compiler_tester/src/test/case/mod.rs | 103 +++------- compiler_tester/src/test/context/case.rs | 15 ++ compiler_tester/src/test/context/input.rs | 18 ++ compiler_tester/src/test/context/mod.rs | 2 + compiler_tester/src/test/description.rs | 68 +++++++ compiler_tester/src/test/mod.rs | 58 +++--- compiler_tester/src/test/selector.rs | 36 ++++ 43 files changed, 1071 insertions(+), 657 deletions(-) create mode 100644 benchmark_analyzer/src/benchmark/format/csv.rs create mode 100644 benchmark_analyzer/src/benchmark/format/json.rs create mode 100644 benchmark_analyzer/src/benchmark/format/mod.rs create mode 100644 benchmark_analyzer/src/benchmark/group/element/input.rs rename benchmark_analyzer/src/benchmark/group/{element.rs => element/mod.rs} (66%) create mode 100644 benchmark_analyzer/src/benchmark/group/element/selector.rs create mode 100644 benchmark_analyzer/src/benchmark/metadata.rs create mode 100644 compiler_tester/src/compiler_tester/arguments/benchmark_format.rs rename compiler_tester/src/compiler_tester/{arguments.rs => arguments/mod.rs} (93%) create mode 100644 compiler_tester/src/summary/benchmark_adapters/input.rs create mode 100644 compiler_tester/src/summary/benchmark_adapters/metadata.rs create mode 100644 compiler_tester/src/summary/benchmark_adapters/mod.rs create mode 100644 compiler_tester/src/summary/benchmark_adapters/selector.rs create mode 100644 compiler_tester/src/test/case/input/identifier.rs create mode 100644 compiler_tester/src/test/context/case.rs create mode 100644 compiler_tester/src/test/context/input.rs create mode 100644 compiler_tester/src/test/context/mod.rs create mode 100644 compiler_tester/src/test/description.rs create mode 100644 compiler_tester/src/test/selector.rs diff --git a/README.md b/README.md index e9834f0f..0993fad5 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ By default, the Tester SHOULD run the entire Collection in all possible combinat but it MAY omit some subset of the combinations for the sake of saving time, e.g. when only front-end changes have been made, and there is no point in running tests in all LLVM optimization modes. + + ## Building
@@ -123,6 +125,8 @@ made, and there is no point in running tests in all LLVM optimization modes. When the build succeeds, you can run the tests using [the examples below](#usage). + + ## GitHub Actions The `era-compiler-tester` is integrated into the GitHub Actions workflows of the following projects: @@ -138,6 +142,8 @@ If these tags exist, the tester from these tags will be used by the workflows in When testing is done, these tags should be removed. + + ## What is supported ### Languages @@ -181,6 +187,8 @@ Currently only relevant for the Solidity compiler, where you can choose the IR: Most of the specifiers support wildcards `*` (any), `^` ('3' and 'z'). With no mode argument, iterates over all option combinations (approximately 800). + + ## Usage Each command assumes you are at the root of the `compiler-tester` repository. @@ -251,10 +259,7 @@ cargo run --release --bin compiler-tester -- \ --zkvyper '../era-compiler-vyper/target/release/zkvyper' ``` -## Tracing -If you run the tester with `-T` flag, JSON trace files will be written to the `./trace/` directory. -The trace files can be used with our [custom ZKsync EraVM assembly tracer](https://staging-scan-v2.zksync.dev/tools/debugger) for debugging and research purposes. ## Benchmarking @@ -291,10 +296,18 @@ cargo run --release --bin benchmark-analyzer -- --reference reference.json --can After you make any changes in LLVM, you only need to repeat steps 2-3 to update the working branch benchmark data. +### Report formats + +Use the parameter `--benchmark-format` to select the output format: `json` (default), or `csv`. + + + ## Troubleshooting - Unset any LLVM-related environment variables you may have set, especially `LLVM_SYS__PREFIX` (see e.g. [https://crates.io/crates/llvm-sys](https://crates.io/crates/llvm-sys) and [https://llvm.org/docs/GettingStarted.html#local-llvm-configuration](https://llvm.org/docs/GettingStarted.html#local-llvm-configuration)). To make sure: `set | grep LLVM`. + + ## License The Era Compiler Tester is distributed under the terms of either @@ -304,10 +317,14 @@ The Era Compiler Tester is distributed under the terms of either at your option. + + ## Resources [ZKsync Era compiler toolchain documentation](https://docs.zksync.io/zk-stack/components/compiler/toolchain) + + ## Official Links - [Website](https://zksync.io/) @@ -316,6 +333,8 @@ at your option. - [Twitter for Devs](https://twitter.com/ZKsyncDevs) - [Discord](https://join.zksync.dev/) + + ## Disclaimer ZKsync Era has been through extensive testing and audits, and although it is live, it is still in alpha state and diff --git a/benchmark_analyzer/src/benchmark/format/csv.rs b/benchmark_analyzer/src/benchmark/format/csv.rs new file mode 100644 index 00000000..a9e22762 --- /dev/null +++ b/benchmark_analyzer/src/benchmark/format/csv.rs @@ -0,0 +1,71 @@ +//! +//! Serializing benchmark data to CSV. +//! + +use std::fmt::Write; + +use super::Benchmark; +use super::IBenchmarkSerializer; +use crate::benchmark::group::element::selector::Selector; +use crate::benchmark::group::element::Element; +use crate::benchmark::metadata::Metadata; + +/// +/// Serialize the benchmark to CSV in the following format: +/// "group_name", "element_name", "size_str", "cycles", "ergs", "gas" +/// +#[derive(Default)] +pub struct Csv; + +impl IBenchmarkSerializer for Csv { + type Err = std::fmt::Error; + + fn serialize_to_string(&self, benchmark: &Benchmark) -> Result { + let mut result = String::with_capacity(estimate_csv_size(benchmark)); + result.push_str( + r#""group", "mode", "version", "path", "case", "input", "size", "cycles", "ergs", "gas""#, + ); + result.push('\n'); + for (group_name, group) in &benchmark.groups { + for Element { + metadata: + Metadata { + selector: Selector { path, case, input }, + mode, + version, + group: _, + }, + size, + cycles, + ergs, + gas, + } in group.elements.values() + { + let size_str = size.map(|s| s.to_string()).unwrap_or_default(); + let mode = mode.as_deref().unwrap_or_default(); + let input = input.clone().map(|s| s.to_string()).unwrap_or_default(); + let case = case.as_deref().unwrap_or_default(); + let version = version.as_deref().unwrap_or_default(); + writeln!( + &mut result, + r#""{group_name}", "{mode}", "{version}", "{path}", "{case}", "{input}", {size_str}, {cycles}, {ergs}, {gas}"#, + )?; + } + } + Ok(result) + } +} + +fn estimate_csv_line_length() -> usize { + let number_fields = 4; + let number_field_estimated_max_length = 15; + let group_name_estimated_max = 10; + let test_name_estimated_max = 300; + group_name_estimated_max + + test_name_estimated_max + + number_fields * number_field_estimated_max_length +} + +fn estimate_csv_size(benchmark: &Benchmark) -> usize { + (benchmark.groups.len() + 1) * estimate_csv_line_length() +} diff --git a/benchmark_analyzer/src/benchmark/format/json.rs b/benchmark_analyzer/src/benchmark/format/json.rs new file mode 100644 index 00000000..04e19a33 --- /dev/null +++ b/benchmark_analyzer/src/benchmark/format/json.rs @@ -0,0 +1,18 @@ +//! +//! Serializing benchmark data to JSON. +//! + +use super::Benchmark; +use super::IBenchmarkSerializer; + +/// Serialize the benchmark data to JSON using `serde` library. +#[derive(Default)] +pub struct Json; + +impl IBenchmarkSerializer for Json { + type Err = serde_json::error::Error; + + fn serialize_to_string(&self, benchmark: &Benchmark) -> Result { + serde_json::to_string(benchmark) + } +} diff --git a/benchmark_analyzer/src/benchmark/format/mod.rs b/benchmark_analyzer/src/benchmark/format/mod.rs new file mode 100644 index 00000000..f22ad54f --- /dev/null +++ b/benchmark_analyzer/src/benchmark/format/mod.rs @@ -0,0 +1,15 @@ +//! +//! Serialization of benchmark data in different output formats. +//! + +pub mod csv; +pub mod json; + +use crate::benchmark::Benchmark; + +/// Serialization format for benchmark data. +pub trait IBenchmarkSerializer { + type Err: std::error::Error; + /// Serialize benchmark data in the selected format. + fn serialize_to_string(&self, benchmark: &Benchmark) -> anyhow::Result; +} diff --git a/benchmark_analyzer/src/benchmark/group/element/input.rs b/benchmark_analyzer/src/benchmark/group/element/input.rs new file mode 100644 index 00000000..55e52616 --- /dev/null +++ b/benchmark_analyzer/src/benchmark/group/element/input.rs @@ -0,0 +1,54 @@ +//! +//! Identifier for the test input. Describes the input type and position but not the actual contents. +//! + +use serde::Deserialize; +use serde::Serialize; + +/// +/// Identifier for the test input. Describes the input type and position but not the actual contents. +/// +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub enum Input { + /// The contract deploy, regardless of target. + Deployer { + /// Contract identifier, usually file name and contract name separated by a colon. + contract_identifier: String, + }, + /// The contract call. + Runtime { + /// Index in the array of inputs. + input_index: usize, + /// Input name, provided in the test description. + name: String, + }, + /// The storage empty check. + StorageEmpty { + /// Index in the array of inputs. + input_index: usize, + }, + /// Check account balance. + Balance { + /// Index in the array of inputs. + input_index: usize, + }, +} + +impl std::fmt::Display for Input { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Input::Deployer { + contract_identifier, + } => f.write_fmt(format_args!("#deployer:{contract_identifier}")), + Input::Runtime { input_index, name } => { + f.write_fmt(format_args!("{name}:{input_index}")) + } + Input::StorageEmpty { input_index } => { + f.write_fmt(format_args!("#storage_empty_check:{input_index}")) + } + Input::Balance { input_index } => { + f.write_fmt(format_args!("#balance_check:{input_index}")) + } + } + } +} diff --git a/benchmark_analyzer/src/benchmark/group/element.rs b/benchmark_analyzer/src/benchmark/group/element/mod.rs similarity index 66% rename from benchmark_analyzer/src/benchmark/group/element.rs rename to benchmark_analyzer/src/benchmark/group/element/mod.rs index d4dabf96..b3fe8c1d 100644 --- a/benchmark_analyzer/src/benchmark/group/element.rs +++ b/benchmark_analyzer/src/benchmark/group/element/mod.rs @@ -2,14 +2,21 @@ //! The benchmark element. //! +pub mod input; +pub mod selector; + use serde::Deserialize; use serde::Serialize; +use crate::benchmark::metadata::Metadata; + /// /// The benchmark element. /// #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Element { + /// Associated metadata. + pub metadata: Metadata, /// The contract size, `Some` for contracts deploys. pub size: Option, /// The number of cycles. @@ -24,8 +31,15 @@ impl Element { /// /// A shortcut constructor. /// - pub fn new(size: Option, cycles: usize, ergs: u64, gas: u64) -> Self { + pub fn new( + metadata: Metadata, + size: Option, + cycles: usize, + ergs: u64, + gas: u64, + ) -> Self { Self { + metadata, size, cycles, ergs, diff --git a/benchmark_analyzer/src/benchmark/group/element/selector.rs b/benchmark_analyzer/src/benchmark/group/element/selector.rs new file mode 100644 index 00000000..b804d954 --- /dev/null +++ b/benchmark_analyzer/src/benchmark/group/element/selector.rs @@ -0,0 +1,39 @@ +//! +//! Test selector, unambiously locating a test suite, or a specific input. +//! + +use serde::Deserialize; +use serde::Serialize; + +use crate::benchmark::group::element::input::Input; + +/// +/// Test selector, unambiously locating a test suite, case, or input. +/// +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Selector { + /// Path to the file containing test. + pub path: String, + /// Name of the case, if any. `None` means nameless case. + pub case: Option, + /// Identifier of the specific input. + pub input: Option, +} + +impl std::fmt::Display for Selector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + path: filename, + case: case_name, + input, + } = self; + f.write_fmt(format_args!("{filename}"))?; + if let Some(case_name) = case_name { + f.write_fmt(format_args!("::{case_name}"))?; + } + if let Some(input) = input { + f.write_fmt(format_args!("[{input}]"))?; + } + Ok(()) + } +} diff --git a/benchmark_analyzer/src/benchmark/metadata.rs b/benchmark_analyzer/src/benchmark/metadata.rs new file mode 100644 index 00000000..bda33fa1 --- /dev/null +++ b/benchmark_analyzer/src/benchmark/metadata.rs @@ -0,0 +1,29 @@ +//! +//! Information associated with the benchmark element. +//! + +use serde::Deserialize; +use serde::Serialize; + +use crate::benchmark::group::element::selector::Selector; + +/// +/// Encoded compiler mode. In future, it can be expanded into a structured type +/// shared between crates `benchmark_analyzer` and `compiler_tester`. +/// +pub type Mode = String; + +/// +/// Information associated with the benchmark element. +/// +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Metadata { + /// Test selector. + pub selector: Selector, + /// Compiler mode. + pub mode: Option, + /// Compiler version, e.g. solc. + pub version: Option, + /// Test group + pub group: String, +} diff --git a/benchmark_analyzer/src/benchmark/mod.rs b/benchmark_analyzer/src/benchmark/mod.rs index ad724502..9839fc50 100644 --- a/benchmark_analyzer/src/benchmark/mod.rs +++ b/benchmark_analyzer/src/benchmark/mod.rs @@ -2,11 +2,14 @@ //! The benchmark representation. //! +pub mod format; pub mod group; +pub mod metadata; use std::collections::BTreeMap; use std::path::PathBuf; +use format::IBenchmarkSerializer; use serde::Deserialize; use serde::Serialize; @@ -202,12 +205,16 @@ impl Benchmark { } /// - /// Writes the benchmark to a file. + /// Writes the benchmark results to a file using a provided serializer. /// - pub fn write_to_file(self, path: PathBuf) -> anyhow::Result<()> { - let contents = serde_json::to_string(&self).expect("Always valid"); + pub fn write_to_file( + self, + path: PathBuf, + serializer: impl IBenchmarkSerializer, + ) -> anyhow::Result<()> { + let contents = serializer.serialize_to_string(&self).expect("Always valid"); std::fs::write(path.as_path(), contents) - .map_err(|error| anyhow::anyhow!("Benchmark file {:?} reading: {}", path, error))?; + .map_err(|error| anyhow::anyhow!("Benchmark file {path:?} reading: {error}"))?; Ok(()) } } diff --git a/benchmark_analyzer/src/lib.rs b/benchmark_analyzer/src/lib.rs index f01677a8..88547fa1 100644 --- a/benchmark_analyzer/src/lib.rs +++ b/benchmark_analyzer/src/lib.rs @@ -4,10 +4,14 @@ pub(crate) mod benchmark; +pub use self::benchmark::format::csv::Csv as CsvSerializer; +pub use self::benchmark::format::json::Json as JsonSerializer; +pub use self::benchmark::group::element::input::Input; +pub use self::benchmark::group::element::selector::Selector as TestSelector; pub use self::benchmark::group::element::Element as BenchmarkElement; pub use self::benchmark::group::Group as BenchmarkGroup; +pub use self::benchmark::metadata::Metadata; pub use self::benchmark::Benchmark; - /// /// The all elements group name. /// diff --git a/compiler_tester/src/compiler_tester/arguments/benchmark_format.rs b/compiler_tester/src/compiler_tester/arguments/benchmark_format.rs new file mode 100644 index 00000000..d5e74081 --- /dev/null +++ b/compiler_tester/src/compiler_tester/arguments/benchmark_format.rs @@ -0,0 +1,36 @@ +/// Output format for benchmark data. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub enum BenchmarkFormat { + #[default] + Json, + Csv, +} + +impl std::str::FromStr for BenchmarkFormat { + type Err = anyhow::Error; + + fn from_str(string: &str) -> Result { + match string.to_lowercase().as_str() { + "json" => Ok(Self::Json), + "csv" => Ok(Self::Csv), + string => anyhow::bail!( + "Unknown benchmark format `{string}`. Supported formats: {}", + vec![Self::Json, Self::Csv] + .into_iter() + .map(|element| element.to_string().to_lowercase()) + .collect::>() + .join(", ") + ), + } + } +} + +impl std::fmt::Display for BenchmarkFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let repr = match self { + BenchmarkFormat::Json => "json", + BenchmarkFormat::Csv => "csv", + }; + f.write_str(repr) + } +} diff --git a/compiler_tester/src/compiler_tester/arguments.rs b/compiler_tester/src/compiler_tester/arguments/mod.rs similarity index 93% rename from compiler_tester/src/compiler_tester/arguments.rs rename to compiler_tester/src/compiler_tester/arguments/mod.rs index efe394e0..c2335c15 100644 --- a/compiler_tester/src/compiler_tester/arguments.rs +++ b/compiler_tester/src/compiler_tester/arguments/mod.rs @@ -4,8 +4,11 @@ use std::path::PathBuf; +use benchmark_format::BenchmarkFormat; use clap::Parser; +pub mod benchmark_format; + /// /// The compiler tester arguments. /// @@ -40,6 +43,10 @@ pub struct Arguments { #[structopt(short, long)] pub benchmark: Option, + /// The benchmark output format. + #[structopt(long = "benchmark-format", default_value_t = BenchmarkFormat::Json)] + pub benchmark_format: BenchmarkFormat, + /// Sets the number of threads, which execute the tests concurrently. #[structopt(short, long)] pub threads: Option, diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index 6ddd067a..fa15a793 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::Instant; +use arguments::benchmark_format::BenchmarkFormat; use clap::Parser; use colored::Colorize; @@ -222,7 +223,14 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { if let Some(path) = arguments.benchmark { let benchmark = summary.benchmark(toolchain)?; - benchmark.write_to_file(path)?; + match arguments.benchmark_format { + BenchmarkFormat::Json => { + benchmark.write_to_file(path, benchmark_analyzer::JsonSerializer)? + } + BenchmarkFormat::Csv => { + benchmark.write_to_file(path, benchmark_analyzer::CsvSerializer)? + } + } } if !summary.is_successful() { @@ -236,7 +244,7 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { mod tests { use std::path::PathBuf; - use crate::arguments::Arguments; + use crate::arguments::{benchmark_format::BenchmarkFormat, Arguments}; #[test] fn test_manually() { @@ -250,6 +258,7 @@ mod tests { path: vec!["tests/solidity/simple/default.sol".to_owned()], group: vec![], benchmark: None, + benchmark_format: BenchmarkFormat::Json, threads: Some(1), dump_system: false, disable_deployer: false, diff --git a/compiler_tester/src/compilers/eravm/mode.rs b/compiler_tester/src/compilers/eravm/mode.rs index 050c1f41..158b5e85 100644 --- a/compiler_tester/src/compilers/eravm/mode.rs +++ b/compiler_tester/src/compilers/eravm/mode.rs @@ -5,7 +5,7 @@ /// /// The compiler tester EraVM mode. /// -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct Mode {} impl std::fmt::Display for Mode { diff --git a/compiler_tester/src/compilers/llvm/mode.rs b/compiler_tester/src/compilers/llvm/mode.rs index 0b7cea09..419038bc 100644 --- a/compiler_tester/src/compilers/llvm/mode.rs +++ b/compiler_tester/src/compilers/llvm/mode.rs @@ -9,7 +9,7 @@ use crate::compilers::mode::Mode as ModeWrapper; /// /// The compiler tester LLVM mode. /// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Mode { /// The optimizer settings. pub llvm_optimizer_settings: era_compiler_llvm_context::OptimizerSettings, diff --git a/compiler_tester/src/compilers/mode/mod.rs b/compiler_tester/src/compilers/mode/mod.rs index 2a1b945a..e2e8b95f 100644 --- a/compiler_tester/src/compilers/mode/mod.rs +++ b/compiler_tester/src/compilers/mode/mod.rs @@ -17,7 +17,7 @@ use crate::compilers::yul::mode_upstream::Mode as YulUpstreamMode; /// /// The compiler mode. /// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] #[allow(clippy::upper_case_acronyms)] pub enum Mode { /// The `Solidity` mode. diff --git a/compiler_tester/src/compilers/solidity/mode.rs b/compiler_tester/src/compilers/solidity/mode.rs index 3b4c7ecd..964fd3d9 100644 --- a/compiler_tester/src/compilers/solidity/mode.rs +++ b/compiler_tester/src/compilers/solidity/mode.rs @@ -11,7 +11,7 @@ use crate::compilers::mode::Mode as ModeWrapper; /// /// The compiler tester Solidity mode. /// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, @@ -126,13 +126,13 @@ impl Mode { } } } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}{}{} {}", + /// + /// Returns a string representation excluding the solc version. + /// + pub fn repr_without_version(&self) -> String { + format!( + "{}{}{}", match self.solc_codegen { era_solc::StandardJsonInputCodegen::Yul => "Y", era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => "I", @@ -140,7 +140,11 @@ impl std::fmt::Display for Mode { }, if self.solc_optimize { '+' } else { '-' }, self.llvm_optimizer_settings, - self.solc_version, ) } } +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.repr_without_version(), self.solc_version) + } +} diff --git a/compiler_tester/src/compilers/solidity/upstream/mode.rs b/compiler_tester/src/compilers/solidity/upstream/mode.rs index 41fc264a..46a84994 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mode.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mode.rs @@ -9,7 +9,7 @@ use crate::compilers::mode::Mode as ModeWrapper; /// /// The compiler tester Solidity mode. /// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, @@ -111,24 +111,28 @@ impl Mode { } } } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// + /// Returns a string representation excluding the solc version. + /// + pub fn repr_without_version(&self) -> String { if self.via_mlir { - return write!(f, "L {}", self.solc_version); + return "L".to_owned(); } - - write!( - f, - "{}{} {}", + format!( + "{}{}", match self.solc_codegen { era_solc::StandardJsonInputCodegen::Yul => "Y", era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => "I", era_solc::StandardJsonInputCodegen::EVMLA => "E", }, if self.solc_optimize { '+' } else { '-' }, - self.solc_version, ) } } + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.repr_without_version(), self.solc_version) + } +} diff --git a/compiler_tester/src/compilers/vyper/mode.rs b/compiler_tester/src/compilers/vyper/mode.rs index bae8363c..fcbd9be2 100644 --- a/compiler_tester/src/compilers/vyper/mode.rs +++ b/compiler_tester/src/compilers/vyper/mode.rs @@ -9,7 +9,7 @@ use crate::compilers::mode::Mode as ModeWrapper; /// /// The compiler tester Vyper mode. /// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Mode { /// The Vyper compiler version. pub vyper_version: semver::Version, @@ -74,16 +74,21 @@ impl Mode { } }) } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "V{}{} {}", + /// + /// Returns a string representation excluding the vyper version. + /// + pub fn repr_without_version(&self) -> String { + format!( + "V{}{}", if self.vyper_optimize { '+' } else { '-' }, self.llvm_optimizer_settings, - self.vyper_version, ) } } + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.repr_without_version(), self.vyper_version,) + } +} diff --git a/compiler_tester/src/compilers/yul/mode.rs b/compiler_tester/src/compilers/yul/mode.rs index 8f334a75..2f405409 100644 --- a/compiler_tester/src/compilers/yul/mode.rs +++ b/compiler_tester/src/compilers/yul/mode.rs @@ -9,7 +9,7 @@ use crate::compilers::mode::Mode as ModeWrapper; /// /// The compiler tester Yul mode. /// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Mode { /// The optimizer settings. pub llvm_optimizer_settings: era_compiler_llvm_context::OptimizerSettings, diff --git a/compiler_tester/src/compilers/yul/mode_upstream.rs b/compiler_tester/src/compilers/yul/mode_upstream.rs index 0dcc3ae6..a7a9db7f 100644 --- a/compiler_tester/src/compilers/yul/mode_upstream.rs +++ b/compiler_tester/src/compilers/yul/mode_upstream.rs @@ -7,7 +7,7 @@ use crate::compilers::mode::Mode as ModeWrapper; /// /// The compiler tester upstream Yul mode. /// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Mode { /// The Solidity compiler version. pub solc_version: semver::Version, @@ -42,19 +42,21 @@ impl Mode { _ => panic!("Non-Yul-upstream mode"), } } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// + /// Returns a string representation excluding the solc version. + /// + pub fn repr_without_version(&self) -> String { if self.via_mlir { - return write!(f, "L {}", self.solc_version); + String::from("L") + } else { + format!("Y{}", if self.solc_optimize { '+' } else { '-' },) } + } +} - write!( - f, - "Y{} {}", - if self.solc_optimize { '+' } else { '-' }, - self.solc_version, - ) +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.repr_without_version(), self.solc_version) } } diff --git a/compiler_tester/src/directories/ethereum/test.rs b/compiler_tester/src/directories/ethereum/test.rs index 31a43242..1f2f7742 100644 --- a/compiler_tester/src/directories/ethereum/test.rs +++ b/compiler_tester/src/directories/ethereum/test.rs @@ -14,6 +14,8 @@ use crate::environment::Environment; use crate::filters::Filters; use crate::summary::Summary; use crate::test::case::Case; +use crate::test::description::TestDescription; +use crate::test::selector::TestSelector; use crate::test::Test; use crate::vm::address_iterator::AddressIterator; use crate::vm::eravm::address_iterator::EraVMAddressIterator; @@ -24,8 +26,8 @@ use crate::vm::evm::address_iterator::EVMAddressIterator; /// #[derive(Debug)] pub struct EthereumTest { - /// The test identifier. - pub identifier: String, + /// The test selector. + pub selector: TestSelector, /// The index test entity. pub index_entity: solidity_adapter::EnabledTest, /// The test data. @@ -41,9 +43,9 @@ impl EthereumTest { summary: Arc>, filters: &Filters, ) -> Option { - let identifier = index_entity.path.to_string_lossy().to_string(); + let path = index_entity.path.to_string_lossy().to_string(); - if !filters.check_case_path(&identifier) { + if !filters.check_case_path(&path) { return None; } @@ -51,16 +53,21 @@ impl EthereumTest { return None; } + let selector = TestSelector { + path, + case: None, + input: None, + }; let test = match solidity_adapter::Test::try_from(index_entity.path.as_path()) { Ok(test) => test, Err(error) => { - Summary::invalid(summary, None, identifier, error); + Summary::invalid(summary, TestDescription::default_for(selector), error); return None; } }; Some(Self { - identifier, + selector, index_entity, test, }) @@ -177,9 +184,12 @@ impl EthereumTest { None => { Summary::invalid( summary, - Some(mode.to_owned()), - self.identifier.to_owned(), - anyhow::anyhow!("The Ethereum test `{}` sources are empty", self.identifier), + TestDescription { + group: None, + mode: Some(mode.clone()), + selector: self.selector.clone(), + }, + anyhow::anyhow!("The Ethereum test `{}` sources are empty", &self.selector), ); None } @@ -204,6 +214,12 @@ impl Buildable for EthereumTest { let last_source = self.last_source(summary.clone(), &mode)?; + let test_description = TestDescription { + group: None, + mode: Some(mode.clone()), + selector: self.selector.clone(), + }; + let (contract_address, libraries_addresses, libraries) = match self.get_addresses( EraVMAddressIterator::new(), calls.as_slice(), @@ -213,7 +229,7 @@ impl Buildable for EthereumTest { (contract_address, libraries_addresses, libraries) } Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -221,7 +237,7 @@ impl Buildable for EthereumTest { let evm_version = self.test.params.evm_version; let eravm_input = match compiler .compile_for_eravm( - self.identifier.to_owned(), + self.selector.to_string(), self.test.sources.clone(), libraries, &mode, @@ -232,7 +248,7 @@ impl Buildable for EthereumTest { { Ok(output) => output, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -244,7 +260,7 @@ impl Buildable for EthereumTest { ) { Ok(instance) => instance, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -257,12 +273,7 @@ impl Buildable for EthereumTest { ) { Ok(case) => case, Err(error) => { - Summary::invalid( - summary.clone(), - Some(mode), - self.identifier.to_owned(), - error, - ); + Summary::invalid(summary.clone(), test_description, error); return None; } }; @@ -281,7 +292,7 @@ impl Buildable for EthereumTest { .collect(); Some(Test::new( - self.identifier.to_owned(), + self.selector.to_string(), vec![case], mode, self.index_entity.group.clone(), @@ -305,6 +316,11 @@ impl Buildable for EthereumTest { let mut calls = self.test.calls.clone(); self.insert_deploy_calls(&mut calls); + let test_description = TestDescription { + group: None, + mode: Some(mode.clone()), + selector: self.selector.clone(), + }; let last_source = self.last_source(summary.clone(), &mode)?; let (contract_address, libraries_addresses, libraries) = match self.get_addresses( @@ -316,7 +332,7 @@ impl Buildable for EthereumTest { (contract_address, libraries_addresses, libraries) } Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -324,7 +340,7 @@ impl Buildable for EthereumTest { let evm_version = self.test.params.evm_version; let evm_input = match compiler .compile_for_evm( - self.identifier.to_owned(), + self.selector.to_string(), self.test.sources.clone(), libraries, &mode, @@ -336,7 +352,7 @@ impl Buildable for EthereumTest { { Ok(output) => output, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -348,7 +364,7 @@ impl Buildable for EthereumTest { ) { Ok(instance) => instance, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -361,18 +377,13 @@ impl Buildable for EthereumTest { ) { Ok(case) => case, Err(error) => { - Summary::invalid( - summary.clone(), - Some(mode), - self.identifier.to_owned(), - error, - ); + Summary::invalid(summary.clone(), test_description, error); return None; } }; Some(Test::new( - self.identifier.to_owned(), + self.selector.path.to_string(), vec![case], mode, self.index_entity.group.clone(), diff --git a/compiler_tester/src/directories/matter_labs/test/mod.rs b/compiler_tester/src/directories/matter_labs/test/mod.rs index 8a32951e..e79a194d 100644 --- a/compiler_tester/src/directories/matter_labs/test/mod.rs +++ b/compiler_tester/src/directories/matter_labs/test/mod.rs @@ -19,7 +19,9 @@ use crate::environment::Environment; use crate::filters::Filters; use crate::summary::Summary; use crate::test::case::Case; +use crate::test::description::TestDescription; use crate::test::instance::Instance; +use crate::test::selector::TestSelector; use crate::test::Test; use crate::vm::address_iterator::AddressIterator; use crate::vm::eravm::address_iterator::EraVMAddressIterator; @@ -61,8 +63,8 @@ pub fn default_caller_address() -> String { pub struct MatterLabsTest { /// The test path. path: PathBuf, - /// The test identifier. - identifier: String, + /// The test selector. + selector: TestSelector, /// The test metadata. metadata: Metadata, /// The test sources. @@ -74,16 +76,22 @@ impl MatterLabsTest { /// Try to create new test. /// pub fn new(path: PathBuf, summary: Arc>, filters: &Filters) -> Option { - let identifier = path.to_string_lossy().to_string(); + let selector = TestSelector { + path: path.to_string_lossy().to_string(), + case: None, + input: None, + }; - if !filters.check_test_path(identifier.as_str()) { + if !filters.check_test_path(selector.path.to_string().as_str()) { return None; } + let test_description = TestDescription::default_for(selector.clone()); + let main_file_string = match std::fs::read_to_string(path.as_path()) { Ok(data) => data, Err(error) => { - Summary::invalid(summary, None, identifier.clone(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -93,13 +101,13 @@ impl MatterLabsTest { { Ok(metadata) => metadata, Err(error) => { - Summary::invalid(summary, None, identifier.clone(), error); + Summary::invalid(summary, test_description, error); return None; } }; if metadata.ignore { - Summary::ignored(summary, identifier.clone()); + Summary::ignored(summary, test_description); return None; } @@ -147,7 +155,7 @@ impl MatterLabsTest { { Ok(source) => source, Err(error) => { - Summary::invalid(summary, None, identifier.clone(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -157,12 +165,19 @@ impl MatterLabsTest { }; metadata.cases.retain(|case| { - let case_name = format!("{}::{}", identifier, case.name); + let selector_with_case = TestSelector { + path: selector.path.clone(), + case: Some(case.name.clone()), + input: selector.input.clone(), + }; if case.ignore { - Summary::ignored(summary.clone(), case_name); + Summary::ignored( + summary.clone(), + TestDescription::default_for(selector_with_case), + ); return false; } - + let case_name = selector_with_case.to_string(); if !filters.check_case_path(&case_name) { return false; } @@ -171,7 +186,7 @@ impl MatterLabsTest { Some(Self { path, - identifier, + selector, metadata, sources, }) @@ -215,9 +230,9 @@ impl MatterLabsTest { ) { if contracts.is_empty() { let contract_name = if is_multi_contract { - format!("{}:{}", self.identifier, SIMPLE_TESTS_CONTRACT_NAME) + format!("{}:{}", self.selector.path, SIMPLE_TESTS_CONTRACT_NAME) } else { - self.identifier.to_owned() + self.selector.path.to_string() }; contracts.insert(SIMPLE_TESTS_INSTANCE.to_owned(), contract_name); } @@ -398,7 +413,6 @@ impl Buildable for MatterLabsTest { debug_config: Option, ) -> Option { mode.enable_eravm_extensions(self.metadata.enable_eravm_extensions); - self.check_filters(filters, &mode, era_compiler_common::Target::EraVM)?; let mut contracts = self.metadata.contracts.clone(); @@ -407,11 +421,16 @@ impl Buildable for MatterLabsTest { let mut eravm_address_iterator = EraVMAddressIterator::new(); let evm_address_iterator = EVMAddressIterator::default(); - let (libraries, library_addresses) = self.get_libraries(&mut eravm_address_iterator); + let test_description = TestDescription { + group: None, + mode: Some(mode.clone()), + selector: self.selector.clone(), + }; + let (libraries, library_addresses) = self.get_libraries(&mut eravm_address_iterator); let eravm_input = match compiler .compile_for_eravm( - self.identifier.to_owned(), + self.selector.path.to_string(), self.sources.clone(), libraries, &mode, @@ -422,7 +441,7 @@ impl Buildable for MatterLabsTest { { Ok(vm_input) => vm_input, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -434,7 +453,7 @@ impl Buildable for MatterLabsTest { ) { Ok(instances) => instances, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -442,7 +461,7 @@ impl Buildable for MatterLabsTest { let evm_instances = match self.get_evm_instances() { Ok(evm_instances) => evm_instances, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -462,7 +481,7 @@ impl Buildable for MatterLabsTest { let case = match case.normalize(&contracts, &instances, environment) { Ok(case) => case, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -475,7 +494,7 @@ impl Buildable for MatterLabsTest { ) { Ok(_) => {} Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } } @@ -492,7 +511,7 @@ impl Buildable for MatterLabsTest { { Ok(case) => case, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -514,7 +533,7 @@ impl Buildable for MatterLabsTest { .collect(); Some(Test::new( - self.identifier.to_owned(), + self.selector.to_string(), cases, mode, self.metadata.group.clone(), @@ -542,9 +561,15 @@ impl Buildable for MatterLabsTest { let (libraries, library_addresses) = self.get_libraries(&mut evm_address_iterator); + let test_description = TestDescription { + group: None, + mode: Some(mode.clone()), + selector: self.selector.clone(), + }; + let evm_input = match compiler .compile_for_evm( - self.identifier.to_owned(), + self.selector.path.to_string(), sources, libraries, &mode, @@ -556,7 +581,7 @@ impl Buildable for MatterLabsTest { { Ok(output) => output, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -564,7 +589,7 @@ impl Buildable for MatterLabsTest { let mut instances = match evm_input.get_instances(&contracts, library_addresses, None) { Ok(instances) => instances, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -583,7 +608,7 @@ impl Buildable for MatterLabsTest { { Ok(case) => case, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -596,7 +621,7 @@ impl Buildable for MatterLabsTest { ) { Ok(_) => {} Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } } @@ -613,7 +638,7 @@ impl Buildable for MatterLabsTest { { Ok(case) => case, Err(error) => { - Summary::invalid(summary, Some(mode), self.identifier.to_owned(), error); + Summary::invalid(summary, test_description, error); return None; } }; @@ -622,7 +647,7 @@ impl Buildable for MatterLabsTest { } Some(Test::new( - self.identifier.to_owned(), + self.selector.to_string(), cases, mode, self.metadata.group.clone(), diff --git a/compiler_tester/src/summary/benchmark_adapters/input.rs b/compiler_tester/src/summary/benchmark_adapters/input.rs new file mode 100644 index 00000000..e5ff9ad2 --- /dev/null +++ b/compiler_tester/src/summary/benchmark_adapters/input.rs @@ -0,0 +1,29 @@ +//! +//! Converts `[InputIdentifier]` to the representation used by the benchmark. +//! + +use crate::test::case::input::identifier::InputIdentifier; + +impl From for benchmark_analyzer::Input { + /// + /// Converts `[InputIdentifier]` to the representation used by the benchmark. + /// + fn from(val: InputIdentifier) -> Self { + match val { + InputIdentifier::Deployer { + contract_identifier, + } => benchmark_analyzer::Input::Deployer { + contract_identifier, + }, + InputIdentifier::Runtime { input_index, name } => { + benchmark_analyzer::Input::Runtime { input_index, name } + } + InputIdentifier::StorageEmpty { input_index } => { + benchmark_analyzer::Input::StorageEmpty { input_index } + } + InputIdentifier::Balance { input_index } => { + benchmark_analyzer::Input::Balance { input_index } + } + } + } +} diff --git a/compiler_tester/src/summary/benchmark_adapters/metadata.rs b/compiler_tester/src/summary/benchmark_adapters/metadata.rs new file mode 100644 index 00000000..d713251d --- /dev/null +++ b/compiler_tester/src/summary/benchmark_adapters/metadata.rs @@ -0,0 +1,54 @@ +//! +//! Converts `[TestDescription]` to the representation used by the benchmark. +//! + +use crate::test::description::TestDescription; +use crate::Mode; + +pub fn convert_description( + description: &TestDescription, + default_group: &str, +) -> benchmark_analyzer::Metadata { + let TestDescription { + group, + mode, + selector, + } = description.clone(); + let selector = selector.into(); + let version = match &mode { + Some(mode) => mode_version(mode.clone()).map(|m| m.to_string()), + None => None, + }; + let mode = mode.map(mode_string).unwrap_or_default(); + let group = group.unwrap_or(default_group.to_string()); + benchmark_analyzer::Metadata { + selector, + mode, + version, + group, + } +} + +fn mode_version(mode: Mode) -> Option { + match mode { + Mode::Solidity(mode) => Some(mode.solc_version), + Mode::SolidityUpstream(mode) => Some(mode.solc_version), + Mode::Yul(_) => None, + Mode::YulUpstream(mode) => Some(mode.solc_version), + Mode::Vyper(mode) => Some(mode.vyper_version), + Mode::LLVM(_) => None, + Mode::EraVM(_) => None, + } +} + +fn mode_string(mode: Mode) -> Option { + match mode { + Mode::Solidity(mode) => Some(mode.repr_without_version()), + Mode::SolidityUpstream(mode) => Some(mode.repr_without_version()), + Mode::Yul(_) => None, + Mode::YulUpstream(mode) => Some(mode.repr_without_version()), + Mode::Vyper(mode) => Some(mode.repr_without_version()), + Mode::LLVM(_) => None, + Mode::EraVM(_) => None, + } +} diff --git a/compiler_tester/src/summary/benchmark_adapters/mod.rs b/compiler_tester/src/summary/benchmark_adapters/mod.rs new file mode 100644 index 00000000..9968d960 --- /dev/null +++ b/compiler_tester/src/summary/benchmark_adapters/mod.rs @@ -0,0 +1,8 @@ +//! +//! Adapters to convert the test information defined in `compiler_tester` to the +//! representation used by `benchmark_analyzer`. +//! + +pub mod input; +pub mod metadata; +pub mod selector; diff --git a/compiler_tester/src/summary/benchmark_adapters/selector.rs b/compiler_tester/src/summary/benchmark_adapters/selector.rs new file mode 100644 index 00000000..9f027e63 --- /dev/null +++ b/compiler_tester/src/summary/benchmark_adapters/selector.rs @@ -0,0 +1,13 @@ +//! +//! Converts `[TestSelector]` to the representation used by the benchmark. +//! + +use crate::test::selector::TestSelector; + +impl From for benchmark_analyzer::TestSelector { + fn from(selector: TestSelector) -> Self { + let TestSelector { path, case, input } = selector; + let input = input.map(Into::into); + benchmark_analyzer::TestSelector { path, case, input } + } +} diff --git a/compiler_tester/src/summary/element/mod.rs b/compiler_tester/src/summary/element/mod.rs index fd0f6e48..f1a6a01c 100644 --- a/compiler_tester/src/summary/element/mod.rs +++ b/compiler_tester/src/summary/element/mod.rs @@ -6,7 +6,7 @@ pub mod outcome; use colored::Colorize; -use crate::compilers::mode::Mode; +use crate::test::description::TestDescription; use self::outcome::passed_variant::PassedVariant; use self::outcome::Outcome; @@ -16,10 +16,8 @@ use self::outcome::Outcome; /// #[derive(Debug)] pub struct Element { - /// The mode. - pub mode: Option, - /// The test name. - pub name: String, + /// Information about test instance. + pub test_description: TestDescription, /// The test outcome. pub outcome: Outcome, } @@ -28,10 +26,9 @@ impl Element { /// /// A shortcut constructor. /// - pub fn new(mode: Option, name: String, outcome: Outcome) -> Self { + pub fn new(name: TestDescription, outcome: Outcome) -> Self { Self { - mode, - name, + test_description: name, outcome, } } @@ -103,13 +100,14 @@ impl Element { Some(format!( "{:16} {:>7} {} {}", - self.mode + self.test_description + .mode .as_ref() .map(|mode| mode.to_string()) .unwrap_or_default() .bright_white(), outcome, - self.name, + self.test_description.selector, details )) } diff --git a/compiler_tester/src/summary/mod.rs b/compiler_tester/src/summary/mod.rs index 891fa381..1f20ff10 100644 --- a/compiler_tester/src/summary/mod.rs +++ b/compiler_tester/src/summary/mod.rs @@ -2,15 +2,17 @@ //! The compiler tester summary. //! +pub mod benchmark_adapters; pub mod element; use std::sync::Arc; use std::sync::Mutex; +use benchmark_adapters::metadata::convert_description; use colored::Colorize; -use crate::compilers::mode::Mode; use crate::test::case::input::output::Output; +use crate::test::description::TestDescription; use crate::toolchain::Toolchain; use self::element::outcome::passed_variant::PassedVariant; @@ -130,19 +132,25 @@ impl Summary { let key = format!( "{:24} {}", element + .test_description .mode .as_ref() .map(|mode| mode.to_string()) .unwrap_or_default(), - element.name + element.test_description.selector ); let mode = element + .test_description .mode .as_ref() .and_then(|mode| mode.llvm_optimizer_settings().cloned()); + let metadata = { + let default_group = group.clone().unwrap_or_default(); + convert_description(&element.test_description, &default_group) + }; let benchmark_element = - benchmark_analyzer::BenchmarkElement::new(size, cycles, ergs, gas); + benchmark_analyzer::BenchmarkElement::new(metadata, size, cycles, ergs, gas); if let Some(group) = group { let group_key = match mode { Some(ref mode) => format!("{group} {mode}"), @@ -191,9 +199,7 @@ impl Summary { /// pub fn passed_deploy( summary: Arc>, - mode: Mode, - name: String, - group: Option, + test: TestDescription, size: usize, cycles: usize, ergs: u64, @@ -205,7 +211,7 @@ impl Summary { ergs, gas, }; - Self::passed(summary, mode, name, group, passed_variant); + Self::passed(summary, test, passed_variant); } /// @@ -213,28 +219,21 @@ impl Summary { /// pub fn passed_runtime( summary: Arc>, - mode: Mode, - name: String, - group: Option, + test: TestDescription, cycles: usize, ergs: u64, gas: u64, ) { let passed_variant = PassedVariant::Runtime { cycles, ergs, gas }; - Self::passed(summary, mode, name, group, passed_variant); + Self::passed(summary, test, passed_variant); } /// /// Adds a passed outcome of a special call, like `storageEmpty` or `balance`. /// - pub fn passed_special( - summary: Arc>, - mode: Mode, - name: String, - group: Option, - ) { + pub fn passed_special(summary: Arc>, test: TestDescription) { let passed_variant = PassedVariant::Special; - Self::passed(summary, mode, name, group, passed_variant); + Self::passed(summary, test, passed_variant); } /// @@ -242,46 +241,40 @@ impl Summary { /// pub fn failed( summary: Arc>, - mode: Mode, - name: String, + test: TestDescription, expected: Output, found: Output, calldata: Vec, ) { - let element = Element::new(Some(mode), name, Outcome::failed(expected, found, calldata)); + let element = Element::new(test, Outcome::failed(expected, found, calldata)); summary.lock().expect("Sync").push_element(element); } /// /// Adds an invalid outcome. /// - pub fn invalid(summary: Arc>, mode: Option, name: String, error: S) + pub fn invalid(summary: Arc>, test: TestDescription, error: S) where S: ToString, { - let element = Element::new(mode, name, Outcome::invalid(error)); + let element = Element::new(test, Outcome::invalid(error)); summary.lock().expect("Sync").push_element(element); } /// /// Adds an ignored outcome. /// - pub fn ignored(summary: Arc>, name: String) { - let element = Element::new(None, name, Outcome::ignored()); + pub fn ignored(summary: Arc>, test: TestDescription) { + let element = Element::new(test.with_erased_mode(), Outcome::ignored()); summary.lock().expect("Sync").push_element(element); } /// /// The unified function for passed outcomes. /// - fn passed( - summary: Arc>, - mode: Mode, - name: String, - group: Option, - passed_variant: PassedVariant, - ) { - let element = Element::new(Some(mode), name, Outcome::passed(group, passed_variant)); + fn passed(summary: Arc>, test: TestDescription, passed_variant: PassedVariant) { + let group = test.group.clone(); + let element = Element::new(test, Outcome::passed(group, passed_variant)); summary.lock().expect("Sync").push_element(element); } diff --git a/compiler_tester/src/test/case/input/balance.rs b/compiler_tester/src/test/case/input/balance.rs index c0f9ac97..db6ad675 100644 --- a/compiler_tester/src/test/case/input/balance.rs +++ b/compiler_tester/src/test/case/input/balance.rs @@ -5,8 +5,10 @@ use std::sync::Arc; use std::sync::Mutex; -use crate::compilers::mode::Mode; use crate::summary::Summary; +use crate::test::case::input::identifier::InputIdentifier; +use crate::test::context::input::InputContext; +use crate::test::description::TestDescription; use crate::vm::eravm::system_context::SystemContext; use crate::vm::eravm::EraVM; use crate::vm::evm::EVM; @@ -41,13 +43,11 @@ impl Balance { self, summary: Arc>, vm: &mut EraVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) { - let name = format!("{name_prefix}[#balance_check:{index}]"); - + let input_index = context.selector; + let identifier = + TestDescription::from_context(context, InputIdentifier::Balance { input_index }); let rich_addresses = SystemContext::get_rich_addresses(); if rich_addresses.contains(&self.address) { vm.mint_ether( @@ -62,12 +62,11 @@ impl Balance { let found = vm.get_balance(self.address); if found == self.balance { - Summary::passed_special(summary, mode, name, test_group); + Summary::passed_special(summary, identifier); } else { Summary::failed( summary, - mode, - name, + identifier, self.balance.into(), found.into(), self.address.to_fixed_bytes().to_vec(), @@ -82,10 +81,7 @@ impl Balance { self, _summary: Arc>, _vm: &EVM, - _mode: Mode, - _test_group: Option, - _name_prefix: String, - _index: usize, + _context: InputContext<'_>, ) { todo!() } @@ -93,16 +89,9 @@ impl Balance { /// /// Runs the balance check on REVM. /// - pub fn run_revm( - self, - summary: Arc>, - vm: &mut Revm, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, - ) { - let name = format!("{name_prefix}[#balance_check:{index}]"); + pub fn run_revm(self, summary: Arc>, vm: &mut Revm, context: InputContext<'_>) { + let input_index = context.selector; + let test = TestDescription::from_context(context, InputIdentifier::Balance { input_index }); let found = vm .state .context @@ -112,12 +101,11 @@ impl Balance { Ok(found) => { let u256_found = web3::types::U256::from(found.data.to_be_bytes()); if u256_found == self.balance { - Summary::passed_special(summary, mode, name, test_group); + Summary::passed_special(summary, test); } else { Summary::failed( summary, - mode, - name, + test, self.balance.into(), u256_found.into(), self.address.to_fixed_bytes().to_vec(), @@ -127,8 +115,7 @@ impl Balance { Err(_) => { Summary::failed( summary, - mode, - name, + test, self.balance.into(), web3::types::U256::zero().into(), self.address.to_fixed_bytes().to_vec(), @@ -144,20 +131,17 @@ impl Balance { self, summary: Arc>, vm: &EraVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) { - let name = format!("{name_prefix}[#balance_check:{index}]"); + let input_index = context.selector; + let test = TestDescription::from_context(context, InputIdentifier::Balance { input_index }); let found = vm.get_balance(self.address); if found == self.balance { - Summary::passed_special(summary, mode, name, test_group); + Summary::passed_special(summary, test); } else { Summary::failed( summary, - mode, - name, + test, self.balance.into(), found.into(), self.address.to_fixed_bytes().to_vec(), diff --git a/compiler_tester/src/test/case/input/deploy_eravm.rs b/compiler_tester/src/test/case/input/deploy_eravm.rs index b6ef0a26..790b9e1c 100644 --- a/compiler_tester/src/test/case/input/deploy_eravm.rs +++ b/compiler_tester/src/test/case/input/deploy_eravm.rs @@ -5,11 +5,13 @@ use std::sync::Arc; use std::sync::Mutex; -use crate::compilers::mode::Mode; use crate::summary::Summary; use crate::test::case::input::calldata::Calldata; +use crate::test::case::input::identifier::InputIdentifier; use crate::test::case::input::output::Output; use crate::test::case::input::storage::Storage; +use crate::test::description::TestDescription; +use crate::test::InputContext; use crate::vm::eravm::deployers::EraVMDeployer; use crate::vm::eravm::EraVM; @@ -67,18 +69,21 @@ impl DeployEraVM { self, summary: Arc>, vm: &mut EraVM, - mode: Mode, deployer: &mut D, - test_group: Option, - name_prefix: String, + context: InputContext<'_>, ) where D: EraVMDeployer, { - let name = format!("{name_prefix}[#deployer:{}]", self.path); - + let test = TestDescription::from_context( + context, + InputIdentifier::Deployer { + contract_identifier: self.path, + }, + ); + let name = test.selector.path.to_string(); vm.populate_storage(self.storage.inner); let result = match deployer.deploy_eravm::( - name.clone(), + name, self.caller, self.hash, self.calldata.inner.clone(), @@ -87,7 +92,7 @@ impl DeployEraVM { ) { Ok(result) => result, Err(error) => { - Summary::invalid(summary, Some(mode), name, error); + Summary::invalid(summary, test, error); return; } }; @@ -96,9 +101,7 @@ impl DeployEraVM { let build_size = vm.get_contract_size(self.hash); Summary::passed_deploy( summary, - mode, - name, - test_group, + test, build_size, result.cycles, result.ergs, @@ -107,8 +110,7 @@ impl DeployEraVM { } else { Summary::failed( summary, - mode, - name, + test, self.expected, result.output, self.calldata.inner, diff --git a/compiler_tester/src/test/case/input/deploy_evm.rs b/compiler_tester/src/test/case/input/deploy_evm.rs index a5b6b7bd..d667058c 100644 --- a/compiler_tester/src/test/case/input/deploy_evm.rs +++ b/compiler_tester/src/test/case/input/deploy_evm.rs @@ -9,11 +9,13 @@ use revm::primitives::EVMError; use revm::primitives::ExecutionResult; use solidity_adapter::EVMVersion; -use crate::compilers::mode::Mode; use crate::summary::Summary; use crate::test::case::input::calldata::Calldata; +use crate::test::case::input::identifier::InputIdentifier; use crate::test::case::input::output::Output; use crate::test::case::input::storage::Storage; +use crate::test::description::TestDescription; +use crate::test::InputContext; use crate::vm::eravm::deployers::EraVMDeployer; use crate::vm::eravm::EraVM; use crate::vm::evm::EVM; @@ -76,15 +78,19 @@ impl DeployEVM { self, summary: Arc>, vm: &mut EVM, - mode: Mode, - test_group: Option, - name_prefix: String, + context: InputContext<'_>, ) { - let name = format!("{}[#deployer:{}]", name_prefix, self.identifier); + let test = TestDescription::from_context( + context, + InputIdentifier::Deployer { + contract_identifier: self.identifier.clone(), + }, + ); + let name = test.selector.to_string(); vm.populate_storage(self.storage.inner); let result = match vm.execute_deploy_code( - name.clone(), + name, self.identifier.as_str(), self.caller, self.value, @@ -92,25 +98,16 @@ impl DeployEVM { ) { Ok(execution_result) => execution_result, Err(error) => { - Summary::invalid(summary, Some(mode), name, error); + Summary::invalid(summary, test, error); return; } }; if result.output == self.expected { - Summary::passed_runtime( - summary, - mode, - name, - test_group, - result.cycles, - 0, - result.gas, - ); + Summary::passed_runtime(summary, test, result.cycles, 0, result.gas); } else { Summary::failed( summary, - mode, - name, + test, self.expected, result.output, self.calldata.inner, @@ -121,17 +118,19 @@ impl DeployEVM { /// /// Runs the deploy transaction on native REVM. /// - pub fn run_revm( + pub fn run_revm<'b>( self, summary: Arc>, - vm: Revm, - mode: Mode, - test_group: Option, - name_prefix: String, + vm: Revm<'b>, evm_version: Option, - ) -> Revm { - let name = format!("{}[#deployer:{}]", name_prefix, self.identifier); - + context: InputContext<'_>, + ) -> Revm<'b> { + let test = TestDescription::from_context( + context, + InputIdentifier::Deployer { + contract_identifier: self.identifier.clone(), + }, + ); let size = self.deploy_code.len(); let vm = vm.update_deploy_balance(&self.caller); @@ -141,48 +140,15 @@ impl DeployEVM { let result = match vm.state.transact_commit() { Ok(res) => res, Err(error) => { - match error { - EVMError::Transaction(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Transaction: {error:?}"), - ); - } - EVMError::Header(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Header: {error:?}"), - ); - } - EVMError::Database(_error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - "Error on Database", - ); - } - EVMError::Custom(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Custom: {error:?}"), - ); - } - EVMError::Precompile(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Precompile: {error:?}"), - ); - } - } + let error_msg = match error { + EVMError::Transaction(error) => format!("Error on Transaction: {error:?}"), + EVMError::Header(error) => format!("Error on Header: {error:?}"), + EVMError::Database(_error) => "Error on Database".into(), + EVMError::Custom(error) => format!("Error on Custom: {error:?}"), + EVMError::Precompile(error) => format!("Error on Precompile: {error:?}"), + }; + + Summary::invalid(summary.clone(), test, error_msg); return vm; } }; @@ -205,18 +171,11 @@ impl DeployEVM { }; if output == self.expected { - Summary::passed_deploy(summary, mode, name, test_group, size, 0, 0, gas); + Summary::passed_deploy(summary, test, size, 0, 0, gas); } else if let Some(error) = error { - Summary::invalid(summary, Some(mode), name, format!("{error:?}")); + Summary::invalid(summary, test, format!("{error:?}")); } else { - Summary::failed( - summary, - mode, - name, - self.expected, - output, - self.calldata.inner, - ); + Summary::failed(summary, test, self.expected, output, self.calldata.inner); } vm @@ -229,20 +188,24 @@ impl DeployEVM { self, summary: Arc>, vm: &mut EraVM, - mode: Mode, deployer: &mut D, - test_group: Option, - name_prefix: String, + context: InputContext<'_>, ) where D: EraVMDeployer, { - let name = format!("{}[#deployer:{}]", name_prefix, self.identifier); - + let test = TestDescription::from_context( + context, + InputIdentifier::Deployer { + contract_identifier: self.identifier.clone(), + }, + ); + + let name = test.selector.to_string(); let size = self.deploy_code.len(); vm.populate_storage(self.storage.inner); let result = match deployer.deploy_evm::( - name.clone(), + name, self.caller, self.deploy_code, self.calldata.inner.clone(), @@ -251,26 +214,16 @@ impl DeployEVM { ) { Ok(result) => result, Err(error) => { - Summary::invalid(summary, Some(mode), name, error); + Summary::invalid(summary, test, error); return; } }; if result.output == self.expected { - Summary::passed_deploy( - summary, - mode, - name, - test_group, - size, - result.cycles, - result.ergs, - result.gas, - ); + Summary::passed_deploy(summary, test, size, result.cycles, result.ergs, result.gas); } else { Summary::failed( summary, - mode, - name, + test, self.expected, result.output, self.calldata.inner, diff --git a/compiler_tester/src/test/case/input/identifier.rs b/compiler_tester/src/test/case/input/identifier.rs new file mode 100644 index 00000000..6210abf5 --- /dev/null +++ b/compiler_tester/src/test/case/input/identifier.rs @@ -0,0 +1,37 @@ +//! +//! Identifier for the test input. Describes the input type and position but not the actual contents. +//! + +/// +/// Identifier for the test input. Describes the input type and position but not the actual contents. +/// +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum InputIdentifier { + /// The contract deploy, regardless of target. + Deployer { contract_identifier: String }, + /// The contract call. + Runtime { input_index: usize, name: String }, + /// The storage empty check. + StorageEmpty { input_index: usize }, + /// Check account balance. + Balance { input_index: usize }, +} + +impl std::fmt::Display for InputIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InputIdentifier::Deployer { + contract_identifier, + } => f.write_fmt(format_args!("#deployer:{contract_identifier}")), + InputIdentifier::Runtime { input_index, name } => { + f.write_fmt(format_args!("{name}:{input_index}")) + } + InputIdentifier::StorageEmpty { input_index } => { + f.write_fmt(format_args!("#storage_empty_check:{input_index}")) + } + InputIdentifier::Balance { input_index } => { + f.write_fmt(format_args!("#balance_check:{input_index}")) + } + } + } +} diff --git a/compiler_tester/src/test/case/input/mod.rs b/compiler_tester/src/test/case/input/mod.rs index e0750819..663420a0 100644 --- a/compiler_tester/src/test/case/input/mod.rs +++ b/compiler_tester/src/test/case/input/mod.rs @@ -6,6 +6,7 @@ pub mod balance; pub mod calldata; pub mod deploy_eravm; pub mod deploy_evm; +pub mod identifier; pub mod output; pub mod runtime; pub mod storage; @@ -21,6 +22,7 @@ use crate::compilers::mode::Mode; use crate::directories::matter_labs::test::metadata::case::input::Input as MatterLabsTestInput; use crate::summary::Summary; use crate::test::instance::Instance; +use crate::test::InputContext; use crate::vm::eravm::deployers::EraVMDeployer; use crate::vm::eravm::EraVM; use crate::vm::evm::EVM; @@ -367,35 +369,19 @@ impl Input { self, summary: Arc>, vm: &mut EraVM, - mode: Mode, deployer: &mut D, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) where D: EraVMDeployer, { match self { - Self::DeployEraVM(deploy) => { - deploy.run_eravm::<_, M>(summary, vm, mode, deployer, test_group, name_prefix) - } - Self::DeployEVM(deploy) => deploy.run_evm_interpreter::<_, M>( - summary, - vm, - mode, - deployer, - test_group, - name_prefix, - ), - Self::Runtime(runtime) => { - runtime.run_eravm::(summary, vm, mode, test_group, name_prefix, index) - } - Self::StorageEmpty(storage_empty) => { - storage_empty.run_eravm(summary, vm, mode, test_group, name_prefix, index) - } - Self::Balance(balance_check) => { - balance_check.run_eravm(summary, vm, mode, test_group, name_prefix, index) + Self::DeployEraVM(deploy) => deploy.run_eravm::<_, M>(summary, vm, deployer, context), + Self::DeployEVM(deploy) => { + deploy.run_evm_interpreter::<_, M>(summary, vm, deployer, context) } + Self::Runtime(runtime) => runtime.run_eravm::(summary, vm, context), + Self::StorageEmpty(storage_empty) => storage_empty.run_eravm(summary, vm, context), + Self::Balance(balance_check) => balance_check.run_eravm(summary, vm, context), }; } @@ -406,61 +392,39 @@ impl Input { self, summary: Arc>, vm: &mut EVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) { match self { Self::DeployEraVM { .. } => panic!("EraVM deploy transaction cannot be run on EVM"), - Self::DeployEVM(deploy) => { - deploy.run_evm_emulator(summary, vm, mode, test_group, name_prefix) - } - Self::Runtime(runtime) => { - runtime.run_evm_emulator(summary, vm, mode, test_group, name_prefix, index) - } + Self::DeployEVM(deploy) => deploy.run_evm_emulator(summary, vm, context), + Self::Runtime(runtime) => runtime.run_evm_emulator(summary, vm, context), Self::StorageEmpty(storage_empty) => { - storage_empty.run_evm_emulator(summary, vm, mode, test_group, name_prefix, index) - } - Self::Balance(balance_check) => { - balance_check.run_evm_emulator(summary, vm, mode, test_group, name_prefix, index) + storage_empty.run_evm_emulator(summary, vm, context) } + Self::Balance(balance_check) => balance_check.run_evm_emulator(summary, vm, context), }; } /// /// Runs the input on REVM. /// - pub fn run_revm( + pub fn run_revm<'b>( self, summary: Arc>, - mut vm: Revm, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + mut vm: Revm<'b>, evm_version: Option, - ) -> Revm { + context: InputContext<'_>, + ) -> Revm<'b> { match self { Self::DeployEraVM { .. } => panic!("EraVM deploy transaction cannot be run on REVM"), - Self::DeployEVM(deploy) => { - deploy.run_revm(summary, vm, mode, test_group, name_prefix, evm_version) - } - Self::Runtime(runtime) => runtime.run_revm( - summary, - vm, - mode, - test_group, - name_prefix, - index, - evm_version, - ), + Self::DeployEVM(deploy) => deploy.run_revm(summary, vm, evm_version, context), + Self::Runtime(runtime) => runtime.run_revm(summary, vm, evm_version, context), Self::StorageEmpty(storage_empty) => { - storage_empty.run_revm(summary, &mut vm, mode, test_group, name_prefix, index); + storage_empty.run_revm(summary, &mut vm, context); vm } Self::Balance(balance_check) => { - balance_check.run_revm(summary, &mut vm, mode, test_group, name_prefix, index); + balance_check.run_revm(summary, &mut vm, context); vm } } @@ -473,11 +437,8 @@ impl Input { self, summary: Arc>, vm: &mut EraVM, - mode: Mode, deployer: &mut D, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) where D: EraVMDeployer, { @@ -485,23 +446,14 @@ impl Input { Self::DeployEraVM { .. } => { panic!("EraVM deploy transaction cannot be run on EVM interpreter") } - Self::DeployEVM(deploy) => deploy.run_evm_interpreter::<_, M>( - summary, - vm, - mode, - deployer, - test_group, - name_prefix, - ), - Self::Runtime(runtime) => { - runtime.run_evm_interpreter::(summary, vm, mode, test_group, name_prefix, index) + Self::DeployEVM(deploy) => { + deploy.run_evm_interpreter::<_, M>(summary, vm, deployer, context) } + Self::Runtime(runtime) => runtime.run_evm_interpreter::(summary, vm, context), Self::StorageEmpty(storage_empty) => { - storage_empty.run_evm_interpreter(summary, vm, mode, test_group, name_prefix, index) - } - Self::Balance(balance_check) => { - balance_check.run_evm_interpreter(summary, vm, mode, test_group, name_prefix, index) + storage_empty.run_evm_interpreter(summary, vm, context) } + Self::Balance(balance_check) => balance_check.run_evm_interpreter(summary, vm, context), }; } } diff --git a/compiler_tester/src/test/case/input/runtime.rs b/compiler_tester/src/test/case/input/runtime.rs index ad4b6dbc..7070f5cf 100644 --- a/compiler_tester/src/test/case/input/runtime.rs +++ b/compiler_tester/src/test/case/input/runtime.rs @@ -10,11 +10,13 @@ use revm::primitives::EVMError; use revm::primitives::ExecutionResult; use solidity_adapter::EVMVersion; -use crate::compilers::mode::Mode; use crate::summary::Summary; use crate::test::case::input::calldata::Calldata; +use crate::test::case::input::identifier::InputIdentifier; use crate::test::case::input::output::Output; use crate::test::case::input::storage::Storage; +use crate::test::context::input::InputContext; +use crate::test::description::TestDescription; use crate::vm::eravm::system_context::SystemContext; use crate::vm::eravm::EraVM; use crate::vm::evm::EVM; @@ -76,15 +78,20 @@ impl Runtime { self, summary: Arc>, vm: &mut EraVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) { - let name = format!("{name_prefix}[{}:{index}]", self.name); + let group = context.case_context.group.clone(); + let input_index = context.selector; + let test = TestDescription::from_context( + context, + InputIdentifier::Runtime { + input_index, + name: self.name, + }, + ); + let name = test.selector.to_string(); vm.populate_storage(self.storage.inner); - - let vm_function = match test_group.as_deref() { + let vm_function = match group.as_deref() { Some(benchmark_analyzer::Benchmark::EVM_INTERPRETER_GROUP_NAME) => { EraVM::execute_evm_interpreter:: } @@ -92,7 +99,7 @@ impl Runtime { }; let result = match vm_function( vm, - name.clone(), + name, self.address, self.caller, self.value, @@ -101,26 +108,17 @@ impl Runtime { ) { Ok(result) => result, Err(error) => { - Summary::invalid(summary, Some(mode), name, error); + Summary::invalid(summary, test, error); return; } }; if result.output == self.expected { - Summary::passed_runtime( - summary, - mode, - name, - test_group, - result.cycles, - result.ergs, - result.gas, - ); + Summary::passed_runtime(summary, test, result.cycles, result.ergs, result.gas); } else { Summary::failed( summary, - mode, - name, + test, self.expected, result.output, self.calldata.inner, @@ -135,15 +133,20 @@ impl Runtime { self, summary: Arc>, vm: &mut EVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) { - let name = format!("{}[{}:{}]", name_prefix, self.name, index); + let input_index = context.selector; + let test = TestDescription::from_context( + context, + InputIdentifier::Runtime { + input_index, + name: self.name, + }, + ); + let name = test.selector.to_string(); vm.populate_storage(self.storage.inner); let result = match vm.execute_runtime_code( - name.clone(), + name, self.address, self.caller, self.value, @@ -151,25 +154,16 @@ impl Runtime { ) { Ok(execution_result) => execution_result, Err(error) => { - Summary::invalid(summary, Some(mode), name, error); + Summary::invalid(summary, test, error); return; } }; if result.output == self.expected { - Summary::passed_runtime( - summary, - mode, - name, - test_group, - result.cycles, - result.ergs, - result.gas, - ); + Summary::passed_runtime(summary, test, result.cycles, result.ergs, result.gas); } else { Summary::failed( summary, - mode, - name, + test, self.expected, result.output, self.calldata.inner, @@ -180,22 +174,26 @@ impl Runtime { /// /// Runs the call on REVM. /// - pub fn run_revm( + pub fn run_revm<'b>( self, summary: Arc>, - vm: Revm, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + vm: Revm<'b>, evm_version: Option, - ) -> Revm { - let name = format!("{}[{}:{}]", name_prefix, self.name, index); + context: InputContext<'_>, + ) -> Revm<'b> { + let input_index = context.selector; + let test = TestDescription::from_context( + context, + InputIdentifier::Runtime { + input_index, + name: self.name, + }, + ); // On revm we can't send a tx with a tx_origin different from the tx_sender, // this specific test expects tx_origin to be that value, so we change the sender let mut caller = self.caller; - if name_prefix == "solidity/test/libsolidity/semanticTests/state/tx_origin.sol" { + if test.selector.path == "solidity/test/libsolidity/semanticTests/state/tx_origin.sol" { caller = web3::types::Address::from_str("0x9292929292929292929292929292929292929292") .unwrap(); } @@ -219,48 +217,15 @@ impl Runtime { let result = match vm.state.transact_commit() { Ok(result) => result, Err(error) => { - match error { - EVMError::Transaction(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Transaction: {error:?}"), - ); - } - EVMError::Header(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Header: {error:?}"), - ); - } - EVMError::Database(_error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - "Error on Database", - ); - } - EVMError::Custom(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Custom: {error:?}"), - ); - } - EVMError::Precompile(error) => { - Summary::invalid( - summary.clone(), - Some(mode.clone()), - name.clone(), - format!("Error on Precompile: {error:?}"), - ); - } - } + let error_msg = match error { + EVMError::Transaction(error) => format!("Error on Transaction: {error:?}"), + EVMError::Header(error) => format!("Error on Header: {error:?}"), + EVMError::Database(_error) => "Error on Database".into(), + EVMError::Custom(error) => format!("Error on Custom: {error:?}"), + EVMError::Precompile(error) => format!("Error on Precompile: {error:?}"), + }; + + Summary::invalid(summary.clone(), test, error_msg); return vm; } }; @@ -289,18 +254,11 @@ impl Runtime { }; if output == self.expected { - Summary::passed_runtime(summary, mode, name, test_group, 0, 0, gas); + Summary::passed_runtime(summary, test, 0, 0, gas); } else if let Some(error) = error { - Summary::invalid(summary, Some(mode), name, format!("{error:?}")); + Summary::invalid(summary, test, format!("{error:?}")); } else { - Summary::failed( - summary, - mode, - name, - self.expected, - output, - self.calldata.inner, - ); + Summary::failed(summary, test, self.expected, output, self.calldata.inner); }; vm } @@ -312,15 +270,20 @@ impl Runtime { self, summary: Arc>, vm: &mut EraVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) { - let name = format!("{}[{}:{}]", name_prefix, self.name, index); + let input_index = context.selector; + let test = TestDescription::from_context( + context, + InputIdentifier::Runtime { + input_index, + name: self.name, + }, + ); + let name = test.selector.to_string(); vm.populate_storage(self.storage.inner); let result = match vm.execute_evm_interpreter::( - name.clone(), + name, self.address, self.caller, self.value, @@ -329,26 +292,17 @@ impl Runtime { ) { Ok(result) => result, Err(error) => { - Summary::invalid(summary, Some(mode), name, error); + Summary::invalid(summary, test, error); return; } }; if result.output == self.expected { - Summary::passed_runtime( - summary, - mode, - name, - test_group, - result.cycles, - result.ergs, - result.gas, - ); + Summary::passed_runtime(summary, test, result.cycles, result.ergs, result.gas); } else { Summary::failed( summary, - mode, - name, + test, self.expected, result.output, self.calldata.inner, diff --git a/compiler_tester/src/test/case/input/storage_empty.rs b/compiler_tester/src/test/case/input/storage_empty.rs index 7807a80d..dce8342f 100644 --- a/compiler_tester/src/test/case/input/storage_empty.rs +++ b/compiler_tester/src/test/case/input/storage_empty.rs @@ -5,8 +5,10 @@ use std::sync::Arc; use std::sync::Mutex; -use crate::compilers::mode::Mode; use crate::summary::Summary; +use crate::test::case::input::identifier::InputIdentifier; +use crate::test::description::TestDescription; +use crate::test::InputContext; use crate::vm::eravm::EraVM; use crate::vm::evm::EVM; use crate::vm::revm::Revm; @@ -33,29 +35,15 @@ impl StorageEmpty { /// /// Runs the storage empty check on EraVM. /// - pub fn run_eravm( - self, - summary: Arc>, - vm: &EraVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, - ) { - let name = format!("{name_prefix}[#storage_empty_check:{index}]"); - + pub fn run_eravm(self, summary: Arc>, vm: &EraVM, context: InputContext<'_>) { + let input_index = context.selector; + let test = + TestDescription::from_context(context, InputIdentifier::StorageEmpty { input_index }); let found = vm.is_storage_empty(); if found == self.is_empty { - Summary::passed_special(summary, mode, name, test_group); + Summary::passed_special(summary, test); } else { - Summary::failed( - summary, - mode, - name, - self.is_empty.into(), - found.into(), - vec![], - ); + Summary::failed(summary, test, self.is_empty.into(), found.into(), vec![]); } } @@ -66,10 +54,7 @@ impl StorageEmpty { self, _summary: Arc>, _vm: &EVM, - _mode: Mode, - _test_group: Option, - _name_prefix: String, - _index: usize, + _context: InputContext<'_>, ) { todo!() } @@ -77,17 +62,10 @@ impl StorageEmpty { /// /// Runs the storage empty check on REVM. /// - pub fn run_revm( - self, - summary: Arc>, - vm: &mut Revm, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, - ) { - let name = format!("{name_prefix}[#storage_empty_check:{index}]"); - + pub fn run_revm(self, summary: Arc>, vm: &mut Revm, context: InputContext<'_>) { + let input_index = context.selector; + let test = + TestDescription::from_context(context, InputIdentifier::StorageEmpty { input_index }); let mut is_empty = true; for cache_account in vm.state.db().cache.accounts.values() { let plain_account = cache_account.clone().account; @@ -101,16 +79,9 @@ impl StorageEmpty { } if is_empty == self.is_empty { - Summary::passed_special(summary, mode, name, test_group); + Summary::passed_special(summary, test); } else { - Summary::failed( - summary, - mode, - name, - self.is_empty.into(), - is_empty.into(), - vec![], - ); + Summary::failed(summary, test, self.is_empty.into(), is_empty.into(), vec![]); } } @@ -121,25 +92,16 @@ impl StorageEmpty { self, summary: Arc>, vm: &EraVM, - mode: Mode, - test_group: Option, - name_prefix: String, - index: usize, + context: InputContext<'_>, ) { - let name = format!("{name_prefix}[#storage_empty_check:{index}]"); - + let input_index = context.selector; + let test = + TestDescription::from_context(context, InputIdentifier::StorageEmpty { input_index }); let found = vm.is_storage_empty(); if found == self.is_empty { - Summary::passed_special(summary, mode, name, test_group); + Summary::passed_special(summary, test); } else { - Summary::failed( - summary, - mode, - name, - self.is_empty.into(), - found.into(), - vec![], - ); + Summary::failed(summary, test, self.is_empty.into(), found.into(), vec![]); } } } diff --git a/compiler_tester/src/test/case/mod.rs b/compiler_tester/src/test/case/mod.rs index 8f9d0ba4..ba7803d7 100644 --- a/compiler_tester/src/test/case/mod.rs +++ b/compiler_tester/src/test/case/mod.rs @@ -19,6 +19,9 @@ use crate::vm::revm::Revm; use self::input::Input; +use super::CaseContext; +use super::InputContext; + /// /// The test case. /// @@ -100,28 +103,17 @@ impl Case { self, summary: Arc>, mut vm: EraVM, - mode: &Mode, - test_name: String, - test_group: Option, + context: &CaseContext, ) where D: EraVMDeployer, { - let name = if let Some(case_name) = self.name { - format!("{test_name}::{case_name}") - } else { - test_name - }; - for (index, input) in self.inputs.into_iter().enumerate() { - input.run_eravm::<_, M>( - summary.clone(), - &mut vm, - mode.to_owned(), - &mut D::new(), - test_group.clone(), - name.clone(), - index, - ) + let context = InputContext { + case_context: context, + case_name: &self.name, + selector: index, + }; + input.run_eravm::<_, M>(summary.clone(), &mut vm, &mut D::new(), context) } } @@ -132,25 +124,15 @@ impl Case { self, summary: Arc>, mut vm: EVM, - mode: &Mode, - test_name: String, - test_group: Option, + context: &CaseContext, ) { - let name = if let Some(case_name) = self.name { - format!("{test_name}::{case_name}") - } else { - test_name - }; - for (index, input) in self.inputs.into_iter().enumerate() { - input.run_evm_emulator( - summary.clone(), - &mut vm, - mode.clone(), - test_group.clone(), - name.clone(), - index, - ) + let context = InputContext { + case_context: context, + case_name: &self.name, + selector: index, + }; + input.run_evm_emulator(summary.clone(), &mut vm, context) } } @@ -160,28 +142,17 @@ impl Case { pub fn run_revm( self, summary: Arc>, - mode: &Mode, - test_name: String, - test_group: Option, evm_version: Option, + context: &CaseContext, ) { - let name = if let Some(case_name) = self.name { - format!("{test_name}::{case_name}") - } else { - test_name - }; - let mut vm = Revm::new(); for (index, input) in self.inputs.into_iter().enumerate() { - vm = input.run_revm( - summary.clone(), - vm, - mode.clone(), - test_group.clone(), - name.clone(), - index, - evm_version, - ) + let context = InputContext { + case_context: context, + case_name: &self.name, + selector: index, + }; + vm = input.run_revm(summary.clone(), vm, evm_version, context) } } @@ -192,29 +163,19 @@ impl Case { self, summary: Arc>, mut vm: EraVM, - mode: &Mode, - test_name: String, - test_group: Option, + context: &CaseContext<'_>, ) where D: EraVMDeployer, { - let name = if let Some(case_name) = self.name { - format!("{test_name}::{case_name}") - } else { - test_name - }; - for (index, input) in self.inputs.into_iter().enumerate() { vm.increment_evm_block_number_and_timestamp(); - input.run_evm_interpreter::<_, M>( - summary.clone(), - &mut vm, - mode.clone(), - &mut D::new(), - test_group.clone(), - name.clone(), - index, - ) + + let context = InputContext { + case_context: context, + case_name: &self.name, + selector: index, + }; + input.run_evm_interpreter::<_, M>(summary.clone(), &mut vm, &mut D::new(), context) } } } diff --git a/compiler_tester/src/test/context/case.rs b/compiler_tester/src/test/context/case.rs new file mode 100644 index 00000000..079412c6 --- /dev/null +++ b/compiler_tester/src/test/context/case.rs @@ -0,0 +1,15 @@ +//! +//! Context used to process test cases, consisting of a number of inputs. +//! + +use crate::Mode; + +/// +/// Context used to process test cases, consisting of a number of inputs. +/// +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CaseContext<'a> { + pub mode: &'a Mode, + pub group: &'a Option, + pub name: &'a str, +} diff --git a/compiler_tester/src/test/context/input.rs b/compiler_tester/src/test/context/input.rs new file mode 100644 index 00000000..cd710eae --- /dev/null +++ b/compiler_tester/src/test/context/input.rs @@ -0,0 +1,18 @@ +//! +//! Context used to process test inputs, organized in test cases. +//! + +use super::case::CaseContext; + +/// +/// Context used to process test inputs, organized in test cases. +/// +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct InputContext<'a> { + /// Context of the parent case, which contains this input. + pub case_context: &'a CaseContext<'a>, + /// Optional name of the case. + pub case_name: &'a Option, + /// Index of the input in the case's array of inputs. + pub selector: usize, +} diff --git a/compiler_tester/src/test/context/mod.rs b/compiler_tester/src/test/context/mod.rs new file mode 100644 index 00000000..1f756d3b --- /dev/null +++ b/compiler_tester/src/test/context/mod.rs @@ -0,0 +1,2 @@ +pub mod case; +pub mod input; diff --git a/compiler_tester/src/test/description.rs b/compiler_tester/src/test/description.rs new file mode 100644 index 00000000..85755f8b --- /dev/null +++ b/compiler_tester/src/test/description.rs @@ -0,0 +1,68 @@ +//! +//! Test description with additional information such as the compiler mode and test group. +//! + +use crate::Mode; + +use crate::test::case::input::identifier::InputIdentifier; +use crate::test::context::input::InputContext; +use crate::test::selector::TestSelector; + +/// +/// Test description with additional information such as the compiler mode and test group. +/// +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TestDescription { + /// Test group. + pub group: Option, + /// Compiler mode. + pub mode: Option, + /// Test selector, matching a precise input location or a case collecting several inputs. + pub selector: TestSelector, +} + +impl TestDescription { + /// + /// Creates a test description matching specified selector with no additonal + /// information. + /// + pub fn default_for(test: TestSelector) -> Self { + Self { + group: None, + mode: None, + selector: test, + } + } + + /// + /// Erase information about mode from this selector. + /// + pub fn with_erased_mode(self) -> Self { + let Self { + group, + mode: _, + selector: identifier, + } = self; + Self { + group, + mode: None, + selector: identifier, + } + } + + /// + /// Create a selector from accumulated input context and provided input + /// identifier. + /// + pub fn from_context(ctx: InputContext<'_>, input: InputIdentifier) -> Self { + Self { + group: ctx.case_context.group.clone(), + mode: Some(ctx.case_context.mode.clone()), + selector: TestSelector { + path: ctx.case_context.name.to_string(), + case: ctx.case_name.clone(), + input: Some(input), + }, + } + } +} diff --git a/compiler_tester/src/test/mod.rs b/compiler_tester/src/test/mod.rs index 3e77b457..06430046 100644 --- a/compiler_tester/src/test/mod.rs +++ b/compiler_tester/src/test/mod.rs @@ -3,7 +3,10 @@ //! pub mod case; +pub mod context; +pub mod description; pub mod instance; +pub mod selector; use solidity_adapter::EVMVersion; use std::collections::HashMap; @@ -13,6 +16,8 @@ use std::sync::Mutex; use crate::compilers::mode::Mode; use crate::summary::Summary; use crate::test::case::Case; +use crate::test::context::case::CaseContext; +use crate::test::context::input::InputContext; use crate::vm::eravm::deployers::EraVMDeployer; use crate::vm::eravm::EraVM; use crate::vm::evm::input::build::Build as EVMBuild; @@ -72,15 +77,14 @@ impl Test { where D: EraVMDeployer, { + let context = CaseContext { + name: &self.name, + mode: &self.mode, + group: &self.group, + }; for case in self.cases { let vm = EraVM::clone_with_contracts(vm.clone(), self.eravm_builds.clone(), None); - case.run_eravm::( - summary.clone(), - vm.clone(), - &self.mode, - self.name.clone(), - self.group.clone(), - ); + case.run_eravm::(summary.clone(), vm.clone(), &context); } } @@ -97,13 +101,13 @@ impl Test { let invoker = EVMInvoker::new(&config, &resolver); let vm = EVM::new(self.evm_builds.clone(), invoker); - case.run_evm_emulator( - summary.clone(), - vm, - &self.mode, - self.name.clone(), - self.group.clone(), - ); + + let context = CaseContext { + name: &self.name, + mode: &self.mode, + group: &self.group, + }; + case.run_evm_emulator(summary.clone(), vm, &context); } } @@ -112,13 +116,12 @@ impl Test { /// pub fn run_revm(self, summary: Arc>) { for case in self.cases { - case.run_revm( - summary.clone(), - &self.mode, - self.name.clone(), - self.group.clone(), - self.evm_version, - ); + let context = CaseContext { + name: &self.name, + mode: &self.mode, + group: &self.group, + }; + case.run_revm(summary.clone(), self.evm_version, &context); } } @@ -135,13 +138,12 @@ impl Test { self.eravm_builds.clone(), self.evm_version, ); - case.run_evm_interpreter::( - summary.clone(), - vm, - &self.mode, - self.name.clone(), - self.group.clone(), - ); + let context = CaseContext { + name: &self.name, + mode: &self.mode, + group: &self.group, + }; + case.run_evm_interpreter::(summary.clone(), vm, &context); } } } diff --git a/compiler_tester/src/test/selector.rs b/compiler_tester/src/test/selector.rs new file mode 100644 index 00000000..54936d0a --- /dev/null +++ b/compiler_tester/src/test/selector.rs @@ -0,0 +1,36 @@ +//! +//! Test selector, unambiously locating a test suite, or a specific input. +//! + +use crate::test::case::input::identifier::InputIdentifier; + +/// +/// Test selector, unambiously locating a test suite, case, or input. +/// +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TestSelector { + /// Path to the file containing test. + pub path: String, + /// Name of the case, if any. `None` means nameless case. + pub case: Option, + /// Identifier of the specific input. + pub input: Option, +} + +impl std::fmt::Display for TestSelector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + path: filename, + case: case_name, + input, + } = self; + f.write_fmt(format_args!("{filename}"))?; + if let Some(case_name) = case_name { + f.write_fmt(format_args!("::{case_name}"))?; + } + if let Some(input) = input { + f.write_fmt(format_args!("[{input}]"))?; + } + Ok(()) + } +} From 15531d474b739aa3bde564c63a87b18d45775269 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Fri, 6 Dec 2024 16:30:12 +0800 Subject: [PATCH 25/31] fix(evm): supports EVMLA PUSHSIZE (#126) --- Cargo.lock | 6 +++--- benchmark_analyzer/src/lib.rs | 1 + compiler_tester/src/test/case/input/deploy_evm.rs | 9 ++++++--- compiler_tester/src/vm/revm/init.rs | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ece9db6..b40cf1ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1072,7 +1072,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c0cb2c6764f8ecbf209f75e518a4261375fb5eed" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#cad69d46f008e96909282921ce79fca6d2fe9b5a" dependencies = [ "anyhow", "clap", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "era-solc" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c0cb2c6764f8ecbf209f75e518a4261375fb5eed" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#cad69d46f008e96909282921ce79fca6d2fe9b5a" dependencies = [ "anyhow", "boolinator", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#c0cb2c6764f8ecbf209f75e518a4261375fb5eed" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#cad69d46f008e96909282921ce79fca6d2fe9b5a" dependencies = [ "anyhow", "regex", diff --git a/benchmark_analyzer/src/lib.rs b/benchmark_analyzer/src/lib.rs index 88547fa1..f18b07de 100644 --- a/benchmark_analyzer/src/lib.rs +++ b/benchmark_analyzer/src/lib.rs @@ -12,6 +12,7 @@ pub use self::benchmark::group::element::Element as BenchmarkElement; pub use self::benchmark::group::Group as BenchmarkGroup; pub use self::benchmark::metadata::Metadata; pub use self::benchmark::Benchmark; + /// /// The all elements group name. /// diff --git a/compiler_tester/src/test/case/input/deploy_evm.rs b/compiler_tester/src/test/case/input/deploy_evm.rs index d667058c..ebb9fdf6 100644 --- a/compiler_tester/src/test/case/input/deploy_evm.rs +++ b/compiler_tester/src/test/case/input/deploy_evm.rs @@ -131,11 +131,14 @@ impl DeployEVM { contract_identifier: self.identifier.clone(), }, ); + let size = self.deploy_code.len(); + let calldata = self.calldata.inner.clone(); + let mut code = self.deploy_code; + code.extend(self.calldata.inner); let vm = vm.update_deploy_balance(&self.caller); - let mut vm = - vm.fill_deploy_new_transaction(self.caller, self.value, evm_version, self.deploy_code); + let mut vm = vm.fill_deploy_new_transaction(self.caller, self.value, evm_version, code); let result = match vm.state.transact_commit() { Ok(res) => res, @@ -175,7 +178,7 @@ impl DeployEVM { } else if let Some(error) = error { Summary::invalid(summary, test, format!("{error:?}")); } else { - Summary::failed(summary, test, self.expected, output, self.calldata.inner); + Summary::failed(summary, test, self.expected, output, calldata); } vm diff --git a/compiler_tester/src/vm/revm/init.rs b/compiler_tester/src/vm/revm/init.rs index e452ceb9..4d95b39a 100644 --- a/compiler_tester/src/vm/revm/init.rs +++ b/compiler_tester/src/vm/revm/init.rs @@ -124,7 +124,7 @@ impl<'a> Revm<'a> { caller: web3::types::Address, value: Option, evm_version: Option, - deploy_code: Vec, + code: Vec, ) -> Self { let vm = self .state @@ -146,7 +146,7 @@ impl<'a> Revm<'a> { env.tx.gas_limit = evm_context.block_gas_limit; env.tx.access_list = vec![]; env.tx.caller = web3_address_to_revm_address(&caller); - env.tx.data = revm::primitives::Bytes::from(deploy_code); + env.tx.data = revm::primitives::Bytes::from(code); env.tx.value = revm::primitives::U256::from(value.unwrap_or_default()); env.tx.transact_to = TxKind::Create; }) From fdf40e27ac941b7b2429ac0d3bd612523fb2d43a Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Sat, 7 Dec 2024 03:42:30 +0800 Subject: [PATCH 26/31] fix: remove the branch replacing call(0x04) with memcopy (#127) --- .gitmodules | 2 +- Cargo.lock | 10 +++++----- compiler_tester/Cargo.toml | 8 ++++---- .../src/vm/eravm/system_contracts.rs | 10 +++++++++- system-contracts-stable-build | Bin 946341 -> 948150 bytes tests | 2 +- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.gitmodules b/.gitmodules index 72622394..faaab707 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "tests"] path = tests url = https://github.com/matter-labs/era-compiler-tests - branch = main + branch = az-identity-precompile-no-memcopy [submodule "solidity"] path = solidity url = https://github.com/ethereum/solidity diff --git a/Cargo.lock b/Cargo.lock index b40cf1ce..b2e501dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1057,7 +1057,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#050e09791561286b1faf116eb5ee4cd6a010dd9a" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=az-identity-precompile-no-memcopy#3068d634773ec2074b9baf98be694d45782a17dc" dependencies = [ "anyhow", "era-compiler-common", @@ -1072,7 +1072,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#cad69d46f008e96909282921ce79fca6d2fe9b5a" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=az-identity-precompile-no-memcopy#34093762e4f2856ae6b8bede0844e5dbff0977b2" dependencies = [ "anyhow", "clap", @@ -1099,7 +1099,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#1bf4baeafcacf72f9e11adae106c6c6b6339df51" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=az-identity-precompile-no-memcopy#1369f180ada4114a522b65221b56be9ced4006c9" dependencies = [ "anyhow", "clap", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "era-solc" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#cad69d46f008e96909282921ce79fca6d2fe9b5a" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=az-identity-precompile-no-memcopy#34093762e4f2856ae6b8bede0844e5dbff0977b2" dependencies = [ "anyhow", "boolinator", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#cad69d46f008e96909282921ce79fca6d2fe9b5a" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=az-identity-precompile-no-memcopy#34093762e4f2856ae6b8bede0844e5dbff0977b2" dependencies = [ "anyhow", "regex", diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 9fec6205..9a9eceb3 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -48,10 +48,10 @@ vm2 = { git = "https://github.com/matter-labs/vm2", optional = true, package = " era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } era-compiler-downloader = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } -era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", branch = "main" } -era-compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } -era-solc = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } -era-compiler-vyper = { git = "https://github.com/matter-labs/era-compiler-vyper", branch = "main" } +era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", branch = "az-identity-precompile-no-memcopy" } +era-compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "az-identity-precompile-no-memcopy" } +era-solc = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "az-identity-precompile-no-memcopy" } +era-compiler-vyper = { git = "https://github.com/matter-labs/era-compiler-vyper", branch = "az-identity-precompile-no-memcopy" } solidity-adapter = { path = "../solidity_adapter" } benchmark-analyzer = { path = "../benchmark_analyzer" } diff --git a/compiler_tester/src/vm/eravm/system_contracts.rs b/compiler_tester/src/vm/eravm/system_contracts.rs index fae0d6b2..faf0c818 100644 --- a/compiler_tester/src/vm/eravm/system_contracts.rs +++ b/compiler_tester/src/vm/eravm/system_contracts.rs @@ -59,6 +59,9 @@ impl SystemContracts { const PATH_SHA256: &'static str = "era-contracts/system-contracts/contracts/precompiles/SHA256.yul"; + /// The `identity` system contract implementation path. + const PATH_IDENTITY: &'static str = "tests/solidity/simple/system/identity.sol:Identity"; + /// The `ecadd` system contract implementation path. const PATH_ECADD: &'static str = "era-contracts/system-contracts/contracts/precompiles/EcAdd.yul"; @@ -194,6 +197,10 @@ impl SystemContracts { let solidity_system_contracts = vec![ (web3::types::Address::zero(), Self::PATH_EMPTY_CONTRACT), + ( + web3::types::Address::from_low_u64_be(zkevm_opcode_defs::ADDRESS_IDENTITY.into()), + Self::PATH_IDENTITY, + ), ( web3::types::Address::from_low_u64_be( zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into(), @@ -274,8 +281,9 @@ impl SystemContracts { debug_config.clone(), )?; - let mut solidity_file_paths = Vec::with_capacity(solidity_system_contracts.len() + 1); + let mut solidity_file_paths = Vec::with_capacity(solidity_system_contracts.len() + 2); for pattern in [ + "tests/solidity/simple/system/identity.sol", "era-contracts/system-contracts/contracts/*.sol", "era-contracts/system-contracts/contracts/libraries/**/*.sol", "era-contracts/system-contracts/contracts/interfaces/**/*.sol", diff --git a/system-contracts-stable-build b/system-contracts-stable-build index d6f4aaa3529dd281193915016e440d5b7ae18b5f..0da7f24c7905d54404ac764929eb52ac725d7bfe 100644 GIT binary patch delta 591 zcmZ4b&~n>*OGe?1j1DXb1_lNu3QPVUWU|=yqlGmBs z$S*&+S5RQ`0e;Qt6V5OzOxK;mA~IQkk!8|yF8`>qf_Hu{EAMZ8a7*ys(xylM!^@vl z7TW5Fo@7(bjXk-EL5mS+fuQ78=E8>xnV}^!dW1S_0@Leado{Mrbmmo*cAO8i3<4Ni zSSI%~ii&2Yq~?`mmQ?B$=jT{?LOIh9^s{MCJ}fN9nv+tN!#VxnHzvvH4I5cFryDE; z60_Vn1ST5w3rMHrf+m$)t&ypla*t-)GOAhlPCSy)$GF3?!v+d n#7scU48$NlD-g2*F*^`*05K;Ja{)0o5c2>r?{*g!zQh~=Mp=-p delta 73 zcmdn?-g4AiX1jNih%mT!$K+Fcj>_E%`#GF9P1;ik=JV4C5 KJ&2nxB?kZu4HIYp diff --git a/tests b/tests index 9c809589..9549358c 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit 9c80958998132ae8f1a6df1ba9f9db4f6c66c48f +Subproject commit 9549358c0cf1c5a4846511f08d34ff8781a39cd2 From c74eb447596091c66f446e51548ab65e56cc5883 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Sat, 7 Dec 2024 13:10:55 +0800 Subject: [PATCH 27/31] chore: update the minimal proxy contract assembly (#128) --- .gitmodules | 2 +- Cargo.lock | 28 ++++++++++++++-------------- compiler_tester/Cargo.toml | 8 ++++---- tests | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.gitmodules b/.gitmodules index faaab707..72622394 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "tests"] path = tests url = https://github.com/matter-labs/era-compiler-tests - branch = az-identity-precompile-no-memcopy + branch = main [submodule "solidity"] path = solidity url = https://github.com/ethereum/solidity diff --git a/Cargo.lock b/Cargo.lock index b2e501dc..d1953aed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -636,9 +636,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" @@ -1057,7 +1057,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=az-identity-precompile-no-memcopy#3068d634773ec2074b9baf98be694d45782a17dc" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#0902a3c9e777e1f3b22ad54c81f1a74f475a375c" dependencies = [ "anyhow", "era-compiler-common", @@ -1072,7 +1072,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=az-identity-precompile-no-memcopy#34093762e4f2856ae6b8bede0844e5dbff0977b2" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#55a36d78bbbdbb2611c13719c116a71c683474cc" dependencies = [ "anyhow", "clap", @@ -1099,7 +1099,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=az-identity-precompile-no-memcopy#1369f180ada4114a522b65221b56be9ced4006c9" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#0a9ad163aaa761af7fe7b8ccd9936ce42526402e" dependencies = [ "anyhow", "clap", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "era-solc" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=az-identity-precompile-no-memcopy#34093762e4f2856ae6b8bede0844e5dbff0977b2" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#55a36d78bbbdbb2611c13719c116a71c683474cc" dependencies = [ "anyhow", "boolinator", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=az-identity-precompile-no-memcopy#34093762e4f2856ae6b8bede0844e5dbff0977b2" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#55a36d78bbbdbb2611c13719c116a71c683474cc" dependencies = [ "anyhow", "regex", @@ -2373,14 +2373,14 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.7.0" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8781a75c6205af67215f382092b6e0a4ff3734798523e69073d4bcd294ec767b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 1.0.109", ] [[package]] @@ -3588,9 +3588,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -3623,9 +3623,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 9a9eceb3..9fec6205 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -48,10 +48,10 @@ vm2 = { git = "https://github.com/matter-labs/vm2", optional = true, package = " era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } era-compiler-downloader = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } -era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", branch = "az-identity-precompile-no-memcopy" } -era-compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "az-identity-precompile-no-memcopy" } -era-solc = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "az-identity-precompile-no-memcopy" } -era-compiler-vyper = { git = "https://github.com/matter-labs/era-compiler-vyper", branch = "az-identity-precompile-no-memcopy" } +era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", branch = "main" } +era-compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } +era-solc = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } +era-compiler-vyper = { git = "https://github.com/matter-labs/era-compiler-vyper", branch = "main" } solidity-adapter = { path = "../solidity_adapter" } benchmark-analyzer = { path = "../benchmark_analyzer" } diff --git a/tests b/tests index 9549358c..bc1cd8e1 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit 9549358c0cf1c5a4846511f08d34ff8781a39cd2 +Subproject commit bc1cd8e1ac2cf7f6ce5f5e9ec1f68188c171607a From 8d2673a04dc66e7a82c0b2a9c36cf75712a11a38 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Sat, 21 Dec 2024 02:22:18 +0800 Subject: [PATCH 28/31] release: v1.5.8 (#129) --- Cargo.lock | 584 ++++++++++++------ compiler_tester/Cargo.toml | 3 +- .../arguments => }/benchmark_format.rs | 10 +- .../{arguments/mod.rs => arguments.rs} | 7 +- compiler_tester/src/compiler_tester/main.rs | 9 +- compiler_tester/src/compilers/vyper/mod.rs | 5 +- compiler_tester/src/lib.rs | 2 + solidity_adapter/Cargo.toml | 3 +- 8 files changed, 423 insertions(+), 200 deletions(-) rename compiler_tester/src/{compiler_tester/arguments => }/benchmark_format.rs (87%) rename compiler_tester/src/compiler_tester/{arguments/mod.rs => arguments.rs} (94%) diff --git a/Cargo.lock b/Cargo.lock index d1953aed..40e98d3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db948902dfbae96a73c2fbf1f7abec62af034ab883e4c777c3fd29702bd6e2c" +checksum = "6259a506ab13e1d658796c31e6e39d2e2ee89243bcc505ddc613b35732e0a430" dependencies = [ "alloy-rlp", "bytes", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" +checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097" dependencies = [ "alloy-rlp-derive", "arrayvec 0.7.6", @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" +checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" dependencies = [ "proc-macro2", "quote", @@ -393,6 +393,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -576,9 +582,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.2" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ "shlex", ] @@ -589,6 +595,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cid" version = "0.5.1" @@ -681,7 +693,7 @@ dependencies = [ "once_cell", "rayon", "regex", - "reqwest", + "reqwest 0.11.27", "revm", "rlp", "ron", @@ -767,18 +779,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -795,9 +807,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -1028,7 +1040,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era-compiler-common" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ada30723f8315764ff965b7703159187afd668bf" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#10ea52ee6a27ae3392c25647f5f70fb60e3ce2a6" dependencies = [ "anyhow", "base58", @@ -1044,12 +1056,12 @@ dependencies = [ [[package]] name = "era-compiler-downloader" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ada30723f8315764ff965b7703159187afd668bf" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#10ea52ee6a27ae3392c25647f5f70fb60e3ce2a6" dependencies = [ "anyhow", "colored", "openssl", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", ] @@ -1057,7 +1069,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#0902a3c9e777e1f3b22ad54c81f1a74f475a375c" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#564e41b4d9e0b6a3a0d2f875c6dd891477cef70c" dependencies = [ "anyhow", "era-compiler-common", @@ -1072,7 +1084,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#55a36d78bbbdbb2611c13719c116a71c683474cc" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#901dc4ef0ca77a9edda10b2af1c08e8c0f00c054" dependencies = [ "anyhow", "clap", @@ -1092,16 +1104,17 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "thiserror", + "thiserror 1.0.64", "zkevm_opcode_defs", ] [[package]] name = "era-compiler-vyper" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#0a9ad163aaa761af7fe7b8ccd9936ce42526402e" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#7ac06bd910b64343216d65b9251383d9c0d24d33" dependencies = [ "anyhow", + "boolinator", "clap", "era-compiler-common", "era-compiler-llvm-context", @@ -1122,7 +1135,7 @@ dependencies = [ [[package]] name = "era-solc" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#55a36d78bbbdbb2611c13719c116a71c683474cc" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#901dc4ef0ca77a9edda10b2af1c08e8c0f00c054" dependencies = [ "anyhow", "boolinator", @@ -1139,12 +1152,12 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#55a36d78bbbdbb2611c13719c116a71c683474cc" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#901dc4ef0ca77a9edda10b2af1c08e8c0f00c054" dependencies = [ "anyhow", "regex", "serde", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -1170,7 +1183,7 @@ dependencies = [ "serde", "serde_json", "sha3 0.10.8", - "thiserror", + "thiserror 1.0.64", "uint", ] @@ -1227,9 +1240,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fastrlp" @@ -1242,6 +1255,17 @@ dependencies = [ "bytes", ] +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.0" @@ -1284,9 +1308,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -1431,8 +1455,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1469,7 +1495,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -1493,30 +1519,6 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" -[[package]] -name = "headers" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.5.0" @@ -1552,11 +1554,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1570,6 +1572,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1577,7 +1590,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.2.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1595,17 +1631,17 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1617,18 +1653,41 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http", - "hyper", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", + "webpki-roots", ] [[package]] @@ -1638,12 +1697,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.32", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -1762,16 +1840,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -1853,7 +1921,7 @@ dependencies = [ "once_cell", "parking_lot", "serde", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -1927,9 +1995,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", @@ -1994,9 +2062,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.167" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libm" @@ -2095,9 +2163,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -2435,12 +2503,12 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.8", "ucd-trie", ] @@ -2595,6 +2663,58 @@ dependencies = [ "byteorder", ] +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.8", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.8", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -2671,9 +2791,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -2719,16 +2839,15 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", "hyper-tls", "ipnet", "js-sys", @@ -2738,15 +2857,54 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", "tokio-rustls", "tower-service", "url", @@ -2754,7 +2912,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots", - "winreg", + "windows-registry", ] [[package]] @@ -2867,7 +3025,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64", + "base64 0.21.7", "bitflags 2.6.0", "serde", "serde_derive", @@ -2875,16 +3033,18 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +checksum = "f5ef8fb1dd8de3870cb8400d51b4c2023854bbafd5431a3ac7e7317243e22d2f" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", "ark-ff 0.4.2", "bytes", - "fastrlp", + "fastrlp 0.3.1", + "fastrlp 0.4.0", "num-bigint", + "num-integer", "num-traits", "parity-scale-codec", "primitive-types", @@ -2941,27 +3101,29 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ - "log", + "once_cell", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] @@ -2970,16 +3132,35 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +dependencies = [ + "web-time", ] [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -3053,16 +3234,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sec1" version = "0.7.3" @@ -3079,11 +3250,11 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.27.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ - "secp256k1-sys 0.8.1", + "secp256k1-sys 0.9.2", ] [[package]] @@ -3098,9 +3269,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.8.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] @@ -3129,9 +3300,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -3254,17 +3425,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - [[package]] name = "sha2" version = "0.9.9" @@ -3472,6 +3632,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -3529,7 +3698,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +dependencies = [ + "thiserror-impl 2.0.8", ] [[package]] @@ -3543,6 +3721,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "thiserror-impl" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -3613,9 +3802,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", "tokio", @@ -3735,27 +3924,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-xid" version = "0.2.6" @@ -3793,7 +3967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 1.0.3", + "idna", "percent-encoding", ] @@ -3865,9 +4039,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -3876,13 +4050,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.90", @@ -3891,9 +4064,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.47" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", @@ -3904,9 +4077,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3914,9 +4087,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -3927,45 +4100,53 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "web3" -version = "0.19.0" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5388522c899d1e1c96a4c307e3797e0f697ba7c77dd8e0e625ecba9dd0342937" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web3" +version = "0.20.0" +source = "git+https://github.com/jacques-kigo/rust-web3?branch=master#61c8dd5c23f89cc5f7cc365c894b69500fba8993" dependencies = [ "arrayvec 0.7.6", - "base64", + "base64 0.22.1", "bytes", "derive_more 0.99.18", "ethabi", "ethereum-types", "futures", "futures-timer", - "headers", "hex", - "idna 0.4.0", + "idna", "jsonrpc-core", "log", "once_cell", "parking_lot", "pin-project", - "reqwest", + "reqwest 0.12.9", "rlp", - "secp256k1 0.27.0", + "secp256k1 0.28.2", "serde", "serde_json", "tiny-keccak", @@ -3974,9 +4155,12 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "which" @@ -3990,6 +4174,36 @@ dependencies = [ "winsafe", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 9fec6205..5f573431 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -57,7 +57,8 @@ solidity-adapter = { path = "../solidity_adapter" } benchmark-analyzer = { path = "../benchmark_analyzer" } [dependencies.web3] -version = "=0.19.0" +git = "https://github.com/jacques-kigo/rust-web3" +branch = "master" default-features = false features = ["http-rustls-tls", "test", "signing"] diff --git a/compiler_tester/src/compiler_tester/arguments/benchmark_format.rs b/compiler_tester/src/benchmark_format.rs similarity index 87% rename from compiler_tester/src/compiler_tester/arguments/benchmark_format.rs rename to compiler_tester/src/benchmark_format.rs index d5e74081..28002bbb 100644 --- a/compiler_tester/src/compiler_tester/arguments/benchmark_format.rs +++ b/compiler_tester/src/benchmark_format.rs @@ -1,8 +1,16 @@ +//! +//! Output format for benchmark data. +//! + +/// /// Output format for benchmark data. -#[derive(Clone, Debug, Default, Eq, PartialEq)] +/// +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub enum BenchmarkFormat { #[default] + /// JSON format. Json, + /// CSV format. Csv, } diff --git a/compiler_tester/src/compiler_tester/arguments/mod.rs b/compiler_tester/src/compiler_tester/arguments.rs similarity index 94% rename from compiler_tester/src/compiler_tester/arguments/mod.rs rename to compiler_tester/src/compiler_tester/arguments.rs index c2335c15..45e912e7 100644 --- a/compiler_tester/src/compiler_tester/arguments/mod.rs +++ b/compiler_tester/src/compiler_tester/arguments.rs @@ -4,11 +4,8 @@ use std::path::PathBuf; -use benchmark_format::BenchmarkFormat; use clap::Parser; -pub mod benchmark_format; - /// /// The compiler tester arguments. /// @@ -44,8 +41,8 @@ pub struct Arguments { pub benchmark: Option, /// The benchmark output format. - #[structopt(long = "benchmark-format", default_value_t = BenchmarkFormat::Json)] - pub benchmark_format: BenchmarkFormat, + #[structopt(long = "benchmark-format", default_value_t = compiler_tester::BenchmarkFormat::Json)] + pub benchmark_format: compiler_tester::BenchmarkFormat, /// Sets the number of threads, which execute the tests concurrently. #[structopt(short, long)] diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index fa15a793..5735712c 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -8,7 +8,6 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::Instant; -use arguments::benchmark_format::BenchmarkFormat; use clap::Parser; use colored::Colorize; @@ -224,10 +223,10 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { if let Some(path) = arguments.benchmark { let benchmark = summary.benchmark(toolchain)?; match arguments.benchmark_format { - BenchmarkFormat::Json => { + compiler_tester::BenchmarkFormat::Json => { benchmark.write_to_file(path, benchmark_analyzer::JsonSerializer)? } - BenchmarkFormat::Csv => { + compiler_tester::BenchmarkFormat::Csv => { benchmark.write_to_file(path, benchmark_analyzer::CsvSerializer)? } } @@ -244,7 +243,7 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { mod tests { use std::path::PathBuf; - use crate::arguments::{benchmark_format::BenchmarkFormat, Arguments}; + use crate::arguments::Arguments; #[test] fn test_manually() { @@ -258,7 +257,7 @@ mod tests { path: vec!["tests/solidity/simple/default.sol".to_owned()], group: vec![], benchmark: None, - benchmark_format: BenchmarkFormat::Json, + benchmark_format: compiler_tester::BenchmarkFormat::Json, threads: Some(1), dump_system: false, disable_deployer: false, diff --git a/compiler_tester/src/compilers/vyper/mod.rs b/compiler_tester/src/compilers/vyper/mod.rs index f1ae1747..a110b797 100644 --- a/compiler_tester/src/compilers/vyper/mod.rs +++ b/compiler_tester/src/compilers/vyper/mod.rs @@ -132,11 +132,12 @@ impl VyperCompiler { &mode.vyper_version, paths, &[ - era_compiler_vyper::VyperSelection::IRJson, - era_compiler_vyper::VyperSelection::EraVMAssembly, + era_compiler_vyper::VyperSelector::IRJson, + era_compiler_vyper::VyperSelector::EraVMAssembly, ], evm_version, true, + None, mode.vyper_optimize, ) } diff --git a/compiler_tester/src/lib.rs b/compiler_tester/src/lib.rs index ff5c2a57..c177a3e0 100644 --- a/compiler_tester/src/lib.rs +++ b/compiler_tester/src/lib.rs @@ -7,6 +7,7 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] +pub(crate) mod benchmark_format; pub(crate) mod compilers; pub(crate) mod directories; pub(crate) mod environment; @@ -26,6 +27,7 @@ use itertools::Itertools; use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; +pub use crate::benchmark_format::BenchmarkFormat; pub use crate::compilers::eravm::EraVMCompiler; pub use crate::compilers::llvm::LLVMCompiler; pub use crate::compilers::mode::llvm_options::LLVMOptions; diff --git a/solidity_adapter/Cargo.toml b/solidity_adapter/Cargo.toml index 6d35c09d..5dd255ac 100644 --- a/solidity_adapter/Cargo.toml +++ b/solidity_adapter/Cargo.toml @@ -30,6 +30,7 @@ md5 = "=0.7.0" era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } [dependencies.web3] -version = "=0.19.0" +git = "https://github.com/jacques-kigo/rust-web3" +branch = "master" default-features = false features = ["http-rustls-tls", "test", "signing"] From a98fc016b0075c694a09413dcaa0326cfb62807a Mon Sep 17 00:00:00 2001 From: Igor Zhirkov Date: Mon, 6 Jan 2025 16:23:23 +0100 Subject: [PATCH 29/31] feat: LNT support and comparison of custom benchmarking groups (#130) --- Cargo.lock | 152 +++++++--- README.md | 28 +- benchmark_analyzer/Cargo.toml | 5 + .../src/analysis/evm_interpreter/mod.rs | 59 ++++ benchmark_analyzer/src/analysis/mod.rs | 277 ++++++++++++++++++ .../src/benchmark/format/csv.rs | 71 ----- .../src/benchmark/format/json.rs | 18 -- .../src/benchmark/format/mod.rs | 15 - benchmark_analyzer/src/benchmark/group/mod.rs | 211 ------------- benchmark_analyzer/src/benchmark/metadata.rs | 29 -- benchmark_analyzer/src/benchmark/mod.rs | 232 --------------- .../src/benchmark_analyzer/arguments.rs | 8 + .../src/benchmark_analyzer/main.rs | 44 ++- benchmark_analyzer/src/lib.rs | 42 ++- .../src/model/benchmark/metadata.rs | 36 +++ benchmark_analyzer/src/model/benchmark/mod.rs | 78 +++++ .../src/model/benchmark/test/codegen/mod.rs | 26 ++ .../codegen/versioned/executable/metadata.rs | 12 + .../test/codegen/versioned/executable/mod.rs | 26 ++ .../codegen/versioned/executable/run}/mod.rs | 24 +- .../benchmark/test/codegen/versioned/mod.rs | 27 ++ .../element => model/benchmark/test}/input.rs | 24 ++ .../src/model/benchmark/test/metadata.rs | 29 ++ .../src/model/benchmark/test/mod.rs | 44 +++ .../benchmark/test}/selector.rs | 4 +- benchmark_analyzer/src/model/context/mod.rs | 35 +++ .../src/model/evm_interpreter.rs | 148 ++++++++++ benchmark_analyzer/src/model/mod.rs | 7 + .../src/output/comparison_result.rs | 16 + benchmark_analyzer/src/output/file.rs | 15 + benchmark_analyzer/src/output/format/csv.rs | 88 ++++++ .../format/json/lnt/benchmark/machine.rs | 20 ++ .../output/format/json/lnt/benchmark/mod.rs | 30 ++ .../json/lnt/benchmark/run_description.rs | 17 ++ .../json/lnt/benchmark/test_description.rs | 18 ++ .../src/output/format/json/lnt/error.rs | 25 ++ .../src/output/format/json/lnt/mod.rs | 127 ++++++++ .../src/output/format/json/mod.rs | 20 ++ .../src/output/format/json/native/mod.rs | 21 ++ benchmark_analyzer/src/output/format/mod.rs | 6 + benchmark_analyzer/src/output/mod.rs | 26 ++ benchmark_analyzer/src/results/group.rs | 175 +++++++++++ .../group/results.rs => results/mod.rs} | 96 +++--- benchmark_analyzer/src/util/btreemap.rs | 162 ++++++++++ benchmark_analyzer/src/util/mod.rs | 5 + compiler_tester/Cargo.toml | 1 + .../arguments}/benchmark_format.rs | 6 +- .../{arguments.rs => arguments/mod.rs} | 18 +- .../compiler_tester/arguments/validation.rs | 28 ++ compiler_tester/src/compiler_tester/main.rs | 78 +++-- compiler_tester/src/compilers/eravm/mode.rs | 15 + compiler_tester/src/compilers/llvm/mode.rs | 17 +- compiler_tester/src/compilers/mode/imode.rs | 31 ++ compiler_tester/src/compilers/mode/mod.rs | 52 +++- .../src/compilers/solidity/mode.rs | 33 ++- .../src/compilers/solidity/upstream/mode.rs | 41 +-- compiler_tester/src/compilers/vyper/mode.rs | 25 +- compiler_tester/src/compilers/yul/mode.rs | 15 +- .../src/compilers/yul/mode_upstream.rs | 25 +- .../src/directories/matter_labs/test/mod.rs | 25 +- compiler_tester/src/lib.rs | 2 - .../src/summary/benchmark_adapters/input.rs | 3 + .../summary/benchmark_adapters/metadata.rs | 49 +--- .../src/summary/benchmark_adapters/mod.rs | 1 + .../src/summary/benchmark_adapters/mode.rs | 36 +++ compiler_tester/src/summary/mod.rs | 180 +++++++----- .../src/test/case/input/identifier.rs | 5 + .../src/test/case/input/runtime.rs | 19 +- compiler_tester/src/test/context/mod.rs | 5 + .../src/{utils.rs => utils/mod.rs} | 2 + compiler_tester/src/utils/timer.rs | 115 ++++++++ 71 files changed, 2457 insertions(+), 948 deletions(-) create mode 100644 benchmark_analyzer/src/analysis/evm_interpreter/mod.rs create mode 100644 benchmark_analyzer/src/analysis/mod.rs delete mode 100644 benchmark_analyzer/src/benchmark/format/csv.rs delete mode 100644 benchmark_analyzer/src/benchmark/format/json.rs delete mode 100644 benchmark_analyzer/src/benchmark/format/mod.rs delete mode 100644 benchmark_analyzer/src/benchmark/group/mod.rs delete mode 100644 benchmark_analyzer/src/benchmark/metadata.rs delete mode 100644 benchmark_analyzer/src/benchmark/mod.rs create mode 100644 benchmark_analyzer/src/model/benchmark/metadata.rs create mode 100644 benchmark_analyzer/src/model/benchmark/mod.rs create mode 100644 benchmark_analyzer/src/model/benchmark/test/codegen/mod.rs create mode 100644 benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/metadata.rs create mode 100644 benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/mod.rs rename benchmark_analyzer/src/{benchmark/group/element => model/benchmark/test/codegen/versioned/executable/run}/mod.rs (56%) create mode 100644 benchmark_analyzer/src/model/benchmark/test/codegen/versioned/mod.rs rename benchmark_analyzer/src/{benchmark/group/element => model/benchmark/test}/input.rs (72%) create mode 100644 benchmark_analyzer/src/model/benchmark/test/metadata.rs create mode 100644 benchmark_analyzer/src/model/benchmark/test/mod.rs rename benchmark_analyzer/src/{benchmark/group/element => model/benchmark/test}/selector.rs (88%) create mode 100644 benchmark_analyzer/src/model/context/mod.rs create mode 100644 benchmark_analyzer/src/model/evm_interpreter.rs create mode 100644 benchmark_analyzer/src/model/mod.rs create mode 100644 benchmark_analyzer/src/output/comparison_result.rs create mode 100644 benchmark_analyzer/src/output/file.rs create mode 100644 benchmark_analyzer/src/output/format/csv.rs create mode 100644 benchmark_analyzer/src/output/format/json/lnt/benchmark/machine.rs create mode 100644 benchmark_analyzer/src/output/format/json/lnt/benchmark/mod.rs create mode 100644 benchmark_analyzer/src/output/format/json/lnt/benchmark/run_description.rs create mode 100644 benchmark_analyzer/src/output/format/json/lnt/benchmark/test_description.rs create mode 100644 benchmark_analyzer/src/output/format/json/lnt/error.rs create mode 100644 benchmark_analyzer/src/output/format/json/lnt/mod.rs create mode 100644 benchmark_analyzer/src/output/format/json/mod.rs create mode 100644 benchmark_analyzer/src/output/format/json/native/mod.rs create mode 100644 benchmark_analyzer/src/output/format/mod.rs create mode 100644 benchmark_analyzer/src/output/mod.rs create mode 100644 benchmark_analyzer/src/results/group.rs rename benchmark_analyzer/src/{benchmark/group/results.rs => results/mod.rs} (86%) create mode 100644 benchmark_analyzer/src/util/btreemap.rs create mode 100644 benchmark_analyzer/src/util/mod.rs rename compiler_tester/src/{ => compiler_tester/arguments}/benchmark_format.rs (82%) rename compiler_tester/src/compiler_tester/{arguments.rs => arguments/mod.rs} (84%) create mode 100644 compiler_tester/src/compiler_tester/arguments/validation.rs create mode 100644 compiler_tester/src/compilers/mode/imode.rs create mode 100644 compiler_tester/src/summary/benchmark_adapters/mode.rs rename compiler_tester/src/{utils.rs => utils/mod.rs} (99%) create mode 100644 compiler_tester/src/utils/timer.rs diff --git a/Cargo.lock b/Cargo.lock index 40e98d3c..d576cfbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,7 +127,22 @@ checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", ] [[package]] @@ -345,7 +360,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -410,8 +425,11 @@ name = "benchmark-analyzer" version = "1.5.0" dependencies = [ "anyhow", + "chrono", "clap", "colored", + "era-compiler-common", + "regex", "serde", "serde_json", ] @@ -601,6 +619,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "cid" version = "0.5.1" @@ -643,7 +676,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -675,6 +708,7 @@ dependencies = [ "anyhow", "benchmark-analyzer", "bincode", + "chrono", "clap", "colored", "era-compiler-common", @@ -897,7 +931,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -917,7 +951,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "unicode-xid", ] @@ -950,7 +984,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1017,7 +1051,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1028,7 +1062,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1398,7 +1432,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1722,6 +1756,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -1837,7 +1894,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1896,7 +1953,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1931,7 +1988,7 @@ source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c5d783 dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2335,14 +2392,14 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -2382,7 +2439,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2508,7 +2565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.8", + "thiserror 2.0.9", "ucd-trie", ] @@ -2529,7 +2586,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2676,7 +2733,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.8", + "thiserror 2.0.9", "tokio", "tracing", ] @@ -2695,7 +2752,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.8", + "thiserror 2.0.9", "tinyvec", "tracing", "web-time", @@ -2712,7 +2769,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3216,7 +3273,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3361,7 +3418,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3617,9 +3674,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", @@ -3649,7 +3706,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3703,11 +3760,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.8" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl 2.0.8", + "thiserror-impl 2.0.9", ] [[package]] @@ -3718,18 +3775,18 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] name = "thiserror-impl" -version = "2.0.8" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3762,9 +3819,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -3876,7 +3933,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4058,7 +4115,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "wasm-bindgen-shared", ] @@ -4093,7 +4150,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4174,6 +4231,15 @@ dependencies = [ "winsafe", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-registry" version = "0.2.0" @@ -4427,7 +4493,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "synstructure", ] @@ -4449,7 +4515,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4469,7 +4535,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "synstructure", ] @@ -4490,7 +4556,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4512,7 +4578,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] diff --git a/README.md b/README.md index 0993fad5..76142535 100644 --- a/README.md +++ b/README.md @@ -296,10 +296,36 @@ cargo run --release --bin benchmark-analyzer -- --reference reference.json --can After you make any changes in LLVM, you only need to repeat steps 2-3 to update the working branch benchmark data. +### Comparing results + +By default, benchmark analyzer compares tests from groups with the same name, which means that every test should be compiled with the same codegen and optimizations. +To compare two groups with different names, use the options `--query-reference` and `--query-candidate`. Then, use benchmark analyzer: + +```shell +cargo run --release --bin benchmark-analyzer -- --reference reference.json --candidate candidate.json --query-reference "M0B0" --query-candidate "M3B3" +``` + +The queries are regular expressions, and the group name, codegen, and +optimization options are matched against it. + + + ### Report formats -Use the parameter `--benchmark-format` to select the output format: `json` (default), or `csv`. +Use the parameter `--benchmark-format` of compiler tester to select the output format: `json` (default), `csv`, or `json-lnt`. + +If `json-lnt` format is selected: +1. The benchmark report will consist of multiple files. They will be placed in the directory provided via the `--output` argument. +2. It is mandatory to pass a JSON file with additional information using `--benchmark-context`. Here is a minimal example: + +```json +{ + "machine": "some_machine", + "target": "some_target", + "toolchain": "some_solc_type" +} +``` ## Troubleshooting diff --git a/benchmark_analyzer/Cargo.toml b/benchmark_analyzer/Cargo.toml index 4e23ff99..1ee1078b 100644 --- a/benchmark_analyzer/Cargo.toml +++ b/benchmark_analyzer/Cargo.toml @@ -3,6 +3,7 @@ name = "benchmark-analyzer" version = "1.5.0" authors = [ "Oleksandr Zarudnyi ", + "Igor Zhirkov ", ] license = "MIT OR Apache-2.0" edition = "2021" @@ -16,6 +17,10 @@ path = "src/benchmark_analyzer/main.rs" clap = { version = "=4.5.21", features = ["derive"] } anyhow = "=1.0.89" colored = "=2.1.0" +regex = "=1.11.0" serde = { version = "=1.0.210", features = [ "derive" ] } serde_json = "=1.0.128" +chrono = { version = "=0.4.38", features = [ "serde", "clock" ] } + +era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } diff --git a/benchmark_analyzer/src/analysis/evm_interpreter/mod.rs b/benchmark_analyzer/src/analysis/evm_interpreter/mod.rs new file mode 100644 index 00000000..83e39a43 --- /dev/null +++ b/benchmark_analyzer/src/analysis/evm_interpreter/mod.rs @@ -0,0 +1,59 @@ +//! +//! Collects definitions related to the analysis of EVM interpreter tests. +//! + +use std::collections::BTreeMap; + +use crate::model::benchmark::test::codegen::versioned::executable::run::Run; +use crate::model::benchmark::test::metadata::Metadata as TestMetadata; +use crate::model::evm_interpreter::OPCODES; +use crate::results::group::Group; + +const OPTIMIZE_FOR_CYCLES: &str = "+M3B3"; + +/// +/// Returns `true` if the group collects measurements of EVM Interpreter tests +/// compiled for maximum performance. +/// +pub fn is_evm_interpreter_cycles_tests_group(group: &Group<'_>) -> bool { + matches!( + group, + Group::EVMInterpreter { + optimizations: OPTIMIZE_FOR_CYCLES, + .. + } + ) +} + +/// +/// Returns the EVM interpreter ergs/gas ratio for every EVM bytecode. +/// +pub fn opcode_cost_ratios<'a>( + group: &BTreeMap<&'a str, (&'a TestMetadata, &'a Run)>, +) -> Vec<(String, f64)> { + let mut results = Vec::new(); + + for evm_opcode in OPCODES.into_iter() { + // Case name corresponds to the EVM bytecode + // Collect three last #fallback's to get the gas and ergs measurements + let runs = group + .values() + .filter_map(|(metadata, run)| match &metadata.selector.case { + Some(case) if case == evm_opcode => match &metadata.selector.input { + Some(input) if input.is_fallback() => Some(*run), + _ => None, + }, + _ => None, + }) + .collect::>(); + let [_skip, full, template]: [&'a Run; 3] = runs + .try_into() + .unwrap_or_else(|_| panic!("Failed to extract three #fallback tests from the EVM interpreter tests attributed to the opcode {evm_opcode}")); + + let ergs_difference = full.ergs as i64 - template.ergs as i64; + let gas_difference = full.gas as i64 - template.gas as i64; + let ergs_gas_ratio = (ergs_difference as f64) / (gas_difference as f64); + results.push((evm_opcode.to_owned(), ergs_gas_ratio)); + } + results +} diff --git a/benchmark_analyzer/src/analysis/mod.rs b/benchmark_analyzer/src/analysis/mod.rs new file mode 100644 index 00000000..0dc7024b --- /dev/null +++ b/benchmark_analyzer/src/analysis/mod.rs @@ -0,0 +1,277 @@ +//! +//! Provides tools for collecting and comparing benchmark results. +//! + +pub mod evm_interpreter; + +use std::collections::BTreeMap; + +use evm_interpreter::is_evm_interpreter_cycles_tests_group; +use evm_interpreter::opcode_cost_ratios; + +use crate::model::benchmark::test::codegen::versioned::executable::run::Run; +use crate::model::benchmark::test::metadata::Metadata as TestMetadata; +use crate::model::benchmark::Benchmark; +use crate::results::group::Group; +use crate::results::Results; +use crate::util::btreemap::cross_join_filter_map; +use crate::util::btreemap::intersect_keys; +use crate::util::btreemap::intersect_map; + +type GroupRuns<'a> = BTreeMap<&'a str, (&'a TestMetadata, &'a Run)>; + +/// +/// Collects measurements from a benchmark into groups. +/// Groups may intersect. +/// +fn collect_runs(benchmark: &Benchmark) -> BTreeMap, GroupRuns> { + let mut result: BTreeMap, GroupRuns> = BTreeMap::new(); + + for (test_identifier, test) in &benchmark.tests { + for (codegen, codegen_group) in &test.codegen_groups { + for versioned_group in codegen_group.versioned_groups.values() { + for (mode, executable) in &versioned_group.executables { + for tag in test + .metadata + .tags + .iter() + .map(Some) + .chain(std::iter::once(None)) + { + let tag = tag.map(|t| t.as_str()); + result + .entry(Group::from_tag(tag, Some(codegen), Some(mode))) + .or_default() + .insert(test_identifier.as_str(), (&test.metadata, &executable.run)); + } + } + } + } + } + + result +} + +/// +/// Compare two benchmark runs [reference] and [candidate] by groups. +/// Every resulting group is either: +/// - a result of comparison of a group from [reference] with a group from [candidate] sharing the same name +/// - or a result of comparing two distinct groups from [reference] and +/// [candidate] for which [custom_group_comparisons] returned `true`. +/// +pub fn compare<'a>( + reference: &'a Benchmark, + candidate: &'a Benchmark, + custom_group_comparisons: impl Fn(&Group, &Group) -> bool, +) -> Vec<(Group<'a>, Results<'a>)> { + let groups = { + let reference_runs: BTreeMap, GroupRuns<'a>> = collect_runs(reference); + let candidate_runs: BTreeMap, GroupRuns<'a>> = collect_runs(candidate); + + let comparisons: Vec<(Group<'a>, GroupRuns<'a>, GroupRuns<'a>)> = + cross_join_filter_map(&reference_runs, &candidate_runs, |g1, g2| { + if custom_group_comparisons(g1, g2) { + Some(Group::new_comparison(g1, g2)) + } else { + None + } + }); + + intersect_keys(reference_runs, candidate_runs).chain(comparisons) + }; + + let results: Vec<(Group<'_>, Results<'_>)> = groups + .map(|(group_name, reference_tests, candidate_tests)| { + let ratios = if is_evm_interpreter_cycles_tests_group(&group_name) { + Some(( + opcode_cost_ratios(&reference_tests), + opcode_cost_ratios(&candidate_tests), + )) + } else { + None + }; + + let runs: Vec<(&TestMetadata, &Run, &Run)> = intersect_map( + reference_tests, + candidate_tests, + |_id, (metadata, run_reference), (_, run_candidate)| { + (metadata, run_reference, run_candidate) + }, + ) + .collect(); + let results = { + let mut runs = compare_runs(runs); + + if let Some((reference_ratios, candidate_ratios)) = ratios { + runs.set_evm_interpreter_ratios(reference_ratios, candidate_ratios); + } + runs + }; + (group_name, results) + }) + .collect(); + + results +} + +/// +/// Compare two sets of measurements. +/// The parameter `[run]` is a vector of triples where each element contains: +/// - metadata for a test, +/// - measurement in the first set, +/// - measurement in the second set. +/// +fn compare_runs<'a>(runs: Vec<(&'a TestMetadata, &'a Run, &'a Run)>) -> Results<'a> { + let elements_number = runs.len(); + + let mut size_factors = Vec::with_capacity(elements_number); + let mut size_min = 1.0; + let mut size_max = 1.0; + let mut size_negatives: Vec<(f64, &TestMetadata)> = Vec::with_capacity(elements_number); + let mut size_positives: Vec<(f64, &TestMetadata)> = Vec::with_capacity(elements_number); + let mut size_total_reference: u64 = 0; + let mut size_total_candidate: u64 = 0; + + let mut cycles_factors = Vec::with_capacity(elements_number); + let mut cycles_min = 1.0; + let mut cycles_max = 1.0; + let mut cycles_negatives: Vec<(f64, &TestMetadata)> = Vec::with_capacity(elements_number); + let mut cycles_positives: Vec<(f64, &TestMetadata)> = Vec::with_capacity(elements_number); + let mut cycles_total_reference: u64 = 0; + let mut cycles_total_candidate: u64 = 0; + + let mut ergs_factors = Vec::with_capacity(elements_number); + let mut ergs_min = 1.0; + let mut ergs_max = 1.0; + let mut ergs_negatives: Vec<(f64, &TestMetadata)> = Vec::with_capacity(elements_number); + let mut ergs_positives: Vec<(f64, &TestMetadata)> = Vec::with_capacity(elements_number); + let mut ergs_total_reference: u64 = 0; + let mut ergs_total_candidate: u64 = 0; + + let mut gas_factors = Vec::with_capacity(elements_number); + let mut gas_min = 1.0; + let mut gas_max = 1.0; + let mut gas_negatives = Vec::with_capacity(elements_number); + let mut gas_positives = Vec::with_capacity(elements_number); + let mut gas_total_reference: u64 = 0; + let mut gas_total_candidate: u64 = 0; + + for (metadata, reference, candidate) in runs { + let file_path = &metadata.selector.path; + // FIXME: ad-hoc patch + if file_path.contains(crate::model::evm_interpreter::TEST_PATH) { + if let Some(input) = &metadata.selector.input { + if input.is_deployer() { + continue; + } + } + } + + cycles_total_reference += reference.cycles as u64; + cycles_total_candidate += candidate.cycles as u64; + let cycles_factor = (candidate.cycles as f64) / (reference.cycles as f64); + if cycles_factor > 1.0 { + cycles_negatives.push((cycles_factor, metadata)); + } + if cycles_factor < 1.0 { + cycles_positives.push((cycles_factor, metadata)); + } + if cycles_factor < cycles_min { + cycles_min = cycles_factor; + } + if cycles_factor > cycles_max { + cycles_max = cycles_factor; + } + cycles_factors.push(cycles_factor); + + ergs_total_reference += reference.ergs; + ergs_total_candidate += candidate.ergs; + let ergs_factor = (candidate.ergs as f64) / (reference.ergs as f64); + if ergs_factor > 1.0 { + ergs_negatives.push((ergs_factor, metadata)); + } + if ergs_factor < 1.0 { + ergs_positives.push((ergs_factor, metadata)); + } + if ergs_factor < ergs_min { + ergs_min = ergs_factor; + } + if ergs_factor > ergs_max { + ergs_max = ergs_factor; + } + ergs_factors.push(ergs_factor); + + gas_total_reference += reference.gas; + gas_total_candidate += candidate.gas; + let gas_factor = (candidate.gas as f64) / (reference.gas as f64); + if gas_factor > 1.0 { + gas_negatives.push((gas_factor, metadata)); + } + if gas_factor < 1.0 { + gas_positives.push((gas_factor, metadata)); + } + if gas_factor < gas_min { + gas_min = gas_factor; + } + if gas_factor > gas_max { + gas_max = gas_factor; + } + gas_factors.push(gas_factor); + + let reference_size = match reference.size { + Some(size) => size, + None => continue, + }; + let candidate_size = match candidate.size { + Some(size) => size, + None => continue, + }; + size_total_reference += reference_size as u64; + size_total_candidate += candidate_size as u64; + let size_factor = (candidate_size as f64) / (reference_size as f64); + if size_factor > 1.0 { + size_negatives.push((size_factor, metadata)); + } + if size_factor < 1.0 { + size_positives.push((size_factor, metadata)); + } + if size_factor < size_min { + size_min = size_factor; + } + if size_factor > size_max { + size_max = size_factor; + } + size_factors.push(size_factor); + } + + let size_total = (size_total_candidate as f64) / (size_total_reference as f64); + + let cycles_total = (cycles_total_candidate as f64) / (cycles_total_reference as f64); + + let ergs_total = (ergs_total_candidate as f64) / (ergs_total_reference as f64); + + let gas_total = (gas_total_candidate as f64) / (gas_total_reference as f64); + + Results::new( + size_min, + size_max, + size_total, + size_negatives, + size_positives, + cycles_min, + cycles_max, + cycles_total, + cycles_negatives, + cycles_positives, + ergs_min, + ergs_max, + ergs_total, + ergs_negatives, + ergs_positives, + gas_min, + gas_max, + gas_total, + gas_negatives, + gas_positives, + ) +} diff --git a/benchmark_analyzer/src/benchmark/format/csv.rs b/benchmark_analyzer/src/benchmark/format/csv.rs deleted file mode 100644 index a9e22762..00000000 --- a/benchmark_analyzer/src/benchmark/format/csv.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! -//! Serializing benchmark data to CSV. -//! - -use std::fmt::Write; - -use super::Benchmark; -use super::IBenchmarkSerializer; -use crate::benchmark::group::element::selector::Selector; -use crate::benchmark::group::element::Element; -use crate::benchmark::metadata::Metadata; - -/// -/// Serialize the benchmark to CSV in the following format: -/// "group_name", "element_name", "size_str", "cycles", "ergs", "gas" -/// -#[derive(Default)] -pub struct Csv; - -impl IBenchmarkSerializer for Csv { - type Err = std::fmt::Error; - - fn serialize_to_string(&self, benchmark: &Benchmark) -> Result { - let mut result = String::with_capacity(estimate_csv_size(benchmark)); - result.push_str( - r#""group", "mode", "version", "path", "case", "input", "size", "cycles", "ergs", "gas""#, - ); - result.push('\n'); - for (group_name, group) in &benchmark.groups { - for Element { - metadata: - Metadata { - selector: Selector { path, case, input }, - mode, - version, - group: _, - }, - size, - cycles, - ergs, - gas, - } in group.elements.values() - { - let size_str = size.map(|s| s.to_string()).unwrap_or_default(); - let mode = mode.as_deref().unwrap_or_default(); - let input = input.clone().map(|s| s.to_string()).unwrap_or_default(); - let case = case.as_deref().unwrap_or_default(); - let version = version.as_deref().unwrap_or_default(); - writeln!( - &mut result, - r#""{group_name}", "{mode}", "{version}", "{path}", "{case}", "{input}", {size_str}, {cycles}, {ergs}, {gas}"#, - )?; - } - } - Ok(result) - } -} - -fn estimate_csv_line_length() -> usize { - let number_fields = 4; - let number_field_estimated_max_length = 15; - let group_name_estimated_max = 10; - let test_name_estimated_max = 300; - group_name_estimated_max - + test_name_estimated_max - + number_fields * number_field_estimated_max_length -} - -fn estimate_csv_size(benchmark: &Benchmark) -> usize { - (benchmark.groups.len() + 1) * estimate_csv_line_length() -} diff --git a/benchmark_analyzer/src/benchmark/format/json.rs b/benchmark_analyzer/src/benchmark/format/json.rs deleted file mode 100644 index 04e19a33..00000000 --- a/benchmark_analyzer/src/benchmark/format/json.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! -//! Serializing benchmark data to JSON. -//! - -use super::Benchmark; -use super::IBenchmarkSerializer; - -/// Serialize the benchmark data to JSON using `serde` library. -#[derive(Default)] -pub struct Json; - -impl IBenchmarkSerializer for Json { - type Err = serde_json::error::Error; - - fn serialize_to_string(&self, benchmark: &Benchmark) -> Result { - serde_json::to_string(benchmark) - } -} diff --git a/benchmark_analyzer/src/benchmark/format/mod.rs b/benchmark_analyzer/src/benchmark/format/mod.rs deleted file mode 100644 index f22ad54f..00000000 --- a/benchmark_analyzer/src/benchmark/format/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! -//! Serialization of benchmark data in different output formats. -//! - -pub mod csv; -pub mod json; - -use crate::benchmark::Benchmark; - -/// Serialization format for benchmark data. -pub trait IBenchmarkSerializer { - type Err: std::error::Error; - /// Serialize benchmark data in the selected format. - fn serialize_to_string(&self, benchmark: &Benchmark) -> anyhow::Result; -} diff --git a/benchmark_analyzer/src/benchmark/group/mod.rs b/benchmark_analyzer/src/benchmark/group/mod.rs deleted file mode 100644 index 94d7db2a..00000000 --- a/benchmark_analyzer/src/benchmark/group/mod.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! -//! The benchmark group representation. -//! - -pub mod element; -pub mod results; - -use std::collections::BTreeMap; - -use serde::Deserialize; -use serde::Serialize; - -use crate::benchmark::Benchmark; - -use self::element::Element; -use self::results::Results; - -/// -/// The benchmark group representation. -/// -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct Group { - /// The group elements. - pub elements: BTreeMap, -} - -impl Group { - /// - /// Compares two benchmark groups. - /// - pub fn compare<'a>(reference: &'a Self, candidate: &'a Self) -> Results<'a> { - let elements_number = reference.elements.len(); - - let mut size_factors = Vec::with_capacity(elements_number); - let mut size_min = 1.0; - let mut size_max = 1.0; - let mut size_negatives = Vec::with_capacity(elements_number); - let mut size_positives = Vec::with_capacity(elements_number); - let mut size_total_reference: u64 = 0; - let mut size_total_candidate: u64 = 0; - - let mut cycles_factors = Vec::with_capacity(elements_number); - let mut cycles_min = 1.0; - let mut cycles_max = 1.0; - let mut cycles_negatives = Vec::with_capacity(elements_number); - let mut cycles_positives = Vec::with_capacity(elements_number); - let mut cycles_total_reference: u64 = 0; - let mut cycles_total_candidate: u64 = 0; - - let mut ergs_factors = Vec::with_capacity(elements_number); - let mut ergs_min = 1.0; - let mut ergs_max = 1.0; - let mut ergs_negatives = Vec::with_capacity(elements_number); - let mut ergs_positives = Vec::with_capacity(elements_number); - let mut ergs_total_reference: u64 = 0; - let mut ergs_total_candidate: u64 = 0; - - let mut gas_factors = Vec::with_capacity(elements_number); - let mut gas_min = 1.0; - let mut gas_max = 1.0; - let mut gas_negatives = Vec::with_capacity(elements_number); - let mut gas_positives = Vec::with_capacity(elements_number); - let mut gas_total_reference: u64 = 0; - let mut gas_total_candidate: u64 = 0; - - for (path, reference) in reference.elements.iter() { - if path.contains("tests/solidity/complex/interpreter/test.json") - && path.contains("#deployer") - { - continue; - } - let candidate = match candidate.elements.get(path.as_str()) { - Some(candidate) => candidate, - None => continue, - }; - - cycles_total_reference += reference.cycles as u64; - cycles_total_candidate += candidate.cycles as u64; - let cycles_factor = (candidate.cycles as f64) / (reference.cycles as f64); - if cycles_factor > 1.0 { - cycles_negatives.push((cycles_factor, path.as_str())); - } - if cycles_factor < 1.0 { - cycles_positives.push((cycles_factor, path.as_str())); - } - if cycles_factor < cycles_min { - cycles_min = cycles_factor; - } - if cycles_factor > cycles_max { - cycles_max = cycles_factor; - } - cycles_factors.push(cycles_factor); - - ergs_total_reference += reference.ergs; - ergs_total_candidate += candidate.ergs; - let ergs_factor = (candidate.ergs as f64) / (reference.ergs as f64); - if ergs_factor > 1.0 { - ergs_negatives.push((ergs_factor, path.as_str())); - } - if ergs_factor < 1.0 { - ergs_positives.push((ergs_factor, path.as_str())); - } - if ergs_factor < ergs_min { - ergs_min = ergs_factor; - } - if ergs_factor > ergs_max { - ergs_max = ergs_factor; - } - ergs_factors.push(ergs_factor); - - gas_total_reference += reference.gas; - gas_total_candidate += candidate.gas; - let gas_factor = (candidate.gas as f64) / (reference.gas as f64); - if gas_factor > 1.0 { - gas_negatives.push((gas_factor, path.as_str())); - } - if gas_factor < 1.0 { - gas_positives.push((gas_factor, path.as_str())); - } - if gas_factor < gas_min { - gas_min = gas_factor; - } - if gas_factor > gas_max { - gas_max = gas_factor; - } - gas_factors.push(gas_factor); - - let reference_size = match reference.size { - Some(size) => size, - None => continue, - }; - let candidate_size = match candidate.size { - Some(size) => size, - None => continue, - }; - size_total_reference += reference_size as u64; - size_total_candidate += candidate_size as u64; - let size_factor = (candidate_size as f64) / (reference_size as f64); - if size_factor > 1.0 { - size_negatives.push((size_factor, path.as_str())); - } - if size_factor < 1.0 { - size_positives.push((size_factor, path.as_str())); - } - if size_factor < size_min { - size_min = size_factor; - } - if size_factor > size_max { - size_max = size_factor; - } - size_factors.push(size_factor); - } - - let size_total = (size_total_candidate as f64) / (size_total_reference as f64); - - let cycles_total = (cycles_total_candidate as f64) / (cycles_total_reference as f64); - - let ergs_total = (ergs_total_candidate as f64) / (ergs_total_reference as f64); - - let gas_total = (gas_total_candidate as f64) / (gas_total_reference as f64); - - Results::new( - size_min, - size_max, - size_total, - size_negatives, - size_positives, - cycles_min, - cycles_max, - cycles_total, - cycles_negatives, - cycles_positives, - ergs_min, - ergs_max, - ergs_total, - ergs_negatives, - ergs_positives, - gas_min, - gas_max, - gas_total, - gas_negatives, - gas_positives, - ) - } - - /// - /// Returns the EVM interpreter ergs/gas ratio. - /// - pub fn evm_interpreter_ratios(&self) -> Vec<(String, f64)> { - let mut results = Vec::with_capacity(Benchmark::EVM_OPCODES.len()); - for evm_opcode in Benchmark::EVM_OPCODES.into_iter() { - let name_substring = format!("test.json::{evm_opcode}["); - let [full, template]: [Element; 2] = self - .elements - .iter() - .filter(|element| element.0.contains(name_substring.as_str())) - .rev() - .take(2) - .map(|element| (element.1.to_owned())) - .collect::>() - .try_into() - .expect("Always valid"); - - let ergs_difference = full.ergs - template.ergs; - let gas_difference = full.gas - template.gas; - let ergs_gas_ratio = (ergs_difference as f64) / (gas_difference as f64); - results.push((evm_opcode.to_owned(), ergs_gas_ratio)); - } - results - } -} diff --git a/benchmark_analyzer/src/benchmark/metadata.rs b/benchmark_analyzer/src/benchmark/metadata.rs deleted file mode 100644 index bda33fa1..00000000 --- a/benchmark_analyzer/src/benchmark/metadata.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! -//! Information associated with the benchmark element. -//! - -use serde::Deserialize; -use serde::Serialize; - -use crate::benchmark::group::element::selector::Selector; - -/// -/// Encoded compiler mode. In future, it can be expanded into a structured type -/// shared between crates `benchmark_analyzer` and `compiler_tester`. -/// -pub type Mode = String; - -/// -/// Information associated with the benchmark element. -/// -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Metadata { - /// Test selector. - pub selector: Selector, - /// Compiler mode. - pub mode: Option, - /// Compiler version, e.g. solc. - pub version: Option, - /// Test group - pub group: String, -} diff --git a/benchmark_analyzer/src/benchmark/mod.rs b/benchmark_analyzer/src/benchmark/mod.rs deleted file mode 100644 index 9839fc50..00000000 --- a/benchmark_analyzer/src/benchmark/mod.rs +++ /dev/null @@ -1,232 +0,0 @@ -//! -//! The benchmark representation. -//! - -pub mod format; -pub mod group; -pub mod metadata; - -use std::collections::BTreeMap; -use std::path::PathBuf; - -use format::IBenchmarkSerializer; -use serde::Deserialize; -use serde::Serialize; - -use self::group::results::Results; -use self::group::Group; - -/// -/// The benchmark representation. -/// -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct Benchmark { - /// The benchmark groups. - pub groups: BTreeMap, -} - -impl Benchmark { - /// The EVM interpreter group identifier. - pub const EVM_INTERPRETER_GROUP_NAME: &'static str = "EVMInterpreter"; - - /// The EVM interpreter cycles group identifier. - pub const EVM_INTERPRETER_GROUP_NAME_CYCLES: &'static str = "EVMInterpreter M3B3"; - - /// The EVM opcodes to test. - pub const EVM_OPCODES: [&'static str; 135] = [ - "ADD", - "MUL", - "SUB", - "DIV", - "SDIV", - "MOD", - "SMOD", - "ADDMOD", - "MULMOD", - "EXP", - "SIGNEXTEND", - "LT", - "GT", - "SLT", - "SGT", - "EQ", - "ISZERO", - "AND", - "OR", - "XOR", - "NOT", - "BYTE", - "SHL", - "SHR", - "SAR", - "SGT", - "SHA3", - "ADDRESS", - "BALANCE", - "ORIGIN", - "CALLER", - "CALLVALUE", - "CALLDATALOAD", - "CALLDATASIZE", - "CALLDATACOPY", - "CODESIZE", - "CODECOPY", - "GASPRICE", - "EXTCODESIZE", - "EXTCODECOPY", - "RETURNDATASIZE", - "RETURNDATACOPY", - "EXTCODEHASH", - "BLOCKHASH", - "COINBASE", - "TIMESTAMP", - "NUMBER", - "PREVRANDAO", - "GASLIMIT", - "CHAINID", - "SELFBALANCE", - "BASEFEE", - "POP", - "MLOAD", - "MSTORE", - "MSTORE8", - "SLOAD", - "SSTORE", - "JUMP", - "JUMPI", - "PC", - "MSIZE", - "GAS", - "JUMPDEST", - "PUSH0", - "PUSH1", - "PUSH2", - "PUSH4", - "PUSH5", - "PUSH6", - "PUSH7", - "PUSH8", - "PUSH9", - "PUSH10", - "PUSH11", - "PUSH12", - "PUSH13", - "PUSH14", - "PUSH15", - "PUSH16", - "PUSH17", - "PUSH18", - "PUSH19", - "PUSH20", - "PUSH21", - "PUSH22", - "PUSH23", - "PUSH24", - "PUSH25", - "PUSH26", - "PUSH27", - "PUSH28", - "PUSH29", - "PUSH30", - "PUSH31", - "PUSH32", - "DUP1", - "DUP2", - "DUP3", - "DUP4", - "DUP5", - "DUP6", - "DUP7", - "DUP8", - "DUP9", - "DUP10", - "DUP11", - "DUP12", - "DUP13", - "DUP14", - "DUP15", - "DUP16", - "SWAP1", - "SWAP2", - "SWAP3", - "SWAP4", - "SWAP5", - "SWAP6", - "SWAP7", - "SWAP8", - "SWAP9", - "SWAP10", - "SWAP11", - "SWAP12", - "SWAP13", - "SWAP14", - "SWAP15", - "SWAP16", - "CALL", - "STATICCALL", - "DELEGATECALL", - "CREATE", - "CREATE2", - "RETURN", - "REVERT", - ]; - - /// - /// Compares two benchmarks. - /// - pub fn compare<'a>(reference: &'a Self, candidate: &'a Self) -> BTreeMap<&'a str, Results<'a>> { - let mut results = BTreeMap::new(); - - for (group_name, reference_group) in reference.groups.iter() { - let candidate_group = match candidate.groups.get(group_name) { - Some(candidate_group) => candidate_group, - None => continue, - }; - - let mut group_results = Group::compare(reference_group, candidate_group); - if group_name.starts_with(Self::EVM_INTERPRETER_GROUP_NAME_CYCLES) { - if let (Some(reference_ratios), Some(candidate_ratios)) = ( - reference - .groups - .get(group_name.as_str()) - .map(|group| group.evm_interpreter_ratios()), - candidate - .groups - .get(group_name.as_str()) - .map(|group| group.evm_interpreter_ratios()), - ) { - group_results.set_evm_interpreter_ratios(reference_ratios, candidate_ratios); - } - } - results.insert(group_name.as_str(), group_results); - } - - results - } - - /// - /// Writes the benchmark results to a file using a provided serializer. - /// - pub fn write_to_file( - self, - path: PathBuf, - serializer: impl IBenchmarkSerializer, - ) -> anyhow::Result<()> { - let contents = serializer.serialize_to_string(&self).expect("Always valid"); - std::fs::write(path.as_path(), contents) - .map_err(|error| anyhow::anyhow!("Benchmark file {path:?} reading: {error}"))?; - Ok(()) - } -} - -impl TryFrom for Benchmark { - type Error = anyhow::Error; - - fn try_from(path: PathBuf) -> Result { - let text = std::fs::read_to_string(path.as_path()) - .map_err(|error| anyhow::anyhow!("Benchmark file {:?} reading: {}", path, error))?; - let json: Self = serde_json::from_str(text.as_str()) - .map_err(|error| anyhow::anyhow!("Benchmark file {:?} parsing: {}", path, error))?; - Ok(json) - } -} diff --git a/benchmark_analyzer/src/benchmark_analyzer/arguments.rs b/benchmark_analyzer/src/benchmark_analyzer/arguments.rs index f86f5077..3fffc116 100644 --- a/benchmark_analyzer/src/benchmark_analyzer/arguments.rs +++ b/benchmark_analyzer/src/benchmark_analyzer/arguments.rs @@ -27,4 +27,12 @@ pub struct Arguments { /// Maximum number of results displayed in a group. #[structopt(long, default_value_t = 100)] pub group_max: usize, + + /// Regular expression to select reference group for the comparison. + #[structopt(long)] + pub query_reference: Option, + + /// Regular expression to select candidate group for the comparison. + #[structopt(long)] + pub query_candidate: Option, } diff --git a/benchmark_analyzer/src/benchmark_analyzer/main.rs b/benchmark_analyzer/src/benchmark_analyzer/main.rs index 5c3cdaf9..572f9a3e 100644 --- a/benchmark_analyzer/src/benchmark_analyzer/main.rs +++ b/benchmark_analyzer/src/benchmark_analyzer/main.rs @@ -9,6 +9,7 @@ use std::io::Write; use clap::Parser; use self::arguments::Arguments; +use benchmark_analyzer::ResultsGroup; /// /// The application entry point. @@ -16,18 +17,47 @@ use self::arguments::Arguments; fn main() -> anyhow::Result<()> { let arguments = Arguments::try_parse()?; - let reference = benchmark_analyzer::Benchmark::try_from(arguments.reference)?; - let candidate = benchmark_analyzer::Benchmark::try_from(arguments.candidate)?; + let reference_benchmark = benchmark_analyzer::Benchmark::try_from(arguments.reference)?; + let candidate_benchmark = benchmark_analyzer::Benchmark::try_from(arguments.candidate)?; - let groups_results = benchmark_analyzer::Benchmark::compare(&reference, &candidate); + let groups_results = if let (Some(reference_query), Some(candidate_query)) = + (arguments.query_reference, arguments.query_candidate) + { + // If the user provides regular expressions to select groups for + // comparison, the analyzer will compare all groups with the same names, + // plus all pairs of groups matching regular expressions + // [regex_reference] and [regex_candidate]. + + let regex_reference = + regex::Regex::new(&reference_query).expect("Invalid reference query regexp"); + let regex_candidate = + regex::Regex::new(&candidate_query).expect("Invalid candidate query regexp"); + + benchmark_analyzer::analysis::compare( + &reference_benchmark, + &candidate_benchmark, + |g1: &ResultsGroup<'_>, g2: &ResultsGroup<'_>| { + g1.regex_matches(®ex_reference) && g2.regex_matches(®ex_candidate) + }, + ) + } else { + // If the user did not provide regular expressions to select groups for + // comparison, the analyzer will compare only the groups with the same + // names. + benchmark_analyzer::analysis::compare( + &reference_benchmark, + &candidate_benchmark, + |_: &ResultsGroup<'_>, _: &ResultsGroup<'_>| false, + ) + }; match arguments.output_file { Some(output_path) => { let mut file = std::fs::File::create(output_path)?; for (group_name, mut results) in groups_results.into_iter() { results.sort_worst(); - results.print_worst_results(arguments.group_max, group_name); - results.write_all(&mut file, group_name)?; + results.print_worst_results(arguments.group_max, &group_name.to_string()); + results.write_all(&mut file, &group_name.to_string())?; writeln!(file)?; println!(); println!(); @@ -37,8 +67,8 @@ fn main() -> anyhow::Result<()> { let mut stdout = std::io::stdout(); for (group_name, mut results) in groups_results.into_iter() { results.sort_worst(); - results.print_worst_results(arguments.group_max, group_name); - results.write_all(&mut stdout, group_name)?; + results.print_worst_results(arguments.group_max, &group_name.to_string()); + results.write_all(&mut stdout, &group_name.to_string())?; writeln!(stdout)?; println!(); println!(); diff --git a/benchmark_analyzer/src/lib.rs b/benchmark_analyzer/src/lib.rs index f18b07de..622f4eb5 100644 --- a/benchmark_analyzer/src/lib.rs +++ b/benchmark_analyzer/src/lib.rs @@ -2,18 +2,34 @@ //! The benchmark analyzer library. //! -pub(crate) mod benchmark; +pub mod analysis; +pub mod model; +pub mod output; +pub mod results; +pub mod util; -pub use self::benchmark::format::csv::Csv as CsvSerializer; -pub use self::benchmark::format::json::Json as JsonSerializer; -pub use self::benchmark::group::element::input::Input; -pub use self::benchmark::group::element::selector::Selector as TestSelector; -pub use self::benchmark::group::element::Element as BenchmarkElement; -pub use self::benchmark::group::Group as BenchmarkGroup; -pub use self::benchmark::metadata::Metadata; -pub use self::benchmark::Benchmark; +pub use crate::output::format::csv::Csv as CsvSerializer; +pub use crate::output::format::json::lnt::JsonLNT as JsonLNTSerializer; +pub use crate::output::format::json::native::Json as JsonNativeSerializer; -/// -/// The all elements group name. -/// -pub const BENCHMARK_ALL_GROUP_NAME: &str = "All"; +pub use crate::model::benchmark::test::codegen::versioned::executable::run::Run; +pub use crate::model::benchmark::test::codegen::versioned::executable::Executable; +pub use crate::model::benchmark::test::codegen::versioned::VersionedGroup; +pub use crate::model::benchmark::test::codegen::CodegenGroup; +pub use crate::model::benchmark::test::input::Input; +pub use crate::model::benchmark::test::selector::Selector as TestSelector; +pub use crate::model::benchmark::test::Test; +pub use crate::model::benchmark::write_to_file; +pub use crate::model::benchmark::Benchmark; +pub use crate::model::context::validate_context; +pub use crate::model::context::Context as BenchmarkContext; + +// Metadata for various parts of the model +pub use crate::model::benchmark::metadata::BenchmarkVersion; +pub use crate::model::benchmark::metadata::Metadata as BenchmarkMetadata; +pub use crate::model::benchmark::test::codegen::versioned::executable::metadata::Metadata as ExecutableMetadata; +pub use crate::model::benchmark::test::metadata::Metadata as TestMetadata; + +pub use crate::results::group::Group as ResultsGroup; + +pub use crate::model::evm_interpreter::GROUP_NAME as TEST_GROUP_EVM_INTERPRETER; diff --git a/benchmark_analyzer/src/model/benchmark/metadata.rs b/benchmark_analyzer/src/model/benchmark/metadata.rs new file mode 100644 index 00000000..93695e63 --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/metadata.rs @@ -0,0 +1,36 @@ +//! +//! Information associated with the benchmark run. +//! + +use chrono::DateTime; +use chrono::Utc; +use serde::Deserialize; +use serde::Serialize; + +use crate::model::context::Context; + +/// Version of the benchmark format. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub enum BenchmarkVersion { + #[default] + /// Flat format, a map from key (Identifier + mode) to measurements. + V1, + /// New format with metadata. + V2, +} + +/// +/// Information associated with the benchmark run. +/// +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Metadata { + /// Version for the benchmark report. + pub version: BenchmarkVersion, + /// Start of the benchmark run. + pub start: DateTime, + /// End of the benchmark run. + pub end: DateTime, + /// Context of benchmarking, passed from compiler tester. + #[serde(skip)] + pub context: Option, +} diff --git a/benchmark_analyzer/src/model/benchmark/mod.rs b/benchmark_analyzer/src/model/benchmark/mod.rs new file mode 100644 index 00000000..0e82ceb5 --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/mod.rs @@ -0,0 +1,78 @@ +//! +//! The benchmark representation. +//! + +pub mod metadata; +pub mod test; + +use std::collections::BTreeMap; +use std::path::PathBuf; + +use serde::Deserialize; +use serde::Serialize; + +use crate::output::comparison_result::Output; +use crate::output::file::File; +use crate::output::IBenchmarkSerializer; + +use metadata::Metadata; + +use self::test::Test; + +/// +/// The benchmark representation. +/// +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct Benchmark { + /// Metadata related to the whole benchmark. + pub metadata: Metadata, + /// The tests. + pub tests: BTreeMap, +} + +/// +/// Writes the benchmark results to a file using a provided serializer. +/// +pub fn write_to_file( + benchmark: &Benchmark, + path: PathBuf, + serializer: impl IBenchmarkSerializer, +) -> anyhow::Result<()> { + match serializer + .serialize_to_string(benchmark) + .expect("Always valid") + { + Output::SingleFile(contents) => { + std::fs::write(path.as_path(), contents) + .map_err(|error| anyhow::anyhow!("Benchmark file {path:?} writing: {error}"))?; + } + Output::MultipleFiles(files) => { + if !files.is_empty() { + std::fs::create_dir_all(&path)?; + } + for File { + path: relative_path, + contents, + } in files + { + let file_path = path.join(relative_path); + std::fs::write(file_path.as_path(), contents).map_err(|error| { + anyhow::anyhow!("Benchmark file {file_path:?} writing: {error}") + })?; + } + } + } + Ok(()) +} + +impl TryFrom for Benchmark { + type Error = anyhow::Error; + + fn try_from(path: PathBuf) -> Result { + let text = std::fs::read_to_string(path.as_path()) + .map_err(|error| anyhow::anyhow!("Benchmark file {path:?} reading: {error}"))?; + let json: Self = serde_json::from_str(text.as_str()) + .map_err(|error| anyhow::anyhow!("Benchmark file {path:?} parsing: {error}"))?; + Ok(json) + } +} diff --git a/benchmark_analyzer/src/model/benchmark/test/codegen/mod.rs b/benchmark_analyzer/src/model/benchmark/test/codegen/mod.rs new file mode 100644 index 00000000..1cde5971 --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/test/codegen/mod.rs @@ -0,0 +1,26 @@ +//! +//! Groups test instances by the code generator version. +//! + +pub mod versioned; + +use std::collections::BTreeMap; + +use serde::Deserialize; +use serde::Serialize; +use versioned::VersionedGroup; + +/// +/// The language version associated with a test. +/// +pub type Version = String; + +/// +/// Groups test instances by the code generator version. +/// +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct CodegenGroup { + #[serde(flatten)] + /// Inner groups that differ by the associated language version. + pub versioned_groups: BTreeMap, +} diff --git a/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/metadata.rs b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/metadata.rs new file mode 100644 index 00000000..bc9776de --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/metadata.rs @@ -0,0 +1,12 @@ +//! +//! Information associated with an executable in a benchmark. +//! + +use serde::Deserialize; +use serde::Serialize; + +/// +/// Information associated with an executable in a benchmark. +/// +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct Metadata {} diff --git a/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/mod.rs b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/mod.rs new file mode 100644 index 00000000..65353f30 --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/mod.rs @@ -0,0 +1,26 @@ +//! +//! Executable is the compiled artifact corresponding to the test. +//! Executables differ by compilation flags. +//! + +pub mod metadata; +pub mod run; + +use metadata::Metadata; +use run::Run; +use serde::Deserialize; +use serde::Serialize; + +/// +/// Executable is the compiled artifact corresponding to the test. +/// Executables differ by compilation flags. +/// +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Executable { + #[serde(default, skip)] + /// Metadata associated with the compiled executable. + pub metadata: Metadata, + #[serde(flatten)] + /// Measurements. + pub run: Run, +} diff --git a/benchmark_analyzer/src/benchmark/group/element/mod.rs b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/run/mod.rs similarity index 56% rename from benchmark_analyzer/src/benchmark/group/element/mod.rs rename to benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/run/mod.rs index b3fe8c1d..48652f60 100644 --- a/benchmark_analyzer/src/benchmark/group/element/mod.rs +++ b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/executable/run/mod.rs @@ -1,22 +1,15 @@ //! -//! The benchmark element. +//! A run of a test with fixed compiler options (mode). //! -pub mod input; -pub mod selector; - use serde::Deserialize; use serde::Serialize; -use crate::benchmark::metadata::Metadata; - /// -/// The benchmark element. +/// A run of a test with fixed compiler options (mode). /// #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Element { - /// Associated metadata. - pub metadata: Metadata, +pub struct Run { /// The contract size, `Some` for contracts deploys. pub size: Option, /// The number of cycles. @@ -27,19 +20,12 @@ pub struct Element { pub gas: u64, } -impl Element { +impl Run { /// /// A shortcut constructor. /// - pub fn new( - metadata: Metadata, - size: Option, - cycles: usize, - ergs: u64, - gas: u64, - ) -> Self { + pub fn new(size: Option, cycles: usize, ergs: u64, gas: u64) -> Self { Self { - metadata, size, cycles, ergs, diff --git a/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/mod.rs b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/mod.rs new file mode 100644 index 00000000..ce3ced25 --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/test/codegen/versioned/mod.rs @@ -0,0 +1,27 @@ +//! +//! Groups test runs by the language version associated with them. +//! + +pub mod executable; + +use std::collections::BTreeMap; + +use executable::Executable; +use serde::Deserialize; +use serde::Serialize; + +/// +/// Encoded compiler mode. In future, it can be replaced with a structured type +/// shared between crates `benchmark_analyzer` and `compiler_tester`. +/// +pub type Mode = String; + +/// +/// Groups test runs by the language version associated with them. +/// +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct VersionedGroup { + #[serde(flatten)] + /// Compiled executables associated with test runs. + pub executables: BTreeMap, +} diff --git a/benchmark_analyzer/src/benchmark/group/element/input.rs b/benchmark_analyzer/src/model/benchmark/test/input.rs similarity index 72% rename from benchmark_analyzer/src/benchmark/group/element/input.rs rename to benchmark_analyzer/src/model/benchmark/test/input.rs index 55e52616..0190968e 100644 --- a/benchmark_analyzer/src/benchmark/group/element/input.rs +++ b/benchmark_analyzer/src/model/benchmark/test/input.rs @@ -15,6 +15,11 @@ pub enum Input { /// Contract identifier, usually file name and contract name separated by a colon. contract_identifier: String, }, + /// The fallback method. + Fallback { + /// Index in the array of inputs. + input_index: usize, + }, /// The contract call. Runtime { /// Index in the array of inputs. @@ -34,6 +39,24 @@ pub enum Input { }, } +impl Input { + /// Returns `true` if the input is [`Deployer`]. + /// + /// [`Deployer`]: Input::Deployer + #[must_use] + pub fn is_deployer(&self) -> bool { + matches!(self, Self::Deployer { .. }) + } + + /// Returns `true` if the input is [`Fallback`]. + /// + /// [`Fallback`]: Input::Fallback + #[must_use] + pub fn is_fallback(&self) -> bool { + matches!(self, Self::Fallback { .. }) + } +} + impl std::fmt::Display for Input { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -49,6 +72,7 @@ impl std::fmt::Display for Input { Input::Balance { input_index } => { f.write_fmt(format_args!("#balance_check:{input_index}")) } + Input::Fallback { input_index } => f.write_fmt(format_args!("#fallback:{input_index}")), } } } diff --git a/benchmark_analyzer/src/model/benchmark/test/metadata.rs b/benchmark_analyzer/src/model/benchmark/test/metadata.rs new file mode 100644 index 00000000..ebd565c9 --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/test/metadata.rs @@ -0,0 +1,29 @@ +//! +//! Information associated with a specific test in benchmark. +//! + +use serde::Deserialize; +use serde::Serialize; + +use crate::model::benchmark::test::selector::Selector; + +/// +/// Information associated with a specific test in benchmark. +/// +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Metadata { + #[serde(default)] + /// Tests may be tagged with one or many groups. + pub tags: Vec, + /// Test selector. + pub selector: Selector, +} + +impl Metadata { + /// + /// Creates a new instance of test metadata provided with the test selector and tags. + /// + pub fn new(selector: Selector, tags: Vec) -> Self { + Self { selector, tags } + } +} diff --git a/benchmark_analyzer/src/model/benchmark/test/mod.rs b/benchmark_analyzer/src/model/benchmark/test/mod.rs new file mode 100644 index 00000000..ebd82fd1 --- /dev/null +++ b/benchmark_analyzer/src/model/benchmark/test/mod.rs @@ -0,0 +1,44 @@ +//! +//! The benchmark group representation. +//! + +pub mod codegen; +pub mod input; +pub mod metadata; +pub mod selector; + +use std::collections::BTreeMap; + +use codegen::CodegenGroup; +use metadata::Metadata; +use serde::Deserialize; +use serde::Serialize; + +/// +/// The codegen associated with a test definition. +/// +pub type Codegen = String; + +/// +/// The benchmark group representation. +/// +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct Test { + /// Metadata for this test. + #[serde(default)] + pub metadata: Metadata, + /// Versions. + pub codegen_groups: BTreeMap, +} + +impl Test { + /// + /// Creates a new test with provided metadata. + /// + pub fn new(metadata: Metadata) -> Self { + Self { + codegen_groups: Default::default(), + metadata, + } + } +} diff --git a/benchmark_analyzer/src/benchmark/group/element/selector.rs b/benchmark_analyzer/src/model/benchmark/test/selector.rs similarity index 88% rename from benchmark_analyzer/src/benchmark/group/element/selector.rs rename to benchmark_analyzer/src/model/benchmark/test/selector.rs index b804d954..00abf174 100644 --- a/benchmark_analyzer/src/benchmark/group/element/selector.rs +++ b/benchmark_analyzer/src/model/benchmark/test/selector.rs @@ -5,12 +5,12 @@ use serde::Deserialize; use serde::Serialize; -use crate::benchmark::group::element::input::Input; +use crate::model::benchmark::test::input::Input; /// /// Test selector, unambiously locating a test suite, case, or input. /// -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] pub struct Selector { /// Path to the file containing test. pub path: String, diff --git a/benchmark_analyzer/src/model/context/mod.rs b/benchmark_analyzer/src/model/context/mod.rs new file mode 100644 index 00000000..9bd9ca65 --- /dev/null +++ b/benchmark_analyzer/src/model/context/mod.rs @@ -0,0 +1,35 @@ +//! +//! A context for benchmarking, passed by compiler-tester. +//! + +/// +/// A context for benchmarking, passed by compiler-tester. +/// +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Context { + /// Unique identifier of the machine. + pub machine: String, + /// Target, for example "eravm" or "evm". + pub target: era_compiler_common::Target, + /// Type of `solc`, for example `zksync` + pub toolchain: String, +} + +/// +/// Checks that the context is well-formed. +/// +pub fn validate_context(context: &Context) -> anyhow::Result<()> { + let Context { + machine, + toolchain, + target: _, + } = context; + + if machine.is_empty() { + anyhow::bail!("The `machine` field in the benchmark context is empty") + } + if toolchain.is_empty() { + anyhow::bail!("The `toolchain` field in the benchmark context is empty") + } + Ok(()) +} diff --git a/benchmark_analyzer/src/model/evm_interpreter.rs b/benchmark_analyzer/src/model/evm_interpreter.rs new file mode 100644 index 00000000..91e09836 --- /dev/null +++ b/benchmark_analyzer/src/model/evm_interpreter.rs @@ -0,0 +1,148 @@ +//! +//! Collects the benchmark model's definitions related to EVM Interpreter test suite. +//! + +/// Path to EVM interpreter test. +pub const TEST_PATH: &str = "tests/solidity/complex/interpreter/test.json"; + +/// Component of a name of the results group where tests for EVM opcodes reside. +pub const GROUP_NAME: &str = "EVMInterpreter"; + +/// The EVM opcodes to test. +pub const OPCODES: [&str; 135] = [ + "ADD", + "MUL", + "SUB", + "DIV", + "SDIV", + "MOD", + "SMOD", + "ADDMOD", + "MULMOD", + "EXP", + "SIGNEXTEND", + "LT", + "GT", + "SLT", + "SGT", + "EQ", + "ISZERO", + "AND", + "OR", + "XOR", + "NOT", + "BYTE", + "SHL", + "SHR", + "SAR", + "SGT", + "SHA3", + "ADDRESS", + "BALANCE", + "ORIGIN", + "CALLER", + "CALLVALUE", + "CALLDATALOAD", + "CALLDATASIZE", + "CALLDATACOPY", + "CODESIZE", + "CODECOPY", + "GASPRICE", + "EXTCODESIZE", + "EXTCODECOPY", + "RETURNDATASIZE", + "RETURNDATACOPY", + "EXTCODEHASH", + "BLOCKHASH", + "COINBASE", + "TIMESTAMP", + "NUMBER", + "PREVRANDAO", + "GASLIMIT", + "CHAINID", + "SELFBALANCE", + "BASEFEE", + "POP", + "MLOAD", + "MSTORE", + "MSTORE8", + "SLOAD", + "SSTORE", + "JUMP", + "JUMPI", + "PC", + "MSIZE", + "GAS", + "JUMPDEST", + "PUSH0", + "PUSH1", + "PUSH2", + "PUSH4", + "PUSH5", + "PUSH6", + "PUSH7", + "PUSH8", + "PUSH9", + "PUSH10", + "PUSH11", + "PUSH12", + "PUSH13", + "PUSH14", + "PUSH15", + "PUSH16", + "PUSH17", + "PUSH18", + "PUSH19", + "PUSH20", + "PUSH21", + "PUSH22", + "PUSH23", + "PUSH24", + "PUSH25", + "PUSH26", + "PUSH27", + "PUSH28", + "PUSH29", + "PUSH30", + "PUSH31", + "PUSH32", + "DUP1", + "DUP2", + "DUP3", + "DUP4", + "DUP5", + "DUP6", + "DUP7", + "DUP8", + "DUP9", + "DUP10", + "DUP11", + "DUP12", + "DUP13", + "DUP14", + "DUP15", + "DUP16", + "SWAP1", + "SWAP2", + "SWAP3", + "SWAP4", + "SWAP5", + "SWAP6", + "SWAP7", + "SWAP8", + "SWAP9", + "SWAP10", + "SWAP11", + "SWAP12", + "SWAP13", + "SWAP14", + "SWAP15", + "SWAP16", + "CALL", + "STATICCALL", + "DELEGATECALL", + "CREATE", + "CREATE2", + "RETURN", + "REVERT", +]; diff --git a/benchmark_analyzer/src/model/mod.rs b/benchmark_analyzer/src/model/mod.rs new file mode 100644 index 00000000..1c6ad9a0 --- /dev/null +++ b/benchmark_analyzer/src/model/mod.rs @@ -0,0 +1,7 @@ +//! +//! Defines a model of benchmark data. +//! + +pub mod benchmark; +pub mod context; +pub mod evm_interpreter; diff --git a/benchmark_analyzer/src/output/comparison_result.rs b/benchmark_analyzer/src/output/comparison_result.rs new file mode 100644 index 00000000..7f25f048 --- /dev/null +++ b/benchmark_analyzer/src/output/comparison_result.rs @@ -0,0 +1,16 @@ +//! +//! Result of comparing two benchmarks. +//! + +use super::file::File; + +/// +/// Result of comparing two benchmarks. +/// +pub enum Output { + /// Benchmark output is a single unnamed file. + SingleFile(String), + /// Benchmark output is structured as a file tree, relative to some + /// user-provided output directory. + MultipleFiles(Vec), +} diff --git a/benchmark_analyzer/src/output/file.rs b/benchmark_analyzer/src/output/file.rs new file mode 100644 index 00000000..8813c0aa --- /dev/null +++ b/benchmark_analyzer/src/output/file.rs @@ -0,0 +1,15 @@ +//! +//! Represents a single benchmark output file in a set of many. +//! + +use std::path::PathBuf; + +/// +/// Represents a single benchmark output file in a set of many. +/// +pub struct File { + /// Path to this file relative to user-provided root. + pub path: PathBuf, + /// File contents. + pub contents: String, +} diff --git a/benchmark_analyzer/src/output/format/csv.rs b/benchmark_analyzer/src/output/format/csv.rs new file mode 100644 index 00000000..52c3fa12 --- /dev/null +++ b/benchmark_analyzer/src/output/format/csv.rs @@ -0,0 +1,88 @@ +//! +//! Serializing benchmark data to CSV. +//! + +use std::fmt::Write as _; + +use crate::model::benchmark::test::metadata::Metadata as TestMetadata; +use crate::model::benchmark::test::selector::Selector; +use crate::model::benchmark::test::Test; +use crate::model::benchmark::Benchmark; +use crate::output::IBenchmarkSerializer; +use crate::output::Output; + +/// +/// Serialize the benchmark to CSV in the following format: +/// "group", "codegen", "version", "optimizations", "path", "case", "input", "size", "cycles", "ergs", "gas"" +/// +#[derive(Default)] +pub struct Csv; + +impl IBenchmarkSerializer for Csv { + type Err = std::fmt::Error; + + fn serialize_to_string(&self, benchmark: &Benchmark) -> Result { + let mut result = String::with_capacity(estimate_csv_size(benchmark)); + result.push_str( + r#""group", "codegen", "version", "optimizations", "path", "case", "input", "size", "cycles", "ergs", "gas""#, + ); + + result.push('\n'); + for Test { + metadata: + TestMetadata { + tags, + selector: Selector { path, case, input }, + }, + codegen_groups, + } in benchmark.tests.values() + { + for (codegen, codegen_group) in codegen_groups { + for (version, versioned_group) in &codegen_group.versioned_groups { + for ( + optimizations, + crate::Executable { + run: + crate::Run { + size, + cycles, + ergs, + gas, + }, + .. + }, + ) in &versioned_group.executables + { + let tags = { + let mut tags = tags.clone(); + tags.sort(); + tags.join(" ") + }; + let size_str = size.map(|s| s.to_string()).unwrap_or_default(); + let input = input.clone().map(|s| s.to_string()).unwrap_or_default(); + let case = case.as_deref().unwrap_or_default(); + writeln!( + &mut result, + r#""{tags}", "{codegen}", "{version}", "{optimizations}", "{path}", "{case}", "{input}", {size_str}, {cycles}, {ergs}, {gas}"#, + )?; + } + } + } + } + Ok(Output::SingleFile(result)) + } +} + +fn estimate_csv_line_length() -> usize { + let number_fields = 4; + let number_field_estimated_max_length = 15; + let group_name_estimated_max = 10; + let test_name_estimated_max = 300; + group_name_estimated_max + + test_name_estimated_max + + number_fields * number_field_estimated_max_length +} + +fn estimate_csv_size(benchmark: &Benchmark) -> usize { + (benchmark.tests.len() + 1) * estimate_csv_line_length() +} diff --git a/benchmark_analyzer/src/output/format/json/lnt/benchmark/machine.rs b/benchmark_analyzer/src/output/format/json/lnt/benchmark/machine.rs new file mode 100644 index 00000000..8536b686 --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/lnt/benchmark/machine.rs @@ -0,0 +1,20 @@ +//! +//! Description of the `machine` section in the JSON file generated for LNT. +//! See https://llvm.org/docs/lnt/importing_data.html +//! + +/// +/// Description of the `machine` section in the JSON file generated for LNT. +/// See https://llvm.org/docs/lnt/importing_data.html +/// +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct Machine { + /// Machine name, for example "LNT-AArch64-A53-O3__clang_DEV__aarch64". + pub name: String, + /// Target name, for example "eravm" or "solc". + pub target: era_compiler_common::Target, + /// Optimizations level, for example "+M3B3". + pub optimizations: String, + /// Type of solc, for example, "zksync". + pub toolchain: String, +} diff --git a/benchmark_analyzer/src/output/format/json/lnt/benchmark/mod.rs b/benchmark_analyzer/src/output/format/json/lnt/benchmark/mod.rs new file mode 100644 index 00000000..2e8fda40 --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/lnt/benchmark/mod.rs @@ -0,0 +1,30 @@ +//! Root benchmark structure describing a single JSON file passed to LNT. +//! One such file is generated for every machine configuration. +//! See https://llvm.org/docs/lnt/importing_data.html + +pub mod machine; +pub mod run_description; +pub mod test_description; + +use machine::Machine; +use run_description::RunDescription; +use test_description::TestDescription; + +use crate::BenchmarkVersion; + +/// +/// Root benchmark structure describing a single JSON file passed to LNT. +/// One such file is generated for every machine configuration. +/// See https://llvm.org/docs/lnt/importing_data.html +/// +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct LntBenchmark { + /// Benchmark format version + pub format_version: BenchmarkVersion, + /// Machine description is used as a group identifier + pub machine: Machine, + /// Describes the runtime benchmark characteristics, for example, when it has started and when it has ended + pub run: RunDescription, + /// Tests grouped in this benchmark. + pub tests: Vec, +} diff --git a/benchmark_analyzer/src/output/format/json/lnt/benchmark/run_description.rs b/benchmark_analyzer/src/output/format/json/lnt/benchmark/run_description.rs new file mode 100644 index 00000000..bb0ae38d --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/lnt/benchmark/run_description.rs @@ -0,0 +1,17 @@ +//! +//! Description of the benchmark run in a JSON file passed to LNT. +//! + +use chrono::DateTime; +use chrono::Utc; + +/// +/// Description of the benchmark run in a JSON file passed to LNT. +/// +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct RunDescription { + /// Time when benchmark run was started. + pub start_time: DateTime, + /// Time when benchmark run was finished. + pub end_time: DateTime, +} diff --git a/benchmark_analyzer/src/output/format/json/lnt/benchmark/test_description.rs b/benchmark_analyzer/src/output/format/json/lnt/benchmark/test_description.rs new file mode 100644 index 00000000..17eb3371 --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/lnt/benchmark/test_description.rs @@ -0,0 +1,18 @@ +//! +//! Description of a single measurement in a JSON file passed to LNT. +//! + +use crate::model::benchmark::test::codegen::versioned::executable::run::Run; + +/// +/// Description of a single measurement in a JSON file passed to LNT. +/// +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct TestDescription { + /// A unique identifier of the test, incorporating language version, optimization levels and so on. + /// See [crate::output::format::json::lnt::test_name]. + pub name: String, + /// Measurements: gas, ergs, cycles, and size for contract deploys. + #[serde(flatten)] + pub measurements: Run, +} diff --git a/benchmark_analyzer/src/output/format/json/lnt/error.rs b/benchmark_analyzer/src/output/format/json/lnt/error.rs new file mode 100644 index 00000000..1b9e73ac --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/lnt/error.rs @@ -0,0 +1,25 @@ +//! +//! Errors occuring during generation of LNT-compatible JSON files. +//! + +/// +/// Errors occuring during generation of LNT-compatible JSON files. +/// +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub enum LntSerializationError { + /// + /// No instance of [crate::model::context::Context] is provided. + /// + NoContext, +} + +impl std::fmt::Display for LntSerializationError { + #[allow(unreachable_patterns)] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LntSerializationError::NoContext => f.write_str("LNT backend requires explicitly passed benchmark context, but no context was provided."), + _ => f.write_fmt(format_args!("{self:?}")), + } + } +} +impl std::error::Error for LntSerializationError {} diff --git a/benchmark_analyzer/src/output/format/json/lnt/mod.rs b/benchmark_analyzer/src/output/format/json/lnt/mod.rs new file mode 100644 index 00000000..be2d1c1e --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/lnt/mod.rs @@ -0,0 +1,127 @@ +//! +//! JSON format compatible with LNT. +//! + +pub mod benchmark; +pub mod error; + +use std::collections::BTreeMap; +use std::path::PathBuf; + +use benchmark::machine::Machine; +use benchmark::run_description::RunDescription; +use benchmark::test_description::TestDescription; +use benchmark::LntBenchmark; +use error::LntSerializationError; + +use crate::model::benchmark::test::metadata::Metadata as TestMetadata; +use crate::model::benchmark::test::selector::Selector; +use crate::model::benchmark::test::Test; +use crate::model::benchmark::Benchmark; +use crate::output::format::json::make_json_file; +use crate::output::IBenchmarkSerializer; +use crate::output::Output; + +/// +/// Serialize the benchmark to a set of JSON files compatible with LNT format. +/// +#[derive(Default)] +pub struct JsonLNT; + +/// +/// Generate the test name for a measurement, containing a unique test identifier. +/// +fn test_name(selector: &Selector, version: impl std::fmt::Display) -> String { + fn shorten_file_name(name: &str) -> String { + let path_buf = PathBuf::from(name); + path_buf + .file_name() + .expect("Always valid") + .to_str() + .expect("Always valid") + .to_string() + } + let Selector { path, case, input } = selector; + let short_path = shorten_file_name(path); + let short_input = match input { + Some(crate::Input::Deployer { + contract_identifier, + }) => Some(crate::Input::Deployer { + contract_identifier: shorten_file_name(contract_identifier), + }), + _ => input.clone(), + }; + format!( + "{} {version}", + Selector { + path: short_path.to_string(), + case: case.clone(), + input: short_input, + } + ) +} + +impl IBenchmarkSerializer for JsonLNT { + type Err = LntSerializationError; + + fn serialize_to_string(&self, benchmark: &Benchmark) -> anyhow::Result { + let mut files: BTreeMap = Default::default(); + + let context = if let Some(context) = &benchmark.metadata.context { + context + } else { + return Err(LntSerializationError::NoContext); + }; + + for Test { + metadata: TestMetadata { selector, .. }, + codegen_groups, + } in benchmark.tests.values() + { + for (codegen, codegen_group) in codegen_groups { + for (version, versioned_group) in &codegen_group.versioned_groups { + for ( + optimizations, + crate::Executable { + run: measurements, .. + }, + ) in &versioned_group.executables + { + let machine_name = format!("{}-{codegen}-{optimizations}", context.machine); + + let machine = Machine { + name: context.machine.clone(), + target: context.target, + optimizations: optimizations.to_owned(), + toolchain: context.toolchain.clone(), + }; + let run = RunDescription { + start_time: benchmark.metadata.start, + end_time: benchmark.metadata.end, + }; + files + .entry(machine_name) + .or_insert(LntBenchmark { + format_version: benchmark.metadata.version.clone(), + machine, + run, + tests: vec![], + }) + .tests + .push(TestDescription { + name: test_name(selector, version), + measurements: measurements.clone(), + }); + } + } + } + } + + Ok(Output::MultipleFiles( + files + .iter() + .map(|(key, value)| make_json_file(key, value)) + .collect(), + )) + } +} diff --git a/benchmark_analyzer/src/output/format/json/mod.rs b/benchmark_analyzer/src/output/format/json/mod.rs new file mode 100644 index 00000000..3aac068d --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/mod.rs @@ -0,0 +1,20 @@ +//! +//! Defines JSON-based output formats. +//! + +pub mod lnt; +pub mod native; + +use crate::output::file::File; + +/// +/// Create a new [`crate::output::File`] instance with an object serialized to JSON. +/// +pub(crate) fn make_json_file(filename: impl std::fmt::Display, object: T) -> File +where + T: Sized + serde::Serialize, +{ + let path = format!("{filename}.json").into(); + let contents = serde_json::to_string_pretty(&object).expect("Always valid"); + File { path, contents } +} diff --git a/benchmark_analyzer/src/output/format/json/native/mod.rs b/benchmark_analyzer/src/output/format/json/native/mod.rs new file mode 100644 index 00000000..42cf7f82 --- /dev/null +++ b/benchmark_analyzer/src/output/format/json/native/mod.rs @@ -0,0 +1,21 @@ +//! +//! Native JSON format that corresponds to the inner benchmark analyzer data model. +//! + +use crate::model::benchmark::Benchmark; +use crate::output::comparison_result::Output; +use crate::output::IBenchmarkSerializer; + +/// +/// Serialize the benchmark internal model to JSON using `serde` library. +/// +#[derive(Default)] +pub struct Json; + +impl IBenchmarkSerializer for Json { + type Err = serde_json::error::Error; + + fn serialize_to_string(&self, benchmark: &Benchmark) -> Result { + serde_json::to_string_pretty(benchmark).map(Output::SingleFile) + } +} diff --git a/benchmark_analyzer/src/output/format/mod.rs b/benchmark_analyzer/src/output/format/mod.rs new file mode 100644 index 00000000..4c33cf82 --- /dev/null +++ b/benchmark_analyzer/src/output/format/mod.rs @@ -0,0 +1,6 @@ +//! +//! Serialization of benchmark data in different output formats. +//! + +pub mod csv; +pub mod json; diff --git a/benchmark_analyzer/src/output/mod.rs b/benchmark_analyzer/src/output/mod.rs new file mode 100644 index 00000000..01903740 --- /dev/null +++ b/benchmark_analyzer/src/output/mod.rs @@ -0,0 +1,26 @@ +//! +//! Benchmark-analyzer output. +//! + +pub mod comparison_result; +pub mod file; +pub mod format; + +use comparison_result::Output; + +use crate::model::benchmark::Benchmark; + +/// +/// Serialization format for benchmark data. +/// +pub trait IBenchmarkSerializer { + /// + /// Type of serialization error. + /// + type Err: std::error::Error; + + /// + /// Serialize benchmark data in the selected format. + /// + fn serialize_to_string(&self, benchmark: &Benchmark) -> anyhow::Result; +} diff --git a/benchmark_analyzer/src/results/group.rs b/benchmark_analyzer/src/results/group.rs new file mode 100644 index 00000000..08b03131 --- /dev/null +++ b/benchmark_analyzer/src/results/group.rs @@ -0,0 +1,175 @@ +//! +//! A group of results +//! + +use crate::model::evm_interpreter; +use regex::Regex; +use std::fmt::Display; + +/// +/// Group of results. +/// +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub enum Group<'a> { + /// A group with EVM interpreter tests. + EVMInterpreter { + /// Codegen used to produce executables for all tests in this group. + codegen: &'a str, + /// Optimization level used to produce executables for all tests in this group. + optimizations: &'a str, + }, + /// A default group containing all tests. + Default { + /// Codegen used to produce executables for all tests in this group. + codegen: &'a str, + /// Optimization level used to produce executables for all tests in this group. + optimizations: &'a str, + }, + /// A user-named group. + Named { + /// Group name, provided by the user in the test definition file. + name: &'a str, + /// Codegen used to produce executables for all tests in this group. + codegen: &'a str, + /// Optimization level used to produce executables for all tests in this group. + optimizations: &'a str, + }, + /// A group comparing two groups with distinct names: + /// - one belonging to a reference run, + /// - another belonging to a candidate run. + Comparison { + /// Group belonging to the reference run. + reference: Box>, + /// Group belonging to the candidate run. + candidate: Box>, + }, +} + +impl Group<'_> { + fn comparison_name(reference: &Group<'_>, candidate: &Group<'_>) -> String { + if reference.name() == candidate.name() { + format!( + "{}: {}{} vs {}{}", + reference.name(), + reference.codegen().unwrap_or_default(), + reference.optimizations().unwrap_or_default(), + candidate.codegen().unwrap_or_default(), + candidate.optimizations().unwrap_or_default(), + ) + } else { + format!("{} vs {}", reference.name(), candidate.name()) + } + } + + /// + /// Returns true if the provided regular expression matches the string representation of the group. + /// + pub fn regex_matches(&self, regex: &Regex) -> bool { + !self.is_comparison() && (regex.is_match(&self.to_string())) + } + + /// + /// Codegen used in this group. + /// + pub fn codegen(&self) -> Option { + match self { + Group::EVMInterpreter { codegen, .. } => Some(codegen.to_string()), + Group::Default { codegen, .. } => Some(codegen.to_string()), + Group::Named { codegen, .. } => Some(codegen.to_string()), + Group::Comparison { .. } => None, + } + } + + /// + /// Optimizations used in this group. + /// + pub fn optimizations(&self) -> Option { + match self { + Group::EVMInterpreter { optimizations, .. } => Some(optimizations.to_string()), + Group::Default { optimizations, .. } => Some(optimizations.to_string()), + Group::Named { optimizations, .. } => Some(optimizations.to_string()), + Group::Comparison { .. } => None, + } + } + + /// + /// Name of the group. + /// + pub fn name(&self) -> String { + match self { + Group::EVMInterpreter { .. } => "EVMInterpreter".into(), + Group::Default { .. } => "All".into(), + Group::Named { name, .. } => name.to_string(), + Group::Comparison { .. } => "Comparison".into(), + } + } + + /// Returns `true` if the group is [`Comparison`]. + /// + /// [`Comparison`]: Group::Comparison + #[must_use] + pub fn is_comparison(&self) -> bool { + matches!(self, Self::Comparison { .. }) + } +} + +impl<'a> Group<'a> { + /// + /// Create a new group provided an optional tag, codegen and optimization level. + /// + pub fn from_tag(tag: Option<&'a str>, codegen: Option<&'a str>, opt: Option<&'a str>) -> Self { + let codegen = codegen.unwrap_or_default(); + let optimizations = opt.unwrap_or_default(); + match tag { + None => Self::Default { + optimizations, + codegen, + }, + Some(group_name) if group_name == evm_interpreter::GROUP_NAME => Self::EVMInterpreter { + optimizations, + codegen, + }, + Some(name) => Self::Named { + name, + codegen, + optimizations, + }, + } + } + + /// + /// Create a new group that compares two groups with distinct names: + /// - one belonging to a reference run, + /// - another belonging to a candidate run. + /// + pub fn new_comparison(reference: &Self, candidate: &Self) -> Self { + Self::Comparison { + reference: Box::new(reference.clone()), + candidate: Box::new(candidate.clone()), + } + } +} + +impl Display for Group<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Group::EVMInterpreter { + codegen, + optimizations, + } => f.write_fmt(format_args!("{} {codegen} {optimizations}", self.name())), + Group::Default { + codegen, + optimizations, + } => f.write_fmt(format_args!("{} {codegen} {optimizations}", self.name())), + Group::Named { + name, + codegen, + optimizations, + } => f.write_fmt(format_args!("{name} {codegen} {optimizations}")), + Group::Comparison { + reference, + candidate, + } => f.write_str(&Self::comparison_name(reference, candidate)), + } + } +} diff --git a/benchmark_analyzer/src/benchmark/group/results.rs b/benchmark_analyzer/src/results/mod.rs similarity index 86% rename from benchmark_analyzer/src/benchmark/group/results.rs rename to benchmark_analyzer/src/results/mod.rs index 3b306b92..e5970591 100644 --- a/benchmark_analyzer/src/benchmark/group/results.rs +++ b/benchmark_analyzer/src/results/mod.rs @@ -2,6 +2,9 @@ //! The benchmark group results. //! +pub mod group; + +use crate::model::benchmark::test::metadata::Metadata as TestMetadata; use colored::Colorize; use std::cmp; @@ -17,9 +20,9 @@ pub struct Results<'a> { /// The size total decrease result. pub size_total: f64, /// The size negative result test names. - pub size_negatives: Vec<(f64, &'a str)>, + pub size_negatives: Vec<(f64, &'a TestMetadata)>, /// The size positive result test names. - pub size_positives: Vec<(f64, &'a str)>, + pub size_positives: Vec<(f64, &'a TestMetadata)>, /// The cycles best result. pub cycles_best: f64, @@ -28,9 +31,9 @@ pub struct Results<'a> { /// The cycles total decrease result. pub cycles_total: f64, /// The cycles negative result test names. - pub cycles_negatives: Vec<(f64, &'a str)>, + pub cycles_negatives: Vec<(f64, &'a TestMetadata)>, /// The cycles positive result test names. - pub cycles_positives: Vec<(f64, &'a str)>, + pub cycles_positives: Vec<(f64, &'a TestMetadata)>, /// The ergs best result. pub ergs_best: f64, @@ -39,9 +42,9 @@ pub struct Results<'a> { /// The ergs total decrease result. pub ergs_total: f64, /// The ergs negative result test names. - pub ergs_negatives: Vec<(f64, &'a str)>, + pub ergs_negatives: Vec<(f64, &'a TestMetadata)>, /// The ergs positive result test names. - pub ergs_positives: Vec<(f64, &'a str)>, + pub ergs_positives: Vec<(f64, &'a TestMetadata)>, /// The gas best result. pub gas_best: f64, @@ -50,9 +53,9 @@ pub struct Results<'a> { /// The gas total decrease result. pub gas_total: f64, /// The gas negative result test names. - pub gas_negatives: Vec<(f64, &'a str)>, + pub gas_negatives: Vec<(f64, &'a TestMetadata)>, /// The gas positive result test names. - pub gas_positives: Vec<(f64, &'a str)>, + pub gas_positives: Vec<(f64, &'a TestMetadata)>, /// The EVM interpreter reference ratios. pub evm_interpreter_reference_ratios: Option>, @@ -69,26 +72,26 @@ impl<'a> Results<'a> { size_best: f64, size_worst: f64, size_total: f64, - size_negatives: Vec<(f64, &'a str)>, - size_positives: Vec<(f64, &'a str)>, + size_negatives: Vec<(f64, &'a TestMetadata)>, + size_positives: Vec<(f64, &'a TestMetadata)>, cycles_best: f64, cycles_worst: f64, cycles_total: f64, - cycles_negatives: Vec<(f64, &'a str)>, - cycles_positives: Vec<(f64, &'a str)>, + cycles_negatives: Vec<(f64, &'a TestMetadata)>, + cycles_positives: Vec<(f64, &'a TestMetadata)>, ergs_best: f64, ergs_worst: f64, ergs_total: f64, - ergs_negatives: Vec<(f64, &'a str)>, - ergs_positives: Vec<(f64, &'a str)>, + ergs_negatives: Vec<(f64, &'a TestMetadata)>, + ergs_positives: Vec<(f64, &'a TestMetadata)>, gas_best: f64, gas_worst: f64, gas_total: f64, - gas_negatives: Vec<(f64, &'a str)>, - gas_positives: Vec<(f64, &'a str)>, + gas_negatives: Vec<(f64, &'a TestMetadata)>, + gas_positives: Vec<(f64, &'a TestMetadata)>, ) -> Self { Self { size_best, @@ -206,7 +209,7 @@ impl<'a> Results<'a> { self.size_negatives.len() ); for (value, path) in self.size_negatives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); println!( @@ -216,7 +219,7 @@ impl<'a> Results<'a> { self.cycles_negatives.len() ); for (value, path) in self.cycles_negatives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); println!( @@ -226,7 +229,7 @@ impl<'a> Results<'a> { self.ergs_negatives.len() ); for (value, path) in self.ergs_negatives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); println!( @@ -236,7 +239,7 @@ impl<'a> Results<'a> { self.gas_negatives.len() ); for (value, path) in self.gas_negatives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); @@ -247,7 +250,7 @@ impl<'a> Results<'a> { self.size_positives.len() ); for (value, path) in self.size_positives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); println!( @@ -257,7 +260,7 @@ impl<'a> Results<'a> { self.cycles_positives.len() ); for (value, path) in self.cycles_positives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); println!( @@ -267,7 +270,7 @@ impl<'a> Results<'a> { self.ergs_positives.len() ); for (value, path) in self.ergs_positives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); println!( @@ -277,7 +280,7 @@ impl<'a> Results<'a> { self.gas_positives.len() ); for (value, path) in self.gas_positives.iter().take(count) { - println!("{:010}: {}", Self::format_f64(*value), path); + println!("{:010}: {}", Self::format_f64(*value), path.selector); } println!(); } @@ -293,24 +296,24 @@ impl<'a> Results<'a> { w, "╔═╡ {} ╞{}╡ {} ╞═╗", "Size (-%)".bright_white(), - "═".repeat(cmp::max(24 - group_name.len(), 0)), + "═".repeat(cmp::max(44 - group_name.len(), 0)), group_name.bright_white() )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Best".bright_white(), Self::format_f64(self.size_best) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Worst".bright_white(), Self::format_f64(self.size_worst) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Total".bright_white(), Self::format_f64(self.size_total) )?; @@ -319,24 +322,24 @@ impl<'a> Results<'a> { w, "╠═╡ {} ╞{}╡ {} ╞═╣", "Cycles (-%)".bright_white(), - "═".repeat(cmp::max(22 - group_name.len(), 0)), + "═".repeat(cmp::max(42 - group_name.len(), 0)), group_name.bright_white() )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Best".bright_white(), Self::format_f64(self.cycles_best) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Worst".bright_white(), Self::format_f64(self.cycles_worst) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Total".bright_white(), Self::format_f64(self.cycles_total) )?; @@ -345,24 +348,24 @@ impl<'a> Results<'a> { w, "╠═╡ {} ╞{}╡ {} ╞═╣", "Ergs (-%)".bright_white(), - "═".repeat(cmp::max(24 - group_name.len(), 0)), + "═".repeat(cmp::max(44 - group_name.len(), 0)), group_name.bright_white() )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Best".bright_white(), Self::format_f64(self.ergs_best) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Worst".bright_white(), Self::format_f64(self.ergs_worst) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Total".bright_white(), Self::format_f64(self.ergs_total) )?; @@ -371,24 +374,24 @@ impl<'a> Results<'a> { w, "╠══╡ {} ╞{}╡ {} ╞═╣", "Gas (-%)".bright_white(), - "═".repeat(cmp::max(24 - group_name.len(), 0)), + "═".repeat(cmp::max(44 - group_name.len(), 0)), group_name.bright_white() )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Best".bright_white(), Self::format_f64(self.gas_best) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Worst".bright_white(), Self::format_f64(self.gas_worst) )?; writeln!( w, - "║ {:33} {:07} ║", + "║ {:53} {:07} ║", "Total".bright_white(), Self::format_f64(self.gas_total) )?; @@ -401,7 +404,7 @@ impl<'a> Results<'a> { w, "╠═╡ {} ╞{}╡ {} ╞═╣", "Ergs/gas".bright_white(), - "═".repeat(cmp::max(25 - group_name.len(), 0)), + "═".repeat(cmp::max(45 - group_name.len(), 0)), group_name.bright_white() )?; for (opcode, reference_ratio) in gas_reference_ratios.iter() { @@ -421,7 +424,7 @@ impl<'a> Results<'a> { writeln!( w, - "║ {:32} {} ║", + "║ {:52} {} ║", if is_positive { opcode.green() } else if is_negative { @@ -443,7 +446,7 @@ impl<'a> Results<'a> { w, "╠═╡ {} ╞{}╡ {} ╞═╣", "Ergs/gas (-%)".bright_white(), - "═".repeat(cmp::max(20 - group_name.len(), 0)), + "═".repeat(cmp::max(40 - group_name.len(), 0)), group_name.bright_white() )?; for (opcode, reference_ratio) in gas_reference_ratios.iter() { @@ -466,7 +469,7 @@ impl<'a> Results<'a> { writeln!( w, - "║ {:32} {} ║", + "║ {:52} {} ║", if is_positive { opcode.green() } else if is_negative { @@ -485,7 +488,10 @@ impl<'a> Results<'a> { } } } - writeln!(w, "╚═══════════════════════════════════════════╝")?; + writeln!( + w, + "╚═══════════════════════════════════════════════════════════════╝" + )?; Ok(()) } diff --git a/benchmark_analyzer/src/util/btreemap.rs b/benchmark_analyzer/src/util/btreemap.rs new file mode 100644 index 00000000..437f562c --- /dev/null +++ b/benchmark_analyzer/src/util/btreemap.rs @@ -0,0 +1,162 @@ +//! +//! Utility functions +//! + +use std::collections::BTreeMap; + +/// Intersects two `BTreeMap` instances and merges their entries using a +/// specified merger function. +/// +/// # Arguments +/// +/// * `map1` - The first `BTreeMap` containing keys of type `K` and values of +/// type `V1`. +/// * `map2` - The second `BTreeMap` containing keys of type `K` and values of +/// type `V2`. This map is modified during the intersection. +/// * `merger` - A closure that takes a key of type `K`, and a value from each +/// map (`V1` and `V2`), and returns a merged result of type `R`. +/// +/// # Returns +/// +/// An iterator that yields merged results of type `R` for each intersecting key +/// from the maps. +/// +/// # Example +/// +/// ```rust +/// use benchmark_analyzer::util::btreemap::intersect_map; +/// +/// let first = [(1, 1), (2, 2), (3, 3)]; +/// let second = [(1, 10), (3, 30)]; +/// let expected: Vec<_> = [111, 333].into(); +/// assert_eq!( +/// intersect_map(first.into(), second.into(), |k, v1, v2| 100 * k + v1 + v2) +/// .collect::>(), +/// expected +/// ) +/// ``` + +pub fn intersect_map( + map1: BTreeMap, + mut map2: BTreeMap, + merger: impl Fn(K, V1, V2) -> R + 'static, +) -> impl Iterator +where + K: Ord, +{ + map1.into_iter().filter_map(move |(key, value1)| { + map2.remove(&key).map(|value2| merger(key, value1, value2)) + }) +} + +/// Perform a cross join on two `BTreeMap` instances, applying a +/// selector function to each pair of keys. If the selector function returns an +/// `Option::Some`, it includes the transformed key along with cloned values +/// from both maps into the result vector. +/// +/// # Arguments +/// +/// * `map1` - A reference to the first `BTreeMap` with key type `K` and value +/// type `V1`. +/// * `map2` - A reference to the second `BTreeMap` with key type `K` and value +/// type `V2`. +/// * `selector` - A closure or function that takes two keys (one from each map) +/// and returns an `Option`, where `N` is the type of the transformed key +/// to be included in the result if the option is `Some`. +/// +/// # Returns +/// +/// A vector containing tuples of the transformed key type `N`, and values from +/// both maps (`V1` and `V2`), corresponding to each pair of matched keys for +/// which the selector function has returned `Some`. +/// +/// # Type Parameters +/// +/// * `K` - The type of key used in both input maps, which must implement `Ord`. +/// * `N` - The type for transformed key pairs. +/// * `V1` - The type of value in the first map, which must implement `Clone`. +/// * `V2` - The type of value in the second map, which must implement `Clone`. +/// +/// # Example +/// +/// ```rust +/// use std::collections::BTreeMap; +/// +/// // Assume we have two BTreeMaps. +/// let map1: BTreeMap<_, _> = [(1, "a"), (2, "b")].iter().cloned().collect(); +/// let map2: BTreeMap<_, _> = [(1, "x"), (2, "y")].iter().cloned().collect(); +/// +/// // Define a selector function that combines the keys. +/// let selector = |k1: &i32, k2: &i32| if k1 == k2 { Some(k1 + k2) } else { None }; +/// +/// // Execute the cross join with filtering using the selector. +/// let result = cross_join_filter_map(&map1, &map2, selector); +/// +/// // Result now contains: [(2, "a", "x"), (4, "b", "y")] +/// assert_eq!(result, vec![(2, "a", "x"), (4, "b", "y")]); +/// ``` + +pub fn cross_join_filter_map( + map1: &BTreeMap, + map2: &BTreeMap, + selector: impl Fn(&K, &K) -> Option, +) -> Vec<(N, V1, V2)> +where + K: Ord, + V1: Clone, + V2: Clone, +{ + let mut result: Vec<(N, V1, V2)> = Vec::new(); + + for (key1, value1) in map1 { + for (key2, value2) in map2 { + if let Some(new_key) = selector(key1, key2) { + result.push((new_key, value1.clone(), value2.clone())); + } + } + } + + result +} + +/// Returns an iterator over +/// the elements that are common to both `map1` and `map2`. +/// +/// # Arguments +/// +/// * `map1` - A BTreeMap where the keys are compared. +/// * `map2` - A mutable BTreeMap from which matching keys are removed and their values paired with those from `map1`. +/// +/// # Returns +/// +/// An iterator over tuples `(K, V1, V2)` where: +/// * `K` is the common key. +/// * `V1` is the associated value from `map1`. +/// * `V2` is the associated value from `map2`. +/// +/// The iterator only includes keys that are present in both maps. +/// +/// # Example +/// +/// ```rust +/// use benchmark_analyzer::util::btreemap::intersect_keys; +/// +/// let first = [(1, "1"), (2, "2"), (3, "3")]; +/// let second = [(1, "11"), (3, "33")]; +/// let expected: Vec<_> = [(1, "1", "11"), (3, "3", "33")].into(); +/// assert_eq!( +/// intersect_keys(first.into(), second.into()).collect::>(), +/// expected +/// ) +/// ``` +/// +pub fn intersect_keys( + map1: BTreeMap, + mut map2: BTreeMap, +) -> impl Iterator +where + K: Ord, +{ + map1.into_iter() + .filter_map(move |(key, value1)| map2.remove(&key).map(|value2| (key, value1, value2))) +} diff --git a/benchmark_analyzer/src/util/mod.rs b/benchmark_analyzer/src/util/mod.rs new file mode 100644 index 00000000..e0e4bfb0 --- /dev/null +++ b/benchmark_analyzer/src/util/mod.rs @@ -0,0 +1,5 @@ +//! +//! Utility functions. +//! + +pub mod btreemap; diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 5f573431..092d386e 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -38,6 +38,7 @@ once_cell = "=1.20.2" rayon = "=1.10.0" lazy_static = "=1.5.0" bincode = "=1.3.3" +chrono = "=0.4.38" evm = { git = "https://github.com/rust-ethereum/evm", rev = "f7a23df6c478ca6a151af5f60e62944800529a61" } revm = { git = "https://github.com/bluealloy/revm", rev = "fa5650ee8a4d802f4f3557014dd157adfb074460" } diff --git a/compiler_tester/src/benchmark_format.rs b/compiler_tester/src/compiler_tester/arguments/benchmark_format.rs similarity index 82% rename from compiler_tester/src/benchmark_format.rs rename to compiler_tester/src/compiler_tester/arguments/benchmark_format.rs index 28002bbb..4bbc89e5 100644 --- a/compiler_tester/src/benchmark_format.rs +++ b/compiler_tester/src/compiler_tester/arguments/benchmark_format.rs @@ -8,10 +8,12 @@ #[derive(Debug, Default, Clone, PartialEq, Eq)] pub enum BenchmarkFormat { #[default] - /// JSON format. + /// Unstable JSON format, corresponds to the inner data model of benchmark analyzer. Json, /// CSV format. Csv, + /// JSON format compatible with LNT. + JsonLNT, } impl std::str::FromStr for BenchmarkFormat { @@ -20,6 +22,7 @@ impl std::str::FromStr for BenchmarkFormat { fn from_str(string: &str) -> Result { match string.to_lowercase().as_str() { "json" => Ok(Self::Json), + "json-lnt" => Ok(Self::JsonLNT), "csv" => Ok(Self::Csv), string => anyhow::bail!( "Unknown benchmark format `{string}`. Supported formats: {}", @@ -37,6 +40,7 @@ impl std::fmt::Display for BenchmarkFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let repr = match self { BenchmarkFormat::Json => "json", + BenchmarkFormat::JsonLNT => "json-lnt", BenchmarkFormat::Csv => "csv", }; f.write_str(repr) diff --git a/compiler_tester/src/compiler_tester/arguments.rs b/compiler_tester/src/compiler_tester/arguments/mod.rs similarity index 84% rename from compiler_tester/src/compiler_tester/arguments.rs rename to compiler_tester/src/compiler_tester/arguments/mod.rs index 45e912e7..951b091b 100644 --- a/compiler_tester/src/compiler_tester/arguments.rs +++ b/compiler_tester/src/compiler_tester/arguments/mod.rs @@ -2,10 +2,16 @@ //! The compiler tester arguments. //! +pub mod benchmark_format; +pub mod validation; + use std::path::PathBuf; +use benchmark_format::BenchmarkFormat; use clap::Parser; +pub const ARGUMENT_BENCHMARK_CONTEXT: &str = "benchmark-context"; + /// /// The compiler tester arguments. /// @@ -40,9 +46,15 @@ pub struct Arguments { #[structopt(short, long)] pub benchmark: Option, - /// The benchmark output format. - #[structopt(long = "benchmark-format", default_value_t = compiler_tester::BenchmarkFormat::Json)] - pub benchmark_format: compiler_tester::BenchmarkFormat, + /// The benchmark output format: `json`, `csv`, or `json-lnt`. + /// Using `json-lnt` requires providing the path to a JSON file describing the + /// benchmarking context via `--benchmark-context`. + #[structopt(long = "benchmark-format", default_value_t = BenchmarkFormat::Json)] + pub benchmark_format: BenchmarkFormat, + + /// The benchmark context to pass additional data to backends. + #[structopt(long = ARGUMENT_BENCHMARK_CONTEXT )] + pub benchmark_context: Option, /// Sets the number of threads, which execute the tests concurrently. #[structopt(short, long)] diff --git a/compiler_tester/src/compiler_tester/arguments/validation.rs b/compiler_tester/src/compiler_tester/arguments/validation.rs new file mode 100644 index 00000000..2c1eb263 --- /dev/null +++ b/compiler_tester/src/compiler_tester/arguments/validation.rs @@ -0,0 +1,28 @@ +//! +//! Validate the arguments passed from user, checking invariants that are not +//! expressed in the type system. +//! + +use super::benchmark_format::BenchmarkFormat; +use super::Arguments; + +use super::ARGUMENT_BENCHMARK_CONTEXT; + +/// +/// Validate the arguments passed from user, checking invariants that are not +/// expressed in the type system. +/// +pub fn validate_arguments(arguments: Arguments) -> anyhow::Result { + match (&arguments.benchmark_format, &arguments.benchmark_context) { + (BenchmarkFormat::JsonLNT, None) => + anyhow::bail!("Generation of LNT-compatible benchmark results in JSON format requires passing a valid context in the argument `--{ARGUMENT_BENCHMARK_CONTEXT}` to compiler tester.") + , + (BenchmarkFormat::JsonLNT, Some(_)) => (), + (_, Some(_)) => + anyhow::bail!("Only LNT backend in JSON format supports passing a valid context in the argument `--{ARGUMENT_BENCHMARK_CONTEXT}` to compiler tester.") + , + _ => (), + } + + Ok(arguments) +} diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index 5735712c..36e7faec 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -8,6 +8,8 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::Instant; +use arguments::benchmark_format::BenchmarkFormat; +use arguments::validation::validate_arguments; use clap::Parser; use colored::Colorize; @@ -38,6 +40,7 @@ fn main() { /// The entry point wrapper used for proper error handling. /// fn main_inner(arguments: Arguments) -> anyhow::Result<()> { + let arguments = validate_arguments(arguments)?; println!( " {} {} v{} (LLVM build {})", "Starting".bright_green().bold(), @@ -93,17 +96,6 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { .build_global() .expect("Thread pool configuration failure"); - let summary = compiler_tester::Summary::new(arguments.verbose, arguments.quiet).wrap(); - - let filters = compiler_tester::Filters::new(arguments.path, arguments.mode, arguments.group); - - let compiler_tester = compiler_tester::CompilerTester::new( - summary.clone(), - filters, - debug_config.clone(), - arguments.workflow, - )?; - let toolchain = match (arguments.target, arguments.toolchain) { (era_compiler_common::Target::EraVM, Some(toolchain)) => toolchain, (era_compiler_common::Target::EraVM, None) => compiler_tester::Toolchain::IrLLVM, @@ -145,6 +137,19 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { ), }; + let summary = compiler_tester::Summary::new(arguments.verbose, arguments.quiet) + .start_timer()? + .wrap(); + + let filters = compiler_tester::Filters::new(arguments.path, arguments.mode, arguments.group); + + let compiler_tester = compiler_tester::CompilerTester::new( + summary.clone(), + filters, + debug_config.clone(), + arguments.workflow, + )?; + let run_time_start = Instant::now(); println!( " {} tests with {} worker threads", @@ -211,7 +216,7 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { } }?; - let summary = compiler_tester::Summary::unwrap_arc(summary); + let summary = compiler_tester::Summary::unwrap_arc(summary).stop_timer()?; print!("{summary}"); println!( " {} running tests in {}m{:02}s", @@ -221,14 +226,28 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { ); if let Some(path) = arguments.benchmark { - let benchmark = summary.benchmark(toolchain)?; + let context = if let Some(context_path) = arguments.benchmark_context { + Some(read_context(context_path)?) + } else { + None + }; + let benchmark = summary.benchmark(toolchain, context)?; match arguments.benchmark_format { - compiler_tester::BenchmarkFormat::Json => { - benchmark.write_to_file(path, benchmark_analyzer::JsonSerializer)? - } - compiler_tester::BenchmarkFormat::Csv => { - benchmark.write_to_file(path, benchmark_analyzer::CsvSerializer)? - } + BenchmarkFormat::Json => benchmark_analyzer::write_to_file( + &benchmark, + path, + benchmark_analyzer::JsonNativeSerializer, + )?, + BenchmarkFormat::Csv => benchmark_analyzer::write_to_file( + &benchmark, + path, + benchmark_analyzer::CsvSerializer, + )?, + BenchmarkFormat::JsonLNT => benchmark_analyzer::write_to_file( + &benchmark, + path, + benchmark_analyzer::JsonLNTSerializer, + )?, } } @@ -239,6 +258,24 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { Ok(()) } +/// +/// Reads the benchmarking context from a JSON file and validates its correctness. +/// Benchmarking context provides additional information about benchmarking that +/// will be used to generate a report. +/// +/// # Errors +/// +/// This function will return an error if +/// - file can't be read, +/// - deserialization from JSON file failed, +/// - the context validation failed. +fn read_context(path: PathBuf) -> anyhow::Result { + let contents = std::fs::read_to_string(path)?; + let context: benchmark_analyzer::BenchmarkContext = serde_json::de::from_str(&contents)?; + benchmark_analyzer::validate_context(&context)?; + Ok(context) +} + #[cfg(test)] mod tests { use std::path::PathBuf; @@ -257,7 +294,8 @@ mod tests { path: vec!["tests/solidity/simple/default.sol".to_owned()], group: vec![], benchmark: None, - benchmark_format: compiler_tester::BenchmarkFormat::Json, + benchmark_format: crate::BenchmarkFormat::Json, + benchmark_context: None, threads: Some(1), dump_system: false, disable_deployer: false, diff --git a/compiler_tester/src/compilers/eravm/mode.rs b/compiler_tester/src/compilers/eravm/mode.rs index 158b5e85..98dfbac6 100644 --- a/compiler_tester/src/compilers/eravm/mode.rs +++ b/compiler_tester/src/compilers/eravm/mode.rs @@ -2,6 +2,8 @@ //! The compiler tester EraVM mode. //! +use crate::compilers::mode::imode::IMode; + /// /// The compiler tester EraVM mode. /// @@ -13,3 +15,16 @@ impl std::fmt::Display for Mode { write!(f, "") } } +impl IMode for Mode { + fn optimizations(&self) -> Option { + None + } + + fn codegen(&self) -> Option { + None + } + + fn version(&self) -> Option { + None + } +} diff --git a/compiler_tester/src/compilers/llvm/mode.rs b/compiler_tester/src/compilers/llvm/mode.rs index 419038bc..54ec79a5 100644 --- a/compiler_tester/src/compilers/llvm/mode.rs +++ b/compiler_tester/src/compilers/llvm/mode.rs @@ -2,6 +2,7 @@ //! The compiler tester LLVM mode. //! +use crate::compilers::mode::imode::IMode; use crate::compilers::mode::llvm_options::LLVMOptions; use crate::compilers::mode::Mode as ModeWrapper; @@ -45,8 +46,22 @@ impl Mode { } } +impl IMode for Mode { + fn optimizations(&self) -> Option { + Some(format!("{}", self.llvm_optimizer_settings)) + } + + fn codegen(&self) -> Option { + None + } + + fn version(&self) -> Option { + None + } +} + impl std::fmt::Display for Mode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.llvm_optimizer_settings,) + write!(f, "{}", self.optimizations().unwrap_or_default(),) } } diff --git a/compiler_tester/src/compilers/mode/imode.rs b/compiler_tester/src/compilers/mode/imode.rs new file mode 100644 index 00000000..92a72475 --- /dev/null +++ b/compiler_tester/src/compilers/mode/imode.rs @@ -0,0 +1,31 @@ +//! +//! Common interface for different compiler modes. +//! + +/// +/// Common interface for different compiler modes. +/// +pub trait IMode { + /// Optimization level, if applicable. + fn optimizations(&self) -> Option; + + /// Codegen version, if applicable. + fn codegen(&self) -> Option; + + /// Language version, if applicable. + fn version(&self) -> Option; +} + +pub fn mode_to_string_aux(mode: &impl IMode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (i, element) in [mode.optimizations(), mode.codegen(), mode.version()] + .iter() + .flatten() + .enumerate() + { + if i > 0 { + write!(f, " ")?; + } + write!(f, "{}", element)?; + } + Ok(()) +} diff --git a/compiler_tester/src/compilers/mode/mod.rs b/compiler_tester/src/compilers/mode/mod.rs index e2e8b95f..5bcb58b5 100644 --- a/compiler_tester/src/compilers/mode/mod.rs +++ b/compiler_tester/src/compilers/mode/mod.rs @@ -2,9 +2,13 @@ //! The compiler mode. //! +pub mod imode; pub mod llvm_options; use std::collections::HashSet; +use std::fmt::Display; + +use imode::{mode_to_string_aux, IMode}; use crate::compilers::eravm::mode::Mode as EraVMMode; use crate::compilers::llvm::mode::Mode as LLVMMode; @@ -253,16 +257,46 @@ impl From for Mode { } } -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl IMode for Mode { + fn optimizations(&self) -> Option { + match self { + Mode::Solidity(mode) => mode.optimizations(), + Mode::SolidityUpstream(mode) => mode.optimizations(), + Mode::Yul(mode) => mode.optimizations(), + Mode::YulUpstream(mode) => mode.optimizations(), + Mode::Vyper(mode) => mode.optimizations(), + Mode::LLVM(mode) => mode.optimizations(), + Mode::EraVM(mode) => mode.optimizations(), + } + } + + fn codegen(&self) -> Option { + match self { + Mode::Solidity(mode) => mode.codegen(), + Mode::SolidityUpstream(mode) => mode.codegen(), + Mode::Yul(mode) => mode.codegen(), + Mode::YulUpstream(mode) => mode.codegen(), + Mode::Vyper(mode) => mode.codegen(), + Mode::LLVM(mode) => mode.codegen(), + Mode::EraVM(mode) => mode.codegen(), + } + } + + fn version(&self) -> Option { match self { - Self::Solidity(inner) => write!(f, "{inner}"), - Self::SolidityUpstream(inner) => write!(f, "{inner}"), - Self::Yul(inner) => write!(f, "{inner}"), - Self::YulUpstream(inner) => write!(f, "{inner}"), - Self::Vyper(inner) => write!(f, "{inner}"), - Self::LLVM(inner) => write!(f, "{inner}"), - Self::EraVM(inner) => write!(f, "{inner}"), + Mode::Solidity(mode) => mode.version(), + Mode::SolidityUpstream(mode) => mode.version(), + Mode::Yul(mode) => mode.version(), + Mode::YulUpstream(mode) => mode.version(), + Mode::Vyper(mode) => mode.version(), + Mode::LLVM(mode) => mode.version(), + Mode::EraVM(mode) => mode.version(), } } } + +impl Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + mode_to_string_aux(self, f) + } +} diff --git a/compiler_tester/src/compilers/solidity/mode.rs b/compiler_tester/src/compilers/solidity/mode.rs index 964fd3d9..f89463c6 100644 --- a/compiler_tester/src/compilers/solidity/mode.rs +++ b/compiler_tester/src/compilers/solidity/mode.rs @@ -6,6 +6,7 @@ use itertools::Itertools; use crate::compilers::mode::llvm_options::LLVMOptions; +use crate::compilers::mode::imode::IMode; use crate::compilers::mode::Mode as ModeWrapper; /// @@ -126,25 +127,29 @@ impl Mode { } } } +} - /// - /// Returns a string representation excluding the solc version. - /// - pub fn repr_without_version(&self) -> String { - format!( - "{}{}{}", - match self.solc_codegen { +impl IMode for Mode { + fn optimizations(&self) -> Option { + Some(format!( + "{}{}", + if self.solc_optimize { '+' } else { '-' }, + self.llvm_optimizer_settings, + )) + } + + fn codegen(&self) -> Option { + Some( + (match self.solc_codegen { era_solc::StandardJsonInputCodegen::Yul => "Y", era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => "I", era_solc::StandardJsonInputCodegen::EVMLA => "E", - }, - if self.solc_optimize { '+' } else { '-' }, - self.llvm_optimizer_settings, + }) + .to_string(), ) } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {}", self.repr_without_version(), self.solc_version) + + fn version(&self) -> Option { + Some(self.solc_version.to_string()) } } diff --git a/compiler_tester/src/compilers/solidity/upstream/mode.rs b/compiler_tester/src/compilers/solidity/upstream/mode.rs index 46a84994..4d4d9616 100644 --- a/compiler_tester/src/compilers/solidity/upstream/mode.rs +++ b/compiler_tester/src/compilers/solidity/upstream/mode.rs @@ -4,7 +4,7 @@ use itertools::Itertools; -use crate::compilers::mode::Mode as ModeWrapper; +use crate::compilers::mode::{imode::IMode, Mode as ModeWrapper}; /// /// The compiler tester Solidity mode. @@ -111,28 +111,29 @@ impl Mode { } } } +} - /// - /// Returns a string representation excluding the solc version. - /// - pub fn repr_without_version(&self) -> String { - if self.via_mlir { - return "L".to_owned(); - } - format!( - "{}{}", - match self.solc_codegen { - era_solc::StandardJsonInputCodegen::Yul => "Y", - era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => "I", - era_solc::StandardJsonInputCodegen::EVMLA => "E", - }, - if self.solc_optimize { '+' } else { '-' }, +impl IMode for Mode { + fn optimizations(&self) -> Option { + Some((if self.solc_optimize { "+" } else { "-" }).to_string()) + } + + fn codegen(&self) -> Option { + Some( + (if self.via_mlir { + "L" + } else { + match self.solc_codegen { + era_solc::StandardJsonInputCodegen::Yul => "Y", + era_solc::StandardJsonInputCodegen::EVMLA if self.via_ir => "I", + era_solc::StandardJsonInputCodegen::EVMLA => "E", + } + }) + .to_string(), ) } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {}", self.repr_without_version(), self.solc_version) + fn version(&self) -> Option { + Some(self.solc_version.to_string()) } } diff --git a/compiler_tester/src/compilers/vyper/mode.rs b/compiler_tester/src/compilers/vyper/mode.rs index fcbd9be2..03a80fbf 100644 --- a/compiler_tester/src/compilers/vyper/mode.rs +++ b/compiler_tester/src/compilers/vyper/mode.rs @@ -2,6 +2,7 @@ //! The compiler tester Vyper mode. //! +use crate::compilers::mode::imode::IMode; use crate::compilers::mode::llvm_options::LLVMOptions; use crate::compilers::mode::Mode as ModeWrapper; @@ -74,21 +75,21 @@ impl Mode { } }) } - - /// - /// Returns a string representation excluding the vyper version. - /// - pub fn repr_without_version(&self) -> String { - format!( - "V{}{}", +} +impl IMode for Mode { + fn optimizations(&self) -> Option { + Some(format!( + "{}{}", if self.vyper_optimize { '+' } else { '-' }, self.llvm_optimizer_settings, - ) + )) + } + + fn codegen(&self) -> Option { + Some("V".into()) } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {}", self.repr_without_version(), self.vyper_version,) + fn version(&self) -> Option { + Some(format!("{}", self.vyper_version)) } } diff --git a/compiler_tester/src/compilers/yul/mode.rs b/compiler_tester/src/compilers/yul/mode.rs index 2f405409..2044eb07 100644 --- a/compiler_tester/src/compilers/yul/mode.rs +++ b/compiler_tester/src/compilers/yul/mode.rs @@ -2,6 +2,7 @@ //! The compiler tester Yul mode. //! +use crate::compilers::mode::imode::IMode; use crate::compilers::mode::llvm_options::LLVMOptions; use crate::compilers::mode::Mode as ModeWrapper; @@ -51,8 +52,16 @@ impl Mode { } } -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.llvm_optimizer_settings) +impl IMode for Mode { + fn optimizations(&self) -> Option { + Some(format!("{}", self.llvm_optimizer_settings)) + } + + fn codegen(&self) -> Option { + None + } + + fn version(&self) -> Option { + None } } diff --git a/compiler_tester/src/compilers/yul/mode_upstream.rs b/compiler_tester/src/compilers/yul/mode_upstream.rs index a7a9db7f..9f6faffa 100644 --- a/compiler_tester/src/compilers/yul/mode_upstream.rs +++ b/compiler_tester/src/compilers/yul/mode_upstream.rs @@ -2,7 +2,7 @@ //! The compiler tester upstream Yul mode. //! -use crate::compilers::mode::Mode as ModeWrapper; +use crate::compilers::mode::{imode::IMode, Mode as ModeWrapper}; /// /// The compiler tester upstream Yul mode. @@ -42,21 +42,18 @@ impl Mode { _ => panic!("Non-Yul-upstream mode"), } } +} - /// - /// Returns a string representation excluding the solc version. - /// - pub fn repr_without_version(&self) -> String { - if self.via_mlir { - String::from("L") - } else { - format!("Y{}", if self.solc_optimize { '+' } else { '-' },) - } +impl IMode for Mode { + fn optimizations(&self) -> Option { + Some((if self.solc_optimize { "+" } else { "-" }).to_string()) + } + + fn codegen(&self) -> Option { + Some((if self.via_mlir { "L" } else { "Y" }).to_string()) } -} -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {}", self.repr_without_version(), self.solc_version) + fn version(&self) -> Option { + Some(format!("{}", self.solc_version)) } } diff --git a/compiler_tester/src/directories/matter_labs/test/mod.rs b/compiler_tester/src/directories/matter_labs/test/mod.rs index e79a194d..184d2fca 100644 --- a/compiler_tester/src/directories/matter_labs/test/mod.rs +++ b/compiler_tester/src/directories/matter_labs/test/mod.rs @@ -304,14 +304,18 @@ impl MatterLabsTest { Ok(instances) } + fn is_evm_interpreter_test(&self) -> bool { + matches!( + self.metadata.group.as_deref(), + Some(benchmark_analyzer::TEST_GROUP_EVM_INTERPRETER) + ) + } /// /// Returns cases needed for running benchmarks on the EVM interpreter. /// - fn evm_interpreter_benchmark_cases(&self) -> Vec { - if self.metadata.group.as_deref() - != Some(benchmark_analyzer::Benchmark::EVM_INTERPRETER_GROUP_NAME) - { - return vec![]; + fn evm_interpreter_benchmark_cases(&self) -> Option> { + if !self.is_evm_interpreter_test() { + return None; } let mut evm_contracts: Vec = self @@ -398,7 +402,7 @@ impl MatterLabsTest { expected_evm: None, }) } - metadata_cases + Some(metadata_cases) } } @@ -467,8 +471,13 @@ impl Buildable for MatterLabsTest { }; instances.extend(evm_instances); - let mut metadata_cases = self.metadata.cases.to_owned(); - metadata_cases.extend(self.evm_interpreter_benchmark_cases()); + let metadata_cases = { + let mut base_cases = self.metadata.cases.to_owned(); + if let Some(opcode_test_cases) = self.evm_interpreter_benchmark_cases() { + base_cases.extend(opcode_test_cases); + } + base_cases + }; let mut cases = Vec::with_capacity(metadata_cases.len()); for case in metadata_cases.into_iter() { diff --git a/compiler_tester/src/lib.rs b/compiler_tester/src/lib.rs index c177a3e0..ff5c2a57 100644 --- a/compiler_tester/src/lib.rs +++ b/compiler_tester/src/lib.rs @@ -7,7 +7,6 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] -pub(crate) mod benchmark_format; pub(crate) mod compilers; pub(crate) mod directories; pub(crate) mod environment; @@ -27,7 +26,6 @@ use itertools::Itertools; use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; -pub use crate::benchmark_format::BenchmarkFormat; pub use crate::compilers::eravm::EraVMCompiler; pub use crate::compilers::llvm::LLVMCompiler; pub use crate::compilers::mode::llvm_options::LLVMOptions; diff --git a/compiler_tester/src/summary/benchmark_adapters/input.rs b/compiler_tester/src/summary/benchmark_adapters/input.rs index e5ff9ad2..85b59b97 100644 --- a/compiler_tester/src/summary/benchmark_adapters/input.rs +++ b/compiler_tester/src/summary/benchmark_adapters/input.rs @@ -24,6 +24,9 @@ impl From for benchmark_analyzer::Input { InputIdentifier::Balance { input_index } => { benchmark_analyzer::Input::Balance { input_index } } + InputIdentifier::Fallback { input_index } => { + benchmark_analyzer::Input::Fallback { input_index } + } } } } diff --git a/compiler_tester/src/summary/benchmark_adapters/metadata.rs b/compiler_tester/src/summary/benchmark_adapters/metadata.rs index d713251d..cef3f580 100644 --- a/compiler_tester/src/summary/benchmark_adapters/metadata.rs +++ b/compiler_tester/src/summary/benchmark_adapters/metadata.rs @@ -3,52 +3,9 @@ //! use crate::test::description::TestDescription; -use crate::Mode; -pub fn convert_description( - description: &TestDescription, - default_group: &str, -) -> benchmark_analyzer::Metadata { - let TestDescription { - group, - mode, - selector, - } = description.clone(); - let selector = selector.into(); - let version = match &mode { - Some(mode) => mode_version(mode.clone()).map(|m| m.to_string()), - None => None, - }; - let mode = mode.map(mode_string).unwrap_or_default(); - let group = group.unwrap_or(default_group.to_string()); - benchmark_analyzer::Metadata { - selector, - mode, - version, - group, - } -} - -fn mode_version(mode: Mode) -> Option { - match mode { - Mode::Solidity(mode) => Some(mode.solc_version), - Mode::SolidityUpstream(mode) => Some(mode.solc_version), - Mode::Yul(_) => None, - Mode::YulUpstream(mode) => Some(mode.solc_version), - Mode::Vyper(mode) => Some(mode.vyper_version), - Mode::LLVM(_) => None, - Mode::EraVM(_) => None, - } -} - -fn mode_string(mode: Mode) -> Option { - match mode { - Mode::Solidity(mode) => Some(mode.repr_without_version()), - Mode::SolidityUpstream(mode) => Some(mode.repr_without_version()), - Mode::Yul(_) => None, - Mode::YulUpstream(mode) => Some(mode.repr_without_version()), - Mode::Vyper(mode) => Some(mode.repr_without_version()), - Mode::LLVM(_) => None, - Mode::EraVM(_) => None, +impl From for benchmark_analyzer::ExecutableMetadata { + fn from(_: TestDescription) -> Self { + benchmark_analyzer::ExecutableMetadata {} } } diff --git a/compiler_tester/src/summary/benchmark_adapters/mod.rs b/compiler_tester/src/summary/benchmark_adapters/mod.rs index 9968d960..24643d12 100644 --- a/compiler_tester/src/summary/benchmark_adapters/mod.rs +++ b/compiler_tester/src/summary/benchmark_adapters/mod.rs @@ -5,4 +5,5 @@ pub mod input; pub mod metadata; +pub mod mode; pub mod selector; diff --git a/compiler_tester/src/summary/benchmark_adapters/mode.rs b/compiler_tester/src/summary/benchmark_adapters/mode.rs new file mode 100644 index 00000000..76b063de --- /dev/null +++ b/compiler_tester/src/summary/benchmark_adapters/mode.rs @@ -0,0 +1,36 @@ +//! +//! Representation of compiler mode stored in the benchmark. +//! + +use crate::compilers::mode::imode::IMode; + +const DEFAULT_CODEGEN: &str = "NoCodegen"; +const DEFAULT_OPTIMIZATIONS: &str = "NoOptimizations"; +const DEFAULT_VERSION: &str = "NoVersion"; + +/// +/// Representation of compiler mode stored in the benchmark. +/// +pub struct ModeInfo { + /// Codegen type if applicable, or a default value [`DEFAULT_CODEGEN`]. + pub codegen: String, + /// Optimization level if applicable, or a default value [`DEFAULT_OPTIMIZATIONS`]. + pub optimizations: String, + /// Language version if applicable, or a default value [`DEFAULT_VERSION`]. + pub version: String, +} + +impl From for ModeInfo +where + T: IMode, +{ + fn from(value: T) -> ModeInfo { + ModeInfo { + codegen: value.codegen().unwrap_or(DEFAULT_CODEGEN.into()), + optimizations: value + .optimizations() + .unwrap_or(DEFAULT_OPTIMIZATIONS.into()), + version: value.version().unwrap_or(DEFAULT_VERSION.into()), + } + } +} diff --git a/compiler_tester/src/summary/mod.rs b/compiler_tester/src/summary/mod.rs index 1f20ff10..fdf5ef0a 100644 --- a/compiler_tester/src/summary/mod.rs +++ b/compiler_tester/src/summary/mod.rs @@ -8,17 +8,21 @@ pub mod element; use std::sync::Arc; use std::sync::Mutex; -use benchmark_adapters::metadata::convert_description; +use benchmark_adapters::mode::ModeInfo; use colored::Colorize; use crate::test::case::input::output::Output; use crate::test::description::TestDescription; use crate::toolchain::Toolchain; +use crate::utils::timer::Timer; use self::element::outcome::passed_variant::PassedVariant; use self::element::outcome::Outcome; use self::element::Element; +const BENCHMARK_FORMAT_VERSION: benchmark_analyzer::BenchmarkVersion = + benchmark_analyzer::BenchmarkVersion::V2; + /// /// The compiler tester summary. /// @@ -38,6 +42,7 @@ pub struct Summary { invalid: usize, /// The ignored tests counter. ignored: usize, + timer: Timer, } impl Summary { @@ -56,9 +61,33 @@ impl Summary { failed: 0, invalid: 0, ignored: 0, + timer: Timer::default(), } } + /// Starts the timer associated with the object. + /// + /// # Returns + /// + /// `anyhow::Result`: If the timer is successfully started, the function + /// returns `Ok(self)`, allowing method chaining. If the timer is in an invalid + /// state (e.g., already started or stopped), it returns an error. + pub fn start_timer(mut self) -> anyhow::Result { + self.timer.start()?; + Ok(self) + } + + /// Stops the timer associated with the object. + /// + /// # Returns + /// + /// `anyhow::Result`: If the timer is successfully stopped, the function + /// returns `Ok(self)`, permitting method chaining. An error is returned if the + /// timer is in an invalid state (e.g., not started or already stopped). + pub fn stop_timer(mut self) -> anyhow::Result { + self.timer.stop()?; + Ok(self) + } /// /// Whether the test run has been successful. /// @@ -78,40 +107,39 @@ impl Summary { /// /// Returns the benchmark structure. /// - pub fn benchmark(&self, toolchain: Toolchain) -> anyhow::Result { + pub fn benchmark( + &self, + toolchain: Toolchain, + context: Option, + ) -> anyhow::Result { + if let Toolchain::SolcLLVM = toolchain { + anyhow::bail!("The benchmarking is not supported for the SolcLLVM toolchain.") + } + let mut benchmark = benchmark_analyzer::Benchmark::default(); - match toolchain { - Toolchain::IrLLVM => { - benchmark.groups.insert( - format!( - "{} {}", - benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME, - era_compiler_llvm_context::OptimizerSettings::cycles(), - ), - benchmark_analyzer::BenchmarkGroup::default(), - ); - benchmark.groups.insert( - format!( - "{} {}", - benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME, - era_compiler_llvm_context::OptimizerSettings::size(), - ), - benchmark_analyzer::BenchmarkGroup::default(), - ); - } - Toolchain::Solc => { - benchmark.groups.insert( - benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME.to_owned(), - benchmark_analyzer::BenchmarkGroup::default(), - ); - } - Toolchain::SolcLLVM => { - anyhow::bail!("The benchmarking is not supported for the SolcLLVM toolchain.") - } + + if let (Some(start), Some(end)) = (self.timer.get_start(), self.timer.get_end()) { + benchmark.metadata = benchmark_analyzer::BenchmarkMetadata { + version: BENCHMARK_FORMAT_VERSION, + start, + end, + context, + }; + } else { + anyhow::bail!("Invalid state: the time of running the benchmark was not measured."); } - for element in self.elements.iter() { - let (size, cycles, ergs, group, gas) = match &element.outcome { + for Element { + test_description: + TestDescription { + group, + mode, + selector, + }, + outcome, + } in self.elements.iter() + { + let (size, cycles, ergs, gas) = match outcome { Outcome::Passed { variant: PassedVariant::Deploy { @@ -120,59 +148,51 @@ impl Summary { ergs, gas, }, - group, - } => (Some(*size), *cycles, *ergs, group.clone(), *gas), + .. + } => (Some(*size), *cycles, *ergs, *gas), Outcome::Passed { variant: PassedVariant::Runtime { cycles, ergs, gas }, - group, - } => (None, *cycles, *ergs, group.clone(), *gas), + .. + } => (None, *cycles, *ergs, *gas), _ => continue, }; - let key = format!( - "{:24} {}", - element - .test_description - .mode - .as_ref() - .map(|mode| mode.to_string()) - .unwrap_or_default(), - element.test_description.selector - ); - let mode = element - .test_description - .mode - .as_ref() - .and_then(|mode| mode.llvm_optimizer_settings().cloned()); - - let metadata = { - let default_group = group.clone().unwrap_or_default(); - convert_description(&element.test_description, &default_group) - }; - let benchmark_element = - benchmark_analyzer::BenchmarkElement::new(metadata, size, cycles, ergs, gas); - if let Some(group) = group { - let group_key = match mode { - Some(ref mode) => format!("{group} {mode}"), - None => group, - }; - benchmark - .groups - .entry(group_key) - .or_default() - .elements - .insert(key.clone(), benchmark_element.clone()); - } - - let group_key = match mode { - Some(ref mode) => { - format!("{} {mode}", benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME) - } - None => benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME.to_owned(), - }; - if let Some(group) = benchmark.groups.get_mut(group_key.as_str()) { - group.elements.insert(key, benchmark_element); - } + let test_name = selector.to_string(); + + let tags: Vec = group.iter().cloned().collect(); + + let ModeInfo { + codegen, + optimizations, + version, + } = mode + .clone() + .expect("The compiler mode is missing from description.") + .into(); + + benchmark + .tests + .entry(test_name) + .or_insert(benchmark_analyzer::Test::new( + benchmark_analyzer::TestMetadata::new(selector.clone().into(), tags), + )) + .codegen_groups + .entry(codegen) + .or_insert(Default::default()) + .versioned_groups + .entry(version) + .or_insert(Default::default()) + .executables + .entry(optimizations) + .or_insert(benchmark_analyzer::Executable { + metadata: benchmark_analyzer::ExecutableMetadata {}, + run: benchmark_analyzer::Run { + size, + cycles, + ergs, + gas, + }, + }); } Ok(benchmark) } diff --git a/compiler_tester/src/test/case/input/identifier.rs b/compiler_tester/src/test/case/input/identifier.rs index 6210abf5..21bcbd8b 100644 --- a/compiler_tester/src/test/case/input/identifier.rs +++ b/compiler_tester/src/test/case/input/identifier.rs @@ -9,6 +9,8 @@ pub enum InputIdentifier { /// The contract deploy, regardless of target. Deployer { contract_identifier: String }, + /// The fallback method. + Fallback { input_index: usize }, /// The contract call. Runtime { input_index: usize, name: String }, /// The storage empty check. @@ -32,6 +34,9 @@ impl std::fmt::Display for InputIdentifier { InputIdentifier::Balance { input_index } => { f.write_fmt(format_args!("#balance_check:{input_index}")) } + InputIdentifier::Fallback { input_index } => { + f.write_fmt(format_args!("#fallback:{input_index}")) + } } } } diff --git a/compiler_tester/src/test/case/input/runtime.rs b/compiler_tester/src/test/case/input/runtime.rs index 7070f5cf..f6f5cbc5 100644 --- a/compiler_tester/src/test/case/input/runtime.rs +++ b/compiler_tester/src/test/case/input/runtime.rs @@ -84,15 +84,12 @@ impl Runtime { let input_index = context.selector; let test = TestDescription::from_context( context, - InputIdentifier::Runtime { - input_index, - name: self.name, - }, + Self::select_input_identifier(self.name, input_index), ); let name = test.selector.to_string(); vm.populate_storage(self.storage.inner); let vm_function = match group.as_deref() { - Some(benchmark_analyzer::Benchmark::EVM_INTERPRETER_GROUP_NAME) => { + Some(benchmark_analyzer::TEST_GROUP_EVM_INTERPRETER) => { EraVM::execute_evm_interpreter:: } _ => EraVM::execute::, @@ -126,6 +123,13 @@ impl Runtime { } } + fn select_input_identifier(name: String, input_index: usize) -> InputIdentifier { + match name.as_str() { + "#fallback" => InputIdentifier::Fallback { input_index }, + _ => InputIdentifier::Runtime { input_index, name }, + } + } + /// /// Runs the call on EVM emulator. /// @@ -138,10 +142,7 @@ impl Runtime { let input_index = context.selector; let test = TestDescription::from_context( context, - InputIdentifier::Runtime { - input_index, - name: self.name, - }, + Self::select_input_identifier(self.name, input_index), ); let name = test.selector.to_string(); vm.populate_storage(self.storage.inner); diff --git a/compiler_tester/src/test/context/mod.rs b/compiler_tester/src/test/context/mod.rs index 1f756d3b..849b7764 100644 --- a/compiler_tester/src/test/context/mod.rs +++ b/compiler_tester/src/test/context/mod.rs @@ -1,2 +1,7 @@ +//! +//! Context accumulates information as we traverse the tree of tests, cases and +//! their inputs. +//! + pub mod case; pub mod input; diff --git a/compiler_tester/src/utils.rs b/compiler_tester/src/utils/mod.rs similarity index 99% rename from compiler_tester/src/utils.rs rename to compiler_tester/src/utils/mod.rs index 3d606c98..23b0d796 100644 --- a/compiler_tester/src/utils.rs +++ b/compiler_tester/src/utils/mod.rs @@ -2,6 +2,8 @@ //! The compiler tester utils. //! +pub mod timer; + use sha3::Digest; /// diff --git a/compiler_tester/src/utils/timer.rs b/compiler_tester/src/utils/timer.rs new file mode 100644 index 00000000..a53dbf05 --- /dev/null +++ b/compiler_tester/src/utils/timer.rs @@ -0,0 +1,115 @@ +//! +//! A simple timer capable of measuring time intervals. +//! + +#![allow(dead_code)] + +type TimeStamp = chrono::DateTime; + +/// +/// A simple timer capable of measuring time intervals between invokations of +/// `[start]` and `[stop]` methods. +/// +#[derive(Clone, Default, Debug)] +pub struct Timer { + /// Start time, + start: Option, + /// End time, + end: Option, +} + +impl Timer { + /// Starts the timer. This sets the `start` time to the current time. + /// + /// # Returns + /// + /// An Ok result if the timer was successfully started, or an error if + /// the timer was already started or stopped before. + /// + pub fn start(&mut self) -> anyhow::Result<()> { + match (self.start, self.end) { + (None, None) => { + self.start = Some(chrono::offset::Utc::now()); + Ok(()) + } + _ => anyhow::bail!("Malformed timer state: {self:?}"), + } + } + + /// Stops the timer from ticking. Assumes the timer has been started with + /// `[start]`. + /// + /// # Returns + /// + /// An Ok result if the timer was successfully stopped, or an error if + /// the timer has not been started or if it was already stopped. + /// + pub fn stop(&mut self) -> anyhow::Result<()> { + match (self.start, self.end) { + (Some(_), None) => { + self.end = Some(chrono::offset::Utc::now()); + Ok(()) + } + _ => anyhow::bail!("Malformed timer state: {self:?}"), + } + } + + /// + /// Checks if timer is ticking now. + /// + /// # Returns + /// + /// `true` if the timer has been started and is currently running, `false` otherwise. + /// + pub fn is_started(&self) -> bool { + self.start.is_some() + } + + /// Returns true if the timer was started and then subsequently stopped. + /// + /// # Returns + /// + /// `true` if the timer has finished (started and stopped), `false` otherwise. + /// + pub fn is_finished(&self) -> bool { + self.end.is_some() + } + + /// Returns the start time of this [`Timer`]. + /// + /// # Returns + /// + /// An `Option` containing the start `TimeStamp` if the timer has been started, + /// or `None` if it has not been started. + /// + pub fn get_start(&self) -> Option { + self.start + } + + /// Returns the end time of this [`Timer`]. + /// + /// # Returns + /// + /// An `Option` containing the end `TimeStamp` if the timer has been stopped, + /// or `None` if it is still running or has not been started. + /// + pub fn get_end(&self) -> Option { + self.end + } + + /// Returns the elapsed time between the start and end of the timer. + /// + /// # Returns + /// + /// An Ok result with the `chrono::TimeDelta` representing the elapsed time + /// if the timer was started (and optionally stopped), or an error if the + /// timer was not started correctly. + /// + pub fn elapsed(&self) -> anyhow::Result { + match (self.start, self.end) { + (Some(start), Some(end)) => Ok(end - start), + (Some(start), None) => Ok(chrono::Utc::now() - start), + _ => anyhow::bail!("Malformed timer state: {self:?}"), + } + } +} From a0674971313b7b678384841c9e3c05327c3bccb6 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Thu, 9 Jan 2025 23:49:08 +0800 Subject: [PATCH 30/31] release: v1.5.9 (#131) --- Cargo.lock | 178 ++++++++++-------- benchmark_analyzer/src/util/btreemap.rs | 2 + compiler_tester/src/compilers/eravm/mod.rs | 9 +- compiler_tester/src/compilers/llvm/mod.rs | 8 +- compiler_tester/src/compilers/solidity/mod.rs | 29 +-- compiler_tester/src/compilers/vyper/mod.rs | 12 +- compiler_tester/src/compilers/yul/mod.rs | 14 +- 7 files changed, 139 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d576cfbc..e2781d99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6259a506ab13e1d658796c31e6e39d2e2ee89243bcc505ddc613b35732e0a430" +checksum = "788bb18e8f61d5d9340b52143f27771daf7e1dccbaf2741621d2493f9debf52e" dependencies = [ "alloy-rlp", "bytes", @@ -93,7 +93,6 @@ dependencies = [ "const-hex", "derive_more 1.0.0", "foldhash", - "hex-literal", "indexmap", "itoa", "k256", @@ -127,7 +126,7 @@ checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -360,7 +359,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -600,9 +599,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.5" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "shlex", ] @@ -676,7 +675,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -931,7 +930,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -951,7 +950,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", "unicode-xid", ] @@ -984,7 +983,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -1051,7 +1050,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -1062,7 +1061,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -1117,8 +1116,8 @@ dependencies = [ [[package]] name = "era-compiler-solidity" -version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#901dc4ef0ca77a9edda10b2af1c08e8c0f00c054" +version = "1.5.9" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2333c0e275e4a83f494990ae3ce7484c643a908d" dependencies = [ "anyhow", "clap", @@ -1144,8 +1143,8 @@ dependencies = [ [[package]] name = "era-compiler-vyper" -version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#7ac06bd910b64343216d65b9251383d9c0d24d33" +version = "1.5.9" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#51f8baf7514e2482145d1b2f1e37615fb50bb2ca" dependencies = [ "anyhow", "boolinator", @@ -1168,8 +1167,8 @@ dependencies = [ [[package]] name = "era-solc" -version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#901dc4ef0ca77a9edda10b2af1c08e8c0f00c054" +version = "1.5.9" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2333c0e275e4a83f494990ae3ce7484c643a908d" dependencies = [ "anyhow", "boolinator", @@ -1185,8 +1184,8 @@ dependencies = [ [[package]] name = "era-yul" -version = "1.5.8" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#901dc4ef0ca77a9edda10b2af1c08e8c0f00c054" +version = "1.5.9" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2333c0e275e4a83f494990ae3ce7484c643a908d" dependencies = [ "anyhow", "regex", @@ -1432,7 +1431,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -1571,12 +1570,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - [[package]] name = "hmac" version = "0.12.1" @@ -1894,7 +1887,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -1953,7 +1946,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -1988,7 +1981,7 @@ source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-17#c5d783 dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -2152,9 +2145,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -2392,7 +2385,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -2439,7 +2432,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -2565,35 +2558,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.9", + "thiserror 2.0.10", "ucd-trie", ] [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -2733,7 +2726,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.9", + "thiserror 2.0.10", "tokio", "tracing", ] @@ -2752,7 +2745,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.9", + "thiserror 2.0.10", "tinyvec", "tracing", "web-time", @@ -2774,9 +2767,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2932,9 +2925,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -2963,6 +2956,7 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-rustls", + "tower", "tower-service", "url", "wasm-bindgen", @@ -3158,9 +3152,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ "bitflags 2.6.0", "errno", @@ -3273,7 +3267,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -3357,9 +3351,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -3418,7 +3412,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -3674,9 +3668,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.91" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -3706,7 +3700,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -3738,12 +3732,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3760,11 +3755,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.10", ] [[package]] @@ -3775,18 +3770,18 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -3834,9 +3829,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -3905,9 +3900,30 @@ checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.6.20", + "winnow 0.6.22", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3933,7 +3949,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -4115,7 +4131,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", "wasm-bindgen-shared", ] @@ -4150,7 +4166,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4201,7 +4217,7 @@ dependencies = [ "once_cell", "parking_lot", "pin-project", - "reqwest 0.12.9", + "reqwest 0.12.12", "rlp", "secp256k1 0.28.2", "serde", @@ -4429,9 +4445,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] @@ -4493,7 +4509,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", "synstructure", ] @@ -4515,7 +4531,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -4535,7 +4551,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", "synstructure", ] @@ -4556,7 +4572,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] @@ -4578,7 +4594,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.95", ] [[package]] diff --git a/benchmark_analyzer/src/util/btreemap.rs b/benchmark_analyzer/src/util/btreemap.rs index 437f562c..cdf1c816 100644 --- a/benchmark_analyzer/src/util/btreemap.rs +++ b/benchmark_analyzer/src/util/btreemap.rs @@ -82,6 +82,8 @@ where /// ```rust /// use std::collections::BTreeMap; /// +/// use benchmark_analyzer::util::btreemap::cross_join_filter_map; +/// /// // Assume we have two BTreeMaps. /// let map1: BTreeMap<_, _> = [(1, "a"), (2, "b")].iter().cloned().collect(); /// let map2: BTreeMap<_, _> = [(1, "x"), (2, "y")].iter().cloned().collect(); diff --git a/compiler_tester/src/compilers/eravm/mod.rs b/compiler_tester/src/compilers/eravm/mod.rs index 57eb6897..39745912 100644 --- a/compiler_tester/src/compilers/eravm/mod.rs +++ b/compiler_tester/src/compilers/eravm/mod.rs @@ -4,7 +4,6 @@ pub mod mode; -use std::collections::BTreeMap; use std::collections::HashMap; use era_solc::CollectableError; @@ -27,7 +26,7 @@ impl Compiler for EraVMCompiler { &self, _test_path: String, sources: Vec<(String, String)>, - _libraries: era_solc::StandardJsonInputLibraries, + libraries: era_solc::StandardJsonInputLibraries, _mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -55,9 +54,9 @@ impl Compiler for EraVMCompiler { true, debug_config.clone(), )?; - build.collect_errors()?; - let build = build.link(BTreeMap::new()); - build.collect_errors()?; + build.check_errors()?; + let build = build.link(libraries.as_linker_symbols()?); + build.check_errors()?; let builds = build .results .into_iter() diff --git a/compiler_tester/src/compilers/llvm/mod.rs b/compiler_tester/src/compilers/llvm/mod.rs index d55d3c44..55d24a2f 100644 --- a/compiler_tester/src/compilers/llvm/mod.rs +++ b/compiler_tester/src/compilers/llvm/mod.rs @@ -72,9 +72,9 @@ impl Compiler for LLVMCompiler { true, debug_config.clone(), )?; - build.collect_errors()?; + build.check_errors()?; let build = build.link(linker_symbols); - build.collect_errors()?; + build.check_errors()?; let builds = build .results .into_iter() @@ -113,13 +113,13 @@ impl Compiler for LLVMCompiler { let build = project.compile_to_evm( &mut vec![], + era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, - era_compiler_common::HashType::Ipfs, None, debug_config.clone(), )?; - build.collect_errors()?; + build.check_errors()?; let builds: HashMap = build .results .into_iter() diff --git a/compiler_tester/src/compilers/solidity/mod.rs b/compiler_tester/src/compilers/solidity/mod.rs index d6a98f06..38a5af88 100644 --- a/compiler_tester/src/compilers/solidity/mod.rs +++ b/compiler_tester/src/compilers/solidity/mod.rs @@ -182,7 +182,7 @@ impl SolidityCompiler { let mut output_selection = era_solc::StandardJsonInputSelection::new_required(mode.solc_codegen); output_selection.extend(era_solc::StandardJsonInputSelection::new(vec![ - era_solc::StandardJsonInputSelectionFlag::EraVMAssembly, + era_solc::StandardJsonInputSelector::EraVMAssembly, ])); let evm_version = if mode.solc_version >= era_solc::Compiler::FIRST_CANCUN_VERSION { @@ -212,7 +212,10 @@ impl SolidityCompiler { output_selection, era_solc::StandardJsonInputMetadata::default(), vec![], - vec![era_solc::StandardJsonInputErrorType::SendTransfer], + vec![ + era_solc::StandardJsonInputErrorType::SendTransfer, + era_solc::StandardJsonInputErrorType::AssemblyCreate, + ], vec![], false, mode.via_ir, @@ -334,7 +337,7 @@ impl Compiler for SolidityCompiler { let mut solc_output = self .standard_json_output_cached(test_path, &sources, &libraries, mode) .map_err(|error| anyhow::anyhow!("Solidity standard JSON I/O error: {}", error))?; - solc_output.collect_errors()?; + solc_output.check_errors()?; let method_identifiers = Self::get_method_identifiers(&solc_output) .map_err(|error| anyhow::anyhow!("Failed to get method identifiers: {}", error))?; @@ -367,9 +370,9 @@ impl Compiler for SolidityCompiler { true, debug_config, )?; - build.collect_errors()?; + build.check_errors()?; let build = build.link(linker_symbols); - build.collect_errors()?; + build.check_errors()?; let builds = build .results .iter() @@ -377,13 +380,15 @@ impl Compiler for SolidityCompiler { let build = build.to_owned().expect("Always valid"); let build = era_compiler_llvm_context::EraVMBuild::new_with_bytecode_hash( build.build.bytecode, - build.build.bytecode_hash.expect("Always valid"), + build.build.bytecode_hash.ok_or_else(|| { + anyhow::anyhow!("Bytecode hash not found in the build artifacts") + })?, None, build.build.assembly, ); - (path.to_owned(), build) + Ok((path.to_owned(), build)) }) - .collect(); + .collect::>>()?; build.write_to_standard_json( &mut solc_output, @@ -393,7 +398,7 @@ impl Compiler for SolidityCompiler { Self::LAST_ZKSYNC_SOLC_REVISION, )), )?; - solc_output.collect_errors()?; + solc_output.check_errors()?; Ok(EraVMInput::new( builds, @@ -416,7 +421,7 @@ impl Compiler for SolidityCompiler { let mut solc_output = self.standard_json_output_cached(test_path, &sources, &libraries, mode)?; - solc_output.collect_errors()?; + solc_output.check_errors()?; let method_identifiers = Self::get_method_identifiers(&solc_output)?; @@ -434,13 +439,13 @@ impl Compiler for SolidityCompiler { let build = project.compile_to_evm( &mut vec![], + era_compiler_common::HashType::Ipfs, mode.llvm_optimizer_settings.to_owned(), llvm_options, - era_compiler_common::HashType::Ipfs, None, debug_config, )?; - build.collect_errors()?; + build.check_errors()?; let builds: HashMap = build .results .into_iter() diff --git a/compiler_tester/src/compilers/vyper/mod.rs b/compiler_tester/src/compilers/vyper/mod.rs index a110b797..907d6505 100644 --- a/compiler_tester/src/compilers/vyper/mod.rs +++ b/compiler_tester/src/compilers/vyper/mod.rs @@ -196,7 +196,7 @@ impl Compiler for VyperCompiler { &self, test_path: String, sources: Vec<(String, String)>, - _libraries: era_solc::StandardJsonInputLibraries, + libraries: era_solc::StandardJsonInputLibraries, mode: &Mode, llvm_options: Vec, debug_config: Option, @@ -234,20 +234,22 @@ impl Compiler for VyperCompiler { vec![], debug_config, )?; - build.link(BTreeMap::new())?; + build.link(libraries.as_linker_symbols()?)?; let builds = build .contracts .into_iter() .map(|(path, contract)| { let build = era_compiler_llvm_context::EraVMBuild::new_with_bytecode_hash( contract.build.bytecode, - contract.build.bytecode_hash.expect("Always exists"), + contract.build.bytecode_hash.ok_or_else(|| { + anyhow::anyhow!("Bytecode hash not found in the build artifacts") + })?, None, contract.build.assembly, ); - (path, build) + Ok((path, build)) }) - .collect::>(); + .collect::>>()?; Ok(EraVMInput::new( builds, diff --git a/compiler_tester/src/compilers/yul/mod.rs b/compiler_tester/src/compilers/yul/mod.rs index f8b45d66..133f710a 100644 --- a/compiler_tester/src/compilers/yul/mod.rs +++ b/compiler_tester/src/compilers/yul/mod.rs @@ -102,21 +102,23 @@ impl Compiler for YulCompiler { true, debug_config.clone(), )?; - build.collect_errors()?; + build.check_errors()?; let build = build.link(linker_symbols); - build.collect_errors()?; + build.check_errors()?; let builds = build .results - .into_iter() - .map(|(path, result)| { + .into_values() + .map(|result| { let contract = result.expect("Always valid"); let build = era_compiler_llvm_context::EraVMBuild::new_with_bytecode_hash( contract.build.bytecode, - contract.build.bytecode_hash.expect("Always exists"), + contract.build.bytecode_hash.ok_or_else(|| { + anyhow::anyhow!("Bytecode hash not found in the build artifacts") + })?, None, contract.build.assembly, ); - Ok((path, build)) + Ok((contract.name.path, build)) }) .collect::>>()?; From cddaada18e62d97702f5a55416dbbd5036fe8569 Mon Sep 17 00:00:00 2001 From: Igor Zhirkov Date: Fri, 10 Jan 2025 14:55:54 +0100 Subject: [PATCH 31/31] Fix mode filters (#132) --- README.md | 3 ++- compiler_tester/src/compilers/mode/imode.rs | 4 ++-- compiler_tester/src/compilers/mode/mod.rs | 1 + compiler_tester/src/filters.rs | 7 ++++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 76142535..2eabfa22 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,6 @@ Most of the specifiers support wildcards `*` (any), `^` ('3' and 'z'). With no mode argument, iterates over all option combinations (approximately 800). - ## Usage Each command assumes you are at the root of the `compiler-tester` repository. @@ -228,6 +227,8 @@ cargo run --release --bin compiler-tester -- -DT \ --zksolc '../era-compiler-solidity/target/release/zksolc' ``` +Modes are insensitive to spaces, therefore options such as `'Y+M3B3 0.8.26'` and `'Y + M3B3 0.8.26'` are equivalent. + ### Example 2 Run all simple Yul tests. This currently runs about three hundred tests and takes about eight minutes. diff --git a/compiler_tester/src/compilers/mode/imode.rs b/compiler_tester/src/compilers/mode/imode.rs index 92a72475..e479d05a 100644 --- a/compiler_tester/src/compilers/mode/imode.rs +++ b/compiler_tester/src/compilers/mode/imode.rs @@ -17,13 +17,13 @@ pub trait IMode { } pub fn mode_to_string_aux(mode: &impl IMode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for (i, element) in [mode.optimizations(), mode.codegen(), mode.version()] + for (i, element) in [mode.codegen(), mode.optimizations(), mode.version()] .iter() .flatten() .enumerate() { if i > 0 { - write!(f, " ")?; + write!(f, " ")?; } write!(f, "{}", element)?; } diff --git a/compiler_tester/src/compilers/mode/mod.rs b/compiler_tester/src/compilers/mode/mod.rs index 5bcb58b5..7072f1d5 100644 --- a/compiler_tester/src/compilers/mode/mod.rs +++ b/compiler_tester/src/compilers/mode/mod.rs @@ -211,6 +211,7 @@ impl Mode { } } + current = current.replace(' ', ""); current } } diff --git a/compiler_tester/src/filters.rs b/compiler_tester/src/filters.rs index 7e09992c..8a0ed2e7 100644 --- a/compiler_tester/src/filters.rs +++ b/compiler_tester/src/filters.rs @@ -30,7 +30,12 @@ impl Filters { ) -> Self { Self { path_filters: path_filters.into_iter().collect(), - mode_filters: mode_filters.into_iter().collect(), + // Mode filters are stripped of spaces so filters like "Y+M3B3 + // 0.2.1 " and "Y +M3B3 0.2.1" become equivalent + mode_filters: mode_filters + .into_iter() + .map(|f| f.replace(' ', "")) + .collect(), group_filters: group_filters.into_iter().collect(), } }

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 Date: Sun, 13 Oct 2024 14:30:07 +0800 Subject: [PATCH 10/31] feat: test solc v0.8.28 (#111) --- .github/workflows/sanitizers.yml | 4 +- Cargo.lock | 28 ++++-- compiler_tester/src/compiler_tester/main.rs | 2 +- .../src/test/case/input/balance.rs | 22 ++++- .../src/test/case/input/deploy_eravm.rs | 2 +- .../src/test/case/input/deploy_evm.rs | 6 +- compiler_tester/src/test/case/input/mod.rs | 10 +- .../src/test/case/input/runtime.rs | 3 +- compiler_tester/src/test/case/mod.rs | 3 +- configs/solc-bin-default.json | 6 ++ configs/solc-bin-upstream.json | 6 ++ ethereum.yaml | 92 +++++++++++++++++++ solidity | 2 +- tests | 2 +- 14 files changed, 159 insertions(+), 29 deletions(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 1dd6eac6..82496c7e 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -30,8 +30,8 @@ on: mode: required: false type: string - default: 'Y+M3B3 0.8.27' - description: 'Mode filter for the era-compiler-tester. For example: Y+M3B3 0.8.27' + default: 'Y+M3B3 0.8.28' + description: 'Mode filter for the era-compiler-tester. For example: Y+M3B3 0.8.28' target: required: false type: string diff --git a/Cargo.lock b/Cargo.lock index 91f9f649..aa0a6527 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -949,7 +949,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era-compiler-common" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#3207c451486e49df6e241167d8c6577222ecbd8e" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ce91855778bd5e1821f9b46751f92bafafd88435" dependencies = [ "anyhow", "base58", @@ -978,7 +978,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#736a2011b8de90887b429ded48556dd49333d365" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#000c36938519192e5b52fceffb729bbf3f4287c7" dependencies = [ "anyhow", "era-compiler-common", @@ -987,13 +987,13 @@ dependencies = [ "num", "semver 1.0.23", "serde", - "zkevm_opcode_defs 0.150.0 (git+https://github.com/matter-labs/era-zkevm_opcode_defs?branch=v1.5.0)", + "zkevm_opcode_defs 0.150.5", ] [[package]] name = "era-compiler-solidity" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2de7a1a6d8f6c766a785d022fecb12b67fa2142e" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#ffcbcec20c7d358a87396422db25c3cbca6362a6" dependencies = [ "anyhow", "era-compiler-common", @@ -1017,7 +1017,7 @@ dependencies = [ "structopt", "thiserror", "which", - "zkevm_opcode_defs 0.150.0 (git+https://github.com/matter-labs/era-zkevm_opcode_defs?branch=v1.5.0)", + "zkevm_opcode_defs 0.150.5", ] [[package]] @@ -1046,7 +1046,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#2de7a1a6d8f6c766a785d022fecb12b67fa2142e" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#ffcbcec20c7d358a87396422db25c3cbca6362a6" dependencies = [ "anyhow", "regex", @@ -4088,6 +4088,22 @@ dependencies = [ "sha3 0.10.8", ] +[[package]] +name = "zkevm_opcode_defs" +version = "0.150.5" +source = "git+https://github.com/matter-labs/zksync-protocol#9bf5cf839f76a19f7c21981d8c56a7f8bbe03d7e" +dependencies = [ + "bitflags 2.6.0", + "blake2", + "ethereum-types", + "k256", + "lazy_static", + "p256", + "serde", + "sha2 0.10.8", + "sha3 0.10.8", +] + [[package]] name = "zkevm_tester" version = "1.5.0" diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index a8452cc3..b69e227f 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -242,7 +242,7 @@ mod tests { verbosity: false, quiet: false, debug: false, - modes: vec!["Y+M3B3 0.8.27".to_owned()], + modes: vec!["Y+M3B3 0.8.28".to_owned()], paths: vec!["tests/solidity/simple/default.sol".to_owned()], groups: vec![], benchmark: None, diff --git a/compiler_tester/src/test/case/input/balance.rs b/compiler_tester/src/test/case/input/balance.rs index 9da934c6..c0f9ac97 100644 --- a/compiler_tester/src/test/case/input/balance.rs +++ b/compiler_tester/src/test/case/input/balance.rs @@ -5,10 +5,9 @@ use std::sync::Arc; use std::sync::Mutex; -use web3::types::U256; - use crate::compilers::mode::Mode; use crate::summary::Summary; +use crate::vm::eravm::system_context::SystemContext; use crate::vm::eravm::EraVM; use crate::vm::evm::EVM; use crate::vm::revm::revm_type_conversions::web3_address_to_revm_address; @@ -41,13 +40,26 @@ impl Balance { pub fn run_eravm( self, summary: Arc>, - vm: &EraVM, + vm: &mut EraVM, mode: Mode, test_group: Option, name_prefix: String, index: usize, ) { let name = format!("{name_prefix}[#balance_check:{index}]"); + + let rich_addresses = SystemContext::get_rich_addresses(); + if rich_addresses.contains(&self.address) { + vm.mint_ether( + self.address, + web3::types::U256::from_str_radix( + "10000000000000000000000000", + era_compiler_common::BASE_HEXADECIMAL, + ) + .expect("Always valid"), + ); + } + let found = vm.get_balance(self.address); if found == self.balance { Summary::passed_special(summary, mode, name, test_group); @@ -98,7 +110,7 @@ impl Balance { .balance(web3_address_to_revm_address(&self.address)); match found { Ok(found) => { - let u256_found = U256::from(found.data.to_be_bytes()); + let u256_found = web3::types::U256::from(found.data.to_be_bytes()); if u256_found == self.balance { Summary::passed_special(summary, mode, name, test_group); } else { @@ -118,7 +130,7 @@ impl Balance { mode, name, self.balance.into(), - U256::zero().into(), + web3::types::U256::zero().into(), self.address.to_fixed_bytes().to_vec(), ); } diff --git a/compiler_tester/src/test/case/input/deploy_eravm.rs b/compiler_tester/src/test/case/input/deploy_eravm.rs index 6e0493c4..b6ef0a26 100644 --- a/compiler_tester/src/test/case/input/deploy_eravm.rs +++ b/compiler_tester/src/test/case/input/deploy_eravm.rs @@ -74,7 +74,7 @@ impl DeployEraVM { ) where D: EraVMDeployer, { - let name = format!("{}[#deployer:{}]", name_prefix, self.path); + let name = format!("{name_prefix}[#deployer:{}]", self.path); vm.populate_storage(self.storage.inner); let result = match deployer.deploy_eravm::( diff --git a/compiler_tester/src/test/case/input/deploy_evm.rs b/compiler_tester/src/test/case/input/deploy_evm.rs index 45f56422..a5b6b7bd 100644 --- a/compiler_tester/src/test/case/input/deploy_evm.rs +++ b/compiler_tester/src/test/case/input/deploy_evm.rs @@ -121,15 +121,15 @@ impl DeployEVM { /// /// Runs the deploy transaction on native REVM. /// - pub fn run_revm<'a>( + pub fn run_revm( self, summary: Arc>, - vm: Revm<'a>, + vm: Revm, mode: Mode, test_group: Option, name_prefix: String, evm_version: Option, - ) -> Revm<'a> { + ) -> Revm { let name = format!("{}[#deployer:{}]", name_prefix, self.identifier); let size = self.deploy_code.len(); diff --git a/compiler_tester/src/test/case/input/mod.rs b/compiler_tester/src/test/case/input/mod.rs index f1f6764d..e0750819 100644 --- a/compiler_tester/src/test/case/input/mod.rs +++ b/compiler_tester/src/test/case/input/mod.rs @@ -17,8 +17,6 @@ use std::str::FromStr; use std::sync::Arc; use std::sync::Mutex; -use solidity_adapter::EVMVersion; - use crate::compilers::mode::Mode; use crate::directories::matter_labs::test::metadata::case::input::Input as MatterLabsTestInput; use crate::summary::Summary; @@ -433,16 +431,16 @@ impl Input { /// /// Runs the input on REVM. /// - pub fn run_revm<'a>( + pub fn run_revm( self, summary: Arc>, - mut vm: Revm<'a>, + mut vm: Revm, mode: Mode, test_group: Option, name_prefix: String, index: usize, - evm_version: Option, - ) -> Revm<'a> { + evm_version: Option, + ) -> Revm { match self { Self::DeployEraVM { .. } => panic!("EraVM deploy transaction cannot be run on REVM"), Self::DeployEVM(deploy) => { diff --git a/compiler_tester/src/test/case/input/runtime.rs b/compiler_tester/src/test/case/input/runtime.rs index 2c04493e..398a3672 100644 --- a/compiler_tester/src/test/case/input/runtime.rs +++ b/compiler_tester/src/test/case/input/runtime.rs @@ -81,8 +81,9 @@ impl Runtime { name_prefix: String, index: usize, ) { - let name = format!("{}[{}:{}]", name_prefix, self.name, index); + let name = format!("{name_prefix}[{}:{index}]", self.name); vm.populate_storage(self.storage.inner); + let vm_function = match test_group.as_deref() { Some(benchmark_analyzer::Benchmark::EVM_INTERPRETER_GROUP_NAME) => { EraVM::execute_evm_interpreter:: diff --git a/compiler_tester/src/test/case/mod.rs b/compiler_tester/src/test/case/mod.rs index 6f8f58ad..8f9d0ba4 100644 --- a/compiler_tester/src/test/case/mod.rs +++ b/compiler_tester/src/test/case/mod.rs @@ -4,7 +4,6 @@ pub mod input; -use solidity_adapter::test::params::evm_version; use std::collections::BTreeMap; use std::sync::Arc; use std::sync::Mutex; @@ -164,7 +163,7 @@ impl Case { mode: &Mode, test_name: String, test_group: Option, - evm_version: Option, + evm_version: Option, ) { let name = if let Some(case_name) = self.name { format!("{test_name}::{case_name}") diff --git a/configs/solc-bin-default.json b/configs/solc-bin-default.json index 6f52210b..e07047a8 100644 --- a/configs/solc-bin-default.json +++ b/configs/solc-bin-default.json @@ -481,6 +481,12 @@ "destination": "./solc-bin/solc-${VERSION}" }, "0.8.27": { + "is_enabled": false, + "protocol": "https", + "source": "https://github.com/matter-labs/era-solidity/releases/download/${VERSION}-1.0.1/solc-${PLATFORM}-${VERSION}-1.0.1", + "destination": "./solc-bin/solc-${VERSION}" + }, + "0.8.28": { "is_enabled": true, "protocol": "https", "source": "https://github.com/matter-labs/era-solidity/releases/download/${VERSION}-1.0.1/solc-${PLATFORM}-${VERSION}-1.0.1", diff --git a/configs/solc-bin-upstream.json b/configs/solc-bin-upstream.json index f523e737..df4e1bf6 100644 --- a/configs/solc-bin-upstream.json +++ b/configs/solc-bin-upstream.json @@ -481,6 +481,12 @@ "destination": "./solc-bin-upstream/solc-${VERSION}" }, "0.8.27": { + "is_enabled": false, + "protocol": "compiler-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin-upstream/solc-${VERSION}" + }, + "0.8.28": { "is_enabled": true, "protocol": "compiler-bin-list", "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", diff --git a/ethereum.yaml b/ethereum.yaml index 970b006e..aed98eaa 100644 --- a/ethereum.yaml +++ b/ethereum.yaml @@ -2149,6 +2149,10 @@ entries: modes: - Y+ version: '>=0.4.12' + transient_state_variable_initialization.sol: + hash: '0x702b8ca960d8a816c995de79c5d8134c' + enabled: true + version: '>=0.8.28' constructor_inheritance_init_order.sol: hash: '0x732e47de4aeaedcef778c08b21b2b210' enabled: true @@ -3617,6 +3621,14 @@ entries: - E - I version: '>=0.7.0' + transient_value_types.sol: + hash: '0xb1e434bbc71f843cd4d678eaf26be538' + enabled: true + version: '>=0.8.28' + transient_value_types_multi_frame_call.sol: + hash: '0xb5bdfeb2071e62c3505275035131f306' + enabled: true + version: '>=0.8.28' value_types.sol: hash: '0xba12d166de3a7cf7e09fb51f7bb45240' enabled: true @@ -3807,6 +3819,14 @@ entries: hash: '0xd7e21a5b8052338d07f212197e327a3b' enabled: true version: '>=0.4.12' + transient_storage_state_variable.sol: + hash: '0xf343442238f0126ff68e34b9428e86ed' + enabled: true + version: '>=0.8.28' + transient_storage_state_variable_abstract_contract.sol: + hash: '0x48bd4cb65538a1f033d0ffff71046daf' + enabled: true + version: '>=0.8.28' value_for_constructor.sol: hash: '0xfc70977e16cd72eb8121fffb33ce2abd' enabled: true @@ -3994,6 +4014,10 @@ entries: hash: '0x04cc5fce661ac43a24ff58258ddbdc00' enabled: true version: '>=0.4.12' + inline_assembly_transient_storage_access_inside_function.sol: + hash: '0x9a81fdaadd4790381c2ce5f0efdf6ae7' + enabled: true + version: '>=0.8.28' inline_assembly_write_to_stack.sol: hash: '0xfef6d721eb252e69d0f9b2d50938ba14' enabled: true @@ -4890,6 +4914,10 @@ entries: - E - I version: '>=0.4.14' + transient_state_variable_value_type.sol: + hash: '0x5c021cca1a4512a8730f227c6b36d1e2' + enabled: true + version: '>=0.8.28' multiSource: enabled: true entries: @@ -4960,6 +4988,10 @@ entries: hash: '0x1b5e6e137084de716e0d9d8446564dc7' enabled: true version: '>=0.4.12' + compound_assign_transient_storage.sol: + hash: '0xbd0c4b63b99f5d44ffb9a89f531f6192' + enabled: true + version: '>=0.8.28' shifts: enabled: true entries: @@ -5135,6 +5167,10 @@ entries: hash: '0x4e8524a65fb68db9368603447805c15a' enabled: true version: '>=0.4.21' + transient_storage_variable_increment_decrement.sol: + hash: '0x5d0aa44215d67a5009bec972dd3e44e6' + enabled: true + version: '>=0.8.28' userDefined: enabled: true entries: @@ -6616,6 +6652,14 @@ entries: hash: '0x03f114444079f9008d577cb44bca08f1' enabled: true version: '>=0.4.12' + delete_transient_state_variable.sol: + hash: '0x807c0ba2ce1f5c24a5353d93a1f957e5' + enabled: true + version: '>=0.8.28' + delete_transient_state_variable_non_zero_offset.sol: + hash: '0xc9979162f04813bc1e517a39fc4a3600' + enabled: true + version: '>=0.8.28' mapping_local_assignment.sol: hash: '0x6e2c8c62d5b866f8b2058c2fac97b584' enabled: true @@ -6652,6 +6696,46 @@ entries: hash: '0xae0630569bf5603ed696749ab21c00bc' enabled: true version: '>=0.4.21' + transient_function_type_state_variable.sol: + hash: '0x85b3b01bd9a867454ff3b3a0210afccf' + enabled: true + version: '>=0.8.28' + transient_state_address_variable_members.sol: + hash: '0xbb703fd9fdd2d7edd8f291abc690d643' + enabled: true + version: '>=0.8.28' + transient_state_enum_variable.sol: + hash: '0x901a1be1942a9f1cd22fa6be99fc8808' + enabled: true + version: '>=0.8.28' + transient_state_variable.sol: + hash: '0xe0e4957bc6b30a1aa0711c069d89626f' + enabled: true + version: '>=0.8.28' + transient_state_variable_cleanup_assignment.sol: + hash: '0xe6873be855417289eed3758ee10c1623' + enabled: true + version: '>=0.8.28' + transient_state_variable_cleanup_tstore.sol: + hash: '0x07a01321e9066c7d538ef425f4a35f23' + enabled: true + version: '>=0.8.28' + transient_state_variable_slot_inline_assembly.sol: + hash: '0x7ba508b71048e6d2cb26d03422fcac1c' + enabled: true + version: '>=0.8.28' + transient_state_variable_slots_and_offsets.sol: + hash: '0x3efe44053d16de0f8abd9bfc12e8adb0' + enabled: true + version: '>=0.8.28' + transient_state_variable_tuple_assignment.sol: + hash: '0x10686230bd53840bc3593c731b0f6e23' + enabled: true + version: '>=0.8.28' + transient_state_variable_udvt.sol: + hash: '0x239da4e075afd9b12c2c717e51d5b274' + enabled: true + version: '>=0.8.28' various: enabled: true entries: @@ -6757,6 +6841,10 @@ entries: hash: '0xfc575b0ea2e377c487c4baf0f21f8cb9' enabled: true version: '>=0.4.12' + different_call_type_transient.sol: + hash: '0x53e563a335d8bb3a02b8a021dabe6af5' + enabled: true + version: '>=0.8.28' empty_name_return_parameter.sol: hash: '0x5f095962f4ae9a08b908384aa3be701d' enabled: true @@ -6942,6 +7030,10 @@ entries: hash: '0x4051de6e556f6a42d4486dadce1e24bd' enabled: true version: '>=0.5.0' + transient_storage_reentrancy_lock.sol: + hash: '0x7974f3e336b300ecde5071cf652e2109' + enabled: true + version: '>=0.8.28' tuples.sol: hash: '0xbd8afa8671e0a31c4726453fde7251ca' enabled: true diff --git a/solidity b/solidity index 40a35a09..7893614a 160000 --- a/solidity +++ b/solidity @@ -1 +1 @@ -Subproject commit 40a35a097cb1e03550c7ce415f2b46ad81e882a6 +Subproject commit 7893614a31fbeacd1966994e310ed4f760772658 diff --git a/tests b/tests index 21b0bf0e..22539413 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit 21b0bf0e044ee6b136fb3065aaf33ab7cd985d24 +Subproject commit 2253941334797eb2a997941845fb9eb0d436558b From c0b8a367cf1004c61ab573d1967ea53f28d5ad7f Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Mon, 14 Oct 2024 19:17:11 +0800 Subject: [PATCH 11/31] fix(evm): memcpy and event intrinsic argument order (#112) --- Cargo.lock | 10 +++++----- compiler_tester/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa0a6527..2763dc26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -978,7 +978,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#000c36938519192e5b52fceffb729bbf3f4287c7" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#7de0c55b51bf0bab90624413665f97697eeea55b" dependencies = [ "anyhow", "era-compiler-common", @@ -993,7 +993,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#ffcbcec20c7d358a87396422db25c3cbca6362a6" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#5f4484c03144800fa72ad7685de20643a6a7b88e" dependencies = [ "anyhow", "era-compiler-common", @@ -1023,7 +1023,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#ff8f1dd7c1102c0c003cdb19b2a04e726c666abd" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#cb25078222a377cd7e8414a42138d9a1f734c0b2" dependencies = [ "anyhow", "era-compiler-common", @@ -1040,13 +1040,13 @@ dependencies = [ "serde_json", "structopt", "which", - "zkevm_opcode_defs 0.150.0 (git+https://github.com/matter-labs/era-zkevm_opcode_defs?branch=v1.5.0)", + "zkevm_opcode_defs 0.150.5", ] [[package]] name = "era-yul" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#ffcbcec20c7d358a87396422db25c3cbca6362a6" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#5f4484c03144800fa72ad7685de20643a6a7b88e" dependencies = [ "anyhow", "regex", diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 51851d13..92d6ebd7 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -41,10 +41,10 @@ bincode = "1.3" evm = { git = "https://github.com/rust-ethereum/evm", rev = "f7a23df6c478ca6a151af5f60e62944800529a61" } revm = { git = "https://github.com/bluealloy/revm", rev = "fa5650ee8a4d802f4f3557014dd157adfb074460" } -vm2 = { git = "https://github.com/matter-labs/vm2", optional = true, package = "zksync_vm2" } zkevm_opcode_defs = { git = "https://github.com/matter-labs/era-zkevm_opcode_defs", branch = "v1.5.0" } zkevm_tester = { git = "https://github.com/matter-labs/era-zkevm_tester", branch = "v1.5.0" } +vm2 = { git = "https://github.com/matter-labs/vm2", optional = true, package = "zksync_vm2" } era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } era-compiler-downloader = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } From 3c025e6136d1cd3a4cd94633f0e01c08463b51a4 Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Tue, 15 Oct 2024 14:39:04 +0800 Subject: [PATCH 12/31] release: v1.5.5 (#113) --- Cargo.lock | 52 +++++++++++++++++------------------ benchmark_analyzer/Cargo.toml | 10 +++---- compiler_tester/Cargo.toml | 49 +++++++++++++++------------------ coverage_watcher/Cargo.toml | 8 +++--- fuzzer/Cargo.toml | 7 +++-- solidity_adapter/Cargo.toml | 18 ++++++------ 6 files changed, 70 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2763dc26..eb74e343 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb848c43f6b06ae3de2e4a67496cbbabd78ae87db0f1248934f15d76192c6a" +checksum = "38f35429a652765189c1c5092870d8360ee7b7769b09b06d89ebaefd34676446" dependencies = [ "alloy-rlp", "bytes", @@ -527,9 +527,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.28" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "jobserver", "libc", @@ -965,7 +965,7 @@ dependencies = [ [[package]] name = "era-compiler-downloader" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#3207c451486e49df6e241167d8c6577222ecbd8e" +source = "git+https://github.com/matter-labs/era-compiler-common?branch=main#ce91855778bd5e1821f9b46751f92bafafd88435" dependencies = [ "anyhow", "colored", @@ -993,7 +993,7 @@ dependencies = [ [[package]] name = "era-compiler-solidity" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#5f4484c03144800fa72ad7685de20643a6a7b88e" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#66e5526a7344126d22f3a2318851be9927ff648d" dependencies = [ "anyhow", "era-compiler-common", @@ -1023,7 +1023,7 @@ dependencies = [ [[package]] name = "era-compiler-vyper" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#cb25078222a377cd7e8414a42138d9a1f734c0b2" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#3732eddf8343bdc6d1b671d5348cfe2db63b9766" dependencies = [ "anyhow", "era-compiler-common", @@ -1046,7 +1046,7 @@ dependencies = [ [[package]] name = "era-yul" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#5f4484c03144800fa72ad7685de20643a6a7b88e" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#66e5526a7344126d22f3a2318851be9927ff648d" dependencies = [ "anyhow", "regex", @@ -1711,9 +1711,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb94a0ffd3f3ee755c20f7d8752f45cac88605a4dcf808abcff72873296ec7b" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -2224,9 +2224,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -3672,9 +3672,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.94" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef073ced962d62984fb38a36e5fdc1a2b23c9e0e1fa0689bb97afa4202ef6887" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -3683,9 +3683,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.94" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4bfab14ef75323f4eb75fa52ee0a3fb59611977fd3240da19b2cf36ff85030e" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -3698,9 +3698,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65471f79c1022ffa5291d33520cbbb53b7687b01c2f8e83b57d102eed7ed479d" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -3710,9 +3710,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.94" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7bec9830f60924d9ceb3ef99d55c155be8afa76954edffbb5936ff4509474e7" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3720,9 +3720,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.94" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c74f6e152a76a2ad448e223b0fc0b6b5747649c3d769cc6bf45737bf97d0ed6" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -3733,15 +3733,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.94" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a42f6c679374623f295a8623adfe63d9284091245c3504bde47c17a3ce2777d9" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44188d185b5bdcae1052d08bcbcf9091a5524038d4572cc4f4f2bb9d5554ddd9" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/benchmark_analyzer/Cargo.toml b/benchmark_analyzer/Cargo.toml index c10c048e..92ae8982 100644 --- a/benchmark_analyzer/Cargo.toml +++ b/benchmark_analyzer/Cargo.toml @@ -13,9 +13,9 @@ name = "benchmark-analyzer" path = "src/benchmark_analyzer/main.rs" [dependencies] -structopt = { version = "0.3", default-features = false } -anyhow = "1.0" -colored = "2.1" +structopt = { version = "=0.3.26", default-features = false } +anyhow = "=1.0.89" +colored = "=2.1.0" -serde = { version = "1.0", features = [ "derive" ] } -serde_json = "1.0" +serde = { version = "=1.0.210", features = [ "derive" ] } +serde_json = "=1.0.128" diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 92d6ebd7..1c8f04ad 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -17,27 +17,27 @@ path = "src/compiler_tester/main.rs" doctest = false [dependencies] -structopt = { version = "0.3", default-features = false } -anyhow = "1.0" -which = "6.0" -colored = "2.1" +structopt = { version = "=0.3.26", default-features = false } +anyhow = "=1.0.89" +which = "=6.0.3" +colored = "=2.1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -serde_yaml = "0.9" -md5 = "0.7" -hex = "0.4" -sha3 = "0.10" -ron = "0.8" -rlp = "0.5" -regex = "1.10" -glob = "0.3" -semver = { version = "1.0", features = ["serde"] } -itertools = "0.13" -once_cell = "1.19" -rayon = "1.10" -lazy_static = "1.4" -bincode = "1.3" +serde = { version = "=1.0.210", features = ["derive"] } +serde_json = "=1.0.128" +serde_yaml = "=0.9.34" +md5 = "=0.7.0" +hex = "=0.4.3" +sha3 = "=0.10.8" +ron = "=0.8.1" +rlp = "=0.5.2" +regex = "=1.11.0" +glob = "=0.3.1" +semver = { version = "=1.0.23", features = ["serde"] } +itertools = "=0.13.0" +once_cell = "=1.20.2" +rayon = "=1.10.0" +lazy_static = "=1.5.0" +bincode = "=1.3.3" evm = { git = "https://github.com/rust-ethereum/evm", rev = "f7a23df6c478ca6a151af5f60e62944800529a61" } revm = { git = "https://github.com/bluealloy/revm", rev = "fa5650ee8a4d802f4f3557014dd157adfb074460" } @@ -52,21 +52,16 @@ era-compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler era-compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", branch = "main" } era-compiler-vyper = { git = "https://github.com/matter-labs/era-compiler-vyper", branch = "main" } -# era-compiler-common = { path = "../../era-compiler-common" } -# era-compiler-llvm-context = { path = "../../era-compiler-llvm-context" } -# era-compiler-solidity = { path = "../../era-compiler-solidity" } -# era-compiler-vyper = { path = "../../era-compiler-vyper" } - solidity-adapter = { path = "../solidity_adapter" } benchmark-analyzer = { path = "../benchmark_analyzer" } [dependencies.web3] -version = "0.19" +version = "=0.19.0" default-features = false features = ["http-rustls-tls", "test", "signing"] [dependencies.reqwest] -version = "0.11" +version = "=0.11.27" default-features = false features = ["blocking"] diff --git a/coverage_watcher/Cargo.toml b/coverage_watcher/Cargo.toml index ab4da2b4..930e120b 100644 --- a/coverage_watcher/Cargo.toml +++ b/coverage_watcher/Cargo.toml @@ -14,10 +14,10 @@ name = "coverage-watcher" path = "src/coverage_watcher/main.rs" [dependencies] -structopt = { version = "0.3", default-features = false } -anyhow = "1.0" +structopt = { version = "=0.3.26", default-features = false } +anyhow = "=1.0.89" -serde = { version = "1.0", features = [ "derive" ] } -serde_yaml = "0.9" +serde = { version = "=1.0.210", features = [ "derive" ] } +serde_yaml = "=0.9.34" era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } diff --git a/fuzzer/Cargo.toml b/fuzzer/Cargo.toml index 7d4daf5f..85a1434d 100644 --- a/fuzzer/Cargo.toml +++ b/fuzzer/Cargo.toml @@ -26,9 +26,10 @@ doc = false bench = false [dependencies] -libfuzzer-sys = "0.4" -anyhow = "1.0" -semver = { version = "1.0", features = ["serde"] } +anyhow = "=1.0.89" +semver = { version = "=1.0.23", features = ["serde"] } + +libfuzzer-sys = "=0.4.7" zkevm_tester = { git = "https://github.com/matter-labs/era-zkevm_tester", branch = "v1.5.0" } diff --git a/solidity_adapter/Cargo.toml b/solidity_adapter/Cargo.toml index a468f761..fb47b94c 100644 --- a/solidity_adapter/Cargo.toml +++ b/solidity_adapter/Cargo.toml @@ -17,19 +17,19 @@ path = "src/tests_updater/main.rs" doctest = false [dependencies] -structopt = { version = "0.3", default-features = false } -anyhow = "1.0" -colored = "2.0" +structopt = { version = "=0.3.26", default-features = false } +anyhow = "=1.0.89" +colored = "=2.1.0" -serde = { version = "1.0", features = [ "derive" ] } -serde_yaml = "0.9" -semver = { version = "1.0", features = [ "serde" ] } -regex = "1.10" -md5 = "0.7" +serde = { version = "=1.0.210", features = [ "derive" ] } +serde_yaml = "=0.9.34" +semver = { version = "=1.0.23", features = [ "serde" ] } +regex = "=1.11.0" +md5 = "=0.7.0" era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } [dependencies.web3] -version = "0.19" +version = "=0.19.0" default-features = false features = ["http-rustls-tls", "test", "signing"] From 9e0bc1b2e603b4ce571267024112d3df5136a9fc Mon Sep 17 00:00:00 2001 From: Oleksandr Zarudnyi Date: Thu, 17 Oct 2024 01:42:24 +0800 Subject: [PATCH 13/31] fix: benchmark reports for EVM (#114) --- Cargo.lock | 87 ++++++----------- .../src/benchmark/group/element.rs | 4 +- benchmark_analyzer/src/benchmark/group/mod.rs | 32 +++++++ .../src/benchmark/group/results.rs | 88 ++++++++++++++++++ compiler_tester/Cargo.toml | 2 +- compiler_tester/src/compiler_tester/main.rs | 2 +- compiler_tester/src/summary/mod.rs | 64 ++++++++----- .../src/test/case/input/runtime.rs | 24 +++-- system-contracts-stable-build | Bin 994031 -> 979219 bytes 9 files changed, 206 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb74e343..b1709d34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,7 +610,7 @@ dependencies = [ "structopt", "web3", "which", - "zkevm_opcode_defs 0.150.0 (git+https://github.com/matter-labs/era-zkevm_opcode_defs?branch=v1.5.0)", + "zkevm_opcode_defs", "zkevm_tester", "zksync_vm2", ] @@ -978,7 +978,7 @@ dependencies = [ [[package]] name = "era-compiler-llvm-context" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#7de0c55b51bf0bab90624413665f97697eeea55b" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?branch=main#23bd5c502ab9d246c44ed61802153de47a7d9dfd" dependencies = [ "anyhow", "era-compiler-common", @@ -987,13 +987,13 @@ dependencies = [ "num", "semver 1.0.23", "serde", - "zkevm_opcode_defs 0.150.5", + "zkevm_opcode_defs", ] [[package]] name = "era-compiler-solidity" -version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#66e5526a7344126d22f3a2318851be9927ff648d" +version = "1.5.6" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#12befad5d65a40166a8ffc6fd4f2008cb92a173b" dependencies = [ "anyhow", "era-compiler-common", @@ -1017,13 +1017,13 @@ dependencies = [ "structopt", "thiserror", "which", - "zkevm_opcode_defs 0.150.5", + "zkevm_opcode_defs", ] [[package]] name = "era-compiler-vyper" version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#3732eddf8343bdc6d1b671d5348cfe2db63b9766" +source = "git+https://github.com/matter-labs/era-compiler-vyper?branch=main#be758e3a119f45e960f796f177db2de5d56c72ab" dependencies = [ "anyhow", "era-compiler-common", @@ -1040,13 +1040,13 @@ dependencies = [ "serde_json", "structopt", "which", - "zkevm_opcode_defs 0.150.5", + "zkevm_opcode_defs", ] [[package]] name = "era-yul" -version = "1.5.5" -source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#66e5526a7344126d22f3a2318851be9927ff648d" +version = "1.5.6" +source = "git+https://github.com/matter-labs/era-compiler-solidity?branch=main#12befad5d65a40166a8ffc6fd4f2008cb92a173b" dependencies = [ "anyhow", "regex", @@ -1505,9 +1505,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" dependencies = [ "bytes", "futures-channel", @@ -2082,9 +2082,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "7b8cefcf97f41316955f9294cd61f639bdcfa9f2f230faac6cb896aa8ab64704" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -2123,9 +2123,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -2357,9 +2357,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -4030,8 +4030,9 @@ dependencies = [ [[package]] name = "zk_evm" -version = "0.150.0" -source = "git+https://github.com/matter-labs/era-zk_evm?branch=v1.5.0#9ddd65ce2910049bae402aff0230e6e1bfed578a" +version = "0.150.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c14bda6c101389145cd01fac900f1392876bc0284d98faf7f376237baa2cb19d" dependencies = [ "anyhow", "lazy_static", @@ -4044,54 +4045,22 @@ dependencies = [ [[package]] name = "zk_evm_abstractions" -version = "0.150.0" +version = "0.150.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc313cea4ac9ef6b855264b1425cbe9de30dd8f009559dabcb6b2896122da5db" +checksum = "a008f2442fc6a508bdd1f902380242cb6ff11b8b27acdac2677c6d9f75cbb004" dependencies = [ "anyhow", "num_enum", "serde", "static_assertions", - "zkevm_opcode_defs 0.150.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zkevm_opcode_defs", ] [[package]] name = "zkevm_opcode_defs" -version = "0.150.0" +version = "0.150.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3328c012d444bdbfadb754a72c01a56879eb66584efc71eac457e89e7843608" -dependencies = [ - "bitflags 2.6.0", - "blake2", - "ethereum-types", - "k256", - "lazy_static", - "p256", - "serde", - "sha2 0.10.8", - "sha3 0.10.8", -] - -[[package]] -name = "zkevm_opcode_defs" -version = "0.150.0" -source = "git+https://github.com/matter-labs/era-zkevm_opcode_defs?branch=v1.5.0#cf5f9c18c580f845b32fc2a4d565a77380fd15bc" -dependencies = [ - "bitflags 2.6.0", - "blake2", - "ethereum-types", - "k256", - "lazy_static", - "p256", - "serde", - "sha2 0.10.8", - "sha3 0.10.8", -] - -[[package]] -name = "zkevm_opcode_defs" -version = "0.150.5" -source = "git+https://github.com/matter-labs/zksync-protocol#9bf5cf839f76a19f7c21981d8c56a7f8bbe03d7e" +checksum = "762b5f1c1b283c5388995a85d40a05aef1c14f50eb904998b7e9364739f5b899" dependencies = [ "bitflags 2.6.0", "blake2", @@ -4107,7 +4076,7 @@ dependencies = [ [[package]] name = "zkevm_tester" version = "1.5.0" -source = "git+https://github.com/matter-labs/era-zkevm_tester?branch=v1.5.0#5e79ef0c9d3a47c826c388b49245baf68264c946" +source = "git+https://github.com/matter-labs/era-zkevm_tester?branch=v1.5.0#393e31814c6838378d84e9932f1b378c1da5b5fe" dependencies = [ "anyhow", "ethabi", @@ -4131,7 +4100,7 @@ dependencies = [ "enum_dispatch", "primitive-types", "zk_evm_abstractions", - "zkevm_opcode_defs 0.150.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zkevm_opcode_defs", "zksync_vm2_interface", ] diff --git a/benchmark_analyzer/src/benchmark/group/element.rs b/benchmark_analyzer/src/benchmark/group/element.rs index 550cae70..d4dabf96 100644 --- a/benchmark_analyzer/src/benchmark/group/element.rs +++ b/benchmark_analyzer/src/benchmark/group/element.rs @@ -14,9 +14,9 @@ pub struct Element { pub size: Option, /// The number of cycles. pub cycles: usize, - /// The number of ergs. + /// The amount of ergs. pub ergs: u64, - /// The number of EVM gas. + /// The amount of EVM gas. pub gas: u64, } diff --git a/benchmark_analyzer/src/benchmark/group/mod.rs b/benchmark_analyzer/src/benchmark/group/mod.rs index 78dca30d..94d7db2a 100644 --- a/benchmark_analyzer/src/benchmark/group/mod.rs +++ b/benchmark_analyzer/src/benchmark/group/mod.rs @@ -55,6 +55,14 @@ impl Group { let mut ergs_total_reference: u64 = 0; let mut ergs_total_candidate: u64 = 0; + let mut gas_factors = Vec::with_capacity(elements_number); + let mut gas_min = 1.0; + let mut gas_max = 1.0; + let mut gas_negatives = Vec::with_capacity(elements_number); + let mut gas_positives = Vec::with_capacity(elements_number); + let mut gas_total_reference: u64 = 0; + let mut gas_total_candidate: u64 = 0; + for (path, reference) in reference.elements.iter() { if path.contains("tests/solidity/complex/interpreter/test.json") && path.contains("#deployer") @@ -100,6 +108,23 @@ impl Group { } ergs_factors.push(ergs_factor); + gas_total_reference += reference.gas; + gas_total_candidate += candidate.gas; + let gas_factor = (candidate.gas as f64) / (reference.gas as f64); + if gas_factor > 1.0 { + gas_negatives.push((gas_factor, path.as_str())); + } + if gas_factor < 1.0 { + gas_positives.push((gas_factor, path.as_str())); + } + if gas_factor < gas_min { + gas_min = gas_factor; + } + if gas_factor > gas_max { + gas_max = gas_factor; + } + gas_factors.push(gas_factor); + let reference_size = match reference.size { Some(size) => size, None => continue, @@ -132,6 +157,8 @@ impl Group { let ergs_total = (ergs_total_candidate as f64) / (ergs_total_reference as f64); + let gas_total = (gas_total_candidate as f64) / (gas_total_reference as f64); + Results::new( size_min, size_max, @@ -148,6 +175,11 @@ impl Group { ergs_total, ergs_negatives, ergs_positives, + gas_min, + gas_max, + gas_total, + gas_negatives, + gas_positives, ) } diff --git a/benchmark_analyzer/src/benchmark/group/results.rs b/benchmark_analyzer/src/benchmark/group/results.rs index e59efd1a..3b306b92 100644 --- a/benchmark_analyzer/src/benchmark/group/results.rs +++ b/benchmark_analyzer/src/benchmark/group/results.rs @@ -43,6 +43,17 @@ pub struct Results<'a> { /// The ergs positive result test names. pub ergs_positives: Vec<(f64, &'a str)>, + /// The gas best result. + pub gas_best: f64, + /// The gas worst result. + pub gas_worst: f64, + /// The gas total decrease result. + pub gas_total: f64, + /// The gas negative result test names. + pub gas_negatives: Vec<(f64, &'a str)>, + /// The gas positive result test names. + pub gas_positives: Vec<(f64, &'a str)>, + /// The EVM interpreter reference ratios. pub evm_interpreter_reference_ratios: Option>, /// The EVM interpreter candidate ratios. @@ -72,6 +83,12 @@ impl<'a> Results<'a> { ergs_total: f64, ergs_negatives: Vec<(f64, &'a str)>, ergs_positives: Vec<(f64, &'a str)>, + + gas_best: f64, + gas_worst: f64, + gas_total: f64, + gas_negatives: Vec<(f64, &'a str)>, + gas_positives: Vec<(f64, &'a str)>, ) -> Self { Self { size_best, @@ -92,6 +109,12 @@ impl<'a> Results<'a> { ergs_negatives, ergs_positives, + gas_best, + gas_worst, + gas_total, + gas_negatives, + gas_positives, + evm_interpreter_reference_ratios: None, evm_interpreter_candidate_ratios: None, } @@ -134,6 +157,14 @@ impl<'a> Results<'a> { std::cmp::Ordering::Equal } }); + self.gas_negatives.sort_by(|a, b| { + if a.0 > b.0 { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Equal + } + }); + self.size_positives.sort_by(|a, b| { if a.0 < b.0 { std::cmp::Ordering::Less @@ -155,6 +186,13 @@ impl<'a> Results<'a> { std::cmp::Ordering::Equal } }); + self.gas_positives.sort_by(|a, b| { + if a.0 < b.0 { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Equal + } + }); } /// @@ -191,6 +229,17 @@ impl<'a> Results<'a> { println!("{:010}: {}", Self::format_f64(*value), path); } println!(); + println!( + "Group '{}' gas (-%) worst {} out of {}:", + group_name, + count, + self.gas_negatives.len() + ); + for (value, path) in self.gas_negatives.iter().take(count) { + println!("{:010}: {}", Self::format_f64(*value), path); + } + println!(); + println!( "Group '{}' size (-%) best {} out of {}:", group_name, @@ -221,6 +270,16 @@ impl<'a> Results<'a> { println!("{:010}: {}", Self::format_f64(*value), path); } println!(); + println!( + "Group '{}' gas (-%) best {} out of {}:", + group_name, + count, + self.gas_positives.len() + ); + for (value, path) in self.gas_positives.iter().take(count) { + println!("{:010}: {}", Self::format_f64(*value), path); + } + println!(); } /// @@ -255,6 +314,7 @@ impl<'a> Results<'a> { "Total".bright_white(), Self::format_f64(self.size_total) )?; + writeln!( w, "╠═╡ {} ╞{}╡ {} ╞═╣", @@ -280,6 +340,7 @@ impl<'a> Results<'a> { "Total".bright_white(), Self::format_f64(self.cycles_total) )?; + writeln!( w, "╠═╡ {} ╞{}╡ {} ╞═╣", @@ -305,6 +366,33 @@ impl<'a> Results<'a> { "Total".bright_white(), Self::format_f64(self.ergs_total) )?; + + writeln!( + w, + "╠══╡ {} ╞{}╡ {} ╞═╣", + "Gas (-%)".bright_white(), + "═".repeat(cmp::max(24 - group_name.len(), 0)), + group_name.bright_white() + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Best".bright_white(), + Self::format_f64(self.gas_best) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Worst".bright_white(), + Self::format_f64(self.gas_worst) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Total".bright_white(), + Self::format_f64(self.gas_total) + )?; + if let (Some(gas_reference_ratios), Some(gas_candidate_ratios)) = ( self.evm_interpreter_reference_ratios.as_deref(), self.evm_interpreter_candidate_ratios.as_deref(), diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml index 1c8f04ad..8c601faf 100644 --- a/compiler_tester/Cargo.toml +++ b/compiler_tester/Cargo.toml @@ -42,7 +42,7 @@ bincode = "=1.3.3" evm = { git = "https://github.com/rust-ethereum/evm", rev = "f7a23df6c478ca6a151af5f60e62944800529a61" } revm = { git = "https://github.com/bluealloy/revm", rev = "fa5650ee8a4d802f4f3557014dd157adfb074460" } -zkevm_opcode_defs = { git = "https://github.com/matter-labs/era-zkevm_opcode_defs", branch = "v1.5.0" } +zkevm_opcode_defs = "=0.150.6" zkevm_tester = { git = "https://github.com/matter-labs/era-zkevm_tester", branch = "v1.5.0" } vm2 = { git = "https://github.com/matter-labs/vm2", optional = true, package = "zksync_vm2" } diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs index b69e227f..ab0433be 100644 --- a/compiler_tester/src/compiler_tester/main.rs +++ b/compiler_tester/src/compiler_tester/main.rs @@ -217,7 +217,7 @@ fn main_inner(arguments: Arguments) -> anyhow::Result<()> { ); if let Some(path) = arguments.benchmark { - let benchmark = summary.benchmark()?; + let benchmark = summary.benchmark(toolchain)?; benchmark.write_to_file(path)?; } diff --git a/compiler_tester/src/summary/mod.rs b/compiler_tester/src/summary/mod.rs index a3ab5a56..891fa381 100644 --- a/compiler_tester/src/summary/mod.rs +++ b/compiler_tester/src/summary/mod.rs @@ -11,6 +11,7 @@ use colored::Colorize; use crate::compilers::mode::Mode; use crate::test::case::input::output::Output; +use crate::toolchain::Toolchain; use self::element::outcome::passed_variant::PassedVariant; use self::element::outcome::Outcome; @@ -75,24 +76,37 @@ impl Summary { /// /// Returns the benchmark structure. /// - pub fn benchmark(&self) -> anyhow::Result { + pub fn benchmark(&self, toolchain: Toolchain) -> anyhow::Result { let mut benchmark = benchmark_analyzer::Benchmark::default(); - benchmark.groups.insert( - format!( - "{} {}", - benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME, - era_compiler_llvm_context::OptimizerSettings::cycles(), - ), - benchmark_analyzer::BenchmarkGroup::default(), - ); - benchmark.groups.insert( - format!( - "{} {}", - benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME, - era_compiler_llvm_context::OptimizerSettings::size(), - ), - benchmark_analyzer::BenchmarkGroup::default(), - ); + match toolchain { + Toolchain::IrLLVM => { + benchmark.groups.insert( + format!( + "{} {}", + benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME, + era_compiler_llvm_context::OptimizerSettings::cycles(), + ), + benchmark_analyzer::BenchmarkGroup::default(), + ); + benchmark.groups.insert( + format!( + "{} {}", + benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME, + era_compiler_llvm_context::OptimizerSettings::size(), + ), + benchmark_analyzer::BenchmarkGroup::default(), + ); + } + Toolchain::Solc => { + benchmark.groups.insert( + benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME.to_owned(), + benchmark_analyzer::BenchmarkGroup::default(), + ); + } + Toolchain::SolcLLVM => { + anyhow::bail!("The benchmarking is not supported for the SolcLLVM toolchain.") + } + } for element in self.elements.iter() { let (size, cycles, ergs, group, gas) = match &element.outcome { @@ -125,21 +139,29 @@ impl Summary { let mode = element .mode .as_ref() - .and_then(|mode| mode.llvm_optimizer_settings().cloned()) - .unwrap_or(era_compiler_llvm_context::OptimizerSettings::none()); + .and_then(|mode| mode.llvm_optimizer_settings().cloned()); let benchmark_element = benchmark_analyzer::BenchmarkElement::new(size, cycles, ergs, gas); if let Some(group) = group { + let group_key = match mode { + Some(ref mode) => format!("{group} {mode}"), + None => group, + }; benchmark .groups - .entry(format!("{} {}", group, mode)) + .entry(group_key) .or_default() .elements .insert(key.clone(), benchmark_element.clone()); } - let group_key = format!("{} {}", benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME, mode); + let group_key = match mode { + Some(ref mode) => { + format!("{} {mode}", benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME) + } + None => benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME.to_owned(), + }; if let Some(group) = benchmark.groups.get_mut(group_key.as_str()) { group.elements.insert(key, benchmark_element); } diff --git a/compiler_tester/src/test/case/input/runtime.rs b/compiler_tester/src/test/case/input/runtime.rs index 398a3672..ad4b6dbc 100644 --- a/compiler_tester/src/test/case/input/runtime.rs +++ b/compiler_tester/src/test/case/input/runtime.rs @@ -264,10 +264,10 @@ impl Runtime { return vm; } }; - let output = match result { + let (output, gas, error) = match result { ExecutionResult::Success { reason: _, - gas_used: _, + gas_used, gas_refunded: _, logs, output, @@ -277,23 +277,21 @@ impl Runtime { } else { vm }; - transform_success_output(output, logs) + (transform_success_output(output, logs), gas_used, None) } - ExecutionResult::Revert { - gas_used: _, - output, - } => { + ExecutionResult::Revert { gas_used, output } => { let return_data_value = revm_bytes_to_vec_value(output); - Output::new(return_data_value, true, vec![]) + (Output::new(return_data_value, true, vec![]), gas_used, None) + } + ExecutionResult::Halt { reason, gas_used } => { + (Output::new(vec![], true, vec![]), gas_used, Some(reason)) } - ExecutionResult::Halt { - reason: _, - gas_used: _, - } => Output::new(vec![], true, vec![]), }; if output == self.expected { - Summary::passed_runtime(summary, mode, name, test_group, 0, 0, 0); + Summary::passed_runtime(summary, mode, name, test_group, 0, 0, gas); + } else if let Some(error) = error { + Summary::invalid(summary, Some(mode), name, format!("{error:?}")); } else { Summary::failed( summary, diff --git a/system-contracts-stable-build b/system-contracts-stable-build index 28bc182ba83102f2b208edbde4534f3dca6b90f1..69366327deb3dee7e2b88ce432b7ed5f5fa22e12 100644 GIT binary patch delta 210964 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 delta 226388 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