This commit is contained in:
pgherveou
2025-10-08 08:22:26 +02:00
parent 6e64f678ee
commit 765569a8b6
39 changed files with 662 additions and 1178 deletions
+2 -1
View File
@@ -13,4 +13,5 @@ resolc-compiler-tests
workdir workdir
!/schema.json !/schema.json
!/dev-genesis.json !/dev-genesis.json
geth-dev/
+6 -7
View File
@@ -1,9 +1,11 @@
//! This module implements a cached file system allowing for results to be stored in-memory rather //! This module implements a cached file system allowing for results to be stored in-memory rather
//! rather being queried from the file system again. //! rather being queried from the file system again.
use std::fs; use std::{
use std::io::{Error, Result}; fs,
use std::path::{Path, PathBuf}; io::{Error, Result},
path::{Path, PathBuf},
};
use moka::sync::Cache; use moka::sync::Cache;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@@ -25,10 +27,7 @@ pub fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
pub fn read_to_string(path: impl AsRef<Path>) -> Result<String> { pub fn read_to_string(path: impl AsRef<Path>) -> Result<String> {
let content = read(path)?; let content = read(path)?;
String::from_utf8(content).map_err(|_| { String::from_utf8(content).map_err(|_| {
Error::new( Error::new(std::io::ErrorKind::InvalidData, "The contents of the file are not valid UTF8")
std::io::ErrorKind::InvalidData,
"The contents of the file are not valid UTF8",
)
}) })
} }
+1 -4
View File
@@ -12,10 +12,7 @@ pub fn clear_directory(path: impl AsRef<Path>) -> Result<()> {
.with_context(|| format!("Failed to read directory: {}", path.as_ref().display()))? .with_context(|| format!("Failed to read directory: {}", path.as_ref().display()))?
{ {
let entry = entry.with_context(|| { let entry = entry.with_context(|| {
format!( format!("Failed to read an entry in directory: {}", path.as_ref().display())
"Failed to read an entry in directory: {}",
path.as_ref().display()
)
})?; })?;
let entry_path = entry.path(); let entry_path = entry.path();
+2 -6
View File
@@ -1,5 +1,4 @@
use std::ops::ControlFlow; use std::{ops::ControlFlow, time::Duration};
use std::time::Duration;
use anyhow::{Context as _, Result, anyhow}; use anyhow::{Context as _, Result, anyhow};
@@ -38,10 +37,7 @@ where
)); ));
} }
match future() match future().await.context("Polled future returned an error during polling loop")? {
.await
.context("Polled future returned an error during polling loop")?
{
ControlFlow::Continue(()) => { ControlFlow::Continue(()) => {
let next_wait_duration = match polling_wait_behavior { let next_wait_duration = match polling_wait_behavior {
PollingWaitBehavior::Constant(duration) => duration, PollingWaitBehavior::Constant(duration) => duration,
+2 -6
View File
@@ -1,9 +1,7 @@
use crate::types::VersionOrRequirement; use crate::types::VersionOrRequirement;
use semver::Version; use semver::Version;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Display; use std::{fmt::Display, str::FromStr, sync::LazyLock};
use std::str::FromStr;
use std::sync::LazyLock;
/// This represents a mode that a given test should be run with, if possible. /// This represents a mode that a given test should be run with, if possible.
/// ///
@@ -78,9 +76,7 @@ impl FromStr for ModePipeline {
// Don't go via Yul IR // Don't go via Yul IR
"E" => Ok(ModePipeline::ViaEVMAssembly), "E" => Ok(ModePipeline::ViaEVMAssembly),
// Anything else that we see isn't a mode at all // Anything else that we see isn't a mode at all
_ => Err(anyhow::anyhow!( _ => Err(anyhow::anyhow!("Unsupported pipeline '{s}': expected 'Y' or 'E'")),
"Unsupported pipeline '{s}': expected 'Y' or 'E'"
)),
} }
} }
} }
@@ -1,5 +1,4 @@
use alloy::primitives::U256; use alloy::{primitives::U256, signers::local::PrivateKeySigner};
use alloy::signers::local::PrivateKeySigner;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
/// This is a sequential private key allocator. When instantiated, it allocated private keys in /// This is a sequential private key allocator. When instantiated, it allocated private keys in
+1 -2
View File
@@ -10,8 +10,7 @@ use std::{
pin::Pin, pin::Pin,
}; };
use alloy::json_abi::JsonAbi; use alloy::{json_abi::JsonAbi, primitives::Address};
use alloy::primitives::Address;
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use semver::Version; use semver::Version;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
+1 -3
View File
@@ -141,9 +141,7 @@ impl SolidityCompiler for Resolc {
output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()), output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()),
via_ir: Some(true), via_ir: Some(true),
optimizer: SolcStandardJsonInputSettingsOptimizer::new( optimizer: SolcStandardJsonInputSettingsOptimizer::new(
optimization optimization.unwrap_or(ModeOptimizerSetting::M0).optimizations_enabled(),
.unwrap_or(ModeOptimizerSetting::M0)
.optimizations_enabled(),
None, None,
&Version::new(0, 0, 0), &Version::new(0, 0, 0),
false, false,
+3 -9
View File
@@ -21,8 +21,7 @@ use foundry_compilers_artifacts::{
output_selection::{ output_selection::{
BytecodeOutputSelection, ContractOutputSelection, EvmOutputSelection, OutputSelection, BytecodeOutputSelection, ContractOutputSelection, EvmOutputSelection, OutputSelection,
}, },
solc::CompilerOutput as SolcOutput, solc::{CompilerOutput as SolcOutput, *},
solc::*,
}; };
use semver::Version; use semver::Version;
use tokio::{io::AsyncWriteExt, process::Command as AsyncCommand}; use tokio::{io::AsyncWriteExt, process::Command as AsyncCommand};
@@ -57,9 +56,7 @@ impl Solc {
// resolution for us. Therefore, even if the download didn't proceed, this function will // resolution for us. Therefore, even if the download didn't proceed, this function will
// resolve the version requirement into a canonical version of the compiler. It's then up // resolve the version requirement into a canonical version of the compiler. It's then up
// to us to either use the provided path or not. // to us to either use the provided path or not.
let version = version let version = version.into().unwrap_or_else(|| solc_configuration.version.clone().into());
.into()
.unwrap_or_else(|| solc_configuration.version.clone().into());
let (version, path) = let (version, path) =
download_solc(working_directory_configuration.as_path(), version, false) download_solc(working_directory_configuration.as_path(), version, false)
.await .await
@@ -253,10 +250,7 @@ impl SolidityCompiler for Solc {
let map = compiler_output let map = compiler_output
.contracts .contracts
.entry(contract_path.canonicalize().with_context(|| { .entry(contract_path.canonicalize().with_context(|| {
format!( format!("Failed to canonicalize contract path {}", contract_path.display())
"Failed to canonicalize contract path {}",
contract_path.display()
)
})?) })?)
.or_default(); .or_default();
for (contract_name, contract_info) in contracts.into_iter() { for (contract_name, contract_info) in contracts.into_iter() {
+8 -22
View File
@@ -9,9 +9,8 @@ use semver::Version;
async fn contracts_can_be_compiled_with_solc() { async fn contracts_can_be_compiled_with_solc() {
// Arrange // Arrange
let args = TestExecutionContext::default(); let args = TestExecutionContext::default();
let solc = Solc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30))) let solc =
.await Solc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30))).await.unwrap();
.unwrap();
// Act // Act
let output = Compiler::new() let output = Compiler::new()
@@ -28,18 +27,12 @@ async fn contracts_can_be_compiled_with_solc() {
let main_file_contracts = output let main_file_contracts = output
.contracts .contracts
.get( .get(&PathBuf::from("./tests/assets/array_one_element/main.sol").canonicalize().unwrap())
&PathBuf::from("./tests/assets/array_one_element/main.sol")
.canonicalize()
.unwrap(),
)
.unwrap(); .unwrap();
let callable_file_contracts = output let callable_file_contracts = output
.contracts .contracts
.get( .get(
&PathBuf::from("./tests/assets/array_one_element/callable.sol") &PathBuf::from("./tests/assets/array_one_element/callable.sol").canonicalize().unwrap(),
.canonicalize()
.unwrap(),
) )
.unwrap(); .unwrap();
assert!(main_file_contracts.contains_key("Main")); assert!(main_file_contracts.contains_key("Main"));
@@ -50,9 +43,8 @@ async fn contracts_can_be_compiled_with_solc() {
async fn contracts_can_be_compiled_with_resolc() { async fn contracts_can_be_compiled_with_resolc() {
// Arrange // Arrange
let args = TestExecutionContext::default(); let args = TestExecutionContext::default();
let resolc = Resolc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30))) let resolc =
.await Resolc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30))).await.unwrap();
.unwrap();
// Act // Act
let output = Compiler::new() let output = Compiler::new()
@@ -69,18 +61,12 @@ async fn contracts_can_be_compiled_with_resolc() {
let main_file_contracts = output let main_file_contracts = output
.contracts .contracts
.get( .get(&PathBuf::from("./tests/assets/array_one_element/main.sol").canonicalize().unwrap())
&PathBuf::from("./tests/assets/array_one_element/main.sol")
.canonicalize()
.unwrap(),
)
.unwrap(); .unwrap();
let callable_file_contracts = output let callable_file_contracts = output
.contracts .contracts
.get( .get(
&PathBuf::from("./tests/assets/array_one_element/callable.sol") &PathBuf::from("./tests/assets/array_one_element/callable.sol").canonicalize().unwrap(),
.canonicalize()
.unwrap(),
) )
.unwrap(); .unwrap();
assert!(main_file_contracts.contains_key("Main")); assert!(main_file_contracts.contains_key("Main"));
+18 -17
View File
@@ -626,11 +626,7 @@ pub struct KurtosisConfiguration {
/// ///
/// If this is not specified, then the tool assumes that it should use the kurtosis binary that's /// If this is not specified, then the tool assumes that it should use the kurtosis binary that's
/// provided in the user's $PATH. /// provided in the user's $PATH.
#[clap( #[clap(id = "kurtosis.path", long = "kurtosis.path", default_value = "kurtosis")]
id = "kurtosis.path",
long = "kurtosis.path",
default_value = "kurtosis"
)]
pub path: PathBuf, pub path: PathBuf,
} }
@@ -641,11 +637,7 @@ pub struct KitchensinkConfiguration {
/// ///
/// If this is not specified, then the tool assumes that it should use the kitchensink binary /// If this is not specified, then the tool assumes that it should use the kitchensink binary
/// that's provided in the user's $PATH. /// that's provided in the user's $PATH.
#[clap( #[clap(id = "kitchensink.path", long = "kitchensink.path", default_value = "substrate-node")]
id = "kitchensink.path",
long = "kitchensink.path",
default_value = "substrate-node"
)]
pub path: PathBuf, pub path: PathBuf,
/// The amount of time to wait upon startup before considering that the node timed out. /// The amount of time to wait upon startup before considering that the node timed out.
@@ -785,6 +777,20 @@ impl WalletConfiguration {
} }
} }
impl Default for WalletConfiguration {
fn default() -> Self {
let mut config = Self::parse_from::<[&str; 0], &str>([]);
config.additional_keys = 0;
config
// config.default_key = PrivateKeySigner::from_bytes(
// &FixedBytes::from_hex_str(
// "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
// )
// .unwrap(),
// )
}
}
fn serialize_private_key<S>(value: &PrivateKeySigner, serializer: S) -> Result<S::Ok, S::Error> fn serialize_private_key<S>(value: &PrivateKeySigner, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
@@ -823,10 +829,7 @@ impl ConcurrencyConfiguration {
pub fn concurrency_limit(&self) -> Option<usize> { pub fn concurrency_limit(&self) -> Option<usize> {
match self.ignore_concurrency_limit { match self.ignore_concurrency_limit {
true => None, true => None,
false => Some( false => Some(self.number_concurrent_tasks.unwrap_or(20 * self.number_of_nodes)),
self.number_concurrent_tasks
.unwrap_or(20 * self.number_of_nodes),
),
} }
} }
} }
@@ -917,9 +920,7 @@ impl Serialize for WorkingDirectoryConfiguration {
} }
fn parse_duration(s: &str) -> anyhow::Result<Duration> { fn parse_duration(s: &str) -> anyhow::Result<Duration> {
u64::from_str(s) u64::from_str(s).map(Duration::from_millis).map_err(Into::into)
.map(Duration::from_millis)
.map_err(Into::into)
} }
/// The Solidity compatible node implementation. /// The Solidity compatible node implementation.
@@ -206,9 +206,7 @@ where
"Deployed library" "Deployed library"
); );
let library_address = receipt let library_address = receipt.contract_address.expect("Failed to deploy the library");
.contract_address
.expect("Failed to deploy the library");
deployed_libraries.get_or_insert_default().insert( deployed_libraries.get_or_insert_default().insert(
library_instance.clone(), library_instance.clone(),
@@ -236,10 +234,8 @@ where
}) })
.context("Failed to compile the post-link contracts")?; .context("Failed to compile the post-link contracts")?;
self.execution_state = ExecutionState::new( self.execution_state =
compiler_output.contracts, ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default());
deployed_libraries.unwrap_or_default(),
);
Ok(()) Ok(())
} }
@@ -325,11 +321,7 @@ where
) -> Result<HashMap<ContractInstance, TransactionReceipt>> { ) -> Result<HashMap<ContractInstance, TransactionReceipt>> {
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new(); let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
for instance in step.find_all_contract_instances().into_iter() { for instance in step.find_all_contract_instances().into_iter() {
if !self if !self.execution_state.deployed_contracts.contains_key(&instance) {
.execution_state
.deployed_contracts
.contains_key(&instance)
{
instances_we_must_deploy.entry(instance).or_insert(false); instances_we_must_deploy.entry(instance).or_insert(false);
} }
} }
@@ -341,15 +333,11 @@ where
let mut receipts = HashMap::new(); let mut receipts = HashMap::new();
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() { for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
let calldata = deploy_with_constructor_arguments.then_some(&step.calldata); let calldata = deploy_with_constructor_arguments.then_some(&step.calldata);
let value = deploy_with_constructor_arguments let value = deploy_with_constructor_arguments.then_some(step.value).flatten();
.then_some(step.value)
.flatten();
let caller = { let caller = {
let context = self.default_resolution_context(); let context = self.default_resolution_context();
step.caller step.caller.resolve_address(self.resolver.as_ref(), context).await?
.resolve_address(self.resolver.as_ref(), context)
.await?
}; };
if let (_, _, Some(receipt)) = self if let (_, _, Some(receipt)) = self
.get_or_deploy_contract_instance(&instance, caller, calldata, value) .get_or_deploy_contract_instance(&instance, caller, calldata, value)
@@ -424,18 +412,13 @@ where
}; };
// Handling the return data variable assignments. // Handling the return data variable assignments.
for (variable_name, output_word) in assignments.return_data.iter().zip( for (variable_name, output_word) in assignments
tracing_result .return_data
.output .iter()
.as_ref() .zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32))
.unwrap_or_default() {
.to_vec()
.chunks(32),
) {
let value = U256::from_be_slice(output_word); let value = U256::from_be_slice(output_word);
self.execution_state self.execution_state.variables.insert(variable_name.clone(), value);
.variables
.insert(variable_name.clone(), value);
tracing::info!( tracing::info!(
variable_name, variable_name,
variable_value = hex::encode(value.to_be_bytes::<32>()), variable_value = hex::encode(value.to_be_bytes::<32>()),
@@ -508,9 +491,8 @@ where
}) })
.context("Failed to send message on the watcher's tx")?; .context("Failed to send message on the watcher's tx")?;
let res = futures::future::try_join_all(tasks) let res =
.await futures::future::try_join_all(tasks).await.context("Repetition execution failed")?;
.context("Repetition execution failed")?;
Ok(res.into_iter().sum()) Ok(res.into_iter().sum())
} }
@@ -533,9 +515,7 @@ where
let account = private_key.address(); let account = private_key.address();
let variable = U256::from_be_slice(account.0.as_slice()); let variable = U256::from_be_slice(account.0.as_slice());
self.execution_state self.execution_state.variables.insert(variable_name.to_string(), variable);
.variables
.insert(variable_name.to_string(), variable);
Ok(1) Ok(1)
} }
@@ -560,10 +540,8 @@ where
calldata: Option<&Calldata>, calldata: Option<&Calldata>,
value: Option<EtherValue>, value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> { ) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
if let Some((_, address, abi)) = self if let Some((_, address, abi)) =
.execution_state self.execution_state.deployed_contracts.get(contract_instance)
.deployed_contracts
.get(contract_instance)
{ {
info!( info!(
@@ -606,16 +584,9 @@ where
let Some(ContractPathAndIdent { let Some(ContractPathAndIdent {
contract_source_path, contract_source_path,
contract_ident, contract_ident,
}) = self }) = self.test_definition.metadata.contract_sources()?.remove(contract_instance)
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else { else {
anyhow::bail!( anyhow::bail!("Contract source not found for instance {:?}", contract_instance)
"Contract source not found for instance {:?}",
contract_instance
)
}; };
let Some((code, abi)) = self let Some((code, abi)) = self
@@ -625,10 +596,7 @@ where
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref())) .and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
.cloned() .cloned()
else { else {
anyhow::bail!( anyhow::bail!("Failed to find information for contract {:?}", contract_instance)
"Failed to find information for contract {:?}",
contract_instance
)
}; };
let mut code = match alloy::hex::decode(&code) { let mut code = match alloy::hex::decode(&code) {
@@ -680,10 +648,9 @@ where
.reporter .reporter
.report_contract_deployed_event(contract_instance.clone(), address)?; .report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state.deployed_contracts.insert( self.execution_state
contract_instance.clone(), .deployed_contracts
(contract_ident, address, abi.clone()), .insert(contract_instance.clone(), (contract_ident, address, abi.clone()));
);
Ok((address, abi, receipt)) Ok((address, abi, receipt))
} }
@@ -696,9 +663,7 @@ where
match step_address { match step_address {
StepAddress::Address(address) => Ok(*address), StepAddress::Address(address) => Ok(*address),
StepAddress::ResolvableAddress(resolvable) => { StepAddress::ResolvableAddress(resolvable) => {
let Some(instance) = resolvable let Some(instance) = resolvable.strip_suffix(".address").map(ContractInstance::new)
.strip_suffix(".address")
.map(ContractInstance::new)
else { else {
bail!("Not an address variable"); bail!("Not an address variable");
}; };
@@ -736,15 +701,15 @@ where
transaction: TransactionRequest, transaction: TransactionRequest,
) -> anyhow::Result<TransactionReceipt> { ) -> anyhow::Result<TransactionReceipt> {
let node = self.platform_information.node; let node = self.platform_information.node;
let transaction_hash = node let transaction_hash =
.submit_transaction(transaction) node.submit_transaction(transaction).await.context("Failed to submit transaction")?;
.await
.context("Failed to submit transaction")?;
Span::current().record("transaction_hash", display(transaction_hash)); Span::current().record("transaction_hash", display(transaction_hash));
info!("Submitted transaction"); info!("Submitted transaction");
self.watcher_tx self.watcher_tx
.send(WatcherEvent::SubmittedTransaction { transaction_hash }) .send(WatcherEvent::SubmittedTransaction {
transaction_hash,
})
.context("Failed to send the transaction hash to the watcher")?; .context("Failed to send the transaction hash to the watcher")?;
info!("Starting to poll for transaction receipt"); info!("Starting to poll for transaction receipt");
@@ -44,12 +44,8 @@ pub async fn handle_differential_benchmarks(
info!(len = metadata_files.len(), "Discovered metadata files"); info!(len = metadata_files.len(), "Discovered metadata files");
// Discover the list of platforms that the tests should run on based on the context. // Discover the list of platforms that the tests should run on based on the context.
let platforms = context let platforms =
.platforms context.platforms.iter().copied().map(Into::<&dyn Platform>::into).collect::<Vec<_>>();
.iter()
.copied()
.map(Into::<&dyn Platform>::into)
.collect::<Vec<_>>();
// Starting the nodes of the various platforms specified in the context. Note that we use the // Starting the nodes of the various platforms specified in the context. Note that we use the
// node pool since it contains all of the code needed to spawn nodes from A to Z and therefore // node pool since it contains all of the code needed to spawn nodes from A to Z and therefore
@@ -96,13 +92,8 @@ pub async fn handle_differential_benchmarks(
// Creating the objects that will be shared between the various runs. The cached compiler is the // Creating the objects that will be shared between the various runs. The cached compiler is the
// only one at the current moment of time that's safe to share between runs. // only one at the current moment of time that's safe to share between runs.
let cached_compiler = CachedCompiler::new( let cached_compiler = CachedCompiler::new(
context context.working_directory.as_path().join("compilation_cache"),
.working_directory context.compilation_configuration.invalidate_compilation_cache,
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
) )
.await .await
.map(Arc::new) .map(Arc::new)
@@ -161,9 +152,7 @@ pub async fn handle_differential_benchmarks(
watcher.run(), watcher.run(),
driver.execute_all().inspect(|_| { driver.execute_all().inspect(|_| {
info!("All transactions submitted - driver completed execution"); info!("All transactions submitted - driver completed execution");
watcher_tx watcher_tx.send(WatcherEvent::AllTransactionsSubmitted).unwrap()
.send(WatcherEvent::AllTransactionsSubmitted)
.unwrap()
}), }),
) )
.await .await
@@ -80,12 +80,13 @@ impl Watcher {
// Subsequent repetition starts are ignored since certain workloads can // Subsequent repetition starts are ignored since certain workloads can
// contain nested repetitions and therefore there's no use in doing any // contain nested repetitions and therefore there's no use in doing any
// action if the repetitions are nested. // action if the repetitions are nested.
WatcherEvent::RepetitionStartEvent { .. } => {} WatcherEvent::RepetitionStartEvent {
WatcherEvent::SubmittedTransaction { transaction_hash } => { ..
watch_for_transaction_hashes } => {}
.write() WatcherEvent::SubmittedTransaction {
.await transaction_hash,
.insert(transaction_hash); } => {
watch_for_transaction_hashes.write().await.insert(transaction_hash);
} }
WatcherEvent::AllTransactionsSubmitted => { WatcherEvent::AllTransactionsSubmitted => {
*all_transactions_submitted.write().await = true; *all_transactions_submitted.write().await = true;
@@ -151,15 +152,8 @@ impl Watcher {
use std::io::Write; use std::io::Write;
let mut stderr = std::io::stderr().lock(); let mut stderr = std::io::stderr().lock();
writeln!( writeln!(stderr, "Watcher information for {}", self.platform_identifier)?;
stderr, writeln!(stderr, "block_number,block_timestamp,mined_gas,block_gas_limit,tx_count")?;
"Watcher information for {}",
self.platform_identifier
)?;
writeln!(
stderr,
"block_number,block_timestamp,mined_gas,block_gas_limit,tx_count"
)?;
for block in mined_blocks_information { for block in mined_blocks_information {
writeln!( writeln!(
stderr, stderr,
+51 -91
View File
@@ -76,7 +76,9 @@ impl<'a> Driver<'a, StepsIterator> {
.into_iter() .into_iter()
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
Ok(Self { platform_drivers }) Ok(Self {
platform_drivers,
})
} }
async fn create_platform_driver( async fn create_platform_driver(
@@ -112,9 +114,7 @@ impl<'a> Driver<'a, StepsIterator> {
pub async fn execute_all(mut self) -> Result<usize> { pub async fn execute_all(mut self) -> Result<usize> {
let platform_drivers = std::mem::take(&mut self.platform_drivers); let platform_drivers = std::mem::take(&mut self.platform_drivers);
let results = futures::future::try_join_all( let results = futures::future::try_join_all(
platform_drivers platform_drivers.into_values().map(|driver| driver.execute_all()),
.into_values()
.map(|driver| driver.execute_all()),
) )
.await .await
.context("Failed to execute all of the steps on the driver")?; .context("Failed to execute all of the steps on the driver")?;
@@ -210,12 +210,8 @@ where
) )
}) })
.context("Failed to get the contract instances from the metadata file")?; .context("Failed to get the contract instances from the metadata file")?;
for library_instance in test_definition for library_instance in
.metadata test_definition.metadata.libraries.iter().flatten().flat_map(|(_, map)| map.values())
.libraries
.iter()
.flatten()
.flat_map(|(_, map)| map.values())
{ {
let ContractPathAndIdent { let ContractPathAndIdent {
contract_source_path: library_source_path, contract_source_path: library_source_path,
@@ -252,11 +248,8 @@ where
TransactionRequest::default().from(deployer_address), TransactionRequest::default().from(deployer_address),
code, code,
); );
let receipt = platform_information let receipt =
.node platform_information.node.execute_transaction(tx).await.inspect_err(|err| {
.execute_transaction(tx)
.await
.inspect_err(|err| {
error!( error!(
?err, ?err,
%library_instance, %library_instance,
@@ -265,9 +258,7 @@ where
) )
})?; })?;
let library_address = receipt let library_address = receipt.contract_address.expect("Failed to deploy the library");
.contract_address
.expect("Failed to deploy the library");
deployed_libraries.get_or_insert_default().insert( deployed_libraries.get_or_insert_default().insert(
library_instance.clone(), library_instance.clone(),
@@ -295,10 +286,7 @@ where
}) })
.context("Failed to compile the post-link contracts")?; .context("Failed to compile the post-link contracts")?;
Ok(ExecutionState::new( Ok(ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default()))
compiler_output.contracts,
deployed_libraries.unwrap_or_default(),
))
} }
// endregion:Constructors & Initialization // endregion:Constructors & Initialization
@@ -392,11 +380,7 @@ where
) -> Result<HashMap<ContractInstance, TransactionReceipt>> { ) -> Result<HashMap<ContractInstance, TransactionReceipt>> {
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new(); let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
for instance in step.find_all_contract_instances().into_iter() { for instance in step.find_all_contract_instances().into_iter() {
if !self if !self.execution_state.deployed_contracts.contains_key(&instance) {
.execution_state
.deployed_contracts
.contains_key(&instance)
{
instances_we_must_deploy.entry(instance).or_insert(false); instances_we_must_deploy.entry(instance).or_insert(false);
} }
} }
@@ -408,16 +392,13 @@ where
let mut receipts = HashMap::new(); let mut receipts = HashMap::new();
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() { for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
let calldata = deploy_with_constructor_arguments.then_some(&step.calldata); let calldata = deploy_with_constructor_arguments.then_some(&step.calldata);
let value = deploy_with_constructor_arguments let value = deploy_with_constructor_arguments.then_some(step.value).flatten();
.then_some(step.value)
.flatten();
let caller = { let caller = {
let context = self.default_resolution_context(); let context = self.default_resolution_context();
let resolver = self.platform_information.node.resolver().await?; let resolver = self.platform_information.node.resolver().await?;
step.caller let resolved = step.caller.resolve_address(resolver.as_ref(), context).await?;
.resolve_address(resolver.as_ref(), context) self.platform_information.node.resolve_signer_or_default(resolved)
.await?
}; };
if let (_, _, Some(receipt)) = self if let (_, _, Some(receipt)) = self
.get_or_deploy_contract_instance(&instance, caller, calldata, value) .get_or_deploy_contract_instance(&instance, caller, calldata, value)
@@ -445,7 +426,7 @@ where
.context("Failed to find deployment receipt for constructor call"), .context("Failed to find deployment receipt for constructor call"),
Method::Fallback | Method::FunctionName(_) => { Method::Fallback | Method::FunctionName(_) => {
let resolver = self.platform_information.node.resolver().await?; let resolver = self.platform_information.node.resolver().await?;
let tx = match step let mut tx = match step
.as_transaction(resolver.as_ref(), self.default_resolution_context()) .as_transaction(resolver.as_ref(), self.default_resolution_context())
.await .await
{ {
@@ -455,6 +436,11 @@ where
} }
}; };
// Resolve the signer to ensure we use an address that has keys
if let Some(from) = tx.from {
tx.from = Some(self.platform_information.node.resolve_signer_or_default(from));
}
self.platform_information.node.execute_transaction(tx).await self.platform_information.node.execute_transaction(tx).await
} }
} }
@@ -503,18 +489,13 @@ where
}; };
// Handling the return data variable assignments. // Handling the return data variable assignments.
for (variable_name, output_word) in assignments.return_data.iter().zip( for (variable_name, output_word) in assignments
tracing_result .return_data
.output .iter()
.as_ref() .zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32))
.unwrap_or_default() {
.to_vec()
.chunks(32),
) {
let value = U256::from_be_slice(output_word); let value = U256::from_be_slice(output_word);
self.execution_state self.execution_state.variables.insert(variable_name.clone(), value);
.variables
.insert(variable_name.clone(), value);
tracing::info!( tracing::info!(
variable_name, variable_name,
variable_value = hex::encode(value.to_be_bytes::<32>()), variable_value = hex::encode(value.to_be_bytes::<32>()),
@@ -546,7 +527,10 @@ where
expected: Some(Expected::ExpectedMany(expected)), expected: Some(Expected::ExpectedMany(expected)),
.. ..
} => expected.clone(), } => expected.clone(),
FunctionCallStep { expected: None, .. } => vec![ExpectedOutput::new().with_success()], FunctionCallStep {
expected: None,
..
} => vec![ExpectedOutput::new().with_success()],
}; };
// This is a bit of a special case and we have to support it separately on it's own. If it's // This is a bit of a special case and we have to support it separately on it's own. If it's
@@ -562,8 +546,7 @@ where
futures::stream::iter(expectations.into_iter().map(Ok)) futures::stream::iter(expectations.into_iter().map(Ok))
.try_for_each_concurrent(None, |expectation| async { .try_for_each_concurrent(None, |expectation| async {
self.handle_function_call_assertion_item(receipt, tracing_result, expectation) self.handle_function_call_assertion_item(receipt, tracing_result, expectation).await
.await
}) })
.await .await
} }
@@ -664,11 +647,8 @@ where
} }
// Handling the topics assertion. // Handling the topics assertion.
for (expected, actual) in expected_event for (expected, actual) in
.topics expected_event.topics.as_slice().iter().zip(actual_event.topics())
.as_slice()
.iter()
.zip(actual_event.topics())
{ {
let expected = Calldata::new_compound([expected]); let expected = Calldata::new_compound([expected]);
if !expected if !expected
@@ -764,11 +744,8 @@ where
.resolve_address(resolver.as_ref(), self.default_resolution_context()) .resolve_address(resolver.as_ref(), self.default_resolution_context())
.await?; .await?;
let storage = self let storage =
.platform_information self.platform_information.node.latest_state_proof(address, Default::default()).await?;
.node
.latest_state_proof(address, Default::default())
.await?;
let is_empty = storage.storage_hash == EMPTY_ROOT_HASH; let is_empty = storage.storage_hash == EMPTY_ROOT_HASH;
let expected = step.is_storage_empty; let expected = step.is_storage_empty;
@@ -818,9 +795,8 @@ where
}) })
.map(|driver| driver.execute_all()) .map(|driver| driver.execute_all())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let res = futures::future::try_join_all(tasks) let res =
.await futures::future::try_join_all(tasks).await.context("Repetition execution failed")?;
.context("Repetition execution failed")?;
Ok(res.first().copied().unwrap_or_default()) Ok(res.first().copied().unwrap_or_default())
} }
@@ -838,9 +814,7 @@ where
let account = private_key.address(); let account = private_key.address();
let variable = U256::from_be_slice(account.0.as_slice()); let variable = U256::from_be_slice(account.0.as_slice());
self.execution_state self.execution_state.variables.insert(variable_name.to_string(), variable);
.variables
.insert(variable_name.to_string(), variable);
Ok(1) Ok(1)
} }
@@ -863,10 +837,8 @@ where
calldata: Option<&Calldata>, calldata: Option<&Calldata>,
value: Option<EtherValue>, value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> { ) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
if let Some((_, address, abi)) = self if let Some((_, address, abi)) =
.execution_state self.execution_state.deployed_contracts.get(contract_instance)
.deployed_contracts
.get(contract_instance)
{ {
info!( info!(
@@ -876,6 +848,7 @@ where
Ok((*address, abi.clone(), None)) Ok((*address, abi.clone(), None))
} else { } else {
info!("Contract instance requires deployment."); info!("Contract instance requires deployment.");
let (address, abi, receipt) = self let (address, abi, receipt) = self
.deploy_contract(contract_instance, deployer, calldata, value) .deploy_contract(contract_instance, deployer, calldata, value)
.await .await
@@ -907,16 +880,9 @@ where
let Some(ContractPathAndIdent { let Some(ContractPathAndIdent {
contract_source_path, contract_source_path,
contract_ident, contract_ident,
}) = self }) = self.test_definition.metadata.contract_sources()?.remove(contract_instance)
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else { else {
anyhow::bail!( anyhow::bail!("Contract source not found for instance {:?}", contract_instance)
"Contract source not found for instance {:?}",
contract_instance
)
}; };
let Some((code, abi)) = self let Some((code, abi)) = self
@@ -926,10 +892,7 @@ where
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref())) .and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
.cloned() .cloned()
else { else {
anyhow::bail!( anyhow::bail!("Failed to find information for contract {:?}", contract_instance)
"Failed to find information for contract {:?}",
contract_instance
)
}; };
let mut code = match alloy::hex::decode(&code) { let mut code = match alloy::hex::decode(&code) {
@@ -947,13 +910,13 @@ where
if let Some(calldata) = calldata { if let Some(calldata) = calldata {
let resolver = self.platform_information.node.resolver().await?; let resolver = self.platform_information.node.resolver().await?;
let calldata = calldata let calldata =
.calldata(resolver.as_ref(), self.default_resolution_context()) calldata.calldata(resolver.as_ref(), self.default_resolution_context()).await?;
.await?;
code.extend(calldata); code.extend(calldata);
} }
let tx = { let tx = {
let deployer = self.platform_information.node.resolve_signer_or_default(deployer);
let tx = TransactionRequest::default().from(deployer); let tx = TransactionRequest::default().from(deployer);
let tx = match value { let tx = match value {
Some(ref value) => tx.value(value.into_inner()), Some(ref value) => tx.value(value.into_inner()),
@@ -982,10 +945,9 @@ where
.reporter .reporter
.report_contract_deployed_event(contract_instance.clone(), address)?; .report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state.deployed_contracts.insert( self.execution_state
contract_instance.clone(), .deployed_contracts
(contract_ident, address, abi.clone()), .insert(contract_instance.clone(), (contract_ident, address, abi.clone()));
);
Ok((address, abi, receipt)) Ok((address, abi, receipt))
} }
@@ -998,9 +960,7 @@ where
match step_address { match step_address {
StepAddress::Address(address) => Ok(*address), StepAddress::Address(address) => Ok(*address),
StepAddress::ResolvableAddress(resolvable) => { StepAddress::ResolvableAddress(resolvable) => {
let Some(instance) = resolvable let Some(instance) = resolvable.strip_suffix(".address").map(ContractInstance::new)
.strip_suffix(".address")
.map(ContractInstance::new)
else { else {
bail!("Not an address variable"); bail!("Not an address variable");
}; };
@@ -7,10 +7,10 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use crate::Platform;
use anyhow::Context as _; use anyhow::Context as _;
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use revive_dt_common::types::PrivateKeyAllocator; use revive_dt_common::types::PrivateKeyAllocator;
use crate::Platform;
use tokio::sync::{Mutex, RwLock, Semaphore}; use tokio::sync::{Mutex, RwLock, Semaphore};
use tracing::{Instrument, error, info, info_span, instrument}; use tracing::{Instrument, error, info, info_span, instrument};
@@ -37,12 +37,8 @@ pub async fn handle_differential_tests(
info!(len = metadata_files.len(), "Discovered metadata files"); info!(len = metadata_files.len(), "Discovered metadata files");
// Discover the list of platforms that the tests should run on based on the context. // Discover the list of platforms that the tests should run on based on the context.
let platforms = context let platforms =
.platforms context.platforms.iter().copied().map(Into::<&dyn Platform>::into).collect::<Vec<_>>();
.iter()
.copied()
.map(Into::<&dyn Platform>::into)
.collect::<Vec<_>>();
// Starting the nodes of the various platforms specified in the context. // Starting the nodes of the various platforms specified in the context.
let platforms_and_nodes = { let platforms_and_nodes = {
@@ -85,13 +81,8 @@ pub async fn handle_differential_tests(
// Creating everything else required for the driver to run. // Creating everything else required for the driver to run.
let cached_compiler = CachedCompiler::new( let cached_compiler = CachedCompiler::new(
context context.working_directory.as_path().join("compilation_cache"),
.working_directory context.compilation_configuration.invalidate_compilation_cache,
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
) )
.await .await
.map(Arc::new) .map(Arc::new)
@@ -101,11 +92,8 @@ pub async fn handle_differential_tests(
))); )));
// Creating the driver and executing all of the steps. // Creating the driver and executing all of the steps.
let semaphore = context let semaphore =
.concurrency_configuration context.concurrency_configuration.concurrency_limit().map(Semaphore::new).map(Arc::new);
.concurrency_limit()
.map(Semaphore::new)
.map(Arc::new);
let running_task_list = Arc::new(RwLock::new(BTreeSet::<usize>::new())); let running_task_list = Arc::new(RwLock::new(BTreeSet::<usize>::new()));
let driver_task = futures::future::join_all(test_definitions.iter().enumerate().map( let driver_task = futures::future::join_all(test_definitions.iter().enumerate().map(
|(test_id, test_definition)| { |(test_id, test_definition)| {
@@ -172,20 +160,14 @@ pub async fn handle_differential_tests(
)) ))
.inspect(|_| { .inspect(|_| {
info!("Finished executing all test cases"); info!("Finished executing all test cases");
reporter_clone reporter_clone.report_completion_event().expect("Can't fail")
.report_completion_event()
.expect("Can't fail")
}); });
let cli_reporting_task = start_cli_reporting_task(reporter); let cli_reporting_task = start_cli_reporting_task(reporter);
tokio::task::spawn(async move { tokio::task::spawn(async move {
loop { loop {
let remaining_tasks = running_task_list.read().await; let remaining_tasks = running_task_list.read().await;
info!( info!(count = remaining_tasks.len(), ?remaining_tasks, "Remaining Tests");
count = remaining_tasks.len(),
?remaining_tasks,
"Remaining Tests"
);
tokio::time::sleep(Duration::from_secs(10)).await tokio::time::sleep(Duration::from_secs(10)).await
} }
}); });
@@ -227,7 +209,9 @@ async fn start_cli_reporting_task(reporter: Reporter) {
for (case_idx, case_status) in case_status.into_iter() { for (case_idx, case_status) in case_status.into_iter() {
let _ = write!(buf, "\tCase Index {case_idx:>3}: "); let _ = write!(buf, "\tCase Index {case_idx:>3}: ");
let _ = match case_status { let _ = match case_status {
TestCaseStatus::Succeeded { steps_executed } => { TestCaseStatus::Succeeded {
steps_executed,
} => {
number_of_successes += 1; number_of_successes += 1;
writeln!( writeln!(
buf, buf,
@@ -235,7 +219,9 @@ async fn start_cli_reporting_task(reporter: Reporter) {
GREEN, BOLD, BOLD_RESET, steps_executed, COLOR_RESET GREEN, BOLD, BOLD_RESET, steps_executed, COLOR_RESET
) )
} }
TestCaseStatus::Failed { reason } => { TestCaseStatus::Failed {
reason,
} => {
number_of_failures += 1; number_of_failures += 1;
writeln!( writeln!(
buf, buf,
@@ -247,7 +233,10 @@ async fn start_cli_reporting_task(reporter: Reporter) {
COLOR_RESET, COLOR_RESET,
) )
} }
TestCaseStatus::Ignored { reason, .. } => writeln!( TestCaseStatus::Ignored {
reason,
..
} => writeln!(
buf, buf,
"{}{}Case Ignored{} - Reason: {}{}", "{}{}Case Ignored{} - Reason: {}{}",
GREY, GREY,
+9 -13
View File
@@ -8,10 +8,10 @@ use std::{
sync::{Arc, LazyLock}, sync::{Arc, LazyLock},
}; };
use crate::Platform;
use futures::FutureExt; use futures::FutureExt;
use revive_dt_common::{iterators::FilesWithExtensionIterator, types::CompilerIdentifier}; use revive_dt_common::{iterators::FilesWithExtensionIterator, types::CompilerIdentifier};
use revive_dt_compiler::{Compiler, CompilerOutput, Mode, SolidityCompiler}; use revive_dt_compiler::{Compiler, CompilerOutput, Mode, SolidityCompiler};
use crate::Platform;
use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata}; use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata};
use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address}; use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address};
@@ -225,9 +225,7 @@ async fn compile_contracts(
.flat_map(|value| value.iter()) .flat_map(|value| value.iter())
.map(|(instance, (ident, address, abi))| (instance, ident, address, abi)) .map(|(instance, (ident, address, abi))| (instance, ident, address, abi))
.flat_map(|(_, ident, address, _)| { .flat_map(|(_, ident, address, _)| {
all_sources_in_dir all_sources_in_dir.iter().map(move |path| (ident, address, path))
.iter()
.map(move |path| (ident, address, path))
}) })
.fold(compiler, |compiler, (ident, address, path)| { .fold(compiler, |compiler, (ident, address, path)| {
compiler.with_library(path, ident.as_str(), *address) compiler.with_library(path, ident.as_str(), *address)
@@ -309,19 +307,15 @@ impl ArtifactsCache {
pub async fn insert(&self, key: &CacheKey<'_>, value: &CacheValue) -> Result<()> { pub async fn insert(&self, key: &CacheKey<'_>, value: &CacheValue) -> Result<()> {
let key = bson::to_vec(key).context("Failed to serialize cache key (bson)")?; let key = bson::to_vec(key).context("Failed to serialize cache key (bson)")?;
let value = bson::to_vec(value).context("Failed to serialize cache value (bson)")?; let value = bson::to_vec(value).context("Failed to serialize cache value (bson)")?;
cacache::write(self.path.as_path(), key.encode_hex(), value) cacache::write(self.path.as_path(), key.encode_hex(), value).await.with_context(|| {
.await format!("Failed to write cache entry under {}", self.path.display())
.with_context(|| { })?;
format!("Failed to write cache entry under {}", self.path.display())
})?;
Ok(()) Ok(())
} }
pub async fn get(&self, key: &CacheKey<'_>) -> Option<CacheValue> { pub async fn get(&self, key: &CacheKey<'_>) -> Option<CacheValue> {
let key = bson::to_vec(key).ok()?; let key = bson::to_vec(key).ok()?;
let value = cacache::read(self.path.as_path(), key.encode_hex()) let value = cacache::read(self.path.as_path(), key.encode_hex()).await.ok()?;
.await
.ok()?;
let value = bson::from_slice::<CacheValue>(&value).ok()?; let value = bson::from_slice::<CacheValue>(&value).ok()?;
Some(value) Some(value)
} }
@@ -370,6 +364,8 @@ struct CacheValue {
impl CacheValue { impl CacheValue {
pub fn new(compiler_output: CompilerOutput) -> Self { pub fn new(compiler_output: CompilerOutput) -> Self {
Self { compiler_output } Self {
compiler_output,
}
} }
} }
+3 -5
View File
@@ -2,9 +2,9 @@
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use crate::Platform;
use anyhow::Context as _; use anyhow::Context as _;
use revive_dt_config::*; use revive_dt_config::*;
use crate::Platform;
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
/// The node pool starts one or more [Node] which then can be accessed /// The node pool starts one or more [Node] which then can be accessed
@@ -37,10 +37,8 @@ impl NodePool {
); );
} }
let pre_transactions_tasks = nodes let pre_transactions_tasks =
.iter_mut() nodes.iter_mut().map(|node| node.pre_transactions()).collect::<Vec<_>>();
.map(|node| node.pre_transactions())
.collect::<Vec<_>>();
futures::future::try_join_all(pre_transactions_tasks) futures::future::try_join_all(pre_transactions_tasks)
.await .await
.context("Failed to run the pre-transactions task")?; .context("Failed to run the pre-transactions task")?;
+56 -69
View File
@@ -1,28 +1,22 @@
use std::collections::BTreeMap; use std::{borrow::Cow, collections::BTreeMap, path::Path, sync::Arc};
use std::sync::Arc;
use std::{borrow::Cow, path::Path};
use futures::{Stream, StreamExt, stream}; use futures::{Stream, StreamExt, stream};
use indexmap::{IndexMap, indexmap}; use indexmap::{IndexMap, indexmap};
use revive_dt_common::iterators::EitherIter; use revive_dt_common::{iterators::EitherIter, types::PlatformIdentifier};
use revive_dt_common::types::PlatformIdentifier;
use revive_dt_config::Context; use revive_dt_config::Context;
use revive_dt_format::mode::ParsedMode; use revive_dt_format::mode::ParsedMode;
use serde_json::{Value, json}; use serde_json::{Value, json};
use revive_dt_compiler::Mode; use revive_dt_compiler::{Mode, SolidityCompiler};
use revive_dt_compiler::SolidityCompiler;
use revive_dt_format::{ use revive_dt_format::{
case::{Case, CaseIdx}, case::{Case, CaseIdx},
metadata::MetadataFile, metadata::MetadataFile,
}; };
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use revive_dt_report::{ExecutionSpecificReporter, Reporter}; use revive_dt_report::{ExecutionSpecificReporter, Reporter, TestSpecificReporter, TestSpecifier};
use revive_dt_report::{TestSpecificReporter, TestSpecifier};
use tracing::{debug, error, info}; use tracing::{debug, error, info};
use crate::Platform; use crate::{Platform, helpers::NodePool};
use crate::helpers::NodePool;
pub async fn create_test_definitions_stream<'a>( pub async fn create_test_definitions_stream<'a>(
// This is only required for creating the compiler objects and is not used anywhere else in the // This is only required for creating the compiler objects and is not used anywhere else in the
@@ -72,72 +66,68 @@ pub async fn create_test_definitions_stream<'a>(
// Inform the reporter of each one of the test cases that were discovered which we expect to // Inform the reporter of each one of the test cases that were discovered which we expect to
// run. // run.
.inspect(|(_, _, _, _, reporter)| { .inspect(|(_, _, _, _, reporter)| {
reporter reporter.report_test_case_discovery_event().expect("Can't fail");
.report_test_case_discovery_event()
.expect("Can't fail");
}), }),
) )
// Creating the Test Definition objects from all of the various objects we have and creating // Creating the Test Definition objects from all of the various objects we have and creating
// their required dependencies (e.g., compiler). // their required dependencies (e.g., compiler).
.filter_map( .filter_map(move |(metadata_file, case_idx, case, mode, reporter)| async move {
move |(metadata_file, case_idx, case, mode, reporter)| async move { let mut platforms = BTreeMap::new();
let mut platforms = BTreeMap::new(); for (platform, node_pool) in platforms_and_nodes.values() {
for (platform, node_pool) in platforms_and_nodes.values() { let node = node_pool.round_robbin();
let node = node_pool.round_robbin(); let compiler = platform
let compiler = platform .new_compiler(context.clone(), mode.version.clone().map(Into::into))
.new_compiler(context.clone(), mode.version.clone().map(Into::into)) .await
.await .inspect_err(|err| {
.inspect_err(|err| { error!(
error!( ?err,
?err, platform_identifier = %platform.platform_identifier(),
platform_identifier = %platform.platform_identifier(), "Failed to instantiate the compiler"
"Failed to instantiate the compiler"
)
})
.ok()?;
reporter
.report_node_assigned_event(
node.id(),
platform.platform_identifier(),
node.connection_string(),
) )
.expect("Can't fail"); })
.ok()?;
let reporter = reporter
reporter.execution_specific_reporter(node.id(), platform.platform_identifier()); .report_node_assigned_event(
node.id(),
platforms.insert(
platform.platform_identifier(), platform.platform_identifier(),
TestPlatformInformation { node.connection_string(),
platform: *platform, )
node, .expect("Can't fail");
compiler,
reporter,
},
);
}
Some(TestDefinition { let reporter =
/* Metadata file information */ reporter.execution_specific_reporter(node.id(), platform.platform_identifier());
metadata: metadata_file,
metadata_file_path: metadata_file.metadata_file_path.as_path(),
/* Mode Information */ platforms.insert(
mode: mode.clone(), platform.platform_identifier(),
TestPlatformInformation {
platform: *platform,
node,
compiler,
reporter,
},
);
}
/* Case Information */ Some(TestDefinition {
case_idx: CaseIdx::new(case_idx), /* Metadata file information */
case, metadata: metadata_file,
metadata_file_path: metadata_file.metadata_file_path.as_path(),
/* Platform and Node Assignment Information */ /* Mode Information */
platforms, mode: mode.clone(),
/* Reporter */ /* Case Information */
reporter, case_idx: CaseIdx::new(case_idx),
}) case,
},
) /* Platform and Node Assignment Information */
platforms,
/* Reporter */
reporter,
})
})
// Filter out the test cases which are incompatible or that can't run in the current setup. // Filter out the test cases which are incompatible or that can't run in the current setup.
.filter_map(move |test| async move { .filter_map(move |test| async move {
match test.check_compatibility() { match test.check_compatibility() {
@@ -280,10 +270,7 @@ impl<'a> TestDefinition<'a> {
if is_allowed { if is_allowed {
Ok(()) Ok(())
} else { } else {
Err(( Err(("EVM version is incompatible for the platforms specified", error_map))
"EVM version is incompatible for the platforms specified",
error_map,
))
} }
} }
+20 -39
View File
@@ -3,8 +3,8 @@
//! This crate defines the testing configuration and //! This crate defines the testing configuration and
//! provides a helper utility to execute tests. //! provides a helper utility to execute tests.
pub mod helpers;
pub mod differential_tests; pub mod differential_tests;
pub mod helpers;
use std::{ use std::{
pin::Pin, pin::Pin,
@@ -17,9 +17,11 @@ use revive_dt_common::types::*;
use revive_dt_compiler::{SolidityCompiler, revive_resolc::Resolc, solc::Solc}; use revive_dt_compiler::{SolidityCompiler, revive_resolc::Resolc, solc::Solc};
use revive_dt_config::*; use revive_dt_config::*;
use revive_dt_node::{ use revive_dt_node::{
Node, node_implementations::geth::GethNode, Node,
node_implementations::lighthouse_geth::LighthouseGethNode, node_implementations::{
node_implementations::substrate::SubstrateNode, node_implementations::zombienet::ZombieNode, geth::GethNode, lighthouse_geth::LighthouseGethNode, substrate::SubstrateNode,
zombienet::ZombieNode,
},
}; };
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use tracing::info; use tracing::info;
@@ -36,11 +38,7 @@ pub trait Platform {
/// Returns a full identifier for the platform. /// Returns a full identifier for the platform.
fn full_identifier(&self) -> (NodeIdentifier, VmIdentifier, CompilerIdentifier) { fn full_identifier(&self) -> (NodeIdentifier, VmIdentifier, CompilerIdentifier) {
( (self.node_identifier(), self.vm_identifier(), self.compiler_identifier())
self.node_identifier(),
self.vm_identifier(),
self.compiler_identifier(),
)
} }
/// Returns the identifier of the node used. /// Returns the identifier of the node used.
@@ -182,9 +180,7 @@ impl Platform for KitchensinkPolkavmResolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context) let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context).path.clone();
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -234,9 +230,7 @@ impl Platform for KitchensinkRevmSolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context) let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context).path.clone();
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -286,9 +280,8 @@ impl Platform for ReviveDevNodePolkavmResolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context) let revive_dev_node_path =
.path AsRef::<ReviveDevNodeConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -338,9 +331,8 @@ impl Platform for ReviveDevNodeRevmSolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context) let revive_dev_node_path =
.path AsRef::<ReviveDevNodeConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -390,9 +382,8 @@ impl Platform for ZombienetPolkavmResolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context) let polkadot_parachain_path =
.path AsRef::<PolkadotParachainConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = ZombieNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -438,9 +429,8 @@ impl Platform for ZombienetRevmSolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context) let polkadot_parachain_path =
.path AsRef::<PolkadotParachainConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = ZombieNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -519,17 +509,8 @@ fn spawn_node<T: Node + EthereumNode + Send + Sync>(
mut node: T, mut node: T,
genesis: Genesis, genesis: Genesis,
) -> anyhow::Result<T> { ) -> anyhow::Result<T> {
info!( info!(id = node.id(), connection_string = node.connection_string(), "Spawning node");
id = node.id(), node.spawn(genesis).context("Failed to spawn node process")?;
connection_string = node.connection_string(), info!(id = node.id(), connection_string = node.connection_string(), "Spawned node");
"Spawning node"
);
node.spawn(genesis)
.context("Failed to spawn node process")?;
info!(
id = node.id(),
connection_string = node.connection_string(),
"Spawned node"
);
Ok(node) Ok(node)
} }
+19 -25
View File
@@ -54,39 +54,33 @@ pub struct Case {
impl Case { impl Case {
pub fn steps_iterator(&self) -> impl Iterator<Item = Step> { pub fn steps_iterator(&self) -> impl Iterator<Item = Step> {
let steps_len = self.steps.len(); let steps_len = self.steps.len();
self.steps self.steps.clone().into_iter().enumerate().map(move |(idx, mut step)| {
.clone() let Step::FunctionCall(ref mut input) = step else {
.into_iter() return step;
.enumerate() };
.map(move |(idx, mut step)| {
let Step::FunctionCall(ref mut input) = step else {
return step;
};
if idx + 1 == steps_len { if idx + 1 == steps_len {
if input.expected.is_none() { if input.expected.is_none() {
input.expected = self.expected.clone(); input.expected = self.expected.clone();
}
// TODO: What does it mean for us to have an `expected` field on the case itself
// but the final input also has an expected field that doesn't match the one on
// the case? What are we supposed to do with that final expected field on the
// case?
step
} else {
step
} }
})
// TODO: What does it mean for us to have an `expected` field on the case itself
// but the final input also has an expected field that doesn't match the one on
// the case? What are we supposed to do with that final expected field on the
// case?
step
} else {
step
}
})
} }
pub fn steps_iterator_for_benchmarks( pub fn steps_iterator_for_benchmarks(
&self, &self,
default_repeat_count: usize, default_repeat_count: usize,
) -> Box<dyn Iterator<Item = Step> + '_> { ) -> Box<dyn Iterator<Item = Step> + '_> {
let contains_repeat = self let contains_repeat = self.steps_iterator().any(|step| matches!(&step, Step::Repeat(..)));
.steps_iterator()
.any(|step| matches!(&step, Step::Repeat(..)));
if contains_repeat { if contains_repeat {
Box::new(self.steps_iterator()) as Box<_> Box::new(self.steps_iterator()) as Box<_>
} else { } else {
+40 -22
View File
@@ -13,8 +13,14 @@ use anyhow::Context as _;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum Corpus { pub enum Corpus {
SinglePath { name: String, path: PathBuf }, SinglePath {
MultiplePaths { name: String, paths: Vec<PathBuf> }, name: String,
path: PathBuf,
},
MultiplePaths {
name: String,
paths: Vec<PathBuf>,
},
} }
impl Corpus { impl Corpus {
@@ -86,46 +92,58 @@ impl Corpus {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
tests.sort_by(|a, b| a.metadata_file_path.cmp(&b.metadata_file_path)); tests.sort_by(|a, b| a.metadata_file_path.cmp(&b.metadata_file_path));
tests.dedup_by(|a, b| a.metadata_file_path == b.metadata_file_path); tests.dedup_by(|a, b| a.metadata_file_path == b.metadata_file_path);
info!( info!(len = tests.len(), corpus_name = self.name(), "Found tests in Corpus");
len = tests.len(),
corpus_name = self.name(),
"Found tests in Corpus"
);
tests tests
} }
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
match self { match self {
Corpus::SinglePath { name, .. } | Corpus::MultiplePaths { name, .. } => name.as_str(), Corpus::SinglePath {
name,
..
}
| Corpus::MultiplePaths {
name,
..
} => name.as_str(),
} }
} }
pub fn paths_iter(&self) -> impl Iterator<Item = &Path> { pub fn paths_iter(&self) -> impl Iterator<Item = &Path> {
match self { match self {
Corpus::SinglePath { path, .. } => { Corpus::SinglePath {
Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>> path,
} ..
Corpus::MultiplePaths { paths, .. } => { } => Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>>,
Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>> Corpus::MultiplePaths {
} paths,
..
} => Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>,
} }
} }
pub fn paths_iter_mut(&mut self) -> impl Iterator<Item = &mut PathBuf> { pub fn paths_iter_mut(&mut self) -> impl Iterator<Item = &mut PathBuf> {
match self { match self {
Corpus::SinglePath { path, .. } => { Corpus::SinglePath {
Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>> path,
} ..
Corpus::MultiplePaths { paths, .. } => { } => Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>>,
Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>> Corpus::MultiplePaths {
} paths,
..
} => Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>,
} }
} }
pub fn path_count(&self) -> usize { pub fn path_count(&self) -> usize {
match self { match self {
Corpus::SinglePath { .. } => 1, Corpus::SinglePath {
Corpus::MultiplePaths { paths, .. } => paths.len(), ..
} => 1,
Corpus::MultiplePaths {
paths,
..
} => paths.len(),
} }
} }
} }
+5 -17
View File
@@ -44,9 +44,7 @@ impl MetadataFile {
if self.corpus_file_path.is_file() { if self.corpus_file_path.is_file() {
&self.corpus_file_path &self.corpus_file_path
} else { } else {
self.metadata_file_path self.metadata_file_path.strip_prefix(&self.corpus_file_path).unwrap()
.strip_prefix(&self.corpus_file_path)
.unwrap()
} }
} }
} }
@@ -167,10 +165,8 @@ impl Metadata {
) in contracts ) in contracts
{ {
let alias = alias.clone(); let alias = alias.clone();
let absolute_path = directory let absolute_path =
.join(contract_source_path) directory.join(contract_source_path).canonicalize().map_err(|error| {
.canonicalize()
.map_err(|error| {
anyhow::anyhow!( anyhow::anyhow!(
"Failed to canonicalize contract source path '{}': {error}", "Failed to canonicalize contract source path '{}': {error}",
directory.join(contract_source_path).display() directory.join(contract_source_path).display()
@@ -335,12 +331,7 @@ pub struct ContractPathAndIdent {
impl Display for ContractPathAndIdent { impl Display for ContractPathAndIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(f, "{}:{}", self.contract_source_path.display(), self.contract_ident.as_ref())
f,
"{}:{}",
self.contract_source_path.display(),
self.contract_ident.as_ref()
)
} }
} }
@@ -596,10 +587,7 @@ mod test {
// Assert // Assert
let identifier = identifier.expect("Failed to parse"); let identifier = identifier.expect("Failed to parse");
assert_eq!( assert_eq!(identifier.contract_source_path.display().to_string(), "ERC20/ERC20.sol");
identifier.contract_source_path.display().to_string(),
"ERC20/ERC20.sol"
);
assert_eq!(identifier.contract_ident, "ERC20".to_owned().into()); assert_eq!(identifier.contract_ident, "ERC20".to_owned().into());
// Act // Act
+16 -18
View File
@@ -1,13 +1,12 @@
use anyhow::Context as _; use anyhow::Context as _;
use regex::Regex; use regex::Regex;
use revive_dt_common::iterators::EitherIter; use revive_dt_common::{
use revive_dt_common::types::{Mode, ModeOptimizerSetting, ModePipeline}; iterators::EitherIter,
types::{Mode, ModeOptimizerSetting, ModePipeline},
};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::{collections::HashSet, fmt::Display, str::FromStr, sync::LazyLock};
use std::fmt::Display;
use std::str::FromStr;
use std::sync::LazyLock;
/// This represents a mode that has been parsed from test metadata. /// This represents a mode that has been parsed from test metadata.
/// ///
@@ -94,7 +93,11 @@ impl Display for ParsedMode {
if let Some(pipeline) = self.pipeline { if let Some(pipeline) = self.pipeline {
pipeline.fmt(f)?; pipeline.fmt(f)?;
if let Some(optimize_flag) = self.optimize_flag { if let Some(optimize_flag) = self.optimize_flag {
f.write_str(if optimize_flag { "+" } else { "-" })?; f.write_str(if optimize_flag {
"+"
} else {
"-"
})?;
} }
has_written = true; has_written = true;
} }
@@ -158,13 +161,11 @@ impl ParsedMode {
); );
pipeline_iter.flat_map(move |pipeline| { pipeline_iter.flat_map(move |pipeline| {
optimize_settings_iter optimize_settings_iter.clone().map(move |optimize_setting| Mode {
.clone() pipeline,
.map(move |optimize_setting| Mode { optimize_setting,
pipeline, version: self.version.clone(),
optimize_setting, })
version: self.version.clone(),
})
}) })
} }
@@ -236,10 +237,7 @@ mod tests {
("Y+", vec!["Y M3"]), ("Y+", vec!["Y M3"]),
("Y-", vec!["Y M0"]), ("Y-", vec!["Y M0"]),
("Y <=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8"]), ("Y <=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8"]),
( ("<=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8", "E M0 <=0.8", "E M3 <=0.8"]),
"<=0.8",
vec!["Y M0 <=0.8", "Y M3 <=0.8", "E M0 <=0.8", "E M3 <=0.8"],
),
]; ];
for (actual, expected) in strings { for (actual, expected) in strings {
+34 -109
View File
@@ -1,11 +1,10 @@
use std::{collections::HashMap, fmt::Display, str::FromStr}; use std::{collections::HashMap, fmt::Display, str::FromStr};
use alloy::primitives::{FixedBytes, utils::parse_units};
use alloy::{ use alloy::{
eips::BlockNumberOrTag, eips::BlockNumberOrTag,
json_abi::Function, json_abi::Function,
network::TransactionBuilder, network::TransactionBuilder,
primitives::{Address, Bytes, U256}, primitives::{Address, Bytes, FixedBytes, U256, utils::parse_units},
rpc::types::TransactionRequest, rpc::types::TransactionRequest,
}; };
use anyhow::Context as _; use anyhow::Context as _;
@@ -17,8 +16,10 @@ use serde::{Deserialize, Serialize};
use revive_dt_common::macros::define_wrapper_type; use revive_dt_common::macros::define_wrapper_type;
use tracing::{Instrument, info_span, instrument}; use tracing::{Instrument, info_span, instrument};
use crate::traits::ResolverApi; use crate::{
use crate::{metadata::ContractInstance, traits::ResolutionContext}; metadata::ContractInstance,
traits::{ResolutionContext, ResolverApi},
};
/// A test step. /// A test step.
/// ///
@@ -77,12 +78,7 @@ impl StepPath {
impl Display for StepPath { impl Display for StepPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0 self.0.iter().map(|idx| idx.to_string()).collect::<Vec<_>>().join(".").fmt(f)
.iter()
.map(|idx| idx.to_string())
.collect::<Vec<_>>()
.join(".")
.fmt(f)
} }
} }
@@ -90,10 +86,7 @@ impl FromStr for StepPath {
type Err = anyhow::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
s.split(".") s.split(".").map(StepIdx::from_str).collect::<anyhow::Result<Vec<_>>>().map(Self)
.map(StepIdx::from_str)
.collect::<anyhow::Result<Vec<_>>>()
.map(Self)
} }
} }
@@ -455,9 +448,7 @@ impl StepAddress {
impl FunctionCallStep { impl FunctionCallStep {
pub const fn default_caller_address() -> Address { pub const fn default_caller_address() -> Address {
Address(FixedBytes(alloy::hex!( Address(FixedBytes(alloy::hex!("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1")))
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"
)))
} }
pub const fn default_caller() -> StepAddress { pub const fn default_caller() -> StepAddress {
@@ -547,11 +538,9 @@ impl FunctionCallStep {
.await .await
.context("Failed to encode input bytes for transaction request")?; .context("Failed to encode input bytes for transaction request")?;
let caller = self.caller.resolve_address(resolver, context).await?; let caller = self.caller.resolve_address(resolver, context).await?;
let transaction_request = TransactionRequest::default().from(caller).value( let transaction_request = TransactionRequest::default()
self.value .from(caller)
.map(|value| value.into_inner()) .value(self.value.map(|value| value.into_inner()).unwrap_or_default());
.unwrap_or_default(),
);
match self.method { match self.method {
Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)), Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)),
_ => Ok(transaction_request _ => Ok(transaction_request
@@ -607,11 +596,7 @@ impl Calldata {
pub fn new_compound(items: impl IntoIterator<Item = impl AsRef<str>>) -> Self { pub fn new_compound(items: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
Self::Compound( Self::Compound(
items items.into_iter().map(|item| item.as_ref().to_owned()).map(CalldataItem::new).collect(),
.into_iter()
.map(|item| item.as_ref().to_owned())
.map(CalldataItem::new)
.collect(),
) )
} }
@@ -633,8 +618,7 @@ impl Calldata {
context: ResolutionContext<'_>, context: ResolutionContext<'_>,
) -> anyhow::Result<Vec<u8>> { ) -> anyhow::Result<Vec<u8>> {
let mut buffer = Vec::<u8>::with_capacity(self.size_requirement()); let mut buffer = Vec::<u8>::with_capacity(self.size_requirement());
self.calldata_into_slice(&mut buffer, resolver, context) self.calldata_into_slice(&mut buffer, resolver, context).await?;
.await?;
Ok(buffer) Ok(buffer)
} }
@@ -725,10 +709,7 @@ impl CalldataItem {
) -> anyhow::Result<U256> { ) -> anyhow::Result<U256> {
let mut stack = Vec::<CalldataToken<U256>>::new(); let mut stack = Vec::<CalldataToken<U256>>::new();
for token in self for token in self.calldata_tokens().map(|token| token.resolve(resolver, context)) {
.calldata_tokens()
.map(|token| token.resolve(resolver, context))
{
let token = token.await?; let token = token.await?;
let new_token = match token { let new_token = match token {
CalldataToken::Item(_) => token, CalldataToken::Item(_) => token,
@@ -769,9 +750,7 @@ impl CalldataItem {
// Empty stack means that we got an empty compound calldata which we resolve to zero. // Empty stack means that we got an empty compound calldata which we resolve to zero.
[] => Ok(U256::ZERO), [] => Ok(U256::ZERO),
[CalldataToken::Item(item)] => Ok(*item), [CalldataToken::Item(item)] => Ok(*item),
_ => Err(anyhow::anyhow!( _ => Err(anyhow::anyhow!("Invalid calldata arithmetic operation - Invalid stack")),
"Invalid calldata arithmetic operation - Invalid stack"
)),
} }
} }
@@ -915,10 +894,7 @@ impl<T: AsRef<str>> CalldataToken<T> {
.await .await
.map(U256::from) .map(U256::from)
} else if let Some(variable_name) = item.strip_prefix(Self::VARIABLE_PREFIX) { } else if let Some(variable_name) = item.strip_prefix(Self::VARIABLE_PREFIX) {
context context.variable(variable_name).context("Variable lookup failed").copied()
.variable(variable_name)
.context("Variable lookup failed")
.copied()
} else { } else {
U256::from_str_radix(item, 10) U256::from_str_radix(item, 10)
.map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error)) .map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))
@@ -959,9 +935,12 @@ impl<'de> Deserialize<'de> for EtherValue {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use alloy::primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, address}; use alloy::{
use alloy::sol_types::SolValue; eips::BlockNumberOrTag,
use alloy::{eips::BlockNumberOrTag, json_abi::JsonAbi}; json_abi::JsonAbi,
primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, address},
sol_types::SolValue,
};
use std::{collections::HashMap, pin::Pin}; use std::{collections::HashMap, pin::Pin};
use super::*; use super::*;
@@ -1045,13 +1024,7 @@ mod tests {
"#; "#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap(); let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap();
let selector = parsed_abi let selector = parsed_abi.function("store").unwrap().first().unwrap().selector().0;
.function("store")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input = FunctionCallStep { let input = FunctionCallStep {
instance: ContractInstance::new("Contract"), instance: ContractInstance::new("Contract"),
@@ -1089,13 +1062,7 @@ mod tests {
]"#; ]"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
let selector = parsed_abi let selector = parsed_abi.function("send").unwrap().first().unwrap().selector().0;
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: FunctionCallStep = FunctionCallStep { let input: FunctionCallStep = FunctionCallStep {
instance: "Contract".to_owned().into(), instance: "Contract".to_owned().into(),
@@ -1117,10 +1084,7 @@ mod tests {
type T = (alloy::primitives::Address,); type T = (alloy::primitives::Address,);
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap(); let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
assert_eq!( assert_eq!(decoded.0, address!("0x1000000000000000000000000000000000000001"));
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
} }
#[tokio::test] #[tokio::test]
@@ -1136,13 +1100,7 @@ mod tests {
]"#; ]"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
let selector = parsed_abi let selector = parsed_abi.function("send").unwrap().first().unwrap().selector().0;
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: FunctionCallStep = FunctionCallStep { let input: FunctionCallStep = FunctionCallStep {
instance: ContractInstance::new("Contract"), instance: ContractInstance::new("Contract"),
@@ -1164,10 +1122,7 @@ mod tests {
type T = (alloy::primitives::Address,); type T = (alloy::primitives::Address,);
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap(); let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
assert_eq!( assert_eq!(decoded.0, address!("0x1000000000000000000000000000000000000001"));
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
} }
async fn resolve_calldata_item( async fn resolve_calldata_item(
@@ -1204,12 +1159,7 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(
resolved, resolved,
U256::from( U256::from(MockResolver.block_gas_limit(Default::default()).await.unwrap())
MockResolver
.block_gas_limit(Default::default())
.await
.unwrap()
)
) )
} }
@@ -1226,11 +1176,7 @@ mod tests {
assert_eq!( assert_eq!(
resolved, resolved,
U256::from_be_slice( U256::from_be_slice(
MockResolver MockResolver.block_coinbase(Default::default()).await.unwrap().as_ref()
.block_coinbase(Default::default())
.await
.unwrap()
.as_ref()
) )
) )
} }
@@ -1245,13 +1191,7 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(resolved, MockResolver.block_difficulty(Default::default()).await.unwrap())
resolved,
MockResolver
.block_difficulty(Default::default())
.await
.unwrap()
)
} }
#[tokio::test] #[tokio::test]
@@ -1266,11 +1206,7 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(
resolved, resolved,
MockResolver MockResolver.block_base_fee(Default::default()).await.map(U256::from).unwrap()
.block_base_fee(Default::default())
.await
.map(U256::from)
.unwrap()
) )
} }
@@ -1300,10 +1236,7 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap()))
resolved,
U256::from(MockResolver.last_block_number().await.unwrap())
)
} }
#[tokio::test] #[tokio::test]
@@ -1318,12 +1251,7 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(
resolved, resolved,
U256::from( U256::from(MockResolver.block_timestamp(Default::default()).await.unwrap())
MockResolver
.block_timestamp(Default::default())
.await
.unwrap()
)
) )
} }
@@ -1401,10 +1329,7 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap() + 10));
resolved,
U256::from(MockResolver.last_block_number().await.unwrap() + 10)
);
} }
#[tokio::test] #[tokio::test]
+8 -10
View File
@@ -1,10 +1,10 @@
use std::collections::HashMap; use std::{collections::HashMap, pin::Pin};
use std::pin::Pin;
use alloy::eips::BlockNumberOrTag; use alloy::{
use alloy::json_abi::JsonAbi; eips::BlockNumberOrTag,
use alloy::primitives::TxHash; json_abi::JsonAbi,
use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256}; primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, U256},
};
use anyhow::Result; use anyhow::Result;
use crate::metadata::{ContractIdent, ContractInstance}; use crate::metadata::{ContractIdent, ContractInstance};
@@ -149,8 +149,7 @@ impl<'a> ResolutionContext<'a> {
&self, &self,
instance: &ContractInstance, instance: &ContractInstance,
) -> Option<&(ContractIdent, Address, JsonAbi)> { ) -> Option<&(ContractIdent, Address, JsonAbi)> {
self.deployed_contracts self.deployed_contracts.and_then(|deployed_contracts| deployed_contracts.get(instance))
.and_then(|deployed_contracts| deployed_contracts.get(instance))
} }
pub fn deployed_contract_address(&self, instance: &ContractInstance) -> Option<&Address> { pub fn deployed_contract_address(&self, instance: &ContractInstance) -> Option<&Address> {
@@ -162,8 +161,7 @@ impl<'a> ResolutionContext<'a> {
} }
pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> { pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> {
self.variables self.variables.and_then(|variables| variables.get(name.as_ref()))
.and_then(|variables| variables.get(name.as_ref()))
} }
pub fn tip_block_number(&self) -> Option<&'a BlockNumber> { pub fn tip_block_number(&self) -> Option<&'a BlockNumber> {
+14 -36
View File
@@ -11,7 +11,6 @@ use revive_dt_format::{
corpus::Corpus, corpus::Corpus,
metadata::{Metadata, MetadataFile}, metadata::{Metadata, MetadataFile},
}; };
use revive_dt_node_interaction::EthereumNode;
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::{BTreeMap, HashSet}, collections::{BTreeMap, HashSet},
@@ -130,10 +129,7 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display); println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display);
println!(" Error loading metadata: {}", e); println!(" Error loading metadata: {}", e);
failed_files += 1; failed_files += 1;
failures.push(( failures.push((file_display.clone(), format!("Error loading metadata: {}", e)));
file_display.clone(),
format!("Error loading metadata: {}", e),
));
if args.bail { if args.bail {
break; break;
} }
@@ -233,10 +229,7 @@ fn discover_test_files(path: &Path) -> anyhow::Result<Vec<PathBuf>> {
files.push(metadata.metadata_file_path); files.push(metadata.metadata_file_path);
} }
} }
_ => anyhow::bail!( _ => anyhow::bail!("Unsupported file extension: {}. Expected .sol or .json", extension),
"Unsupported file extension: {}. Expected .sol or .json",
extension
),
} }
} else if path.is_dir() { } else if path.is_dir() {
// Walk directory recursively for .sol files // Walk directory recursively for .sol files
@@ -291,12 +284,10 @@ async fn execute_test_file(
let test_context = TestExecutionContext::default(); let test_context = TestExecutionContext::default();
let context = revive_dt_config::Context::Test(Box::new(test_context)); let context = revive_dt_config::Context::Test(Box::new(test_context));
// Optionally start a node based on the --start-platform flag
let node: &'static dyn revive_dt_node_interaction::EthereumNode = if args.start_platform { let node: &'static dyn revive_dt_node_interaction::EthereumNode = if args.start_platform {
info!("Starting blockchain node..."); info!("Starting blockchain node...");
let node_handle = platform let node_handle =
.new_node(context.clone()) platform.new_node(context.clone()).context("Failed to spawn node thread")?;
.context("Failed to spawn node thread")?;
info!("Waiting for node to start..."); info!("Waiting for node to start...");
let node = node_handle let node = node_handle
@@ -304,18 +295,12 @@ async fn execute_test_file(
.map_err(|e| anyhow::anyhow!("Node thread panicked: {:?}", e))? .map_err(|e| anyhow::anyhow!("Node thread panicked: {:?}", e))?
.context("Failed to start node")?; .context("Failed to start node")?;
info!( info!("Node started with ID: {}, connection: {}", node.id(), node.connection_string());
"Node started with ID: {}, connection: {}",
node.id(),
node.connection_string()
);
// Run pre-transactions on the node // Run pre-transactions on the node
let node = Box::leak(node); // Leak to get 'static lifetime for simplicity let node = Box::leak(node); // Leak to get 'static lifetime for simplicity
info!("Running pre-transactions..."); info!("Running pre-transactions...");
node.pre_transactions() node.pre_transactions().await.context("Failed to run pre-transactions")?;
.await
.context("Failed to run pre-transactions")?;
info!("Pre-transactions completed"); info!("Pre-transactions completed");
node node
@@ -340,9 +325,8 @@ async fn execute_test_file(
.context("Failed to create cached compiler")?; .context("Failed to create cached compiler")?;
// Create a private key allocator // Create a private key allocator
let private_key_allocator = Arc::new(Mutex::new(PrivateKeyAllocator::new( let private_key_allocator =
alloy::primitives::U256::from(100), Arc::new(Mutex::new(PrivateKeyAllocator::new(alloy::primitives::U256::from(100))));
)));
// Create reporter infrastructure (minimal, just for the Driver API) // Create reporter infrastructure (minimal, just for the Driver API)
// Note: We need to keep the report_task alive, otherwise the reporter channel closes // Note: We need to keep the report_task alive, otherwise the reporter channel closes
@@ -352,10 +336,7 @@ async fn execute_test_file(
// Spawn the report task in the background to keep the channel open // Spawn the report task in the background to keep the channel open
tokio::spawn(report_task); tokio::spawn(report_task);
info!( info!("Building test definitions for {} case(s)", metadata_file.cases.len());
"Building test definitions for {} case(s)",
metadata_file.cases.len()
);
// Build all test definitions upfront // Build all test definitions upfront
let mut test_definitions = Vec::new(); let mut test_definitions = Vec::new();
for (case_idx, case) in metadata_file.cases.iter().enumerate() { for (case_idx, case) in metadata_file.cases.iter().enumerate() {
@@ -404,20 +385,17 @@ async fn execute_test_file(
test_definition.case.steps.len(), test_definition.case.steps.len(),
test_definition.case_idx test_definition.case_idx
); );
let steps_executed = driver.execute_all().await.context(format!( let steps_executed = driver
"Failed to execute case {}", .execute_all()
test_definition.case_idx .await
))?; .context(format!("Failed to execute case {}", test_definition.case_idx))?;
info!( info!(
"✓ Case {} completed successfully, executed {} step(s)", "✓ Case {} completed successfully, executed {} step(s)",
test_definition.case_idx, steps_executed test_definition.case_idx, steps_executed
); );
} }
info!("─────────────────────────────────────────────────────────────────"); info!("─────────────────────────────────────────────────────────────────");
info!( info!("All {} test case(s) executed successfully", test_definitions.len());
"All {} test case(s) executed successfully",
test_definitions.len()
);
Ok(()) Ok(())
} }
+11 -12
View File
@@ -1,11 +1,14 @@
//! This crate implements all node interactions. //! This crate implements all node interactions.
use std::pin::Pin; use std::{pin::Pin, sync::Arc};
use std::sync::Arc;
use alloy::primitives::{Address, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256}; use alloy::{
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace}; primitives::{Address, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256},
use alloy::rpc::types::{EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest}; rpc::types::{
EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest,
trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace},
},
};
use anyhow::Result; use anyhow::Result;
use futures::Stream; use futures::Stream;
@@ -75,13 +78,9 @@ pub trait EthereumNode {
>, >,
>; >;
/// Creates a node instance from an existing running node. /// Checks if the provided address is in the wallet. If it is, returns the address.
fn new_existing() -> Self /// Otherwise, returns the default signer's address.
where fn resolve_signer_or_default(&self, address: Address) -> Address;
Self: Sized,
{
panic!("new_existing is not implemented for this node type")
}
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+9 -24
View File
@@ -33,10 +33,7 @@ impl Process {
let log_file_prefix = log_file_prefix.into(); let log_file_prefix = log_file_prefix.into();
let (stdout_file_name, stderr_file_name) = match log_file_prefix { let (stdout_file_name, stderr_file_name) = match log_file_prefix {
Some(prefix) => ( Some(prefix) => (format!("{prefix}_stdout.log"), format!("{prefix}_stderr.log")),
format!("{prefix}_stdout.log"),
format!("{prefix}_stderr.log"),
),
None => ("stdout.log".to_string(), "stderr.log".to_string()), None => ("stdout.log".to_string(), "stderr.log".to_string()),
}; };
@@ -57,20 +54,16 @@ impl Process {
.context("Failed to open the stderr logs file")?; .context("Failed to open the stderr logs file")?;
let mut command = { let mut command = {
let stdout_logs_file = stdout_logs_file let stdout_logs_file =
.try_clone() stdout_logs_file.try_clone().context("Failed to clone the stdout logs file")?;
.context("Failed to clone the stdout logs file")?; let stderr_logs_file =
let stderr_logs_file = stderr_logs_file stderr_logs_file.try_clone().context("Failed to clone the stderr logs file")?;
.try_clone()
.context("Failed to clone the stderr logs file")?;
let mut command = Command::new(binary_path.as_ref()); let mut command = Command::new(binary_path.as_ref());
command_building_callback(&mut command, stdout_logs_file, stderr_logs_file); command_building_callback(&mut command, stdout_logs_file, stderr_logs_file);
command command
}; };
let mut child = command let mut child = command.spawn().context("Failed to spawn the built command")?;
.spawn()
.context("Failed to spawn the built command")?;
match process_readiness_wait_behavior { match process_readiness_wait_behavior {
ProcessReadinessWaitBehavior::NoStartupWait => {} ProcessReadinessWaitBehavior::NoStartupWait => {}
@@ -128,11 +121,7 @@ impl Process {
} }
} }
ProcessReadinessWaitBehavior::WaitForCommandToExit => { ProcessReadinessWaitBehavior::WaitForCommandToExit => {
if !child if !child.wait().context("Failed waiting for process to finish")?.success() {
.wait()
.context("Failed waiting for process to finish")?
.success()
{
anyhow::bail!("Failed to spawn command"); anyhow::bail!("Failed to spawn command");
} }
} }
@@ -149,12 +138,8 @@ impl Process {
impl Drop for Process { impl Drop for Process {
fn drop(&mut self) { fn drop(&mut self) {
self.child.kill().expect("Failed to kill the process"); self.child.kill().expect("Failed to kill the process");
self.stdout_logs_file self.stdout_logs_file.flush().expect("Failed to flush the stdout logs file");
.flush() self.stderr_logs_file.flush().expect("Failed to flush the stderr logs file");
.expect("Failed to flush the stdout logs file");
self.stderr_logs_file
.flush()
.expect("Failed to flush the stderr logs file");
} }
} }
+54 -81
View File
@@ -18,7 +18,9 @@ use alloy::{
eips::BlockNumberOrTag, eips::BlockNumberOrTag,
genesis::{Genesis, GenesisAccount}, genesis::{Genesis, GenesisAccount},
network::{Ethereum, EthereumWallet, NetworkWallet}, network::{Ethereum, EthereumWallet, NetworkWallet},
primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256}, primitives::{
Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, StorageKey, TxHash, U256,
},
providers::{ providers::{
Provider, Provider,
ext::DebugApi, ext::DebugApi,
@@ -75,6 +77,7 @@ pub struct GethNode {
wallet: Arc<EthereumWallet>, wallet: Arc<EthereumWallet>,
nonce_manager: CachedNonceManager, nonce_manager: CachedNonceManager,
provider: OnceCell<ConcreteProvider<Ethereum, Arc<EthereumWallet>>>, provider: OnceCell<ConcreteProvider<Ethereum, Arc<EthereumWallet>>>,
chain_id: ChainId,
} }
impl GethNode { impl GethNode {
@@ -105,9 +108,7 @@ impl GethNode {
let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context); let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context);
let geth_configuration = AsRef::<GethConfiguration>::as_ref(&context); let geth_configuration = AsRef::<GethConfiguration>::as_ref(&context);
let geth_directory = working_directory_configuration let geth_directory = working_directory_configuration.as_path().join(Self::BASE_DIRECTORY);
.as_path()
.join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = geth_directory.join(id.to_string()); let base_directory = geth_directory.join(id.to_string());
@@ -125,6 +126,25 @@ impl GethNode {
wallet: wallet.clone(), wallet: wallet.clone(),
nonce_manager: Default::default(), nonce_manager: Default::default(),
provider: Default::default(), provider: Default::default(),
chain_id: CHAIN_ID,
}
}
pub fn new_existing() -> Self {
let wallet_config = revive_dt_config::WalletConfiguration::default();
Self {
connection_string: "http://localhost:8545".to_string(),
base_directory: PathBuf::new(),
data_directory: PathBuf::new(),
logs_directory: PathBuf::new(),
geth: PathBuf::new(),
id: 0,
chain_id: 1337,
handle: None,
start_timeout: Duration::from_secs(0),
wallet: wallet_config.wallet(),
nonce_manager: Default::default(),
provider: Default::default(),
} }
} }
@@ -176,11 +196,7 @@ impl GethNode {
.read_to_string(&mut stderr) .read_to_string(&mut stderr)
.context("Failed to read geth --init stderr")?; .context("Failed to read geth --init stderr")?;
if !child if !child.wait().context("Failed waiting for geth --init process to finish")?.success() {
.wait()
.context("Failed waiting for geth --init process to finish")?
.success()
{
anyhow::bail!("failed to initialize geth node #{:?}: {stderr}", &self.id); anyhow::bail!("failed to initialize geth node #{:?}: {stderr}", &self.id);
} }
@@ -240,8 +256,7 @@ impl GethNode {
Ok(process) => self.handle = Some(process), Ok(process) => self.handle = Some(process),
Err(err) => { Err(err) => {
error!(?err, "Failed to start geth, shutting down gracefully"); error!(?err, "Failed to start geth, shutting down gracefully");
self.shutdown() self.shutdown().context("Failed to gracefully shutdown after geth start error")?;
.context("Failed to gracefully shutdown after geth start error")?;
return Err(err); return Err(err);
} }
} }
@@ -255,7 +270,7 @@ impl GethNode {
construct_concurrency_limited_provider::<Ethereum, _>( construct_concurrency_limited_provider::<Ethereum, _>(
self.connection_string.as_str(), self.connection_string.as_str(),
FallbackGasFiller::default(), FallbackGasFiller::default(),
ChainIdFiller::new(Some(CHAIN_ID)), ChainIdFiller::new(Some(self.chain_id)),
NonceFiller::new(self.nonce_manager.clone()), NonceFiller::new(self.nonce_manager.clone()),
self.wallet.clone(), self.wallet.clone(),
) )
@@ -386,10 +401,7 @@ impl EthereumNode for GethNode {
} }
}, },
) )
.instrument(tracing::info_span!( .instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash))
"Awaiting transaction receipt",
?transaction_hash
))
.await .await
}) })
} }
@@ -401,10 +413,8 @@ impl EthereumNode for GethNode {
trace_options: GethDebugTracingOptions, trace_options: GethDebugTracingOptions,
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
Box::pin(async move { Box::pin(async move {
let provider = self let provider =
.provider() self.provider().await.context("Failed to create provider for tracing")?;
.await
.context("Failed to create provider for tracing")?;
poll( poll(
Self::TRACE_POLLING_DURATION, Self::TRACE_POLLING_DURATION,
PollingWaitBehavior::Constant(Duration::from_millis(200)), PollingWaitBehavior::Constant(Duration::from_millis(200)),
@@ -412,10 +422,7 @@ impl EthereumNode for GethNode {
let provider = provider.clone(); let provider = provider.clone();
let trace_options = trace_options.clone(); let trace_options = trace_options.clone();
async move { async move {
match provider match provider.debug_trace_transaction(tx_hash, trace_options).await {
.debug_trace_transaction(tx_hash, trace_options)
.await
{
Ok(trace) => Ok(ControlFlow::Break(trace)), Ok(trace) => Ok(ControlFlow::Break(trace)),
Err(error) => { Err(error) => {
let error_string = error.to_string(); let error_string = error.to_string();
@@ -495,7 +502,10 @@ impl EthereumNode for GethNode {
Box::pin(async move { Box::pin(async move {
let id = self.id; let id = self.id;
let provider = self.provider().await?; let provider = self.provider().await?;
Ok(Arc::new(GethNodeResolver { id, provider }) as Arc<dyn ResolverApi>) Ok(Arc::new(GethNodeResolver {
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -543,19 +553,13 @@ impl EthereumNode for GethNode {
}) })
} }
fn new_existing() -> Self { fn resolve_signer_or_default(&self, address: Address) -> Address {
Self { let signer_addresses: Vec<_> =
connection_string: "http://localhost:8545".to_string(), <EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet).collect();
base_directory: PathBuf::new(), if signer_addresses.contains(&address) {
data_directory: PathBuf::new(), address
logs_directory: PathBuf::new(), } else {
geth: PathBuf::new(), self.wallet.default_signer().address()
id: 0,
handle: None,
start_timeout: Duration::from_secs(0),
wallet: Arc::new(EthereumWallet::default()),
nonce_manager: Default::default(),
provider: Default::default(),
} }
} }
} }
@@ -644,10 +648,7 @@ impl ResolverApi for GethNodeResolver {
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -764,11 +765,7 @@ mod tests {
// Arrange // Arrange
let (context, node) = shared_state(); let (context, node) = shared_state();
let account_address = context let account_address = context.wallet_configuration.wallet().default_signer().address();
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default() let transaction = TransactionRequest::default()
.to(account_address) .to(account_address)
.value(U256::from(100_000_000_000_000u128)); .value(U256::from(100_000_000_000_000u128));
@@ -791,10 +788,7 @@ mod tests {
// Assert // Assert
let version = version.expect("Failed to get the version"); let version = version.expect("Failed to get the version");
assert!( assert!(version.starts_with("geth version"), "expected version string, got: '{version}'");
version.starts_with("geth version"),
"expected version string, got: '{version}'"
);
} }
#[tokio::test] #[tokio::test]
@@ -818,12 +812,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -836,12 +826,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -854,12 +840,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -872,12 +854,7 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -890,12 +867,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -132,9 +132,7 @@ impl LighthouseGethNode {
let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context); let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context);
let kurtosis_configuration = AsRef::<KurtosisConfiguration>::as_ref(&context); let kurtosis_configuration = AsRef::<KurtosisConfiguration>::as_ref(&context);
let geth_directory = working_directory_configuration let geth_directory = working_directory_configuration.as_path().join(Self::BASE_DIRECTORY);
.as_path()
.join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = geth_directory.join(id.to_string()); let base_directory = geth_directory.join(id.to_string());
@@ -147,10 +145,7 @@ impl LighthouseGethNode {
http_connection_string: String::default(), http_connection_string: String::default(),
enclave_name: format!( enclave_name: format!(
"enclave-{}-{}", "enclave-{}-{}",
SystemTime::now() SystemTime::now().duration_since(UNIX_EPOCH).expect("Must not fail").as_nanos(),
.duration_since(UNIX_EPOCH)
.expect("Must not fail")
.as_nanos(),
id id
), ),
@@ -183,8 +178,7 @@ impl LighthouseGethNode {
fn init(&mut self, _: Genesis) -> anyhow::Result<&mut Self> { fn init(&mut self, _: Genesis) -> anyhow::Result<&mut Self> {
self.init_directories() self.init_directories()
.context("Failed to initialize the directories of the Lighthouse Geth node.")?; .context("Failed to initialize the directories of the Lighthouse Geth node.")?;
self.init_kurtosis_config_file() self.init_kurtosis_config_file().context("Failed to write the config file to the FS")?;
.context("Failed to write the config file to the FS")?;
Ok(self) Ok(self)
} }
@@ -426,9 +420,8 @@ impl LighthouseGethNode {
.context("Full block subscriber")?; .context("Full block subscriber")?;
let mut tx_hashes = futures::future::try_join_all( let mut tx_hashes = futures::future::try_join_all(
NetworkWallet::<Ethereum>::signer_addresses(self.wallet.as_ref()) NetworkWallet::<Ethereum>::signer_addresses(self.wallet.as_ref()).enumerate().map(
.enumerate() |(nonce, address)| async move {
.map(|(nonce, address)| async move {
let mut transaction = TransactionRequest::default() let mut transaction = TransactionRequest::default()
.from(self.prefunded_account_address) .from(self.prefunded_account_address)
.to(address) .to(address)
@@ -436,7 +429,8 @@ impl LighthouseGethNode {
.value(INITIAL_BALANCE.try_into().unwrap()); .value(INITIAL_BALANCE.try_into().unwrap());
transaction.chain_id = Some(CHAIN_ID); transaction.chain_id = Some(CHAIN_ID);
self.submit_transaction(transaction).await self.submit_transaction(transaction).await
}), },
),
) )
.await .await
.context("Failed to submit all transactions")? .context("Failed to submit all transactions")?
@@ -531,10 +525,7 @@ impl LighthouseGethNode {
} }
}, },
) )
.instrument(tracing::info_span!( .instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash))
"Awaiting transaction receipt",
?transaction_hash
))
.await .await
}) })
} }
@@ -623,9 +614,7 @@ impl EthereumNode for LighthouseGethNode {
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
Box::pin(async move { Box::pin(async move {
let provider = Arc::new( let provider = Arc::new(
self.http_provider() self.http_provider().await.context("Failed to create provider for tracing")?,
.await
.context("Failed to create provider for tracing")?,
); );
poll( poll(
Self::TRACE_POLLING_DURATION, Self::TRACE_POLLING_DURATION,
@@ -634,10 +623,7 @@ impl EthereumNode for LighthouseGethNode {
let provider = provider.clone(); let provider = provider.clone();
let trace_options = trace_options.clone(); let trace_options = trace_options.clone();
async move { async move {
match provider match provider.debug_trace_transaction(tx_hash, trace_options).await {
.debug_trace_transaction(tx_hash, trace_options)
.await
{
Ok(trace) => Ok(ControlFlow::Break(trace)), Ok(trace) => Ok(ControlFlow::Break(trace)),
Err(error) => { Err(error) => {
let error_string = error.to_string(); let error_string = error.to_string();
@@ -717,7 +703,10 @@ impl EthereumNode for LighthouseGethNode {
Box::pin(async move { Box::pin(async move {
let id = self.id; let id = self.id;
let provider = self.ws_provider().await?; let provider = self.ws_provider().await?;
Ok(Arc::new(LighthouseGethNodeResolver { id, provider }) as Arc<dyn ResolverApi>) Ok(Arc::new(LighthouseGethNodeResolver {
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -761,6 +750,16 @@ impl EthereumNode for LighthouseGethNode {
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>) as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
}) })
} }
fn resolve_signer_or_default(&self, address: Address) -> Address {
let signer_addresses: Vec<_> =
<EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet).collect();
if signer_addresses.contains(&address) {
address
} else {
self.wallet.default_signer().address()
}
}
} }
pub struct LighthouseGethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> { pub struct LighthouseGethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
@@ -849,10 +848,7 @@ impl<F: TxFiller<Ethereum>, P: Provider<Ethereum>> ResolverApi
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -906,11 +902,7 @@ impl Node for LighthouseGethNode {
.spawn() .spawn()
.expect("Failed to spawn the enclave kill command"); .expect("Failed to spawn the enclave kill command");
if !child if !child.wait().expect("Failed to wait for the enclave kill command").success() {
.wait()
.expect("Failed to wait for the enclave kill command")
.success()
{
let stdout = { let stdout = {
let mut stdout = String::default(); let mut stdout = String::default();
child child
@@ -1136,11 +1128,7 @@ mod tests {
let (context, node) = new_node(); let (context, node) = new_node();
node.fund_all_accounts().await.expect("Failed"); node.fund_all_accounts().await.expect("Failed");
let account_address = context let account_address = context.wallet_configuration.wallet().default_signer().address();
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default() let transaction = TransactionRequest::default()
.to(account_address) .to(account_address)
.value(U256::from(100_000_000_000_000u128)); .value(U256::from(100_000_000_000_000u128));
@@ -1163,10 +1151,7 @@ mod tests {
// Assert // Assert
let version = version.expect("Failed to get the version"); let version = version.expect("Failed to get the version");
assert!( assert!(version.starts_with("CLI Version"), "expected version string, got: '{version}'");
version.starts_with("CLI Version"),
"expected version string, got: '{version}'"
);
} }
#[tokio::test] #[tokio::test]
@@ -1190,12 +1175,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1208,12 +1189,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -1226,12 +1203,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1244,12 +1217,7 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -1262,12 +1230,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
+74 -103
View File
@@ -108,9 +108,7 @@ impl SubstrateNode {
) -> Self { ) -> Self {
let working_directory_path = let working_directory_path =
AsRef::<WorkingDirectoryConfiguration>::as_ref(&context).as_path(); AsRef::<WorkingDirectoryConfiguration>::as_ref(&context).as_path();
let eth_rpc_path = AsRef::<EthRpcConfiguration>::as_ref(&context) let eth_rpc_path = AsRef::<EthRpcConfiguration>::as_ref(&context).path.as_path();
.path
.as_path();
let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet(); let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet();
let substrate_directory = working_directory_path.join(Self::BASE_DIRECTORY); let substrate_directory = working_directory_path.join(Self::BASE_DIRECTORY);
@@ -134,6 +132,24 @@ impl SubstrateNode {
} }
} }
pub fn new_existing() -> Self {
let wallet_config = revive_dt_config::WalletConfiguration::default();
Self {
id: 0,
node_binary: PathBuf::new(),
eth_proxy_binary: PathBuf::new(),
export_chainspec_command: String::new(),
rpc_url: "http://localhost:8545".to_string(),
base_directory: PathBuf::new(),
logs_directory: PathBuf::new(),
substrate_process: None,
eth_proxy_process: None,
wallet: wallet_config.wallet(),
nonce_manager: Default::default(),
provider: Default::default(),
}
}
fn init(&mut self, mut genesis: Genesis) -> anyhow::Result<&mut Self> { fn init(&mut self, mut genesis: Genesis) -> anyhow::Result<&mut Self> {
let _ = remove_dir_all(self.base_directory.as_path()); let _ = remove_dir_all(self.base_directory.as_path());
let _ = clear_directory(&self.base_directory); let _ = clear_directory(&self.base_directory);
@@ -309,15 +325,12 @@ impl SubstrateNode {
&self, &self,
genesis: &Genesis, genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> { ) -> anyhow::Result<Vec<(String, u128)>> {
genesis genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| {
.alloc let substrate_address = Self::eth_to_substrate_address(address);
.iter() let balance = acc.balance.try_into()?;
.try_fold(Vec::new(), |mut vec, (address, acc)| { vec.push((substrate_address, balance));
let substrate_address = Self::eth_to_substrate_address(address); Ok(vec)
let balance = acc.balance.try_into()?; })
vec.push((substrate_address, balance));
Ok(vec)
})
} }
fn eth_to_substrate_address(address: &Address) -> String { fn eth_to_substrate_address(address: &Address) -> String {
@@ -412,10 +425,7 @@ impl EthereumNode for SubstrateNode {
transaction: TransactionRequest, transaction: TransactionRequest,
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
Box::pin(async move { Box::pin(async move {
let provider = self let provider = self.provider().await.context("Failed to create the provider")?;
.provider()
.await
.context("Failed to create the provider")?;
execute_transaction(provider, transaction).await execute_transaction(provider, transaction).await
}) })
} }
@@ -492,7 +502,10 @@ impl EthereumNode for SubstrateNode {
Box::pin(async move { Box::pin(async move {
let id = self.id; let id = self.id;
let provider = self.provider().await?; let provider = self.provider().await?;
Ok(Arc::new(SubstrateNodeResolver { id, provider }) as Arc<dyn ResolverApi>) Ok(Arc::new(SubstrateNodeResolver {
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -513,10 +526,8 @@ impl EthereumNode for SubstrateNode {
.provider() .provider()
.await .await
.context("Failed to create the provider for block subscription")?; .context("Failed to create the provider for block subscription")?;
let mut block_subscription = provider let mut block_subscription =
.watch_full_blocks() provider.watch_full_blocks().await.context("Failed to create the blocks stream")?;
.await
.context("Failed to create the blocks stream")?;
block_subscription.set_channel_size(0xFFFF); block_subscription.set_channel_size(0xFFFF);
block_subscription.set_poll_interval(Duration::from_secs(1)); block_subscription.set_poll_interval(Duration::from_secs(1));
let block_stream = block_subscription.into_stream(); let block_stream = block_subscription.into_stream();
@@ -542,20 +553,13 @@ impl EthereumNode for SubstrateNode {
}) })
} }
fn new_existing() -> Self { fn resolve_signer_or_default(&self, address: Address) -> Address {
Self { let signer_addresses: Vec<_> =
id: 0, <EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet).collect();
node_binary: PathBuf::new(), if signer_addresses.contains(&address) {
eth_proxy_binary: PathBuf::new(), address
export_chainspec_command: String::new(), } else {
rpc_url: "http://localhost:8545".to_string(), self.wallet.default_signer().address()
base_directory: PathBuf::new(),
logs_directory: PathBuf::new(),
substrate_process: None,
eth_proxy_process: None,
wallet: Arc::new(EthereumWallet::default()),
nonce_manager: Default::default(),
provider: Default::default(),
} }
} }
} }
@@ -644,10 +648,7 @@ impl ResolverApi for SubstrateNodeResolver {
.context("Failed to get the substrate block")? .context("Failed to get the substrate block")?
.context("Failed to get the substrate block, perhaps the chain has no blocks?") .context("Failed to get the substrate block, perhaps the chain has no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -926,25 +927,26 @@ impl TransactionBuilder<ReviveNetwork> for <Ethereum as Network>::TransactionReq
); );
match result { match result {
Ok(unsigned_tx) => Ok(unsigned_tx), Ok(unsigned_tx) => Ok(unsigned_tx),
Err(UnbuiltTransactionError { request, error }) => { Err(UnbuiltTransactionError {
Err(UnbuiltTransactionError::<ReviveNetwork> { request,
request, error,
error: match error { }) => Err(UnbuiltTransactionError::<ReviveNetwork> {
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => { request,
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) error: match error {
} TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => {
TransactionBuilderError::UnsupportedSignatureType => { TransactionBuilderError::InvalidTransactionRequest(tx_type, items)
TransactionBuilderError::UnsupportedSignatureType }
} TransactionBuilderError::UnsupportedSignatureType => {
TransactionBuilderError::Signer(error) => { TransactionBuilderError::UnsupportedSignatureType
TransactionBuilderError::Signer(error) }
} TransactionBuilderError::Signer(error) => {
TransactionBuilderError::Custom(error) => { TransactionBuilderError::Signer(error)
TransactionBuilderError::Custom(error) }
} TransactionBuilderError::Custom(error) => {
}, TransactionBuilderError::Custom(error)
}) }
} },
}),
} }
} }
@@ -1212,11 +1214,7 @@ mod tests {
let provider = node.provider().await.expect("Failed to create provider"); let provider = node.provider().await.expect("Failed to create provider");
let account_address = context let account_address = context.wallet_configuration.wallet().default_signer().address();
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default() let transaction = TransactionRequest::default()
.to(account_address) .to(account_address)
.value(U256::from(100_000_000_000_000u128)); .value(U256::from(100_000_000_000_000u128));
@@ -1256,14 +1254,11 @@ mod tests {
); );
// Call `init()` // Call `init()`
dummy_node dummy_node.init(serde_json::from_str(genesis_content).unwrap()).expect("init failed");
.init(serde_json::from_str(genesis_content).unwrap())
.expect("init failed");
// Check that the patched chainspec file was generated // Check that the patched chainspec file was generated
let final_chainspec_path = dummy_node let final_chainspec_path =
.base_directory dummy_node.base_directory.join(SubstrateNode::CHAIN_SPEC_JSON_FILE);
.join(SubstrateNode::CHAIN_SPEC_JSON_FILE);
assert!(final_chainspec_path.exists(), "Chainspec file should exist"); assert!(final_chainspec_path.exists(), "Chainspec file should exist");
let contents = fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec"); let contents = fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec");
@@ -1369,10 +1364,7 @@ mod tests {
for (eth_addr, expected_ss58) in cases { for (eth_addr, expected_ss58) in cases {
let result = SubstrateNode::eth_to_substrate_address(&eth_addr.parse().unwrap()); let result = SubstrateNode::eth_to_substrate_address(&eth_addr.parse().unwrap());
assert_eq!( assert_eq!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}");
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
} }
} }
@@ -1423,12 +1415,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1441,12 +1429,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -1459,12 +1443,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1477,12 +1457,7 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -1495,12 +1470,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -130,14 +130,10 @@ impl ZombieNode {
+ AsRef<EthRpcConfiguration> + AsRef<EthRpcConfiguration>
+ AsRef<WalletConfiguration>, + AsRef<WalletConfiguration>,
) -> Self { ) -> Self {
let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context) let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context).path.to_owned();
.path
.to_owned();
let working_directory_path = AsRef::<WorkingDirectoryConfiguration>::as_ref(&context); let working_directory_path = AsRef::<WorkingDirectoryConfiguration>::as_ref(&context);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = working_directory_path let base_directory = working_directory_path.join(Self::BASE_DIRECTORY).join(id.to_string());
.join(Self::BASE_DIRECTORY)
.join(id.to_string());
let logs_directory = base_directory.join(Self::LOGS_DIRECTORY); let logs_directory = base_directory.join(Self::LOGS_DIRECTORY);
let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet(); let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet();
@@ -169,10 +165,8 @@ impl ZombieNode {
let template_chainspec_path = self.base_directory.join(Self::CHAIN_SPEC_JSON_FILE); let template_chainspec_path = self.base_directory.join(Self::CHAIN_SPEC_JSON_FILE);
self.prepare_chainspec(template_chainspec_path.clone(), genesis)?; self.prepare_chainspec(template_chainspec_path.clone(), genesis)?;
let polkadot_parachain_path = self let polkadot_parachain_path =
.polkadot_parachain_path self.polkadot_parachain_path.to_str().context("Invalid polkadot parachain path")?;
.to_str()
.context("Invalid polkadot parachain path")?;
let node_rpc_port = Self::NODE_BASE_RPC_PORT + self.id as u16; let node_rpc_port = Self::NODE_BASE_RPC_PORT + self.id as u16;
@@ -204,10 +198,8 @@ impl ZombieNode {
} }
fn spawn_process(&mut self) -> anyhow::Result<()> { fn spawn_process(&mut self) -> anyhow::Result<()> {
let network_config = self let network_config =
.network_config self.network_config.clone().context("Node not initialized, call init() first")?;
.clone()
.context("Node not initialized, call init() first")?;
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
let network = rt.block_on(async { let network = rt.block_on(async {
@@ -272,17 +264,12 @@ impl ZombieNode {
mut genesis: Genesis, mut genesis: Genesis,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut cmd: Command = std::process::Command::new(&self.polkadot_parachain_path); let mut cmd: Command = std::process::Command::new(&self.polkadot_parachain_path);
cmd.arg(Self::EXPORT_CHAINSPEC_COMMAND) cmd.arg(Self::EXPORT_CHAINSPEC_COMMAND).arg("--chain").arg("asset-hub-westend-local");
.arg("--chain")
.arg("asset-hub-westend-local");
let output = cmd.output().context("Failed to export the chain-spec")?; let output = cmd.output().context("Failed to export the chain-spec")?;
if !output.status.success() { if !output.status.success() {
anyhow::bail!( anyhow::bail!("Build chain-spec failed: {}", String::from_utf8_lossy(&output.stderr));
"Build chain-spec failed: {}",
String::from_utf8_lossy(&output.stderr)
);
} }
let content = String::from_utf8(output.stdout) let content = String::from_utf8(output.stdout)
@@ -343,15 +330,12 @@ impl ZombieNode {
&self, &self,
genesis: &Genesis, genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> { ) -> anyhow::Result<Vec<(String, u128)>> {
genesis genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| {
.alloc let polkadot_address = Self::eth_to_polkadot_address(address);
.iter() let balance = acc.balance.try_into()?;
.try_fold(Vec::new(), |mut vec, (address, acc)| { vec.push((polkadot_address, balance));
let polkadot_address = Self::eth_to_polkadot_address(address); Ok(vec)
let balance = acc.balance.try_into()?; })
vec.push((polkadot_address, balance));
Ok(vec)
})
} }
fn eth_to_polkadot_address(address: &Address) -> String { fn eth_to_polkadot_address(address: &Address) -> String {
@@ -535,7 +519,10 @@ impl EthereumNode for ZombieNode {
let id = self.id; let id = self.id;
let provider = self.provider().await?; let provider = self.provider().await?;
Ok(Arc::new(ZombieNodeResolver { id, provider }) as Arc<dyn ResolverApi>) Ok(Arc::new(ZombieNodeResolver {
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -556,10 +543,8 @@ impl EthereumNode for ZombieNode {
.provider() .provider()
.await .await
.context("Failed to create the provider for block subscription")?; .context("Failed to create the provider for block subscription")?;
let mut block_subscription = provider let mut block_subscription =
.watch_full_blocks() provider.watch_full_blocks().await.context("Failed to create the blocks stream")?;
.await
.context("Failed to create the blocks stream")?;
block_subscription.set_channel_size(0xFFFF); block_subscription.set_channel_size(0xFFFF);
block_subscription.set_poll_interval(Duration::from_secs(1)); block_subscription.set_poll_interval(Duration::from_secs(1));
let block_stream = block_subscription.into_stream(); let block_stream = block_subscription.into_stream();
@@ -584,6 +569,16 @@ impl EthereumNode for ZombieNode {
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>) as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
}) })
} }
fn resolve_signer_or_default(&self, address: Address) -> Address {
let signer_addresses: Vec<_> =
<EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet).collect();
if signer_addresses.contains(&address) {
address
} else {
self.wallet.default_signer().address()
}
}
} }
pub struct ZombieNodeResolver<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> { pub struct ZombieNodeResolver<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> {
@@ -672,10 +667,7 @@ impl<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> ResolverApi
.context("Failed to get the zombie block")? .context("Failed to get the zombie block")?
.context("Failed to get the zombie block, perhaps the chain has no blocks?") .context("Failed to get the zombie block, perhaps the chain has no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -787,10 +779,8 @@ mod tests {
pub async fn new_node() -> (TestExecutionContext, ZombieNode) { pub async fn new_node() -> (TestExecutionContext, ZombieNode) {
let context = test_config(); let context = test_config();
let mut node = ZombieNode::new( let mut node =
context.polkadot_parachain_configuration.path.clone(), ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
&context,
);
let genesis = context.genesis_configuration.genesis().unwrap().clone(); let genesis = context.genesis_configuration.genesis().unwrap().clone();
node.init(genesis).unwrap(); node.init(genesis).unwrap();
@@ -855,14 +845,11 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let mut node = ZombieNode::new( let mut node =
context.polkadot_parachain_configuration.path.clone(), ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
&context,
);
// Call `init()` // Call `init()`
node.init(serde_json::from_str(genesis_content).unwrap()) node.init(serde_json::from_str(genesis_content).unwrap()).expect("init failed");
.expect("init failed");
// Check that the patched chainspec file was generated // Check that the patched chainspec file was generated
let final_chainspec_path = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE); let final_chainspec_path = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE);
@@ -903,10 +890,7 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let node = ZombieNode::new( let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
context.polkadot_parachain_configuration.path.clone(),
&context,
);
let result = node let result = node
.extract_balance_from_genesis_file(&serde_json::from_str(genesis_json).unwrap()) .extract_balance_from_genesis_file(&serde_json::from_str(genesis_json).unwrap())
@@ -968,10 +952,7 @@ mod tests {
for (eth_addr, expected_ss58) in cases { for (eth_addr, expected_ss58) in cases {
let result = ZombieNode::eth_to_polkadot_address(&eth_addr.parse().unwrap()); let result = ZombieNode::eth_to_polkadot_address(&eth_addr.parse().unwrap());
assert_eq!( assert_eq!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}");
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
} }
} }
@@ -979,10 +960,7 @@ mod tests {
fn eth_rpc_version_works() { fn eth_rpc_version_works() {
// Arrange // Arrange
let context = test_config(); let context = test_config();
let node = ZombieNode::new( let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act // Act
let version = node.eth_rpc_version().unwrap(); let version = node.eth_rpc_version().unwrap();
@@ -998,10 +976,7 @@ mod tests {
fn version_works() { fn version_works() {
// Arrange // Arrange
let context = test_config(); let context = test_config();
let node = ZombieNode::new( let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act // Act
let version = node.version().unwrap(); let version = node.version().unwrap();
@@ -1039,12 +1014,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1057,12 +1028,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -1075,12 +1042,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1093,12 +1056,7 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -1111,12 +1069,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -55,10 +55,7 @@ where
let future = self.service.call(req); let future = self.service.call(req);
Box::pin(async move { Box::pin(async move {
let _permit = semaphore let _permit = semaphore.acquire().await.expect("Semaphore has been closed");
.acquire()
.await
.expect("Semaphore has been closed");
tracing::debug!( tracing::debug!(
available_permits = semaphore.available_permits(), available_permits = semaphore.available_permits(),
"Acquired Semaphore Permit" "Acquired Semaphore Permit"
+14 -19
View File
@@ -80,10 +80,8 @@ where
NonceFiller: TxFiller<N>, NonceFiller: TxFiller<N>,
WalletFiller<W>: TxFiller<N>, WalletFiller<W>: TxFiller<N>,
{ {
let sendable_transaction = provider let sendable_transaction =
.fill(transaction) provider.fill(transaction).await.context("Failed to fill transaction")?;
.await
.context("Failed to fill transaction")?;
let transaction_envelope = sendable_transaction let transaction_envelope = sendable_transaction
.try_into_envelope() .try_into_envelope()
@@ -105,24 +103,21 @@ where
debug!(%tx_hash, "Submitted Transaction"); debug!(%tx_hash, "Submitted Transaction");
pending_transaction.set_timeout(Some(Duration::from_secs(120))); pending_transaction.set_timeout(Some(Duration::from_secs(120)));
let tx_hash = pending_transaction.watch().await.context(format!( let tx_hash = pending_transaction
"Transaction inclusion watching timeout for {tx_hash}" .watch()
))?; .await
.context(format!("Transaction inclusion watching timeout for {tx_hash}"))?;
poll( poll(Duration::from_secs(60), PollingWaitBehavior::Constant(Duration::from_secs(3)), || {
Duration::from_secs(60), let provider = provider.clone();
PollingWaitBehavior::Constant(Duration::from_secs(3)),
|| {
let provider = provider.clone();
async move { async move {
match provider.get_transaction_receipt(tx_hash).await { match provider.get_transaction_receipt(tx_hash).await {
Ok(Some(receipt)) => Ok(ControlFlow::Break(receipt)), Ok(Some(receipt)) => Ok(ControlFlow::Break(receipt)),
_ => Ok(ControlFlow::Continue(())), _ => Ok(ControlFlow::Continue(())),
}
} }
}, }
) })
.await .await
.context(format!("Polling for receipt failed for {tx_hash}")) .context(format!("Polling for receipt failed for {tx_hash}"))
} }
+10 -36
View File
@@ -123,12 +123,8 @@ impl ReportAggregator {
file_name.push_str(".json"); file_name.push_str(".json");
file_name file_name
}; };
let file_path = self let file_path =
.report self.report.context.working_directory_configuration().as_path().join(file_name);
.context
.working_directory_configuration()
.as_path()
.join(file_name);
let file = OpenOptions::new() let file = OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
@@ -136,10 +132,7 @@ impl ReportAggregator {
.read(false) .read(false)
.open(&file_path) .open(&file_path)
.with_context(|| { .with_context(|| {
format!( format!("Failed to open report file for writing: {}", file_path.display())
"Failed to open report file for writing: {}",
file_path.display()
)
})?; })?;
serde_json::to_writer_pretty(&file, &self.report).with_context(|| { serde_json::to_writer_pretty(&file, &self.report).with_context(|| {
format!("Failed to serialize report JSON to {}", file_path.display()) format!("Failed to serialize report JSON to {}", file_path.display())
@@ -241,10 +234,7 @@ impl ReportAggregator {
.or_default() .or_default()
.iter() .iter()
.map(|(case_idx, case_report)| { .map(|(case_idx, case_report)| {
( (*case_idx, case_report.status.clone().expect("Can't be uninitialized"))
*case_idx,
case_report.status.clone().expect("Can't be uninitialized"),
)
}) })
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
let event = ReporterEvent::MetadataFileSolcModeCombinationExecutionCompleted { let event = ReporterEvent::MetadataFileSolcModeCombinationExecutionCompleted {
@@ -276,16 +266,8 @@ impl ReportAggregator {
&mut self, &mut self,
event: PreLinkContractsCompilationSucceededEvent, event: PreLinkContractsCompilationSucceededEvent,
) { ) {
let include_input = self let include_input = self.report.context.report_configuration().include_compiler_input;
.report let include_output = self.report.context.report_configuration().include_compiler_output;
.context
.report_configuration()
.include_compiler_input;
let include_output = self
.report
.context
.report_configuration()
.include_compiler_output;
let execution_information = self.execution_information(&event.execution_specifier); let execution_information = self.execution_information(&event.execution_specifier);
@@ -313,16 +295,8 @@ impl ReportAggregator {
&mut self, &mut self,
event: PostLinkContractsCompilationSucceededEvent, event: PostLinkContractsCompilationSucceededEvent,
) { ) {
let include_input = self let include_input = self.report.context.report_configuration().include_compiler_input;
.report let include_output = self.report.context.report_configuration().include_compiler_output;
.context
.report_configuration()
.include_compiler_input;
let include_output = self
.report
.context
.report_configuration()
.include_compiler_output;
let execution_information = self.execution_information(&event.execution_specifier); let execution_information = self.execution_information(&event.execution_specifier);
@@ -375,8 +349,8 @@ impl ReportAggregator {
} }
fn handle_libraries_deployed_event(&mut self, event: LibrariesDeployedEvent) { fn handle_libraries_deployed_event(&mut self, event: LibrariesDeployedEvent) {
self.execution_information(&event.execution_specifier) self.execution_information(&event.execution_specifier).deployed_libraries =
.deployed_libraries = Some(event.libraries); Some(event.libraries);
} }
fn handle_contract_deployed_event(&mut self, event: ContractDeployedEvent) { fn handle_contract_deployed_event(&mut self, event: ContractDeployedEvent) {
+4 -2
View File
@@ -8,8 +8,10 @@ use anyhow::Context as _;
use indexmap::IndexMap; use indexmap::IndexMap;
use revive_dt_common::types::PlatformIdentifier; use revive_dt_common::types::PlatformIdentifier;
use revive_dt_compiler::{CompilerInput, CompilerOutput}; use revive_dt_compiler::{CompilerInput, CompilerOutput};
use revive_dt_format::metadata::Metadata; use revive_dt_format::{
use revive_dt_format::{corpus::Corpus, metadata::ContractInstance}; corpus::Corpus,
metadata::{ContractInstance, Metadata},
};
use semver::Version; use semver::Version;
use tokio::sync::{broadcast, oneshot}; use tokio::sync::{broadcast, oneshot};
+9 -30
View File
@@ -22,9 +22,8 @@ pub(crate) async fn get_or_download(
working_directory: &Path, working_directory: &Path,
downloader: &SolcDownloader, downloader: &SolcDownloader,
) -> anyhow::Result<(Version, PathBuf)> { ) -> anyhow::Result<(Version, PathBuf)> {
let target_directory = working_directory let target_directory =
.join(SOLC_CACHE_DIRECTORY) working_directory.join(SOLC_CACHE_DIRECTORY).join(downloader.version.to_string());
.join(downloader.version.to_string());
let target_file = target_directory.join(downloader.target); let target_file = target_directory.join(downloader.target);
let mut cache = SOLC_CACHER.lock().await; let mut cache = SOLC_CACHER.lock().await;
@@ -34,19 +33,11 @@ pub(crate) async fn get_or_download(
} }
create_dir_all(&target_directory).with_context(|| { create_dir_all(&target_directory).with_context(|| {
format!( format!("Failed to create solc cache directory: {}", target_directory.display())
"Failed to create solc cache directory: {}",
target_directory.display()
)
})?; })?;
download_to_file(&target_file, downloader) download_to_file(&target_file, downloader)
.await .await
.with_context(|| { .with_context(|| format!("Failed to write downloaded solc to {}", target_file.display()))?;
format!(
"Failed to write downloaded solc to {}",
target_file.display()
)
})?;
cache.insert(target_file.clone()); cache.insert(target_file.clone());
Ok((downloader.version.clone(), target_file)) Ok((downloader.version.clone(), target_file))
@@ -70,15 +61,9 @@ async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> anyhow::R
} }
let mut file = BufWriter::new(file); let mut file = BufWriter::new(file);
file.write_all( file.write_all(&downloader.download().await.context("Failed to download solc binary bytes")?)
&downloader .with_context(|| format!("Failed to write solc binary to {}", path.display()))?;
.download() file.flush().with_context(|| format!("Failed to flush file {}", path.display()))?;
.await
.context("Failed to download solc binary bytes")?,
)
.with_context(|| format!("Failed to write solc binary to {}", path.display()))?;
file.flush()
.with_context(|| format!("Failed to flush file {}", path.display()))?;
drop(file); drop(file);
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@@ -91,17 +76,11 @@ async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> anyhow::R
.stdout(std::process::Stdio::null()) .stdout(std::process::Stdio::null())
.spawn() .spawn()
.with_context(|| { .with_context(|| {
format!( format!("Failed to spawn xattr to remove quarantine attribute on {}", path.display())
"Failed to spawn xattr to remove quarantine attribute on {}",
path.display()
)
})? })?
.wait() .wait()
.with_context(|| { .with_context(|| {
format!( format!("Failed waiting for xattr operation to complete on {}", path.display())
"Failed waiting for xattr operation to complete on {}",
path.display()
)
})?; })?;
Ok(()) Ok(())
+8 -41
View File
@@ -130,11 +130,7 @@ impl SolcDownloader {
})?; })?;
let path = build.path.clone(); let path = build.path.clone();
let expected_digest = build let expected_digest = build.sha256.strip_prefix("0x").unwrap_or(&build.sha256).to_string();
.sha256
.strip_prefix("0x")
.unwrap_or(&build.sha256)
.to_string();
let url = format!("{}/{}/{}", Self::BASE_URL, self.target, path.display()); let url = format!("{}/{}/{}", Self::BASE_URL, self.target, path.display());
let file = reqwest::get(&url) let file = reqwest::get(&url)
@@ -159,54 +155,25 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn try_get_windows() { async fn try_get_windows() {
let version = List::download(List::WINDOWS_URL) let version = List::download(List::WINDOWS_URL).await.unwrap().latest_release;
.await SolcDownloader::windows(version).await.unwrap().download().await.unwrap();
.unwrap()
.latest_release;
SolcDownloader::windows(version)
.await
.unwrap()
.download()
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]
async fn try_get_macosx() { async fn try_get_macosx() {
let version = List::download(List::MACOSX_URL) let version = List::download(List::MACOSX_URL).await.unwrap().latest_release;
.await SolcDownloader::macosx(version).await.unwrap().download().await.unwrap();
.unwrap()
.latest_release;
SolcDownloader::macosx(version)
.await
.unwrap()
.download()
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]
async fn try_get_linux() { async fn try_get_linux() {
let version = List::download(List::LINUX_URL) let version = List::download(List::LINUX_URL).await.unwrap().latest_release;
.await SolcDownloader::linux(version).await.unwrap().download().await.unwrap();
.unwrap()
.latest_release;
SolcDownloader::linux(version)
.await
.unwrap()
.download()
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]
async fn try_get_wasm() { async fn try_get_wasm() {
let version = List::download(List::WASM_URL).await.unwrap().latest_release; let version = List::download(List::WASM_URL).await.unwrap().latest_release;
SolcDownloader::wasm(version) SolcDownloader::wasm(version).await.unwrap().download().await.unwrap();
.await
.unwrap()
.download()
.await
.unwrap();
} }
} }