Compare commits

..

7 Commits

Author SHA1 Message Date
pgherveou 1659164310 save before flight 2025-10-13 13:05:07 +02:00
pgherveou 0a68800856 nit 2025-10-08 18:26:43 +02:00
pgherveou 8303d789cd use 10^6 for gas filler 2025-10-08 15:15:08 +02:00
pgherveou 40bf44fe58 fix 2025-10-08 14:50:50 +02:00
pgherveou ba8ad03290 fix 2025-10-08 14:06:03 +02:00
pgherveou 3dd99f3ac8 Merge branch 'pg/fmt' into pg/ml-runner 2025-10-08 11:42:37 +00:00
pgherveou 6618463c68 fix 2025-10-08 11:40:08 +00:00
57 changed files with 12195 additions and 12814 deletions
-19
View File
@@ -18,28 +18,9 @@ env:
POLKADOT_VERSION: polkadot-stable2506-2 POLKADOT_VERSION: polkadot-stable2506-2
jobs: jobs:
fmt:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install nightly toolchain
run: rustup toolchain install nightly
- name: Install rustfmt for nightly
run: rustup component add --toolchain nightly rustfmt
- name: Cargo fmt
run: cargo +nightly fmt --all -- --check
cache-polkadot: cache-polkadot:
name: Build and cache Polkadot binaries on ${{ matrix.os }} name: Build and cache Polkadot binaries on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
needs: [fmt]
strategy: strategy:
matrix: matrix:
os: [ubuntu-24.04, macos-14] os: [ubuntu-24.04, macos-14]
+25
View File
@@ -0,0 +1,25 @@
# Basic
edition = "2024"
hard_tabs = true
max_width = 100
use_small_heuristics = "Max"
# Imports
imports_granularity = "Crate"
reorder_imports = true
# Consistency
newline_style = "Unix"
# Misc
chain_width = 80
spaces_around_ranges = false
binop_separator = "Back"
reorder_impl_items = false
match_arm_leading_pipes = "Preserve"
match_arm_blocks = false
match_block_trailing_comma = true
trailing_comma = "Vertical"
trailing_semicolon = false
use_field_init_shorthand = true
# Format comments
comment_width = 100
wrap_comments = true
+1 -1
View File
@@ -1,7 +1,7 @@
.PHONY: format clippy test machete .PHONY: format clippy test machete
format: format:
cargo fmt --all -- --check cargo +nightly fmt --all -- --check
clippy: clippy:
cargo clippy --all-features --workspace -- --deny warnings cargo clippy --all-features --workspace -- --deny warnings
+3 -6
View File
@@ -20,17 +20,14 @@ pub fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
let content = fs::read(path.as_path())?; let content = fs::read(path.as_path())?;
READ_CACHE.insert(path, content.clone()); READ_CACHE.insert(path, content.clone());
Ok(content) Ok(content)
} },
} }
} }
pub fn read_to_string(path: impl AsRef<Path>) -> Result<String> { pub fn read_to_string(path: impl AsRef<Path>) -> Result<String> {
let content = read(path)?; let content = read(path)?;
String::from_utf8(content).map_err(|_| { String::from_utf8(content).map_err(|_| {
Error::new( Error::new(std::io::ErrorKind::InvalidData, "The contents of the file are not valid UTF8")
std::io::ErrorKind::InvalidData,
"The contents of the file are not valid UTF8",
)
}) })
} }
@@ -46,6 +43,6 @@ pub fn read_dir(path: impl AsRef<Path>) -> Result<Box<dyn Iterator<Item = Result
.collect(); .collect();
READ_DIR_CACHE.insert(path.clone(), entries); READ_DIR_CACHE.insert(path.clone(), entries);
Ok(read_dir(path).unwrap()) Ok(read_dir(path).unwrap())
} },
} }
} }
+1 -4
View File
@@ -12,10 +12,7 @@ pub fn clear_directory(path: impl AsRef<Path>) -> Result<()> {
.with_context(|| format!("Failed to read directory: {}", path.as_ref().display()))? .with_context(|| format!("Failed to read directory: {}", path.as_ref().display()))?
{ {
let entry = entry.with_context(|| { let entry = entry.with_context(|| {
format!( format!("Failed to read an entry in directory: {}", path.as_ref().display())
"Failed to read an entry in directory: {}",
path.as_ref().display()
)
})?; })?;
let entry_path = entry.path(); let entry_path = entry.path();
+5 -9
View File
@@ -37,17 +37,13 @@ where
)); ));
} }
match future() match future().await.context("Polled future returned an error during polling loop")? {
.await
.context("Polled future returned an error during polling loop")?
{
ControlFlow::Continue(()) => { ControlFlow::Continue(()) => {
let next_wait_duration = match polling_wait_behavior { let next_wait_duration = match polling_wait_behavior {
PollingWaitBehavior::Constant(duration) => duration, PollingWaitBehavior::Constant(duration) => duration,
PollingWaitBehavior::ExponentialBackoff => { PollingWaitBehavior::ExponentialBackoff =>
Duration::from_secs(2u64.pow(retries)) Duration::from_secs(2u64.pow(retries))
.min(EXPONENTIAL_BACKOFF_MAX_WAIT_DURATION) .min(EXPONENTIAL_BACKOFF_MAX_WAIT_DURATION),
}
}; };
let next_wait_duration = let next_wait_duration =
next_wait_duration.min(max_allowed_wait_duration - total_wait_duration); next_wait_duration.min(max_allowed_wait_duration - total_wait_duration);
@@ -55,10 +51,10 @@ where
retries += 1; retries += 1;
tokio::time::sleep(next_wait_duration).await; tokio::time::sleep(next_wait_duration).await;
} },
ControlFlow::Break(output) => { ControlFlow::Break(output) => {
break Ok(output); break Ok(output);
} },
} }
} }
} }
@@ -75,13 +75,12 @@ impl Iterator for FilesWithExtensionIterator {
for entry_path in iterator.flatten() { for entry_path in iterator.flatten() {
if entry_path.is_dir() { if entry_path.is_dir() {
self.directories_to_search.push(entry_path) self.directories_to_search.push(entry_path)
} else if entry_path.is_file() } else if entry_path.is_file() &&
&& entry_path.extension().is_some_and(|ext| { entry_path.extension().is_some_and(|ext| {
self.allowed_extensions self.allowed_extensions
.iter() .iter()
.any(|allowed| ext.eq_ignore_ascii_case(allowed.as_ref())) .any(|allowed| ext.eq_ignore_ascii_case(allowed.as_ref()))
}) }) {
{
self.files_matching_allowed_extensions.push(entry_path) self.files_matching_allowed_extensions.push(entry_path)
} }
} }
+1 -3
View File
@@ -76,9 +76,7 @@ impl FromStr for ModePipeline {
// Don't go via Yul IR // Don't go via Yul IR
"E" => Ok(ModePipeline::ViaEVMAssembly), "E" => Ok(ModePipeline::ViaEVMAssembly),
// Anything else that we see isn't a mode at all // Anything else that we see isn't a mode at all
_ => Err(anyhow::anyhow!( _ => Err(anyhow::anyhow!("Unsupported pipeline '{s}': expected 'Y' or 'E'")),
"Unsupported pipeline '{s}': expected 'Y' or 'E'"
)),
} }
} }
} }
@@ -15,10 +15,7 @@ pub struct PrivateKeyAllocator {
impl PrivateKeyAllocator { impl PrivateKeyAllocator {
/// Creates a new instance of the private key allocator. /// Creates a new instance of the private key allocator.
pub fn new(highest_private_key_inclusive: U256) -> Self { pub fn new(highest_private_key_inclusive: U256) -> Self {
Self { Self { next_private_key: U256::ONE, highest_private_key_inclusive }
next_private_key: U256::ONE,
highest_private_key_inclusive,
}
} }
/// Allocates a new private key and errors out if the maximum private key has been reached. /// Allocates a new private key and errors out if the maximum private key has been reached.
+1 -4
View File
@@ -7,10 +7,7 @@ pub struct RoundRobinPool<T> {
impl<T> RoundRobinPool<T> { impl<T> RoundRobinPool<T> {
pub fn new(items: Vec<T>) -> Self { pub fn new(items: Vec<T>) -> Self {
Self { Self { next_index: Default::default(), items }
next_index: Default::default(),
items,
}
} }
pub fn round_robin(&self) -> &T { pub fn round_robin(&self) -> &T {
+13 -20
View File
@@ -60,10 +60,7 @@ impl Resolc {
Ok(COMPILERS_CACHE Ok(COMPILERS_CACHE
.entry(solc.clone()) .entry(solc.clone())
.or_insert_with(|| { .or_insert_with(|| {
Self(Arc::new(ResolcInner { Self(Arc::new(ResolcInner { solc, resolc_path: resolc_configuration.path.clone() }))
solc,
resolc_path: resolc_configuration.path.clone(),
}))
}) })
.clone()) .clone())
} }
@@ -141,9 +138,7 @@ impl SolidityCompiler for Resolc {
output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()), output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()),
via_ir: Some(true), via_ir: Some(true),
optimizer: SolcStandardJsonInputSettingsOptimizer::new( optimizer: SolcStandardJsonInputSettingsOptimizer::new(
optimization optimization.unwrap_or(ModeOptimizerSetting::M0).optimizations_enabled(),
.unwrap_or(ModeOptimizerSetting::M0)
.optimizations_enabled(),
None, None,
&Version::new(0, 0, 0), &Version::new(0, 0, 0),
false, false,
@@ -253,16 +248,14 @@ impl SolidityCompiler for Resolc {
.evm .evm
.and_then(|evm| evm.bytecode.clone()) .and_then(|evm| evm.bytecode.clone())
.context("Unexpected - Contract compiled with resolc has no bytecode")?; .context("Unexpected - Contract compiled with resolc has no bytecode")?;
let abi = let abi = {
{
let metadata = contract_information let metadata = contract_information
.metadata .metadata
.as_ref() .as_ref()
.context("No metadata found for the contract")?; .context("No metadata found for the contract")?;
let solc_metadata_str = match metadata { let solc_metadata_str = match metadata {
serde_json::Value::String(solc_metadata_str) => { serde_json::Value::String(solc_metadata_str) =>
solc_metadata_str.as_str() solc_metadata_str.as_str(),
}
serde_json::Value::Object(metadata_object) => { serde_json::Value::Object(metadata_object) => {
let solc_metadata_value = metadata_object let solc_metadata_value = metadata_object
.get("solc_metadata") .get("solc_metadata")
@@ -270,13 +263,13 @@ impl SolidityCompiler for Resolc {
solc_metadata_value solc_metadata_value
.as_str() .as_str()
.context("The 'solc_metadata' field is not a string")? .context("The 'solc_metadata' field is not a string")?
} },
serde_json::Value::Null serde_json::Value::Null |
| serde_json::Value::Bool(_) serde_json::Value::Bool(_) |
| serde_json::Value::Number(_) serde_json::Value::Number(_) |
| serde_json::Value::Array(_) => { serde_json::Value::Array(_) => {
anyhow::bail!("Unsupported type of metadata {metadata:?}") anyhow::bail!("Unsupported type of metadata {metadata:?}")
} },
}; };
let solc_metadata = let solc_metadata =
serde_json::from_str::<serde_json::Value>(solc_metadata_str).context( serde_json::from_str::<serde_json::Value>(solc_metadata_str).context(
@@ -304,7 +297,7 @@ impl SolidityCompiler for Resolc {
optimize_setting: ModeOptimizerSetting, optimize_setting: ModeOptimizerSetting,
pipeline: ModePipeline, pipeline: ModePipeline,
) -> bool { ) -> bool {
pipeline == ModePipeline::ViaYulIR pipeline == ModePipeline::ViaYulIR &&
&& SolidityCompiler::supports_mode(&self.0.solc, optimize_setting, pipeline) SolidityCompiler::supports_mode(&self.0.solc, optimize_setting, pipeline)
} }
} }
+5 -13
View File
@@ -56,9 +56,7 @@ impl Solc {
// resolution for us. Therefore, even if the download didn't proceed, this function will // resolution for us. Therefore, even if the download didn't proceed, this function will
// resolve the version requirement into a canonical version of the compiler. It's then up // resolve the version requirement into a canonical version of the compiler. It's then up
// to us to either use the provided path or not. // to us to either use the provided path or not.
let version = version let version = version.into().unwrap_or_else(|| solc_configuration.version.clone().into());
.into()
.unwrap_or_else(|| solc_configuration.version.clone().into());
let (version, path) = let (version, path) =
download_solc(working_directory_configuration.as_path(), version, false) download_solc(working_directory_configuration.as_path(), version, false)
.await .await
@@ -72,10 +70,7 @@ impl Solc {
solc_version = %version, solc_version = %version,
"Created a new solc compiler object" "Created a new solc compiler object"
); );
Self(Arc::new(SolcInner { Self(Arc::new(SolcInner { solc_path: path, solc_version: version }))
solc_path: path,
solc_version: version,
}))
}) })
.clone()) .clone())
} }
@@ -252,10 +247,7 @@ impl SolidityCompiler for Solc {
let map = compiler_output let map = compiler_output
.contracts .contracts
.entry(contract_path.canonicalize().with_context(|| { .entry(contract_path.canonicalize().with_context(|| {
format!( format!("Failed to canonicalize contract path {}", contract_path.display())
"Failed to canonicalize contract path {}",
contract_path.display()
)
})?) })?)
.or_default(); .or_default();
for (contract_name, contract_info) in contracts.into_iter() { for (contract_name, contract_info) in contracts.into_iter() {
@@ -286,8 +278,8 @@ impl SolidityCompiler for Solc {
// solc 0.8.13 and above supports --via-ir, and less than that does not. Thus, we support // solc 0.8.13 and above supports --via-ir, and less than that does not. Thus, we support
// mode E (ie no Yul IR) in either case, but only support Y (via Yul IR) if the compiler // mode E (ie no Yul IR) in either case, but only support Y (via Yul IR) if the compiler
// is new enough. // is new enough.
pipeline == ModePipeline::ViaEVMAssembly pipeline == ModePipeline::ViaEVMAssembly ||
|| (pipeline == ModePipeline::ViaYulIR && self.compiler_supports_yul()) (pipeline == ModePipeline::ViaYulIR && self.compiler_supports_yul())
} }
} }
+7 -20
View File
@@ -154,7 +154,7 @@ impl AsRef<GenesisConfiguration> for Context {
Self::Benchmark(..) => { Self::Benchmark(..) => {
static GENESIS: LazyLock<GenesisConfiguration> = LazyLock::new(Default::default); static GENESIS: LazyLock<GenesisConfiguration> = LazyLock::new(Default::default);
&GENESIS &GENESIS
} },
Self::ExportJsonSchema => unreachable!(), Self::ExportJsonSchema => unreachable!(),
} }
} }
@@ -626,11 +626,7 @@ pub struct KurtosisConfiguration {
/// ///
/// If this is not specified, then the tool assumes that it should use the kurtosis binary /// If this is not specified, then the tool assumes that it should use the kurtosis binary
/// that's provided in the user's $PATH. /// that's provided in the user's $PATH.
#[clap( #[clap(id = "kurtosis.path", long = "kurtosis.path", default_value = "kurtosis")]
id = "kurtosis.path",
long = "kurtosis.path",
default_value = "kurtosis"
)]
pub path: PathBuf, pub path: PathBuf,
} }
@@ -641,11 +637,7 @@ pub struct KitchensinkConfiguration {
/// ///
/// If this is not specified, then the tool assumes that it should use the kitchensink binary /// If this is not specified, then the tool assumes that it should use the kitchensink binary
/// that's provided in the user's $PATH. /// that's provided in the user's $PATH.
#[clap( #[clap(id = "kitchensink.path", long = "kitchensink.path", default_value = "substrate-node")]
id = "kitchensink.path",
long = "kitchensink.path",
default_value = "substrate-node"
)]
pub path: PathBuf, pub path: PathBuf,
/// The amount of time to wait upon startup before considering that the node timed out. /// The amount of time to wait upon startup before considering that the node timed out.
@@ -731,11 +723,11 @@ impl GenesisConfiguration {
Some(genesis_path) => { Some(genesis_path) => {
let genesis_content = read_to_string(genesis_path)?; let genesis_content = read_to_string(genesis_path)?;
serde_json::from_str(genesis_content.as_str())? serde_json::from_str(genesis_content.as_str())?
} },
None => DEFAULT_GENESIS.clone(), None => DEFAULT_GENESIS.clone(),
}; };
Ok(self.genesis.get_or_init(|| genesis)) Ok(self.genesis.get_or_init(|| genesis))
} },
} }
} }
} }
@@ -823,10 +815,7 @@ impl ConcurrencyConfiguration {
pub fn concurrency_limit(&self) -> Option<usize> { pub fn concurrency_limit(&self) -> Option<usize> {
match self.ignore_concurrency_limit { match self.ignore_concurrency_limit {
true => None, true => None,
false => Some( false => Some(self.number_concurrent_tasks.unwrap_or(20 * self.number_of_nodes)),
self.number_concurrent_tasks
.unwrap_or(20 * self.number_of_nodes),
),
} }
} }
} }
@@ -917,9 +906,7 @@ impl Serialize for WorkingDirectoryConfiguration {
} }
fn parse_duration(s: &str) -> anyhow::Result<Duration> { fn parse_duration(s: &str) -> anyhow::Result<Duration> {
u64::from_str(s) u64::from_str(s).map(Duration::from_millis).map_err(Into::into)
.map(Duration::from_millis)
.map_err(Into::into)
} }
/// The Solidity compatible node implementation. /// The Solidity compatible node implementation.
@@ -206,9 +206,7 @@ where
"Deployed library" "Deployed library"
); );
let library_address = receipt let library_address = receipt.contract_address.expect("Failed to deploy the library");
.contract_address
.expect("Failed to deploy the library");
deployed_libraries.get_or_insert_default().insert( deployed_libraries.get_or_insert_default().insert(
library_instance.clone(), library_instance.clone(),
@@ -236,10 +234,8 @@ where
}) })
.context("Failed to compile the post-link contracts")?; .context("Failed to compile the post-link contracts")?;
self.execution_state = ExecutionState::new( self.execution_state =
compiler_output.contracts, ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default());
deployed_libraries.unwrap_or_default(),
);
Ok(()) Ok(())
} }
@@ -325,11 +321,7 @@ where
) -> Result<HashMap<ContractInstance, TransactionReceipt>> { ) -> Result<HashMap<ContractInstance, TransactionReceipt>> {
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new(); let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
for instance in step.find_all_contract_instances().into_iter() { for instance in step.find_all_contract_instances().into_iter() {
if !self if !self.execution_state.deployed_contracts.contains_key(&instance) {
.execution_state
.deployed_contracts
.contains_key(&instance)
{
instances_we_must_deploy.entry(instance).or_insert(false); instances_we_must_deploy.entry(instance).or_insert(false);
} }
} }
@@ -341,15 +333,11 @@ where
let mut receipts = HashMap::new(); let mut receipts = HashMap::new();
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() { for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
let calldata = deploy_with_constructor_arguments.then_some(&step.calldata); let calldata = deploy_with_constructor_arguments.then_some(&step.calldata);
let value = deploy_with_constructor_arguments let value = deploy_with_constructor_arguments.then_some(step.value).flatten();
.then_some(step.value)
.flatten();
let caller = { let caller = {
let context = self.default_resolution_context(); let context = self.default_resolution_context();
step.caller step.caller.resolve_address(self.resolver.as_ref(), context).await?
.resolve_address(self.resolver.as_ref(), context)
.await?
}; };
if let (_, _, Some(receipt)) = self if let (_, _, Some(receipt)) = self
.get_or_deploy_contract_instance(&instance, caller, calldata, value) .get_or_deploy_contract_instance(&instance, caller, calldata, value)
@@ -379,7 +367,7 @@ where
.as_transaction(self.resolver.as_ref(), self.default_resolution_context()) .as_transaction(self.resolver.as_ref(), self.default_resolution_context())
.await?; .await?;
self.execute_transaction(tx).await self.execute_transaction(tx).await
} },
} }
} }
@@ -424,18 +412,13 @@ where
}; };
// Handling the return data variable assignments. // Handling the return data variable assignments.
for (variable_name, output_word) in assignments.return_data.iter().zip( for (variable_name, output_word) in assignments
tracing_result .return_data
.output .iter()
.as_ref() .zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32))
.unwrap_or_default() {
.to_vec()
.chunks(32),
) {
let value = U256::from_be_slice(output_word); let value = U256::from_be_slice(output_word);
self.execution_state self.execution_state.variables.insert(variable_name.clone(), value);
.variables
.insert(variable_name.clone(), value);
tracing::info!( tracing::info!(
variable_name, variable_name,
variable_value = hex::encode(value.to_be_bytes::<32>()), variable_value = hex::encode(value.to_be_bytes::<32>()),
@@ -503,9 +486,7 @@ where
// receipt and how this would impact the architecture and the possibility of us not waiting // receipt and how this would impact the architecture and the possibility of us not waiting
// for receipts in the future. // for receipts in the future.
self.watcher_tx self.watcher_tx
.send(WatcherEvent::RepetitionStartEvent { .send(WatcherEvent::RepetitionStartEvent { ignore_block_before: 0 })
ignore_block_before: 0,
})
.context("Failed to send message on the watcher's tx")?; .context("Failed to send message on the watcher's tx")?;
let res = futures::future::try_join_all(tasks) let res = futures::future::try_join_all(tasks)
@@ -533,9 +514,7 @@ where
let account = private_key.address(); let account = private_key.address();
let variable = U256::from_be_slice(account.0.as_slice()); let variable = U256::from_be_slice(account.0.as_slice());
self.execution_state self.execution_state.variables.insert(variable_name.to_string(), variable);
.variables
.insert(variable_name.to_string(), variable);
Ok(1) Ok(1)
} }
@@ -560,10 +539,8 @@ where
calldata: Option<&Calldata>, calldata: Option<&Calldata>,
value: Option<EtherValue>, value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> { ) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
if let Some((_, address, abi)) = self if let Some((_, address, abi)) =
.execution_state self.execution_state.deployed_contracts.get(contract_instance)
.deployed_contracts
.get(contract_instance)
{ {
info!( info!(
@@ -603,19 +580,10 @@ where
calldata: Option<&Calldata>, calldata: Option<&Calldata>,
value: Option<EtherValue>, value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, TransactionReceipt)> { ) -> Result<(Address, JsonAbi, TransactionReceipt)> {
let Some(ContractPathAndIdent { let Some(ContractPathAndIdent { contract_source_path, contract_ident }) =
contract_source_path, self.test_definition.metadata.contract_sources()?.remove(contract_instance)
contract_ident,
}) = self
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else { else {
anyhow::bail!( anyhow::bail!("Contract source not found for instance {:?}", contract_instance)
"Contract source not found for instance {:?}",
contract_instance
)
}; };
let Some((code, abi)) = self let Some((code, abi)) = self
@@ -625,10 +593,7 @@ where
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref())) .and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
.cloned() .cloned()
else { else {
anyhow::bail!( anyhow::bail!("Failed to find information for contract {:?}", contract_instance)
"Failed to find information for contract {:?}",
contract_instance
)
}; };
let mut code = match alloy::hex::decode(&code) { let mut code = match alloy::hex::decode(&code) {
@@ -641,7 +606,7 @@ where
"Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking" "Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking"
); );
anyhow::bail!("Failed to hex-decode the byte code {}", error) anyhow::bail!("Failed to hex-decode the byte code {}", error)
} },
}; };
if let Some(calldata) = calldata { if let Some(calldata) = calldata {
@@ -665,7 +630,7 @@ where
Err(error) => { Err(error) => {
tracing::error!(?error, "Contract deployment transaction failed."); tracing::error!(?error, "Contract deployment transaction failed.");
return Err(error); return Err(error);
} },
}; };
let Some(address) = receipt.contract_address else { let Some(address) = receipt.contract_address else {
@@ -680,10 +645,9 @@ where
.reporter .reporter
.report_contract_deployed_event(contract_instance.clone(), address)?; .report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state.deployed_contracts.insert( self.execution_state
contract_instance.clone(), .deployed_contracts
(contract_ident, address, abi.clone()), .insert(contract_instance.clone(), (contract_ident, address, abi.clone()));
);
Ok((address, abi, receipt)) Ok((address, abi, receipt))
} }
@@ -696,9 +660,7 @@ where
match step_address { match step_address {
StepAddress::Address(address) => Ok(*address), StepAddress::Address(address) => Ok(*address),
StepAddress::ResolvableAddress(resolvable) => { StepAddress::ResolvableAddress(resolvable) => {
let Some(instance) = resolvable let Some(instance) = resolvable.strip_suffix(".address").map(ContractInstance::new)
.strip_suffix(".address")
.map(ContractInstance::new)
else { else {
bail!("Not an address variable"); bail!("Not an address variable");
}; };
@@ -711,7 +673,7 @@ where
) )
.await .await
.map(|v| v.0) .map(|v| v.0)
} },
} }
} }
// endregion:Contract Deployment // endregion:Contract Deployment
@@ -757,7 +719,7 @@ where
Ok(receipt) => { Ok(receipt) => {
info!("Polling succeeded, receipt found"); info!("Polling succeeded, receipt found");
Ok(ControlFlow::Break(receipt)) Ok(ControlFlow::Break(receipt))
} },
Err(_) => Ok(ControlFlow::Continue(())), Err(_) => Ok(ControlFlow::Continue(())),
} }
} }
@@ -96,13 +96,8 @@ pub async fn handle_differential_benchmarks(
// Creating the objects that will be shared between the various runs. The cached compiler is the // Creating the objects that will be shared between the various runs. The cached compiler is the
// only one at the current moment of time that's safe to share between runs. // only one at the current moment of time that's safe to share between runs.
let cached_compiler = CachedCompiler::new( let cached_compiler = CachedCompiler::new(
context context.working_directory.as_path().join("compilation_cache"),
.working_directory context.compilation_configuration.invalidate_compilation_cache,
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
) )
.await .await
.map(Arc::new) .map(Arc::new)
@@ -161,9 +156,7 @@ pub async fn handle_differential_benchmarks(
watcher.run(), watcher.run(),
driver.execute_all().inspect(|_| { driver.execute_all().inspect(|_| {
info!("All transactions submitted - driver completed execution"); info!("All transactions submitted - driver completed execution");
watcher_tx watcher_tx.send(WatcherEvent::AllTransactionsSubmitted).unwrap()
.send(WatcherEvent::AllTransactionsSubmitted)
.unwrap()
}), }),
) )
.await .await
@@ -27,11 +27,7 @@ impl ExecutionState {
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>, compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>, deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
) -> Self { ) -> Self {
Self { Self { compiled_contracts, deployed_contracts, variables: Default::default() }
compiled_contracts,
deployed_contracts,
variables: Default::default(),
}
} }
pub fn empty() -> Self { pub fn empty() -> Self {
@@ -33,14 +33,7 @@ impl Watcher {
blocks_stream: Pin<Box<dyn Stream<Item = MinedBlockInformation>>>, blocks_stream: Pin<Box<dyn Stream<Item = MinedBlockInformation>>>,
) -> (Self, UnboundedSender<WatcherEvent>) { ) -> (Self, UnboundedSender<WatcherEvent>) {
let (tx, rx) = unbounded_channel::<WatcherEvent>(); let (tx, rx) = unbounded_channel::<WatcherEvent>();
( (Self { platform_identifier, rx, blocks_stream }, tx)
Self {
platform_identifier,
rx,
blocks_stream,
},
tx,
)
} }
#[instrument(level = "info", skip_all)] #[instrument(level = "info", skip_all)]
@@ -49,9 +42,8 @@ impl Watcher {
// the watcher of the last block number that it should ignore and what the block number is // the watcher of the last block number that it should ignore and what the block number is
// for the first important block that it should look for. // for the first important block that it should look for.
let ignore_block_before = loop { let ignore_block_before = loop {
let Some(WatcherEvent::RepetitionStartEvent { let Some(WatcherEvent::RepetitionStartEvent { ignore_block_before }) =
ignore_block_before, self.rx.recv().await
}) = self.rx.recv().await
else { else {
continue; continue;
}; };
@@ -80,19 +72,16 @@ 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 } => { WatcherEvent::SubmittedTransaction { transaction_hash } => {
watch_for_transaction_hashes watch_for_transaction_hashes.write().await.insert(transaction_hash);
.write() },
.await
.insert(transaction_hash);
}
WatcherEvent::AllTransactionsSubmitted => { WatcherEvent::AllTransactionsSubmitted => {
*all_transactions_submitted.write().await = true; *all_transactions_submitted.write().await = true;
self.rx.close(); self.rx.close();
info!("Watcher's Events Watching Task Finished"); info!("Watcher's Events Watching Task Finished");
break; break;
} },
} }
} }
} }
@@ -111,8 +100,8 @@ impl Watcher {
continue; continue;
} }
if *all_transactions_submitted.read().await if *all_transactions_submitted.read().await &&
&& watch_for_transaction_hashes.read().await.is_empty() watch_for_transaction_hashes.read().await.is_empty()
{ {
break; break;
} }
@@ -151,15 +140,8 @@ impl Watcher {
use std::io::Write; use std::io::Write;
let mut stderr = std::io::stderr().lock(); let mut stderr = std::io::stderr().lock();
writeln!( writeln!(stderr, "Watcher information for {}", self.platform_identifier)?;
stderr, writeln!(stderr, "block_number,block_timestamp,mined_gas,block_gas_limit,tx_count")?;
"Watcher information for {}",
self.platform_identifier
)?;
writeln!(
stderr,
"block_number,block_timestamp,mined_gas,block_gas_limit,tx_count"
)?;
for block in mined_blocks_information { for block in mined_blocks_information {
writeln!( writeln!(
stderr, stderr,
+66 -109
View File
@@ -112,9 +112,7 @@ impl<'a> Driver<'a, StepsIterator> {
pub async fn execute_all(mut self) -> Result<usize> { pub async fn execute_all(mut self) -> Result<usize> {
let platform_drivers = std::mem::take(&mut self.platform_drivers); let platform_drivers = std::mem::take(&mut self.platform_drivers);
let results = futures::future::try_join_all( let results = futures::future::try_join_all(
platform_drivers platform_drivers.into_values().map(|driver| driver.execute_all()),
.into_values()
.map(|driver| driver.execute_all()),
) )
.await .await
.context("Failed to execute all of the steps on the driver")?; .context("Failed to execute all of the steps on the driver")?;
@@ -252,11 +250,8 @@ where
TransactionRequest::default().from(deployer_address), TransactionRequest::default().from(deployer_address),
code, code,
); );
let receipt = platform_information let receipt =
.node platform_information.node.execute_transaction(tx).await.inspect_err(|err| {
.execute_transaction(tx)
.await
.inspect_err(|err| {
error!( error!(
?err, ?err,
%library_instance, %library_instance,
@@ -265,9 +260,7 @@ where
) )
})?; })?;
let library_address = receipt let library_address = receipt.contract_address.expect("Failed to deploy the library");
.contract_address
.expect("Failed to deploy the library");
deployed_libraries.get_or_insert_default().insert( deployed_libraries.get_or_insert_default().insert(
library_instance.clone(), library_instance.clone(),
@@ -295,10 +288,7 @@ where
}) })
.context("Failed to compile the post-link contracts")?; .context("Failed to compile the post-link contracts")?;
Ok(ExecutionState::new( Ok(ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default()))
compiler_output.contracts,
deployed_libraries.unwrap_or_default(),
))
} }
// endregion:Constructors & Initialization // endregion:Constructors & Initialization
@@ -364,14 +354,31 @@ where
_: &StepPath, _: &StepPath,
step: &FunctionCallStep, step: &FunctionCallStep,
) -> Result<usize> { ) -> Result<usize> {
// Check if this step expects an exception
let expects_exception = step.expected.as_ref().map_or(false, |expected| match expected {
Expected::Expected(exp) => exp.exception,
Expected::ExpectedMany(exps) => exps.iter().any(|exp| exp.exception),
Expected::Calldata(_) => false,
});
let deployment_receipts = self let deployment_receipts = self
.handle_function_call_contract_deployment(step) .handle_function_call_contract_deployment(step)
.await .await
.context("Failed to deploy contracts for the function call step")?; .context("Failed to deploy contracts for the function call step")?;
let execution_receipt = self
.handle_function_call_execution(step, deployment_receipts) let execution_receipt =
.await match self.handle_function_call_execution(step, deployment_receipts).await {
.context("Failed to handle the function call execution")?; Ok(receipt) => Some(receipt),
Err(err) => {
if !expects_exception {
return Err(err).context("Failed to handle the function call execution");
}
tracing::info!("Transaction failed as expected");
None
},
};
if let Some(execution_receipt) = execution_receipt {
let tracing_result = self let tracing_result = self
.handle_function_call_call_frame_tracing(execution_receipt.transaction_hash) .handle_function_call_call_frame_tracing(execution_receipt.transaction_hash)
.await .await
@@ -382,6 +389,8 @@ where
self.handle_function_call_assertions(step, &execution_receipt, &tracing_result) self.handle_function_call_assertions(step, &execution_receipt, &tracing_result)
.await .await
.context("Failed to handle function call assertions")?; .context("Failed to handle function call assertions")?;
}
Ok(1) Ok(1)
} }
@@ -392,11 +401,7 @@ where
) -> Result<HashMap<ContractInstance, TransactionReceipt>> { ) -> Result<HashMap<ContractInstance, TransactionReceipt>> {
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new(); let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
for instance in step.find_all_contract_instances().into_iter() { for instance in step.find_all_contract_instances().into_iter() {
if !self if !self.execution_state.deployed_contracts.contains_key(&instance) {
.execution_state
.deployed_contracts
.contains_key(&instance)
{
instances_we_must_deploy.entry(instance).or_insert(false); instances_we_must_deploy.entry(instance).or_insert(false);
} }
} }
@@ -408,20 +413,13 @@ where
let mut receipts = HashMap::new(); let mut receipts = HashMap::new();
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() { for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
let calldata = deploy_with_constructor_arguments.then_some(&step.calldata); let calldata = deploy_with_constructor_arguments.then_some(&step.calldata);
let value = deploy_with_constructor_arguments let value = deploy_with_constructor_arguments.then_some(step.value).flatten();
.then_some(step.value)
.flatten();
let caller = { let caller = {
let context = self.default_resolution_context(); let context = self.default_resolution_context();
let resolver = self.platform_information.node.resolver().await?; let resolver = self.platform_information.node.resolver().await?;
let resolved = step let resolved = step.caller.resolve_address(resolver.as_ref(), context).await?;
.caller self.platform_information.node.resolve_signer_or_default(resolved)
.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)
@@ -456,20 +454,16 @@ where
Ok(tx) => tx, Ok(tx) => tx,
Err(err) => { Err(err) => {
return Err(err); return Err(err);
} },
}; };
// 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( tx.from = Some(self.platform_information.node.resolve_signer_or_default(from));
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
} },
} }
} }
@@ -516,18 +510,13 @@ where
}; };
// Handling the return data variable assignments. // Handling the return data variable assignments.
for (variable_name, output_word) in assignments.return_data.iter().zip( for (variable_name, output_word) in assignments
tracing_result .return_data
.output .iter()
.as_ref() .zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32))
.unwrap_or_default() {
.to_vec()
.chunks(32),
) {
let value = U256::from_be_slice(output_word); let value = U256::from_be_slice(output_word);
self.execution_state self.execution_state.variables.insert(variable_name.clone(), value);
.variables
.insert(variable_name.clone(), value);
tracing::info!( tracing::info!(
variable_name, variable_name,
variable_value = hex::encode(value.to_be_bytes::<32>()), variable_value = hex::encode(value.to_be_bytes::<32>()),
@@ -547,18 +536,12 @@ where
) -> Result<()> { ) -> Result<()> {
// Resolving the `step.expected` into a series of expectations that we can then assert on. // Resolving the `step.expected` into a series of expectations that we can then assert on.
let mut expectations = match step { let mut expectations = match step {
FunctionCallStep { FunctionCallStep { expected: Some(Expected::Calldata(calldata)), .. } =>
expected: Some(Expected::Calldata(calldata)), vec![ExpectedOutput::new().with_calldata(calldata.clone())],
.. FunctionCallStep { expected: Some(Expected::Expected(expected)), .. } =>
} => vec![ExpectedOutput::new().with_calldata(calldata.clone())], vec![expected.clone()],
FunctionCallStep { FunctionCallStep { expected: Some(Expected::ExpectedMany(expected)), .. } =>
expected: Some(Expected::Expected(expected)), expected.clone(),
..
} => vec![expected.clone()],
FunctionCallStep {
expected: Some(Expected::ExpectedMany(expected)),
..
} => expected.clone(),
FunctionCallStep { expected: None, .. } => vec![ExpectedOutput::new().with_success()], FunctionCallStep { expected: None, .. } => vec![ExpectedOutput::new().with_success()],
}; };
@@ -677,11 +660,8 @@ where
} }
// Handling the topics assertion. // Handling the topics assertion.
for (expected, actual) in expected_event for (expected, actual) in
.topics expected_event.topics.as_slice().iter().zip(actual_event.topics())
.as_slice()
.iter()
.zip(actual_event.topics())
{ {
let expected = Calldata::new_compound([expected]); let expected = Calldata::new_compound([expected]);
if !expected if !expected
@@ -851,9 +831,7 @@ where
let account = private_key.address(); let account = private_key.address();
let variable = U256::from_be_slice(account.0.as_slice()); let variable = U256::from_be_slice(account.0.as_slice());
self.execution_state self.execution_state.variables.insert(variable_name.to_string(), variable);
.variables
.insert(variable_name.to_string(), variable);
Ok(1) Ok(1)
} }
@@ -876,10 +854,8 @@ where
calldata: Option<&Calldata>, calldata: Option<&Calldata>,
value: Option<EtherValue>, value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> { ) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
if let Some((_, address, abi)) = self if let Some((_, address, abi)) =
.execution_state self.execution_state.deployed_contracts.get(contract_instance)
.deployed_contracts
.get(contract_instance)
{ {
info!( info!(
@@ -917,19 +893,10 @@ where
calldata: Option<&Calldata>, calldata: Option<&Calldata>,
value: Option<EtherValue>, value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, TransactionReceipt)> { ) -> Result<(Address, JsonAbi, TransactionReceipt)> {
let Some(ContractPathAndIdent { let Some(ContractPathAndIdent { contract_source_path, contract_ident }) =
contract_source_path, self.test_definition.metadata.contract_sources()?.remove(contract_instance)
contract_ident,
}) = self
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else { else {
anyhow::bail!( anyhow::bail!("Contract source not found for instance {:?}", contract_instance)
"Contract source not found for instance {:?}",
contract_instance
)
}; };
let Some((code, abi)) = self let Some((code, abi)) = self
@@ -939,10 +906,7 @@ where
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref())) .and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
.cloned() .cloned()
else { else {
anyhow::bail!( anyhow::bail!("Failed to find information for contract {:?}", contract_instance)
"Failed to find information for contract {:?}",
contract_instance
)
}; };
let mut code = match alloy::hex::decode(&code) { let mut code = match alloy::hex::decode(&code) {
@@ -955,22 +919,18 @@ where
"Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking" "Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking"
); );
anyhow::bail!("Failed to hex-decode the byte code {}", error) anyhow::bail!("Failed to hex-decode the byte code {}", error)
} },
}; };
if let Some(calldata) = calldata { if let Some(calldata) = calldata {
let resolver = self.platform_information.node.resolver().await?; let resolver = self.platform_information.node.resolver().await?;
let calldata = calldata let calldata =
.calldata(resolver.as_ref(), self.default_resolution_context()) calldata.calldata(resolver.as_ref(), self.default_resolution_context()).await?;
.await?;
code.extend(calldata); code.extend(calldata);
} }
let tx = { let tx = {
let deployer = self let deployer = self.platform_information.node.resolve_signer_or_default(deployer);
.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()),
@@ -984,7 +944,7 @@ where
Err(error) => { Err(error) => {
tracing::error!(?error, "Contract deployment transaction failed."); tracing::error!(?error, "Contract deployment transaction failed.");
return Err(error); return Err(error);
} },
}; };
let Some(address) = receipt.contract_address else { let Some(address) = receipt.contract_address else {
@@ -999,10 +959,9 @@ where
.reporter .reporter
.report_contract_deployed_event(contract_instance.clone(), address)?; .report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state.deployed_contracts.insert( self.execution_state
contract_instance.clone(), .deployed_contracts
(contract_ident, address, abi.clone()), .insert(contract_instance.clone(), (contract_ident, address, abi.clone()));
);
Ok((address, abi, receipt)) Ok((address, abi, receipt))
} }
@@ -1015,9 +974,7 @@ where
match step_address { match step_address {
StepAddress::Address(address) => Ok(*address), StepAddress::Address(address) => Ok(*address),
StepAddress::ResolvableAddress(resolvable) => { StepAddress::ResolvableAddress(resolvable) => {
let Some(instance) = resolvable let Some(instance) = resolvable.strip_suffix(".address").map(ContractInstance::new)
.strip_suffix(".address")
.map(ContractInstance::new)
else { else {
bail!("Not an address variable"); bail!("Not an address variable");
}; };
@@ -1030,7 +987,7 @@ where
) )
.await .await
.map(|v| v.0) .map(|v| v.0)
} },
} }
} }
// endregion:Contract Deployment // endregion:Contract Deployment
@@ -85,13 +85,8 @@ pub async fn handle_differential_tests(
// Creating everything else required for the driver to run. // Creating everything else required for the driver to run.
let cached_compiler = CachedCompiler::new( let cached_compiler = CachedCompiler::new(
context context.working_directory.as_path().join("compilation_cache"),
.working_directory context.compilation_configuration.invalidate_compilation_cache,
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
) )
.await .await
.map(Arc::new) .map(Arc::new)
@@ -146,7 +141,7 @@ pub async fn handle_differential_tests(
drop(permit); drop(permit);
running_task_list.write().await.remove(&test_id); running_task_list.write().await.remove(&test_id);
return; return;
} },
}; };
info!("Created the driver for the test case"); info!("Created the driver for the test case");
@@ -161,7 +156,7 @@ pub async fn handle_differential_tests(
.report_test_failed_event(format!("{error:#}")) .report_test_failed_event(format!("{error:#}"))
.expect("Can't fail"); .expect("Can't fail");
error!("Test Case Failed"); error!("Test Case Failed");
} },
}; };
info!("Finished the execution of the test case"); info!("Finished the execution of the test case");
drop(permit); drop(permit);
@@ -172,20 +167,14 @@ pub async fn handle_differential_tests(
)) ))
.inspect(|_| { .inspect(|_| {
info!("Finished executing all test cases"); info!("Finished executing all test cases");
reporter_clone reporter_clone.report_completion_event().expect("Can't fail")
.report_completion_event()
.expect("Can't fail")
}); });
let cli_reporting_task = start_cli_reporting_task(reporter); let cli_reporting_task = start_cli_reporting_task(reporter);
tokio::task::spawn(async move { tokio::task::spawn(async move {
loop { loop {
let remaining_tasks = running_task_list.read().await; let remaining_tasks = running_task_list.read().await;
info!( info!(count = remaining_tasks.len(), ?remaining_tasks, "Remaining Tests");
count = remaining_tasks.len(),
?remaining_tasks,
"Remaining Tests"
);
tokio::time::sleep(Duration::from_secs(10)).await tokio::time::sleep(Duration::from_secs(10)).await
} }
}); });
@@ -234,7 +223,7 @@ async fn start_cli_reporting_task(reporter: Reporter) {
"{}{}Case Succeeded{} - Steps Executed: {}{}", "{}{}Case Succeeded{} - Steps Executed: {}{}",
GREEN, BOLD, BOLD_RESET, steps_executed, COLOR_RESET GREEN, BOLD, BOLD_RESET, steps_executed, COLOR_RESET
) )
} },
TestCaseStatus::Failed { reason } => { TestCaseStatus::Failed { reason } => {
number_of_failures += 1; number_of_failures += 1;
writeln!( writeln!(
@@ -246,7 +235,7 @@ async fn start_cli_reporting_task(reporter: Reporter) {
reason.trim(), reason.trim(),
COLOR_RESET, COLOR_RESET,
) )
} },
TestCaseStatus::Ignored { reason, .. } => writeln!( TestCaseStatus::Ignored { reason, .. } => writeln!(
buf, buf,
"{}{}Case Ignored{} - Reason: {}{}", "{}{}Case Ignored{} - Reason: {}{}",
@@ -27,10 +27,6 @@ impl ExecutionState {
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>, compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>, deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
) -> Self { ) -> Self {
Self { Self { compiled_contracts, deployed_contracts, variables: Default::default() }
compiled_contracts,
deployed_contracts,
variables: Default::default(),
}
} }
} }
+17 -28
View File
@@ -41,10 +41,7 @@ impl<'a> CachedCompiler<'a> {
.await .await
.context("Failed to invalidate compilation cache directory")?; .context("Failed to invalidate compilation cache directory")?;
} }
Ok(Self { Ok(Self { artifacts_cache: cache, cache_key_lock: Default::default() })
artifacts_cache: cache,
cache_key_lock: Default::default(),
})
} }
/// Compiles or gets the compilation artifacts from the cache. /// Compiles or gets the compilation artifacts from the cache.
@@ -112,7 +109,7 @@ impl<'a> CachedCompiler<'a> {
.await .await
.context("Compilation callback for deployed libraries failed")? .context("Compilation callback for deployed libraries failed")?
.compiler_output .compiler_output
} },
// If no deployed libraries are specified then we can follow the cached flow and attempt // If no deployed libraries are specified then we can follow the cached flow and attempt
// to lookup the compilation artifacts in the cache. // to lookup the compilation artifacts in the cache.
None => { None => {
@@ -126,7 +123,7 @@ impl<'a> CachedCompiler<'a> {
Some(value) => { Some(value) => {
drop(read_guard); drop(read_guard);
value value
} },
None => { None => {
drop(read_guard); drop(read_guard);
self.cache_key_lock self.cache_key_lock
@@ -135,7 +132,7 @@ impl<'a> CachedCompiler<'a> {
.entry(cache_key.clone()) .entry(cache_key.clone())
.or_default() .or_default()
.clone() .clone()
} },
}; };
let _guard = mutex.lock().await; let _guard = mutex.lock().await;
@@ -163,7 +160,7 @@ impl<'a> CachedCompiler<'a> {
.expect("Can't happen"); .expect("Can't happen");
} }
cache_value.compiler_output cache_value.compiler_output
} },
None => { None => {
let compiler_output = compilation_callback() let compiler_output = compilation_callback()
.await .await
@@ -172,18 +169,16 @@ impl<'a> CachedCompiler<'a> {
self.artifacts_cache self.artifacts_cache
.insert( .insert(
&cache_key, &cache_key,
&CacheValue { &CacheValue { compiler_output: compiler_output.clone() },
compiler_output: compiler_output.clone(),
},
) )
.await .await
.context( .context(
"Failed to write the cached value of the compilation artifacts", "Failed to write the cached value of the compilation artifacts",
)?; )?;
compiler_output compiler_output
},
} }
} },
}
}; };
Ok(compiled_contracts) Ok(compiled_contracts)
@@ -225,9 +220,7 @@ async fn compile_contracts(
.flat_map(|value| value.iter()) .flat_map(|value| value.iter())
.map(|(instance, (ident, address, abi))| (instance, ident, address, abi)) .map(|(instance, (ident, address, abi))| (instance, ident, address, abi))
.flat_map(|(_, ident, address, _)| { .flat_map(|(_, ident, address, _)| {
all_sources_in_dir all_sources_in_dir.iter().map(move |path| (ident, address, path))
.iter()
.map(move |path| (ident, address, path))
}) })
.fold(compiler, |compiler, (ident, address, path)| { .fold(compiler, |compiler, (ident, address, path)| {
compiler.with_library(path, ident.as_str(), *address) compiler.with_library(path, ident.as_str(), *address)
@@ -248,7 +241,7 @@ async fn compile_contracts(
output.clone(), output.clone(),
) )
.expect("Can't happen"); .expect("Can't happen");
} },
(Ok(output), false) => { (Ok(output), false) => {
reporter reporter
.report_pre_link_contracts_compilation_succeeded_event( .report_pre_link_contracts_compilation_succeeded_event(
@@ -259,7 +252,7 @@ async fn compile_contracts(
output.clone(), output.clone(),
) )
.expect("Can't happen"); .expect("Can't happen");
} },
(Err(err), true) => { (Err(err), true) => {
reporter reporter
.report_post_link_contracts_compilation_failed_event( .report_post_link_contracts_compilation_failed_event(
@@ -269,7 +262,7 @@ async fn compile_contracts(
format!("{err:#}"), format!("{err:#}"),
) )
.expect("Can't happen"); .expect("Can't happen");
} },
(Err(err), false) => { (Err(err), false) => {
reporter reporter
.report_pre_link_contracts_compilation_failed_event( .report_pre_link_contracts_compilation_failed_event(
@@ -279,7 +272,7 @@ async fn compile_contracts(
format!("{err:#}"), format!("{err:#}"),
) )
.expect("Can't happen"); .expect("Can't happen");
} },
} }
output output
@@ -291,9 +284,7 @@ struct ArtifactsCache {
impl ArtifactsCache { impl ArtifactsCache {
pub fn new(path: impl AsRef<Path>) -> Self { pub fn new(path: impl AsRef<Path>) -> Self {
Self { Self { path: path.as_ref().to_path_buf() }
path: path.as_ref().to_path_buf(),
}
} }
#[instrument(level = "debug", skip_all, err)] #[instrument(level = "debug", skip_all, err)]
@@ -319,9 +310,7 @@ impl ArtifactsCache {
pub async fn get(&self, key: &CacheKey<'_>) -> Option<CacheValue> { pub async fn get(&self, key: &CacheKey<'_>) -> Option<CacheValue> {
let key = bson::to_vec(key).ok()?; let key = bson::to_vec(key).ok()?;
let value = cacache::read(self.path.as_path(), key.encode_hex()) let value = cacache::read(self.path.as_path(), key.encode_hex()).await.ok()?;
.await
.ok()?;
let value = bson::from_slice::<CacheValue>(&value).ok()?; let value = bson::from_slice::<CacheValue>(&value).ok()?;
Some(value) Some(value)
} }
@@ -336,13 +325,13 @@ impl ArtifactsCache {
Some(value) => { Some(value) => {
debug!("Cache hit"); debug!("Cache hit");
Ok(value) Ok(value)
} },
None => { None => {
debug!("Cache miss"); debug!("Cache miss");
let value = callback().await?; let value = callback().await?;
self.insert(key, &value).await?; self.insert(key, &value).await?;
Ok(value) Ok(value)
} },
} }
} }
} }
+3 -8
View File
@@ -37,18 +37,13 @@ impl NodePool {
); );
} }
let pre_transactions_tasks = nodes let pre_transactions_tasks =
.iter_mut() nodes.iter_mut().map(|node| node.pre_transactions()).collect::<Vec<_>>();
.map(|node| node.pre_transactions())
.collect::<Vec<_>>();
futures::future::try_join_all(pre_transactions_tasks) futures::future::try_join_all(pre_transactions_tasks)
.await .await
.context("Failed to run the pre-transactions task")?; .context("Failed to run the pre-transactions task")?;
Ok(Self { Ok(Self { nodes, next: Default::default() })
nodes,
next: Default::default(),
})
} }
/// Get a handle to the next node. /// Get a handle to the next node.
+8 -21
View File
@@ -66,15 +66,12 @@ pub async fn create_test_definitions_stream<'a>(
// Inform the reporter of each one of the test cases that were discovered which we // Inform the reporter of each one of the test cases that were discovered which we
// expect to run. // expect to run.
.inspect(|(_, _, _, _, reporter)| { .inspect(|(_, _, _, _, reporter)| {
reporter reporter.report_test_case_discovery_event().expect("Can't fail");
.report_test_case_discovery_event()
.expect("Can't fail");
}), }),
) )
// Creating the Test Definition objects from all of the various objects we have and creating // Creating the Test Definition objects from all of the various objects we have and creating
// their required dependencies (e.g., compiler). // their required dependencies (e.g., compiler).
.filter_map( .filter_map(move |(metadata_file, case_idx, case, mode, reporter)| async move {
move |(metadata_file, case_idx, case, mode, reporter)| async move {
let mut platforms = BTreeMap::new(); let mut platforms = BTreeMap::new();
for (platform, node_pool) in platforms_and_nodes.values() { for (platform, node_pool) in platforms_and_nodes.values() {
let node = node_pool.round_robbin(); let node = node_pool.round_robbin();
@@ -103,12 +100,7 @@ pub async fn create_test_definitions_stream<'a>(
platforms.insert( platforms.insert(
platform.platform_identifier(), platform.platform_identifier(),
TestPlatformInformation { TestPlatformInformation { platform: *platform, node, compiler, reporter },
platform: *platform,
node,
compiler,
reporter,
},
); );
} }
@@ -130,8 +122,7 @@ pub async fn create_test_definitions_stream<'a>(
/* Reporter */ /* Reporter */
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() {
@@ -156,7 +147,7 @@ pub async fn create_test_definitions_stream<'a>(
) )
.expect("Can't fail"); .expect("Can't fail");
None None
} },
} }
}) })
.inspect(|test| { .inspect(|test| {
@@ -230,9 +221,8 @@ impl<'a> TestDefinition<'a> {
for (_, platform_information) in self.platforms.iter() { for (_, platform_information) in self.platforms.iter() {
let is_allowed_for_platform = match self.metadata.targets.as_ref() { let is_allowed_for_platform = match self.metadata.targets.as_ref() {
None => true, None => true,
Some(required_vm_identifiers) => { Some(required_vm_identifiers) =>
required_vm_identifiers.contains(&platform_information.platform.vm_identifier()) required_vm_identifiers.contains(&platform_information.platform.vm_identifier()),
}
}; };
is_allowed &= is_allowed_for_platform; is_allowed &= is_allowed_for_platform;
error_map.insert( error_map.insert(
@@ -274,10 +264,7 @@ impl<'a> TestDefinition<'a> {
if is_allowed { if is_allowed {
Ok(()) Ok(())
} else { } else {
Err(( Err(("EVM version is incompatible for the platforms specified", error_map))
"EVM version is incompatible for the platforms specified",
error_map,
))
} }
} }
+38 -71
View File
@@ -37,11 +37,7 @@ pub trait Platform {
/// Returns a full identifier for the platform. /// Returns a full identifier for the platform.
fn full_identifier(&self) -> (NodeIdentifier, VmIdentifier, CompilerIdentifier) { fn full_identifier(&self) -> (NodeIdentifier, VmIdentifier, CompilerIdentifier) {
( (self.node_identifier(), self.vm_identifier(), self.compiler_identifier())
self.node_identifier(),
self.vm_identifier(),
self.compiler_identifier(),
)
} }
/// Returns the identifier of the node used. /// Returns the identifier of the node used.
@@ -183,9 +179,7 @@ impl Platform for KitchensinkPolkavmResolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context) let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context).path.clone();
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -235,9 +229,7 @@ impl Platform for KitchensinkRevmSolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context) let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context).path.clone();
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -287,9 +279,8 @@ impl Platform for ReviveDevNodePolkavmResolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context) let revive_dev_node_path =
.path AsRef::<ReviveDevNodeConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -339,9 +330,8 @@ impl Platform for ReviveDevNodeRevmSolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context) let revive_dev_node_path =
.path AsRef::<ReviveDevNodeConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
@@ -391,9 +381,8 @@ impl Platform for ZombienetPolkavmResolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context) let polkadot_parachain_path =
.path AsRef::<PolkadotParachainConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = ZombieNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -439,9 +428,8 @@ impl Platform for ZombienetRevmSolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context) let polkadot_parachain_path =
.path AsRef::<PolkadotParachainConfiguration>::as_ref(&context).path.clone();
.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = ZombieNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -466,24 +454,18 @@ impl From<PlatformIdentifier> for Box<dyn Platform> {
fn from(value: PlatformIdentifier) -> Self { fn from(value: PlatformIdentifier) -> Self {
match value { match value {
PlatformIdentifier::GethEvmSolc => Box::new(GethEvmSolcPlatform) as Box<_>, PlatformIdentifier::GethEvmSolc => Box::new(GethEvmSolcPlatform) as Box<_>,
PlatformIdentifier::LighthouseGethEvmSolc => { PlatformIdentifier::LighthouseGethEvmSolc =>
Box::new(LighthouseGethEvmSolcPlatform) as Box<_> Box::new(LighthouseGethEvmSolcPlatform) as Box<_>,
} PlatformIdentifier::KitchensinkPolkavmResolc =>
PlatformIdentifier::KitchensinkPolkavmResolc => { Box::new(KitchensinkPolkavmResolcPlatform) as Box<_>,
Box::new(KitchensinkPolkavmResolcPlatform) as Box<_> PlatformIdentifier::KitchensinkRevmSolc =>
} Box::new(KitchensinkRevmSolcPlatform) as Box<_>,
PlatformIdentifier::KitchensinkRevmSolc => { PlatformIdentifier::ReviveDevNodePolkavmResolc =>
Box::new(KitchensinkRevmSolcPlatform) as Box<_> Box::new(ReviveDevNodePolkavmResolcPlatform) as Box<_>,
} PlatformIdentifier::ReviveDevNodeRevmSolc =>
PlatformIdentifier::ReviveDevNodePolkavmResolc => { Box::new(ReviveDevNodeRevmSolcPlatform) as Box<_>,
Box::new(ReviveDevNodePolkavmResolcPlatform) as Box<_> PlatformIdentifier::ZombienetPolkavmResolc =>
} Box::new(ZombienetPolkavmResolcPlatform) as Box<_>,
PlatformIdentifier::ReviveDevNodeRevmSolc => {
Box::new(ReviveDevNodeRevmSolcPlatform) as Box<_>
}
PlatformIdentifier::ZombienetPolkavmResolc => {
Box::new(ZombienetPolkavmResolcPlatform) as Box<_>
}
PlatformIdentifier::ZombienetRevmSolc => Box::new(ZombienetRevmSolcPlatform) as Box<_>, PlatformIdentifier::ZombienetRevmSolc => Box::new(ZombienetRevmSolcPlatform) as Box<_>,
} }
} }
@@ -493,24 +475,18 @@ impl From<PlatformIdentifier> for &dyn Platform {
fn from(value: PlatformIdentifier) -> Self { fn from(value: PlatformIdentifier) -> Self {
match value { match value {
PlatformIdentifier::GethEvmSolc => &GethEvmSolcPlatform as &dyn Platform, PlatformIdentifier::GethEvmSolc => &GethEvmSolcPlatform as &dyn Platform,
PlatformIdentifier::LighthouseGethEvmSolc => { PlatformIdentifier::LighthouseGethEvmSolc =>
&LighthouseGethEvmSolcPlatform as &dyn Platform &LighthouseGethEvmSolcPlatform as &dyn Platform,
} PlatformIdentifier::KitchensinkPolkavmResolc =>
PlatformIdentifier::KitchensinkPolkavmResolc => { &KitchensinkPolkavmResolcPlatform as &dyn Platform,
&KitchensinkPolkavmResolcPlatform as &dyn Platform PlatformIdentifier::KitchensinkRevmSolc =>
} &KitchensinkRevmSolcPlatform as &dyn Platform,
PlatformIdentifier::KitchensinkRevmSolc => { PlatformIdentifier::ReviveDevNodePolkavmResolc =>
&KitchensinkRevmSolcPlatform as &dyn Platform &ReviveDevNodePolkavmResolcPlatform as &dyn Platform,
} PlatformIdentifier::ReviveDevNodeRevmSolc =>
PlatformIdentifier::ReviveDevNodePolkavmResolc => { &ReviveDevNodeRevmSolcPlatform as &dyn Platform,
&ReviveDevNodePolkavmResolcPlatform as &dyn Platform PlatformIdentifier::ZombienetPolkavmResolc =>
} &ZombienetPolkavmResolcPlatform as &dyn Platform,
PlatformIdentifier::ReviveDevNodeRevmSolc => {
&ReviveDevNodeRevmSolcPlatform as &dyn Platform
}
PlatformIdentifier::ZombienetPolkavmResolc => {
&ZombienetPolkavmResolcPlatform as &dyn Platform
}
PlatformIdentifier::ZombienetRevmSolc => &ZombienetRevmSolcPlatform as &dyn Platform, PlatformIdentifier::ZombienetRevmSolc => &ZombienetRevmSolcPlatform as &dyn Platform,
} }
} }
@@ -520,17 +496,8 @@ fn spawn_node<T: Node + EthereumNode + Send + Sync>(
mut node: T, mut node: T,
genesis: Genesis, genesis: Genesis,
) -> anyhow::Result<T> { ) -> anyhow::Result<T> {
info!( info!(id = node.id(), connection_string = node.connection_string(), "Spawning node");
id = node.id(), node.spawn(genesis).context("Failed to spawn node process")?;
connection_string = node.connection_string(), info!(id = node.id(), connection_string = node.connection_string(), "Spawned node");
"Spawning node"
);
node.spawn(genesis)
.context("Failed to spawn node process")?;
info!(
id = node.id(),
connection_string = node.connection_string(),
"Spawned node"
);
Ok(node) Ok(node)
} }
+1 -1
View File
@@ -76,6 +76,6 @@ fn main() -> anyhow::Result<()> {
let schema = schema_for!(Metadata); let schema = schema_for!(Metadata);
println!("{}", serde_json::to_string_pretty(&schema).unwrap()); println!("{}", serde_json::to_string_pretty(&schema).unwrap());
Ok(()) Ok(())
} },
} }
} }
+2 -8
View File
@@ -54,11 +54,7 @@ pub struct Case {
impl Case { impl Case {
pub fn steps_iterator(&self) -> impl Iterator<Item = Step> { pub fn steps_iterator(&self) -> impl Iterator<Item = Step> {
let steps_len = self.steps.len(); let steps_len = self.steps.len();
self.steps self.steps.clone().into_iter().enumerate().map(move |(idx, mut step)| {
.clone()
.into_iter()
.enumerate()
.map(move |(idx, mut step)| {
let Step::FunctionCall(ref mut input) = step else { let Step::FunctionCall(ref mut input) = step else {
return step; return step;
}; };
@@ -84,9 +80,7 @@ impl Case {
&self, &self,
default_repeat_count: usize, default_repeat_count: usize,
) -> Box<dyn Iterator<Item = Step> + '_> { ) -> Box<dyn Iterator<Item = Step> + '_> {
let contains_repeat = self let contains_repeat = self.steps_iterator().any(|step| matches!(&step, Step::Repeat(..)));
.steps_iterator()
.any(|step| matches!(&step, Step::Repeat(..)));
if contains_repeat { if contains_repeat {
Box::new(self.steps_iterator()) as Box<_> Box::new(self.steps_iterator()) as Box<_>
} else { } else {
+9 -17
View File
@@ -86,11 +86,7 @@ impl Corpus {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
tests.sort_by(|a, b| a.metadata_file_path.cmp(&b.metadata_file_path)); tests.sort_by(|a, b| a.metadata_file_path.cmp(&b.metadata_file_path));
tests.dedup_by(|a, b| a.metadata_file_path == b.metadata_file_path); tests.dedup_by(|a, b| a.metadata_file_path == b.metadata_file_path);
info!( info!(len = tests.len(), corpus_name = self.name(), "Found tests in Corpus");
len = tests.len(),
corpus_name = self.name(),
"Found tests in Corpus"
);
tests tests
} }
@@ -102,23 +98,19 @@ impl Corpus {
pub fn paths_iter(&self) -> impl Iterator<Item = &Path> { pub fn paths_iter(&self) -> impl Iterator<Item = &Path> {
match self { match self {
Corpus::SinglePath { path, .. } => { Corpus::SinglePath { 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 { paths, .. } => { Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>,
Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>
}
} }
} }
pub fn paths_iter_mut(&mut self) -> impl Iterator<Item = &mut PathBuf> { pub fn paths_iter_mut(&mut self) -> impl Iterator<Item = &mut PathBuf> {
match self { match self {
Corpus::SinglePath { path, .. } => { Corpus::SinglePath { 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 { paths, .. } => { Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>,
Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>
}
} }
} }
+19 -65
View File
@@ -44,9 +44,7 @@ impl MetadataFile {
if self.corpus_file_path.is_file() { if self.corpus_file_path.is_file() {
&self.corpus_file_path &self.corpus_file_path
} else { } else {
self.metadata_file_path self.metadata_file_path.strip_prefix(&self.corpus_file_path).unwrap()
.strip_prefix(&self.corpus_file_path)
.unwrap()
} }
} }
} }
@@ -159,19 +157,10 @@ impl Metadata {
return Ok(sources); return Ok(sources);
}; };
for ( for (alias, ContractPathAndIdent { contract_source_path, contract_ident }) in contracts {
alias,
ContractPathAndIdent {
contract_source_path,
contract_ident,
},
) in contracts
{
let alias = alias.clone(); let alias = alias.clone();
let absolute_path = directory let absolute_path =
.join(contract_source_path) directory.join(contract_source_path).canonicalize().map_err(|error| {
.canonicalize()
.map_err(|error| {
anyhow::anyhow!( anyhow::anyhow!(
"Failed to canonicalize contract source path '{}': {error}", "Failed to canonicalize contract source path '{}': {error}",
directory.join(contract_source_path).display() directory.join(contract_source_path).display()
@@ -181,10 +170,7 @@ impl Metadata {
sources.insert( sources.insert(
alias, alias,
ContractPathAndIdent { ContractPathAndIdent { contract_source_path: absolute_path, contract_ident },
contract_source_path: absolute_path,
contract_ident,
},
); );
} }
@@ -222,11 +208,11 @@ impl Metadata {
Ok(mut metadata) => { Ok(mut metadata) => {
metadata.file_path = Some(path.to_path_buf()); metadata.file_path = Some(path.to_path_buf());
Some(metadata) Some(metadata)
} },
Err(err) => { Err(err) => {
error!(path = %path.display(), %err, "Deserialization of metadata failed"); error!(path = %path.display(), %err, "Deserialization of metadata failed");
None None
} },
} }
} }
@@ -259,11 +245,11 @@ impl Metadata {
.into(), .into(),
); );
Some(metadata) Some(metadata)
} },
Err(err) => { Err(err) => {
error!(path = %path.display(), %err, "Failed to deserialize metadata"); error!(path = %path.display(), %err, "Failed to deserialize metadata");
None None
} },
} }
} }
@@ -337,12 +323,7 @@ pub struct ContractPathAndIdent {
impl Display for ContractPathAndIdent { impl Display for ContractPathAndIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(f, "{}:{}", self.contract_source_path.display(), self.contract_ident.as_ref())
f,
"{}:{}",
self.contract_source_path.display(),
self.contract_ident.as_ref()
)
} }
} }
@@ -362,7 +343,7 @@ impl FromStr for ContractPathAndIdent {
Some(ref mut path) => { Some(ref mut path) => {
path.push(':'); path.push(':');
path.push_str(next_item); path.push_str(next_item);
} },
None => path = Some(next_item.to_owned()), None => path = Some(next_item.to_owned()),
} }
} else { } else {
@@ -382,7 +363,7 @@ impl FromStr for ContractPathAndIdent {
contract_source_path: PathBuf::from(path), contract_source_path: PathBuf::from(path),
contract_ident: ContractIdent::new(identifier), contract_ident: ContractIdent::new(identifier),
}) })
} },
(None, None) => anyhow::bail!("Failed to find the path and identifier"), (None, None) => anyhow::bail!("Failed to find the path and identifier"),
} }
} }
@@ -420,43 +401,23 @@ pub struct EvmVersionRequirement {
impl EvmVersionRequirement { impl EvmVersionRequirement {
pub fn new_greater_than_or_equals(version: EVMVersion) -> Self { pub fn new_greater_than_or_equals(version: EVMVersion) -> Self {
Self { Self { ordering: Ordering::Greater, or_equal: true, evm_version: version }
ordering: Ordering::Greater,
or_equal: true,
evm_version: version,
}
} }
pub fn new_greater_than(version: EVMVersion) -> Self { pub fn new_greater_than(version: EVMVersion) -> Self {
Self { Self { ordering: Ordering::Greater, or_equal: false, evm_version: version }
ordering: Ordering::Greater,
or_equal: false,
evm_version: version,
}
} }
pub fn new_equals(version: EVMVersion) -> Self { pub fn new_equals(version: EVMVersion) -> Self {
Self { Self { ordering: Ordering::Equal, or_equal: false, evm_version: version }
ordering: Ordering::Equal,
or_equal: false,
evm_version: version,
}
} }
pub fn new_less_than(version: EVMVersion) -> Self { pub fn new_less_than(version: EVMVersion) -> Self {
Self { Self { ordering: Ordering::Less, or_equal: false, evm_version: version }
ordering: Ordering::Less,
or_equal: false,
evm_version: version,
}
} }
pub fn new_less_than_or_equals(version: EVMVersion) -> Self { pub fn new_less_than_or_equals(version: EVMVersion) -> Self {
Self { Self { ordering: Ordering::Less, or_equal: true, evm_version: version }
ordering: Ordering::Less,
or_equal: true,
evm_version: version,
}
} }
pub fn matches(&self, other: &EVMVersion) -> bool { pub fn matches(&self, other: &EVMVersion) -> bool {
@@ -467,11 +428,7 @@ impl EvmVersionRequirement {
impl Display for EvmVersionRequirement { impl Display for EvmVersionRequirement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { let Self { ordering, or_equal, evm_version } = self;
ordering,
or_equal,
evm_version,
} = self;
match ordering { match ordering {
Ordering::Less => write!(f, "<")?, Ordering::Less => write!(f, "<")?,
Ordering::Equal => write!(f, "=")?, Ordering::Equal => write!(f, "=")?,
@@ -598,10 +555,7 @@ mod test {
// Assert // Assert
let identifier = identifier.expect("Failed to parse"); let identifier = identifier.expect("Failed to parse");
assert_eq!( assert_eq!(identifier.contract_source_path.display().to_string(), "ERC20/ERC20.sol");
identifier.contract_source_path.display().to_string(),
"ERC20/ERC20.sol"
);
assert_eq!(identifier.contract_ident, "ERC20".to_owned().into()); assert_eq!(identifier.contract_ident, "ERC20".to_owned().into());
// Act // Act
+6 -20
View File
@@ -77,12 +77,7 @@ impl FromStr for ParsedMode {
None => None, None => None,
}; };
Ok(ParsedMode { Ok(ParsedMode { pipeline, optimize_flag, optimize_setting, version })
pipeline,
optimize_flag,
optimize_setting,
version,
})
} }
} }
@@ -138,13 +133,9 @@ impl ParsedMode {
|p| EitherIter::B(std::iter::once(*p)), |p| EitherIter::B(std::iter::once(*p)),
); );
let optimize_flag_setting = self.optimize_flag.map(|flag| { let optimize_flag_setting = self
if flag { .optimize_flag
ModeOptimizerSetting::M3 .map(|flag| if flag { ModeOptimizerSetting::M3 } else { ModeOptimizerSetting::M0 });
} else {
ModeOptimizerSetting::M0
}
});
let optimize_flag_iter = match optimize_flag_setting { let optimize_flag_iter = match optimize_flag_setting {
Some(setting) => EitherIter::A(std::iter::once(setting)), Some(setting) => EitherIter::A(std::iter::once(setting)),
@@ -157,9 +148,7 @@ impl ParsedMode {
); );
pipeline_iter.flat_map(move |pipeline| { pipeline_iter.flat_map(move |pipeline| {
optimize_settings_iter optimize_settings_iter.clone().map(move |optimize_setting| Mode {
.clone()
.map(move |optimize_setting| Mode {
pipeline, pipeline,
optimize_setting, optimize_setting,
version: self.version.clone(), version: self.version.clone(),
@@ -235,10 +224,7 @@ mod tests {
("Y+", vec!["Y M3"]), ("Y+", vec!["Y M3"]),
("Y-", vec!["Y M0"]), ("Y-", vec!["Y M0"]),
("Y <=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8"]), ("Y <=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8"]),
( ("<=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8", "E M0 <=0.8", "E M3 <=0.8"]),
"<=0.8",
vec!["Y M0 <=0.8", "Y M3 <=0.8", "E M0 <=0.8", "E M3 <=0.8"],
),
]; ];
for (actual, expected) in strings { for (actual, expected) in strings {
+32 -106
View File
@@ -78,12 +78,7 @@ impl StepPath {
impl Display for StepPath { impl Display for StepPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0 self.0.iter().map(|idx| idx.to_string()).collect::<Vec<_>>().join(".").fmt(f)
.iter()
.map(|idx| idx.to_string())
.collect::<Vec<_>>()
.join(".")
.fmt(f)
} }
} }
@@ -456,9 +451,7 @@ impl StepAddress {
impl FunctionCallStep { impl FunctionCallStep {
pub const fn default_caller_address() -> Address { pub const fn default_caller_address() -> Address {
Address(FixedBytes(alloy::hex!( Address(FixedBytes(alloy::hex!("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1")))
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"
)))
} }
pub const fn default_caller() -> StepAddress { pub const fn default_caller() -> StepAddress {
@@ -483,7 +476,7 @@ impl FunctionCallStep {
.context("Failed to produce calldata for deployer/fallback method")?; .context("Failed to produce calldata for deployer/fallback method")?;
Ok(calldata.into()) Ok(calldata.into())
} },
Method::FunctionName(ref function_name) => { Method::FunctionName(ref function_name) => {
let Some(abi) = context.deployed_contract_abi(&self.instance) else { let Some(abi) = context.deployed_contract_abi(&self.instance) else {
anyhow::bail!("ABI for instance '{}' not found", self.instance.as_ref()); anyhow::bail!("ABI for instance '{}' not found", self.instance.as_ref());
@@ -534,7 +527,7 @@ impl FunctionCallStep {
.context("Failed to append encoded argument to calldata buffer")?; .context("Failed to append encoded argument to calldata buffer")?;
Ok(calldata.into()) Ok(calldata.into())
} },
} }
} }
@@ -549,11 +542,9 @@ impl FunctionCallStep {
.await .await
.context("Failed to encode input bytes for transaction request")?; .context("Failed to encode input bytes for transaction request")?;
let caller = self.caller.resolve_address(resolver, context).await?; let caller = self.caller.resolve_address(resolver, context).await?;
let transaction_request = TransactionRequest::default().from(caller).value( let transaction_request = TransactionRequest::default()
self.value .from(caller)
.map(|value| value.into_inner()) .value(self.value.map(|value| value.into_inner()).unwrap_or_default());
.unwrap_or_default(),
);
match self.method { match self.method {
Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)), Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)),
_ => Ok(transaction_request _ => Ok(transaction_request
@@ -635,8 +626,7 @@ impl Calldata {
context: ResolutionContext<'_>, context: ResolutionContext<'_>,
) -> anyhow::Result<Vec<u8>> { ) -> anyhow::Result<Vec<u8>> {
let mut buffer = Vec::<u8>::with_capacity(self.size_requirement()); let mut buffer = Vec::<u8>::with_capacity(self.size_requirement());
self.calldata_into_slice(&mut buffer, resolver, context) self.calldata_into_slice(&mut buffer, resolver, context).await?;
.await?;
Ok(buffer) Ok(buffer)
} }
@@ -649,7 +639,7 @@ impl Calldata {
match self { match self {
Calldata::Single(bytes) => { Calldata::Single(bytes) => {
buffer.extend_from_slice(bytes); buffer.extend_from_slice(bytes);
} },
Calldata::Compound(items) => { Calldata::Compound(items) => {
let resolved = stream::iter(items.iter().enumerate()) let resolved = stream::iter(items.iter().enumerate())
.map(|(arg_idx, arg)| async move { .map(|(arg_idx, arg)| async move {
@@ -664,7 +654,7 @@ impl Calldata {
.context("Failed to resolve one or more calldata arguments")?; .context("Failed to resolve one or more calldata arguments")?;
buffer.extend(resolved.into_iter().flatten()); buffer.extend(resolved.into_iter().flatten());
} },
}; };
Ok(()) Ok(())
} }
@@ -713,7 +703,7 @@ impl Calldata {
.all(|v| async move { v.is_ok_and(|v| v) }) .all(|v| async move { v.is_ok_and(|v| v) })
.map(Ok) .map(Ok)
.await .await
} },
} }
} }
} }
@@ -727,10 +717,7 @@ impl CalldataItem {
) -> anyhow::Result<U256> { ) -> anyhow::Result<U256> {
let mut stack = Vec::<CalldataToken<U256>>::new(); let mut stack = Vec::<CalldataToken<U256>>::new();
for token in self for token in self.calldata_tokens().map(|token| token.resolve(resolver, context)) {
.calldata_tokens()
.map(|token| token.resolve(resolver, context))
{
let token = token.await?; let token = token.await?;
let new_token = match token { let new_token = match token {
CalldataToken::Item(_) => token, CalldataToken::Item(_) => token,
@@ -752,17 +739,15 @@ impl CalldataItem {
Operation::BitwiseAnd => Some(left_operand & right_operand), Operation::BitwiseAnd => Some(left_operand & right_operand),
Operation::BitwiseOr => Some(left_operand | right_operand), Operation::BitwiseOr => Some(left_operand | right_operand),
Operation::BitwiseXor => Some(left_operand ^ right_operand), Operation::BitwiseXor => Some(left_operand ^ right_operand),
Operation::ShiftLeft => { Operation::ShiftLeft =>
Some(left_operand << usize::try_from(right_operand)?) Some(left_operand << usize::try_from(right_operand)?),
} Operation::ShiftRight =>
Operation::ShiftRight => { Some(left_operand >> usize::try_from(right_operand)?),
Some(left_operand >> usize::try_from(right_operand)?)
}
} }
.context("Invalid calldata arithmetic operation - Invalid operation")?; .context("Invalid calldata arithmetic operation - Invalid operation")?;
CalldataToken::Item(result) CalldataToken::Item(result)
} },
}; };
stack.push(new_token) stack.push(new_token)
} }
@@ -771,9 +756,7 @@ impl CalldataItem {
// Empty stack means that we got an empty compound calldata which we resolve to zero. // Empty stack means that we got an empty compound calldata which we resolve to zero.
[] => Ok(U256::ZERO), [] => Ok(U256::ZERO),
[CalldataToken::Item(item)] => Ok(*item), [CalldataToken::Item(item)] => Ok(*item),
_ => Err(anyhow::anyhow!( _ => Err(anyhow::anyhow!("Invalid calldata arithmetic operation - Invalid stack")),
"Invalid calldata arithmetic operation - Invalid stack"
)),
} }
} }
@@ -917,16 +900,13 @@ impl<T: AsRef<str>> CalldataToken<T> {
.await .await
.map(U256::from) .map(U256::from)
} else if let Some(variable_name) = item.strip_prefix(Self::VARIABLE_PREFIX) { } else if let Some(variable_name) = item.strip_prefix(Self::VARIABLE_PREFIX) {
context context.variable(variable_name).context("Variable lookup failed").copied()
.variable(variable_name)
.context("Variable lookup failed")
.copied()
} else { } else {
U256::from_str_radix(item, 10) U256::from_str_radix(item, 10)
.map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error)) .map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))
}; };
value.map(CalldataToken::Item) value.map(CalldataToken::Item)
} },
Self::Operation(operation) => Ok(CalldataToken::Operation(operation)), Self::Operation(operation) => Ok(CalldataToken::Operation(operation)),
} }
} }
@@ -1050,13 +1030,7 @@ mod tests {
"#; "#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap(); let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap();
let selector = parsed_abi let selector = parsed_abi.function("store").unwrap().first().unwrap().selector().0;
.function("store")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input = FunctionCallStep { let input = FunctionCallStep {
instance: ContractInstance::new("Contract"), instance: ContractInstance::new("Contract"),
@@ -1094,13 +1068,7 @@ mod tests {
]"#; ]"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
let selector = parsed_abi let selector = parsed_abi.function("send").unwrap().first().unwrap().selector().0;
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: FunctionCallStep = FunctionCallStep { let input: FunctionCallStep = FunctionCallStep {
instance: "Contract".to_owned().into(), instance: "Contract".to_owned().into(),
@@ -1122,10 +1090,7 @@ mod tests {
type T = (alloy::primitives::Address,); type T = (alloy::primitives::Address,);
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap(); let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
assert_eq!( assert_eq!(decoded.0, address!("0x1000000000000000000000000000000000000001"));
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
} }
#[tokio::test] #[tokio::test]
@@ -1141,13 +1106,7 @@ mod tests {
]"#; ]"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
let selector = parsed_abi let selector = parsed_abi.function("send").unwrap().first().unwrap().selector().0;
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: FunctionCallStep = FunctionCallStep { let input: FunctionCallStep = FunctionCallStep {
instance: ContractInstance::new("Contract"), instance: ContractInstance::new("Contract"),
@@ -1169,10 +1128,7 @@ mod tests {
type T = (alloy::primitives::Address,); type T = (alloy::primitives::Address,);
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap(); let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
assert_eq!( assert_eq!(decoded.0, address!("0x1000000000000000000000000000000000000001"));
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
} }
async fn resolve_calldata_item( async fn resolve_calldata_item(
@@ -1209,12 +1165,7 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(
resolved, resolved,
U256::from( U256::from(MockResolver.block_gas_limit(Default::default()).await.unwrap())
MockResolver
.block_gas_limit(Default::default())
.await
.unwrap()
)
) )
} }
@@ -1231,11 +1182,7 @@ mod tests {
assert_eq!( assert_eq!(
resolved, resolved,
U256::from_be_slice( U256::from_be_slice(
MockResolver MockResolver.block_coinbase(Default::default()).await.unwrap().as_ref()
.block_coinbase(Default::default())
.await
.unwrap()
.as_ref()
) )
) )
} }
@@ -1250,13 +1197,7 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(resolved, MockResolver.block_difficulty(Default::default()).await.unwrap())
resolved,
MockResolver
.block_difficulty(Default::default())
.await
.unwrap()
)
} }
#[tokio::test] #[tokio::test]
@@ -1271,11 +1212,7 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(
resolved, resolved,
MockResolver MockResolver.block_base_fee(Default::default()).await.map(U256::from).unwrap()
.block_base_fee(Default::default())
.await
.map(U256::from)
.unwrap()
) )
} }
@@ -1305,10 +1242,7 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap()))
resolved,
U256::from(MockResolver.last_block_number().await.unwrap())
)
} }
#[tokio::test] #[tokio::test]
@@ -1323,12 +1257,7 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(
resolved, resolved,
U256::from( U256::from(MockResolver.block_timestamp(Default::default()).await.unwrap())
MockResolver
.block_timestamp(Default::default())
.await
.unwrap()
)
) )
} }
@@ -1406,10 +1335,7 @@ mod tests {
// Assert // Assert
let resolved = resolved.expect("Failed to resolve argument"); let resolved = resolved.expect("Failed to resolve argument");
assert_eq!( assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap() + 10));
resolved,
U256::from(MockResolver.last_block_number().await.unwrap() + 10)
);
} }
#[tokio::test] #[tokio::test]
+6 -7
View File
@@ -135,11 +135,11 @@ impl<'a> ResolutionContext<'a> {
match self.block_number { match self.block_number {
Some(block_number) => match number { Some(block_number) => match number {
BlockNumberOrTag::Latest => BlockNumberOrTag::Number(*block_number), BlockNumberOrTag::Latest => BlockNumberOrTag::Number(*block_number),
n @ (BlockNumberOrTag::Finalized n @ (BlockNumberOrTag::Finalized |
| BlockNumberOrTag::Safe BlockNumberOrTag::Safe |
| BlockNumberOrTag::Earliest BlockNumberOrTag::Earliest |
| BlockNumberOrTag::Pending BlockNumberOrTag::Pending |
| BlockNumberOrTag::Number(_)) => n, BlockNumberOrTag::Number(_)) => n,
}, },
None => number, None => number,
} }
@@ -162,8 +162,7 @@ impl<'a> ResolutionContext<'a> {
} }
pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> { pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> {
self.variables self.variables.and_then(|variables| variables.get(name.as_ref()))
.and_then(|variables| variables.get(name.as_ref()))
} }
pub fn tip_block_number(&self) -> Option<&'a BlockNumber> { pub fn tip_block_number(&self) -> Option<&'a BlockNumber> {
+237 -139
View File
@@ -21,7 +21,7 @@ use std::{
io::{BufRead, BufReader, BufWriter, Write}, io::{BufRead, BufReader, BufWriter, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
time::Instant, time::{Duration, Instant},
}; };
use temp_dir::TempDir; use temp_dir::TempDir;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@@ -40,6 +40,10 @@ struct MlTestRunnerArgs {
#[arg(long = "cached-passed")] #[arg(long = "cached-passed")]
cached_passed: Option<PathBuf>, cached_passed: Option<PathBuf>,
/// File to store tests that have failed (defaults to .<platform>-failed)
#[arg(long = "cached-failed")]
cached_failed: Option<PathBuf>,
/// Stop after the first file failure /// Stop after the first file failure
#[arg(long = "bail")] #[arg(long = "bail")]
bail: bool, bail: bool,
@@ -62,16 +66,24 @@ struct MlTestRunnerArgs {
/// RPC port to connect to when using existing node /// RPC port to connect to when using existing node
#[arg(long = "rpc-port", default_value = "8545")] #[arg(long = "rpc-port", default_value = "8545")]
rpc_port: u16, rpc_port: u16,
/// Show verbose output including cached tests and detailed error messages
#[arg(long = "verbose", short = 'v')]
verbose: bool,
} }
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let args = MlTestRunnerArgs::parse();
// Only set up tracing if RUST_LOG is explicitly set or --verbose is passed
if std::env::var("RUST_LOG").is_ok() || args.verbose {
let subscriber = FmtSubscriber::builder() let subscriber = FmtSubscriber::builder()
.with_env_filter(EnvFilter::from_default_env()) .with_env_filter(EnvFilter::from_default_env())
.with_writer(std::io::stderr) .with_writer(std::io::stderr)
.finish(); .finish();
tracing::subscriber::set_global_default(subscriber).expect("Failed to set tracing subscriber"); tracing::subscriber::set_global_default(subscriber)
.expect("Failed to set tracing subscriber");
let args = MlTestRunnerArgs::parse(); }
info!("ML test runner starting"); info!("ML test runner starting");
info!("Platform: {:?}", args.platform); info!("Platform: {:?}", args.platform);
@@ -84,6 +96,40 @@ fn main() -> anyhow::Result<()> {
.block_on(run(args)) .block_on(run(args))
} }
/// Wait for HTTP server to be ready by attempting to connect to the specified port
async fn wait_for_http_server(port: u16) -> anyhow::Result<()> {
const MAX_RETRIES: u32 = 60;
const RETRY_DELAY: Duration = Duration::from_secs(1);
for attempt in 1..=MAX_RETRIES {
match tokio::net::TcpStream::connect(format!("127.0.0.1:{}", port)).await {
Ok(_) => {
info!("Successfully connected to HTTP server on port {} (attempt {})", port, attempt);
return Ok(());
},
Err(e) => {
if attempt == MAX_RETRIES {
anyhow::bail!(
"Failed to connect to HTTP server on port {} after {} attempts: {}",
port,
MAX_RETRIES,
e
);
}
if attempt % 10 == 0 {
info!(
"Still waiting for HTTP server on port {} (attempt {}/{})",
port, attempt, MAX_RETRIES
);
}
tokio::time::sleep(RETRY_DELAY).await;
},
}
}
unreachable!()
}
async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> { async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
let start_time = Instant::now(); let start_time = Instant::now();
@@ -101,6 +147,84 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
let cached_passed = Arc::new(Mutex::new(cached_passed)); let cached_passed = Arc::new(Mutex::new(cached_passed));
// Set up cached-failed file (defaults to .<platform>-failed)
let cached_failed_path = args
.cached_failed
.clone()
.unwrap_or_else(|| PathBuf::from(format!(".{:?}-failed", args.platform)));
let cached_failed = Arc::new(Mutex::new(HashSet::<String>::new()));
// Get the platform based on CLI args
let platform: &dyn Platform = match args.platform {
PlatformIdentifier::GethEvmSolc => &revive_dt_core::GethEvmSolcPlatform,
PlatformIdentifier::LighthouseGethEvmSolc => &revive_dt_core::LighthouseGethEvmSolcPlatform,
PlatformIdentifier::KitchensinkPolkavmResolc =>
&revive_dt_core::KitchensinkPolkavmResolcPlatform,
PlatformIdentifier::KitchensinkRevmSolc => &revive_dt_core::KitchensinkRevmSolcPlatform,
PlatformIdentifier::ReviveDevNodePolkavmResolc =>
&revive_dt_core::ReviveDevNodePolkavmResolcPlatform,
PlatformIdentifier::ReviveDevNodeRevmSolc => &revive_dt_core::ReviveDevNodeRevmSolcPlatform,
PlatformIdentifier::ZombienetPolkavmResolc =>
&revive_dt_core::ZombienetPolkavmResolcPlatform,
PlatformIdentifier::ZombienetRevmSolc => &revive_dt_core::ZombienetRevmSolcPlatform,
};
let test_context = TestExecutionContext::default();
let context = revive_dt_config::Context::Test(Box::new(test_context));
let node: &'static dyn revive_dt_node_interaction::EthereumNode = if args.start_platform {
info!("Starting blockchain node...");
let node_handle =
platform.new_node(context.clone()).context("Failed to spawn node thread")?;
info!("Waiting for node to start...");
let node = node_handle
.join()
.map_err(|e| anyhow::anyhow!("Node thread panicked: {:?}", e))?
.context("Failed to start node")?;
info!("Node started with ID: {}, connection: {}", node.id(), node.connection_string());
let node = Box::leak(node);
info!("Running pre-transactions...");
node.pre_transactions().await.context("Failed to run pre-transactions")?;
info!("Pre-transactions completed");
node
} else {
info!("Using existing node at port {}", args.rpc_port);
// Wait for the HTTP server to be ready
info!("Waiting for HTTP server to be ready on port {}...", args.rpc_port);
wait_for_http_server(args.rpc_port).await?;
info!("HTTP server is ready");
let existing_node: Box<dyn revive_dt_node_interaction::EthereumNode> = match args.platform {
PlatformIdentifier::GethEvmSolc | PlatformIdentifier::LighthouseGethEvmSolc =>
Box::new(
revive_dt_node::node_implementations::geth::GethNode::new_existing(
&args.private_key,
args.rpc_port,
)
.await?,
),
PlatformIdentifier::KitchensinkPolkavmResolc |
PlatformIdentifier::KitchensinkRevmSolc |
PlatformIdentifier::ReviveDevNodePolkavmResolc |
PlatformIdentifier::ReviveDevNodeRevmSolc |
PlatformIdentifier::ZombienetPolkavmResolc |
PlatformIdentifier::ZombienetRevmSolc => Box::new(
revive_dt_node::node_implementations::substrate::SubstrateNode::new_existing(
&args.private_key,
args.rpc_port,
)
.await?,
),
};
Box::leak(existing_node)
};
let mut passed_files = 0; let mut passed_files = 0;
let mut failed_files = 0; let mut failed_files = 0;
let mut skipped_files = 0; let mut skipped_files = 0;
@@ -116,11 +240,14 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
for test_file in test_files { for test_file in test_files {
let file_display = test_file.display().to_string(); let file_display = test_file.display().to_string();
info!("\n\n == Executing test file: {file_display} == \n\n");
// Check if already passed // Check if already passed
{ {
let cache = cached_passed.lock().await; let cache = cached_passed.lock().await;
if cache.contains(&file_display) { if cache.contains(&file_display) {
println!("test {} ... {YELLOW}cached{COLOUR_RESET}", file_display); if args.verbose {
println!("test {file_display} ... {YELLOW}cached{COLOUR_RESET}");
}
skipped_files += 1; skipped_files += 1;
continue; continue;
} }
@@ -131,56 +258,68 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
Ok(mf) => { Ok(mf) => {
info!("Loaded metadata with {} case(s)", mf.cases.len()); info!("Loaded metadata with {} case(s)", mf.cases.len());
mf mf
} },
Err(e) => { Err(e) => {
println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display); // Skip files without metadata instead of treating them as failures
println!(" Error loading metadata: {}", e); info!("Skipping {} (no metadata): {}", file_display, e);
failed_files += 1; skipped_files += 1;
failures.push((
file_display.clone(),
format!("Error loading metadata: {}", e),
));
if args.bail {
break;
}
continue; continue;
} },
}; };
info!("Executing test file: {}", file_display); // Execute test with 10 second timeout
match execute_test_file(&args, &metadata_file).await { let test_result = tokio::time::timeout(
Duration::from_secs(20),
execute_test_file(&metadata_file, platform, node, &context),
)
.await;
let result = match test_result {
Ok(Ok(_)) => Ok(()),
Ok(Err(e)) => Err(e),
Err(_) => Err(anyhow::anyhow!("Test timed out after 20 seconds")),
};
match result {
Ok(_) => { Ok(_) => {
println!("test {} ... {GREEN}ok{COLOUR_RESET}", file_display); println!("test {file_display} ... {GREEN}ok{COLOUR_RESET}");
info!("Test file passed: {}", file_display);
passed_files += 1; passed_files += 1;
{ // Update cache
if let Some(cache_file) = &args.cached_passed {
let mut cache = cached_passed.lock().await; let mut cache = cached_passed.lock().await;
cache.insert(file_display); cache.insert(file_display);
if let Err(e) = save_cached_passed(cache_file, &cache) {
info!("Failed to save cache: {}", e);
} }
} }
},
Err(e) => { Err(e) => {
println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display); println!("test {file_display} ... {RED}FAILED{COLOUR_RESET}");
failed_files += 1; failed_files += 1;
failures.push((file_display, format!("{:?}", e))); let error_detail = if args.verbose { format!("{:?}", e) } else { format!("{}", e) };
failures.push((file_display.clone(), error_detail));
// Update cached-failed
{
let mut cache = cached_failed.lock().await;
cache.insert(file_display);
if let Err(e) = save_cached_failed(&cached_failed_path, &cache) {
info!("Failed to save cached-failed: {}", e);
}
}
if args.bail { if args.bail {
info!("Bailing after first failure"); info!("Bailing after first failure");
break; break;
} }
},
} }
} }
}
if let Some(cache_file) = &args.cached_passed {
let cache = cached_passed.lock().await;
info!("Saving {} cached passed test(s)", cache.len());
save_cached_passed(cache_file, &cache)?;
}
// Print summary // Print summary
println!(); println!();
if !failures.is_empty() { if !failures.is_empty() && args.verbose {
println!("{BOLD}failures:{BOLD_RESET}"); println!("{BOLD}failures:{BOLD_RESET}");
println!(); println!();
for (file, error) in &failures { for (file, error) in &failures {
@@ -226,7 +365,7 @@ fn discover_test_files(path: &Path) -> anyhow::Result<Vec<PathBuf>> {
"sol" => { "sol" => {
// Single .sol file // Single .sol file
files.push(path.to_path_buf()); files.push(path.to_path_buf());
} },
"json" => { "json" => {
// Corpus file - enumerate its tests // Corpus file - enumerate its tests
let corpus = Corpus::try_from_path(path)?; let corpus = Corpus::try_from_path(path)?;
@@ -234,19 +373,47 @@ fn discover_test_files(path: &Path) -> anyhow::Result<Vec<PathBuf>> {
for metadata in metadata_files { for metadata in metadata_files {
files.push(metadata.metadata_file_path); files.push(metadata.metadata_file_path);
} }
} },
_ => anyhow::bail!( _ => anyhow::bail!("Unsupported file extension: {}. Expected .sol or .json", extension),
"Unsupported file extension: {}. Expected .sol or .json",
extension
),
} }
} else if path.is_dir() { } else if path.is_dir() {
// Walk directory recursively for .sol files // First, find all test.json files
for entry in FilesWithExtensionIterator::new(path) let mut test_json_dirs = HashSet::new();
for json_file in FilesWithExtensionIterator::new(path)
.with_allowed_extension("json")
.with_use_cached_fs(true)
{
if json_file.file_name().and_then(|s| s.to_str()) == Some("test.json") {
if let Some(parent) = json_file.parent() {
test_json_dirs.insert(parent.to_path_buf());
}
// Try to parse as corpus file first, then as metadata file
if let Ok(corpus) = Corpus::try_from_path(&json_file) {
// It's a corpus file - enumerate its tests
let metadata_files = corpus.enumerate_tests();
for metadata in metadata_files {
files.push(metadata.metadata_file_path);
}
} else {
// It's a metadata file - use it directly
files.push(json_file);
}
}
}
// Then, find .sol files that are NOT in directories with test.json
for sol_file in FilesWithExtensionIterator::new(path)
.with_allowed_extension("sol") .with_allowed_extension("sol")
.with_use_cached_fs(true) .with_use_cached_fs(true)
{ {
files.push(entry); if let Some(parent) = sol_file.parent() {
if !test_json_dirs.contains(parent) {
files.push(sol_file);
}
} else {
files.push(sol_file);
}
} }
} else { } else {
anyhow::bail!("Path is neither a file nor a directory: {}", path.display()); anyhow::bail!("Path is neither a file nor a directory: {}", path.display());
@@ -269,8 +436,10 @@ fn load_metadata_file(path: &Path) -> anyhow::Result<MetadataFile> {
/// Execute all test cases in a metadata file /// Execute all test cases in a metadata file
async fn execute_test_file( async fn execute_test_file(
args: &MlTestRunnerArgs,
metadata_file: &MetadataFile, metadata_file: &MetadataFile,
platform: &dyn Platform,
node: &'static dyn revive_dt_node_interaction::EthereumNode,
context: &revive_dt_config::Context,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
if metadata_file.cases.is_empty() { if metadata_file.cases.is_empty() {
anyhow::bail!("No test cases found in file"); anyhow::bail!("No test cases found in file");
@@ -278,103 +447,24 @@ async fn execute_test_file(
info!("Processing {} test case(s)", metadata_file.cases.len()); info!("Processing {} test case(s)", metadata_file.cases.len());
// Get the platform based on CLI args
let platform: &dyn Platform = match args.platform {
PlatformIdentifier::GethEvmSolc => &revive_dt_core::GethEvmSolcPlatform,
PlatformIdentifier::LighthouseGethEvmSolc => &revive_dt_core::LighthouseGethEvmSolcPlatform,
PlatformIdentifier::KitchensinkPolkavmResolc => {
&revive_dt_core::KitchensinkPolkavmResolcPlatform
}
PlatformIdentifier::KitchensinkRevmSolc => &revive_dt_core::KitchensinkRevmSolcPlatform,
PlatformIdentifier::ReviveDevNodePolkavmResolc => {
&revive_dt_core::ReviveDevNodePolkavmResolcPlatform
}
PlatformIdentifier::ReviveDevNodeRevmSolc => &revive_dt_core::ReviveDevNodeRevmSolcPlatform,
PlatformIdentifier::ZombienetPolkavmResolc => {
&revive_dt_core::ZombienetPolkavmResolcPlatform
}
PlatformIdentifier::ZombienetRevmSolc => &revive_dt_core::ZombienetRevmSolcPlatform,
};
let temp_dir = TempDir::new()?; let temp_dir = TempDir::new()?;
info!("Created temporary directory: {}", temp_dir.path().display()); info!("Created temporary directory: {}", temp_dir.path().display());
let test_context = TestExecutionContext::default();
let context = revive_dt_config::Context::Test(Box::new(test_context));
let node: &'static dyn revive_dt_node_interaction::EthereumNode = if args.start_platform {
info!("Starting blockchain node...");
let node_handle = platform
.new_node(context.clone())
.context("Failed to spawn node thread")?;
info!("Waiting for node to start...");
let node = node_handle
.join()
.map_err(|e| anyhow::anyhow!("Node thread panicked: {:?}", e))?
.context("Failed to start node")?;
info!(
"Node started with ID: {}, connection: {}",
node.id(),
node.connection_string()
);
let node = Box::leak(node);
info!("Running pre-transactions...");
node.pre_transactions()
.await
.context("Failed to run pre-transactions")?;
info!("Pre-transactions completed");
node
} else {
info!("Using existing node");
let existing_node: Box<dyn revive_dt_node_interaction::EthereumNode> = match args.platform {
PlatformIdentifier::GethEvmSolc | PlatformIdentifier::LighthouseGethEvmSolc => {
Box::new(
revive_dt_node::node_implementations::geth::GethNode::new_existing(
&args.private_key,
args.rpc_port,
)
.await?,
)
}
PlatformIdentifier::KitchensinkPolkavmResolc
| PlatformIdentifier::KitchensinkRevmSolc
| PlatformIdentifier::ReviveDevNodePolkavmResolc
| PlatformIdentifier::ReviveDevNodeRevmSolc
| PlatformIdentifier::ZombienetPolkavmResolc
| PlatformIdentifier::ZombienetRevmSolc => Box::new(
revive_dt_node::node_implementations::substrate::SubstrateNode::new_existing(
&args.private_key,
args.rpc_port,
)
.await?,
),
};
Box::leak(existing_node)
};
info!("Initializing cached compiler"); info!("Initializing cached compiler");
let cached_compiler = CachedCompiler::new(temp_dir.path().join("compilation_cache"), false) let cached_compiler = CachedCompiler::new(temp_dir.path().join("compilation_cache"), false)
.await .await
.map(Arc::new) .map(Arc::new)
.context("Failed to create cached compiler")?; .context("Failed to create cached compiler")?;
let private_key_allocator = Arc::new(Mutex::new(PrivateKeyAllocator::new( let private_key_allocator =
alloy::primitives::U256::from(100), Arc::new(Mutex::new(PrivateKeyAllocator::new(alloy::primitives::U256::from(100))));
)));
let (reporter, report_task) = let (reporter, report_task) =
revive_dt_report::ReportAggregator::new(context.clone()).into_task(); revive_dt_report::ReportAggregator::new(context.clone()).into_task();
tokio::spawn(report_task); tokio::spawn(report_task);
info!( info!("Building test definitions for {} case(s)", metadata_file.cases.len());
"Building test definitions for {} case(s)",
metadata_file.cases.len()
);
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() {
info!("Building test definition for case {}", case_idx); info!("Building test definition for case {}", case_idx);
@@ -421,20 +511,17 @@ async fn execute_test_file(
test_definition.case.steps.len(), test_definition.case.steps.len(),
test_definition.case_idx test_definition.case_idx
); );
let steps_executed = driver.execute_all().await.context(format!( let steps_executed = driver
"Failed to execute case {}", .execute_all()
test_definition.case_idx .await
))?; .context(format!("Failed to execute case {}", test_definition.case_idx))?;
info!( info!(
"✓ Case {} completed successfully, executed {} step(s)", "✓ Case {} completed successfully, executed {} step(s)",
test_definition.case_idx, steps_executed test_definition.case_idx, steps_executed
); );
} }
info!("─────────────────────────────────────────────────────────────────"); info!("─────────────────────────────────────────────────────────────────");
info!( info!("All {} test case(s) executed successfully", test_definitions.len());
"All {} test case(s) executed successfully",
test_definitions.len()
);
Ok(()) Ok(())
} }
@@ -477,12 +564,7 @@ async fn build_test_definition<'a>(
let mut platforms = BTreeMap::new(); let mut platforms = BTreeMap::new();
platforms.insert( platforms.insert(
platform.platform_identifier(), platform.platform_identifier(),
TestPlatformInformation { TestPlatformInformation { platform, node, compiler, reporter: execution_reporter },
platform,
node,
compiler,
reporter: execution_reporter,
},
); );
let test_definition = TestDefinition { let test_definition = TestDefinition {
@@ -496,7 +578,7 @@ async fn build_test_definition<'a>(
}; };
if let Err((reason, _)) = test_definition.check_compatibility() { if let Err((reason, _)) = test_definition.check_compatibility() {
println!(" Skipping case {}: {}", case_idx, reason); info!("Skipping case {}: {}", case_idx, reason);
return Ok(None); return Ok(None);
} }
@@ -539,3 +621,19 @@ fn save_cached_passed(path: &Path, cache: &HashSet<String>) -> anyhow::Result<()
writer.flush()?; writer.flush()?;
Ok(()) Ok(())
} }
/// Save cached failed tests to file
fn save_cached_failed(path: &Path, cache: &HashSet<String>) -> anyhow::Result<()> {
let file = File::create(path).context("Failed to create cached-failed file")?;
let mut writer = BufWriter::new(file);
let mut entries: Vec<_> = cache.iter().collect();
entries.sort();
for entry in entries {
writeln!(writer, "{}", entry)?;
}
writer.flush()?;
Ok(())
}
+13 -32
View File
@@ -33,10 +33,7 @@ impl Process {
let log_file_prefix = log_file_prefix.into(); let log_file_prefix = log_file_prefix.into();
let (stdout_file_name, stderr_file_name) = match log_file_prefix { let (stdout_file_name, stderr_file_name) = match log_file_prefix {
Some(prefix) => ( Some(prefix) => (format!("{prefix}_stdout.log"), format!("{prefix}_stderr.log")),
format!("{prefix}_stdout.log"),
format!("{prefix}_stderr.log"),
),
None => ("stdout.log".to_string(), "stderr.log".to_string()), None => ("stdout.log".to_string(), "stderr.log".to_string()),
}; };
@@ -57,23 +54,19 @@ impl Process {
.context("Failed to open the stderr logs file")?; .context("Failed to open the stderr logs file")?;
let mut command = { let mut command = {
let stdout_logs_file = stdout_logs_file let stdout_logs_file =
.try_clone() stdout_logs_file.try_clone().context("Failed to clone the stdout logs file")?;
.context("Failed to clone the stdout logs file")?; let stderr_logs_file =
let stderr_logs_file = stderr_logs_file stderr_logs_file.try_clone().context("Failed to clone the stderr logs file")?;
.try_clone()
.context("Failed to clone the stderr logs file")?;
let mut command = Command::new(binary_path.as_ref()); let mut command = Command::new(binary_path.as_ref());
command_building_callback(&mut command, stdout_logs_file, stderr_logs_file); command_building_callback(&mut command, stdout_logs_file, stderr_logs_file);
command command
}; };
let mut child = command let mut child = command.spawn().context("Failed to spawn the built command")?;
.spawn()
.context("Failed to spawn the built command")?;
match process_readiness_wait_behavior { match process_readiness_wait_behavior {
ProcessReadinessWaitBehavior::NoStartupWait => {} ProcessReadinessWaitBehavior::NoStartupWait => {},
ProcessReadinessWaitBehavior::WaitDuration(duration) => std::thread::sleep(duration), ProcessReadinessWaitBehavior::WaitDuration(duration) => std::thread::sleep(duration),
ProcessReadinessWaitBehavior::TimeBoundedWaitFunction { ProcessReadinessWaitBehavior::TimeBoundedWaitFunction {
max_wait_duration, max_wait_duration,
@@ -126,35 +119,23 @@ impl Process {
) )
} }
} }
} },
ProcessReadinessWaitBehavior::WaitForCommandToExit => { ProcessReadinessWaitBehavior::WaitForCommandToExit => {
if !child if !child.wait().context("Failed waiting for process to finish")?.success() {
.wait()
.context("Failed waiting for process to finish")?
.success()
{
anyhow::bail!("Failed to spawn command"); anyhow::bail!("Failed to spawn command");
} }
} },
} }
Ok(Self { Ok(Self { child, stdout_logs_file, stderr_logs_file })
child,
stdout_logs_file,
stderr_logs_file,
})
} }
} }
impl Drop for Process { impl Drop for Process {
fn drop(&mut self) { fn drop(&mut self) {
self.child.kill().expect("Failed to kill the process"); self.child.kill().expect("Failed to kill the process");
self.stdout_logs_file self.stdout_logs_file.flush().expect("Failed to flush the stdout logs file");
.flush() self.stderr_logs_file.flush().expect("Failed to flush the stderr logs file");
.expect("Failed to flush the stdout logs file");
self.stderr_logs_file
.flush()
.expect("Failed to flush the stderr logs file");
} }
} }
+25 -70
View File
@@ -94,8 +94,8 @@ impl GethNode {
const TRANSACTION_INDEXING_ERROR: &str = "transaction indexing is in progress"; const TRANSACTION_INDEXING_ERROR: &str = "transaction indexing is in progress";
const TRANSACTION_TRACING_ERROR: &str = "historical state not available in path scheme yet"; const TRANSACTION_TRACING_ERROR: &str = "historical state not available in path scheme yet";
const RECEIPT_POLLING_DURATION: Duration = Duration::from_secs(5 * 60); const RECEIPT_POLLING_DURATION: Duration = Duration::from_secs(10);
const TRACE_POLLING_DURATION: Duration = Duration::from_secs(60); const TRACE_POLLING_DURATION: Duration = Duration::from_secs(10);
pub fn new( pub fn new(
context: impl AsRef<WorkingDirectoryConfiguration> context: impl AsRef<WorkingDirectoryConfiguration>
@@ -108,9 +108,7 @@ impl GethNode {
let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context); let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context);
let geth_configuration = AsRef::<GethConfiguration>::as_ref(&context); let geth_configuration = AsRef::<GethConfiguration>::as_ref(&context);
let geth_directory = working_directory_configuration let geth_directory = working_directory_configuration.as_path().join(Self::BASE_DIRECTORY);
.as_path()
.join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = geth_directory.join(id.to_string()); let base_directory = geth_directory.join(id.to_string());
@@ -139,10 +137,7 @@ impl GethNode {
signers::local::PrivateKeySigner, signers::local::PrivateKeySigner,
}; };
let key_str = private_key let key_str = private_key.trim().strip_prefix("0x").unwrap_or(private_key.trim());
.trim()
.strip_prefix("0x")
.unwrap_or(private_key.trim());
let key_bytes = alloy::hex::decode(key_str) let key_bytes = alloy::hex::decode(key_str)
.map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?;
@@ -336,15 +331,14 @@ impl GethNode {
ProcessReadinessWaitBehavior::TimeBoundedWaitFunction { ProcessReadinessWaitBehavior::TimeBoundedWaitFunction {
max_wait_duration: self.start_timeout, max_wait_duration: self.start_timeout,
check_function: Box::new(|_, stderr_line| match stderr_line { check_function: Box::new(|_, stderr_line| match stderr_line {
Some(line) => { Some(line) =>
if line.contains(Self::ERROR_MARKER) { if line.contains(Self::ERROR_MARKER) {
anyhow::bail!("Failed to start geth {line}"); anyhow::bail!("Failed to start geth {line}");
} else if line.contains(Self::READY_MARKER) { } else if line.contains(Self::READY_MARKER) {
Ok(true) Ok(true)
} else { } else {
Ok(false) Ok(false)
} },
}
None => Ok(false), None => Ok(false),
}), }),
}, },
@@ -357,7 +351,7 @@ impl GethNode {
self.shutdown() self.shutdown()
.context("Failed to gracefully shutdown after geth start error")?; .context("Failed to gracefully shutdown after geth start error")?;
return Err(err); return Err(err);
} },
} }
Ok(self) Ok(self)
@@ -497,15 +491,12 @@ impl EthereumNode for GethNode {
true => Ok(ControlFlow::Continue(())), true => Ok(ControlFlow::Continue(())),
false => Err(error.into()), false => Err(error.into()),
} }
} },
} }
} }
}, },
) )
.instrument(tracing::info_span!( .instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash))
"Awaiting transaction receipt",
?transaction_hash
))
.await .await
}) })
} }
@@ -517,10 +508,8 @@ impl EthereumNode for GethNode {
trace_options: GethDebugTracingOptions, trace_options: GethDebugTracingOptions,
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
Box::pin(async move { Box::pin(async move {
let provider = self let provider =
.provider() self.provider().await.context("Failed to create provider for tracing")?;
.await
.context("Failed to create provider for tracing")?;
poll( poll(
Self::TRACE_POLLING_DURATION, Self::TRACE_POLLING_DURATION,
PollingWaitBehavior::Constant(Duration::from_millis(200)), PollingWaitBehavior::Constant(Duration::from_millis(200)),
@@ -528,10 +517,7 @@ impl EthereumNode for GethNode {
let provider = provider.clone(); let provider = provider.clone();
let trace_options = trace_options.clone(); let trace_options = trace_options.clone();
async move { async move {
match provider match provider.debug_trace_transaction(tx_hash, trace_options).await {
.debug_trace_transaction(tx_hash, trace_options)
.await
{
Ok(trace) => Ok(ControlFlow::Break(trace)), Ok(trace) => Ok(ControlFlow::Break(trace)),
Err(error) => { Err(error) => {
let error_string = error.to_string(); let error_string = error.to_string();
@@ -539,7 +525,7 @@ impl EthereumNode for GethNode {
true => Ok(ControlFlow::Continue(())), true => Ok(ControlFlow::Continue(())),
false => Err(error.into()), false => Err(error.into()),
} }
} },
} }
} }
}, },
@@ -754,10 +740,7 @@ impl ResolverApi for GethNodeResolver {
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -874,11 +857,7 @@ mod tests {
// Arrange // Arrange
let (context, node) = shared_state(); let (context, node) = shared_state();
let account_address = context let account_address = context.wallet_configuration.wallet().default_signer().address();
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default() let transaction = TransactionRequest::default()
.to(account_address) .to(account_address)
.value(U256::from(100_000_000_000_000u128)); .value(U256::from(100_000_000_000_000u128));
@@ -901,10 +880,7 @@ mod tests {
// Assert // Assert
let version = version.expect("Failed to get the version"); let version = version.expect("Failed to get the version");
assert!( assert!(version.starts_with("geth version"), "expected version string, got: '{version}'");
version.starts_with("geth version"),
"expected version string, got: '{version}'"
);
} }
#[tokio::test] #[tokio::test]
@@ -928,12 +904,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -946,12 +918,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -964,12 +932,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -982,12 +946,7 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -1000,12 +959,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -116,7 +116,7 @@ impl LighthouseGethNode {
const TRANSACTION_INDEXING_ERROR: &str = "transaction indexing is in progress"; const TRANSACTION_INDEXING_ERROR: &str = "transaction indexing is in progress";
const TRANSACTION_TRACING_ERROR: &str = "historical state not available in path scheme yet"; const TRANSACTION_TRACING_ERROR: &str = "historical state not available in path scheme yet";
const RECEIPT_POLLING_DURATION: Duration = Duration::from_secs(5 * 60); const RECEIPT_POLLING_DURATION: Duration = Duration::from_secs(30);
const TRACE_POLLING_DURATION: Duration = Duration::from_secs(60); const TRACE_POLLING_DURATION: Duration = Duration::from_secs(60);
const VALIDATOR_MNEMONIC: &str = "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete"; const VALIDATOR_MNEMONIC: &str = "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete";
@@ -132,9 +132,7 @@ impl LighthouseGethNode {
let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context); let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context);
let kurtosis_configuration = AsRef::<KurtosisConfiguration>::as_ref(&context); let kurtosis_configuration = AsRef::<KurtosisConfiguration>::as_ref(&context);
let geth_directory = working_directory_configuration let geth_directory = working_directory_configuration.as_path().join(Self::BASE_DIRECTORY);
.as_path()
.join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = geth_directory.join(id.to_string()); let base_directory = geth_directory.join(id.to_string());
@@ -147,10 +145,7 @@ impl LighthouseGethNode {
http_connection_string: String::default(), http_connection_string: String::default(),
enclave_name: format!( enclave_name: format!(
"enclave-{}-{}", "enclave-{}-{}",
SystemTime::now() SystemTime::now().duration_since(UNIX_EPOCH).expect("Must not fail").as_nanos(),
.duration_since(UNIX_EPOCH)
.expect("Must not fail")
.as_nanos(),
id id
), ),
@@ -526,15 +521,12 @@ impl LighthouseGethNode {
true => Ok(ControlFlow::Continue(())), true => Ok(ControlFlow::Continue(())),
false => Err(error.into()), false => Err(error.into()),
} }
} },
} }
} }
}, },
) )
.instrument(tracing::info_span!( .instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash))
"Awaiting transaction receipt",
?transaction_hash
))
.await .await
}) })
} }
@@ -623,9 +615,7 @@ impl EthereumNode for LighthouseGethNode {
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
Box::pin(async move { Box::pin(async move {
let provider = Arc::new( let provider = Arc::new(
self.http_provider() self.http_provider().await.context("Failed to create provider for tracing")?,
.await
.context("Failed to create provider for tracing")?,
); );
poll( poll(
Self::TRACE_POLLING_DURATION, Self::TRACE_POLLING_DURATION,
@@ -634,10 +624,7 @@ impl EthereumNode for LighthouseGethNode {
let provider = provider.clone(); let provider = provider.clone();
let trace_options = trace_options.clone(); let trace_options = trace_options.clone();
async move { async move {
match provider match provider.debug_trace_transaction(tx_hash, trace_options).await {
.debug_trace_transaction(tx_hash, trace_options)
.await
{
Ok(trace) => Ok(ControlFlow::Break(trace)), Ok(trace) => Ok(ControlFlow::Break(trace)),
Err(error) => { Err(error) => {
let error_string = error.to_string(); let error_string = error.to_string();
@@ -645,7 +632,7 @@ impl EthereumNode for LighthouseGethNode {
true => Ok(ControlFlow::Continue(())), true => Ok(ControlFlow::Continue(())),
false => Err(error.into()), false => Err(error.into()),
} }
} },
} }
} }
}, },
@@ -859,10 +846,7 @@ impl<F: TxFiller<Ethereum>, P: Provider<Ethereum>> ResolverApi
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -916,11 +900,7 @@ impl Node for LighthouseGethNode {
.spawn() .spawn()
.expect("Failed to spawn the enclave kill command"); .expect("Failed to spawn the enclave kill command");
if !child if !child.wait().expect("Failed to wait for the enclave kill command").success() {
.wait()
.expect("Failed to wait for the enclave kill command")
.success()
{
let stdout = { let stdout = {
let mut stdout = String::default(); let mut stdout = String::default();
child child
@@ -1146,11 +1126,7 @@ mod tests {
let (context, node) = new_node(); let (context, node) = new_node();
node.fund_all_accounts().await.expect("Failed"); node.fund_all_accounts().await.expect("Failed");
let account_address = context let account_address = context.wallet_configuration.wallet().default_signer().address();
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default() let transaction = TransactionRequest::default()
.to(account_address) .to(account_address)
.value(U256::from(100_000_000_000_000u128)); .value(U256::from(100_000_000_000_000u128));
@@ -1173,10 +1149,7 @@ mod tests {
// Assert // Assert
let version = version.expect("Failed to get the version"); let version = version.expect("Failed to get the version");
assert!( assert!(version.starts_with("CLI Version"), "expected version string, got: '{version}'");
version.starts_with("CLI Version"),
"expected version string, got: '{version}'"
);
} }
#[tokio::test] #[tokio::test]
@@ -1200,12 +1173,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1218,12 +1187,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -1236,12 +1201,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1254,12 +1215,7 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -1272,12 +1228,8 @@ mod tests {
let (_context, node) = new_node(); let (_context, node) = new_node();
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -109,9 +109,7 @@ impl SubstrateNode {
) -> Self { ) -> Self {
let working_directory_path = let working_directory_path =
AsRef::<WorkingDirectoryConfiguration>::as_ref(&context).as_path(); AsRef::<WorkingDirectoryConfiguration>::as_ref(&context).as_path();
let eth_rpc_path = AsRef::<EthRpcConfiguration>::as_ref(&context) let eth_rpc_path = AsRef::<EthRpcConfiguration>::as_ref(&context).path.as_path();
.path
.as_path();
let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet(); let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet();
let substrate_directory = working_directory_path.join(Self::BASE_DIRECTORY); let substrate_directory = working_directory_path.join(Self::BASE_DIRECTORY);
@@ -143,10 +141,7 @@ impl SubstrateNode {
signers::local::PrivateKeySigner, signers::local::PrivateKeySigner,
}; };
let key_str = private_key let key_str = private_key.trim().strip_prefix("0x").unwrap_or(private_key.trim());
.trim()
.strip_prefix("0x")
.unwrap_or(private_key.trim());
let key_bytes = alloy::hex::decode(key_str) let key_bytes = alloy::hex::decode(key_str)
.map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?;
@@ -320,7 +315,7 @@ impl SubstrateNode {
self.shutdown() self.shutdown()
.context("Failed to gracefully shutdown after substrate start error")?; .context("Failed to gracefully shutdown after substrate start error")?;
return Err(err); return Err(err);
} },
} }
let eth_proxy_process = Process::new( let eth_proxy_process = Process::new(
@@ -355,7 +350,7 @@ impl SubstrateNode {
self.shutdown() self.shutdown()
.context("Failed to gracefully shutdown after eth proxy start error")?; .context("Failed to gracefully shutdown after eth proxy start error")?;
return Err(err); return Err(err);
} },
} }
Ok(()) Ok(())
@@ -365,10 +360,7 @@ impl SubstrateNode {
&self, &self,
genesis: &Genesis, genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> { ) -> anyhow::Result<Vec<(String, u128)>> {
genesis genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| {
.alloc
.iter()
.try_fold(Vec::new(), |mut vec, (address, acc)| {
let substrate_address = Self::eth_to_substrate_address(address); let substrate_address = Self::eth_to_substrate_address(address);
let balance = acc.balance.try_into()?; let balance = acc.balance.try_into()?;
vec.push((substrate_address, balance)); vec.push((substrate_address, balance));
@@ -468,10 +460,7 @@ impl EthereumNode for SubstrateNode {
transaction: TransactionRequest, transaction: TransactionRequest,
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
Box::pin(async move { Box::pin(async move {
let provider = self let provider = self.provider().await.context("Failed to create the provider")?;
.provider()
.await
.context("Failed to create the provider")?;
execute_transaction(provider, transaction).await execute_transaction(provider, transaction).await
}) })
} }
@@ -693,10 +682,7 @@ impl ResolverApi for SubstrateNodeResolver {
.context("Failed to get the substrate block")? .context("Failed to get the substrate block")?
.context("Failed to get the substrate block, perhaps the chain has no blocks?") .context("Failed to get the substrate block, perhaps the chain has no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -975,33 +961,27 @@ impl TransactionBuilder<ReviveNetwork> for <Ethereum as Network>::TransactionReq
); );
match result { match result {
Ok(unsigned_tx) => Ok(unsigned_tx), Ok(unsigned_tx) => Ok(unsigned_tx),
Err(UnbuiltTransactionError { request, error }) => { Err(UnbuiltTransactionError { request, error }) =>
Err(UnbuiltTransactionError::<ReviveNetwork> { Err(UnbuiltTransactionError::<ReviveNetwork> {
request, request,
error: match error { error: match error {
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => { 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::Custom(error) =>
TransactionBuilderError::Signer(error) TransactionBuilderError::Custom(error),
}
TransactionBuilderError::Custom(error) => {
TransactionBuilderError::Custom(error)
}
}, },
}) }),
}
} }
} }
async fn build<W: alloy::network::NetworkWallet<ReviveNetwork>>( async fn build<W: alloy::network::NetworkWallet<ReviveNetwork>>(
self, self,
wallet: &W, wallet: &W,
) -> Result<<ReviveNetwork as Network>::TxEnvelope, TransactionBuilderError<ReviveNetwork>> ) -> Result<<ReviveNetwork as Network>::TxEnvelope, TransactionBuilderError<ReviveNetwork>> {
{
Ok(wallet.sign_request(self).await?) Ok(wallet.sign_request(self).await?)
} }
} }
@@ -1261,11 +1241,7 @@ mod tests {
let provider = node.provider().await.expect("Failed to create provider"); let provider = node.provider().await.expect("Failed to create provider");
let account_address = context let account_address = context.wallet_configuration.wallet().default_signer().address();
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default() let transaction = TransactionRequest::default()
.to(account_address) .to(account_address)
.value(U256::from(100_000_000_000_000u128)); .value(U256::from(100_000_000_000_000u128));
@@ -1310,9 +1286,8 @@ mod tests {
.expect("init failed"); .expect("init failed");
// Check that the patched chainspec file was generated // Check that the patched chainspec file was generated
let final_chainspec_path = dummy_node let final_chainspec_path =
.base_directory dummy_node.base_directory.join(SubstrateNode::CHAIN_SPEC_JSON_FILE);
.join(SubstrateNode::CHAIN_SPEC_JSON_FILE);
assert!(final_chainspec_path.exists(), "Chainspec file should exist"); assert!(final_chainspec_path.exists(), "Chainspec file should exist");
let contents = fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec"); let contents = fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec");
@@ -1418,10 +1393,7 @@ mod tests {
for (eth_addr, expected_ss58) in cases { for (eth_addr, expected_ss58) in cases {
let result = SubstrateNode::eth_to_substrate_address(&eth_addr.parse().unwrap()); let result = SubstrateNode::eth_to_substrate_address(&eth_addr.parse().unwrap());
assert_eq!( assert_eq!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}");
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
} }
} }
@@ -1472,12 +1444,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1490,12 +1458,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -1508,12 +1472,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1526,12 +1486,7 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -1544,12 +1499,8 @@ mod tests {
let node = shared_node(); let node = shared_node();
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -122,14 +122,10 @@ impl ZombieNode {
+ AsRef<EthRpcConfiguration> + AsRef<EthRpcConfiguration>
+ AsRef<WalletConfiguration>, + AsRef<WalletConfiguration>,
) -> Self { ) -> Self {
let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context) let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context).path.to_owned();
.path
.to_owned();
let working_directory_path = AsRef::<WorkingDirectoryConfiguration>::as_ref(&context); let working_directory_path = AsRef::<WorkingDirectoryConfiguration>::as_ref(&context);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = working_directory_path let base_directory = working_directory_path.join(Self::BASE_DIRECTORY).join(id.to_string());
.join(Self::BASE_DIRECTORY)
.join(id.to_string());
let base_directory = base_directory.canonicalize().unwrap_or(base_directory); let base_directory = base_directory.canonicalize().unwrap_or(base_directory);
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();
@@ -197,10 +193,8 @@ impl ZombieNode {
} }
fn spawn_process(&mut self) -> anyhow::Result<()> { fn spawn_process(&mut self) -> anyhow::Result<()> {
let network_config = self let network_config =
.network_config self.network_config.clone().context("Node not initialized, call init() first")?;
.clone()
.context("Node not initialized, call init() first")?;
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
let network = rt.block_on(async { let network = rt.block_on(async {
@@ -248,7 +242,7 @@ impl ZombieNode {
self.shutdown() self.shutdown()
.context("Failed to gracefully shutdown after eth proxy start error")?; .context("Failed to gracefully shutdown after eth proxy start error")?;
return Err(err); return Err(err);
} },
} }
tracing::debug!("eth-rpc is up"); tracing::debug!("eth-rpc is up");
@@ -272,10 +266,7 @@ impl ZombieNode {
let output = cmd.output().context("Failed to export the chain-spec")?; let output = cmd.output().context("Failed to export the chain-spec")?;
if !output.status.success() { if !output.status.success() {
anyhow::bail!( anyhow::bail!("Build chain-spec failed: {}", String::from_utf8_lossy(&output.stderr));
"Build chain-spec failed: {}",
String::from_utf8_lossy(&output.stderr)
);
} }
let content = String::from_utf8(output.stdout) let content = String::from_utf8(output.stdout)
@@ -336,10 +327,7 @@ impl ZombieNode {
&self, &self,
genesis: &Genesis, genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> { ) -> anyhow::Result<Vec<(String, u128)>> {
genesis genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| {
.alloc
.iter()
.try_fold(Vec::new(), |mut vec, (address, acc)| {
let polkadot_address = Self::eth_to_polkadot_address(address); let polkadot_address = Self::eth_to_polkadot_address(address);
let balance = acc.balance.try_into()?; let balance = acc.balance.try_into()?;
vec.push((polkadot_address, balance)); vec.push((polkadot_address, balance));
@@ -440,16 +428,20 @@ impl EthereumNode for ZombieNode {
transaction: alloy::rpc::types::TransactionRequest, transaction: alloy::rpc::types::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 receipt = self let pending = self
.provider() .provider()
.await .await
.context("Failed to create provider for transaction submission")? .context("Failed to create provider for transaction submission")?
.send_transaction(transaction) .send_transaction(transaction)
.await .await
.context("Failed to submit transaction to proxy")? .context("Failed to submit transaction to proxy")?;
.get_receipt()
let receipt =
tokio::time::timeout(std::time::Duration::from_secs(120), pending.get_receipt())
.await .await
.context("Timeout waiting for transaction receipt")?
.context("Failed to fetch transaction receipt from proxy")?; .context("Failed to fetch transaction receipt from proxy")?;
Ok(receipt) Ok(receipt)
}) })
} }
@@ -675,10 +667,7 @@ impl<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> ResolverApi
.context("Failed to get the zombie block")? .context("Failed to get the zombie block")?
.context("Failed to get the zombie block, perhaps the chain has no blocks?") .context("Failed to get the zombie block, perhaps the chain has no blocks?")
.and_then(|block| { .and_then(|block| {
block block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
}) })
}) })
} }
@@ -790,10 +779,8 @@ mod tests {
pub async fn new_node() -> (TestExecutionContext, ZombieNode) { pub async fn new_node() -> (TestExecutionContext, ZombieNode) {
let context = test_config(); let context = test_config();
let mut node = ZombieNode::new( let mut node =
context.polkadot_parachain_configuration.path.clone(), ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
&context,
);
let genesis = context.genesis_configuration.genesis().unwrap().clone(); let genesis = context.genesis_configuration.genesis().unwrap().clone();
node.init(genesis).unwrap(); node.init(genesis).unwrap();
@@ -858,14 +845,11 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let mut node = ZombieNode::new( let mut node =
context.polkadot_parachain_configuration.path.clone(), ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
&context,
);
// Call `init()` // Call `init()`
node.init(serde_json::from_str(genesis_content).unwrap()) node.init(serde_json::from_str(genesis_content).unwrap()).expect("init failed");
.expect("init failed");
// Check that the patched chainspec file was generated // Check that the patched chainspec file was generated
let final_chainspec_path = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE); let final_chainspec_path = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE);
@@ -906,10 +890,7 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let node = ZombieNode::new( let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
context.polkadot_parachain_configuration.path.clone(),
&context,
);
let result = node let result = node
.extract_balance_from_genesis_file(&serde_json::from_str(genesis_json).unwrap()) .extract_balance_from_genesis_file(&serde_json::from_str(genesis_json).unwrap())
@@ -971,10 +952,7 @@ mod tests {
for (eth_addr, expected_ss58) in cases { for (eth_addr, expected_ss58) in cases {
let result = ZombieNode::eth_to_polkadot_address(&eth_addr.parse().unwrap()); let result = ZombieNode::eth_to_polkadot_address(&eth_addr.parse().unwrap());
assert_eq!( assert_eq!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}");
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
} }
} }
@@ -982,10 +960,7 @@ mod tests {
fn eth_rpc_version_works() { fn eth_rpc_version_works() {
// Arrange // Arrange
let context = test_config(); let context = test_config();
let node = ZombieNode::new( let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act // Act
let version = node.eth_rpc_version().unwrap(); let version = node.eth_rpc_version().unwrap();
@@ -1001,10 +976,7 @@ mod tests {
fn version_works() { fn version_works() {
// Arrange // Arrange
let context = test_config(); let context = test_config();
let node = ZombieNode::new( let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act // Act
let version = node.version().unwrap(); let version = node.version().unwrap();
@@ -1042,12 +1014,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let gas_limit = node let gas_limit =
.resolver() node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = gas_limit.expect("Failed to get the gas limit"); let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1060,12 +1028,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let coinbase = node let coinbase =
.resolver() node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let _ = coinbase.expect("Failed to get the coinbase");
@@ -1078,12 +1042,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_difficulty = node let block_difficulty =
.resolver() node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1096,12 +1056,7 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_hash = node let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_hash.expect("Failed to get the block hash"); let _ = block_hash.expect("Failed to get the block hash");
@@ -1114,12 +1069,8 @@ mod tests {
let node = shared_node().await; let node = shared_node().await;
// Act // Act
let block_timestamp = node let block_timestamp =
.resolver() node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert // Assert
let _ = block_timestamp.expect("Failed to get the block timestamp"); let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -11,9 +11,7 @@ pub struct ConcurrencyLimiterLayer {
impl ConcurrencyLimiterLayer { impl ConcurrencyLimiterLayer {
pub fn new(permit_count: usize) -> Self { pub fn new(permit_count: usize) -> Self {
Self { Self { semaphore: Arc::new(Semaphore::new(permit_count)) }
semaphore: Arc::new(Semaphore::new(permit_count)),
}
} }
} }
@@ -21,10 +19,7 @@ impl<S> Layer<S> for ConcurrencyLimiterLayer {
type Service = ConcurrencyLimiterService<S>; type Service = ConcurrencyLimiterService<S>;
fn layer(&self, inner: S) -> Self::Service { fn layer(&self, inner: S) -> Self::Service {
ConcurrencyLimiterService { ConcurrencyLimiterService { service: inner, semaphore: self.semaphore.clone() }
service: inner,
semaphore: self.semaphore.clone(),
}
} }
} }
@@ -55,10 +50,7 @@ where
let future = self.service.call(req); let future = self.service.call(req);
Box::pin(async move { Box::pin(async move {
let _permit = semaphore let _permit = semaphore.acquire().await.expect("Semaphore has been closed");
.acquire()
.await
.expect("Semaphore has been closed");
tracing::debug!( tracing::debug!(
available_permits = semaphore.available_permits(), available_permits = semaphore.available_permits(),
"Acquired Semaphore Permit" "Acquired Semaphore Permit"
@@ -21,18 +21,13 @@ impl FallbackGasFiller {
default_max_fee_per_gas: u128, default_max_fee_per_gas: u128,
default_priority_fee: u128, default_priority_fee: u128,
) -> Self { ) -> Self {
Self { Self { inner: GasFiller, default_gas_limit, default_max_fee_per_gas, default_priority_fee }
inner: GasFiller,
default_gas_limit,
default_max_fee_per_gas,
default_priority_fee,
}
} }
} }
impl Default for FallbackGasFiller { impl Default for FallbackGasFiller {
fn default() -> Self { fn default() -> Self {
FallbackGasFiller::new(25_000_000, 1_000_000_000, 1_000_000_000) FallbackGasFiller::new(10_000_000, 1_000_000_000, 1_000_000_000)
} }
} }
@@ -56,12 +51,9 @@ where
provider: &P, provider: &P,
tx: &<N as Network>::TransactionRequest, tx: &<N as Network>::TransactionRequest,
) -> TransportResult<Self::Fillable> { ) -> TransportResult<Self::Fillable> {
// Try to fetch GasFillers fillable (gas_price, base_fee, estimate_gas, …) // Try to fetch GasFiller's "fillable" (gas_price, base_fee, estimate_gas, …)
// If it errors (i.e. tx would revert under eth_estimateGas), swallow it. // Propagate errors so caller can handle them appropriately
match self.inner.prepare(provider, tx).await { self.inner.prepare(provider, tx).await.map(Some)
Ok(fill) => Ok(Some(fill)),
Err(_) => Ok(None),
}
} }
async fn fill( async fn fill(
+12 -16
View File
@@ -80,10 +80,8 @@ where
NonceFiller: TxFiller<N>, NonceFiller: TxFiller<N>,
WalletFiller<W>: TxFiller<N>, WalletFiller<W>: TxFiller<N>,
{ {
let sendable_transaction = provider let sendable_transaction =
.fill(transaction) provider.fill(transaction).await.context("Failed to fill transaction")?;
.await
.context("Failed to fill transaction")?;
let transaction_envelope = sendable_transaction let transaction_envelope = sendable_transaction
.try_into_envelope() .try_into_envelope()
@@ -100,29 +98,27 @@ where
} else { } else {
return Err(error).context(format!("Failed to submit transaction {tx_hash}")); return Err(error).context(format!("Failed to submit transaction {tx_hash}"));
} }
} },
}; };
debug!(%tx_hash, "Submitted Transaction"); debug!(%tx_hash, "Submitted Transaction");
pending_transaction.set_timeout(Some(Duration::from_secs(120))); pending_transaction.set_timeout(Some(Duration::from_secs(120)));
let tx_hash = pending_transaction.watch().await.context(format!( let tx_hash = pending_transaction
"Transaction inclusion watching timeout for {tx_hash}" .watch()
))?; .await
.context(format!("Transaction inclusion watching timeout for {tx_hash}"))?;
poll( debug!(%tx_hash, "Transaction included, polling for receipt");
Duration::from_secs(60),
PollingWaitBehavior::Constant(Duration::from_secs(3)), poll(Duration::from_secs(30), PollingWaitBehavior::Constant(Duration::from_secs(3)), || {
|| {
let provider = provider.clone(); 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 timed out for {tx_hash}"))
} }
+37 -87
View File
@@ -66,50 +66,45 @@ impl ReportAggregator {
match event { match event {
RunnerEvent::SubscribeToEvents(event) => { RunnerEvent::SubscribeToEvents(event) => {
self.handle_subscribe_to_events_event(*event); self.handle_subscribe_to_events_event(*event);
} },
RunnerEvent::CorpusFileDiscovery(event) => { RunnerEvent::CorpusFileDiscovery(event) =>
self.handle_corpus_file_discovered_event(*event) self.handle_corpus_file_discovered_event(*event),
}
RunnerEvent::MetadataFileDiscovery(event) => { RunnerEvent::MetadataFileDiscovery(event) => {
self.handle_metadata_file_discovery_event(*event); self.handle_metadata_file_discovery_event(*event);
} },
RunnerEvent::TestCaseDiscovery(event) => { RunnerEvent::TestCaseDiscovery(event) => {
self.handle_test_case_discovery(*event); self.handle_test_case_discovery(*event);
} },
RunnerEvent::TestSucceeded(event) => { RunnerEvent::TestSucceeded(event) => {
self.handle_test_succeeded_event(*event); self.handle_test_succeeded_event(*event);
} },
RunnerEvent::TestFailed(event) => { RunnerEvent::TestFailed(event) => {
self.handle_test_failed_event(*event); self.handle_test_failed_event(*event);
} },
RunnerEvent::TestIgnored(event) => { RunnerEvent::TestIgnored(event) => {
self.handle_test_ignored_event(*event); self.handle_test_ignored_event(*event);
} },
RunnerEvent::NodeAssigned(event) => { RunnerEvent::NodeAssigned(event) => {
self.handle_node_assigned_event(*event); self.handle_node_assigned_event(*event);
} },
RunnerEvent::PreLinkContractsCompilationSucceeded(event) => { RunnerEvent::PreLinkContractsCompilationSucceeded(event) =>
self.handle_pre_link_contracts_compilation_succeeded_event(*event) self.handle_pre_link_contracts_compilation_succeeded_event(*event),
} RunnerEvent::PostLinkContractsCompilationSucceeded(event) =>
RunnerEvent::PostLinkContractsCompilationSucceeded(event) => { self.handle_post_link_contracts_compilation_succeeded_event(*event),
self.handle_post_link_contracts_compilation_succeeded_event(*event) RunnerEvent::PreLinkContractsCompilationFailed(event) =>
} self.handle_pre_link_contracts_compilation_failed_event(*event),
RunnerEvent::PreLinkContractsCompilationFailed(event) => { RunnerEvent::PostLinkContractsCompilationFailed(event) =>
self.handle_pre_link_contracts_compilation_failed_event(*event) self.handle_post_link_contracts_compilation_failed_event(*event),
}
RunnerEvent::PostLinkContractsCompilationFailed(event) => {
self.handle_post_link_contracts_compilation_failed_event(*event)
}
RunnerEvent::LibrariesDeployed(event) => { RunnerEvent::LibrariesDeployed(event) => {
self.handle_libraries_deployed_event(*event); self.handle_libraries_deployed_event(*event);
} },
RunnerEvent::ContractDeployed(event) => { RunnerEvent::ContractDeployed(event) => {
self.handle_contract_deployed_event(*event); self.handle_contract_deployed_event(*event);
} },
RunnerEvent::Completion(event) => { RunnerEvent::Completion(event) => {
self.handle_completion(*event); self.handle_completion(*event);
break; break;
} },
} }
} }
debug!("Report aggregation completed"); debug!("Report aggregation completed");
@@ -123,12 +118,8 @@ impl ReportAggregator {
file_name.push_str(".json"); file_name.push_str(".json");
file_name file_name
}; };
let file_path = self let file_path =
.report self.report.context.working_directory_configuration().as_path().join(file_name);
.context
.working_directory_configuration()
.as_path()
.join(file_name);
let file = OpenOptions::new() let file = OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
@@ -136,10 +127,7 @@ impl ReportAggregator {
.read(false) .read(false)
.open(&file_path) .open(&file_path)
.with_context(|| { .with_context(|| {
format!( format!("Failed to open report file for writing: {}", file_path.display())
"Failed to open report file for writing: {}",
file_path.display()
)
})?; })?;
serde_json::to_writer_pretty(&file, &self.report).with_context(|| { serde_json::to_writer_pretty(&file, &self.report).with_context(|| {
format!("Failed to serialize report JSON to {}", file_path.display()) format!("Failed to serialize report JSON to {}", file_path.display())
@@ -180,9 +168,8 @@ impl ReportAggregator {
// Add information on the fact that the case was ignored to the report. // Add information on the fact that the case was ignored to the report.
let test_case_report = self.test_case_report(&event.test_specifier); let test_case_report = self.test_case_report(&event.test_specifier);
test_case_report.status = Some(TestCaseStatus::Succeeded { test_case_report.status =
steps_executed: event.steps_executed, Some(TestCaseStatus::Succeeded { steps_executed: event.steps_executed });
});
self.handle_post_test_case_status_update(&event.test_specifier); self.handle_post_test_case_status_update(&event.test_specifier);
} }
@@ -197,9 +184,7 @@ impl ReportAggregator {
// Add information on the fact that the case was ignored to the report. // Add information on the fact that the case was ignored to the report.
let test_case_report = self.test_case_report(&event.test_specifier); let test_case_report = self.test_case_report(&event.test_specifier);
test_case_report.status = Some(TestCaseStatus::Failed { test_case_report.status = Some(TestCaseStatus::Failed { reason: event.reason });
reason: event.reason,
});
self.handle_post_test_case_status_update(&event.test_specifier); self.handle_post_test_case_status_update(&event.test_specifier);
} }
@@ -241,10 +226,7 @@ impl ReportAggregator {
.or_default() .or_default()
.iter() .iter()
.map(|(case_idx, case_report)| { .map(|(case_idx, case_report)| {
( (*case_idx, case_report.status.clone().expect("Can't be uninitialized"))
*case_idx,
case_report.status.clone().expect("Can't be uninitialized"),
)
}) })
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
let event = ReporterEvent::MetadataFileSolcModeCombinationExecutionCompleted { let event = ReporterEvent::MetadataFileSolcModeCombinationExecutionCompleted {
@@ -276,29 +258,13 @@ impl ReportAggregator {
&mut self, &mut self,
event: PreLinkContractsCompilationSucceededEvent, event: PreLinkContractsCompilationSucceededEvent,
) { ) {
let include_input = self let include_input = self.report.context.report_configuration().include_compiler_input;
.report let include_output = self.report.context.report_configuration().include_compiler_output;
.context
.report_configuration()
.include_compiler_input;
let include_output = self
.report
.context
.report_configuration()
.include_compiler_output;
let execution_information = self.execution_information(&event.execution_specifier); let execution_information = self.execution_information(&event.execution_specifier);
let compiler_input = if include_input { let compiler_input = if include_input { event.compiler_input } else { None };
event.compiler_input let compiler_output = if include_output { Some(event.compiler_output) } else { None };
} else {
None
};
let compiler_output = if include_output {
Some(event.compiler_output)
} else {
None
};
execution_information.pre_link_compilation_status = Some(CompilationStatus::Success { execution_information.pre_link_compilation_status = Some(CompilationStatus::Success {
is_cached: event.is_cached, is_cached: event.is_cached,
@@ -313,29 +279,13 @@ impl ReportAggregator {
&mut self, &mut self,
event: PostLinkContractsCompilationSucceededEvent, event: PostLinkContractsCompilationSucceededEvent,
) { ) {
let include_input = self let include_input = self.report.context.report_configuration().include_compiler_input;
.report let include_output = self.report.context.report_configuration().include_compiler_output;
.context
.report_configuration()
.include_compiler_input;
let include_output = self
.report
.context
.report_configuration()
.include_compiler_output;
let execution_information = self.execution_information(&event.execution_specifier); let execution_information = self.execution_information(&event.execution_specifier);
let compiler_input = if include_input { let compiler_input = if include_input { event.compiler_input } else { None };
event.compiler_input let compiler_output = if include_output { Some(event.compiler_output) } else { None };
} else {
None
};
let compiler_output = if include_output {
Some(event.compiler_output)
} else {
None
};
execution_information.post_link_compilation_status = Some(CompilationStatus::Success { execution_information.post_link_compilation_status = Some(CompilationStatus::Success {
is_cached: event.is_cached, is_cached: event.is_cached,
@@ -375,8 +325,8 @@ impl ReportAggregator {
} }
fn handle_libraries_deployed_event(&mut self, event: LibrariesDeployedEvent) { fn handle_libraries_deployed_event(&mut self, event: LibrariesDeployedEvent) {
self.execution_information(&event.execution_specifier) self.execution_information(&event.execution_specifier).deployed_libraries =
.deployed_libraries = Some(event.libraries); Some(event.libraries);
} }
fn handle_contract_deployed_event(&mut self, event: ContractDeployedEvent) { fn handle_contract_deployed_event(&mut self, event: ContractDeployedEvent) {
+5 -24
View File
@@ -34,19 +34,11 @@ pub(crate) async fn get_or_download(
} }
create_dir_all(&target_directory).with_context(|| { create_dir_all(&target_directory).with_context(|| {
format!( format!("Failed to create solc cache directory: {}", target_directory.display())
"Failed to create solc cache directory: {}",
target_directory.display()
)
})?; })?;
download_to_file(&target_file, downloader) download_to_file(&target_file, downloader)
.await .await
.with_context(|| { .with_context(|| format!("Failed to write downloaded solc to {}", target_file.display()))?;
format!(
"Failed to write downloaded solc to {}",
target_file.display()
)
})?;
cache.insert(target_file.clone()); cache.insert(target_file.clone());
Ok((downloader.version.clone(), target_file)) Ok((downloader.version.clone(), target_file))
@@ -70,12 +62,7 @@ async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> anyhow::R
} }
let mut file = BufWriter::new(file); let mut file = BufWriter::new(file);
file.write_all( file.write_all(&downloader.download().await.context("Failed to download solc binary bytes")?)
&downloader
.download()
.await
.context("Failed to download solc binary bytes")?,
)
.with_context(|| format!("Failed to write solc binary to {}", path.display()))?; .with_context(|| format!("Failed to write solc binary to {}", path.display()))?;
file.flush() file.flush()
.with_context(|| format!("Failed to flush file {}", path.display()))?; .with_context(|| format!("Failed to flush file {}", path.display()))?;
@@ -91,17 +78,11 @@ async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> anyhow::R
.stdout(std::process::Stdio::null()) .stdout(std::process::Stdio::null())
.spawn() .spawn()
.with_context(|| { .with_context(|| {
format!( format!("Failed to spawn xattr to remove quarantine attribute on {}", path.display())
"Failed to spawn xattr to remove quarantine attribute on {}",
path.display()
)
})? })?
.wait() .wait()
.with_context(|| { .with_context(|| {
format!( format!("Failed waiting for xattr operation to complete on {}", path.display())
"Failed waiting for xattr operation to complete on {}",
path.display()
)
})?; })?;
Ok(()) Ok(())
+11 -52
View File
@@ -67,11 +67,7 @@ impl SolcDownloader {
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
let version_or_requirement = version.into(); let version_or_requirement = version.into();
match version_or_requirement { match version_or_requirement {
VersionOrRequirement::Version(version) => Ok(Self { VersionOrRequirement::Version(version) => Ok(Self { version, target, list }),
version,
target,
list,
}),
VersionOrRequirement::Requirement(requirement) => { VersionOrRequirement::Requirement(requirement) => {
let Some(version) = List::download(list) let Some(version) = List::download(list)
.await .await
@@ -84,12 +80,8 @@ impl SolcDownloader {
else { else {
anyhow::bail!("Failed to find a version that satisfies {requirement:?}"); anyhow::bail!("Failed to find a version that satisfies {requirement:?}");
}; };
Ok(Self { Ok(Self { version, target, list })
version, },
target,
list,
})
}
} }
} }
@@ -130,11 +122,7 @@ impl SolcDownloader {
})?; })?;
let path = build.path.clone(); let path = build.path.clone();
let expected_digest = build let expected_digest = build.sha256.strip_prefix("0x").unwrap_or(&build.sha256).to_string();
.sha256
.strip_prefix("0x")
.unwrap_or(&build.sha256)
.to_string();
let url = format!("{}/{}/{}", Self::BASE_URL, self.target, path.display()); let url = format!("{}/{}/{}", Self::BASE_URL, self.target, path.display());
let file = reqwest::get(&url) let file = reqwest::get(&url)
@@ -159,54 +147,25 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn try_get_windows() { async fn try_get_windows() {
let version = List::download(List::WINDOWS_URL) let version = List::download(List::WINDOWS_URL).await.unwrap().latest_release;
.await SolcDownloader::windows(version).await.unwrap().download().await.unwrap();
.unwrap()
.latest_release;
SolcDownloader::windows(version)
.await
.unwrap()
.download()
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]
async fn try_get_macosx() { async fn try_get_macosx() {
let version = List::download(List::MACOSX_URL) let version = List::download(List::MACOSX_URL).await.unwrap().latest_release;
.await SolcDownloader::macosx(version).await.unwrap().download().await.unwrap();
.unwrap()
.latest_release;
SolcDownloader::macosx(version)
.await
.unwrap()
.download()
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]
async fn try_get_linux() { async fn try_get_linux() {
let version = List::download(List::LINUX_URL) let version = List::download(List::LINUX_URL).await.unwrap().latest_release;
.await SolcDownloader::linux(version).await.unwrap().download().await.unwrap();
.unwrap()
.latest_release;
SolcDownloader::linux(version)
.await
.unwrap()
.download()
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]
async fn try_get_wasm() { async fn try_get_wasm() {
let version = List::download(List::WASM_URL).await.unwrap().latest_release; let version = List::download(List::WASM_URL).await.unwrap().latest_release;
SolcDownloader::wasm(version) SolcDownloader::wasm(version).await.unwrap().download().await.unwrap();
.await
.unwrap()
.download()
.await
.unwrap();
} }
} }