Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

fix(solc): add RuntimeOrHandle & fix solc blocking installation #1260

Merged
merged 8 commits into from
May 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions ethers-solc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ serde_json = "1.0.68"
serde = { version = "1.0.130", features = ["derive"] }
semver = { version = "1.0.9", features = ["serde"] }
walkdir = "2.3.2"
tokio = { version = "1.15.0", default-features = false, features = ["process", "io-util", "fs", "time"], optional = true }
tokio = { version = "1.15.0", default-features = false, features = ["rt"] }
futures-util = { version = "^0.3", optional = true }
once_cell = "1.10.0"
regex = "1.5.5"
Expand All @@ -39,6 +39,7 @@ solang-parser = { default-features = false, version = "=0.1.13" }
rayon = "1.5.2"
rand = { version = "0.8.5", optional = true }
path-slash = "0.1.4"
cfg-if = "1.0.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
home = "0.5.3"
Expand Down Expand Up @@ -80,7 +81,7 @@ required-features = ["full", "project-util"]

[features]
default = ["rustls"]
async = ["tokio", "futures-util"]
async = ["tokio/process", "tokio/io-util", "tokio/fs", "tokio/time", "futures-util"]
full = ["async", "svm-solc"]
svm-solc = ["svm/blocking", "svm-builds", "sha2"]
# Utilities for creating and testing project workspaces
Expand Down
24 changes: 23 additions & 1 deletion ethers-solc/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,21 @@ impl Solc {
/// Blocking version of `Self::install`
#[cfg(all(feature = "svm-solc"))]
pub fn blocking_install(version: &Version) -> std::result::Result<Self, svm::SolcVmError> {
rkrasiuk marked this conversation as resolved.
Show resolved Hide resolved
use crate::utils::RuntimeOrHandle;

tracing::trace!("blocking installing solc version \"{}\"", version);
crate::report::solc_installation_start(version);
match svm::blocking_install(version) {
// the async version `svm::install` is used instead of `svm::blocking_intsall`
// because the underlying `reqwest::blocking::Client` does not behave well
// in tokio rt. see https://github.com/seanmonstar/reqwest/issues/1017
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
let installation = svm::blocking_install(version);
} else {
let installation = RuntimeOrHandle::new().block_on(svm::install(version));
}
};
match installation {
Ok(path) => {
crate::report::solc_installation_success(version);
Ok(Solc::new(path))
Expand Down Expand Up @@ -723,6 +735,7 @@ mod tests {
let other = solc().async_compile(&serde_json::json!(input)).await.unwrap();
assert_eq!(out, other);
}

#[cfg(feature = "async")]
#[tokio::test]
async fn async_solc_compile_works2() {
Expand Down Expand Up @@ -799,6 +812,15 @@ mod tests {
assert_eq!(res.solc, expected);
}

#[test]
#[cfg(feature = "svm-solc")]
fn can_install_solc_in_tokio_rt() {
let version = Version::from_str("0.8.6").unwrap();
let rt = tokio::runtime::Runtime::new().unwrap();
let result = rt.block_on(async { Solc::blocking_install(&version) });
assert!(result.is_ok());
}

#[test]
fn does_not_find_not_installed_version() {
let ver = "1.1.1";
Expand Down
34 changes: 34 additions & 0 deletions ethers-solc/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,40 @@ pub(crate) fn find_fave_or_alt_path(root: impl AsRef<Path>, fave: &str, alt: &st
p
}

#[cfg(not(target_arch = "wasm32"))]
use tokio::runtime::{Handle, Runtime};

#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug)]
pub enum RuntimeOrHandle {
Runtime(Runtime),
Handle(Handle),
}

#[cfg(not(target_arch = "wasm32"))]
impl Default for RuntimeOrHandle {
fn default() -> Self {
Self::new()
}
}

#[cfg(not(target_arch = "wasm32"))]
impl RuntimeOrHandle {
pub fn new() -> RuntimeOrHandle {
match Handle::try_current() {
Ok(handle) => RuntimeOrHandle::Handle(handle),
Err(_) => RuntimeOrHandle::Runtime(Runtime::new().expect("Failed to start runtime")),
}
}

pub fn block_on<F: std::future::Future>(&self, f: F) -> F::Output {
match &self {
RuntimeOrHandle::Runtime(runtime) => runtime.block_on(f),
RuntimeOrHandle::Handle(handle) => tokio::task::block_in_place(|| handle.block_on(f)),
}
}
}

/// Creates a new named tempdir
#[cfg(any(test, feature = "project-util"))]
pub(crate) fn tempdir(name: &str) -> Result<tempfile::TempDir, SolcIoError> {
Expand Down
51 changes: 51 additions & 0 deletions ethers-solc/tests/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use ethers_solc::{
ProjectPathsConfig, Solc, TestFileFilter,
};
use pretty_assertions::assert_eq;
use semver::Version;

#[allow(unused)]
fn init_tracing() {
Expand Down Expand Up @@ -1329,3 +1330,53 @@ fn can_compile_model_checker_sample() {
assert!(!compiled.has_compiler_errors());
assert!(compiled.has_compiler_warnings());
}

fn remove_solc_if_exists(version: &Version) {
match Solc::find_svm_installed_version(version.to_string()).unwrap() {
Some(_) => svm::remove_version(&version).expect("failed to remove version"),
None => {}
};
}

#[tokio::test(flavor = "multi_thread")]
async fn can_install_solc_and_compile_version() {
let project = TempProject::dapptools().unwrap();
let version = Version::new(0, 8, 10);

project
.add_source(
"Contract",
format!(
r#"
pragma solidity {};
contract Contract {{ }}
"#,
version.to_string()
),
)
.unwrap();

remove_solc_if_exists(&version);

let compiled = project.compile().unwrap();
assert!(!compiled.has_compiler_errors());
}

#[tokio::test(flavor = "multi_thread")]
async fn can_install_solc_and_compile_std_json_input_async() {
let tmp = TempProject::dapptools_init().unwrap();
tmp.assert_no_errors();
let source = tmp.list_source_files().into_iter().find(|p| p.ends_with("Dapp.t.sol")).unwrap();
let input = tmp.project().standard_json_input(source).unwrap();
let solc = &tmp.project().solc;

assert!(input.settings.remappings.contains(&"ds-test/=lib/ds-test/src/".parse().unwrap()));
let input: CompilerInput = input.into();
assert!(input.sources.contains_key(Path::new("lib/ds-test/src/test.sol")));

remove_solc_if_exists(&solc.version().expect("failed to get version"));

let out = solc.async_compile(&input).await.unwrap();
assert!(!out.has_error());
assert!(out.sources.contains_key("lib/ds-test/src/test.sol"));
}