This commit is contained in:
pgherveou
2025-10-08 06:28:57 +00:00
parent 765569a8b6
commit 1f84ce6f61
34 changed files with 1093 additions and 503 deletions
+4 -1
View File
@@ -27,7 +27,10 @@ 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(std::io::ErrorKind::InvalidData, "The contents of the file are not valid UTF8") Error::new(
std::io::ErrorKind::InvalidData,
"The contents of the file are not valid UTF8",
)
}) })
} }
+4 -1
View File
@@ -12,7 +12,10 @@ 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!("Failed to read an entry in directory: {}", path.as_ref().display()) format!(
"Failed to read an entry in directory: {}",
path.as_ref().display()
)
})?; })?;
let entry_path = entry.path(); let entry_path = entry.path();
+4 -1
View File
@@ -37,7 +37,10 @@ where
)); ));
} }
match future().await.context("Polled future returned an error during polling loop")? { match future()
.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,
+3 -1
View File
@@ -76,7 +76,9 @@ 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!("Unsupported pipeline '{s}': expected 'Y' or 'E'")), _ => Err(anyhow::anyhow!(
"Unsupported pipeline '{s}': expected 'Y' or 'E'"
)),
} }
} }
} }
+3 -1
View File
@@ -141,7 +141,9 @@ 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.unwrap_or(ModeOptimizerSetting::M0).optimizations_enabled(), optimization
.unwrap_or(ModeOptimizerSetting::M0)
.optimizations_enabled(),
None, None,
&Version::new(0, 0, 0), &Version::new(0, 0, 0),
false, false,
+7 -2
View File
@@ -56,7 +56,9 @@ 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.into().unwrap_or_else(|| solc_configuration.version.clone().into()); let version = version
.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
@@ -250,7 +252,10 @@ 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!("Failed to canonicalize contract path {}", contract_path.display()) format!(
"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() {
+22 -8
View File
@@ -9,8 +9,9 @@ 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 = let solc = Solc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30)))
Solc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30))).await.unwrap(); .await
.unwrap();
// Act // Act
let output = Compiler::new() let output = Compiler::new()
@@ -27,12 +28,18 @@ async fn contracts_can_be_compiled_with_solc() {
let main_file_contracts = output let main_file_contracts = output
.contracts .contracts
.get(&PathBuf::from("./tests/assets/array_one_element/main.sol").canonicalize().unwrap()) .get(
&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").canonicalize().unwrap(), &PathBuf::from("./tests/assets/array_one_element/callable.sol")
.canonicalize()
.unwrap(),
) )
.unwrap(); .unwrap();
assert!(main_file_contracts.contains_key("Main")); assert!(main_file_contracts.contains_key("Main"));
@@ -43,8 +50,9 @@ 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 = let resolc = Resolc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30)))
Resolc::new(&args, VersionOrRequirement::Version(Version::new(0, 8, 30))).await.unwrap(); .await
.unwrap();
// Act // Act
let output = Compiler::new() let output = Compiler::new()
@@ -61,12 +69,18 @@ async fn contracts_can_be_compiled_with_resolc() {
let main_file_contracts = output let main_file_contracts = output
.contracts .contracts
.get(&PathBuf::from("./tests/assets/array_one_element/main.sol").canonicalize().unwrap()) .get(
&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").canonicalize().unwrap(), &PathBuf::from("./tests/assets/array_one_element/callable.sol")
.canonicalize()
.unwrap(),
) )
.unwrap(); .unwrap();
assert!(main_file_contracts.contains_key("Main")); assert!(main_file_contracts.contains_key("Main"));
+17 -4
View File
@@ -626,7 +626,11 @@ 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(id = "kurtosis.path", long = "kurtosis.path", default_value = "kurtosis")] #[clap(
id = "kurtosis.path",
long = "kurtosis.path",
default_value = "kurtosis"
)]
pub path: PathBuf, pub path: PathBuf,
} }
@@ -637,7 +641,11 @@ 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(id = "kitchensink.path", long = "kitchensink.path", default_value = "substrate-node")] #[clap(
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.
@@ -829,7 +837,10 @@ 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(self.number_concurrent_tasks.unwrap_or(20 * self.number_of_nodes)), false => Some(
self.number_concurrent_tasks
.unwrap_or(20 * self.number_of_nodes),
),
} }
} }
} }
@@ -920,7 +931,9 @@ impl Serialize for WorkingDirectoryConfiguration {
} }
fn parse_duration(s: &str) -> anyhow::Result<Duration> { fn parse_duration(s: &str) -> anyhow::Result<Duration> {
u64::from_str(s).map(Duration::from_millis).map_err(Into::into) u64::from_str(s)
.map(Duration::from_millis)
.map_err(Into::into)
} }
/// The Solidity compatible node implementation. /// The Solidity compatible node implementation.
@@ -206,7 +206,9 @@ where
"Deployed library" "Deployed library"
); );
let library_address = receipt.contract_address.expect("Failed to deploy the library"); let library_address = receipt
.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(),
@@ -234,8 +236,10 @@ where
}) })
.context("Failed to compile the post-link contracts")?; .context("Failed to compile the post-link contracts")?;
self.execution_state = self.execution_state = ExecutionState::new(
ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default()); compiler_output.contracts,
deployed_libraries.unwrap_or_default(),
);
Ok(()) Ok(())
} }
@@ -321,7 +325,11 @@ 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.execution_state.deployed_contracts.contains_key(&instance) { if !self
.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);
} }
} }
@@ -333,11 +341,15 @@ 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.then_some(step.value).flatten(); let value = deploy_with_constructor_arguments
.then_some(step.value)
.flatten();
let caller = { let caller = {
let context = self.default_resolution_context(); let context = self.default_resolution_context();
step.caller.resolve_address(self.resolver.as_ref(), context).await? step.caller
.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)
@@ -412,13 +424,18 @@ where
}; };
// Handling the return data variable assignments. // Handling the return data variable assignments.
for (variable_name, output_word) in assignments for (variable_name, output_word) in assignments.return_data.iter().zip(
.return_data tracing_result
.iter() .output
.zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32)) .as_ref()
{ .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.variables.insert(variable_name.clone(), value); self.execution_state
.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>()),
@@ -491,8 +508,9 @@ where
}) })
.context("Failed to send message on the watcher's tx")?; .context("Failed to send message on the watcher's tx")?;
let res = let res = futures::future::try_join_all(tasks)
futures::future::try_join_all(tasks).await.context("Repetition execution failed")?; .await
.context("Repetition execution failed")?;
Ok(res.into_iter().sum()) Ok(res.into_iter().sum())
} }
@@ -515,7 +533,9 @@ 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.variables.insert(variable_name.to_string(), variable); self.execution_state
.variables
.insert(variable_name.to_string(), variable);
Ok(1) Ok(1)
} }
@@ -540,8 +560,10 @@ 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)) = if let Some((_, address, abi)) = self
self.execution_state.deployed_contracts.get(contract_instance) .execution_state
.deployed_contracts
.get(contract_instance)
{ {
info!( info!(
@@ -584,9 +606,16 @@ where
let Some(ContractPathAndIdent { let Some(ContractPathAndIdent {
contract_source_path, contract_source_path,
contract_ident, contract_ident,
}) = self.test_definition.metadata.contract_sources()?.remove(contract_instance) }) = self
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else { else {
anyhow::bail!("Contract source not found for instance {:?}", contract_instance) anyhow::bail!(
"Contract source not found for instance {:?}",
contract_instance
)
}; };
let Some((code, abi)) = self let Some((code, abi)) = self
@@ -596,7 +625,10 @@ 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!("Failed to find information for contract {:?}", contract_instance) anyhow::bail!(
"Failed to find information for contract {:?}",
contract_instance
)
}; };
let mut code = match alloy::hex::decode(&code) { let mut code = match alloy::hex::decode(&code) {
@@ -648,9 +680,10 @@ where
.reporter .reporter
.report_contract_deployed_event(contract_instance.clone(), address)?; .report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state self.execution_state.deployed_contracts.insert(
.deployed_contracts contract_instance.clone(),
.insert(contract_instance.clone(), (contract_ident, address, abi.clone())); (contract_ident, address, abi.clone()),
);
Ok((address, abi, receipt)) Ok((address, abi, receipt))
} }
@@ -663,7 +696,9 @@ 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.strip_suffix(".address").map(ContractInstance::new) let Some(instance) = resolvable
.strip_suffix(".address")
.map(ContractInstance::new)
else { else {
bail!("Not an address variable"); bail!("Not an address variable");
}; };
@@ -701,15 +736,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 = let transaction_hash = node
node.submit_transaction(transaction).await.context("Failed to submit transaction")?; .submit_transaction(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 { .send(WatcherEvent::SubmittedTransaction { transaction_hash })
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,8 +44,12 @@ 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 = let platforms = context
context.platforms.iter().copied().map(Into::<&dyn Platform>::into).collect::<Vec<_>>(); .platforms
.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
@@ -92,8 +96,13 @@ 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.working_directory.as_path().join("compilation_cache"), context
context.compilation_configuration.invalidate_compilation_cache, .working_directory
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
) )
.await .await
.map(Arc::new) .map(Arc::new)
@@ -152,7 +161,9 @@ 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.send(WatcherEvent::AllTransactionsSubmitted).unwrap() watcher_tx
.send(WatcherEvent::AllTransactionsSubmitted)
.unwrap()
}), }),
) )
.await .await
@@ -80,13 +80,12 @@ 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
WatcherEvent::SubmittedTransaction { .write()
transaction_hash, .await
} => { .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;
@@ -152,8 +151,15 @@ 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!(stderr, "Watcher information for {}", self.platform_identifier)?; writeln!(
writeln!(stderr, "block_number,block_timestamp,mined_gas,block_gas_limit,tx_count")?; stderr,
"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,
+103 -45
View File
@@ -76,9 +76,7 @@ impl<'a> Driver<'a, StepsIterator> {
.into_iter() .into_iter()
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
Ok(Self { Ok(Self { platform_drivers })
platform_drivers,
})
} }
async fn create_platform_driver( async fn create_platform_driver(
@@ -114,7 +112,9 @@ 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.into_values().map(|driver| driver.execute_all()), platform_drivers
.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,8 +210,12 @@ 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 for library_instance in test_definition
test_definition.metadata.libraries.iter().flatten().flat_map(|(_, map)| map.values()) .metadata
.libraries
.iter()
.flatten()
.flat_map(|(_, map)| map.values())
{ {
let ContractPathAndIdent { let ContractPathAndIdent {
contract_source_path: library_source_path, contract_source_path: library_source_path,
@@ -248,8 +252,11 @@ where
TransactionRequest::default().from(deployer_address), TransactionRequest::default().from(deployer_address),
code, code,
); );
let receipt = let receipt = platform_information
platform_information.node.execute_transaction(tx).await.inspect_err(|err| { .node
.execute_transaction(tx)
.await
.inspect_err(|err| {
error!( error!(
?err, ?err,
%library_instance, %library_instance,
@@ -258,7 +265,9 @@ where
) )
})?; })?;
let library_address = receipt.contract_address.expect("Failed to deploy the library"); let library_address = receipt
.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(),
@@ -286,7 +295,10 @@ where
}) })
.context("Failed to compile the post-link contracts")?; .context("Failed to compile the post-link contracts")?;
Ok(ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default())) Ok(ExecutionState::new(
compiler_output.contracts,
deployed_libraries.unwrap_or_default(),
))
} }
// endregion:Constructors & Initialization // endregion:Constructors & Initialization
@@ -380,7 +392,11 @@ 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.execution_state.deployed_contracts.contains_key(&instance) { if !self
.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);
} }
} }
@@ -392,13 +408,20 @@ 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.then_some(step.value).flatten(); let value = deploy_with_constructor_arguments
.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?;
let resolved = step.caller.resolve_address(resolver.as_ref(), context).await?; let resolved = step
self.platform_information.node.resolve_signer_or_default(resolved) .caller
.resolve_address(resolver.as_ref(), context)
.await?;
self.platform_information
.node
.resolve_signer_or_default(resolved)
}; };
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)
@@ -438,7 +461,11 @@ where
// Resolve the signer to ensure we use an address that has keys // Resolve the signer to ensure we use an address that has keys
if let Some(from) = tx.from { if let Some(from) = tx.from {
tx.from = Some(self.platform_information.node.resolve_signer_or_default(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
@@ -489,13 +516,18 @@ where
}; };
// Handling the return data variable assignments. // Handling the return data variable assignments.
for (variable_name, output_word) in assignments for (variable_name, output_word) in assignments.return_data.iter().zip(
.return_data tracing_result
.iter() .output
.zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32)) .as_ref()
{ .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.variables.insert(variable_name.clone(), value); self.execution_state
.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>()),
@@ -527,10 +559,7 @@ where
expected: Some(Expected::ExpectedMany(expected)), expected: Some(Expected::ExpectedMany(expected)),
.. ..
} => expected.clone(), } => expected.clone(),
FunctionCallStep { FunctionCallStep { expected: None, .. } => vec![ExpectedOutput::new().with_success()],
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
@@ -546,7 +575,8 @@ 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).await self.handle_function_call_assertion_item(receipt, tracing_result, expectation)
.await
}) })
.await .await
} }
@@ -647,8 +677,11 @@ where
} }
// Handling the topics assertion. // Handling the topics assertion.
for (expected, actual) in for (expected, actual) in expected_event
expected_event.topics.as_slice().iter().zip(actual_event.topics()) .topics
.as_slice()
.iter()
.zip(actual_event.topics())
{ {
let expected = Calldata::new_compound([expected]); let expected = Calldata::new_compound([expected]);
if !expected if !expected
@@ -744,8 +777,11 @@ where
.resolve_address(resolver.as_ref(), self.default_resolution_context()) .resolve_address(resolver.as_ref(), self.default_resolution_context())
.await?; .await?;
let storage = let storage = self
self.platform_information.node.latest_state_proof(address, Default::default()).await?; .platform_information
.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;
@@ -795,8 +831,9 @@ where
}) })
.map(|driver| driver.execute_all()) .map(|driver| driver.execute_all())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let res = let res = futures::future::try_join_all(tasks)
futures::future::try_join_all(tasks).await.context("Repetition execution failed")?; .await
.context("Repetition execution failed")?;
Ok(res.first().copied().unwrap_or_default()) Ok(res.first().copied().unwrap_or_default())
} }
@@ -814,7 +851,9 @@ 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.variables.insert(variable_name.to_string(), variable); self.execution_state
.variables
.insert(variable_name.to_string(), variable);
Ok(1) Ok(1)
} }
@@ -837,8 +876,10 @@ 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)) = if let Some((_, address, abi)) = self
self.execution_state.deployed_contracts.get(contract_instance) .execution_state
.deployed_contracts
.get(contract_instance)
{ {
info!( info!(
@@ -880,9 +921,16 @@ where
let Some(ContractPathAndIdent { let Some(ContractPathAndIdent {
contract_source_path, contract_source_path,
contract_ident, contract_ident,
}) = self.test_definition.metadata.contract_sources()?.remove(contract_instance) }) = self
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else { else {
anyhow::bail!("Contract source not found for instance {:?}", contract_instance) anyhow::bail!(
"Contract source not found for instance {:?}",
contract_instance
)
}; };
let Some((code, abi)) = self let Some((code, abi)) = self
@@ -892,7 +940,10 @@ 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!("Failed to find information for contract {:?}", contract_instance) anyhow::bail!(
"Failed to find information for contract {:?}",
contract_instance
)
}; };
let mut code = match alloy::hex::decode(&code) { let mut code = match alloy::hex::decode(&code) {
@@ -910,13 +961,17 @@ 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 = let calldata = calldata
calldata.calldata(resolver.as_ref(), self.default_resolution_context()).await?; .calldata(resolver.as_ref(), self.default_resolution_context())
.await?;
code.extend(calldata); code.extend(calldata);
} }
let tx = { let tx = {
let deployer = self.platform_information.node.resolve_signer_or_default(deployer); 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()),
@@ -945,9 +1000,10 @@ where
.reporter .reporter
.report_contract_deployed_event(contract_instance.clone(), address)?; .report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state self.execution_state.deployed_contracts.insert(
.deployed_contracts contract_instance.clone(),
.insert(contract_instance.clone(), (contract_ident, address, abi.clone())); (contract_ident, address, abi.clone()),
);
Ok((address, abi, receipt)) Ok((address, abi, receipt))
} }
@@ -960,7 +1016,9 @@ 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.strip_suffix(".address").map(ContractInstance::new) let Some(instance) = resolvable
.strip_suffix(".address")
.map(ContractInstance::new)
else { else {
bail!("Not an address variable"); bail!("Not an address variable");
}; };
@@ -37,8 +37,12 @@ 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 = let platforms = context
context.platforms.iter().copied().map(Into::<&dyn Platform>::into).collect::<Vec<_>>(); .platforms
.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 = {
@@ -81,8 +85,13 @@ 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.working_directory.as_path().join("compilation_cache"), context
context.compilation_configuration.invalidate_compilation_cache, .working_directory
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
) )
.await .await
.map(Arc::new) .map(Arc::new)
@@ -92,8 +101,11 @@ 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 = let semaphore = context
context.concurrency_configuration.concurrency_limit().map(Semaphore::new).map(Arc::new); .concurrency_configuration
.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)| {
@@ -160,14 +172,20 @@ pub async fn handle_differential_tests(
)) ))
.inspect(|_| { .inspect(|_| {
info!("Finished executing all test cases"); info!("Finished executing all test cases");
reporter_clone.report_completion_event().expect("Can't fail") reporter_clone
.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!(count = remaining_tasks.len(), ?remaining_tasks, "Remaining Tests"); info!(
count = remaining_tasks.len(),
?remaining_tasks,
"Remaining Tests"
);
tokio::time::sleep(Duration::from_secs(10)).await tokio::time::sleep(Duration::from_secs(10)).await
} }
}); });
@@ -209,9 +227,7 @@ 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 { TestCaseStatus::Succeeded { steps_executed } => {
steps_executed,
} => {
number_of_successes += 1; number_of_successes += 1;
writeln!( writeln!(
buf, buf,
@@ -219,9 +235,7 @@ 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 { TestCaseStatus::Failed { reason } => {
reason,
} => {
number_of_failures += 1; number_of_failures += 1;
writeln!( writeln!(
buf, buf,
@@ -233,10 +247,7 @@ async fn start_cli_reporting_task(reporter: Reporter) {
COLOR_RESET, COLOR_RESET,
) )
} }
TestCaseStatus::Ignored { TestCaseStatus::Ignored { reason, .. } => writeln!(
reason,
..
} => writeln!(
buf, buf,
"{}{}Case Ignored{} - Reason: {}{}", "{}{}Case Ignored{} - Reason: {}{}",
GREY, GREY,
+12 -8
View File
@@ -225,7 +225,9 @@ 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.iter().map(move |path| (ident, address, path)) all_sources_in_dir
.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)
@@ -307,15 +309,19 @@ 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).await.with_context(|| { cacache::write(self.path.as_path(), key.encode_hex(), value)
format!("Failed to write cache entry under {}", self.path.display()) .await
})?; .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()).await.ok()?; let value = cacache::read(self.path.as_path(), key.encode_hex())
.await
.ok()?;
let value = bson::from_slice::<CacheValue>(&value).ok()?; let value = bson::from_slice::<CacheValue>(&value).ok()?;
Some(value) Some(value)
} }
@@ -364,8 +370,6 @@ struct CacheValue {
impl CacheValue { impl CacheValue {
pub fn new(compiler_output: CompilerOutput) -> Self { pub fn new(compiler_output: CompilerOutput) -> Self {
Self { Self { compiler_output }
compiler_output,
}
} }
} }
+4 -2
View File
@@ -37,8 +37,10 @@ impl NodePool {
); );
} }
let pre_transactions_tasks = let pre_transactions_tasks = nodes
nodes.iter_mut().map(|node| node.pre_transactions()).collect::<Vec<_>>(); .iter_mut()
.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")?;
+58 -51
View File
@@ -66,68 +66,72 @@ 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.report_test_case_discovery_event().expect("Can't fail"); reporter
.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(move |(metadata_file, case_idx, case, mode, reporter)| async move { .filter_map(
let mut platforms = BTreeMap::new(); move |(metadata_file, case_idx, case, mode, reporter)| async move {
for (platform, node_pool) in platforms_and_nodes.values() { let mut platforms = BTreeMap::new();
let node = node_pool.round_robbin(); for (platform, node_pool) in platforms_and_nodes.values() {
let compiler = platform let node = node_pool.round_robbin();
.new_compiler(context.clone(), mode.version.clone().map(Into::into)) let compiler = platform
.await .new_compiler(context.clone(), mode.version.clone().map(Into::into))
.inspect_err(|err| { .await
error!( .inspect_err(|err| {
?err, error!(
platform_identifier = %platform.platform_identifier(), ?err,
"Failed to instantiate the compiler" platform_identifier = %platform.platform_identifier(),
"Failed to instantiate the compiler"
)
})
.ok()?;
reporter
.report_node_assigned_event(
node.id(),
platform.platform_identifier(),
node.connection_string(),
) )
}) .expect("Can't fail");
.ok()?;
reporter let reporter =
.report_node_assigned_event( reporter.execution_specific_reporter(node.id(), platform.platform_identifier());
node.id(),
platforms.insert(
platform.platform_identifier(), platform.platform_identifier(),
node.connection_string(), TestPlatformInformation {
) platform: *platform,
.expect("Can't fail"); node,
compiler,
reporter,
},
);
}
let reporter = Some(TestDefinition {
reporter.execution_specific_reporter(node.id(), platform.platform_identifier()); /* Metadata file information */
metadata: metadata_file,
metadata_file_path: metadata_file.metadata_file_path.as_path(),
platforms.insert( /* Mode Information */
platform.platform_identifier(), mode: mode.clone(),
TestPlatformInformation {
platform: *platform,
node,
compiler,
reporter,
},
);
}
Some(TestDefinition { /* Case Information */
/* Metadata file information */ case_idx: CaseIdx::new(case_idx),
metadata: metadata_file, case,
metadata_file_path: metadata_file.metadata_file_path.as_path(),
/* Mode Information */ /* Platform and Node Assignment Information */
mode: mode.clone(), platforms,
/* Case Information */ /* Reporter */
case_idx: CaseIdx::new(case_idx), reporter,
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() {
@@ -270,7 +274,10 @@ impl<'a> TestDefinition<'a> {
if is_allowed { if is_allowed {
Ok(()) Ok(())
} else { } else {
Err(("EVM version is incompatible for the platforms specified", error_map)) Err((
"EVM version is incompatible for the platforms specified",
error_map,
))
} }
} }
+35 -14
View File
@@ -38,7 +38,11 @@ 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.
@@ -180,7 +184,9 @@ 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).path.clone(); let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context)
.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(
@@ -230,7 +236,9 @@ 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).path.clone(); let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context)
.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(
@@ -280,8 +288,9 @@ 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 = let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context)
AsRef::<ReviveDevNodeConfiguration>::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(
@@ -331,8 +340,9 @@ 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 = let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context)
AsRef::<ReviveDevNodeConfiguration>::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(
@@ -382,8 +392,9 @@ 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 = let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context)
AsRef::<PolkadotParachainConfiguration>::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 = ZombieNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -429,8 +440,9 @@ 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 = let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context)
AsRef::<PolkadotParachainConfiguration>::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 = ZombieNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -509,8 +521,17 @@ 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!(id = node.id(), connection_string = node.connection_string(), "Spawning node"); info!(
node.spawn(genesis).context("Failed to spawn node process")?; id = node.id(),
info!(id = node.id(), connection_string = node.connection_string(), "Spawned node"); connection_string = node.connection_string(),
"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)
} }
+25 -19
View File
@@ -54,33 +54,39 @@ 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.clone().into_iter().enumerate().map(move |(idx, mut step)| { self.steps
let Step::FunctionCall(ref mut input) = step else { .clone()
return step; .into_iter()
}; .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.steps_iterator().any(|step| matches!(&step, Step::Repeat(..))); let contains_repeat = self
.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 {
+22 -40
View File
@@ -13,14 +13,8 @@ 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 { SinglePath { name: String, path: PathBuf },
name: String, MultiplePaths { name: String, paths: Vec<PathBuf> },
path: PathBuf,
},
MultiplePaths {
name: String,
paths: Vec<PathBuf>,
},
} }
impl Corpus { impl Corpus {
@@ -92,58 +86,46 @@ 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!(len = tests.len(), corpus_name = self.name(), "Found tests in Corpus"); info!(
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 { Corpus::SinglePath { name, .. } | Corpus::MultiplePaths { name, .. } => name.as_str(),
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 { Corpus::SinglePath { path, .. } => {
path, Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>>
.. }
} => Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>>, Corpus::MultiplePaths { paths, .. } => {
Corpus::MultiplePaths { Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>
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 { Corpus::SinglePath { path, .. } => {
path, Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>>
.. }
} => Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>>, Corpus::MultiplePaths { paths, .. } => {
Corpus::MultiplePaths { Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>
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 { Corpus::SinglePath { .. } => 1,
.. Corpus::MultiplePaths { paths, .. } => paths.len(),
} => 1,
Corpus::MultiplePaths {
paths,
..
} => paths.len(),
} }
} }
} }
+17 -5
View File
@@ -44,7 +44,9 @@ 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.strip_prefix(&self.corpus_file_path).unwrap() self.metadata_file_path
.strip_prefix(&self.corpus_file_path)
.unwrap()
} }
} }
} }
@@ -165,8 +167,10 @@ impl Metadata {
) in contracts ) in contracts
{ {
let alias = alias.clone(); let alias = alias.clone();
let absolute_path = let absolute_path = directory
directory.join(contract_source_path).canonicalize().map_err(|error| { .join(contract_source_path)
.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()
@@ -331,7 +335,12 @@ 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!(f, "{}:{}", self.contract_source_path.display(), self.contract_ident.as_ref()) write!(
f,
"{}:{}",
self.contract_source_path.display(),
self.contract_ident.as_ref()
)
} }
} }
@@ -587,7 +596,10 @@ mod test {
// Assert // Assert
let identifier = identifier.expect("Failed to parse"); let identifier = identifier.expect("Failed to parse");
assert_eq!(identifier.contract_source_path.display().to_string(), "ERC20/ERC20.sol"); assert_eq!(
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
+12 -11
View File
@@ -93,11 +93,7 @@ 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 { f.write_str(if optimize_flag { "+" } else { "-" })?;
"+"
} else {
"-"
})?;
} }
has_written = true; has_written = true;
} }
@@ -161,11 +157,13 @@ impl ParsedMode {
); );
pipeline_iter.flat_map(move |pipeline| { pipeline_iter.flat_map(move |pipeline| {
optimize_settings_iter.clone().map(move |optimize_setting| Mode { optimize_settings_iter
pipeline, .clone()
optimize_setting, .map(move |optimize_setting| Mode {
version: self.version.clone(), pipeline,
}) optimize_setting,
version: self.version.clone(),
})
}) })
} }
@@ -237,7 +235,10 @@ 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 {
+102 -23
View File
@@ -78,7 +78,12 @@ 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.iter().map(|idx| idx.to_string()).collect::<Vec<_>>().join(".").fmt(f) self.0
.iter()
.map(|idx| idx.to_string())
.collect::<Vec<_>>()
.join(".")
.fmt(f)
} }
} }
@@ -86,7 +91,10 @@ 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(".").map(StepIdx::from_str).collect::<anyhow::Result<Vec<_>>>().map(Self) s.split(".")
.map(StepIdx::from_str)
.collect::<anyhow::Result<Vec<_>>>()
.map(Self)
} }
} }
@@ -448,7 +456,9 @@ impl StepAddress {
impl FunctionCallStep { impl FunctionCallStep {
pub const fn default_caller_address() -> Address { pub const fn default_caller_address() -> Address {
Address(FixedBytes(alloy::hex!("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"))) Address(FixedBytes(alloy::hex!(
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"
)))
} }
pub const fn default_caller() -> StepAddress { pub const fn default_caller() -> StepAddress {
@@ -538,9 +548,11 @@ 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() let transaction_request = TransactionRequest::default().from(caller).value(
.from(caller) self.value
.value(self.value.map(|value| value.into_inner()).unwrap_or_default()); .map(|value| value.into_inner())
.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
@@ -596,7 +608,11 @@ 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.into_iter().map(|item| item.as_ref().to_owned()).map(CalldataItem::new).collect(), items
.into_iter()
.map(|item| item.as_ref().to_owned())
.map(CalldataItem::new)
.collect(),
) )
} }
@@ -618,7 +634,8 @@ 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).await?; self.calldata_into_slice(&mut buffer, resolver, context)
.await?;
Ok(buffer) Ok(buffer)
} }
@@ -709,7 +726,10 @@ 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.calldata_tokens().map(|token| token.resolve(resolver, context)) { for token in self
.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,
@@ -750,7 +770,9 @@ 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!("Invalid calldata arithmetic operation - Invalid stack")), _ => Err(anyhow::anyhow!(
"Invalid calldata arithmetic operation - Invalid stack"
)),
} }
} }
@@ -894,7 +916,10 @@ 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.variable(variable_name).context("Variable lookup failed").copied() context
.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))
@@ -1024,7 +1049,13 @@ 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.function("store").unwrap().first().unwrap().selector().0; let selector = parsed_abi
.function("store")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input = FunctionCallStep { let input = FunctionCallStep {
instance: ContractInstance::new("Contract"), instance: ContractInstance::new("Contract"),
@@ -1062,7 +1093,13 @@ 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.function("send").unwrap().first().unwrap().selector().0; let selector = parsed_abi
.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(),
@@ -1084,7 +1121,10 @@ 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!(decoded.0, address!("0x1000000000000000000000000000000000000001")); assert_eq!(
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
} }
#[tokio::test] #[tokio::test]
@@ -1100,7 +1140,13 @@ 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.function("send").unwrap().first().unwrap().selector().0; let selector = parsed_abi
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: FunctionCallStep = FunctionCallStep { let input: FunctionCallStep = FunctionCallStep {
instance: ContractInstance::new("Contract"), instance: ContractInstance::new("Contract"),
@@ -1122,7 +1168,10 @@ 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!(decoded.0, address!("0x1000000000000000000000000000000000000001")); assert_eq!(
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
} }
async fn resolve_calldata_item( async fn resolve_calldata_item(
@@ -1159,7 +1208,12 @@ 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(MockResolver.block_gas_limit(Default::default()).await.unwrap()) U256::from(
MockResolver
.block_gas_limit(Default::default())
.await
.unwrap()
)
) )
} }
@@ -1176,7 +1230,11 @@ mod tests {
assert_eq!( assert_eq!(
resolved, resolved,
U256::from_be_slice( U256::from_be_slice(
MockResolver.block_coinbase(Default::default()).await.unwrap().as_ref() MockResolver
.block_coinbase(Default::default())
.await
.unwrap()
.as_ref()
) )
) )
} }
@@ -1191,7 +1249,13 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(resolved, MockResolver.block_difficulty(Default::default()).await.unwrap()) assert_eq!(
resolved,
MockResolver
.block_difficulty(Default::default())
.await
.unwrap()
)
} }
#[tokio::test] #[tokio::test]
@@ -1206,7 +1270,11 @@ 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.block_base_fee(Default::default()).await.map(U256::from).unwrap() MockResolver
.block_base_fee(Default::default())
.await
.map(U256::from)
.unwrap()
) )
} }
@@ -1236,7 +1304,10 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap())) assert_eq!(
resolved,
U256::from(MockResolver.last_block_number().await.unwrap())
)
} }
#[tokio::test] #[tokio::test]
@@ -1251,7 +1322,12 @@ 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(MockResolver.block_timestamp(Default::default()).await.unwrap()) U256::from(
MockResolver
.block_timestamp(Default::default())
.await
.unwrap()
)
) )
} }
@@ -1329,7 +1405,10 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap() + 10)); assert_eq!(
resolved,
U256::from(MockResolver.last_block_number().await.unwrap() + 10)
);
} }
#[tokio::test] #[tokio::test]
+4 -2
View File
@@ -149,7 +149,8 @@ impl<'a> ResolutionContext<'a> {
&self, &self,
instance: &ContractInstance, instance: &ContractInstance,
) -> Option<&(ContractIdent, Address, JsonAbi)> { ) -> Option<&(ContractIdent, Address, JsonAbi)> {
self.deployed_contracts.and_then(|deployed_contracts| deployed_contracts.get(instance)) self.deployed_contracts
.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> {
@@ -161,7 +162,8 @@ 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.and_then(|variables| variables.get(name.as_ref())) self.variables
.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> {
+34 -14
View File
@@ -129,7 +129,10 @@ 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((file_display.clone(), format!("Error loading metadata: {}", e))); failures.push((
file_display.clone(),
format!("Error loading metadata: {}", e),
));
if args.bail { if args.bail {
break; break;
} }
@@ -229,7 +232,10 @@ fn discover_test_files(path: &Path) -> anyhow::Result<Vec<PathBuf>> {
files.push(metadata.metadata_file_path); files.push(metadata.metadata_file_path);
} }
} }
_ => anyhow::bail!("Unsupported file extension: {}. Expected .sol or .json", extension), _ => anyhow::bail!(
"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
@@ -286,8 +292,9 @@ async fn execute_test_file(
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 = let node_handle = platform
platform.new_node(context.clone()).context("Failed to spawn node thread")?; .new_node(context.clone())
.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
@@ -295,12 +302,18 @@ 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!("Node started with ID: {}, connection: {}", node.id(), node.connection_string()); info!(
"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().await.context("Failed to run pre-transactions")?; node.pre_transactions()
.await
.context("Failed to run pre-transactions")?;
info!("Pre-transactions completed"); info!("Pre-transactions completed");
node node
@@ -325,8 +338,9 @@ 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 = let private_key_allocator = Arc::new(Mutex::new(PrivateKeyAllocator::new(
Arc::new(Mutex::new(PrivateKeyAllocator::new(alloy::primitives::U256::from(100)))); 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
@@ -336,7 +350,10 @@ 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!("Building test definitions for {} case(s)", metadata_file.cases.len()); info!(
"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() {
@@ -385,17 +402,20 @@ 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 let steps_executed = driver.execute_all().await.context(format!(
.execute_all() "Failed to execute case {}",
.await test_definition.case_idx
.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!("All {} test case(s) executed successfully", test_definitions.len()); info!(
"All {} test case(s) executed successfully",
test_definitions.len()
);
Ok(()) Ok(())
} }
+24 -9
View File
@@ -33,7 +33,10 @@ 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) => (format!("{prefix}_stdout.log"), format!("{prefix}_stderr.log")), Some(prefix) => (
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()),
}; };
@@ -54,16 +57,20 @@ 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 = let stdout_logs_file = stdout_logs_file
stdout_logs_file.try_clone().context("Failed to clone the stdout logs file")?; .try_clone()
let stderr_logs_file = .context("Failed to clone the stdout logs file")?;
stderr_logs_file.try_clone().context("Failed to clone the stderr logs file")?; let stderr_logs_file = 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.spawn().context("Failed to spawn the built command")?; let mut child = command
.spawn()
.context("Failed to spawn the built command")?;
match process_readiness_wait_behavior { match process_readiness_wait_behavior {
ProcessReadinessWaitBehavior::NoStartupWait => {} ProcessReadinessWaitBehavior::NoStartupWait => {}
@@ -121,7 +128,11 @@ impl Process {
} }
} }
ProcessReadinessWaitBehavior::WaitForCommandToExit => { ProcessReadinessWaitBehavior::WaitForCommandToExit => {
if !child.wait().context("Failed waiting for process to finish")?.success() { if !child
.wait()
.context("Failed waiting for process to finish")?
.success()
{
anyhow::bail!("Failed to spawn command"); anyhow::bail!("Failed to spawn command");
} }
} }
@@ -138,8 +149,12 @@ 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.flush().expect("Failed to flush the stdout logs file"); self.stdout_logs_file
self.stderr_logs_file.flush().expect("Failed to flush the stderr logs file"); .flush()
.expect("Failed to flush the stdout logs file");
self.stderr_logs_file
.flush()
.expect("Failed to flush the stderr logs file");
} }
} }
+66 -23
View File
@@ -108,7 +108,9 @@ 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.as_path().join(Self::BASE_DIRECTORY); let geth_directory = working_directory_configuration
.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());
@@ -196,7 +198,11 @@ 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.wait().context("Failed waiting for geth --init process to finish")?.success() { if !child
.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);
} }
@@ -256,7 +262,8 @@ 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().context("Failed to gracefully shutdown after geth start error")?; self.shutdown()
.context("Failed to gracefully shutdown after geth start error")?;
return Err(err); return Err(err);
} }
} }
@@ -401,7 +408,10 @@ impl EthereumNode for GethNode {
} }
}, },
) )
.instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash)) .instrument(tracing::info_span!(
"Awaiting transaction receipt",
?transaction_hash
))
.await .await
}) })
} }
@@ -413,8 +423,10 @@ 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 = let provider = self
self.provider().await.context("Failed to create provider for tracing")?; .provider()
.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)),
@@ -422,7 +434,10 @@ 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.debug_trace_transaction(tx_hash, trace_options).await { match provider
.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();
@@ -502,10 +517,7 @@ 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 { Ok(Arc::new(GethNodeResolver { id, provider }) as Arc<dyn ResolverApi>)
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -648,7 +660,10 @@ 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.header.base_fee_per_gas.context("Failed to get the base fee per gas") block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -765,7 +780,11 @@ mod tests {
// Arrange // Arrange
let (context, node) = shared_state(); let (context, node) = shared_state();
let account_address = context.wallet_configuration.wallet().default_signer().address(); let account_address = context
.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));
@@ -788,7 +807,10 @@ mod tests {
// Assert // Assert
let version = version.expect("Failed to get the version"); let version = version.expect("Failed to get the version");
assert!(version.starts_with("geth version"), "expected version string, got: '{version}'"); assert!(
version.starts_with("geth version"),
"expected version string, got: '{version}'"
);
} }
#[tokio::test] #[tokio::test]
@@ -812,8 +834,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let gas_limit = let gas_limit = node
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -826,8 +852,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let coinbase = let coinbase = node
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -840,8 +870,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_difficulty = let block_difficulty = node
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -854,7 +888,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await; let block_hash = node
.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");
@@ -867,8 +906,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_timestamp = let block_timestamp = node
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await; .resolver()
.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,7 +132,9 @@ 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.as_path().join(Self::BASE_DIRECTORY); let geth_directory = working_directory_configuration
.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());
@@ -145,7 +147,10 @@ impl LighthouseGethNode {
http_connection_string: String::default(), http_connection_string: String::default(),
enclave_name: format!( enclave_name: format!(
"enclave-{}-{}", "enclave-{}-{}",
SystemTime::now().duration_since(UNIX_EPOCH).expect("Must not fail").as_nanos(), SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Must not fail")
.as_nanos(),
id id
), ),
@@ -178,7 +183,8 @@ 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().context("Failed to write the config file to the FS")?; self.init_kurtosis_config_file()
.context("Failed to write the config file to the FS")?;
Ok(self) Ok(self)
} }
@@ -420,8 +426,9 @@ 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()).enumerate().map( NetworkWallet::<Ethereum>::signer_addresses(self.wallet.as_ref())
|(nonce, address)| async move { .enumerate()
.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)
@@ -429,8 +436,7 @@ 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")?
@@ -525,7 +531,10 @@ impl LighthouseGethNode {
} }
}, },
) )
.instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash)) .instrument(tracing::info_span!(
"Awaiting transaction receipt",
?transaction_hash
))
.await .await
}) })
} }
@@ -614,7 +623,9 @@ 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().await.context("Failed to create provider for tracing")?, self.http_provider()
.await
.context("Failed to create provider for tracing")?,
); );
poll( poll(
Self::TRACE_POLLING_DURATION, Self::TRACE_POLLING_DURATION,
@@ -623,7 +634,10 @@ 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.debug_trace_transaction(tx_hash, trace_options).await { match provider
.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();
@@ -703,10 +717,7 @@ 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 { Ok(Arc::new(LighthouseGethNodeResolver { id, provider }) as Arc<dyn ResolverApi>)
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -848,7 +859,10 @@ 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.header.base_fee_per_gas.context("Failed to get the base fee per gas") block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -902,7 +916,11 @@ impl Node for LighthouseGethNode {
.spawn() .spawn()
.expect("Failed to spawn the enclave kill command"); .expect("Failed to spawn the enclave kill command");
if !child.wait().expect("Failed to wait for the enclave kill command").success() { if !child
.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
@@ -1128,7 +1146,11 @@ 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.wallet_configuration.wallet().default_signer().address(); let account_address = context
.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));
@@ -1151,7 +1173,10 @@ mod tests {
// Assert // Assert
let version = version.expect("Failed to get the version"); let version = version.expect("Failed to get the version");
assert!(version.starts_with("CLI Version"), "expected version string, got: '{version}'"); assert!(
version.starts_with("CLI Version"),
"expected version string, got: '{version}'"
);
} }
#[tokio::test] #[tokio::test]
@@ -1175,8 +1200,12 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let gas_limit = let gas_limit = node
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1189,8 +1218,12 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let coinbase = let coinbase = node
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1203,8 +1236,12 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_difficulty = let block_difficulty = node
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1217,7 +1254,12 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await; let block_hash = node
.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");
@@ -1230,8 +1272,12 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_timestamp = let block_timestamp = node
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -108,7 +108,9 @@ 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).path.as_path(); let eth_rpc_path = AsRef::<EthRpcConfiguration>::as_ref(&context)
.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);
@@ -325,12 +327,15 @@ impl SubstrateNode {
&self, &self,
genesis: &Genesis, genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> { ) -> anyhow::Result<Vec<(String, u128)>> {
genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| { genesis
let substrate_address = Self::eth_to_substrate_address(address); .alloc
let balance = acc.balance.try_into()?; .iter()
vec.push((substrate_address, balance)); .try_fold(Vec::new(), |mut vec, (address, acc)| {
Ok(vec) let substrate_address = Self::eth_to_substrate_address(address);
}) 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 {
@@ -425,7 +430,10 @@ 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.provider().await.context("Failed to create the provider")?; let provider = self
.provider()
.await
.context("Failed to create the provider")?;
execute_transaction(provider, transaction).await execute_transaction(provider, transaction).await
}) })
} }
@@ -502,10 +510,7 @@ 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 { Ok(Arc::new(SubstrateNodeResolver { id, provider }) as Arc<dyn ResolverApi>)
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -526,8 +531,10 @@ 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 = let mut block_subscription = provider
provider.watch_full_blocks().await.context("Failed to create the blocks stream")?; .watch_full_blocks()
.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();
@@ -648,7 +655,10 @@ 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.header.base_fee_per_gas.context("Failed to get the base fee per gas") block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -927,26 +937,25 @@ 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 { Err(UnbuiltTransactionError { request, error }) => {
request, Err(UnbuiltTransactionError::<ReviveNetwork> {
error, request,
}) => Err(UnbuiltTransactionError::<ReviveNetwork> { error: match error {
request, TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => {
error: match error { TransactionBuilderError::InvalidTransactionRequest(tx_type, items)
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => { }
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) TransactionBuilderError::UnsupportedSignatureType => {
} TransactionBuilderError::UnsupportedSignatureType
TransactionBuilderError::UnsupportedSignatureType => { }
TransactionBuilderError::UnsupportedSignatureType TransactionBuilderError::Signer(error) => {
} TransactionBuilderError::Signer(error)
TransactionBuilderError::Signer(error) => { }
TransactionBuilderError::Signer(error) TransactionBuilderError::Custom(error) => {
} TransactionBuilderError::Custom(error)
TransactionBuilderError::Custom(error) => { }
TransactionBuilderError::Custom(error) },
} })
}, }
}),
} }
} }
@@ -1214,7 +1223,11 @@ 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.wallet_configuration.wallet().default_signer().address(); let account_address = context
.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));
@@ -1254,11 +1267,14 @@ mod tests {
); );
// Call `init()` // Call `init()`
dummy_node.init(serde_json::from_str(genesis_content).unwrap()).expect("init failed"); dummy_node
.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 = let final_chainspec_path = dummy_node
dummy_node.base_directory.join(SubstrateNode::CHAIN_SPEC_JSON_FILE); .base_directory
.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");
@@ -1364,7 +1380,10 @@ 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!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}"); assert_eq!(
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
} }
} }
@@ -1415,8 +1434,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let gas_limit = let gas_limit = node
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1429,8 +1452,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let coinbase = let coinbase = node
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1443,8 +1470,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_difficulty = let block_difficulty = node
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1457,7 +1488,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await; let block_hash = node
.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");
@@ -1470,8 +1506,12 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_timestamp = let block_timestamp = node
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await; .resolver()
.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,10 +130,14 @@ impl ZombieNode {
+ AsRef<EthRpcConfiguration> + AsRef<EthRpcConfiguration>
+ AsRef<WalletConfiguration>, + AsRef<WalletConfiguration>,
) -> Self { ) -> Self {
let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context).path.to_owned(); let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context)
.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.join(Self::BASE_DIRECTORY).join(id.to_string()); let base_directory = working_directory_path
.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();
@@ -165,8 +169,10 @@ 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 = let polkadot_parachain_path = self
self.polkadot_parachain_path.to_str().context("Invalid polkadot parachain path")?; .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;
@@ -198,8 +204,10 @@ impl ZombieNode {
} }
fn spawn_process(&mut self) -> anyhow::Result<()> { fn spawn_process(&mut self) -> anyhow::Result<()> {
let network_config = let network_config = self
self.network_config.clone().context("Node not initialized, call init() first")?; .network_config
.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 {
@@ -264,12 +272,17 @@ 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).arg("--chain").arg("asset-hub-westend-local"); cmd.arg(Self::EXPORT_CHAINSPEC_COMMAND)
.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!("Build chain-spec failed: {}", String::from_utf8_lossy(&output.stderr)); anyhow::bail!(
"Build chain-spec failed: {}",
String::from_utf8_lossy(&output.stderr)
);
} }
let content = String::from_utf8(output.stdout) let content = String::from_utf8(output.stdout)
@@ -330,12 +343,15 @@ impl ZombieNode {
&self, &self,
genesis: &Genesis, genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> { ) -> anyhow::Result<Vec<(String, u128)>> {
genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| { genesis
let polkadot_address = Self::eth_to_polkadot_address(address); .alloc
let balance = acc.balance.try_into()?; .iter()
vec.push((polkadot_address, balance)); .try_fold(Vec::new(), |mut vec, (address, acc)| {
Ok(vec) let polkadot_address = Self::eth_to_polkadot_address(address);
}) 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 {
@@ -519,10 +535,7 @@ 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 { Ok(Arc::new(ZombieNodeResolver { id, provider }) as Arc<dyn ResolverApi>)
id,
provider,
}) as Arc<dyn ResolverApi>)
}) })
} }
@@ -543,8 +556,10 @@ 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 = let mut block_subscription = provider
provider.watch_full_blocks().await.context("Failed to create the blocks stream")?; .watch_full_blocks()
.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();
@@ -667,7 +682,10 @@ 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.header.base_fee_per_gas.context("Failed to get the base fee per gas") block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -779,8 +797,10 @@ 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 = let mut node = ZombieNode::new(
ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context); context.polkadot_parachain_configuration.path.clone(),
&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();
@@ -845,11 +865,14 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let mut node = let mut node = ZombieNode::new(
ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context); context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Call `init()` // Call `init()`
node.init(serde_json::from_str(genesis_content).unwrap()).expect("init failed"); node.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 = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE); let final_chainspec_path = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE);
@@ -890,7 +913,10 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context); let node = ZombieNode::new(
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())
@@ -952,7 +978,10 @@ 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!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}"); assert_eq!(
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
} }
} }
@@ -960,7 +989,10 @@ 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(context.polkadot_parachain_configuration.path.clone(), &context); let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act // Act
let version = node.eth_rpc_version().unwrap(); let version = node.eth_rpc_version().unwrap();
@@ -976,7 +1008,10 @@ mod tests {
fn version_works() { fn version_works() {
// Arrange // Arrange
let context = test_config(); let context = test_config();
let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context); let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act // Act
let version = node.version().unwrap(); let version = node.version().unwrap();
@@ -1014,8 +1049,12 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let gas_limit = let gas_limit = node
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1028,8 +1067,12 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let coinbase = let coinbase = node
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1042,8 +1085,12 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_difficulty = let block_difficulty = node
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await; .resolver()
.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");
@@ -1056,7 +1103,12 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await; let block_hash = node
.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");
@@ -1069,8 +1121,12 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_timestamp = let block_timestamp = node
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await; .resolver()
.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,7 +55,10 @@ where
let future = self.service.call(req); let future = self.service.call(req);
Box::pin(async move { Box::pin(async move {
let _permit = semaphore.acquire().await.expect("Semaphore has been closed"); let _permit = semaphore
.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"
+19 -14
View File
@@ -80,8 +80,10 @@ where
NonceFiller: TxFiller<N>, NonceFiller: TxFiller<N>,
WalletFiller<W>: TxFiller<N>, WalletFiller<W>: TxFiller<N>,
{ {
let sendable_transaction = let sendable_transaction = provider
provider.fill(transaction).await.context("Failed to fill transaction")?; .fill(transaction)
.await
.context("Failed to fill transaction")?;
let transaction_envelope = sendable_transaction let transaction_envelope = sendable_transaction
.try_into_envelope() .try_into_envelope()
@@ -103,21 +105,24 @@ 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 let tx_hash = pending_transaction.watch().await.context(format!(
.watch() "Transaction inclusion watching timeout for {tx_hash}"
.await ))?;
.context(format!("Transaction inclusion watching timeout for {tx_hash}"))?;
poll(Duration::from_secs(60), PollingWaitBehavior::Constant(Duration::from_secs(3)), || { poll(
let provider = provider.clone(); Duration::from_secs(60),
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}"))
} }
+36 -10
View File
@@ -123,8 +123,12 @@ impl ReportAggregator {
file_name.push_str(".json"); file_name.push_str(".json");
file_name file_name
}; };
let file_path = let file_path = self
self.report.context.working_directory_configuration().as_path().join(file_name); .report
.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)
@@ -132,7 +136,10 @@ impl ReportAggregator {
.read(false) .read(false)
.open(&file_path) .open(&file_path)
.with_context(|| { .with_context(|| {
format!("Failed to open report file for writing: {}", file_path.display()) format!(
"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())
@@ -234,7 +241,10 @@ 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 {
@@ -266,8 +276,16 @@ impl ReportAggregator {
&mut self, &mut self,
event: PreLinkContractsCompilationSucceededEvent, event: PreLinkContractsCompilationSucceededEvent,
) { ) {
let include_input = self.report.context.report_configuration().include_compiler_input; let include_input = self
let include_output = self.report.context.report_configuration().include_compiler_output; .report
.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);
@@ -295,8 +313,16 @@ impl ReportAggregator {
&mut self, &mut self,
event: PostLinkContractsCompilationSucceededEvent, event: PostLinkContractsCompilationSucceededEvent,
) { ) {
let include_input = self.report.context.report_configuration().include_compiler_input; let include_input = self
let include_output = self.report.context.report_configuration().include_compiler_output; .report
.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);
@@ -349,8 +375,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).deployed_libraries = self.execution_information(&event.execution_specifier)
Some(event.libraries); .deployed_libraries = Some(event.libraries);
} }
fn handle_contract_deployed_event(&mut self, event: ContractDeployedEvent) { fn handle_contract_deployed_event(&mut self, event: ContractDeployedEvent) {
+30 -9
View File
@@ -22,8 +22,9 @@ 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 = let target_directory = working_directory
working_directory.join(SOLC_CACHE_DIRECTORY).join(downloader.version.to_string()); .join(SOLC_CACHE_DIRECTORY)
.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;
@@ -33,11 +34,19 @@ pub(crate) async fn get_or_download(
} }
create_dir_all(&target_directory).with_context(|| { create_dir_all(&target_directory).with_context(|| {
format!("Failed to create solc cache directory: {}", target_directory.display()) format!(
"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(|| format!("Failed to write downloaded solc to {}", target_file.display()))?; .with_context(|| {
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))
@@ -61,9 +70,15 @@ 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(&downloader.download().await.context("Failed to download solc binary bytes")?) file.write_all(
.with_context(|| format!("Failed to write solc binary to {}", path.display()))?; &downloader
file.flush().with_context(|| format!("Failed to flush file {}", path.display()))?; .download()
.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")]
@@ -76,11 +91,17 @@ 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!("Failed to spawn xattr to remove quarantine attribute on {}", path.display()) format!(
"Failed to spawn xattr to remove quarantine attribute on {}",
path.display()
)
})? })?
.wait() .wait()
.with_context(|| { .with_context(|| {
format!("Failed waiting for xattr operation to complete on {}", path.display()) format!(
"Failed waiting for xattr operation to complete on {}",
path.display()
)
})?; })?;
Ok(()) Ok(())
+41 -8
View File
@@ -130,7 +130,11 @@ impl SolcDownloader {
})?; })?;
let path = build.path.clone(); let path = build.path.clone();
let expected_digest = build.sha256.strip_prefix("0x").unwrap_or(&build.sha256).to_string(); let expected_digest = build
.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)
@@ -155,25 +159,54 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn try_get_windows() { async fn try_get_windows() {
let version = List::download(List::WINDOWS_URL).await.unwrap().latest_release; let version = List::download(List::WINDOWS_URL)
SolcDownloader::windows(version).await.unwrap().download().await.unwrap(); .await
.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).await.unwrap().latest_release; let version = List::download(List::MACOSX_URL)
SolcDownloader::macosx(version).await.unwrap().download().await.unwrap(); .await
.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).await.unwrap().latest_release; let version = List::download(List::LINUX_URL)
SolcDownloader::linux(version).await.unwrap().download().await.unwrap(); .await
.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).await.unwrap().download().await.unwrap(); SolcDownloader::wasm(version)
.await
.unwrap()
.download()
.await
.unwrap();
} }
} }