From 04cb9bd38c9dbd6388488a0d6cd9377db1e44a44 Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Sat, 19 Oct 2024 03:57:03 +0200 Subject: [PATCH 1/4] update CODEOWNERS (#34) --- .github/CODEOWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fa411185..83ed48c7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,2 @@ -* @hedgar2017 \ No newline at end of file +* @nbaztec @elfedy @Karrq @Jrigada +* @dutterbutter @hedgar2017 From 9a8f29c84a04d0f5d8c06f34dc4d07eb86ecfe7b Mon Sep 17 00:00:00 2001 From: Karrq Date: Fri, 1 Nov 2024 18:14:55 +0100 Subject: [PATCH 2/4] fix: codeowners in one line --- .github/CODEOWNERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 83ed48c7..9af80368 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -* @nbaztec @elfedy @Karrq @Jrigada -* @dutterbutter @hedgar2017 +* @nbaztec @elfedy @Karrq @Jrigada @dutterbutter @hedgar2017 From 7d2dc971a21db4e188fd42d086ad0daea2dc7ae9 Mon Sep 17 00:00:00 2001 From: Karrq Date: Fri, 1 Nov 2024 20:28:07 +0100 Subject: [PATCH 3/4] feat: zksolc v1.5.7 support (#35) * feat: zksolc 1.5.7 support * chore: update version constants --- .../artifacts/zksolc/src/output_selection.rs | 12 ++++++------ crates/compilers/src/compilers/zksolc/mod.rs | 4 ++-- .../src/compilers/zksolc/settings.rs | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/crates/artifacts/zksolc/src/output_selection.rs b/crates/artifacts/zksolc/src/output_selection.rs index a063fb16..e05b2751 100644 --- a/crates/artifacts/zksolc/src/output_selection.rs +++ b/crates/artifacts/zksolc/src/output_selection.rs @@ -7,18 +7,18 @@ use std::collections::HashSet; #[derive(Debug, Default, Serialize, Deserialize, Eq, PartialEq, Clone)] pub struct OutputSelection { /// Only the 'all' wildcard is available for robustness reasons. - #[serde(rename = "*", skip_serializing_if = "Option::is_none")] - pub all: Option, + #[serde(rename = "*")] + pub all: FileOutputSelection, } #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct FileOutputSelection { /// The per-file output selections. - #[serde(rename = "", skip_serializing_if = "Option::is_none")] - pub per_file: Option>, + #[serde(rename = "")] + pub per_file: HashSet, /// The per-contract output selections. - #[serde(rename = "*", skip_serializing_if = "Option::is_none")] - pub per_contract: Option>, + #[serde(rename = "*")] + pub per_contract: HashSet, } /// diff --git a/crates/compilers/src/compilers/zksolc/mod.rs b/crates/compilers/src/compilers/zksolc/mod.rs index 153b76da..e502cf96 100644 --- a/crates/compilers/src/compilers/zksolc/mod.rs +++ b/crates/compilers/src/compilers/zksolc/mod.rs @@ -38,7 +38,7 @@ pub use settings::ZkSolcSettings; pub const ZKSOLC: &str = "zksolc"; pub const ZKSYNC_SOLC_RELEASE: Version = Version::new(1, 0, 1); -pub const ZKSOLC_VERSION: Version = Version::new(1, 5, 4); +pub const ZKSOLC_VERSION: Version = Version::new(1, 5, 7); #[derive(Debug, Clone, Serialize)] enum ZkSolcOS { @@ -336,7 +336,7 @@ impl ZkSolc { pub fn solc_available_versions() -> Vec { let mut ret = vec![]; let min_max_patch_by_minor_versions = - vec![(4, 12, 26), (5, 0, 17), (6, 0, 12), (7, 0, 6), (8, 0, 27)]; + vec![(4, 12, 26), (5, 0, 17), (6, 0, 12), (7, 0, 6), (8, 0, 28)]; for (minor, min_patch, max_patch) in min_max_patch_by_minor_versions { for i in min_patch..=max_patch { ret.push(Version::new(0, minor, i)); diff --git a/crates/compilers/src/compilers/zksolc/settings.rs b/crates/compilers/src/compilers/zksolc/settings.rs index 1711f3c4..1c978a42 100644 --- a/crates/compilers/src/compilers/zksolc/settings.rs +++ b/crates/compilers/src/compilers/zksolc/settings.rs @@ -15,6 +15,19 @@ use std::{ str::FromStr, }; +/// +/// The Solidity compiler codegen. +/// +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Codegen { + /// The Yul IR. + #[default] + Yul, + /// The EVM legacy assembly IR. + EVMLA, +} + /// zksolc standard json input settings. See: /// https://docs.zksync.io/zk-stack/components/compiler/toolchain/solidity.html#standard-json for differences #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -25,6 +38,9 @@ pub struct ZkSettings { /// false by default. #[serde(rename = "viaIR", default, skip_serializing_if = "Option::is_none")] pub via_ir: Option, + /// The Solidity codegen. + #[serde(default)] + pub codegen: Codegen, // TODO: era-compiler-solidity uses a BTreeSet of strings. In theory the serialization // should be the same but maybe we should double check #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -143,6 +159,7 @@ impl Default for ZkSettings { enable_eravm_extensions: false, llvm_options: Default::default(), force_evmla: false, + codegen: Default::default(), } } } @@ -168,6 +185,7 @@ impl CompilerSettings for ZkSolcSettings { enable_eravm_extensions, llvm_options, force_evmla, + codegen, }, .. } = self; @@ -183,6 +201,7 @@ impl CompilerSettings for ZkSolcSettings { && *enable_eravm_extensions == other.settings.enable_eravm_extensions && *llvm_options == other.settings.llvm_options && *force_evmla == other.settings.force_evmla + && *codegen == other.settings.codegen } fn with_remappings(mut self, remappings: &[Remapping]) -> Self { From 8f1613ec0f386f025012dc8ec89037eea7a37b43 Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Mon, 4 Nov 2024 18:23:34 +0200 Subject: [PATCH 4/4] feat: Support error / warning suppression in zksolc (#33) --- .../compilers/src/compilers/zksolc/input.rs | 24 +++- crates/compilers/src/compilers/zksolc/mod.rs | 3 +- .../src/compilers/zksolc/settings.rs | 34 ++++- crates/compilers/tests/zksync.rs | 128 +++++++++++++++++- 4 files changed, 181 insertions(+), 8 deletions(-) diff --git a/crates/compilers/src/compilers/zksolc/input.rs b/crates/compilers/src/compilers/zksolc/input.rs index 0d83cd9c..3d6f5861 100644 --- a/crates/compilers/src/compilers/zksolc/input.rs +++ b/crates/compilers/src/compilers/zksolc/input.rs @@ -1,4 +1,7 @@ -use super::{settings::ZkSolcSettings, ZkSettings}; +use super::{ + settings::{ZkSolcError, ZkSolcSettings, ZkSolcWarning}, + ZkSettings, +}; use crate::{ compilers::{solc::SolcLanguage, CompilerInput}, solc, @@ -8,6 +11,7 @@ use semver::Version; use serde::{Deserialize, Serialize}; use std::{ borrow::Cow, + collections::HashSet, path::{Path, PathBuf}, }; @@ -33,7 +37,7 @@ impl CompilerInput for ZkSolcVersionedInput { version: Version, ) -> Self { let ZkSolcSettings { settings, cli_settings } = settings; - let input = ZkSolcInput { language, sources, settings }.sanitized(&version); + let input = ZkSolcInput::new(language, sources, settings).sanitized(&version); Self { solc_version: version, input, cli_settings } } @@ -65,10 +69,18 @@ impl CompilerInput for ZkSolcVersionedInput { /// Input type `zksolc` expects. #[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct ZkSolcInput { pub language: SolcLanguage, pub sources: Sources, pub settings: ZkSettings, + // For `zksolc` versions <1.5.7, suppressed warnings / errors were specified on the same level + // as `settings`. For `zksolc` 1.5.7+, they are specified inside `settings`. Since we want to + // support both options at the time, we duplicate fields from `settings` here. + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + pub suppressed_warnings: HashSet, + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + pub suppressed_errors: HashSet, } /// Default `language` field is set to `"Solidity"`. @@ -78,11 +90,19 @@ impl Default for ZkSolcInput { language: SolcLanguage::Solidity, sources: Sources::default(), settings: ZkSettings::default(), + suppressed_warnings: HashSet::default(), + suppressed_errors: HashSet::default(), } } } impl ZkSolcInput { + fn new(language: SolcLanguage, sources: Sources, settings: ZkSettings) -> Self { + let suppressed_warnings = settings.suppressed_warnings.clone(); + let suppressed_errors = settings.suppressed_errors.clone(); + Self { language, sources, settings, suppressed_warnings, suppressed_errors } + } + /// Removes the `base` path from all source files pub fn strip_prefix(&mut self, base: impl AsRef) { let base = base.as_ref(); diff --git a/crates/compilers/src/compilers/zksolc/mod.rs b/crates/compilers/src/compilers/zksolc/mod.rs index e502cf96..7208b6f5 100644 --- a/crates/compilers/src/compilers/zksolc/mod.rs +++ b/crates/compilers/src/compilers/zksolc/mod.rs @@ -33,8 +33,7 @@ use std::os::unix::fs::PermissionsExt; pub mod input; pub mod settings; -pub use settings::ZkSettings; -pub use settings::ZkSolcSettings; +pub use settings::{ZkSettings, ZkSolcSettings}; pub const ZKSOLC: &str = "zksolc"; pub const ZKSYNC_SOLC_RELEASE: Version = Version::new(1, 0, 1); diff --git a/crates/compilers/src/compilers/zksolc/settings.rs b/crates/compilers/src/compilers/zksolc/settings.rs index 1c978a42..9c6d6f47 100644 --- a/crates/compilers/src/compilers/zksolc/settings.rs +++ b/crates/compilers/src/compilers/zksolc/settings.rs @@ -9,7 +9,7 @@ use foundry_compilers_artifacts::{ use semver::Version; use serde::{Deserialize, Serialize}; use std::{ - collections::BTreeSet, + collections::{BTreeSet, HashSet}, fmt, path::{Path, PathBuf}, str::FromStr, @@ -17,7 +17,6 @@ use std::{ /// /// The Solidity compiler codegen. -/// #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Codegen { @@ -28,6 +27,25 @@ pub enum Codegen { EVMLA, } +/// `zksolc` warnings that can be suppressed. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +#[non_exhaustive] +pub enum ZkSolcWarning { + /// `txorigin` warning: Using `tx.origin` in place of `msg.sender`. + TxOrigin, +} + +/// `zksolc` errors that can be suppressed. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +#[non_exhaustive] +pub enum ZkSolcError { + /// `sendtransfer` error: Using `send()` or `transfer()` methods on `address payable` instead + /// of `call()`. + SendTransfer, +} + /// zksolc standard json input settings. See: /// https://docs.zksync.io/zk-stack/components/compiler/toolchain/solidity.html#standard-json for differences #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -82,6 +100,12 @@ pub struct ZkSettings { /// Whether to compile via EVM assembly. #[serde(default, rename = "forceEVMLA")] pub force_evmla: bool, + /// Suppressed `zksolc` warnings. + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + pub suppressed_warnings: HashSet, + /// Suppressed `zksolc` errors. + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + pub suppressed_errors: HashSet, } // Analogous to SolcSettings for Zk compiler @@ -160,6 +184,8 @@ impl Default for ZkSettings { llvm_options: Default::default(), force_evmla: false, codegen: Default::default(), + suppressed_errors: Default::default(), + suppressed_warnings: Default::default(), } } } @@ -186,6 +212,8 @@ impl CompilerSettings for ZkSolcSettings { llvm_options, force_evmla, codegen, + suppressed_warnings, + suppressed_errors, }, .. } = self; @@ -202,6 +230,8 @@ impl CompilerSettings for ZkSolcSettings { && *llvm_options == other.settings.llvm_options && *force_evmla == other.settings.force_evmla && *codegen == other.settings.codegen + && *suppressed_warnings == other.settings.suppressed_warnings + && *suppressed_errors == other.settings.suppressed_errors } fn with_remappings(mut self, remappings: &[Remapping]) -> Self { diff --git a/crates/compilers/tests/zksync.rs b/crates/compilers/tests/zksync.rs index efacf653..95d3ef2f 100644 --- a/crates/compilers/tests/zksync.rs +++ b/crates/compilers/tests/zksync.rs @@ -1,11 +1,20 @@ -use std::{collections::HashMap, fs, path::PathBuf, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + fs, + path::PathBuf, + str::FromStr, +}; use foundry_compilers::{ buildinfo::BuildInfo, cache::CompilerCache, project_util::*, resolver::parse::SolData, - zksolc::{input::ZkSolcInput, ZkSolcCompiler, ZkSolcSettings}, + zksolc::{ + input::ZkSolcInput, + settings::{ZkSolcError, ZkSolcWarning}, + ZkSolc, ZkSolcCompiler, ZkSolcSettings, + }, zksync::{self, artifact_output::zk::ZkArtifactOutput}, Graph, ProjectBuilder, ProjectPathsConfig, }; @@ -42,6 +51,121 @@ fn zksync_can_compile_dapp_sample() { assert_eq!(cache, updated_cache); } +fn test_zksync_can_compile_contract_with_suppressed_errors(compiler: ZkSolcCompiler) { + let _ = tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .ok(); + let mut project = TempProject::::dapptools().unwrap(); + project.project_mut().compiler = compiler; + + project + .add_source( + "Erroneous", + r#" + // SPDX-License-Identifier: MIT OR Apache-2.0 + pragma solidity ^0.8.10; + contract Erroneous { + function distribute(address payable recipient) public { + recipient.send(1); + recipient.transfer(1); + } + } + "#, + ) + .unwrap(); + + let compiled = zksync::project_compile(project.project()).unwrap(); + assert!(compiled.has_compiler_errors()); + + project.project_mut().settings.settings.suppressed_errors = + HashSet::from([ZkSolcError::SendTransfer]); + + let compiled = zksync::project_compile(project.project()).unwrap(); + compiled.assert_success(); + assert!(compiled.find_first("Erroneous").is_some()); +} + +#[test] +fn zksync_can_compile_contract_with_suppressed_errors() { + test_zksync_can_compile_contract_with_suppressed_errors(ZkSolcCompiler::default()); +} + +#[test] +fn zksync_pre_1_5_7_can_compile_contract_with_suppressed_errors() { + let compiler = ZkSolcCompiler { + zksolc: ZkSolc::get_path_for_version(&semver::Version::new(1, 5, 6)).unwrap(), + solc: Default::default(), + }; + test_zksync_can_compile_contract_with_suppressed_errors(compiler); +} + +fn test_zksync_can_compile_contract_with_suppressed_warnings(compiler: ZkSolcCompiler) { + let _ = tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .ok(); + let mut project = TempProject::::dapptools().unwrap(); + project.project_mut().compiler = compiler; + + project + .add_source( + "Warning", + r#" + // SPDX-License-Identifier: MIT OR Apache-2.0 + pragma solidity ^0.8.10; + contract Warning { + function test() public view { + require(tx.origin != address(0), "what"); + } + } + "#, + ) + .unwrap(); + + let compiled = zksync::project_compile(project.project()).unwrap(); + compiled.assert_success(); + assert!( + compiled + .compiler_output + .errors + .iter() + .any(|err| err.is_warning() && err.message.contains("tx.origin")), + "{:#?}", + compiled.compiler_output.errors + ); + + project.project_mut().settings.settings.suppressed_warnings = + HashSet::from([ZkSolcWarning::TxOrigin]); + + let compiled = zksync::project_compile(project.project()).unwrap(); + compiled.assert_success(); + assert!(compiled.find_first("Warning").is_some()); + assert!( + !compiled + .compiler_output + .errors + .iter() + .any(|err| err.is_warning() && err.message.contains("tx.origin")), + "{:#?}", + compiled.compiler_output.errors + ); +} + +#[test] +fn zksync_can_compile_contract_with_suppressed_warnings() { + test_zksync_can_compile_contract_with_suppressed_warnings(ZkSolcCompiler::default()); +} + +#[test] +fn zksync_pre_1_5_7_can_compile_contract_with_suppressed_warnings() { + let compiler = ZkSolcCompiler { + zksolc: ZkSolc::get_path_for_version(&semver::Version::new(1, 5, 6)).unwrap(), + solc: Default::default(), + }; + test_zksync_can_compile_contract_with_suppressed_warnings(compiler); +} + #[test] fn zksync_can_compile_dapp_detect_changes_in_libs() { let _ = tracing_subscriber::fmt()