Compare commits

...

1 Commits

Author SHA1 Message Date
Cyrill Leutwiler 12fdd9b4d1 test
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-10-08 13:04:03 +02:00
55 changed files with 12787 additions and 12039 deletions
-25
View File
@@ -1,25 +0,0 @@
# 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
+6 -3
View File
@@ -20,14 +20,17 @@ pub fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
let content = fs::read(path.as_path())?;
READ_CACHE.insert(path, content.clone());
Ok(content)
},
}
}
}
pub fn read_to_string(path: impl AsRef<Path>) -> Result<String> {
let content = read(path)?;
String::from_utf8(content).map_err(|_| {
Error::new(std::io::ErrorKind::InvalidData, "The contents of the file are not valid UTF8")
Error::new(
std::io::ErrorKind::InvalidData,
"The contents of the file are not valid UTF8",
)
})
}
@@ -43,6 +46,6 @@ pub fn read_dir(path: impl AsRef<Path>) -> Result<Box<dyn Iterator<Item = Result
.collect();
READ_DIR_CACHE.insert(path.clone(), entries);
Ok(read_dir(path).unwrap())
},
}
}
}
+4 -1
View File
@@ -12,7 +12,10 @@ pub fn clear_directory(path: impl AsRef<Path>) -> Result<()> {
.with_context(|| format!("Failed to read directory: {}", path.as_ref().display()))?
{
let entry = entry.with_context(|| {
format!("Failed to read an entry in directory: {}", path.as_ref().display())
format!(
"Failed to read an entry in directory: {}",
path.as_ref().display()
)
})?;
let entry_path = entry.path();
+9 -5
View File
@@ -37,13 +37,17 @@ where
));
}
match future().await.context("Polled future returned an error during polling loop")? {
match future()
.await
.context("Polled future returned an error during polling loop")?
{
ControlFlow::Continue(()) => {
let next_wait_duration = match polling_wait_behavior {
PollingWaitBehavior::Constant(duration) => duration,
PollingWaitBehavior::ExponentialBackoff =>
PollingWaitBehavior::ExponentialBackoff => {
Duration::from_secs(2u64.pow(retries))
.min(EXPONENTIAL_BACKOFF_MAX_WAIT_DURATION),
.min(EXPONENTIAL_BACKOFF_MAX_WAIT_DURATION)
}
};
let next_wait_duration =
next_wait_duration.min(max_allowed_wait_duration - total_wait_duration);
@@ -51,10 +55,10 @@ where
retries += 1;
tokio::time::sleep(next_wait_duration).await;
},
}
ControlFlow::Break(output) => {
break Ok(output);
},
}
}
}
}
@@ -75,12 +75,13 @@ impl Iterator for FilesWithExtensionIterator {
for entry_path in iterator.flatten() {
if entry_path.is_dir() {
self.directories_to_search.push(entry_path)
} else if entry_path.is_file() &&
entry_path.extension().is_some_and(|ext| {
} else if entry_path.is_file()
&& entry_path.extension().is_some_and(|ext| {
self.allowed_extensions
.iter()
.any(|allowed| ext.eq_ignore_ascii_case(allowed.as_ref()))
}) {
})
{
self.files_matching_allowed_extensions.push(entry_path)
}
}
+3 -1
View File
@@ -76,7 +76,9 @@ impl FromStr for ModePipeline {
// Don't go via Yul IR
"E" => Ok(ModePipeline::ViaEVMAssembly),
// Anything else that we see isn't a mode at all
_ => Err(anyhow::anyhow!("Unsupported pipeline '{s}': expected 'Y' or 'E'")),
_ => Err(anyhow::anyhow!(
"Unsupported pipeline '{s}': expected 'Y' or 'E'"
)),
}
}
}
@@ -15,7 +15,10 @@ pub struct PrivateKeyAllocator {
impl PrivateKeyAllocator {
/// Creates a new instance of the private key allocator.
pub fn new(highest_private_key_inclusive: U256) -> Self {
Self { next_private_key: U256::ONE, highest_private_key_inclusive }
Self {
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.
+4 -1
View File
@@ -7,7 +7,10 @@ pub struct RoundRobinPool<T> {
impl<T> RoundRobinPool<T> {
pub fn new(items: Vec<T>) -> Self {
Self { next_index: Default::default(), items }
Self {
next_index: Default::default(),
items,
}
}
pub fn round_robin(&self) -> &T {
+20 -13
View File
@@ -60,7 +60,10 @@ impl Resolc {
Ok(COMPILERS_CACHE
.entry(solc.clone())
.or_insert_with(|| {
Self(Arc::new(ResolcInner { solc, resolc_path: resolc_configuration.path.clone() }))
Self(Arc::new(ResolcInner {
solc,
resolc_path: resolc_configuration.path.clone(),
}))
})
.clone())
}
@@ -138,7 +141,9 @@ impl SolidityCompiler for Resolc {
output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()),
via_ir: Some(true),
optimizer: SolcStandardJsonInputSettingsOptimizer::new(
optimization.unwrap_or(ModeOptimizerSetting::M0).optimizations_enabled(),
optimization
.unwrap_or(ModeOptimizerSetting::M0)
.optimizations_enabled(),
None,
&Version::new(0, 0, 0),
false,
@@ -248,14 +253,16 @@ impl SolidityCompiler for Resolc {
.evm
.and_then(|evm| evm.bytecode.clone())
.context("Unexpected - Contract compiled with resolc has no bytecode")?;
let abi = {
let abi =
{
let metadata = contract_information
.metadata
.as_ref()
.context("No metadata found for the contract")?;
let solc_metadata_str = match metadata {
serde_json::Value::String(solc_metadata_str) =>
solc_metadata_str.as_str(),
serde_json::Value::String(solc_metadata_str) => {
solc_metadata_str.as_str()
}
serde_json::Value::Object(metadata_object) => {
let solc_metadata_value = metadata_object
.get("solc_metadata")
@@ -263,13 +270,13 @@ impl SolidityCompiler for Resolc {
solc_metadata_value
.as_str()
.context("The 'solc_metadata' field is not a string")?
},
serde_json::Value::Null |
serde_json::Value::Bool(_) |
serde_json::Value::Number(_) |
serde_json::Value::Array(_) => {
}
serde_json::Value::Null
| serde_json::Value::Bool(_)
| serde_json::Value::Number(_)
| serde_json::Value::Array(_) => {
anyhow::bail!("Unsupported type of metadata {metadata:?}")
},
}
};
let solc_metadata =
serde_json::from_str::<serde_json::Value>(solc_metadata_str).context(
@@ -297,7 +304,7 @@ impl SolidityCompiler for Resolc {
optimize_setting: ModeOptimizerSetting,
pipeline: ModePipeline,
) -> bool {
pipeline == ModePipeline::ViaYulIR &&
SolidityCompiler::supports_mode(&self.0.solc, optimize_setting, pipeline)
pipeline == ModePipeline::ViaYulIR
&& SolidityCompiler::supports_mode(&self.0.solc, optimize_setting, pipeline)
}
}
+13 -5
View File
@@ -56,7 +56,9 @@ impl Solc {
// 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
// to us to either use the provided path or not.
let version = version.into().unwrap_or_else(|| solc_configuration.version.clone().into());
let version = version
.into()
.unwrap_or_else(|| solc_configuration.version.clone().into());
let (version, path) =
download_solc(working_directory_configuration.as_path(), version, false)
.await
@@ -70,7 +72,10 @@ impl Solc {
solc_version = %version,
"Created a new solc compiler object"
);
Self(Arc::new(SolcInner { solc_path: path, solc_version: version }))
Self(Arc::new(SolcInner {
solc_path: path,
solc_version: version,
}))
})
.clone())
}
@@ -247,7 +252,10 @@ impl SolidityCompiler for Solc {
let map = compiler_output
.contracts
.entry(contract_path.canonicalize().with_context(|| {
format!("Failed to canonicalize contract path {}", contract_path.display())
format!(
"Failed to canonicalize contract path {}",
contract_path.display()
)
})?)
.or_default();
for (contract_name, contract_info) in contracts.into_iter() {
@@ -278,8 +286,8 @@ impl SolidityCompiler for Solc {
// 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
// is new enough.
pipeline == ModePipeline::ViaEVMAssembly ||
(pipeline == ModePipeline::ViaYulIR && self.compiler_supports_yul())
pipeline == ModePipeline::ViaEVMAssembly
|| (pipeline == ModePipeline::ViaYulIR && self.compiler_supports_yul())
}
}
+20 -7
View File
@@ -154,7 +154,7 @@ impl AsRef<GenesisConfiguration> for Context {
Self::Benchmark(..) => {
static GENESIS: LazyLock<GenesisConfiguration> = LazyLock::new(Default::default);
&GENESIS
},
}
Self::ExportJsonSchema => unreachable!(),
}
}
@@ -626,7 +626,11 @@ pub struct KurtosisConfiguration {
///
/// If this is not specified, then the tool assumes that it should use the kurtosis binary
/// that's provided in the user's $PATH.
#[clap(id = "kurtosis.path", long = "kurtosis.path", default_value = "kurtosis")]
#[clap(
id = "kurtosis.path",
long = "kurtosis.path",
default_value = "kurtosis"
)]
pub path: PathBuf,
}
@@ -637,7 +641,11 @@ pub struct KitchensinkConfiguration {
///
/// If this is not specified, then the tool assumes that it should use the kitchensink binary
/// that's provided in the user's $PATH.
#[clap(id = "kitchensink.path", long = "kitchensink.path", default_value = "substrate-node")]
#[clap(
id = "kitchensink.path",
long = "kitchensink.path",
default_value = "substrate-node"
)]
pub path: PathBuf,
/// The amount of time to wait upon startup before considering that the node timed out.
@@ -723,11 +731,11 @@ impl GenesisConfiguration {
Some(genesis_path) => {
let genesis_content = read_to_string(genesis_path)?;
serde_json::from_str(genesis_content.as_str())?
},
}
None => DEFAULT_GENESIS.clone(),
};
Ok(self.genesis.get_or_init(|| genesis))
},
}
}
}
}
@@ -815,7 +823,10 @@ impl ConcurrencyConfiguration {
pub fn concurrency_limit(&self) -> Option<usize> {
match self.ignore_concurrency_limit {
true => None,
false => Some(self.number_concurrent_tasks.unwrap_or(20 * self.number_of_nodes)),
false => Some(
self.number_concurrent_tasks
.unwrap_or(20 * self.number_of_nodes),
),
}
}
}
@@ -906,7 +917,9 @@ impl Serialize for WorkingDirectoryConfiguration {
}
fn parse_duration(s: &str) -> anyhow::Result<Duration> {
u64::from_str(s).map(Duration::from_millis).map_err(Into::into)
u64::from_str(s)
.map(Duration::from_millis)
.map_err(Into::into)
}
/// The Solidity compatible node implementation.
@@ -206,7 +206,9 @@ where
"Deployed library"
);
let library_address = receipt.contract_address.expect("Failed to deploy the library");
let library_address = receipt
.contract_address
.expect("Failed to deploy the library");
deployed_libraries.get_or_insert_default().insert(
library_instance.clone(),
@@ -234,8 +236,10 @@ where
})
.context("Failed to compile the post-link contracts")?;
self.execution_state =
ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default());
self.execution_state = ExecutionState::new(
compiler_output.contracts,
deployed_libraries.unwrap_or_default(),
);
Ok(())
}
@@ -321,7 +325,11 @@ where
) -> Result<HashMap<ContractInstance, TransactionReceipt>> {
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
for instance in step.find_all_contract_instances().into_iter() {
if !self.execution_state.deployed_contracts.contains_key(&instance) {
if !self
.execution_state
.deployed_contracts
.contains_key(&instance)
{
instances_we_must_deploy.entry(instance).or_insert(false);
}
}
@@ -333,11 +341,15 @@ where
let mut receipts = HashMap::new();
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
let calldata = deploy_with_constructor_arguments.then_some(&step.calldata);
let value = deploy_with_constructor_arguments.then_some(step.value).flatten();
let value = deploy_with_constructor_arguments
.then_some(step.value)
.flatten();
let caller = {
let context = self.default_resolution_context();
step.caller.resolve_address(self.resolver.as_ref(), context).await?
step.caller
.resolve_address(self.resolver.as_ref(), context)
.await?
};
if let (_, _, Some(receipt)) = self
.get_or_deploy_contract_instance(&instance, caller, calldata, value)
@@ -367,7 +379,7 @@ where
.as_transaction(self.resolver.as_ref(), self.default_resolution_context())
.await?;
self.execute_transaction(tx).await
},
}
}
}
@@ -412,13 +424,18 @@ where
};
// Handling the return data variable assignments.
for (variable_name, output_word) in assignments
.return_data
.iter()
.zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32))
{
for (variable_name, output_word) in assignments.return_data.iter().zip(
tracing_result
.output
.as_ref()
.unwrap_or_default()
.to_vec()
.chunks(32),
) {
let value = U256::from_be_slice(output_word);
self.execution_state.variables.insert(variable_name.clone(), value);
self.execution_state
.variables
.insert(variable_name.clone(), value);
tracing::info!(
variable_name,
variable_value = hex::encode(value.to_be_bytes::<32>()),
@@ -486,7 +503,9 @@ where
// receipt and how this would impact the architecture and the possibility of us not waiting
// for receipts in the future.
self.watcher_tx
.send(WatcherEvent::RepetitionStartEvent { ignore_block_before: 0 })
.send(WatcherEvent::RepetitionStartEvent {
ignore_block_before: 0,
})
.context("Failed to send message on the watcher's tx")?;
let res = futures::future::try_join_all(tasks)
@@ -514,7 +533,9 @@ where
let account = private_key.address();
let variable = U256::from_be_slice(account.0.as_slice());
self.execution_state.variables.insert(variable_name.to_string(), variable);
self.execution_state
.variables
.insert(variable_name.to_string(), variable);
Ok(1)
}
@@ -539,8 +560,10 @@ where
calldata: Option<&Calldata>,
value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
if let Some((_, address, abi)) =
self.execution_state.deployed_contracts.get(contract_instance)
if let Some((_, address, abi)) = self
.execution_state
.deployed_contracts
.get(contract_instance)
{
info!(
@@ -580,10 +603,19 @@ where
calldata: Option<&Calldata>,
value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, TransactionReceipt)> {
let Some(ContractPathAndIdent { contract_source_path, contract_ident }) =
self.test_definition.metadata.contract_sources()?.remove(contract_instance)
let Some(ContractPathAndIdent {
contract_source_path,
contract_ident,
}) = self
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else {
anyhow::bail!("Contract source not found for instance {:?}", contract_instance)
anyhow::bail!(
"Contract source not found for instance {:?}",
contract_instance
)
};
let Some((code, abi)) = self
@@ -593,7 +625,10 @@ where
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
.cloned()
else {
anyhow::bail!("Failed to find information for contract {:?}", contract_instance)
anyhow::bail!(
"Failed to find information for contract {:?}",
contract_instance
)
};
let mut code = match alloy::hex::decode(&code) {
@@ -606,7 +641,7 @@ where
"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)
},
}
};
if let Some(calldata) = calldata {
@@ -630,7 +665,7 @@ where
Err(error) => {
tracing::error!(?error, "Contract deployment transaction failed.");
return Err(error);
},
}
};
let Some(address) = receipt.contract_address else {
@@ -645,9 +680,10 @@ where
.reporter
.report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state
.deployed_contracts
.insert(contract_instance.clone(), (contract_ident, address, abi.clone()));
self.execution_state.deployed_contracts.insert(
contract_instance.clone(),
(contract_ident, address, abi.clone()),
);
Ok((address, abi, receipt))
}
@@ -660,7 +696,9 @@ where
match step_address {
StepAddress::Address(address) => Ok(*address),
StepAddress::ResolvableAddress(resolvable) => {
let Some(instance) = resolvable.strip_suffix(".address").map(ContractInstance::new)
let Some(instance) = resolvable
.strip_suffix(".address")
.map(ContractInstance::new)
else {
bail!("Not an address variable");
};
@@ -673,7 +711,7 @@ where
)
.await
.map(|v| v.0)
},
}
}
}
// endregion:Contract Deployment
@@ -719,7 +757,7 @@ where
Ok(receipt) => {
info!("Polling succeeded, receipt found");
Ok(ControlFlow::Break(receipt))
},
}
Err(_) => Ok(ControlFlow::Continue(())),
}
}
@@ -96,8 +96,13 @@ pub async fn handle_differential_benchmarks(
// Creating the objects that will be shared between the various runs. The cached compiler is the
// only one at the current moment of time that's safe to share between runs.
let cached_compiler = CachedCompiler::new(
context.working_directory.as_path().join("compilation_cache"),
context.compilation_configuration.invalidate_compilation_cache,
context
.working_directory
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
)
.await
.map(Arc::new)
@@ -156,7 +161,9 @@ pub async fn handle_differential_benchmarks(
watcher.run(),
driver.execute_all().inspect(|_| {
info!("All transactions submitted - driver completed execution");
watcher_tx.send(WatcherEvent::AllTransactionsSubmitted).unwrap()
watcher_tx
.send(WatcherEvent::AllTransactionsSubmitted)
.unwrap()
}),
)
.await
@@ -27,7 +27,11 @@ impl ExecutionState {
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
) -> Self {
Self { compiled_contracts, deployed_contracts, variables: Default::default() }
Self {
compiled_contracts,
deployed_contracts,
variables: Default::default(),
}
}
pub fn empty() -> Self {
@@ -33,7 +33,14 @@ impl Watcher {
blocks_stream: Pin<Box<dyn Stream<Item = MinedBlockInformation>>>,
) -> (Self, UnboundedSender<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)]
@@ -42,8 +49,9 @@ impl Watcher {
// 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.
let ignore_block_before = loop {
let Some(WatcherEvent::RepetitionStartEvent { ignore_block_before }) =
self.rx.recv().await
let Some(WatcherEvent::RepetitionStartEvent {
ignore_block_before,
}) = self.rx.recv().await
else {
continue;
};
@@ -72,16 +80,19 @@ impl Watcher {
// Subsequent repetition starts are ignored since certain workloads can
// contain nested repetitions and therefore there's no use in doing any
// action if the repetitions are nested.
WatcherEvent::RepetitionStartEvent { .. } => {},
WatcherEvent::RepetitionStartEvent { .. } => {}
WatcherEvent::SubmittedTransaction { transaction_hash } => {
watch_for_transaction_hashes.write().await.insert(transaction_hash);
},
watch_for_transaction_hashes
.write()
.await
.insert(transaction_hash);
}
WatcherEvent::AllTransactionsSubmitted => {
*all_transactions_submitted.write().await = true;
self.rx.close();
info!("Watcher's Events Watching Task Finished");
break;
},
}
}
}
}
@@ -100,8 +111,8 @@ impl Watcher {
continue;
}
if *all_transactions_submitted.read().await &&
watch_for_transaction_hashes.read().await.is_empty()
if *all_transactions_submitted.read().await
&& watch_for_transaction_hashes.read().await.is_empty()
{
break;
}
@@ -140,8 +151,15 @@ impl Watcher {
use std::io::Write;
let mut stderr = std::io::stderr().lock();
writeln!(stderr, "Watcher information for {}", self.platform_identifier)?;
writeln!(stderr, "block_number,block_timestamp,mined_gas,block_gas_limit,tx_count")?;
writeln!(
stderr,
"Watcher information for {}",
self.platform_identifier
)?;
writeln!(
stderr,
"block_number,block_timestamp,mined_gas,block_gas_limit,tx_count"
)?;
for block in mined_blocks_information {
writeln!(
stderr,
+105 -43
View File
@@ -112,7 +112,9 @@ impl<'a> Driver<'a, StepsIterator> {
pub async fn execute_all(mut self) -> Result<usize> {
let platform_drivers = std::mem::take(&mut self.platform_drivers);
let results = futures::future::try_join_all(
platform_drivers.into_values().map(|driver| driver.execute_all()),
platform_drivers
.into_values()
.map(|driver| driver.execute_all()),
)
.await
.context("Failed to execute all of the steps on the driver")?;
@@ -250,8 +252,11 @@ where
TransactionRequest::default().from(deployer_address),
code,
);
let receipt =
platform_information.node.execute_transaction(tx).await.inspect_err(|err| {
let receipt = platform_information
.node
.execute_transaction(tx)
.await
.inspect_err(|err| {
error!(
?err,
%library_instance,
@@ -260,7 +265,9 @@ where
)
})?;
let library_address = receipt.contract_address.expect("Failed to deploy the library");
let library_address = receipt
.contract_address
.expect("Failed to deploy the library");
deployed_libraries.get_or_insert_default().insert(
library_instance.clone(),
@@ -288,7 +295,10 @@ where
})
.context("Failed to compile the post-link contracts")?;
Ok(ExecutionState::new(compiler_output.contracts, deployed_libraries.unwrap_or_default()))
Ok(ExecutionState::new(
compiler_output.contracts,
deployed_libraries.unwrap_or_default(),
))
}
// endregion:Constructors & Initialization
@@ -382,7 +392,11 @@ where
) -> Result<HashMap<ContractInstance, TransactionReceipt>> {
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
for instance in step.find_all_contract_instances().into_iter() {
if !self.execution_state.deployed_contracts.contains_key(&instance) {
if !self
.execution_state
.deployed_contracts
.contains_key(&instance)
{
instances_we_must_deploy.entry(instance).or_insert(false);
}
}
@@ -394,13 +408,20 @@ where
let mut receipts = HashMap::new();
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
let calldata = deploy_with_constructor_arguments.then_some(&step.calldata);
let value = deploy_with_constructor_arguments.then_some(step.value).flatten();
let value = deploy_with_constructor_arguments
.then_some(step.value)
.flatten();
let caller = {
let context = self.default_resolution_context();
let resolver = self.platform_information.node.resolver().await?;
let resolved = step.caller.resolve_address(resolver.as_ref(), context).await?;
self.platform_information.node.resolve_signer_or_default(resolved)
let resolved = step
.caller
.resolve_address(resolver.as_ref(), context)
.await?;
self.platform_information
.node
.resolve_signer_or_default(resolved)
};
if let (_, _, Some(receipt)) = self
.get_or_deploy_contract_instance(&instance, caller, calldata, value)
@@ -435,16 +456,20 @@ where
Ok(tx) => tx,
Err(err) => {
return Err(err);
},
}
};
// Resolve the signer to ensure we use an address that has keys
if let Some(from) = tx.from {
tx.from = Some(self.platform_information.node.resolve_signer_or_default(from));
tx.from = Some(
self.platform_information
.node
.resolve_signer_or_default(from),
);
}
self.platform_information.node.execute_transaction(tx).await
},
}
}
}
@@ -491,13 +516,18 @@ where
};
// Handling the return data variable assignments.
for (variable_name, output_word) in assignments
.return_data
.iter()
.zip(tracing_result.output.as_ref().unwrap_or_default().to_vec().chunks(32))
{
for (variable_name, output_word) in assignments.return_data.iter().zip(
tracing_result
.output
.as_ref()
.unwrap_or_default()
.to_vec()
.chunks(32),
) {
let value = U256::from_be_slice(output_word);
self.execution_state.variables.insert(variable_name.clone(), value);
self.execution_state
.variables
.insert(variable_name.clone(), value);
tracing::info!(
variable_name,
variable_value = hex::encode(value.to_be_bytes::<32>()),
@@ -517,12 +547,18 @@ where
) -> Result<()> {
// Resolving the `step.expected` into a series of expectations that we can then assert on.
let mut expectations = match step {
FunctionCallStep { expected: Some(Expected::Calldata(calldata)), .. } =>
vec![ExpectedOutput::new().with_calldata(calldata.clone())],
FunctionCallStep { expected: Some(Expected::Expected(expected)), .. } =>
vec![expected.clone()],
FunctionCallStep { expected: Some(Expected::ExpectedMany(expected)), .. } =>
expected.clone(),
FunctionCallStep {
expected: Some(Expected::Calldata(calldata)),
..
} => vec![ExpectedOutput::new().with_calldata(calldata.clone())],
FunctionCallStep {
expected: Some(Expected::Expected(expected)),
..
} => vec![expected.clone()],
FunctionCallStep {
expected: Some(Expected::ExpectedMany(expected)),
..
} => expected.clone(),
FunctionCallStep { expected: None, .. } => vec![ExpectedOutput::new().with_success()],
};
@@ -641,8 +677,11 @@ where
}
// Handling the topics assertion.
for (expected, actual) in
expected_event.topics.as_slice().iter().zip(actual_event.topics())
for (expected, actual) in expected_event
.topics
.as_slice()
.iter()
.zip(actual_event.topics())
{
let expected = Calldata::new_compound([expected]);
if !expected
@@ -812,7 +851,9 @@ where
let account = private_key.address();
let variable = U256::from_be_slice(account.0.as_slice());
self.execution_state.variables.insert(variable_name.to_string(), variable);
self.execution_state
.variables
.insert(variable_name.to_string(), variable);
Ok(1)
}
@@ -835,8 +876,10 @@ where
calldata: Option<&Calldata>,
value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
if let Some((_, address, abi)) =
self.execution_state.deployed_contracts.get(contract_instance)
if let Some((_, address, abi)) = self
.execution_state
.deployed_contracts
.get(contract_instance)
{
info!(
@@ -874,10 +917,19 @@ where
calldata: Option<&Calldata>,
value: Option<EtherValue>,
) -> Result<(Address, JsonAbi, TransactionReceipt)> {
let Some(ContractPathAndIdent { contract_source_path, contract_ident }) =
self.test_definition.metadata.contract_sources()?.remove(contract_instance)
let Some(ContractPathAndIdent {
contract_source_path,
contract_ident,
}) = self
.test_definition
.metadata
.contract_sources()?
.remove(contract_instance)
else {
anyhow::bail!("Contract source not found for instance {:?}", contract_instance)
anyhow::bail!(
"Contract source not found for instance {:?}",
contract_instance
)
};
let Some((code, abi)) = self
@@ -887,7 +939,10 @@ where
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
.cloned()
else {
anyhow::bail!("Failed to find information for contract {:?}", contract_instance)
anyhow::bail!(
"Failed to find information for contract {:?}",
contract_instance
)
};
let mut code = match alloy::hex::decode(&code) {
@@ -900,18 +955,22 @@ where
"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)
},
}
};
if let Some(calldata) = calldata {
let resolver = self.platform_information.node.resolver().await?;
let calldata =
calldata.calldata(resolver.as_ref(), self.default_resolution_context()).await?;
let calldata = calldata
.calldata(resolver.as_ref(), self.default_resolution_context())
.await?;
code.extend(calldata);
}
let tx = {
let deployer = self.platform_information.node.resolve_signer_or_default(deployer);
let deployer = self
.platform_information
.node
.resolve_signer_or_default(deployer);
let tx = TransactionRequest::default().from(deployer);
let tx = match value {
Some(ref value) => tx.value(value.into_inner()),
@@ -925,7 +984,7 @@ where
Err(error) => {
tracing::error!(?error, "Contract deployment transaction failed.");
return Err(error);
},
}
};
let Some(address) = receipt.contract_address else {
@@ -940,9 +999,10 @@ where
.reporter
.report_contract_deployed_event(contract_instance.clone(), address)?;
self.execution_state
.deployed_contracts
.insert(contract_instance.clone(), (contract_ident, address, abi.clone()));
self.execution_state.deployed_contracts.insert(
contract_instance.clone(),
(contract_ident, address, abi.clone()),
);
Ok((address, abi, receipt))
}
@@ -955,7 +1015,9 @@ where
match step_address {
StepAddress::Address(address) => Ok(*address),
StepAddress::ResolvableAddress(resolvable) => {
let Some(instance) = resolvable.strip_suffix(".address").map(ContractInstance::new)
let Some(instance) = resolvable
.strip_suffix(".address")
.map(ContractInstance::new)
else {
bail!("Not an address variable");
};
@@ -968,7 +1030,7 @@ where
)
.await
.map(|v| v.0)
},
}
}
}
// endregion:Contract Deployment
@@ -85,8 +85,13 @@ pub async fn handle_differential_tests(
// Creating everything else required for the driver to run.
let cached_compiler = CachedCompiler::new(
context.working_directory.as_path().join("compilation_cache"),
context.compilation_configuration.invalidate_compilation_cache,
context
.working_directory
.as_path()
.join("compilation_cache"),
context
.compilation_configuration
.invalidate_compilation_cache,
)
.await
.map(Arc::new)
@@ -141,7 +146,7 @@ pub async fn handle_differential_tests(
drop(permit);
running_task_list.write().await.remove(&test_id);
return;
},
}
};
info!("Created the driver for the test case");
@@ -156,7 +161,7 @@ pub async fn handle_differential_tests(
.report_test_failed_event(format!("{error:#}"))
.expect("Can't fail");
error!("Test Case Failed");
},
}
};
info!("Finished the execution of the test case");
drop(permit);
@@ -167,14 +172,20 @@ pub async fn handle_differential_tests(
))
.inspect(|_| {
info!("Finished executing all test cases");
reporter_clone.report_completion_event().expect("Can't fail")
reporter_clone
.report_completion_event()
.expect("Can't fail")
});
let cli_reporting_task = start_cli_reporting_task(reporter);
tokio::task::spawn(async move {
loop {
let remaining_tasks = running_task_list.read().await;
info!(count = remaining_tasks.len(), ?remaining_tasks, "Remaining Tests");
info!(
count = remaining_tasks.len(),
?remaining_tasks,
"Remaining Tests"
);
tokio::time::sleep(Duration::from_secs(10)).await
}
});
@@ -223,7 +234,7 @@ async fn start_cli_reporting_task(reporter: Reporter) {
"{}{}Case Succeeded{} - Steps Executed: {}{}",
GREEN, BOLD, BOLD_RESET, steps_executed, COLOR_RESET
)
},
}
TestCaseStatus::Failed { reason } => {
number_of_failures += 1;
writeln!(
@@ -235,7 +246,7 @@ async fn start_cli_reporting_task(reporter: Reporter) {
reason.trim(),
COLOR_RESET,
)
},
}
TestCaseStatus::Ignored { reason, .. } => writeln!(
buf,
"{}{}Case Ignored{} - Reason: {}{}",
@@ -27,6 +27,10 @@ impl ExecutionState {
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
) -> Self {
Self { compiled_contracts, deployed_contracts, variables: Default::default() }
Self {
compiled_contracts,
deployed_contracts,
variables: Default::default(),
}
}
}
+28 -17
View File
@@ -41,7 +41,10 @@ impl<'a> CachedCompiler<'a> {
.await
.context("Failed to invalidate compilation cache directory")?;
}
Ok(Self { artifacts_cache: cache, cache_key_lock: Default::default() })
Ok(Self {
artifacts_cache: cache,
cache_key_lock: Default::default(),
})
}
/// Compiles or gets the compilation artifacts from the cache.
@@ -109,7 +112,7 @@ impl<'a> CachedCompiler<'a> {
.await
.context("Compilation callback for deployed libraries failed")?
.compiler_output
},
}
// If no deployed libraries are specified then we can follow the cached flow and attempt
// to lookup the compilation artifacts in the cache.
None => {
@@ -123,7 +126,7 @@ impl<'a> CachedCompiler<'a> {
Some(value) => {
drop(read_guard);
value
},
}
None => {
drop(read_guard);
self.cache_key_lock
@@ -132,7 +135,7 @@ impl<'a> CachedCompiler<'a> {
.entry(cache_key.clone())
.or_default()
.clone()
},
}
};
let _guard = mutex.lock().await;
@@ -160,7 +163,7 @@ impl<'a> CachedCompiler<'a> {
.expect("Can't happen");
}
cache_value.compiler_output
},
}
None => {
let compiler_output = compilation_callback()
.await
@@ -169,16 +172,18 @@ impl<'a> CachedCompiler<'a> {
self.artifacts_cache
.insert(
&cache_key,
&CacheValue { compiler_output: compiler_output.clone() },
&CacheValue {
compiler_output: compiler_output.clone(),
},
)
.await
.context(
"Failed to write the cached value of the compilation artifacts",
)?;
compiler_output
},
}
},
}
}
};
Ok(compiled_contracts)
@@ -220,7 +225,9 @@ async fn compile_contracts(
.flat_map(|value| value.iter())
.map(|(instance, (ident, address, abi))| (instance, ident, address, abi))
.flat_map(|(_, ident, address, _)| {
all_sources_in_dir.iter().map(move |path| (ident, address, path))
all_sources_in_dir
.iter()
.map(move |path| (ident, address, path))
})
.fold(compiler, |compiler, (ident, address, path)| {
compiler.with_library(path, ident.as_str(), *address)
@@ -241,7 +248,7 @@ async fn compile_contracts(
output.clone(),
)
.expect("Can't happen");
},
}
(Ok(output), false) => {
reporter
.report_pre_link_contracts_compilation_succeeded_event(
@@ -252,7 +259,7 @@ async fn compile_contracts(
output.clone(),
)
.expect("Can't happen");
},
}
(Err(err), true) => {
reporter
.report_post_link_contracts_compilation_failed_event(
@@ -262,7 +269,7 @@ async fn compile_contracts(
format!("{err:#}"),
)
.expect("Can't happen");
},
}
(Err(err), false) => {
reporter
.report_pre_link_contracts_compilation_failed_event(
@@ -272,7 +279,7 @@ async fn compile_contracts(
format!("{err:#}"),
)
.expect("Can't happen");
},
}
}
output
@@ -284,7 +291,9 @@ struct ArtifactsCache {
impl ArtifactsCache {
pub fn new(path: impl AsRef<Path>) -> Self {
Self { path: path.as_ref().to_path_buf() }
Self {
path: path.as_ref().to_path_buf(),
}
}
#[instrument(level = "debug", skip_all, err)]
@@ -310,7 +319,9 @@ impl ArtifactsCache {
pub async fn get(&self, key: &CacheKey<'_>) -> Option<CacheValue> {
let key = bson::to_vec(key).ok()?;
let value = cacache::read(self.path.as_path(), key.encode_hex()).await.ok()?;
let value = cacache::read(self.path.as_path(), key.encode_hex())
.await
.ok()?;
let value = bson::from_slice::<CacheValue>(&value).ok()?;
Some(value)
}
@@ -325,13 +336,13 @@ impl ArtifactsCache {
Some(value) => {
debug!("Cache hit");
Ok(value)
},
}
None => {
debug!("Cache miss");
let value = callback().await?;
self.insert(key, &value).await?;
Ok(value)
},
}
}
}
}
+8 -3
View File
@@ -37,13 +37,18 @@ impl NodePool {
);
}
let pre_transactions_tasks =
nodes.iter_mut().map(|node| node.pre_transactions()).collect::<Vec<_>>();
let pre_transactions_tasks = nodes
.iter_mut()
.map(|node| node.pre_transactions())
.collect::<Vec<_>>();
futures::future::try_join_all(pre_transactions_tasks)
.await
.context("Failed to run the pre-transactions task")?;
Ok(Self { nodes, next: Default::default() })
Ok(Self {
nodes,
next: Default::default(),
})
}
/// Get a handle to the next node.
+21 -8
View File
@@ -66,12 +66,15 @@ pub async fn create_test_definitions_stream<'a>(
// Inform the reporter of each one of the test cases that were discovered which we
// expect to run.
.inspect(|(_, _, _, _, reporter)| {
reporter.report_test_case_discovery_event().expect("Can't fail");
reporter
.report_test_case_discovery_event()
.expect("Can't fail");
}),
)
// Creating the Test Definition objects from all of the various objects we have and creating
// their required dependencies (e.g., compiler).
.filter_map(move |(metadata_file, case_idx, case, mode, reporter)| async move {
.filter_map(
move |(metadata_file, case_idx, case, mode, reporter)| async move {
let mut platforms = BTreeMap::new();
for (platform, node_pool) in platforms_and_nodes.values() {
let node = node_pool.round_robbin();
@@ -100,7 +103,12 @@ pub async fn create_test_definitions_stream<'a>(
platforms.insert(
platform.platform_identifier(),
TestPlatformInformation { platform: *platform, node, compiler, reporter },
TestPlatformInformation {
platform: *platform,
node,
compiler,
reporter,
},
);
}
@@ -122,7 +130,8 @@ pub async fn create_test_definitions_stream<'a>(
/* Reporter */
reporter,
})
})
},
)
// Filter out the test cases which are incompatible or that can't run in the current setup.
.filter_map(move |test| async move {
match test.check_compatibility() {
@@ -147,7 +156,7 @@ pub async fn create_test_definitions_stream<'a>(
)
.expect("Can't fail");
None
},
}
}
})
.inspect(|test| {
@@ -221,8 +230,9 @@ impl<'a> TestDefinition<'a> {
for (_, platform_information) in self.platforms.iter() {
let is_allowed_for_platform = match self.metadata.targets.as_ref() {
None => true,
Some(required_vm_identifiers) =>
required_vm_identifiers.contains(&platform_information.platform.vm_identifier()),
Some(required_vm_identifiers) => {
required_vm_identifiers.contains(&platform_information.platform.vm_identifier())
}
};
is_allowed &= is_allowed_for_platform;
error_map.insert(
@@ -264,7 +274,10 @@ impl<'a> TestDefinition<'a> {
if is_allowed {
Ok(())
} else {
Err(("EVM version is incompatible for the platforms specified", error_map))
Err((
"EVM version is incompatible for the platforms specified",
error_map,
))
}
}
+71 -38
View File
@@ -37,7 +37,11 @@ pub trait Platform {
/// Returns a full identifier for the platform.
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.
@@ -179,7 +183,9 @@ impl Platform for KitchensinkPolkavmResolcPlatform {
context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context).path.clone();
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context)
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = SubstrateNode::new(
@@ -229,7 +235,9 @@ impl Platform for KitchensinkRevmSolcPlatform {
context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context).path.clone();
let kitchensink_path = AsRef::<KitchensinkConfiguration>::as_ref(&context)
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = SubstrateNode::new(
@@ -279,8 +287,9 @@ impl Platform for ReviveDevNodePolkavmResolcPlatform {
context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_path =
AsRef::<ReviveDevNodeConfiguration>::as_ref(&context).path.clone();
let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context)
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = SubstrateNode::new(
@@ -330,8 +339,9 @@ impl Platform for ReviveDevNodeRevmSolcPlatform {
context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_path =
AsRef::<ReviveDevNodeConfiguration>::as_ref(&context).path.clone();
let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context)
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = SubstrateNode::new(
@@ -381,8 +391,9 @@ impl Platform for ZombienetPolkavmResolcPlatform {
context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let polkadot_parachain_path =
AsRef::<PolkadotParachainConfiguration>::as_ref(&context).path.clone();
let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context)
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -428,8 +439,9 @@ impl Platform for ZombienetRevmSolcPlatform {
context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let polkadot_parachain_path =
AsRef::<PolkadotParachainConfiguration>::as_ref(&context).path.clone();
let polkadot_parachain_path = AsRef::<PolkadotParachainConfiguration>::as_ref(&context)
.path
.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = ZombieNode::new(polkadot_parachain_path, context);
@@ -454,18 +466,24 @@ impl From<PlatformIdentifier> for Box<dyn Platform> {
fn from(value: PlatformIdentifier) -> Self {
match value {
PlatformIdentifier::GethEvmSolc => Box::new(GethEvmSolcPlatform) as Box<_>,
PlatformIdentifier::LighthouseGethEvmSolc =>
Box::new(LighthouseGethEvmSolcPlatform) as Box<_>,
PlatformIdentifier::KitchensinkPolkavmResolc =>
Box::new(KitchensinkPolkavmResolcPlatform) as Box<_>,
PlatformIdentifier::KitchensinkRevmSolc =>
Box::new(KitchensinkRevmSolcPlatform) as Box<_>,
PlatformIdentifier::ReviveDevNodePolkavmResolc =>
Box::new(ReviveDevNodePolkavmResolcPlatform) as Box<_>,
PlatformIdentifier::ReviveDevNodeRevmSolc =>
Box::new(ReviveDevNodeRevmSolcPlatform) as Box<_>,
PlatformIdentifier::ZombienetPolkavmResolc =>
Box::new(ZombienetPolkavmResolcPlatform) as Box<_>,
PlatformIdentifier::LighthouseGethEvmSolc => {
Box::new(LighthouseGethEvmSolcPlatform) as Box<_>
}
PlatformIdentifier::KitchensinkPolkavmResolc => {
Box::new(KitchensinkPolkavmResolcPlatform) as Box<_>
}
PlatformIdentifier::KitchensinkRevmSolc => {
Box::new(KitchensinkRevmSolcPlatform) as Box<_>
}
PlatformIdentifier::ReviveDevNodePolkavmResolc => {
Box::new(ReviveDevNodePolkavmResolcPlatform) as Box<_>
}
PlatformIdentifier::ReviveDevNodeRevmSolc => {
Box::new(ReviveDevNodeRevmSolcPlatform) as Box<_>
}
PlatformIdentifier::ZombienetPolkavmResolc => {
Box::new(ZombienetPolkavmResolcPlatform) as Box<_>
}
PlatformIdentifier::ZombienetRevmSolc => Box::new(ZombienetRevmSolcPlatform) as Box<_>,
}
}
@@ -475,18 +493,24 @@ impl From<PlatformIdentifier> for &dyn Platform {
fn from(value: PlatformIdentifier) -> Self {
match value {
PlatformIdentifier::GethEvmSolc => &GethEvmSolcPlatform as &dyn Platform,
PlatformIdentifier::LighthouseGethEvmSolc =>
&LighthouseGethEvmSolcPlatform as &dyn Platform,
PlatformIdentifier::KitchensinkPolkavmResolc =>
&KitchensinkPolkavmResolcPlatform as &dyn Platform,
PlatformIdentifier::KitchensinkRevmSolc =>
&KitchensinkRevmSolcPlatform as &dyn Platform,
PlatformIdentifier::ReviveDevNodePolkavmResolc =>
&ReviveDevNodePolkavmResolcPlatform as &dyn Platform,
PlatformIdentifier::ReviveDevNodeRevmSolc =>
&ReviveDevNodeRevmSolcPlatform as &dyn Platform,
PlatformIdentifier::ZombienetPolkavmResolc =>
&ZombienetPolkavmResolcPlatform as &dyn Platform,
PlatformIdentifier::LighthouseGethEvmSolc => {
&LighthouseGethEvmSolcPlatform as &dyn Platform
}
PlatformIdentifier::KitchensinkPolkavmResolc => {
&KitchensinkPolkavmResolcPlatform as &dyn Platform
}
PlatformIdentifier::KitchensinkRevmSolc => {
&KitchensinkRevmSolcPlatform as &dyn Platform
}
PlatformIdentifier::ReviveDevNodePolkavmResolc => {
&ReviveDevNodePolkavmResolcPlatform as &dyn Platform
}
PlatformIdentifier::ReviveDevNodeRevmSolc => {
&ReviveDevNodeRevmSolcPlatform as &dyn Platform
}
PlatformIdentifier::ZombienetPolkavmResolc => {
&ZombienetPolkavmResolcPlatform as &dyn Platform
}
PlatformIdentifier::ZombienetRevmSolc => &ZombienetRevmSolcPlatform as &dyn Platform,
}
}
@@ -496,8 +520,17 @@ fn spawn_node<T: Node + EthereumNode + Send + Sync>(
mut node: T,
genesis: Genesis,
) -> anyhow::Result<T> {
info!(id = node.id(), connection_string = node.connection_string(), "Spawning node");
node.spawn(genesis).context("Failed to spawn node process")?;
info!(id = node.id(), connection_string = node.connection_string(), "Spawned node");
info!(
id = node.id(),
connection_string = node.connection_string(),
"Spawning node"
);
node.spawn(genesis)
.context("Failed to spawn node process")?;
info!(
id = node.id(),
connection_string = node.connection_string(),
"Spawned node"
);
Ok(node)
}
+1 -1
View File
@@ -76,6 +76,6 @@ fn main() -> anyhow::Result<()> {
let schema = schema_for!(Metadata);
println!("{}", serde_json::to_string_pretty(&schema).unwrap());
Ok(())
},
}
}
}
+8 -2
View File
@@ -54,7 +54,11 @@ pub struct Case {
impl Case {
pub fn steps_iterator(&self) -> impl Iterator<Item = Step> {
let steps_len = self.steps.len();
self.steps.clone().into_iter().enumerate().map(move |(idx, mut step)| {
self.steps
.clone()
.into_iter()
.enumerate()
.map(move |(idx, mut step)| {
let Step::FunctionCall(ref mut input) = step else {
return step;
};
@@ -80,7 +84,9 @@ impl Case {
&self,
default_repeat_count: usize,
) -> Box<dyn Iterator<Item = Step> + '_> {
let contains_repeat = self.steps_iterator().any(|step| matches!(&step, Step::Repeat(..)));
let contains_repeat = self
.steps_iterator()
.any(|step| matches!(&step, Step::Repeat(..)));
if contains_repeat {
Box::new(self.steps_iterator()) as Box<_>
} else {
+17 -9
View File
@@ -86,7 +86,11 @@ impl Corpus {
.collect::<Vec<_>>();
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);
info!(len = tests.len(), corpus_name = self.name(), "Found tests in Corpus");
info!(
len = tests.len(),
corpus_name = self.name(),
"Found tests in Corpus"
);
tests
}
@@ -98,19 +102,23 @@ impl Corpus {
pub fn paths_iter(&self) -> impl Iterator<Item = &Path> {
match self {
Corpus::SinglePath { path, .. } =>
Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>>,
Corpus::MultiplePaths { paths, .. } =>
Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>,
Corpus::SinglePath { path, .. } => {
Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>>
}
Corpus::MultiplePaths { paths, .. } => {
Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>
}
}
}
pub fn paths_iter_mut(&mut self) -> impl Iterator<Item = &mut PathBuf> {
match self {
Corpus::SinglePath { path, .. } =>
Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>>,
Corpus::MultiplePaths { paths, .. } =>
Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>,
Corpus::SinglePath { path, .. } => {
Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>>
}
Corpus::MultiplePaths { paths, .. } => {
Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>
}
}
}
+65 -19
View File
@@ -44,7 +44,9 @@ impl MetadataFile {
if self.corpus_file_path.is_file() {
&self.corpus_file_path
} else {
self.metadata_file_path.strip_prefix(&self.corpus_file_path).unwrap()
self.metadata_file_path
.strip_prefix(&self.corpus_file_path)
.unwrap()
}
}
}
@@ -157,10 +159,19 @@ impl Metadata {
return Ok(sources);
};
for (alias, ContractPathAndIdent { contract_source_path, contract_ident }) in contracts {
for (
alias,
ContractPathAndIdent {
contract_source_path,
contract_ident,
},
) in contracts
{
let alias = alias.clone();
let absolute_path =
directory.join(contract_source_path).canonicalize().map_err(|error| {
let absolute_path = directory
.join(contract_source_path)
.canonicalize()
.map_err(|error| {
anyhow::anyhow!(
"Failed to canonicalize contract source path '{}': {error}",
directory.join(contract_source_path).display()
@@ -170,7 +181,10 @@ impl Metadata {
sources.insert(
alias,
ContractPathAndIdent { contract_source_path: absolute_path, contract_ident },
ContractPathAndIdent {
contract_source_path: absolute_path,
contract_ident,
},
);
}
@@ -208,11 +222,11 @@ impl Metadata {
Ok(mut metadata) => {
metadata.file_path = Some(path.to_path_buf());
Some(metadata)
},
}
Err(err) => {
error!(path = %path.display(), %err, "Deserialization of metadata failed");
None
},
}
}
}
@@ -245,11 +259,11 @@ impl Metadata {
.into(),
);
Some(metadata)
},
}
Err(err) => {
error!(path = %path.display(), %err, "Failed to deserialize metadata");
None
},
}
}
}
@@ -323,7 +337,12 @@ pub struct ContractPathAndIdent {
impl Display for ContractPathAndIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.contract_source_path.display(), self.contract_ident.as_ref())
write!(
f,
"{}:{}",
self.contract_source_path.display(),
self.contract_ident.as_ref()
)
}
}
@@ -343,7 +362,7 @@ impl FromStr for ContractPathAndIdent {
Some(ref mut path) => {
path.push(':');
path.push_str(next_item);
},
}
None => path = Some(next_item.to_owned()),
}
} else {
@@ -363,7 +382,7 @@ impl FromStr for ContractPathAndIdent {
contract_source_path: PathBuf::from(path),
contract_ident: ContractIdent::new(identifier),
})
},
}
(None, None) => anyhow::bail!("Failed to find the path and identifier"),
}
}
@@ -401,23 +420,43 @@ pub struct EvmVersionRequirement {
impl EvmVersionRequirement {
pub fn new_greater_than_or_equals(version: EVMVersion) -> Self {
Self { ordering: Ordering::Greater, or_equal: true, evm_version: version }
Self {
ordering: Ordering::Greater,
or_equal: true,
evm_version: version,
}
}
pub fn new_greater_than(version: EVMVersion) -> Self {
Self { ordering: Ordering::Greater, or_equal: false, evm_version: version }
Self {
ordering: Ordering::Greater,
or_equal: false,
evm_version: version,
}
}
pub fn new_equals(version: EVMVersion) -> Self {
Self { ordering: Ordering::Equal, or_equal: false, evm_version: version }
Self {
ordering: Ordering::Equal,
or_equal: false,
evm_version: version,
}
}
pub fn new_less_than(version: EVMVersion) -> Self {
Self { ordering: Ordering::Less, or_equal: false, evm_version: version }
Self {
ordering: Ordering::Less,
or_equal: false,
evm_version: version,
}
}
pub fn new_less_than_or_equals(version: EVMVersion) -> Self {
Self { ordering: Ordering::Less, or_equal: true, evm_version: version }
Self {
ordering: Ordering::Less,
or_equal: true,
evm_version: version,
}
}
pub fn matches(&self, other: &EVMVersion) -> bool {
@@ -428,7 +467,11 @@ impl EvmVersionRequirement {
impl Display for EvmVersionRequirement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { ordering, or_equal, evm_version } = self;
let Self {
ordering,
or_equal,
evm_version,
} = self;
match ordering {
Ordering::Less => write!(f, "<")?,
Ordering::Equal => write!(f, "=")?,
@@ -555,7 +598,10 @@ mod test {
// Assert
let identifier = identifier.expect("Failed to parse");
assert_eq!(identifier.contract_source_path.display().to_string(), "ERC20/ERC20.sol");
assert_eq!(
identifier.contract_source_path.display().to_string(),
"ERC20/ERC20.sol"
);
assert_eq!(identifier.contract_ident, "ERC20".to_owned().into());
// Act
+20 -6
View File
@@ -77,7 +77,12 @@ impl FromStr for ParsedMode {
None => None,
};
Ok(ParsedMode { pipeline, optimize_flag, optimize_setting, version })
Ok(ParsedMode {
pipeline,
optimize_flag,
optimize_setting,
version,
})
}
}
@@ -133,9 +138,13 @@ impl ParsedMode {
|p| EitherIter::B(std::iter::once(*p)),
);
let optimize_flag_setting = self
.optimize_flag
.map(|flag| if flag { ModeOptimizerSetting::M3 } else { ModeOptimizerSetting::M0 });
let optimize_flag_setting = self.optimize_flag.map(|flag| {
if flag {
ModeOptimizerSetting::M3
} else {
ModeOptimizerSetting::M0
}
});
let optimize_flag_iter = match optimize_flag_setting {
Some(setting) => EitherIter::A(std::iter::once(setting)),
@@ -148,7 +157,9 @@ impl ParsedMode {
);
pipeline_iter.flat_map(move |pipeline| {
optimize_settings_iter.clone().map(move |optimize_setting| Mode {
optimize_settings_iter
.clone()
.map(move |optimize_setting| Mode {
pipeline,
optimize_setting,
version: self.version.clone(),
@@ -224,7 +235,10 @@ mod tests {
("Y+", vec!["Y M3"]),
("Y-", vec!["Y M0"]),
("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 {
+106 -32
View File
@@ -78,7 +78,12 @@ impl StepPath {
impl Display for StepPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.iter().map(|idx| idx.to_string()).collect::<Vec<_>>().join(".").fmt(f)
self.0
.iter()
.map(|idx| idx.to_string())
.collect::<Vec<_>>()
.join(".")
.fmt(f)
}
}
@@ -451,7 +456,9 @@ impl StepAddress {
impl FunctionCallStep {
pub const fn default_caller_address() -> Address {
Address(FixedBytes(alloy::hex!("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1")))
Address(FixedBytes(alloy::hex!(
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"
)))
}
pub const fn default_caller() -> StepAddress {
@@ -476,7 +483,7 @@ impl FunctionCallStep {
.context("Failed to produce calldata for deployer/fallback method")?;
Ok(calldata.into())
},
}
Method::FunctionName(ref function_name) => {
let Some(abi) = context.deployed_contract_abi(&self.instance) else {
anyhow::bail!("ABI for instance '{}' not found", self.instance.as_ref());
@@ -527,7 +534,7 @@ impl FunctionCallStep {
.context("Failed to append encoded argument to calldata buffer")?;
Ok(calldata.into())
},
}
}
}
@@ -542,9 +549,11 @@ impl FunctionCallStep {
.await
.context("Failed to encode input bytes for transaction request")?;
let caller = self.caller.resolve_address(resolver, context).await?;
let transaction_request = TransactionRequest::default()
.from(caller)
.value(self.value.map(|value| value.into_inner()).unwrap_or_default());
let transaction_request = TransactionRequest::default().from(caller).value(
self.value
.map(|value| value.into_inner())
.unwrap_or_default(),
);
match self.method {
Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)),
_ => Ok(transaction_request
@@ -626,7 +635,8 @@ impl Calldata {
context: ResolutionContext<'_>,
) -> anyhow::Result<Vec<u8>> {
let mut buffer = Vec::<u8>::with_capacity(self.size_requirement());
self.calldata_into_slice(&mut buffer, resolver, context).await?;
self.calldata_into_slice(&mut buffer, resolver, context)
.await?;
Ok(buffer)
}
@@ -639,7 +649,7 @@ impl Calldata {
match self {
Calldata::Single(bytes) => {
buffer.extend_from_slice(bytes);
},
}
Calldata::Compound(items) => {
let resolved = stream::iter(items.iter().enumerate())
.map(|(arg_idx, arg)| async move {
@@ -654,7 +664,7 @@ impl Calldata {
.context("Failed to resolve one or more calldata arguments")?;
buffer.extend(resolved.into_iter().flatten());
},
}
};
Ok(())
}
@@ -703,7 +713,7 @@ impl Calldata {
.all(|v| async move { v.is_ok_and(|v| v) })
.map(Ok)
.await
},
}
}
}
}
@@ -717,7 +727,10 @@ impl CalldataItem {
) -> anyhow::Result<U256> {
let mut stack = Vec::<CalldataToken<U256>>::new();
for token in self.calldata_tokens().map(|token| token.resolve(resolver, context)) {
for token in self
.calldata_tokens()
.map(|token| token.resolve(resolver, context))
{
let token = token.await?;
let new_token = match token {
CalldataToken::Item(_) => token,
@@ -739,15 +752,17 @@ impl CalldataItem {
Operation::BitwiseAnd => Some(left_operand & right_operand),
Operation::BitwiseOr => Some(left_operand | right_operand),
Operation::BitwiseXor => Some(left_operand ^ right_operand),
Operation::ShiftLeft =>
Some(left_operand << usize::try_from(right_operand)?),
Operation::ShiftRight =>
Some(left_operand >> usize::try_from(right_operand)?),
Operation::ShiftLeft => {
Some(left_operand << usize::try_from(right_operand)?)
}
Operation::ShiftRight => {
Some(left_operand >> usize::try_from(right_operand)?)
}
}
.context("Invalid calldata arithmetic operation - Invalid operation")?;
CalldataToken::Item(result)
},
}
};
stack.push(new_token)
}
@@ -756,7 +771,9 @@ impl CalldataItem {
// Empty stack means that we got an empty compound calldata which we resolve to zero.
[] => Ok(U256::ZERO),
[CalldataToken::Item(item)] => Ok(*item),
_ => Err(anyhow::anyhow!("Invalid calldata arithmetic operation - Invalid stack")),
_ => Err(anyhow::anyhow!(
"Invalid calldata arithmetic operation - Invalid stack"
)),
}
}
@@ -900,13 +917,16 @@ impl<T: AsRef<str>> CalldataToken<T> {
.await
.map(U256::from)
} else if let Some(variable_name) = item.strip_prefix(Self::VARIABLE_PREFIX) {
context.variable(variable_name).context("Variable lookup failed").copied()
context
.variable(variable_name)
.context("Variable lookup failed")
.copied()
} else {
U256::from_str_radix(item, 10)
.map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))
};
value.map(CalldataToken::Item)
},
}
Self::Operation(operation) => Ok(CalldataToken::Operation(operation)),
}
}
@@ -1030,7 +1050,13 @@ mod tests {
"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap();
let selector = parsed_abi.function("store").unwrap().first().unwrap().selector().0;
let selector = parsed_abi
.function("store")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input = FunctionCallStep {
instance: ContractInstance::new("Contract"),
@@ -1068,7 +1094,13 @@ mod tests {
]"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
let selector = parsed_abi.function("send").unwrap().first().unwrap().selector().0;
let selector = parsed_abi
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: FunctionCallStep = FunctionCallStep {
instance: "Contract".to_owned().into(),
@@ -1090,7 +1122,10 @@ mod tests {
type T = (alloy::primitives::Address,);
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
assert_eq!(decoded.0, address!("0x1000000000000000000000000000000000000001"));
assert_eq!(
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
}
#[tokio::test]
@@ -1106,7 +1141,13 @@ mod tests {
]"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
let selector = parsed_abi.function("send").unwrap().first().unwrap().selector().0;
let selector = parsed_abi
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: FunctionCallStep = FunctionCallStep {
instance: ContractInstance::new("Contract"),
@@ -1128,7 +1169,10 @@ mod tests {
type T = (alloy::primitives::Address,);
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
assert_eq!(decoded.0, address!("0x1000000000000000000000000000000000000001"));
assert_eq!(
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
}
async fn resolve_calldata_item(
@@ -1165,7 +1209,12 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(
resolved,
U256::from(MockResolver.block_gas_limit(Default::default()).await.unwrap())
U256::from(
MockResolver
.block_gas_limit(Default::default())
.await
.unwrap()
)
)
}
@@ -1182,7 +1231,11 @@ mod tests {
assert_eq!(
resolved,
U256::from_be_slice(
MockResolver.block_coinbase(Default::default()).await.unwrap().as_ref()
MockResolver
.block_coinbase(Default::default())
.await
.unwrap()
.as_ref()
)
)
}
@@ -1197,7 +1250,13 @@ mod tests {
// Assert
let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(resolved, MockResolver.block_difficulty(Default::default()).await.unwrap())
assert_eq!(
resolved,
MockResolver
.block_difficulty(Default::default())
.await
.unwrap()
)
}
#[tokio::test]
@@ -1212,7 +1271,11 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(
resolved,
MockResolver.block_base_fee(Default::default()).await.map(U256::from).unwrap()
MockResolver
.block_base_fee(Default::default())
.await
.map(U256::from)
.unwrap()
)
}
@@ -1242,7 +1305,10 @@ mod tests {
// Assert
let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap()))
assert_eq!(
resolved,
U256::from(MockResolver.last_block_number().await.unwrap())
)
}
#[tokio::test]
@@ -1257,7 +1323,12 @@ mod tests {
let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(
resolved,
U256::from(MockResolver.block_timestamp(Default::default()).await.unwrap())
U256::from(
MockResolver
.block_timestamp(Default::default())
.await
.unwrap()
)
)
}
@@ -1335,7 +1406,10 @@ mod tests {
// Assert
let resolved = resolved.expect("Failed to resolve argument");
assert_eq!(resolved, U256::from(MockResolver.last_block_number().await.unwrap() + 10));
assert_eq!(
resolved,
U256::from(MockResolver.last_block_number().await.unwrap() + 10)
);
}
#[tokio::test]
+7 -6
View File
@@ -135,11 +135,11 @@ impl<'a> ResolutionContext<'a> {
match self.block_number {
Some(block_number) => match number {
BlockNumberOrTag::Latest => BlockNumberOrTag::Number(*block_number),
n @ (BlockNumberOrTag::Finalized |
BlockNumberOrTag::Safe |
BlockNumberOrTag::Earliest |
BlockNumberOrTag::Pending |
BlockNumberOrTag::Number(_)) => n,
n @ (BlockNumberOrTag::Finalized
| BlockNumberOrTag::Safe
| BlockNumberOrTag::Earliest
| BlockNumberOrTag::Pending
| BlockNumberOrTag::Number(_)) => n,
},
None => number,
}
@@ -162,7 +162,8 @@ impl<'a> ResolutionContext<'a> {
}
pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> {
self.variables.and_then(|variables| variables.get(name.as_ref()))
self.variables
.and_then(|variables| variables.get(name.as_ref()))
}
pub fn tip_block_number(&self) -> Option<&'a BlockNumber> {
+55 -26
View File
@@ -131,17 +131,20 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
Ok(mf) => {
info!("Loaded metadata with {} case(s)", mf.cases.len());
mf
},
}
Err(e) => {
println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display);
println!(" Error loading metadata: {}", e);
failed_files += 1;
failures.push((file_display.clone(), format!("Error loading metadata: {}", e)));
failures.push((
file_display.clone(),
format!("Error loading metadata: {}", e),
));
if args.bail {
break;
}
continue;
},
}
};
info!("Executing test file: {}", file_display);
@@ -155,7 +158,7 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
let mut cache = cached_passed.lock().await;
cache.insert(file_display);
}
},
}
Err(e) => {
println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display);
failed_files += 1;
@@ -165,7 +168,7 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
info!("Bailing after first failure");
break;
}
},
}
}
}
@@ -223,7 +226,7 @@ fn discover_test_files(path: &Path) -> anyhow::Result<Vec<PathBuf>> {
"sol" => {
// Single .sol file
files.push(path.to_path_buf());
},
}
"json" => {
// Corpus file - enumerate its tests
let corpus = Corpus::try_from_path(path)?;
@@ -231,8 +234,11 @@ fn discover_test_files(path: &Path) -> anyhow::Result<Vec<PathBuf>> {
for metadata in metadata_files {
files.push(metadata.metadata_file_path);
}
},
_ => anyhow::bail!("Unsupported file extension: {}. Expected .sol or .json", extension),
}
_ => anyhow::bail!(
"Unsupported file extension: {}. Expected .sol or .json",
extension
),
}
} else if path.is_dir() {
// Walk directory recursively for .sol files
@@ -278,13 +284,15 @@ async fn execute_test_file(
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::ZombienetPolkavmResolc => {
&revive_dt_core::ZombienetPolkavmResolcPlatform
}
PlatformIdentifier::ZombienetRevmSolc => &revive_dt_core::ZombienetRevmSolcPlatform,
};
@@ -296,8 +304,9 @@ async fn execute_test_file(
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")?;
let node_handle = platform
.new_node(context.clone())
.context("Failed to spawn node thread")?;
info!("Waiting for node to start...");
let node = node_handle
@@ -305,24 +314,32 @@ async fn execute_test_file(
.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());
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")?;
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(
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
@@ -345,15 +362,19 @@ async fn execute_test_file(
.map(Arc::new)
.context("Failed to create cached compiler")?;
let private_key_allocator =
Arc::new(Mutex::new(PrivateKeyAllocator::new(alloy::primitives::U256::from(100))));
let private_key_allocator = Arc::new(Mutex::new(PrivateKeyAllocator::new(
alloy::primitives::U256::from(100),
)));
let (reporter, report_task) =
revive_dt_report::ReportAggregator::new(context.clone()).into_task();
tokio::spawn(report_task);
info!("Building test definitions for {} case(s)", metadata_file.cases.len());
info!(
"Building test definitions for {} case(s)",
metadata_file.cases.len()
);
let mut test_definitions = Vec::new();
for (case_idx, case) in metadata_file.cases.iter().enumerate() {
info!("Building test definition for case {}", case_idx);
@@ -400,17 +421,20 @@ async fn execute_test_file(
test_definition.case.steps.len(),
test_definition.case_idx
);
let steps_executed = driver
.execute_all()
.await
.context(format!("Failed to execute case {}", test_definition.case_idx))?;
let steps_executed = driver.execute_all().await.context(format!(
"Failed to execute case {}",
test_definition.case_idx
))?;
info!(
"✓ Case {} completed successfully, executed {} step(s)",
test_definition.case_idx, steps_executed
);
}
info!("─────────────────────────────────────────────────────────────────");
info!("All {} test case(s) executed successfully", test_definitions.len());
info!(
"All {} test case(s) executed successfully",
test_definitions.len()
);
Ok(())
}
@@ -453,7 +477,12 @@ async fn build_test_definition<'a>(
let mut platforms = BTreeMap::new();
platforms.insert(
platform.platform_identifier(),
TestPlatformInformation { platform, node, compiler, reporter: execution_reporter },
TestPlatformInformation {
platform,
node,
compiler,
reporter: execution_reporter,
},
);
let test_definition = TestDefinition {
+32 -13
View File
@@ -33,7 +33,10 @@ impl Process {
let log_file_prefix = log_file_prefix.into();
let (stdout_file_name, stderr_file_name) = match log_file_prefix {
Some(prefix) => (format!("{prefix}_stdout.log"), format!("{prefix}_stderr.log")),
Some(prefix) => (
format!("{prefix}_stdout.log"),
format!("{prefix}_stderr.log"),
),
None => ("stdout.log".to_string(), "stderr.log".to_string()),
};
@@ -54,19 +57,23 @@ impl Process {
.context("Failed to open the stderr logs file")?;
let mut command = {
let stdout_logs_file =
stdout_logs_file.try_clone().context("Failed to clone the stdout logs file")?;
let stderr_logs_file =
stderr_logs_file.try_clone().context("Failed to clone the stderr logs file")?;
let stdout_logs_file = stdout_logs_file
.try_clone()
.context("Failed to clone the stdout logs file")?;
let stderr_logs_file = stderr_logs_file
.try_clone()
.context("Failed to clone the stderr logs file")?;
let mut command = Command::new(binary_path.as_ref());
command_building_callback(&mut command, stdout_logs_file, stderr_logs_file);
command
};
let mut child = command.spawn().context("Failed to spawn the built command")?;
let mut child = command
.spawn()
.context("Failed to spawn the built command")?;
match process_readiness_wait_behavior {
ProcessReadinessWaitBehavior::NoStartupWait => {},
ProcessReadinessWaitBehavior::NoStartupWait => {}
ProcessReadinessWaitBehavior::WaitDuration(duration) => std::thread::sleep(duration),
ProcessReadinessWaitBehavior::TimeBoundedWaitFunction {
max_wait_duration,
@@ -119,23 +126,35 @@ impl Process {
)
}
}
},
}
ProcessReadinessWaitBehavior::WaitForCommandToExit => {
if !child.wait().context("Failed waiting for process to finish")?.success() {
if !child
.wait()
.context("Failed waiting for process to finish")?
.success()
{
anyhow::bail!("Failed to spawn command");
}
},
}
}
Ok(Self { child, stdout_logs_file, stderr_logs_file })
Ok(Self {
child,
stdout_logs_file,
stderr_logs_file,
})
}
}
impl Drop for Process {
fn drop(&mut self) {
self.child.kill().expect("Failed to kill the process");
self.stdout_logs_file.flush().expect("Failed to flush the stdout logs file");
self.stderr_logs_file.flush().expect("Failed to flush the stderr logs file");
self.stdout_logs_file
.flush()
.expect("Failed to flush the stdout logs file");
self.stderr_logs_file
.flush()
.expect("Failed to flush the stderr logs file");
}
}
+68 -23
View File
@@ -108,7 +108,9 @@ impl GethNode {
let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context);
let geth_configuration = AsRef::<GethConfiguration>::as_ref(&context);
let geth_directory = working_directory_configuration.as_path().join(Self::BASE_DIRECTORY);
let geth_directory = working_directory_configuration
.as_path()
.join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = geth_directory.join(id.to_string());
@@ -137,7 +139,10 @@ impl GethNode {
signers::local::PrivateKeySigner,
};
let key_str = private_key.trim().strip_prefix("0x").unwrap_or(private_key.trim());
let key_str = private_key
.trim()
.strip_prefix("0x")
.unwrap_or(private_key.trim());
let key_bytes = alloy::hex::decode(key_str)
.map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?;
@@ -331,14 +336,15 @@ impl GethNode {
ProcessReadinessWaitBehavior::TimeBoundedWaitFunction {
max_wait_duration: self.start_timeout,
check_function: Box::new(|_, stderr_line| match stderr_line {
Some(line) =>
Some(line) => {
if line.contains(Self::ERROR_MARKER) {
anyhow::bail!("Failed to start geth {line}");
} else if line.contains(Self::READY_MARKER) {
Ok(true)
} else {
Ok(false)
},
}
}
None => Ok(false),
}),
},
@@ -351,7 +357,7 @@ impl GethNode {
self.shutdown()
.context("Failed to gracefully shutdown after geth start error")?;
return Err(err);
},
}
}
Ok(self)
@@ -491,12 +497,15 @@ impl EthereumNode for GethNode {
true => Ok(ControlFlow::Continue(())),
false => Err(error.into()),
}
},
}
}
}
},
)
.instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash))
.instrument(tracing::info_span!(
"Awaiting transaction receipt",
?transaction_hash
))
.await
})
}
@@ -508,8 +517,10 @@ impl EthereumNode for GethNode {
trace_options: GethDebugTracingOptions,
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
Box::pin(async move {
let provider =
self.provider().await.context("Failed to create provider for tracing")?;
let provider = self
.provider()
.await
.context("Failed to create provider for tracing")?;
poll(
Self::TRACE_POLLING_DURATION,
PollingWaitBehavior::Constant(Duration::from_millis(200)),
@@ -517,7 +528,10 @@ impl EthereumNode for GethNode {
let provider = provider.clone();
let trace_options = trace_options.clone();
async move {
match provider.debug_trace_transaction(tx_hash, trace_options).await {
match provider
.debug_trace_transaction(tx_hash, trace_options)
.await
{
Ok(trace) => Ok(ControlFlow::Break(trace)),
Err(error) => {
let error_string = error.to_string();
@@ -525,7 +539,7 @@ impl EthereumNode for GethNode {
true => Ok(ControlFlow::Continue(())),
false => Err(error.into()),
}
},
}
}
}
},
@@ -740,7 +754,10 @@ impl ResolverApi for GethNodeResolver {
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.and_then(|block| {
block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
})
})
}
@@ -857,7 +874,11 @@ mod tests {
// Arrange
let (context, node) = shared_state();
let account_address = context.wallet_configuration.wallet().default_signer().address();
let account_address = context
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default()
.to(account_address)
.value(U256::from(100_000_000_000_000u128));
@@ -880,7 +901,10 @@ mod tests {
// Assert
let version = version.expect("Failed to get the version");
assert!(version.starts_with("geth version"), "expected version string, got: '{version}'");
assert!(
version.starts_with("geth version"),
"expected version string, got: '{version}'"
);
}
#[tokio::test]
@@ -904,8 +928,12 @@ mod tests {
let node = shared_node();
// Act
let gas_limit =
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
let gas_limit = node
.resolver()
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = gas_limit.expect("Failed to get the gas limit");
@@ -918,8 +946,12 @@ mod tests {
let node = shared_node();
// Act
let coinbase =
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
let coinbase = node
.resolver()
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = coinbase.expect("Failed to get the coinbase");
@@ -932,8 +964,12 @@ mod tests {
let node = shared_node();
// Act
let block_difficulty =
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
let block_difficulty = node
.resolver()
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -946,7 +982,12 @@ mod tests {
let node = shared_node();
// Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
let block_hash = node
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_hash.expect("Failed to get the block hash");
@@ -959,8 +1000,12 @@ mod tests {
let node = shared_node();
// Act
let block_timestamp =
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
let block_timestamp = node
.resolver()
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -132,7 +132,9 @@ impl LighthouseGethNode {
let wallet_configuration = AsRef::<WalletConfiguration>::as_ref(&context);
let kurtosis_configuration = AsRef::<KurtosisConfiguration>::as_ref(&context);
let geth_directory = working_directory_configuration.as_path().join(Self::BASE_DIRECTORY);
let geth_directory = working_directory_configuration
.as_path()
.join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = geth_directory.join(id.to_string());
@@ -145,7 +147,10 @@ impl LighthouseGethNode {
http_connection_string: String::default(),
enclave_name: format!(
"enclave-{}-{}",
SystemTime::now().duration_since(UNIX_EPOCH).expect("Must not fail").as_nanos(),
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Must not fail")
.as_nanos(),
id
),
@@ -521,12 +526,15 @@ impl LighthouseGethNode {
true => Ok(ControlFlow::Continue(())),
false => Err(error.into()),
}
},
}
}
}
},
)
.instrument(tracing::info_span!("Awaiting transaction receipt", ?transaction_hash))
.instrument(tracing::info_span!(
"Awaiting transaction receipt",
?transaction_hash
))
.await
})
}
@@ -615,7 +623,9 @@ impl EthereumNode for LighthouseGethNode {
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
Box::pin(async move {
let provider = Arc::new(
self.http_provider().await.context("Failed to create provider for tracing")?,
self.http_provider()
.await
.context("Failed to create provider for tracing")?,
);
poll(
Self::TRACE_POLLING_DURATION,
@@ -624,7 +634,10 @@ impl EthereumNode for LighthouseGethNode {
let provider = provider.clone();
let trace_options = trace_options.clone();
async move {
match provider.debug_trace_transaction(tx_hash, trace_options).await {
match provider
.debug_trace_transaction(tx_hash, trace_options)
.await
{
Ok(trace) => Ok(ControlFlow::Break(trace)),
Err(error) => {
let error_string = error.to_string();
@@ -632,7 +645,7 @@ impl EthereumNode for LighthouseGethNode {
true => Ok(ControlFlow::Continue(())),
false => Err(error.into()),
}
},
}
}
}
},
@@ -846,7 +859,10 @@ impl<F: TxFiller<Ethereum>, P: Provider<Ethereum>> ResolverApi
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.and_then(|block| {
block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
})
})
}
@@ -900,7 +916,11 @@ impl Node for LighthouseGethNode {
.spawn()
.expect("Failed to spawn the enclave kill command");
if !child.wait().expect("Failed to wait for the enclave kill command").success() {
if !child
.wait()
.expect("Failed to wait for the enclave kill command")
.success()
{
let stdout = {
let mut stdout = String::default();
child
@@ -1126,7 +1146,11 @@ mod tests {
let (context, node) = new_node();
node.fund_all_accounts().await.expect("Failed");
let account_address = context.wallet_configuration.wallet().default_signer().address();
let account_address = context
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default()
.to(account_address)
.value(U256::from(100_000_000_000_000u128));
@@ -1149,7 +1173,10 @@ mod tests {
// Assert
let version = version.expect("Failed to get the version");
assert!(version.starts_with("CLI Version"), "expected version string, got: '{version}'");
assert!(
version.starts_with("CLI Version"),
"expected version string, got: '{version}'"
);
}
#[tokio::test]
@@ -1173,8 +1200,12 @@ mod tests {
let (_context, node) = new_node();
// Act
let gas_limit =
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
let gas_limit = node
.resolver()
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1187,8 +1218,12 @@ mod tests {
let (_context, node) = new_node();
// Act
let coinbase =
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
let coinbase = node
.resolver()
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = coinbase.expect("Failed to get the coinbase");
@@ -1201,8 +1236,12 @@ mod tests {
let (_context, node) = new_node();
// Act
let block_difficulty =
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
let block_difficulty = node
.resolver()
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1215,7 +1254,12 @@ mod tests {
let (_context, node) = new_node();
// Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
let block_hash = node
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_hash.expect("Failed to get the block hash");
@@ -1228,8 +1272,12 @@ mod tests {
let (_context, node) = new_node();
// Act
let block_timestamp =
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
let block_timestamp = node
.resolver()
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -109,7 +109,9 @@ impl SubstrateNode {
) -> Self {
let working_directory_path =
AsRef::<WorkingDirectoryConfiguration>::as_ref(&context).as_path();
let eth_rpc_path = AsRef::<EthRpcConfiguration>::as_ref(&context).path.as_path();
let eth_rpc_path = AsRef::<EthRpcConfiguration>::as_ref(&context)
.path
.as_path();
let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet();
let substrate_directory = working_directory_path.join(Self::BASE_DIRECTORY);
@@ -141,7 +143,10 @@ impl SubstrateNode {
signers::local::PrivateKeySigner,
};
let key_str = private_key.trim().strip_prefix("0x").unwrap_or(private_key.trim());
let key_str = private_key
.trim()
.strip_prefix("0x")
.unwrap_or(private_key.trim());
let key_bytes = alloy::hex::decode(key_str)
.map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?;
@@ -315,7 +320,7 @@ impl SubstrateNode {
self.shutdown()
.context("Failed to gracefully shutdown after substrate start error")?;
return Err(err);
},
}
}
let eth_proxy_process = Process::new(
@@ -350,7 +355,7 @@ impl SubstrateNode {
self.shutdown()
.context("Failed to gracefully shutdown after eth proxy start error")?;
return Err(err);
},
}
}
Ok(())
@@ -360,7 +365,10 @@ impl SubstrateNode {
&self,
genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> {
genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| {
genesis
.alloc
.iter()
.try_fold(Vec::new(), |mut vec, (address, acc)| {
let substrate_address = Self::eth_to_substrate_address(address);
let balance = acc.balance.try_into()?;
vec.push((substrate_address, balance));
@@ -460,7 +468,10 @@ impl EthereumNode for SubstrateNode {
transaction: TransactionRequest,
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
Box::pin(async move {
let provider = self.provider().await.context("Failed to create the provider")?;
let provider = self
.provider()
.await
.context("Failed to create the provider")?;
execute_transaction(provider, transaction).await
})
}
@@ -682,7 +693,10 @@ impl ResolverApi for SubstrateNodeResolver {
.context("Failed to get the substrate block")?
.context("Failed to get the substrate block, perhaps the chain has no blocks?")
.and_then(|block| {
block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
})
})
}
@@ -961,27 +975,33 @@ impl TransactionBuilder<ReviveNetwork> for <Ethereum as Network>::TransactionReq
);
match result {
Ok(unsigned_tx) => Ok(unsigned_tx),
Err(UnbuiltTransactionError { request, error }) =>
Err(UnbuiltTransactionError { request, error }) => {
Err(UnbuiltTransactionError::<ReviveNetwork> {
request,
error: match error {
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) =>
TransactionBuilderError::InvalidTransactionRequest(tx_type, items),
TransactionBuilderError::UnsupportedSignatureType =>
TransactionBuilderError::UnsupportedSignatureType,
TransactionBuilderError::Signer(error) =>
TransactionBuilderError::Signer(error),
TransactionBuilderError::Custom(error) =>
TransactionBuilderError::Custom(error),
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => {
TransactionBuilderError::InvalidTransactionRequest(tx_type, items)
}
TransactionBuilderError::UnsupportedSignatureType => {
TransactionBuilderError::UnsupportedSignatureType
}
TransactionBuilderError::Signer(error) => {
TransactionBuilderError::Signer(error)
}
TransactionBuilderError::Custom(error) => {
TransactionBuilderError::Custom(error)
}
},
}),
})
}
}
}
async fn build<W: alloy::network::NetworkWallet<ReviveNetwork>>(
self,
wallet: &W,
) -> Result<<ReviveNetwork as Network>::TxEnvelope, TransactionBuilderError<ReviveNetwork>> {
) -> Result<<ReviveNetwork as Network>::TxEnvelope, TransactionBuilderError<ReviveNetwork>>
{
Ok(wallet.sign_request(self).await?)
}
}
@@ -1241,7 +1261,11 @@ mod tests {
let provider = node.provider().await.expect("Failed to create provider");
let account_address = context.wallet_configuration.wallet().default_signer().address();
let account_address = context
.wallet_configuration
.wallet()
.default_signer()
.address();
let transaction = TransactionRequest::default()
.to(account_address)
.value(U256::from(100_000_000_000_000u128));
@@ -1286,8 +1310,9 @@ mod tests {
.expect("init failed");
// Check that the patched chainspec file was generated
let final_chainspec_path =
dummy_node.base_directory.join(SubstrateNode::CHAIN_SPEC_JSON_FILE);
let final_chainspec_path = dummy_node
.base_directory
.join(SubstrateNode::CHAIN_SPEC_JSON_FILE);
assert!(final_chainspec_path.exists(), "Chainspec file should exist");
let contents = fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec");
@@ -1393,7 +1418,10 @@ mod tests {
for (eth_addr, expected_ss58) in cases {
let result = SubstrateNode::eth_to_substrate_address(&eth_addr.parse().unwrap());
assert_eq!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}");
assert_eq!(
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
}
}
@@ -1444,8 +1472,12 @@ mod tests {
let node = shared_node();
// Act
let gas_limit =
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
let gas_limit = node
.resolver()
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1458,8 +1490,12 @@ mod tests {
let node = shared_node();
// Act
let coinbase =
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
let coinbase = node
.resolver()
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = coinbase.expect("Failed to get the coinbase");
@@ -1472,8 +1508,12 @@ mod tests {
let node = shared_node();
// Act
let block_difficulty =
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
let block_difficulty = node
.resolver()
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1486,7 +1526,12 @@ mod tests {
let node = shared_node();
// Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
let block_hash = node
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_hash.expect("Failed to get the block hash");
@@ -1499,8 +1544,12 @@ mod tests {
let node = shared_node();
// Act
let block_timestamp =
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
let block_timestamp = node
.resolver()
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -122,10 +122,14 @@ impl ZombieNode {
+ AsRef<EthRpcConfiguration>
+ AsRef<WalletConfiguration>,
) -> Self {
let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context).path.to_owned();
let eth_proxy_binary = AsRef::<EthRpcConfiguration>::as_ref(&context)
.path
.to_owned();
let working_directory_path = AsRef::<WorkingDirectoryConfiguration>::as_ref(&context);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = working_directory_path.join(Self::BASE_DIRECTORY).join(id.to_string());
let base_directory = working_directory_path
.join(Self::BASE_DIRECTORY)
.join(id.to_string());
let base_directory = base_directory.canonicalize().unwrap_or(base_directory);
let logs_directory = base_directory.join(Self::LOGS_DIRECTORY);
let wallet = AsRef::<WalletConfiguration>::as_ref(&context).wallet();
@@ -193,8 +197,10 @@ impl ZombieNode {
}
fn spawn_process(&mut self) -> anyhow::Result<()> {
let network_config =
self.network_config.clone().context("Node not initialized, call init() first")?;
let network_config = self
.network_config
.clone()
.context("Node not initialized, call init() first")?;
let rt = tokio::runtime::Runtime::new().unwrap();
let network = rt.block_on(async {
@@ -242,7 +248,7 @@ impl ZombieNode {
self.shutdown()
.context("Failed to gracefully shutdown after eth proxy start error")?;
return Err(err);
},
}
}
tracing::debug!("eth-rpc is up");
@@ -266,7 +272,10 @@ impl ZombieNode {
let output = cmd.output().context("Failed to export the chain-spec")?;
if !output.status.success() {
anyhow::bail!("Build chain-spec failed: {}", String::from_utf8_lossy(&output.stderr));
anyhow::bail!(
"Build chain-spec failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
let content = String::from_utf8(output.stdout)
@@ -327,7 +336,10 @@ impl ZombieNode {
&self,
genesis: &Genesis,
) -> anyhow::Result<Vec<(String, u128)>> {
genesis.alloc.iter().try_fold(Vec::new(), |mut vec, (address, acc)| {
genesis
.alloc
.iter()
.try_fold(Vec::new(), |mut vec, (address, acc)| {
let polkadot_address = Self::eth_to_polkadot_address(address);
let balance = acc.balance.try_into()?;
vec.push((polkadot_address, balance));
@@ -663,7 +675,10 @@ impl<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> ResolverApi
.context("Failed to get the zombie block")?
.context("Failed to get the zombie block, perhaps the chain has no blocks?")
.and_then(|block| {
block.header.base_fee_per_gas.context("Failed to get the base fee per gas")
block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
})
})
}
@@ -775,8 +790,10 @@ mod tests {
pub async fn new_node() -> (TestExecutionContext, ZombieNode) {
let context = test_config();
let mut node =
ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
let mut node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(),
&context,
);
let genesis = context.genesis_configuration.genesis().unwrap().clone();
node.init(genesis).unwrap();
@@ -841,11 +858,14 @@ mod tests {
"#;
let context = test_config();
let mut node =
ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
let mut node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Call `init()`
node.init(serde_json::from_str(genesis_content).unwrap()).expect("init failed");
node.init(serde_json::from_str(genesis_content).unwrap())
.expect("init failed");
// Check that the patched chainspec file was generated
let final_chainspec_path = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE);
@@ -886,7 +906,10 @@ mod tests {
"#;
let context = test_config();
let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(),
&context,
);
let result = node
.extract_balance_from_genesis_file(&serde_json::from_str(genesis_json).unwrap())
@@ -948,7 +971,10 @@ mod tests {
for (eth_addr, expected_ss58) in cases {
let result = ZombieNode::eth_to_polkadot_address(&eth_addr.parse().unwrap());
assert_eq!(result, expected_ss58, "Mismatch for Ethereum address {eth_addr}");
assert_eq!(
result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}"
);
}
}
@@ -956,7 +982,10 @@ mod tests {
fn eth_rpc_version_works() {
// Arrange
let context = test_config();
let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act
let version = node.eth_rpc_version().unwrap();
@@ -972,7 +1001,10 @@ mod tests {
fn version_works() {
// Arrange
let context = test_config();
let node = ZombieNode::new(context.polkadot_parachain_configuration.path.clone(), &context);
let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(),
&context,
);
// Act
let version = node.version().unwrap();
@@ -1010,8 +1042,12 @@ mod tests {
let node = shared_node().await;
// Act
let gas_limit =
node.resolver().await.unwrap().block_gas_limit(BlockNumberOrTag::Latest).await;
let gas_limit = node
.resolver()
.await
.unwrap()
.block_gas_limit(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = gas_limit.expect("Failed to get the gas limit");
@@ -1024,8 +1060,12 @@ mod tests {
let node = shared_node().await;
// Act
let coinbase =
node.resolver().await.unwrap().block_coinbase(BlockNumberOrTag::Latest).await;
let coinbase = node
.resolver()
.await
.unwrap()
.block_coinbase(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = coinbase.expect("Failed to get the coinbase");
@@ -1038,8 +1078,12 @@ mod tests {
let node = shared_node().await;
// Act
let block_difficulty =
node.resolver().await.unwrap().block_difficulty(BlockNumberOrTag::Latest).await;
let block_difficulty = node
.resolver()
.await
.unwrap()
.block_difficulty(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_difficulty.expect("Failed to get the block difficulty");
@@ -1052,7 +1096,12 @@ mod tests {
let node = shared_node().await;
// Act
let block_hash = node.resolver().await.unwrap().block_hash(BlockNumberOrTag::Latest).await;
let block_hash = node
.resolver()
.await
.unwrap()
.block_hash(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_hash.expect("Failed to get the block hash");
@@ -1065,8 +1114,12 @@ mod tests {
let node = shared_node().await;
// Act
let block_timestamp =
node.resolver().await.unwrap().block_timestamp(BlockNumberOrTag::Latest).await;
let block_timestamp = node
.resolver()
.await
.unwrap()
.block_timestamp(BlockNumberOrTag::Latest)
.await;
// Assert
let _ = block_timestamp.expect("Failed to get the block timestamp");
@@ -11,7 +11,9 @@ pub struct ConcurrencyLimiterLayer {
impl ConcurrencyLimiterLayer {
pub fn new(permit_count: usize) -> Self {
Self { semaphore: Arc::new(Semaphore::new(permit_count)) }
Self {
semaphore: Arc::new(Semaphore::new(permit_count)),
}
}
}
@@ -19,7 +21,10 @@ impl<S> Layer<S> for ConcurrencyLimiterLayer {
type Service = ConcurrencyLimiterService<S>;
fn layer(&self, inner: S) -> Self::Service {
ConcurrencyLimiterService { service: inner, semaphore: self.semaphore.clone() }
ConcurrencyLimiterService {
service: inner,
semaphore: self.semaphore.clone(),
}
}
}
@@ -50,7 +55,10 @@ where
let future = self.service.call(req);
Box::pin(async move {
let _permit = semaphore.acquire().await.expect("Semaphore has been closed");
let _permit = semaphore
.acquire()
.await
.expect("Semaphore has been closed");
tracing::debug!(
available_permits = semaphore.available_permits(),
"Acquired Semaphore Permit"
@@ -21,7 +21,12 @@ impl FallbackGasFiller {
default_max_fee_per_gas: u128,
default_priority_fee: u128,
) -> Self {
Self { inner: GasFiller, default_gas_limit, default_max_fee_per_gas, default_priority_fee }
Self {
inner: GasFiller,
default_gas_limit,
default_max_fee_per_gas,
default_priority_fee,
}
}
}
+14 -9
View File
@@ -80,8 +80,10 @@ where
NonceFiller: TxFiller<N>,
WalletFiller<W>: TxFiller<N>,
{
let sendable_transaction =
provider.fill(transaction).await.context("Failed to fill transaction")?;
let sendable_transaction = provider
.fill(transaction)
.await
.context("Failed to fill transaction")?;
let transaction_envelope = sendable_transaction
.try_into_envelope()
@@ -98,17 +100,19 @@ where
} else {
return Err(error).context(format!("Failed to submit transaction {tx_hash}"));
}
},
}
};
debug!(%tx_hash, "Submitted Transaction");
pending_transaction.set_timeout(Some(Duration::from_secs(120)));
let tx_hash = pending_transaction
.watch()
.await
.context(format!("Transaction inclusion watching timeout for {tx_hash}"))?;
let tx_hash = pending_transaction.watch().await.context(format!(
"Transaction inclusion watching timeout for {tx_hash}"
))?;
poll(Duration::from_secs(60), PollingWaitBehavior::Constant(Duration::from_secs(3)), || {
poll(
Duration::from_secs(60),
PollingWaitBehavior::Constant(Duration::from_secs(3)),
|| {
let provider = provider.clone();
async move {
@@ -117,7 +121,8 @@ where
_ => Ok(ControlFlow::Continue(())),
}
}
})
},
)
.await
.context(format!("Polling for receipt failed for {tx_hash}"))
}
+87 -37
View File
@@ -66,45 +66,50 @@ impl ReportAggregator {
match event {
RunnerEvent::SubscribeToEvents(event) => {
self.handle_subscribe_to_events_event(*event);
},
RunnerEvent::CorpusFileDiscovery(event) =>
self.handle_corpus_file_discovered_event(*event),
}
RunnerEvent::CorpusFileDiscovery(event) => {
self.handle_corpus_file_discovered_event(*event)
}
RunnerEvent::MetadataFileDiscovery(event) => {
self.handle_metadata_file_discovery_event(*event);
},
}
RunnerEvent::TestCaseDiscovery(event) => {
self.handle_test_case_discovery(*event);
},
}
RunnerEvent::TestSucceeded(event) => {
self.handle_test_succeeded_event(*event);
},
}
RunnerEvent::TestFailed(event) => {
self.handle_test_failed_event(*event);
},
}
RunnerEvent::TestIgnored(event) => {
self.handle_test_ignored_event(*event);
},
}
RunnerEvent::NodeAssigned(event) => {
self.handle_node_assigned_event(*event);
},
RunnerEvent::PreLinkContractsCompilationSucceeded(event) =>
self.handle_pre_link_contracts_compilation_succeeded_event(*event),
RunnerEvent::PostLinkContractsCompilationSucceeded(event) =>
self.handle_post_link_contracts_compilation_succeeded_event(*event),
RunnerEvent::PreLinkContractsCompilationFailed(event) =>
self.handle_pre_link_contracts_compilation_failed_event(*event),
RunnerEvent::PostLinkContractsCompilationFailed(event) =>
self.handle_post_link_contracts_compilation_failed_event(*event),
}
RunnerEvent::PreLinkContractsCompilationSucceeded(event) => {
self.handle_pre_link_contracts_compilation_succeeded_event(*event)
}
RunnerEvent::PostLinkContractsCompilationSucceeded(event) => {
self.handle_post_link_contracts_compilation_succeeded_event(*event)
}
RunnerEvent::PreLinkContractsCompilationFailed(event) => {
self.handle_pre_link_contracts_compilation_failed_event(*event)
}
RunnerEvent::PostLinkContractsCompilationFailed(event) => {
self.handle_post_link_contracts_compilation_failed_event(*event)
}
RunnerEvent::LibrariesDeployed(event) => {
self.handle_libraries_deployed_event(*event);
},
}
RunnerEvent::ContractDeployed(event) => {
self.handle_contract_deployed_event(*event);
},
}
RunnerEvent::Completion(event) => {
self.handle_completion(*event);
break;
},
}
}
}
debug!("Report aggregation completed");
@@ -118,8 +123,12 @@ impl ReportAggregator {
file_name.push_str(".json");
file_name
};
let file_path =
self.report.context.working_directory_configuration().as_path().join(file_name);
let file_path = self
.report
.context
.working_directory_configuration()
.as_path()
.join(file_name);
let file = OpenOptions::new()
.create(true)
.write(true)
@@ -127,7 +136,10 @@ impl ReportAggregator {
.read(false)
.open(&file_path)
.with_context(|| {
format!("Failed to open report file for writing: {}", file_path.display())
format!(
"Failed to open report file for writing: {}",
file_path.display()
)
})?;
serde_json::to_writer_pretty(&file, &self.report).with_context(|| {
format!("Failed to serialize report JSON to {}", file_path.display())
@@ -168,8 +180,9 @@ impl ReportAggregator {
// Add information on the fact that the case was ignored to the report.
let test_case_report = self.test_case_report(&event.test_specifier);
test_case_report.status =
Some(TestCaseStatus::Succeeded { steps_executed: event.steps_executed });
test_case_report.status = Some(TestCaseStatus::Succeeded {
steps_executed: event.steps_executed,
});
self.handle_post_test_case_status_update(&event.test_specifier);
}
@@ -184,7 +197,9 @@ impl ReportAggregator {
// Add information on the fact that the case was ignored to the report.
let test_case_report = self.test_case_report(&event.test_specifier);
test_case_report.status = Some(TestCaseStatus::Failed { reason: event.reason });
test_case_report.status = Some(TestCaseStatus::Failed {
reason: event.reason,
});
self.handle_post_test_case_status_update(&event.test_specifier);
}
@@ -226,7 +241,10 @@ impl ReportAggregator {
.or_default()
.iter()
.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<_, _>>();
let event = ReporterEvent::MetadataFileSolcModeCombinationExecutionCompleted {
@@ -258,13 +276,29 @@ impl ReportAggregator {
&mut self,
event: PreLinkContractsCompilationSucceededEvent,
) {
let include_input = self.report.context.report_configuration().include_compiler_input;
let include_output = self.report.context.report_configuration().include_compiler_output;
let include_input = self
.report
.context
.report_configuration()
.include_compiler_input;
let include_output = self
.report
.context
.report_configuration()
.include_compiler_output;
let execution_information = self.execution_information(&event.execution_specifier);
let compiler_input = if include_input { event.compiler_input } else { None };
let compiler_output = if include_output { Some(event.compiler_output) } else { None };
let compiler_input = if include_input {
event.compiler_input
} else {
None
};
let compiler_output = if include_output {
Some(event.compiler_output)
} else {
None
};
execution_information.pre_link_compilation_status = Some(CompilationStatus::Success {
is_cached: event.is_cached,
@@ -279,13 +313,29 @@ impl ReportAggregator {
&mut self,
event: PostLinkContractsCompilationSucceededEvent,
) {
let include_input = self.report.context.report_configuration().include_compiler_input;
let include_output = self.report.context.report_configuration().include_compiler_output;
let include_input = self
.report
.context
.report_configuration()
.include_compiler_input;
let include_output = self
.report
.context
.report_configuration()
.include_compiler_output;
let execution_information = self.execution_information(&event.execution_specifier);
let compiler_input = if include_input { event.compiler_input } else { None };
let compiler_output = if include_output { Some(event.compiler_output) } else { None };
let compiler_input = if include_input {
event.compiler_input
} else {
None
};
let compiler_output = if include_output {
Some(event.compiler_output)
} else {
None
};
execution_information.post_link_compilation_status = Some(CompilationStatus::Success {
is_cached: event.is_cached,
@@ -325,8 +375,8 @@ impl ReportAggregator {
}
fn handle_libraries_deployed_event(&mut self, event: LibrariesDeployedEvent) {
self.execution_information(&event.execution_specifier).deployed_libraries =
Some(event.libraries);
self.execution_information(&event.execution_specifier)
.deployed_libraries = Some(event.libraries);
}
fn handle_contract_deployed_event(&mut self, event: ContractDeployedEvent) {
+24 -5
View File
@@ -34,11 +34,19 @@ pub(crate) async fn get_or_download(
}
create_dir_all(&target_directory).with_context(|| {
format!("Failed to create solc cache directory: {}", target_directory.display())
format!(
"Failed to create solc cache directory: {}",
target_directory.display()
)
})?;
download_to_file(&target_file, downloader)
.await
.with_context(|| format!("Failed to write downloaded solc to {}", target_file.display()))?;
.with_context(|| {
format!(
"Failed to write downloaded solc to {}",
target_file.display()
)
})?;
cache.insert(target_file.clone());
Ok((downloader.version.clone(), target_file))
@@ -62,7 +70,12 @@ async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> anyhow::R
}
let mut file = BufWriter::new(file);
file.write_all(&downloader.download().await.context("Failed to download solc binary bytes")?)
file.write_all(
&downloader
.download()
.await
.context("Failed to download solc binary bytes")?,
)
.with_context(|| format!("Failed to write solc binary to {}", path.display()))?;
file.flush()
.with_context(|| format!("Failed to flush file {}", path.display()))?;
@@ -78,11 +91,17 @@ async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> anyhow::R
.stdout(std::process::Stdio::null())
.spawn()
.with_context(|| {
format!("Failed to spawn xattr to remove quarantine attribute on {}", path.display())
format!(
"Failed to spawn xattr to remove quarantine attribute on {}",
path.display()
)
})?
.wait()
.with_context(|| {
format!("Failed waiting for xattr operation to complete on {}", path.display())
format!(
"Failed waiting for xattr operation to complete on {}",
path.display()
)
})?;
Ok(())
+52 -11
View File
@@ -67,7 +67,11 @@ impl SolcDownloader {
) -> anyhow::Result<Self> {
let version_or_requirement = version.into();
match version_or_requirement {
VersionOrRequirement::Version(version) => Ok(Self { version, target, list }),
VersionOrRequirement::Version(version) => Ok(Self {
version,
target,
list,
}),
VersionOrRequirement::Requirement(requirement) => {
let Some(version) = List::download(list)
.await
@@ -80,8 +84,12 @@ impl SolcDownloader {
else {
anyhow::bail!("Failed to find a version that satisfies {requirement:?}");
};
Ok(Self { version, target, list })
},
Ok(Self {
version,
target,
list,
})
}
}
}
@@ -122,7 +130,11 @@ impl SolcDownloader {
})?;
let path = build.path.clone();
let expected_digest = build.sha256.strip_prefix("0x").unwrap_or(&build.sha256).to_string();
let expected_digest = build
.sha256
.strip_prefix("0x")
.unwrap_or(&build.sha256)
.to_string();
let url = format!("{}/{}/{}", Self::BASE_URL, self.target, path.display());
let file = reqwest::get(&url)
@@ -147,25 +159,54 @@ mod tests {
#[tokio::test]
async fn try_get_windows() {
let version = List::download(List::WINDOWS_URL).await.unwrap().latest_release;
SolcDownloader::windows(version).await.unwrap().download().await.unwrap();
let version = List::download(List::WINDOWS_URL)
.await
.unwrap()
.latest_release;
SolcDownloader::windows(version)
.await
.unwrap()
.download()
.await
.unwrap();
}
#[tokio::test]
async fn try_get_macosx() {
let version = List::download(List::MACOSX_URL).await.unwrap().latest_release;
SolcDownloader::macosx(version).await.unwrap().download().await.unwrap();
let version = List::download(List::MACOSX_URL)
.await
.unwrap()
.latest_release;
SolcDownloader::macosx(version)
.await
.unwrap()
.download()
.await
.unwrap();
}
#[tokio::test]
async fn try_get_linux() {
let version = List::download(List::LINUX_URL).await.unwrap().latest_release;
SolcDownloader::linux(version).await.unwrap().download().await.unwrap();
let version = List::download(List::LINUX_URL)
.await
.unwrap()
.latest_release;
SolcDownloader::linux(version)
.await
.unwrap()
.download()
.await
.unwrap();
}
#[tokio::test]
async fn try_get_wasm() {
let version = List::download(List::WASM_URL).await.unwrap().latest_release;
SolcDownloader::wasm(version).await.unwrap().download().await.unwrap();
SolcDownloader::wasm(version)
.await
.unwrap()
.download()
.await
.unwrap();
}
}