diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 8997278..5656528 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -18,8 +18,6 @@ use serde::{Deserialize, Serialize}; use revive_common::EVMVersion; use revive_dt_common::cached_fs::read_to_string; -use revive_dt_common::types::VersionOrRequirement; -use revive_dt_config::{ResolcConfiguration, SolcConfiguration, WorkingDirectoryConfiguration}; // Re-export this as it's a part of the compiler interface. pub use revive_dt_common::types::{Mode, ModeOptimizerSetting, ModePipeline}; @@ -29,7 +27,7 @@ pub mod revive_resolc; pub mod solc; /// A common interface for all supported Solidity compilers. -pub trait DynSolidityCompiler { +pub trait SolidityCompiler { /// Returns the version of the compiler. fn version(&self) -> &Version; @@ -50,38 +48,6 @@ pub trait DynSolidityCompiler { ) -> bool; } -// TODO: Remove -/// A common interface for all supported Solidity compilers. -pub trait SolidityCompiler: Sized { - /// Instantiates a new compiler object. - /// - /// Based on the given context and [`VersionOrRequirement`] this function instantiates a - /// new compiler object. Certain implementations of this trait might choose to cache the - /// compiler objects and return the same compiler objects if given the same set of arguments. - fn new( - context: impl AsRef - + AsRef - + AsRef, - version: impl Into>, - ) -> impl Future>; - - /// Returns the version of the compiler. - fn version(&self) -> &Version; - - /// Returns the path of the compiler executable. - fn path(&self) -> &Path; - - /// The low-level compiler interface. - fn build(&self, input: CompilerInput) -> impl Future>; - - /// Does the compiler support the provided mode and version settings. - fn supports_mode( - &self, - optimizer_setting: ModeOptimizerSetting, - pipeline: ModePipeline, - ) -> bool; -} - /// The generic compilation input configuration. #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct CompilerInput { @@ -188,11 +154,7 @@ impl Compiler { callback(self) } - pub async fn try_build(self, compiler: &impl SolidityCompiler) -> Result { - compiler.build(self.input).await - } - - pub async fn dyn_try_build(self, compiler: &dyn DynSolidityCompiler) -> Result { + pub async fn try_build(self, compiler: &dyn SolidityCompiler) -> Result { compiler.build(self.input).await } diff --git a/crates/compiler/src/revive_resolc.rs b/crates/compiler/src/revive_resolc.rs index 03074d3..79a32fb 100644 --- a/crates/compiler/src/revive_resolc.rs +++ b/crates/compiler/src/revive_resolc.rs @@ -3,6 +3,7 @@ use std::{ path::PathBuf, + pin::Pin, process::Stdio, sync::{Arc, LazyLock}, }; @@ -17,8 +18,7 @@ use revive_solc_json_interface::{ }; use crate::{ - CompilerInput, CompilerOutput, DynSolidityCompiler, ModeOptimizerSetting, ModePipeline, - SolidityCompiler, solc::Solc, + CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler, solc::Solc, }; use alloy::json_abi::JsonAbi; @@ -38,34 +38,8 @@ struct ResolcInner { resolc_path: PathBuf, } -impl DynSolidityCompiler for Resolc { - fn version(&self) -> &Version { - SolidityCompiler::version(self) - } - - fn path(&self) -> &std::path::Path { - SolidityCompiler::path(self) - } - - fn build( - &self, - input: CompilerInput, - ) -> std::pin::Pin> + '_>> { - Box::pin(SolidityCompiler::build(self, input)) - } - - fn supports_mode( - &self, - optimizer_setting: ModeOptimizerSetting, - pipeline: ModePipeline, - ) -> bool { - SolidityCompiler::supports_mode(self, optimizer_setting, pipeline) - } -} - -// TODO: Remove -impl SolidityCompiler for Resolc { - async fn new( +impl Resolc { + pub async fn new( context: impl AsRef + AsRef + AsRef, @@ -92,11 +66,13 @@ impl SolidityCompiler for Resolc { }) .clone()) } +} +impl SolidityCompiler for Resolc { fn version(&self) -> &Version { // We currently return the solc compiler version since we do not support multiple resolc // compiler versions. - DynSolidityCompiler::version(&self.0.solc) + SolidityCompiler::version(&self.0.solc) } fn path(&self) -> &std::path::Path { @@ -104,7 +80,7 @@ impl SolidityCompiler for Resolc { } #[tracing::instrument(level = "debug", ret)] - async fn build( + fn build( &self, CompilerInput { pipeline, @@ -118,190 +94,196 @@ impl SolidityCompiler for Resolc { // resolc. So, we need to go back to this later once it's supported. revert_string_handling: _, }: CompilerInput, - ) -> Result { - if !matches!(pipeline, None | Some(ModePipeline::ViaYulIR)) { - anyhow::bail!( - "Resolc only supports the Y (via Yul IR) pipeline, but the provided pipeline is {pipeline:?}" - ); - } - - let input = SolcStandardJsonInput { - language: SolcStandardJsonInputLanguage::Solidity, - sources: sources - .into_iter() - .map(|(path, source)| (path.display().to_string(), source.into())) - .collect(), - settings: SolcStandardJsonInputSettings { - evm_version, - libraries: Some( - libraries - .into_iter() - .map(|(source_code, libraries_map)| { - ( - source_code.display().to_string(), - libraries_map - .into_iter() - .map(|(library_ident, library_address)| { - (library_ident, library_address.to_string()) - }) - .collect(), - ) - }) - .collect(), - ), - remappings: None, - output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()), - via_ir: Some(true), - optimizer: SolcStandardJsonInputSettingsOptimizer::new( - optimization - .unwrap_or(ModeOptimizerSetting::M0) - .optimizations_enabled(), - None, - &Version::new(0, 0, 0), - false, - ), - metadata: None, - polkavm: None, - }, - }; - - let path = &self.0.resolc_path; - let mut command = AsyncCommand::new(path); - command - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .arg("--standard-json"); - - if let Some(ref base_path) = base_path { - command.arg("--base-path").arg(base_path); - } - if !allow_paths.is_empty() { - command.arg("--allow-paths").arg( - allow_paths - .iter() - .map(|path| path.display().to_string()) - .collect::>() - .join(","), - ); - } - let mut child = command - .spawn() - .with_context(|| format!("Failed to spawn resolc at {}", path.display()))?; - - let stdin_pipe = child.stdin.as_mut().expect("stdin must be piped"); - let serialized_input = serde_json::to_vec(&input) - .context("Failed to serialize Standard JSON input for resolc")?; - stdin_pipe - .write_all(&serialized_input) - .await - .context("Failed to write Standard JSON to resolc stdin")?; - - let output = child - .wait_with_output() - .await - .context("Failed while waiting for resolc process to finish")?; - let stdout = output.stdout; - let stderr = output.stderr; - - if !output.status.success() { - let json_in = serde_json::to_string_pretty(&input) - .context("Failed to pretty-print Standard JSON input for logging")?; - let message = String::from_utf8_lossy(&stderr); - tracing::error!( - status = %output.status, - message = %message, - json_input = json_in, - "Compilation using resolc failed" - ); - anyhow::bail!("Compilation failed with an error: {message}"); - } - - let parsed = serde_json::from_slice::(&stdout) - .map_err(|e| { - anyhow::anyhow!( - "failed to parse resolc JSON output: {e}\nstderr: {}", - String::from_utf8_lossy(&stderr) - ) - }) - .context("Failed to parse resolc standard JSON output")?; - - tracing::debug!( - output = %serde_json::to_string(&parsed).unwrap(), - "Compiled successfully" - ); - - // Detecting if the compiler output contained errors and reporting them through logs and - // errors instead of returning the compiler output that might contain errors. - for error in parsed.errors.iter().flatten() { - if error.severity == "error" { - tracing::error!( - ?error, - ?input, - output = %serde_json::to_string(&parsed).unwrap(), - "Encountered an error in the compilation" + ) -> Pin> + '_>> { + Box::pin(async move { + if !matches!(pipeline, None | Some(ModePipeline::ViaYulIR)) { + anyhow::bail!( + "Resolc only supports the Y (via Yul IR) pipeline, but the provided pipeline is {pipeline:?}" ); - anyhow::bail!("Encountered an error in the compilation: {error}") } - } - let Some(contracts) = parsed.contracts else { - anyhow::bail!("Unexpected error - resolc output doesn't have a contracts section"); - }; + let input = SolcStandardJsonInput { + language: SolcStandardJsonInputLanguage::Solidity, + sources: sources + .into_iter() + .map(|(path, source)| (path.display().to_string(), source.into())) + .collect(), + settings: SolcStandardJsonInputSettings { + evm_version, + libraries: Some( + libraries + .into_iter() + .map(|(source_code, libraries_map)| { + ( + source_code.display().to_string(), + libraries_map + .into_iter() + .map(|(library_ident, library_address)| { + (library_ident, library_address.to_string()) + }) + .collect(), + ) + }) + .collect(), + ), + remappings: None, + output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()), + via_ir: Some(true), + optimizer: SolcStandardJsonInputSettingsOptimizer::new( + optimization + .unwrap_or(ModeOptimizerSetting::M0) + .optimizations_enabled(), + None, + &Version::new(0, 0, 0), + false, + ), + metadata: None, + polkavm: None, + }, + }; - let mut compiler_output = CompilerOutput::default(); - for (source_path, contracts) in contracts.into_iter() { - let src_for_msg = source_path.clone(); - let source_path = PathBuf::from(source_path) - .canonicalize() - .with_context(|| format!("Failed to canonicalize path {src_for_msg}"))?; + let path = &self.0.resolc_path; + let mut command = AsyncCommand::new(path); + command + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .arg("--standard-json"); - let map = compiler_output.contracts.entry(source_path).or_default(); - for (contract_name, contract_information) in contracts.into_iter() { - let bytecode = contract_information - .evm - .and_then(|evm| evm.bytecode.clone()) - .context("Unexpected - Contract compiled with resolc has no bytecode")?; - let abi = { - let metadata = contract_information - .metadata - .as_ref() - .context("No metadata found for the contract")?; - let solc_metadata_str = match metadata { - serde_json::Value::String(solc_metadata_str) => solc_metadata_str.as_str(), - serde_json::Value::Object(metadata_object) => { - let solc_metadata_value = metadata_object - .get("solc_metadata") - .context("Contract doesn't have a 'solc_metadata' field")?; - solc_metadata_value - .as_str() - .context("The 'solc_metadata' field is not a string")? - } - serde_json::Value::Null - | serde_json::Value::Bool(_) - | serde_json::Value::Number(_) - | serde_json::Value::Array(_) => { - anyhow::bail!("Unsupported type of metadata {metadata:?}") - } - }; - let solc_metadata = - serde_json::from_str::(solc_metadata_str).context( + if let Some(ref base_path) = base_path { + command.arg("--base-path").arg(base_path); + } + if !allow_paths.is_empty() { + command.arg("--allow-paths").arg( + allow_paths + .iter() + .map(|path| path.display().to_string()) + .collect::>() + .join(","), + ); + } + let mut child = command + .spawn() + .with_context(|| format!("Failed to spawn resolc at {}", path.display()))?; + + let stdin_pipe = child.stdin.as_mut().expect("stdin must be piped"); + let serialized_input = serde_json::to_vec(&input) + .context("Failed to serialize Standard JSON input for resolc")?; + stdin_pipe + .write_all(&serialized_input) + .await + .context("Failed to write Standard JSON to resolc stdin")?; + + let output = child + .wait_with_output() + .await + .context("Failed while waiting for resolc process to finish")?; + let stdout = output.stdout; + let stderr = output.stderr; + + if !output.status.success() { + let json_in = serde_json::to_string_pretty(&input) + .context("Failed to pretty-print Standard JSON input for logging")?; + let message = String::from_utf8_lossy(&stderr); + tracing::error!( + status = %output.status, + message = %message, + json_input = json_in, + "Compilation using resolc failed" + ); + anyhow::bail!("Compilation failed with an error: {message}"); + } + + let parsed = serde_json::from_slice::(&stdout) + .map_err(|e| { + anyhow::anyhow!( + "failed to parse resolc JSON output: {e}\nstderr: {}", + String::from_utf8_lossy(&stderr) + ) + }) + .context("Failed to parse resolc standard JSON output")?; + + tracing::debug!( + output = %serde_json::to_string(&parsed).unwrap(), + "Compiled successfully" + ); + + // Detecting if the compiler output contained errors and reporting them through logs and + // errors instead of returning the compiler output that might contain errors. + for error in parsed.errors.iter().flatten() { + if error.severity == "error" { + tracing::error!( + ?error, + ?input, + output = %serde_json::to_string(&parsed).unwrap(), + "Encountered an error in the compilation" + ); + anyhow::bail!("Encountered an error in the compilation: {error}") + } + } + + let Some(contracts) = parsed.contracts else { + anyhow::bail!("Unexpected error - resolc output doesn't have a contracts section"); + }; + + let mut compiler_output = CompilerOutput::default(); + for (source_path, contracts) in contracts.into_iter() { + let src_for_msg = source_path.clone(); + let source_path = PathBuf::from(source_path) + .canonicalize() + .with_context(|| format!("Failed to canonicalize path {src_for_msg}"))?; + + let map = compiler_output.contracts.entry(source_path).or_default(); + for (contract_name, contract_information) in contracts.into_iter() { + let bytecode = contract_information + .evm + .and_then(|evm| evm.bytecode.clone()) + .context("Unexpected - Contract compiled with resolc has no bytecode")?; + let abi = { + let metadata = contract_information + .metadata + .as_ref() + .context("No metadata found for the contract")?; + let solc_metadata_str = match metadata { + serde_json::Value::String(solc_metadata_str) => { + solc_metadata_str.as_str() + } + serde_json::Value::Object(metadata_object) => { + let solc_metadata_value = metadata_object + .get("solc_metadata") + .context("Contract doesn't have a 'solc_metadata' field")?; + solc_metadata_value + .as_str() + .context("The 'solc_metadata' field is not a string")? + } + serde_json::Value::Null + | serde_json::Value::Bool(_) + | serde_json::Value::Number(_) + | serde_json::Value::Array(_) => { + anyhow::bail!("Unsupported type of metadata {metadata:?}") + } + }; + let solc_metadata = serde_json::from_str::( + solc_metadata_str, + ) + .context( "Failed to deserialize the solc_metadata as a serde_json generic value", )?; - let output_value = solc_metadata - .get("output") - .context("solc_metadata doesn't have an output field")?; - let abi_value = output_value - .get("abi") - .context("solc_metadata output doesn't contain an abi field")?; - serde_json::from_value::(abi_value.clone()) - .context("ABI found in solc_metadata output is not valid ABI")? - }; - map.insert(contract_name, (bytecode.object, abi)); + let output_value = solc_metadata + .get("output") + .context("solc_metadata doesn't have an output field")?; + let abi_value = output_value + .get("abi") + .context("solc_metadata output doesn't contain an abi field")?; + serde_json::from_value::(abi_value.clone()) + .context("ABI found in solc_metadata output is not valid ABI")? + }; + map.insert(contract_name, (bytecode.object, abi)); + } } - } - Ok(compiler_output) + Ok(compiler_output) + }) } fn supports_mode( @@ -310,6 +292,6 @@ impl SolidityCompiler for Resolc { pipeline: ModePipeline, ) -> bool { pipeline == ModePipeline::ViaYulIR - && DynSolidityCompiler::supports_mode(&self.0.solc, optimize_setting, pipeline) + && SolidityCompiler::supports_mode(&self.0.solc, optimize_setting, pipeline) } } diff --git a/crates/compiler/src/solc.rs b/crates/compiler/src/solc.rs index 764d671..defdb19 100644 --- a/crates/compiler/src/solc.rs +++ b/crates/compiler/src/solc.rs @@ -3,6 +3,7 @@ use std::{ path::PathBuf, + pin::Pin, process::Stdio, sync::{Arc, LazyLock}, }; @@ -12,10 +13,7 @@ use revive_dt_common::types::VersionOrRequirement; use revive_dt_config::{ResolcConfiguration, SolcConfiguration, WorkingDirectoryConfiguration}; use revive_dt_solc_binaries::download_solc; -use crate::{ - CompilerInput, CompilerOutput, DynSolidityCompiler, ModeOptimizerSetting, ModePipeline, - SolidityCompiler, -}; +use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler}; use anyhow::{Context as _, Result}; use foundry_compilers_artifacts::{ @@ -39,34 +37,8 @@ struct SolcInner { solc_version: Version, } -impl DynSolidityCompiler for Solc { - fn version(&self) -> &Version { - SolidityCompiler::version(self) - } - - fn path(&self) -> &std::path::Path { - SolidityCompiler::path(self) - } - - fn build( - &self, - input: CompilerInput, - ) -> std::pin::Pin> + '_>> { - Box::pin(SolidityCompiler::build(self, input)) - } - - fn supports_mode( - &self, - optimizer_setting: ModeOptimizerSetting, - pipeline: ModePipeline, - ) -> bool { - SolidityCompiler::supports_mode(self, optimizer_setting, pipeline) - } -} - -// TODO: Remove -impl SolidityCompiler for Solc { - async fn new( +impl Solc { + pub async fn new( context: impl AsRef + AsRef + AsRef, @@ -104,7 +76,9 @@ impl SolidityCompiler for Solc { }) .clone()) } +} +impl SolidityCompiler for Solc { fn version(&self) -> &Version { &self.0.solc_version } @@ -114,7 +88,7 @@ impl SolidityCompiler for Solc { } #[tracing::instrument(level = "debug", ret)] - async fn build( + fn build( &self, CompilerInput { pipeline, @@ -126,171 +100,173 @@ impl SolidityCompiler for Solc { libraries, revert_string_handling, }: CompilerInput, - ) -> Result { - // Be careful to entirely omit the viaIR field if the compiler does not support it, - // as it will error if you provide fields it does not know about. Because - // `supports_mode` is called prior to instantiating a compiler, we should never - // ask for something which is invalid. - let via_ir = match (pipeline, self.compiler_supports_yul()) { - (pipeline, true) => pipeline.map(|p| p.via_yul_ir()), - (_pipeline, false) => None, - }; + ) -> Pin> + '_>> { + Box::pin(async move { + // Be careful to entirely omit the viaIR field if the compiler does not support it, + // as it will error if you provide fields it does not know about. Because + // `supports_mode` is called prior to instantiating a compiler, we should never + // ask for something which is invalid. + let via_ir = match (pipeline, self.compiler_supports_yul()) { + (pipeline, true) => pipeline.map(|p| p.via_yul_ir()), + (_pipeline, false) => None, + }; - let input = SolcInput { - language: SolcLanguage::Solidity, - sources: Sources( - sources - .into_iter() - .map(|(source_path, source_code)| (source_path, Source::new(source_code))) - .collect(), - ), - settings: Settings { - optimizer: Optimizer { - enabled: optimization.map(|o| o.optimizations_enabled()), - details: Some(Default::default()), + let input = SolcInput { + language: SolcLanguage::Solidity, + sources: Sources( + sources + .into_iter() + .map(|(source_path, source_code)| (source_path, Source::new(source_code))) + .collect(), + ), + settings: Settings { + optimizer: Optimizer { + enabled: optimization.map(|o| o.optimizations_enabled()), + details: Some(Default::default()), + ..Default::default() + }, + output_selection: OutputSelection::common_output_selection( + [ + ContractOutputSelection::Abi, + ContractOutputSelection::Evm(EvmOutputSelection::ByteCode( + BytecodeOutputSelection::Object, + )), + ] + .into_iter() + .map(|item| item.to_string()), + ), + evm_version: evm_version.map(|version| version.to_string().parse().unwrap()), + via_ir, + libraries: Libraries { + libs: libraries + .into_iter() + .map(|(file_path, libraries)| { + ( + file_path, + libraries + .into_iter() + .map(|(library_name, library_address)| { + (library_name, library_address.to_string()) + }) + .collect(), + ) + }) + .collect(), + }, + debug: revert_string_handling.map(|revert_string_handling| DebuggingSettings { + revert_strings: match revert_string_handling { + crate::RevertString::Default => Some(RevertStrings::Default), + crate::RevertString::Debug => Some(RevertStrings::Debug), + crate::RevertString::Strip => Some(RevertStrings::Strip), + crate::RevertString::VerboseDebug => Some(RevertStrings::VerboseDebug), + }, + debug_info: Default::default(), + }), ..Default::default() }, - output_selection: OutputSelection::common_output_selection( - [ - ContractOutputSelection::Abi, - ContractOutputSelection::Evm(EvmOutputSelection::ByteCode( - BytecodeOutputSelection::Object, - )), - ] - .into_iter() - .map(|item| item.to_string()), - ), - evm_version: evm_version.map(|version| version.to_string().parse().unwrap()), - via_ir, - libraries: Libraries { - libs: libraries - .into_iter() - .map(|(file_path, libraries)| { - ( - file_path, - libraries - .into_iter() - .map(|(library_name, library_address)| { - (library_name, library_address.to_string()) - }) - .collect(), - ) - }) - .collect(), - }, - debug: revert_string_handling.map(|revert_string_handling| DebuggingSettings { - revert_strings: match revert_string_handling { - crate::RevertString::Default => Some(RevertStrings::Default), - crate::RevertString::Debug => Some(RevertStrings::Debug), - crate::RevertString::Strip => Some(RevertStrings::Strip), - crate::RevertString::VerboseDebug => Some(RevertStrings::VerboseDebug), - }, - debug_info: Default::default(), - }), - ..Default::default() - }, - }; + }; - let path = &self.0.solc_path; - let mut command = AsyncCommand::new(path); - command - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .arg("--standard-json"); + let path = &self.0.solc_path; + let mut command = AsyncCommand::new(path); + command + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .arg("--standard-json"); - if let Some(ref base_path) = base_path { - command.arg("--base-path").arg(base_path); - } - if !allow_paths.is_empty() { - command.arg("--allow-paths").arg( - allow_paths - .iter() - .map(|path| path.display().to_string()) - .collect::>() - .join(","), - ); - } - let mut child = command - .spawn() - .with_context(|| format!("Failed to spawn solc at {}", path.display()))?; - - let stdin = child.stdin.as_mut().expect("should be piped"); - let serialized_input = serde_json::to_vec(&input) - .context("Failed to serialize Standard JSON input for solc")?; - stdin - .write_all(&serialized_input) - .await - .context("Failed to write Standard JSON to solc stdin")?; - let output = child - .wait_with_output() - .await - .context("Failed while waiting for solc process to finish")?; - - if !output.status.success() { - let json_in = serde_json::to_string_pretty(&input) - .context("Failed to pretty-print Standard JSON input for logging")?; - let message = String::from_utf8_lossy(&output.stderr); - tracing::error!( - status = %output.status, - message = %message, - json_input = json_in, - "Compilation using solc failed" - ); - anyhow::bail!("Compilation failed with an error: {message}"); - } - - let parsed = serde_json::from_slice::(&output.stdout) - .map_err(|e| { - anyhow::anyhow!( - "failed to parse resolc JSON output: {e}\nstderr: {}", - String::from_utf8_lossy(&output.stdout) - ) - }) - .context("Failed to parse solc standard JSON output")?; - - // Detecting if the compiler output contained errors and reporting them through logs and - // errors instead of returning the compiler output that might contain errors. - for error in parsed.errors.iter() { - if error.severity == Severity::Error { - tracing::error!(?error, ?input, "Encountered an error in the compilation"); - anyhow::bail!("Encountered an error in the compilation: {error}") + if let Some(ref base_path) = base_path { + command.arg("--base-path").arg(base_path); } - } + if !allow_paths.is_empty() { + command.arg("--allow-paths").arg( + allow_paths + .iter() + .map(|path| path.display().to_string()) + .collect::>() + .join(","), + ); + } + let mut child = command + .spawn() + .with_context(|| format!("Failed to spawn solc at {}", path.display()))?; - tracing::debug!( - output = %String::from_utf8_lossy(&output.stdout).to_string(), - "Compiled successfully" - ); + let stdin = child.stdin.as_mut().expect("should be piped"); + let serialized_input = serde_json::to_vec(&input) + .context("Failed to serialize Standard JSON input for solc")?; + stdin + .write_all(&serialized_input) + .await + .context("Failed to write Standard JSON to solc stdin")?; + let output = child + .wait_with_output() + .await + .context("Failed while waiting for solc process to finish")?; - let mut compiler_output = CompilerOutput::default(); - for (contract_path, contracts) in parsed.contracts { - let map = compiler_output - .contracts - .entry(contract_path.canonicalize().with_context(|| { - format!( - "Failed to canonicalize contract path {}", - contract_path.display() + if !output.status.success() { + let json_in = serde_json::to_string_pretty(&input) + .context("Failed to pretty-print Standard JSON input for logging")?; + let message = String::from_utf8_lossy(&output.stderr); + tracing::error!( + status = %output.status, + message = %message, + json_input = json_in, + "Compilation using solc failed" + ); + anyhow::bail!("Compilation failed with an error: {message}"); + } + + let parsed = serde_json::from_slice::(&output.stdout) + .map_err(|e| { + anyhow::anyhow!( + "failed to parse resolc JSON output: {e}\nstderr: {}", + String::from_utf8_lossy(&output.stdout) ) - })?) - .or_default(); - for (contract_name, contract_info) in contracts.into_iter() { - let source_code = contract_info - .evm - .and_then(|evm| evm.bytecode) - .map(|bytecode| match bytecode.object { - BytecodeObject::Bytecode(bytecode) => bytecode.to_string(), - BytecodeObject::Unlinked(unlinked) => unlinked, - }) - .context("Unexpected - contract compiled with solc has no source code")?; - let abi = contract_info - .abi - .context("Unexpected - contract compiled with solc as no ABI")?; - map.insert(contract_name, (source_code, abi)); - } - } + }) + .context("Failed to parse solc standard JSON output")?; - Ok(compiler_output) + // Detecting if the compiler output contained errors and reporting them through logs and + // errors instead of returning the compiler output that might contain errors. + for error in parsed.errors.iter() { + if error.severity == Severity::Error { + tracing::error!(?error, ?input, "Encountered an error in the compilation"); + anyhow::bail!("Encountered an error in the compilation: {error}") + } + } + + tracing::debug!( + output = %String::from_utf8_lossy(&output.stdout).to_string(), + "Compiled successfully" + ); + + let mut compiler_output = CompilerOutput::default(); + for (contract_path, contracts) in parsed.contracts { + let map = compiler_output + .contracts + .entry(contract_path.canonicalize().with_context(|| { + format!( + "Failed to canonicalize contract path {}", + contract_path.display() + ) + })?) + .or_default(); + for (contract_name, contract_info) in contracts.into_iter() { + let source_code = contract_info + .evm + .and_then(|evm| evm.bytecode) + .map(|bytecode| match bytecode.object { + BytecodeObject::Bytecode(bytecode) => bytecode.to_string(), + BytecodeObject::Unlinked(unlinked) => unlinked, + }) + .context("Unexpected - contract compiled with solc has no source code")?; + let abi = contract_info + .abi + .context("Unexpected - contract compiled with solc as no ABI")?; + map.insert(contract_name, (source_code, abi)); + } + } + + Ok(compiler_output) + }) } fn supports_mode( @@ -308,6 +284,6 @@ impl SolidityCompiler for Solc { impl Solc { fn compiler_supports_yul(&self) -> bool { const SOLC_VERSION_SUPPORTING_VIA_YUL_IR: Version = Version::new(0, 8, 13); - DynSolidityCompiler::version(self) >= &SOLC_VERSION_SUPPORTING_VIA_YUL_IR + SolidityCompiler::version(self) >= &SOLC_VERSION_SUPPORTING_VIA_YUL_IR } } diff --git a/crates/core/src/cached_compiler.rs b/crates/core/src/cached_compiler.rs index b382ec9..c10f7e1 100644 --- a/crates/core/src/cached_compiler.rs +++ b/crates/core/src/cached_compiler.rs @@ -10,8 +10,8 @@ use std::{ use futures::FutureExt; use revive_dt_common::{iterators::FilesWithExtensionIterator, types::CompilerIdentifier}; -use revive_dt_compiler::{Compiler, CompilerOutput, DynSolidityCompiler, Mode}; -use revive_dt_core::DynPlatform; +use revive_dt_compiler::{Compiler, CompilerOutput, Mode, SolidityCompiler}; +use revive_dt_core::Platform; use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata}; use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address}; @@ -65,8 +65,8 @@ impl<'a> CachedCompiler<'a> { metadata_file_path: &'a Path, mode: Cow<'a, Mode>, deployed_libraries: Option<&HashMap>, - compiler: &dyn DynSolidityCompiler, - platform: &dyn DynPlatform, + compiler: &dyn SolidityCompiler, + platform: &dyn Platform, reporter: &ExecutionSpecificReporter, ) -> Result { let cache_key = CacheKey { @@ -183,7 +183,7 @@ async fn compile_contracts( mut files_to_compile: impl Iterator, mode: &Mode, deployed_libraries: Option<&HashMap>, - compiler: &dyn DynSolidityCompiler, + compiler: &dyn SolidityCompiler, reporter: &ExecutionSpecificReporter, ) -> Result { let all_sources_in_dir = FilesWithExtensionIterator::new(metadata_directory.as_ref()) @@ -217,7 +217,7 @@ async fn compile_contracts( }); let input = compilation.input().clone(); - let output = compilation.dyn_try_build(compiler).await; + let output = compilation.try_build(compiler).await; match (output.as_ref(), deployed_libraries.is_some()) { (Ok(output), true) => { diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 76d808c..8bc93e2 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -11,61 +11,17 @@ use std::{ use alloy::genesis::Genesis; use anyhow::Context as _; use revive_dt_common::types::*; -use revive_dt_compiler::{ - DynSolidityCompiler, SolidityCompiler, - revive_resolc::{self, Resolc}, - solc::{self, Solc}, -}; +use revive_dt_compiler::{SolidityCompiler, revive_resolc::Resolc, solc::Solc}; use revive_dt_config::*; -use revive_dt_format::traits::ResolverApi; -use revive_dt_node::{ - Node, - geth::{self, GethNode}, - substrate::SubstrateNode, -}; +use revive_dt_node::{Node, geth::GethNode, substrate::SubstrateNode}; use revive_dt_node_interaction::EthereumNode; use tracing::info; pub mod driver; -/// One platform can be tested differentially against another. -/// -/// For this we need a blockchain node implementation and a compiler. -pub trait Platform { - type Blockchain: EthereumNode + Node + ResolverApi; - type Compiler: SolidityCompiler; - - /// Returns the matching [TestingPlatform] of the [revive_dt_config::Arguments]. - fn config_id() -> &'static TestingPlatform; -} - -#[derive(Default)] -pub struct Geth; - -impl Platform for Geth { - type Blockchain = geth::GethNode; - type Compiler = solc::Solc; - - fn config_id() -> &'static TestingPlatform { - &TestingPlatform::Geth - } -} - -#[derive(Default)] -pub struct Kitchensink; - -impl Platform for Kitchensink { - type Blockchain = SubstrateNode; - type Compiler = revive_resolc::Resolc; - - fn config_id() -> &'static TestingPlatform { - &TestingPlatform::Kitchensink - } -} - /// A trait that describes the interface for the platforms that are supported by the tool. #[allow(clippy::type_complexity)] -pub trait DynPlatform { +pub trait Platform { /// Returns the identifier of this platform. This is a combination of the node and the compiler /// used. fn platform_identifier(&self) -> PlatformIdentifier; @@ -100,13 +56,13 @@ pub trait DynPlatform { &self, context: Context, version: Option, - ) -> Pin>>>>; + ) -> Pin>>>>; } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] pub struct GethEvmSolcPlatform; -impl DynPlatform for GethEvmSolcPlatform { +impl Platform for GethEvmSolcPlatform { fn platform_identifier(&self) -> PlatformIdentifier { PlatformIdentifier::GethEvmSolc } @@ -140,10 +96,10 @@ impl DynPlatform for GethEvmSolcPlatform { &self, context: Context, version: Option, - ) -> Pin>>>> { + ) -> Pin>>>> { Box::pin(async move { let compiler = Solc::new(context, version).await; - compiler.map(|compiler| Box::new(compiler) as Box) + compiler.map(|compiler| Box::new(compiler) as Box) }) } } @@ -151,7 +107,7 @@ impl DynPlatform for GethEvmSolcPlatform { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] pub struct KitchensinkPolkavmResolcPlatform; -impl DynPlatform for KitchensinkPolkavmResolcPlatform { +impl Platform for KitchensinkPolkavmResolcPlatform { fn platform_identifier(&self) -> PlatformIdentifier { PlatformIdentifier::KitchensinkPolkavmResolc } @@ -192,10 +148,10 @@ impl DynPlatform for KitchensinkPolkavmResolcPlatform { &self, context: Context, version: Option, - ) -> Pin>>>> { + ) -> Pin>>>> { Box::pin(async move { let compiler = Resolc::new(context, version).await; - compiler.map(|compiler| Box::new(compiler) as Box) + compiler.map(|compiler| Box::new(compiler) as Box) }) } } @@ -203,7 +159,7 @@ impl DynPlatform for KitchensinkPolkavmResolcPlatform { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] pub struct KitchensinkRevmSolcPlatform; -impl DynPlatform for KitchensinkRevmSolcPlatform { +impl Platform for KitchensinkRevmSolcPlatform { fn platform_identifier(&self) -> PlatformIdentifier { PlatformIdentifier::KitchensinkRevmSolc } @@ -244,10 +200,10 @@ impl DynPlatform for KitchensinkRevmSolcPlatform { &self, context: Context, version: Option, - ) -> Pin>>>> { + ) -> Pin>>>> { Box::pin(async move { let compiler = Solc::new(context, version).await; - compiler.map(|compiler| Box::new(compiler) as Box) + compiler.map(|compiler| Box::new(compiler) as Box) }) } } @@ -255,7 +211,7 @@ impl DynPlatform for KitchensinkRevmSolcPlatform { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] pub struct ReviveDevNodePolkavmResolcPlatform; -impl DynPlatform for ReviveDevNodePolkavmResolcPlatform { +impl Platform for ReviveDevNodePolkavmResolcPlatform { fn platform_identifier(&self) -> PlatformIdentifier { PlatformIdentifier::ReviveDevNodePolkavmResolc } @@ -296,10 +252,10 @@ impl DynPlatform for ReviveDevNodePolkavmResolcPlatform { &self, context: Context, version: Option, - ) -> Pin>>>> { + ) -> Pin>>>> { Box::pin(async move { let compiler = Resolc::new(context, version).await; - compiler.map(|compiler| Box::new(compiler) as Box) + compiler.map(|compiler| Box::new(compiler) as Box) }) } } @@ -307,7 +263,7 @@ impl DynPlatform for ReviveDevNodePolkavmResolcPlatform { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] pub struct ReviveDevNodeRevmSolcPlatform; -impl DynPlatform for ReviveDevNodeRevmSolcPlatform { +impl Platform for ReviveDevNodeRevmSolcPlatform { fn platform_identifier(&self) -> PlatformIdentifier { PlatformIdentifier::ReviveDevNodeRevmSolc } @@ -348,15 +304,15 @@ impl DynPlatform for ReviveDevNodeRevmSolcPlatform { &self, context: Context, version: Option, - ) -> Pin>>>> { + ) -> Pin>>>> { Box::pin(async move { let compiler = Solc::new(context, version).await; - compiler.map(|compiler| Box::new(compiler) as Box) + compiler.map(|compiler| Box::new(compiler) as Box) }) } } -impl From for Box { +impl From for Box { fn from(value: PlatformIdentifier) -> Self { match value { PlatformIdentifier::GethEvmSolc => Box::new(GethEvmSolcPlatform) as Box<_>, @@ -376,21 +332,21 @@ impl From for Box { } } -impl From for &dyn DynPlatform { +impl From for &dyn Platform { fn from(value: PlatformIdentifier) -> Self { match value { - PlatformIdentifier::GethEvmSolc => &GethEvmSolcPlatform as &dyn DynPlatform, + PlatformIdentifier::GethEvmSolc => &GethEvmSolcPlatform as &dyn Platform, PlatformIdentifier::KitchensinkPolkavmResolc => { - &KitchensinkPolkavmResolcPlatform as &dyn DynPlatform + &KitchensinkPolkavmResolcPlatform as &dyn Platform } PlatformIdentifier::KitchensinkRevmSolc => { - &KitchensinkRevmSolcPlatform as &dyn DynPlatform + &KitchensinkRevmSolcPlatform as &dyn Platform } PlatformIdentifier::ReviveDevNodePolkavmResolc => { - &ReviveDevNodePolkavmResolcPlatform as &dyn DynPlatform + &ReviveDevNodePolkavmResolcPlatform as &dyn Platform } PlatformIdentifier::ReviveDevNodeRevmSolc => { - &ReviveDevNodeRevmSolcPlatform as &dyn DynPlatform + &ReviveDevNodeRevmSolcPlatform as &dyn Platform } } } diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index 583ea0a..5739c35 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -30,10 +30,10 @@ use tracing::{debug, error, info, info_span, instrument}; use tracing_subscriber::{EnvFilter, FmtSubscriber}; use revive_dt_common::{iterators::EitherIter, types::Mode}; -use revive_dt_compiler::DynSolidityCompiler; +use revive_dt_compiler::SolidityCompiler; use revive_dt_config::{Context, *}; use revive_dt_core::{ - DynPlatform, + Platform, driver::{CaseDriver, CaseState}, }; use revive_dt_format::{ @@ -138,9 +138,9 @@ async fn run_driver( metadata_files: &[MetadataFile], reporter: Reporter, report_aggregator_task: impl Future>, - platforms: Vec<&dyn DynPlatform>, + platforms: Vec<&dyn Platform>, ) -> anyhow::Result<()> { - let mut nodes = Vec::<(&dyn DynPlatform, NodePool)>::new(); + let mut nodes = Vec::<(&dyn Platform, NodePool)>::new(); for platform in platforms.into_iter() { let pool = NodePool::new(Context::ExecuteTests(Box::new(context.clone())), platform) .inspect_err(|err| { @@ -175,7 +175,7 @@ async fn run_driver( async fn tests_stream<'a>( args: &TestExecutionContext, metadata_files: impl IntoIterator + Clone, - nodes: &'a [(&dyn DynPlatform, NodePool)], + nodes: &'a [(&dyn Platform, NodePool)], reporter: Reporter, ) -> impl Stream> { let tests = metadata_files @@ -619,7 +619,7 @@ async fn execute_corpus( .copied() .collect::>() .into_iter() - .map(Into::<&dyn DynPlatform>::into) + .map(Into::<&dyn Platform>::into) .collect::>(); run_driver(context, tests, reporter, report_aggregator_task, platforms).await?; @@ -636,9 +636,9 @@ struct Test<'a> { case_idx: CaseIdx, case: &'a Case, platforms: Vec<( - &'a dyn DynPlatform, + &'a dyn Platform, &'a dyn EthereumNode, - Box, + Box, ExecutionSpecificReporter, )>, reporter: TestSpecificReporter, diff --git a/crates/core/src/pool.rs b/crates/core/src/pool.rs index 31448c7..06fb2be 100644 --- a/crates/core/src/pool.rs +++ b/crates/core/src/pool.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use anyhow::Context as _; use revive_dt_config::*; -use revive_dt_core::DynPlatform; +use revive_dt_core::Platform; use revive_dt_node_interaction::EthereumNode; /// The node pool starts one or more [Node] which then can be accessed @@ -16,7 +16,7 @@ pub struct NodePool { impl NodePool { /// Create a new Pool. This will start as many nodes as there are workers in `config`. - pub fn new(context: Context, platform: &dyn DynPlatform) -> anyhow::Result { + pub fn new(context: Context, platform: &dyn Platform) -> anyhow::Result { let concurrency_configuration = AsRef::::as_ref(&context); let nodes = concurrency_configuration.number_of_nodes; diff --git a/crates/node/src/geth.rs b/crates/node/src/geth.rs index a942ddf..cf58755 100644 --- a/crates/node/src/geth.rs +++ b/crates/node/src/geth.rs @@ -647,158 +647,6 @@ impl, P: Provider> ResolverApi for GethNodeResol } } -// TODO: Remove -impl ResolverApi for GethNode { - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn chain_id( - &self, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_chain_id() - .await - .map_err(Into::into) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn transaction_gas_price( - &self, - tx_hash: TxHash, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_transaction_receipt(tx_hash) - .await? - .context("Failed to get the transaction receipt") - .map(|receipt| receipt.effective_gas_price) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn block_gas_limit( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_block_by_number(number) - .await - .context("Failed to get the geth block")? - .context("Failed to get the Geth block, perhaps there are no blocks?") - .map(|block| block.header.gas_limit as _) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn block_coinbase( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_block_by_number(number) - .await - .context("Failed to get the geth block")? - .context("Failed to get the Geth block, perhaps there are no blocks?") - .map(|block| block.header.beneficiary) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn block_difficulty( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_block_by_number(number) - .await - .context("Failed to get the geth block")? - .context("Failed to get the Geth block, perhaps there are no blocks?") - .map(|block| U256::from_be_bytes(block.header.mix_hash.0)) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn block_base_fee( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_block_by_number(number) - .await - .context("Failed to get the geth block")? - .context("Failed to get the Geth block, perhaps there are no blocks?") - .and_then(|block| { - block - .header - .base_fee_per_gas - .context("Failed to get the base fee per gas") - }) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn block_hash( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_block_by_number(number) - .await - .context("Failed to get the geth block")? - .context("Failed to get the Geth block, perhaps there are no blocks?") - .map(|block| block.header.hash) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn block_timestamp( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_block_by_number(number) - .await - .context("Failed to get the geth block")? - .context("Failed to get the Geth block, perhaps there are no blocks?") - .map(|block| block.header.timestamp) - }) - } - - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] - fn last_block_number(&self) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Geth provider")? - .get_block_number() - .await - .map_err(Into::into) - }) - } -} - impl Node for GethNode { #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] fn shutdown(&mut self) -> anyhow::Result<()> { @@ -884,7 +732,7 @@ mod tests { let (_context, node) = new_node(); // Act - let chain_id = node.chain_id().await; + let chain_id = node.resolver().await.unwrap().chain_id().await; // Assert let chain_id = chain_id.expect("Failed to get the chain id"); @@ -897,7 +745,12 @@ mod tests { let (_context, node) = new_node(); // Act - let gas_limit = node.block_gas_limit(BlockNumberOrTag::Latest).await; + let gas_limit = node + .resolver() + .await + .unwrap() + .block_gas_limit(BlockNumberOrTag::Latest) + .await; // Assert let gas_limit = gas_limit.expect("Failed to get the gas limit"); @@ -910,7 +763,12 @@ mod tests { let (_context, node) = new_node(); // Act - let coinbase = node.block_coinbase(BlockNumberOrTag::Latest).await; + let coinbase = node + .resolver() + .await + .unwrap() + .block_coinbase(BlockNumberOrTag::Latest) + .await; // Assert let coinbase = coinbase.expect("Failed to get the coinbase"); @@ -923,7 +781,12 @@ mod tests { let (_context, node) = new_node(); // Act - let block_difficulty = node.block_difficulty(BlockNumberOrTag::Latest).await; + let block_difficulty = node + .resolver() + .await + .unwrap() + .block_difficulty(BlockNumberOrTag::Latest) + .await; // Assert let block_difficulty = block_difficulty.expect("Failed to get the block difficulty"); @@ -936,7 +799,12 @@ mod tests { let (_context, node) = new_node(); // Act - let block_hash = node.block_hash(BlockNumberOrTag::Latest).await; + let block_hash = node + .resolver() + .await + .unwrap() + .block_hash(BlockNumberOrTag::Latest) + .await; // Assert let _ = block_hash.expect("Failed to get the block hash"); @@ -948,7 +816,12 @@ mod tests { let (_context, node) = new_node(); // Act - let block_timestamp = node.block_timestamp(BlockNumberOrTag::Latest).await; + let block_timestamp = node + .resolver() + .await + .unwrap() + .block_timestamp(BlockNumberOrTag::Latest) + .await; // Assert let _ = block_timestamp.expect("Failed to get the block timestamp"); @@ -960,7 +833,7 @@ mod tests { let (_context, node) = new_node(); // Act - let block_number = node.last_block_number().await; + let block_number = node.resolver().await.unwrap().last_block_number().await; // Assert let block_number = block_number.expect("Failed to get the block number"); diff --git a/crates/node/src/substrate.rs b/crates/node/src/substrate.rs index 22a996f..1cd2cb3 100644 --- a/crates/node/src/substrate.rs +++ b/crates/node/src/substrate.rs @@ -671,149 +671,6 @@ impl, P: Provider> ResolverApi } } -// TODO: Remove -impl ResolverApi for SubstrateNode { - fn chain_id( - &self, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_chain_id() - .await - .map_err(Into::into) - }) - } - - fn transaction_gas_price( - &self, - tx_hash: TxHash, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_transaction_receipt(tx_hash) - .await? - .context("Failed to get the transaction receipt") - .map(|receipt| receipt.effective_gas_price) - }) - } - - fn block_gas_limit( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_block_by_number(number) - .await - .context("Failed to get the kitchensink block")? - .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") - .map(|block| block.header.gas_limit as _) - }) - } - - fn block_coinbase( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_block_by_number(number) - .await - .context("Failed to get the kitchensink block")? - .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") - .map(|block| block.header.beneficiary) - }) - } - - fn block_difficulty( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_block_by_number(number) - .await - .context("Failed to get the kitchensink block")? - .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") - .map(|block| U256::from_be_bytes(block.header.mix_hash.0)) - }) - } - - fn block_base_fee( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_block_by_number(number) - .await - .context("Failed to get the kitchensink block")? - .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") - .and_then(|block| { - block - .header - .base_fee_per_gas - .context("Failed to get the base fee per gas") - }) - }) - } - - fn block_hash( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_block_by_number(number) - .await - .context("Failed to get the kitchensink block")? - .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") - .map(|block| block.header.hash) - }) - } - - fn block_timestamp( - &self, - number: BlockNumberOrTag, - ) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_block_by_number(number) - .await - .context("Failed to get the kitchensink block")? - .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") - .map(|block| block.header.timestamp) - }) - } - - fn last_block_number(&self) -> Pin> + '_>> { - Box::pin(async move { - self.provider() - .await - .context("Failed to get the Kitchensink provider")? - .get_block_number() - .await - .map_err(Into::into) - }) - } -} - impl Node for SubstrateNode { fn shutdown(&mut self) -> anyhow::Result<()> { // Terminate the processes in a graceful manner to allow for the output to be flushed. @@ -1544,7 +1401,7 @@ mod tests { let node = shared_node(); // Act - let chain_id = node.chain_id().await; + let chain_id = node.resolver().await.unwrap().chain_id().await; // Assert let chain_id = chain_id.expect("Failed to get the chain id"); @@ -1557,7 +1414,12 @@ mod tests { let node = shared_node(); // Act - let gas_limit = node.block_gas_limit(BlockNumberOrTag::Latest).await; + let gas_limit = node + .resolver() + .await + .unwrap() + .block_gas_limit(BlockNumberOrTag::Latest) + .await; // Assert let _ = gas_limit.expect("Failed to get the gas limit"); @@ -1569,7 +1431,12 @@ mod tests { let node = shared_node(); // Act - let coinbase = node.block_coinbase(BlockNumberOrTag::Latest).await; + let coinbase = node + .resolver() + .await + .unwrap() + .block_coinbase(BlockNumberOrTag::Latest) + .await; // Assert let _ = coinbase.expect("Failed to get the coinbase"); @@ -1581,7 +1448,12 @@ mod tests { let node = shared_node(); // Act - let block_difficulty = node.block_difficulty(BlockNumberOrTag::Latest).await; + let block_difficulty = node + .resolver() + .await + .unwrap() + .block_difficulty(BlockNumberOrTag::Latest) + .await; // Assert let _ = block_difficulty.expect("Failed to get the block difficulty"); @@ -1593,7 +1465,12 @@ mod tests { let node = shared_node(); // Act - let block_hash = node.block_hash(BlockNumberOrTag::Latest).await; + let block_hash = node + .resolver() + .await + .unwrap() + .block_hash(BlockNumberOrTag::Latest) + .await; // Assert let _ = block_hash.expect("Failed to get the block hash"); @@ -1605,7 +1482,12 @@ mod tests { let node = shared_node(); // Act - let block_timestamp = node.block_timestamp(BlockNumberOrTag::Latest).await; + let block_timestamp = node + .resolver() + .await + .unwrap() + .block_timestamp(BlockNumberOrTag::Latest) + .await; // Assert let _ = block_timestamp.expect("Failed to get the block timestamp"); @@ -1617,7 +1499,7 @@ mod tests { let node = shared_node(); // Act - let block_number = node.last_block_number().await; + let block_number = node.resolver().await.unwrap().last_block_number().await; // Assert let _ = block_number.expect("Failed to get the block number");