Repository: gakonst/ethers-rs
Pull request #1437 (2022-06-30 14:28:56)

fix(solc): apply base path to model checker contracts
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation
when applying the `base_path` also strip it from the model_checker's paths
<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

- [ ] Added Tests
- [ ] Added Documentation
- [ ] Updated the changelog



Patch:
diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs
index a964b20e..7958a211 100644
--- a/ethers-solc/src/artifact_output/configurable.rs
+++ b/ethers-solc/src/artifact_output/configurable.rs
@@ -1,4 +1,12 @@
 //! A configurable artifacts handler implementation
+//!
+//! Configuring artifacts requires two pieces: the `ConfigurableArtifacts` handler, which contains
+//! the configuration of how to construct the `ConfigurableArtifact` type based on a `Contract`. The
+//! `ConfigurableArtifacts` populates a single `Artifact`, the `ConfigurableArtifact`, by default
+//! with essential entries only, such as `abi`, `bytecode`,..., but may include additional values
+//! based on its `ExtraOutputValues` that maps to various objects in the solc contract output, see
+//! also: [`OutputSelection`](crate::artifacts::OutputSelection). In addition to that some output
+//! values can also be emitted as standalone files.
 
 use crate::{
     artifacts::{
@@ -34,6 +42,8 @@ pub struct ConfigurableContractArtifact {
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub assembly: Option<String>,
     #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub opcodes: Option<String>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
     pub method_identifiers: Option<BTreeMap<String, String>>,
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     pub generated_sources: Vec<GeneratedSource>,
@@ -248,6 +258,7 @@ impl ArtifactOutput for ConfigurableArtifacts {
         let mut artifact_assembly = None;
         let mut artifact_storage_layout = None;
         let mut generated_sources = None;
+        let mut opcodes = None;
 
         let Contract {
             abi,
@@ -298,13 +309,17 @@ impl ArtifactOutput for ConfigurableArtifacts {
 
             if self.additional_values.function_debug_data {
                 artifact_function_debug_data =
-                    bytecode.as_ref().map(|b| b.function_debug_data.clone());
+                    bytecode.as_mut().map(|code| std::mem::take(&mut code.function_debug_data));
             }
             if self.additional_values.generated_sources {
                 generated_sources =
                     bytecode.as_mut().map(|code| std::mem::take(&mut code.generated_sources));
             }
 
+            if self.additional_values.opcodes {
+                opcodes = bytecode.as_mut().and_then(|code| code.opcodes.take())
+            }
+
             artifact_bytecode = bytecode.map(Into::into);
             artifact_deployed_bytecode = deployed_bytecode.map(Into::into);
             artifact_method_identifiers = Some(method_identifiers);
@@ -322,6 +337,7 @@ impl ArtifactOutput for ConfigurableArtifacts {
             bytecode: artifact_bytecode,
             deployed_bytecode: artifact_deployed_bytecode,
             assembly: artifact_assembly,
+            opcodes,
             function_debug_data: artifact_function_debug_data,
             method_identifiers: artifact_method_identifiers,
             gas_estimates: artifact_gas_estimates,
@@ -373,6 +389,7 @@ pub struct ExtraOutputValues {
     pub function_debug_data: bool,
     pub generated_sources: bool,
     pub source_map: bool,
+    pub opcodes: bool,
 
     /// PRIVATE: This structure may grow, As such, constructing this structure should
     /// _always_ be done using a public constructor or update syntax:
@@ -408,6 +425,7 @@ impl ExtraOutputValues {
             function_debug_data: true,
             generated_sources: true,
             source_map: true,
+            opcodes: true,
             __non_exhaustive: (),
         }
     }
@@ -444,6 +462,7 @@ impl ExtraOutputValues {
                         config.method_identifiers = true;
                         config.generated_sources = true;
                         config.source_map = true;
+                        config.opcodes = true;
                     }
                     EvmOutputSelection::Assembly => {
                         config.assembly = true;
@@ -457,6 +476,9 @@ impl ExtraOutputValues {
                     EvmOutputSelection::ByteCode(BytecodeOutputSelection::FunctionDebugData) => {
                         config.function_debug_data = true;
                     }
+                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::Opcodes) => {
+                        config.opcodes = true;
+                    }
                     EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources) => {
                         config.generated_sources = true;
                     }
diff --git a/ethers-solc/src/artifact_output/mod.rs b/ethers-solc/src/artifact_output/mod.rs
index db5be8f9..616e6cdb 100644
--- a/ethers-solc/src/artifact_output/mod.rs
+++ b/ethers-solc/src/artifact_output/mod.rs
@@ -1,8 +1,16 @@
 //! Output artifact handling
 
 use crate::{
-    artifacts::FileToContractsMap, error::Result, utils, HardhatArtifact, ProjectPathsConfig,
-    SolcError,
+    artifacts::{
+        contract::{CompactContract, CompactContractBytecode, Contract},
+        BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode,
+        FileToContractsMap, SourceFile,
+    },
+    compile::output::{contracts::VersionedContracts, sources::VersionedSourceFiles},
+    error::Result,
+    sourcemap::{SourceMap, SyntaxError},
+    sources::VersionedSourceFile,
+    utils, HardhatArtifact, ProjectPathsConfig, SolcError,
 };
 use ethers_core::{abi::Abi, types::Bytes};
 use semver::Version;
@@ -13,18 +21,7 @@ use std::{
     fmt, fs, io,
     path::{Path, PathBuf},
 };
-
 mod configurable;
-use crate::{
-    artifacts::{
-        contract::{CompactContract, CompactContractBytecode, Contract},
-        BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode,
-        SourceFile,
-    },
-    compile::output::{contracts::VersionedContracts, sources::VersionedSourceFiles},
-    sourcemap::{SourceMap, SyntaxError},
-    sources::VersionedSourceFile,
-};
 pub use configurable::*;
 
 /// Represents unique artifact metadata for identifying artifacts on output
diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs
index eed1593f..c08b64b2 100644
--- a/ethers-solc/src/artifacts/mod.rs
+++ b/ethers-solc/src/artifacts/mod.rs
@@ -422,6 +422,24 @@ impl Settings {
                 })
                 .collect(),
         );
+
+        if let Some(mut model_checker) = self.model_checker.take() {
+            model_checker.contracts = model_checker
+                .contracts
+                .into_iter()
+                .map(|(path, contracts)| {
+                    (
+                        Path::new(&path)
+                            .strip_prefix(base)
+                            .map(|p| format!("{}", p.display()))
+                            .unwrap_or(path),
+                        contracts,
+                    )
+                })
+                .collect();
+            self.model_checker = Some(model_checker);
+        }
+
         self
     }
 }
diff --git a/ethers-solc/src/compile/output/contracts.rs b/ethers-solc/src/compile/output/contracts.rs
index 6a2f0873..f8de71b2 100644
--- a/ethers-solc/src/compile/output/contracts.rs
+++ b/ethers-solc/src/compile/output/contracts.rs
@@ -166,6 +166,28 @@ impl VersionedContracts {
         })
     }
 
+    /// Returns an iterator over (`file`, `name`, `Contract`)
+    pub fn into_contracts_with_files(self) -> impl Iterator<Item = (String, String, Contract)> {
+        self.0.into_iter().flat_map(|(file, contracts)| {
+            contracts.into_iter().flat_map(move |(name, c)| {
+                let file = file.clone();
+                c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract))
+            })
+        })
+    }
+
+    /// Returns an iterator over (`file`, `name`, `Contract`, `Version`)
+    pub fn into_contracts_with_files_and_version(
+        self,
+    ) -> impl Iterator<Item = (String, String, Contract, Version)> {
+        self.0.into_iter().flat_map(|(file, contracts)| {
+            contracts.into_iter().flat_map(move |(name, c)| {
+                let file = file.clone();
+                c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract, c.version))
+            })
+        })
+    }
+
     /// Sets the contract's file paths to `root` adjoined to `self.file`.
     pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
         let root = root.as_ref();
diff --git a/ethers-solc/src/compile/output/mod.rs b/ethers-solc/src/compile/output/mod.rs
index c7e10779..4724cf7f 100644
--- a/ethers-solc/src/compile/output/mod.rs
+++ b/ethers-solc/src/compile/output/mod.rs
@@ -388,6 +388,32 @@ impl AggregatedCompilerOutput {
         self.contracts.into_contracts()
     }
 
+    /// Returns an iterator over (`file`, `name`, `Contract`)
+    pub fn contracts_with_files_iter(&self) -> impl Iterator<Item = (&String, &String, &Contract)> {
+        self.contracts.contracts_with_files()
+    }
+
+    /// Returns an iterator over (`file`, `name`, `Contract`)
+    pub fn contracts_with_files_into_iter(
+        self,
+    ) -> impl Iterator<Item = (String, String, Contract)> {
+        self.contracts.into_contracts_with_files()
+    }
+
+    /// Returns an iterator over (`file`, `name`, `Contract`, `Version`)
+    pub fn contracts_with_files_and_version_iter(
+        &self,
+    ) -> impl Iterator<Item = (&String, &String, &Contract, &Version)> {
+        self.contracts.contracts_with_files_and_version()
+    }
+
+    /// Returns an iterator over (`file`, `name`, `Contract`, `Version`)
+    pub fn contracts_with_files_and_version_into_iter(
+        self,
+    ) -> impl Iterator<Item = (String, String, Contract, Version)> {
+        self.contracts.into_contracts_with_files_and_version()
+    }
+
     /// Given the contract file's path and the contract's name, tries to return the contract's
     /// bytecode, runtime bytecode, and abi
     /// # Example


Test patch:
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index f02ef995..322e7a20 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -139,6 +139,7 @@ fn can_compile_configured() {
             metadata: true,
             ir: true,
             ir_optimized: true,
+            opcodes: true,
             ..Default::default()
         },
         ..Default::default()
@@ -152,6 +153,7 @@ fn can_compile_configured() {
     assert!(artifact.raw_metadata.is_some());
     assert!(artifact.ir.is_some());
     assert!(artifact.ir_optimized.is_some());
+    assert!(artifact.opcodes.is_some());
 }
 
 #[test]