From ecb767de65d1fa1bc9d4e33c79a8818a9ae610b3 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 14 Aug 2024 21:39:25 +0800 Subject: [PATCH] [wip] feat: compilation restrictions --- Cargo.lock | 53 ++++++++---------- Cargo.toml | 5 +- crates/cast/bin/cmd/storage.rs | 4 +- crates/config/src/compilation.rs | 76 ++++++++++++++++++++++++++ crates/config/src/lib.rs | 76 ++++++++++++++++++++++++-- crates/forge/bin/cmd/bind_json.rs | 8 +-- crates/forge/bin/cmd/eip712.rs | 14 ++--- crates/forge/bin/cmd/test/mod.rs | 4 +- crates/forge/tests/cli/config.rs | 2 + crates/verify/src/etherscan/flatten.rs | 2 +- crates/verify/src/provider.rs | 6 +- 11 files changed, 194 insertions(+), 56 deletions(-) create mode 100644 crates/config/src/compilation.rs diff --git a/Cargo.lock b/Cargo.lock index a4621e5568cb..95be3a1125ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3709,9 +3709,8 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d91e510bd537970f68f8462dea0e8df0a2302d4749fb57bc8e10bbd32a283e2" +version = "0.11.0" +source = "git+https://github.com/foundry-rs/compilers?rev=5b42d05#5b42d05032bde83e36d59d52ba6e9d7b7b6b6a99" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3747,9 +3746,8 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9971eefe4eae1cf2ac707beb4d40f63304b34c81c0961d299e461c14a23b1e7" +version = "0.11.0" +source = "git+https://github.com/foundry-rs/compilers?rev=5b42d05#5b42d05032bde83e36d59d52ba6e9d7b7b6b6a99" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3757,9 +3755,8 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde3d12776c295ad85bcdbbae18f4601e384f40a62b0e3371d880bbcd91c65c" +version = "0.11.0" +source = "git+https://github.com/foundry-rs/compilers?rev=5b42d05#5b42d05032bde83e36d59d52ba6e9d7b7b6b6a99" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3781,9 +3778,8 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "569a769f6105248816c253715ec39977d61d369e9c67e4774d6870da8f64dffc" +version = "0.11.0" +source = "git+https://github.com/foundry-rs/compilers?rev=5b42d05#5b42d05032bde83e36d59d52ba6e9d7b7b6b6a99" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3796,9 +3792,8 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f10ade77fa0eab75e142a76711c42a258781bad0c4516ad64aa413297ebb72e" +version = "0.11.0" +source = "git+https://github.com/foundry-rs/compilers?rev=5b42d05#5b42d05032bde83e36d59d52ba6e9d7b7b6b6a99" dependencies = [ "alloy-primitives", "cfg-if", @@ -6488,9 +6483,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 = "plotters" @@ -7161,9 +7156,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48294aab02ed5d1940ad9b06f2a3230c3f0d98db6eacd618878cf143e204f6b0" +checksum = "b57b33a24b5b8b8efa1da3f60d44f02d6e649f06ef925d7780723ff14ff55321" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -8431,18 +8426,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[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", @@ -8910,9 +8905,9 @@ dependencies = [ [[package]] name = "tracing-tracy" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a90519f16f55e5c62ffd5976349f10744435a919ecff83d918300575dfb69b" +checksum = "dc775fdaf33c3dfd19dc354729e65e87914bc67dcdc390ca1210807b8bee5902" dependencies = [ "tracing-core", "tracing-subscriber", @@ -8921,9 +8916,9 @@ dependencies = [ [[package]] name = "tracy-client" -version = "0.17.3" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373db47331c3407b343538df77eea2516884a0b126cdfb4b135acfd400015dd7" +checksum = "746b078c6a09ebfd5594609049e07116735c304671eaab06ce749854d23435bc" dependencies = [ "loom", "once_cell", @@ -8932,9 +8927,9 @@ dependencies = [ [[package]] name = "tracy-client-sys" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cf0064dcb31c99aa1244c1b93439359e53f72ed217eef5db50abd442241e9a" +checksum = "68613466112302fdbeabc5fa55f7d57462a0b247d5a6b7d7e09401fb471a144d" dependencies = [ "cc", ] diff --git a/Cargo.toml b/Cargo.toml index c7e38cbc9031..9bba274c3aba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,7 +167,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.7.3", default-features = false } -foundry-compilers = { version = "0.11.1", default-features = false } +foundry-compilers = { version = "0.11", default-features = false } foundry-fork-db = "0.3" solang-parser = "=0.3.3" @@ -276,7 +276,7 @@ soldeer = "=0.3.4" proptest = "1" comfy-table = "7" -# [patch.crates-io] +[patch.crates-io] # alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } # alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } # alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } @@ -300,3 +300,4 @@ comfy-table = "7" # alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } # alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } # alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "5b42d05" } \ No newline at end of file diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index a8ced363912a..e6d5ac5fe9b5 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -22,7 +22,7 @@ use foundry_compilers::{ artifacts::{ConfigurableContractArtifact, StorageLayout}, compilers::{ solc::{Solc, SolcCompiler}, - Compiler, CompilerSettings, + Compiler, }, Artifact, Project, }; @@ -288,7 +288,7 @@ fn print_storage(layout: StorageLayout, values: Vec, pretty: bool) fn add_storage_layout_output(project: &mut Project) { project.artifacts.additional_values.storage_layout = true; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { selection.0.values_mut().for_each(|contract_selection| { contract_selection .values_mut() diff --git a/crates/config/src/compilation.rs b/crates/config/src/compilation.rs new file mode 100644 index 000000000000..8ff01ef399c3 --- /dev/null +++ b/crates/config/src/compilation.rs @@ -0,0 +1,76 @@ +use crate::{filter::GlobMatcher, serde_helpers}; +use foundry_compilers::{ + artifacts::EvmVersion, + multi::{MultiCompilerRestrictions, MultiCompilerSettings}, + settings::VyperRestrictions, + solc::{EvmVersionRestriction, SolcRestrictions}, + RestrictionsWithVersion, +}; +use semver::VersionReq; +use serde::{Deserialize, Serialize}; + +/// Keeps possible overrides for default settings which users may configure to construct additional +/// settings profile. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct SettingsOverrides { + pub name: String, + via_ir: Option, + #[serde(default, with = "serde_helpers::display_from_str_opt")] + evm_version: Option, + optimizer: Option, + optimizer_runs: Option, +} + +impl SettingsOverrides { + /// Applies the overrides to the given settings. + pub fn apply(&self, settings: &mut MultiCompilerSettings) { + if let Some(via_ir) = self.via_ir { + settings.solc.via_ir = Some(via_ir); + } + + if let Some(evm_version) = self.evm_version { + settings.solc.evm_version = Some(evm_version); + settings.vyper.evm_version = Some(evm_version); + } + + if let Some(enabled) = self.optimizer { + settings.solc.optimizer.enabled = Some(enabled); + } + + if let Some(optimizer_runs) = self.optimizer_runs { + settings.solc.optimizer.runs = Some(optimizer_runs); + } + } +} + +/// Restrictions for compilation of given paths. +/// +/// Only purpose of this type is to accept user input to later construct +/// `RestrictionsWithVersion`. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct CompilationRestrictions { + pub paths: GlobMatcher, + version: Option, + via_ir: Option, + min_optimizer_runs: Option, + max_optimizer_runs: Option, + #[serde(flatten)] + evm_version: EvmVersionRestriction, +} + +impl From for RestrictionsWithVersion { + fn from(value: CompilationRestrictions) -> Self { + Self { + restrictions: MultiCompilerRestrictions { + solc: SolcRestrictions { + evm_version: value.evm_version, + via_ir: value.via_ir, + min_optimizer_runs: value.min_optimizer_runs, + max_optimizer_runs: value.max_optimizer_runs, + }, + vyper: VyperRestrictions { evm_version: value.evm_version }, + }, + version: value.version, + } + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d77e30492fc5..175a3b3f278d 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -33,8 +33,10 @@ use foundry_compilers::{ Compiler, }, error::SolcError, + multi::{MultiCompilerParsedSource, MultiCompilerRestrictions}, solc::{CliSettings, SolcSettings}, - ConfigurableArtifacts, Project, ProjectPathsConfig, VyperLanguage, + ConfigurableArtifacts, Graph, Project, ProjectPathsConfig, RestrictionsWithVersion, + VyperLanguage, }; use inflector::Inflector; use regex::Regex; @@ -43,7 +45,7 @@ use semver::Version; use serde::{Deserialize, Serialize, Serializer}; use std::{ borrow::Cow, - collections::HashMap, + collections::{BTreeMap, HashMap}, fs, path::{Path, PathBuf}, str::FromStr, @@ -115,6 +117,9 @@ use vyper::VyperConfig; mod bind_json; use bind_json::BindJsonConfig; +mod compilation; +use compilation::{CompilationRestrictions, SettingsOverrides}; + /// Foundry configuration /// /// # Defaults @@ -469,6 +474,14 @@ pub struct Config { #[serde(rename = "__warnings", default, skip_serializing)] pub warnings: Vec, + /// Additional settings profiles to use when compiling. + #[serde(default)] + pub additional_compiler_profiles: Vec, + + /// Restrictions on compilation of certain files. + #[serde(default)] + pub compilation_restrictions: Vec, + /// PRIVATE: This structure may grow, As such, constructing this structure should /// _always_ be done using a public constructor or update syntax: /// @@ -835,12 +848,65 @@ impl Config { self.create_project(false, true) } + /// Builds mapping with additional settings profiles. + fn additional_settings( + &self, + base: &MultiCompilerSettings, + ) -> BTreeMap { + let mut map = BTreeMap::new(); + + for profile in &self.additional_compiler_profiles { + let mut settings = base.clone(); + profile.apply(&mut settings); + map.insert(profile.name.clone(), settings); + } + + map + } + + /// Resolves globs and builds a mapping from individual source files to their restrictions + fn restrictions( + &self, + paths: &ProjectPathsConfig, + ) -> Result>, SolcError> + { + let mut map = BTreeMap::new(); + + let graph = Graph::::resolve(paths)?; + let (sources, _) = graph.into_sources(); + + for res in &self.compilation_restrictions { + for source in sources.keys().filter(|path| { + if res.paths.is_match(path) { + true + } else if let Ok(path) = path.strip_prefix(&paths.root) { + res.paths.is_match(path) + } else { + false + } + }) { + let res: RestrictionsWithVersion<_> = res.clone().into(); + if !map.contains_key(source) { + map.insert(source.clone(), res); + } else { + map.get_mut(source.as_path()).unwrap().merge(res); + } + } + } + + Ok(map) + } + /// Creates a [Project] with the given `cached` and `no_artifacts` flags pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { + let settings = self.compiler_settings()?; + let paths = self.project_paths(); let mut builder = Project::builder() .artifacts(self.configured_artifacts_handler()) - .paths(self.project_paths()) - .settings(self.compiler_settings()?) + .additional_settings(self.additional_settings(&settings)) + .restrictions(self.restrictions(&paths)?) + .settings(settings) + .paths(paths) .ignore_error_codes(self.ignored_error_codes.iter().copied().map(Into::into)) .ignore_paths(self.ignored_file_paths.clone()) .set_compiler_severity_filter(if self.deny_warnings { @@ -2182,6 +2248,8 @@ impl Default for Config { eof_version: None, alphanet: false, transaction_timeout: 120, + additional_compiler_profiles: Default::default(), + compilation_restrictions: vec![], _non_exhaustive: (), } } diff --git a/crates/forge/bin/cmd/bind_json.rs b/crates/forge/bin/cmd/bind_json.rs index bd2d0ea30d67..d6d95e1f03e9 100644 --- a/crates/forge/bin/cmd/bind_json.rs +++ b/crates/forge/bin/cmd/bind_json.rs @@ -11,7 +11,7 @@ use foundry_compilers::{ multi::{MultiCompilerLanguage, MultiCompilerParsedSource}, project::ProjectCompiler, solc::SolcLanguage, - CompilerSettings, Graph, Project, + Graph, Project, }; use foundry_config::Config; use itertools::Itertools; @@ -72,7 +72,7 @@ impl BindJsonArgs { // We only generate bindings for a single Solidity version to avoid conflicts. let mut sources = graph // resolve graph into mapping language -> version -> sources - .into_sources_by_version(project.offline, &project.locked_versions, &project.compiler)? + .into_sources_by_version(&project)? .0 .into_iter() // we are only interested in Solidity sources @@ -81,7 +81,7 @@ impl BindJsonArgs { .1 .into_iter() // For now, we are always picking the latest version. - .max_by(|(v1, _), (v2, _)| v1.cmp(v2)) + .max_by(|(v1, _, _), (v2, _, _)| v1.cmp(v2)) .unwrap() .1; @@ -229,7 +229,7 @@ impl PreprocessedState { fn compile(self) -> Result { let Self { sources, target_path, mut project, config } = self; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::ast_output_selection(); }); diff --git a/crates/forge/bin/cmd/eip712.rs b/crates/forge/bin/cmd/eip712.rs index 636c305806dc..4cd5f6389f10 100644 --- a/crates/forge/bin/cmd/eip712.rs +++ b/crates/forge/bin/cmd/eip712.rs @@ -2,14 +2,10 @@ use clap::{Parser, ValueHint}; use eyre::{Ok, OptionExt, Result}; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::compile::ProjectCompiler; -use foundry_compilers::{ - artifacts::{ - output_selection::OutputSelection, - visitor::{Visitor, Walk}, - ContractDefinition, EnumDefinition, SourceUnit, StructDefinition, TypeDescriptions, - TypeName, - }, - CompilerSettings, +use foundry_compilers::artifacts::{ + output_selection::OutputSelection, + visitor::{Visitor, Walk}, + ContractDefinition, EnumDefinition, SourceUnit, StructDefinition, TypeDescriptions, TypeName, }; use std::{collections::BTreeMap, path::PathBuf}; @@ -31,7 +27,7 @@ impl Eip712Args { let config = self.try_load_config_emit_warnings()?; let mut project = config.create_project(false, true)?; let target_path = dunce::canonicalize(self.target_path)?; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::ast_output_selection(); }); diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 0eebbb9a0ee7..6412dd2255e7 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -23,7 +23,7 @@ use foundry_cli::{ use foundry_common::{cli_warn, compile::ProjectCompiler, evm::EvmArgs, fs, shell}; use foundry_compilers::{ artifacts::output_selection::OutputSelection, - compilers::{multi::MultiCompilerLanguage, CompilerSettings, Language}, + compilers::{multi::MultiCompilerLanguage, Language}, utils::source_files_iter, ProjectCompileOutput, }; @@ -194,7 +194,7 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, ) -> Result> { let mut project = config.create_project(true, true)?; - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::common_output_selection(["abi".to_string()]); }); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 1ddd402722dd..d47f8baa15ae 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -151,6 +151,8 @@ forgetest!(can_extract_config_values, |prj, cmd| { eof_version: None, alphanet: false, transaction_timeout: 120, + additional_compiler_profiles: vec![], + compilation_restrictions: vec![], _non_exhaustive: (), }; prj.write_config(input.clone()); diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index ffe1496cad5d..a0b3defd7f90 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -90,7 +90,7 @@ impl EtherscanFlattenedSource { let out = SolcCompiler::Specific(solc).compile(&input)?; if out.errors.iter().any(|e| e.is_error()) { let mut o = AggregatedCompilerOutput::::default(); - o.extend(version, RawBuildInfo::new(&input, &out, false)?, out); + o.extend(version, RawBuildInfo::new(&input, &out, false)?, "default", out); let diags = o.diagnostics(&[], &[], Default::default()); eyre::bail!( diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 6e5b29d5e34c..3ca620d83ae3 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -9,7 +9,7 @@ use eyre::{OptionExt, Result}; use foundry_common::compile::ProjectCompiler; use foundry_compilers::{ artifacts::{output_selection::OutputSelection, Metadata, Source}, - compilers::{multi::MultiCompilerParsedSource, solc::SolcCompiler, CompilerSettings}, + compilers::{multi::MultiCompilerParsedSource, solc::SolcCompiler}, solc::Solc, Graph, Project, }; @@ -46,7 +46,7 @@ impl VerificationContext { /// Compiles target contract requesting only ABI and returns it. pub fn get_target_abi(&self) -> Result { let mut project = self.project.clone(); - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::common_output_selection(["abi".to_string()]) }); @@ -65,7 +65,7 @@ impl VerificationContext { /// Compiles target file requesting only metadata and returns it. pub fn get_target_metadata(&self) -> Result { let mut project = self.project.clone(); - project.settings.update_output_selection(|selection| { + project.update_output_selection(|selection| { *selection = OutputSelection::common_output_selection(["metadata".to_string()]); });