Compare commits

..

1 Commits

Author SHA1 Message Date
Marios Christou 936676a92c Use absolute path for zombienet to avoid conflicts 2025-10-07 18:12:58 +03:00
18 changed files with 298 additions and 472 deletions
Generated
+2 -13
View File
@@ -782,15 +782,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.18" version = "0.6.18"
@@ -2337,7 +2328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
dependencies = [ dependencies = [
"data-encoding", "data-encoding",
"syn 2.0.101", "syn 1.0.109",
] ]
[[package]] [[package]]
@@ -5620,7 +5611,6 @@ name = "revive-dt-core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"alloy", "alloy",
"ansi_term",
"anyhow", "anyhow",
"bson", "bson",
"cacache", "cacache",
@@ -5668,7 +5658,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"alloy", "alloy",
"anyhow", "anyhow",
"async-stream",
"futures", "futures",
"revive-common", "revive-common",
"revive-dt-common", "revive-dt-common",
@@ -5953,7 +5942,7 @@ dependencies = [
"security-framework 3.3.0", "security-framework 3.3.0",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs 0.26.11", "webpki-root-certs 0.26.11",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
+1 -3
View File
@@ -22,9 +22,7 @@ revive-dt-node-pool = { version = "0.1.0", path = "crates/node-pool" }
revive-dt-report = { version = "0.1.0", path = "crates/report" } revive-dt-report = { version = "0.1.0", path = "crates/report" }
revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" } revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
ansi_term = "0.12.1"
anyhow = "1.0" anyhow = "1.0"
async-stream = { version = "0.3.6" }
bson = { version = "2.15.0" } bson = { version = "2.15.0" }
cacache = { version = "13.1.0" } cacache = { version = "13.1.0" }
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
@@ -75,7 +73,7 @@ revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev
revive-common = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" } revive-common = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" }
revive-differential = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" } revive-differential = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" }
zombienet-sdk = { git = "https://github.com/paritytech/zombienet-sdk.git", rev = "891f6554354ce466abd496366dbf8b4f82141241" } zombienet-sdk = { git = "https://github.com/paritytech/zombienet-sdk.git", rev ="891f6554354ce466abd496366dbf8b4f82141241" }
[workspace.dependencies.alloy] [workspace.dependencies.alloy]
version = "1.0.37" version = "1.0.37"
@@ -7,10 +7,7 @@ pragma solidity >=0.6.9;
import "./callable.sol"; import "./callable.sol";
contract Main { contract Main {
function main( function main(uint[1] calldata p1, Callable callable) public returns(uint) {
uint[1] calldata p1,
Callable callable
) public pure returns (uint) {
return callable.f(p1); return callable.f(p1);
} }
} }
+8 -46
View File
@@ -202,18 +202,6 @@ impl AsRef<ReportConfiguration> for Context {
#[derive(Clone, Debug, Parser, Serialize)] #[derive(Clone, Debug, Parser, Serialize)]
pub struct TestExecutionContext { pub struct TestExecutionContext {
/// The set of platforms that the differential tests should run on.
#[arg(
short = 'p',
long = "platform",
default_values = ["geth-evm-solc", "revive-dev-node-polkavm-resolc"]
)]
pub platforms: Vec<PlatformIdentifier>,
/// The output format to use for the tool's output.
#[arg(short, long, default_value_t = OutputFormat::CargoTestLike)]
pub output_format: OutputFormat,
/// The working directory that the program will use for all of the temporary artifacts needed at /// The working directory that the program will use for all of the temporary artifacts needed at
/// runtime. /// runtime.
/// ///
@@ -227,6 +215,14 @@ pub struct TestExecutionContext {
)] )]
pub working_directory: WorkingDirectoryConfiguration, pub working_directory: WorkingDirectoryConfiguration,
/// The set of platforms that the differential tests should run on.
#[arg(
short = 'p',
long = "platform",
default_values = ["geth-evm-solc", "revive-dev-node-polkavm-resolc"]
)]
pub platforms: Vec<PlatformIdentifier>,
/// Configuration parameters for the corpus files to use. /// Configuration parameters for the corpus files to use.
#[clap(flatten, next_help_heading = "Corpus Configuration")] #[clap(flatten, next_help_heading = "Corpus Configuration")]
pub corpus_configuration: CorpusConfiguration, pub corpus_configuration: CorpusConfiguration,
@@ -684,14 +680,6 @@ pub struct ReviveDevNodeConfiguration {
value_parser = parse_duration value_parser = parse_duration
)] )]
pub start_timeout_ms: Duration, pub start_timeout_ms: Duration,
/// The consensus to use for the spawned revive-dev-node.
#[clap(
id = "revive-dev-node.consensus",
long = "revive-dev-node.consensus",
default_value = "instant-seal"
)]
pub consensus: String,
} }
/// A set of configuration parameters for the ETH RPC. /// A set of configuration parameters for the ETH RPC.
@@ -962,29 +950,3 @@ pub enum TestingPlatform {
/// A polkadot/Substrate based network /// A polkadot/Substrate based network
Zombienet, Zombienet,
} }
/// The output format to use for the test execution output.
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
ValueEnum,
EnumString,
Display,
AsRefStr,
IntoStaticStr,
)]
#[strum(serialize_all = "kebab-case")]
pub enum OutputFormat {
/// The legacy format that was used in the past for the output.
Legacy,
/// An output format that looks heavily resembles the output from `cargo test`.
CargoTestLike,
}
-1
View File
@@ -21,7 +21,6 @@ revive-dt-node = { workspace = true }
revive-dt-node-interaction = { workspace = true } revive-dt-node-interaction = { workspace = true }
revive-dt-report = { workspace = true } revive-dt-report = { workspace = true }
ansi_term = { workspace = true }
alloy = { workspace = true } alloy = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
bson = { workspace = true } bson = { workspace = true }
@@ -22,7 +22,6 @@ use alloy::{
}, },
}; };
use anyhow::{Context as _, Result, bail}; use anyhow::{Context as _, Result, bail};
use futures::TryFutureExt;
use indexmap::IndexMap; use indexmap::IndexMap;
use revive_dt_common::{ use revive_dt_common::{
futures::{PollingWaitBehavior, poll}, futures::{PollingWaitBehavior, poll},
@@ -36,7 +35,7 @@ use revive_dt_format::{
}, },
traits::{ResolutionContext, ResolverApi}, traits::{ResolutionContext, ResolverApi},
}; };
use tokio::sync::{Mutex, OnceCell, mpsc::UnboundedSender}; use tokio::sync::{Mutex, mpsc::UnboundedSender};
use tracing::{Instrument, Span, debug, error, field::display, info, info_span, instrument}; use tracing::{Instrument, Span, debug, error, field::display, info, info_span, instrument};
use crate::{ use crate::{
@@ -124,7 +123,13 @@ where
&self.platform_information.reporter, &self.platform_information.reporter,
) )
.await .await
.inspect_err(|err| error!(?err, "Pre-linking compilation failed")) .inspect_err(|err| {
error!(
?err,
platform_identifier = %self.platform_information.platform.platform_identifier(),
"Pre-linking compilation failed"
)
})
.context("Failed to produce the pre-linking compiled contracts")?; .context("Failed to produce the pre-linking compiled contracts")?;
let mut deployed_libraries = None::<HashMap<_, _>>; let mut deployed_libraries = None::<HashMap<_, _>>;
@@ -132,7 +137,13 @@ where
.test_definition .test_definition
.metadata .metadata
.contract_sources() .contract_sources()
.inspect_err(|err| error!(?err, "Failed to retrieve contract sources from metadata")) .inspect_err(|err| {
error!(
?err,
platform_identifier = %self.platform_information.platform.platform_identifier(),
"Failed to retrieve contract sources from metadata"
)
})
.context("Failed to get the contract instances from the metadata file")?; .context("Failed to get the contract instances from the metadata file")?;
for library_instance in self for library_instance in self
.test_definition .test_definition
@@ -180,19 +191,20 @@ where
TransactionRequest::default().from(deployer_address), TransactionRequest::default().from(deployer_address),
code, code,
); );
let receipt = self let receipt = self.execute_transaction(tx).await.inspect_err(|err| {
.execute_transaction(tx) error!(
.and_then(|(_, receipt_fut)| receipt_fut) ?err,
.await %library_instance,
.inspect_err(|err| { platform_identifier = %self.platform_information.platform.platform_identifier(),
error!( "Failed to deploy the library"
?err, )
%library_instance, })?;
"Failed to deploy the library"
)
})?;
debug!(?library_instance, "Deployed library"); debug!(
?library_instance,
platform_identifier = %self.platform_information.platform.platform_identifier(),
"Deployed library"
);
let library_address = receipt let library_address = receipt
.contract_address .contract_address
@@ -215,7 +227,13 @@ where
&self.platform_information.reporter, &self.platform_information.reporter,
) )
.await .await
.inspect_err(|err| error!(?err, "Post-linking compilation failed")) .inspect_err(|err| {
error!(
?err,
platform_identifier = %self.platform_information.platform.platform_identifier(),
"Post-linking compilation failed"
)
})
.context("Failed to compile the post-link contracts")?; .context("Failed to compile the post-link contracts")?;
self.execution_state = ExecutionState::new( self.execution_state = ExecutionState::new(
@@ -251,6 +269,7 @@ where
skip_all, skip_all,
fields( fields(
driver_id = self.driver_id, driver_id = self.driver_id,
platform_identifier = %self.platform_information.platform.platform_identifier(),
%step_path, %step_path,
), ),
err(Debug), err(Debug),
@@ -286,11 +305,15 @@ where
.handle_function_call_contract_deployment(step) .handle_function_call_contract_deployment(step)
.await .await
.context("Failed to deploy contracts for the function call step")?; .context("Failed to deploy contracts for the function call step")?;
let transaction_hash = self let execution_receipt = self
.handle_function_call_execution(step, deployment_receipts) .handle_function_call_execution(step, deployment_receipts)
.await .await
.context("Failed to handle the function call execution")?; .context("Failed to handle the function call execution")?;
self.handle_function_call_variable_assignment(step, transaction_hash) let tracing_result = self
.handle_function_call_call_frame_tracing(execution_receipt.transaction_hash)
.await
.context("Failed to handle the function call call frame tracing")?;
self.handle_function_call_variable_assignment(step, &tracing_result)
.await .await
.context("Failed to handle function call variable assignment")?; .context("Failed to handle function call variable assignment")?;
Ok(1) Ok(1)
@@ -344,19 +367,18 @@ where
&mut self, &mut self,
step: &FunctionCallStep, step: &FunctionCallStep,
mut deployment_receipts: HashMap<ContractInstance, TransactionReceipt>, mut deployment_receipts: HashMap<ContractInstance, TransactionReceipt>,
) -> Result<TxHash> { ) -> Result<TransactionReceipt> {
match step.method { match step.method {
// This step was already executed when `handle_step` was called. We just need to // This step was already executed when `handle_step` was called. We just need to
// lookup the transaction receipt in this case and continue on. // lookup the transaction receipt in this case and continue on.
Method::Deployer => deployment_receipts Method::Deployer => deployment_receipts
.remove(&step.instance) .remove(&step.instance)
.context("Failed to find deployment receipt for constructor call") .context("Failed to find deployment receipt for constructor call"),
.map(|receipt| receipt.transaction_hash),
Method::Fallback | Method::FunctionName(_) => { Method::Fallback | Method::FunctionName(_) => {
let tx = step let tx = step
.as_transaction(self.resolver.as_ref(), self.default_resolution_context()) .as_transaction(self.resolver.as_ref(), self.default_resolution_context())
.await?; .await?;
Ok(self.execute_transaction(tx).await?.0) self.execute_transaction(tx).await
} }
} }
} }
@@ -395,19 +417,15 @@ where
async fn handle_function_call_variable_assignment( async fn handle_function_call_variable_assignment(
&mut self, &mut self,
step: &FunctionCallStep, step: &FunctionCallStep,
tx_hash: TxHash, tracing_result: &CallFrame,
) -> Result<()> { ) -> Result<()> {
let Some(ref assignments) = step.variable_assignments else { let Some(ref assignments) = step.variable_assignments else {
return Ok(()); return Ok(());
}; };
// Handling the return data variable assignments. // Handling the return data variable assignments.
let callframe = OnceCell::new();
for (variable_name, output_word) in assignments.return_data.iter().zip( for (variable_name, output_word) in assignments.return_data.iter().zip(
callframe tracing_result
.get_or_try_init(|| self.handle_function_call_call_frame_tracing(tx_hash))
.await
.context("Failed to get the callframe trace for transaction")?
.output .output
.as_ref() .as_ref()
.unwrap_or_default() .unwrap_or_default()
@@ -529,6 +547,7 @@ where
skip_all, skip_all,
fields( fields(
driver_id = self.driver_id, driver_id = self.driver_id,
platform_identifier = %self.platform_information.platform.platform_identifier(),
%contract_instance, %contract_instance,
%deployer %deployer
), ),
@@ -571,6 +590,7 @@ where
skip_all, skip_all,
fields( fields(
driver_id = self.driver_id, driver_id = self.driver_id,
platform_identifier = %self.platform_information.platform.platform_identifier(),
%contract_instance, %contract_instance,
%deployer %deployer
), ),
@@ -640,11 +660,7 @@ where
TransactionBuilder::<Ethereum>::with_deploy_code(tx, code) TransactionBuilder::<Ethereum>::with_deploy_code(tx, code)
}; };
let receipt = match self let receipt = match self.execute_transaction(tx).await {
.execute_transaction(tx)
.and_then(|(_, receipt_fut)| receipt_fut)
.await
{
Ok(receipt) => receipt, Ok(receipt) => receipt,
Err(error) => { Err(error) => {
tracing::error!(?error, "Contract deployment transaction failed."); tracing::error!(?error, "Contract deployment transaction failed.");
@@ -718,7 +734,7 @@ where
async fn execute_transaction( async fn execute_transaction(
&self, &self,
transaction: TransactionRequest, transaction: TransactionRequest,
) -> anyhow::Result<(TxHash, impl Future<Output = Result<TransactionReceipt>>)> { ) -> anyhow::Result<TransactionReceipt> {
let node = self.platform_information.node; let node = self.platform_information.node;
let transaction_hash = node let transaction_hash = node
.submit_transaction(transaction) .submit_transaction(transaction)
@@ -731,28 +747,24 @@ where
.send(WatcherEvent::SubmittedTransaction { transaction_hash }) .send(WatcherEvent::SubmittedTransaction { transaction_hash })
.context("Failed to send the transaction hash to the watcher")?; .context("Failed to send the transaction hash to the watcher")?;
Ok((transaction_hash, async move { info!("Starting to poll for transaction receipt");
info!("Starting to poll for transaction receipt"); poll(
poll( Duration::from_secs(30 * 60),
Duration::from_secs(30 * 60), PollingWaitBehavior::Constant(Duration::from_secs(1)),
PollingWaitBehavior::Constant(Duration::from_secs(1)), || {
|| { async move {
async move { match node.get_receipt(transaction_hash).await {
match node.get_receipt(transaction_hash).await { Ok(receipt) => {
Ok(receipt) => { info!("Polling succeeded, receipt found");
info!("Polling succeeded, receipt found"); Ok(ControlFlow::Break(receipt))
Ok(ControlFlow::Break(receipt))
}
Err(_) => Ok(ControlFlow::Continue(())),
} }
Err(_) => Ok(ControlFlow::Continue(())),
} }
.instrument(info_span!("Polling for receipt")) }
}, .instrument(info_span!("Polling for receipt"))
) },
.instrument(info_span!("Polling for receipt", %transaction_hash)) )
.await .await
.inspect(|_| info!("Found the transaction receipt"))
}))
} }
// endregion:Transaction Execution // endregion:Transaction Execution
} }
@@ -8,7 +8,7 @@ use revive_dt_common::types::PrivateKeyAllocator;
use revive_dt_core::Platform; use revive_dt_core::Platform;
use revive_dt_format::steps::{Step, StepIdx, StepPath}; use revive_dt_format::steps::{Step, StepIdx, StepPath};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::{Instrument, error, info, info_span, instrument, warn}; use tracing::{error, info, info_span, instrument, warn};
use revive_dt_config::{BenchmarkingContext, Context}; use revive_dt_config::{BenchmarkingContext, Context};
use revive_dt_report::Reporter; use revive_dt_report::Reporter;
@@ -159,15 +159,12 @@ pub async fn handle_differential_benchmarks(
futures::future::try_join( futures::future::try_join(
watcher.run(), watcher.run(),
driver driver.execute_all().inspect(|_| {
.execute_all() info!("All transactions submitted - driver completed execution");
.instrument(info_span!("Executing Benchmarks", %platform_identifier)) watcher_tx
.inspect(|_| { .send(WatcherEvent::AllTransactionsSubmitted)
info!("All transactions submitted - driver completed execution"); .unwrap()
watcher_tx }),
.send(WatcherEvent::AllTransactionsSubmitted)
.unwrap()
}),
) )
.await .await
.context("Failed to run the driver and executor") .context("Failed to run the driver and executor")
@@ -104,10 +104,6 @@ impl Watcher {
async move { async move {
let mut mined_blocks_information = Vec::new(); let mut mined_blocks_information = Vec::new();
// region:TEMPORARY
eprintln!("Watcher information for {}", self.platform_identifier);
eprintln!("block_number,block_timestamp,mined_gas,block_gas_limit,tx_count");
// endregion:TEMPORARY
while let Some(block) = blocks_information_stream.next().await { while let Some(block) = blocks_information_stream.next().await {
// If the block number is equal to or less than the last block before the // If the block number is equal to or less than the last block before the
// repetition then we ignore it and continue on to the next block. // repetition then we ignore it and continue on to the next block.
@@ -122,9 +118,8 @@ impl Watcher {
} }
info!( info!(
block_number = block.block_number,
block_tx_count = block.transaction_hashes.len(),
remaining_transactions = watch_for_transaction_hashes.read().await.len(), remaining_transactions = watch_for_transaction_hashes.read().await.len(),
block_tx_count = block.transaction_hashes.len(),
"Observed a block" "Observed a block"
); );
@@ -136,20 +131,6 @@ impl Watcher {
watch_for_transaction_hashes.remove(tx_hash); watch_for_transaction_hashes.remove(tx_hash);
} }
// region:TEMPORARY
// TODO: The following core is TEMPORARY and will be removed once we have proper
// reporting in place and then it can be removed. This serves as as way of doing
// some very simple reporting for the time being.
eprintln!(
"\"{}\",\"{}\",\"{}\",\"{}\",\"{}\"",
block.block_number,
block.block_timestamp,
block.mined_gas,
block.block_gas_limit,
block.transaction_hashes.len()
);
// endregion:TEMPORARY
mined_blocks_information.push(block); mined_blocks_information.push(block);
} }
@@ -158,10 +139,41 @@ impl Watcher {
} }
}; };
let (_, _) = let (_, mined_blocks_information) =
futures::future::join(watcher_event_watching_task, block_information_watching_task) futures::future::join(watcher_event_watching_task, block_information_watching_task)
.await; .await;
// region:TEMPORARY
{
// TODO: The following core is TEMPORARY and will be removed once we have proper
// reporting in place and then it can be removed. This serves as as way of doing some
// very simple reporting for the time being.
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"
)?;
for block in mined_blocks_information {
writeln!(
stderr,
"{},{},{},{},{}",
block.block_number,
block.block_timestamp,
block.mined_gas,
block.block_gas_limit,
block.transaction_hashes.len()
)?
}
}
// endregion:TEMPORARY
Ok(()) Ok(())
} }
} }
+56 -121
View File
@@ -7,7 +7,6 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use ansi_term::{ANSIStrings, Color};
use anyhow::Context as _; use anyhow::Context as _;
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use revive_dt_common::types::PrivateKeyAllocator; use revive_dt_common::types::PrivateKeyAllocator;
@@ -15,7 +14,7 @@ use revive_dt_core::Platform;
use tokio::sync::{Mutex, RwLock, Semaphore}; use tokio::sync::{Mutex, RwLock, Semaphore};
use tracing::{Instrument, error, info, info_span, instrument}; use tracing::{Instrument, error, info, info_span, instrument};
use revive_dt_config::{Context, OutputFormat, TestExecutionContext}; use revive_dt_config::{Context, TestExecutionContext};
use revive_dt_report::{Reporter, ReporterEvent, TestCaseStatus}; use revive_dt_report::{Reporter, ReporterEvent, TestCaseStatus};
use crate::{ use crate::{
@@ -177,7 +176,7 @@ pub async fn handle_differential_tests(
.report_completion_event() .report_completion_event()
.expect("Can't fail") .expect("Can't fail")
}); });
let cli_reporting_task = start_cli_reporting_task(context.output_format, reporter); let cli_reporting_task = start_cli_reporting_task(reporter);
tokio::task::spawn(async move { tokio::task::spawn(async move {
loop { loop {
@@ -197,15 +196,21 @@ pub async fn handle_differential_tests(
} }
#[allow(irrefutable_let_patterns, clippy::uninlined_format_args)] #[allow(irrefutable_let_patterns, clippy::uninlined_format_args)]
async fn start_cli_reporting_task(output_format: OutputFormat, reporter: Reporter) { async fn start_cli_reporting_task(reporter: Reporter) {
let mut aggregator_events_rx = reporter.subscribe().await.expect("Can't fail"); let mut aggregator_events_rx = reporter.subscribe().await.expect("Can't fail");
drop(reporter); drop(reporter);
let start = Instant::now(); let start = Instant::now();
let mut global_success_count = 0; const GREEN: &str = "\x1B[32m";
let mut global_failure_count = 0; const RED: &str = "\x1B[31m";
let mut global_ignore_count = 0; const GREY: &str = "\x1B[90m";
const COLOR_RESET: &str = "\x1B[0m";
const BOLD: &str = "\x1B[1m";
const BOLD_RESET: &str = "\x1B[22m";
let mut number_of_successes = 0;
let mut number_of_failures = 0;
let mut buf = BufWriter::new(stderr()); let mut buf = BufWriter::new(stderr());
while let Ok(event) = aggregator_events_rx.recv().await { while let Ok(event) = aggregator_events_rx.recv().await {
@@ -218,125 +223,55 @@ async fn start_cli_reporting_task(output_format: OutputFormat, reporter: Reporte
continue; continue;
}; };
match output_format { let _ = writeln!(buf, "{} - {}", mode, metadata_file_path.display());
OutputFormat::Legacy => { for (case_idx, case_status) in case_status.into_iter() {
let _ = writeln!(buf, "{} - {}", mode, metadata_file_path.display()); let _ = write!(buf, "\tCase Index {case_idx:>3}: ");
for (case_idx, case_status) in case_status.into_iter() { let _ = match case_status {
let _ = write!(buf, "\tCase Index {case_idx:>3}: "); TestCaseStatus::Succeeded { steps_executed } => {
let _ = match case_status { number_of_successes += 1;
TestCaseStatus::Succeeded { steps_executed } => { writeln!(
global_success_count += 1; buf,
writeln!( "{}{}Case Succeeded{} - Steps Executed: {}{}",
buf, GREEN, BOLD, BOLD_RESET, steps_executed, COLOR_RESET
"{}", )
ANSIStrings(&[
Color::Green.bold().paint("Case Succeeded"),
Color::Green
.paint(format!(" - Steps Executed: {steps_executed}")),
])
)
}
TestCaseStatus::Failed { reason } => {
global_failure_count += 1;
writeln!(
buf,
"{}",
ANSIStrings(&[
Color::Red.bold().paint("Case Failed"),
Color::Red.paint(format!(" - Reason: {}", reason.trim())),
])
)
}
TestCaseStatus::Ignored { reason, .. } => {
global_ignore_count += 1;
writeln!(
buf,
"{}",
ANSIStrings(&[
Color::Yellow.bold().paint("Case Ignored"),
Color::Yellow.paint(format!(" - Reason: {}", reason.trim())),
])
)
}
};
} }
let _ = writeln!(buf); TestCaseStatus::Failed { reason } => {
} number_of_failures += 1;
OutputFormat::CargoTestLike => { writeln!(
writeln!( buf,
buf, "{}{}Case Failed{} - Reason: {}{}",
"\t{} {} - {}\n", RED,
Color::Green.paint("Running"), BOLD,
metadata_file_path.display(), BOLD_RESET,
mode reason.trim(),
) COLOR_RESET,
.unwrap(); )
let mut success_count = 0;
let mut failure_count = 0;
let mut ignored_count = 0;
writeln!(buf, "running {} tests", case_status.len()).unwrap();
for (case_idx, case_result) in case_status.iter() {
let status = match case_result {
TestCaseStatus::Succeeded { .. } => {
success_count += 1;
global_success_count += 1;
Color::Green.paint("ok")
}
TestCaseStatus::Failed { reason } => {
failure_count += 1;
global_failure_count += 1;
Color::Red.paint(format!("FAILED, {reason}"))
}
TestCaseStatus::Ignored { reason, .. } => {
ignored_count += 1;
global_ignore_count += 1;
Color::Yellow.paint(format!("ignored, {reason:?}"))
}
};
writeln!(buf, "test case_idx_{} ... {}", case_idx, status).unwrap();
} }
writeln!(buf).unwrap(); TestCaseStatus::Ignored { reason, .. } => writeln!(
let status = if failure_count > 0 {
Color::Red.paint("FAILED")
} else {
Color::Green.paint("ok")
};
writeln!(
buf, buf,
"test result: {}. {} passed; {} failed; {} ignored", "{}{}Case Ignored{} - Reason: {}{}",
status, success_count, failure_count, ignored_count, GREY,
) BOLD,
.unwrap(); BOLD_RESET,
writeln!(buf).unwrap() reason.trim(),
} COLOR_RESET,
),
};
} }
let _ = writeln!(buf);
} }
// Summary at the end. // Summary at the end.
match output_format { let _ = writeln!(
OutputFormat::Legacy => { buf,
writeln!( "{} cases: {}{}{} cases succeeded, {}{}{} cases failed in {} seconds",
buf, number_of_successes + number_of_failures,
"{} cases: {} cases succeeded, {} cases failed in {} seconds", GREEN,
global_success_count + global_failure_count + global_ignore_count, number_of_successes,
Color::Green.paint(global_success_count.to_string()), COLOR_RESET,
Color::Red.paint(global_failure_count.to_string()), RED,
start.elapsed().as_secs() number_of_failures,
) COLOR_RESET,
.unwrap(); start.elapsed().as_secs()
} );
OutputFormat::CargoTestLike => {
writeln!(
buf,
"run finished. {} passed; {} failed; {} ignored; finished in {}s",
global_success_count,
global_failure_count,
global_ignore_count,
start.elapsed().as_secs()
)
.unwrap();
}
}
} }
+10 -18
View File
@@ -16,7 +16,7 @@ use revive_dt_config::*;
use revive_dt_node::{ use revive_dt_node::{
Node, node_implementations::geth::GethNode, Node, node_implementations::geth::GethNode,
node_implementations::lighthouse_geth::LighthouseGethNode, node_implementations::lighthouse_geth::LighthouseGethNode,
node_implementations::substrate::SubstrateNode, node_implementations::zombienet::ZombienetNode, node_implementations::substrate::SubstrateNode, node_implementations::zombienet::ZombieNode,
}; };
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use tracing::info; use tracing::info;
@@ -184,7 +184,6 @@ impl Platform for KitchensinkPolkavmResolcPlatform {
let node = SubstrateNode::new( let node = SubstrateNode::new(
kitchensink_path, kitchensink_path,
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND, SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
context, context,
); );
let node = spawn_node(node, genesis)?; let node = spawn_node(node, genesis)?;
@@ -237,7 +236,6 @@ impl Platform for KitchensinkRevmSolcPlatform {
let node = SubstrateNode::new( let node = SubstrateNode::new(
kitchensink_path, kitchensink_path,
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND, SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
context, context,
); );
let node = spawn_node(node, genesis)?; let node = spawn_node(node, genesis)?;
@@ -282,17 +280,14 @@ impl Platform for ReviveDevNodePolkavmResolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_configuration = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context); let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context)
.path
let revive_dev_node_path = revive_dev_node_configuration.path.clone(); .clone();
let revive_dev_node_consensus = revive_dev_node_configuration.consensus.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
revive_dev_node_path, revive_dev_node_path,
SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND, SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND,
Some(revive_dev_node_consensus),
context, context,
); );
let node = spawn_node(node, genesis)?; let node = spawn_node(node, genesis)?;
@@ -337,17 +332,14 @@ impl Platform for ReviveDevNodeRevmSolcPlatform {
context: Context, context: Context,
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> { ) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context); let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_configuration = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context); let revive_dev_node_path = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context)
.path
let revive_dev_node_path = revive_dev_node_configuration.path.clone(); .clone();
let revive_dev_node_consensus = revive_dev_node_configuration.consensus.clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = SubstrateNode::new( let node = SubstrateNode::new(
revive_dev_node_path, revive_dev_node_path,
SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND, SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND,
Some(revive_dev_node_consensus),
context, context,
); );
let node = spawn_node(node, genesis)?; let node = spawn_node(node, genesis)?;
@@ -397,7 +389,7 @@ impl Platform for ZombienetPolkavmResolcPlatform {
.clone(); .clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = ZombienetNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
let node = spawn_node(node, genesis)?; let node = spawn_node(node, genesis)?;
Ok(Box::new(node) as Box<_>) Ok(Box::new(node) as Box<_>)
})) }))
@@ -409,7 +401,7 @@ impl Platform for ZombienetPolkavmResolcPlatform {
version: Option<VersionOrRequirement>, version: Option<VersionOrRequirement>,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn SolidityCompiler>>>>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn SolidityCompiler>>>>> {
Box::pin(async move { Box::pin(async move {
let compiler = Resolc::new(context, version).await; let compiler = Solc::new(context, version).await;
compiler.map(|compiler| Box::new(compiler) as Box<dyn SolidityCompiler>) compiler.map(|compiler| Box::new(compiler) as Box<dyn SolidityCompiler>)
}) })
} }
@@ -445,7 +437,7 @@ impl Platform for ZombienetRevmSolcPlatform {
.clone(); .clone();
let genesis = genesis_configuration.genesis()?.clone(); let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || { Ok(thread::spawn(move || {
let node = ZombienetNode::new(polkadot_parachain_path, context); let node = ZombieNode::new(polkadot_parachain_path, context);
let node = spawn_node(node, genesis)?; let node = spawn_node(node, genesis)?;
Ok(Box::new(node) as Box<_>) Ok(Box::new(node) as Box<_>)
})) }))
-1
View File
@@ -11,7 +11,6 @@ rust-version.workspace = true
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
alloy = { workspace = true } alloy = { workspace = true }
async-stream = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tower = { workspace = true } tower = { workspace = true }
@@ -1131,7 +1131,6 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
#[ignore = "Ignored since they take a long time to run"]
async fn node_mines_simple_transfer_transaction_and_returns_receipt() { async fn node_mines_simple_transfer_transaction_and_returns_receipt() {
// Arrange // Arrange
let (context, node) = new_node(); let (context, node) = new_node();
@@ -36,8 +36,7 @@ use alloy::{
}, },
}; };
use anyhow::Context as _; use anyhow::Context as _;
use async_stream::stream; use futures::{Stream, StreamExt};
use futures::Stream;
use revive_common::EVMVersion; use revive_common::EVMVersion;
use revive_dt_common::fs::clear_directory; use revive_dt_common::fs::clear_directory;
use revive_dt_format::traits::ResolverApi; use revive_dt_format::traits::ResolverApi;
@@ -81,7 +80,6 @@ pub struct SubstrateNode {
wallet: Arc<EthereumWallet>, wallet: Arc<EthereumWallet>,
nonce_manager: CachedNonceManager, nonce_manager: CachedNonceManager,
provider: OnceCell<ConcreteProvider<ReviveNetwork, Arc<EthereumWallet>>>, provider: OnceCell<ConcreteProvider<ReviveNetwork, Arc<EthereumWallet>>>,
consensus: Option<String>,
} }
impl SubstrateNode { impl SubstrateNode {
@@ -104,7 +102,6 @@ impl SubstrateNode {
pub fn new( pub fn new(
node_path: PathBuf, node_path: PathBuf,
export_chainspec_command: &str, export_chainspec_command: &str,
consensus: Option<String>,
context: impl AsRef<WorkingDirectoryConfiguration> context: impl AsRef<WorkingDirectoryConfiguration>
+ AsRef<EthRpcConfiguration> + AsRef<EthRpcConfiguration>
+ AsRef<WalletConfiguration>, + AsRef<WalletConfiguration>,
@@ -134,7 +131,6 @@ impl SubstrateNode {
wallet: wallet.clone(), wallet: wallet.clone(),
nonce_manager: Default::default(), nonce_manager: Default::default(),
provider: Default::default(), provider: Default::default(),
consensus,
} }
} }
@@ -232,7 +228,7 @@ impl SubstrateNode {
self.logs_directory.as_path(), self.logs_directory.as_path(),
self.node_binary.as_path(), self.node_binary.as_path(),
|command, stdout_file, stderr_file| { |command, stdout_file, stderr_file| {
let cmd = command command
.arg("--dev") .arg("--dev")
.arg("--chain") .arg("--chain")
.arg(chainspec_path) .arg(chainspec_path)
@@ -249,16 +245,9 @@ impl SubstrateNode {
.arg("all") .arg("all")
.arg("--rpc-max-connections") .arg("--rpc-max-connections")
.arg(u32::MAX.to_string()) .arg(u32::MAX.to_string())
.arg("--pool-limit")
.arg(u32::MAX.to_string())
.arg("--pool-kbytes")
.arg(u32::MAX.to_string())
.env("RUST_LOG", Self::SUBSTRATE_LOG_ENV) .env("RUST_LOG", Self::SUBSTRATE_LOG_ENV)
.stdout(stdout_file) .stdout(stdout_file)
.stderr(stderr_file); .stderr(stderr_file);
if let Some(consensus) = self.consensus.as_ref() {
cmd.arg("--consensus").arg(consensus.clone());
}
}, },
ProcessReadinessWaitBehavior::TimeBoundedWaitFunction { ProcessReadinessWaitBehavior::TimeBoundedWaitFunction {
max_wait_duration: Duration::from_secs(30), max_wait_duration: Duration::from_secs(30),
@@ -519,46 +508,37 @@ impl EthereumNode for SubstrateNode {
+ '_, + '_,
>, >,
> { > {
fn create_stream(
provider: ConcreteProvider<ReviveNetwork, Arc<EthereumWallet>>,
) -> impl Stream<Item = MinedBlockInformation> {
stream! {
let mut block_number = provider.get_block_number().await.expect("Failed to get the block number");
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
let Ok(Some(block)) = provider.get_block_by_number(BlockNumberOrTag::Number(block_number)).await
else {
continue;
};
block_number += 1;
yield MinedBlockInformation {
block_number: block.number(),
block_timestamp: block.header.timestamp,
mined_gas: block.header.gas_used as _,
block_gas_limit: block.header.gas_limit,
transaction_hashes: block
.transactions
.into_hashes()
.as_hashes()
.expect("Must be hashes")
.to_vec(),
};
};
}
}
Box::pin(async move { Box::pin(async move {
let provider = self let provider = self
.provider() .provider()
.await .await
.context("Failed to create the provider for a block subscription")?; .context("Failed to create the provider for block subscription")?;
let mut block_subscription = provider
.watch_full_blocks()
.await
.context("Failed to create the blocks stream")?;
block_subscription.set_channel_size(0xFFFF);
block_subscription.set_poll_interval(Duration::from_secs(1));
let block_stream = block_subscription.into_stream();
let stream = Box::pin(create_stream(provider)) let mined_block_information_stream = block_stream.filter_map(|block| async {
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>; let block = block.ok()?;
Some(MinedBlockInformation {
block_number: block.number(),
block_timestamp: block.header.timestamp,
mined_gas: block.header.gas_used as _,
block_gas_limit: block.header.gas_limit,
transaction_hashes: block
.transactions
.into_hashes()
.as_hashes()
.expect("Must be hashes")
.to_vec(),
})
});
Ok(stream) Ok(Box::pin(mined_block_information_stream)
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
}) })
} }
} }
@@ -1190,7 +1170,6 @@ mod tests {
let mut node = SubstrateNode::new( let mut node = SubstrateNode::new(
context.kitchensink_configuration.path.clone(), context.kitchensink_configuration.path.clone(),
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND, SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
&context, &context,
); );
node.init(context.genesis_configuration.genesis().unwrap().clone()) node.init(context.genesis_configuration.genesis().unwrap().clone())
@@ -1256,7 +1235,6 @@ mod tests {
let mut dummy_node = SubstrateNode::new( let mut dummy_node = SubstrateNode::new(
context.kitchensink_configuration.path.clone(), context.kitchensink_configuration.path.clone(),
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND, SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
&context, &context,
); );
@@ -1309,7 +1287,6 @@ mod tests {
let node = SubstrateNode::new( let node = SubstrateNode::new(
context.kitchensink_configuration.path.clone(), context.kitchensink_configuration.path.clone(),
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND, SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
&context, &context,
); );
+73 -101
View File
@@ -55,8 +55,7 @@ use alloy::{
}; };
use anyhow::Context as _; use anyhow::Context as _;
use async_stream::stream; use futures::{Stream, StreamExt};
use futures::Stream;
use revive_common::EVMVersion; use revive_common::EVMVersion;
use revive_dt_common::fs::clear_directory; use revive_dt_common::fs::clear_directory;
use revive_dt_config::*; use revive_dt_config::*;
@@ -74,19 +73,16 @@ use crate::{
constants::INITIAL_BALANCE, constants::INITIAL_BALANCE,
helpers::{Process, ProcessReadinessWaitBehavior}, helpers::{Process, ProcessReadinessWaitBehavior},
node_implementations::substrate::ReviveNetwork, node_implementations::substrate::ReviveNetwork,
provider_utils::{ provider_utils::{ConcreteProvider, FallbackGasFiller, construct_concurrency_limited_provider},
ConcreteProvider, FallbackGasFiller, construct_concurrency_limited_provider,
execute_transaction,
},
}; };
static NODE_COUNT: AtomicU32 = AtomicU32::new(0); static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
/// A Zombienet network where collator is `polkadot-parachain` node with `eth-rpc` [`ZombieNode`] /// A Zombienet network where collator is `polkadot-parachain` node with `eth-rpc`
/// abstracts away the details of managing the zombienet network and provides an interface to /// [`ZombieNode`] abstracts away the details of managing the zombienet network and provides
/// interact with the parachain's Ethereum RPC. /// an interface to interact with the parachain's Ethereum RPC.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ZombienetNode { pub struct ZombieNode {
/* Node Identifier */ /* Node Identifier */
id: u32, id: u32,
connection_string: String, connection_string: String,
@@ -114,7 +110,7 @@ pub struct ZombienetNode {
provider: OnceCell<ConcreteProvider<ReviveNetwork, Arc<EthereumWallet>>>, provider: OnceCell<ConcreteProvider<ReviveNetwork, Arc<EthereumWallet>>>,
} }
impl ZombienetNode { impl ZombieNode {
const BASE_DIRECTORY: &str = "zombienet"; const BASE_DIRECTORY: &str = "zombienet";
const DATA_DIRECTORY: &str = "data"; const DATA_DIRECTORY: &str = "data";
const LOGS_DIRECTORY: &str = "logs"; const LOGS_DIRECTORY: &str = "logs";
@@ -123,8 +119,6 @@ impl ZombienetNode {
const PARACHAIN_ID: u32 = 100; const PARACHAIN_ID: u32 = 100;
const ETH_RPC_BASE_PORT: u16 = 8545; const ETH_RPC_BASE_PORT: u16 = 8545;
const PROXY_LOG_ENV: &str = "info,eth-rpc=debug";
const ETH_RPC_READY_MARKER: &str = "Running JSON-RPC server"; const ETH_RPC_READY_MARKER: &str = "Running JSON-RPC server";
const EXPORT_CHAINSPEC_COMMAND: &str = "build-spec"; const EXPORT_CHAINSPEC_COMMAND: &str = "build-spec";
@@ -184,35 +178,25 @@ impl ZombienetNode {
let node_rpc_port = Self::NODE_BASE_RPC_PORT + self.id as u16; let node_rpc_port = Self::NODE_BASE_RPC_PORT + self.id as u16;
let network_config = NetworkConfigBuilder::new() let network_config = NetworkConfigBuilder::new()
.with_relaychain(|relay_chain| { .with_relaychain(|r| {
relay_chain r.with_chain("westend-local")
.with_chain("westend-local")
.with_default_command("polkadot") .with_default_command("polkadot")
.with_node(|node| node.with_name("alice")) .with_node(|node| node.with_name("alice"))
.with_node(|node| node.with_name("bob")) .with_node(|node| node.with_name("bob"))
}) })
.with_global_settings(|global_settings| { .with_global_settings(|g| g.with_base_dir(&self.base_directory))
// global_settings.with_base_dir(&self.base_directory) .with_parachain(|p| {
global_settings p.with_id(Self::PARACHAIN_ID)
}) .with_chain_spec_path(template_chainspec_path.to_str().unwrap())
.with_parachain(|parachain| {
parachain
.with_id(Self::PARACHAIN_ID)
.with_chain_spec_path(template_chainspec_path.to_path_buf())
.with_chain("asset-hub-westend-local") .with_chain("asset-hub-westend-local")
.with_collator(|node_config| { .with_collator(|n| {
node_config n.with_name("Collator")
.with_name("Collator")
.with_command(polkadot_parachain_path) .with_command(polkadot_parachain_path)
.with_rpc_port(node_rpc_port) .with_rpc_port(node_rpc_port)
.with_args(vec![
("--pool-limit", u32::MAX.to_string().as_str()).into(),
("--pool-kbytes", u32::MAX.to_string().as_str()).into(),
])
}) })
}) })
.build() .build()
.map_err(|err| anyhow::anyhow!("Failed to build zombienet network config: {err:?}"))?; .map_err(|e| anyhow::anyhow!("Failed to build zombienet network config: {e:?}"))?;
self.node_rpc_port = Some(node_rpc_port); self.node_rpc_port = Some(node_rpc_port);
self.network_config = Some(network_config); self.network_config = Some(network_config);
@@ -226,9 +210,6 @@ impl ZombienetNode {
.clone() .clone()
.context("Node not initialized, call init() first")?; .context("Node not initialized, call init() first")?;
// TODO: Look into the possibility of removing this in the future, perhaps by reintroducing
// the blocking runtime abstraction and making it available to the entire program so that we
// don't need to be spawning multiple different runtimes.
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
let network = rt.block_on(async { let network = rt.block_on(async {
network_config network_config
@@ -256,7 +237,6 @@ impl ZombienetNode {
.arg(u32::MAX.to_string()) .arg(u32::MAX.to_string())
.arg("--rpc-port") .arg("--rpc-port")
.arg(eth_rpc_port.to_string()) .arg(eth_rpc_port.to_string())
.env("RUST_LOG", Self::PROXY_LOG_ENV)
.stdout(stdout_file) .stdout(stdout_file)
.stderr(stderr_file); .stderr(stderr_file);
}, },
@@ -292,13 +272,12 @@ impl ZombienetNode {
template_chainspec_path: PathBuf, template_chainspec_path: PathBuf,
mut genesis: Genesis, mut genesis: Genesis,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let output = Command::new(self.polkadot_parachain_path.as_path()) let mut cmd: Command = std::process::Command::new(&self.polkadot_parachain_path);
.arg(Self::EXPORT_CHAINSPEC_COMMAND) cmd.arg(Self::EXPORT_CHAINSPEC_COMMAND)
.arg("--chain") .arg("--chain")
.arg("asset-hub-westend-local") .arg("asset-hub-westend-local");
.env_remove("RUST_LOG")
.output() let output = cmd.output().context("Failed to export the chain-spec")?;
.context("Failed to export the chainspec of the chain")?;
if !output.status.success() { if !output.status.success() {
anyhow::bail!( anyhow::bail!(
@@ -406,7 +385,7 @@ impl ZombienetNode {
.get_or_try_init(|| async move { .get_or_try_init(|| async move {
construct_concurrency_limited_provider::<ReviveNetwork, _>( construct_concurrency_limited_provider::<ReviveNetwork, _>(
self.connection_string.as_str(), self.connection_string.as_str(),
FallbackGasFiller::new(u64::MAX, 5_000_000_000, 1_000_000_000), FallbackGasFiller::new(250_000_000, 5_000_000_000, 1_000_000_000),
ChainIdFiller::default(), // TODO: use CHAIN_ID constant ChainIdFiller::default(), // TODO: use CHAIN_ID constant
NonceFiller::new(self.nonce_manager.clone()), NonceFiller::new(self.nonce_manager.clone()),
self.wallet.clone(), self.wallet.clone(),
@@ -419,7 +398,7 @@ impl ZombienetNode {
} }
} }
impl EthereumNode for ZombienetNode { impl EthereumNode for ZombieNode {
fn pre_transactions(&mut self) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + '_>> { fn pre_transactions(&mut self) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + '_>> {
Box::pin(async move { Ok(()) }) Box::pin(async move { Ok(()) })
} }
@@ -469,11 +448,17 @@ impl EthereumNode for ZombienetNode {
transaction: alloy::rpc::types::TransactionRequest, transaction: alloy::rpc::types::TransactionRequest,
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> { ) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
Box::pin(async move { Box::pin(async move {
let provider = self let receipt = self
.provider() .provider()
.await .await
.context("Failed to create the provider")?; .context("Failed to create provider for transaction submission")?
execute_transaction(provider, transaction).await .send_transaction(transaction)
.await
.context("Failed to submit transaction to proxy")?
.get_receipt()
.await
.context("Failed to fetch transaction receipt from proxy")?;
Ok(receipt)
}) })
} }
@@ -567,46 +552,37 @@ impl EthereumNode for ZombienetNode {
+ '_, + '_,
>, >,
> { > {
fn create_stream(
provider: ConcreteProvider<ReviveNetwork, Arc<EthereumWallet>>,
) -> impl Stream<Item = MinedBlockInformation> {
stream! {
let mut block_number = provider.get_block_number().await.expect("Failed to get the block number");
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
let Ok(Some(block)) = provider.get_block_by_number(BlockNumberOrTag::Number(block_number)).await
else {
continue;
};
block_number += 1;
yield MinedBlockInformation {
block_number: block.number(),
block_timestamp: block.header.timestamp,
mined_gas: block.header.gas_used as _,
block_gas_limit: block.header.gas_limit,
transaction_hashes: block
.transactions
.into_hashes()
.as_hashes()
.expect("Must be hashes")
.to_vec(),
};
};
}
}
Box::pin(async move { Box::pin(async move {
let provider = self let provider = self
.provider() .provider()
.await .await
.context("Failed to create the provider for a block subscription")?; .context("Failed to create the provider for block subscription")?;
let mut block_subscription = provider
.watch_full_blocks()
.await
.context("Failed to create the blocks stream")?;
block_subscription.set_channel_size(0xFFFF);
block_subscription.set_poll_interval(Duration::from_secs(1));
let block_stream = block_subscription.into_stream();
let stream = Box::pin(create_stream(provider)) let mined_block_information_stream = block_stream.filter_map(|block| async {
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>; let block = block.ok()?;
Some(MinedBlockInformation {
block_number: block.number(),
block_timestamp: block.header.timestamp,
mined_gas: block.header.gas_used as _,
block_gas_limit: block.header.gas_limit,
transaction_hashes: block
.transactions
.into_hashes()
.as_hashes()
.expect("Must be hashes")
.to_vec(),
})
});
Ok(stream) Ok(Box::pin(mined_block_information_stream)
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
}) })
} }
} }
@@ -741,7 +717,7 @@ impl<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> ResolverApi
} }
} }
impl Node for ZombienetNode { impl Node for ZombieNode {
fn shutdown(&mut self) -> anyhow::Result<()> { fn shutdown(&mut self) -> anyhow::Result<()> {
// Kill the eth_rpc process // Kill the eth_rpc process
drop(self.eth_rpc_process.take()); drop(self.eth_rpc_process.take());
@@ -786,7 +762,7 @@ impl Node for ZombienetNode {
} }
} }
impl Drop for ZombienetNode { impl Drop for ZombieNode {
fn drop(&mut self) { fn drop(&mut self) {
let _ = self.shutdown(); let _ = self.shutdown();
} }
@@ -810,9 +786,9 @@ mod tests {
TestExecutionContext::default() TestExecutionContext::default()
} }
pub async fn new_node() -> (TestExecutionContext, ZombienetNode) { pub async fn new_node() -> (TestExecutionContext, ZombieNode) {
let context = test_config(); let context = test_config();
let mut node = ZombienetNode::new( let mut node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(), context.polkadot_parachain_configuration.path.clone(),
&context, &context,
); );
@@ -830,9 +806,8 @@ mod tests {
(context, node) (context, node)
} }
pub async fn shared_state() -> &'static (TestExecutionContext, Arc<ZombienetNode>) { pub async fn shared_state() -> &'static (TestExecutionContext, Arc<ZombieNode>) {
static NODE: OnceCell<(TestExecutionContext, Arc<ZombienetNode>)> = static NODE: OnceCell<(TestExecutionContext, Arc<ZombieNode>)> = OnceCell::const_new();
OnceCell::const_new();
NODE.get_or_init(|| async { NODE.get_or_init(|| async {
let (context, node) = new_node().await; let (context, node) = new_node().await;
@@ -841,14 +816,13 @@ mod tests {
.await .await
} }
pub async fn shared_node() -> &'static Arc<ZombienetNode> { pub async fn shared_node() -> &'static Arc<ZombieNode> {
&shared_state().await.1 &shared_state().await.1
} }
} }
use utils::{new_node, test_config}; use utils::{new_node, test_config};
#[tokio::test] #[tokio::test]
#[ignore = "Ignored for the time being"]
async fn test_transfer_transaction_should_return_receipt() { async fn test_transfer_transaction_should_return_receipt() {
let (ctx, node) = new_node().await; let (ctx, node) = new_node().await;
@@ -882,7 +856,7 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let mut node = ZombienetNode::new( let mut node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(), context.polkadot_parachain_configuration.path.clone(),
&context, &context,
); );
@@ -892,19 +866,17 @@ mod tests {
.expect("init failed"); .expect("init failed");
// Check that the patched chainspec file was generated // Check that the patched chainspec file was generated
let final_chainspec_path = node let final_chainspec_path = node.base_directory.join(ZombieNode::CHAIN_SPEC_JSON_FILE);
.base_directory
.join(ZombienetNode::CHAIN_SPEC_JSON_FILE);
assert!(final_chainspec_path.exists(), "Chainspec file should exist"); assert!(final_chainspec_path.exists(), "Chainspec file should exist");
let contents = let contents =
std::fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec"); std::fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec");
// Validate that the Polkadot addresses derived from the Ethereum addresses are in the file // Validate that the Polkadot addresses derived from the Ethereum addresses are in the file
let first_eth_addr = ZombienetNode::eth_to_polkadot_address( let first_eth_addr = ZombieNode::eth_to_polkadot_address(
&"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap(), &"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap(),
); );
let second_eth_addr = ZombienetNode::eth_to_polkadot_address( let second_eth_addr = ZombieNode::eth_to_polkadot_address(
&"Ab8483F64d9C6d1EcF9b849Ae677dD3315835cb2".parse().unwrap(), &"Ab8483F64d9C6d1EcF9b849Ae677dD3315835cb2".parse().unwrap(),
); );
@@ -932,7 +904,7 @@ mod tests {
"#; "#;
let context = test_config(); let context = test_config();
let node = ZombienetNode::new( let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(), context.polkadot_parachain_configuration.path.clone(),
&context, &context,
); );
@@ -968,7 +940,7 @@ mod tests {
]; ];
for eth_addr in eth_addresses { for eth_addr in eth_addresses {
let ss58 = ZombienetNode::eth_to_polkadot_address(&eth_addr.parse().unwrap()); let ss58 = ZombieNode::eth_to_polkadot_address(&eth_addr.parse().unwrap());
println!("Ethereum: {eth_addr} -> Polkadot SS58: {ss58}"); println!("Ethereum: {eth_addr} -> Polkadot SS58: {ss58}");
} }
@@ -996,7 +968,7 @@ mod tests {
]; ];
for (eth_addr, expected_ss58) in cases { for (eth_addr, expected_ss58) in cases {
let result = ZombienetNode::eth_to_polkadot_address(&eth_addr.parse().unwrap()); let result = ZombieNode::eth_to_polkadot_address(&eth_addr.parse().unwrap());
assert_eq!( assert_eq!(
result, expected_ss58, result, expected_ss58,
"Mismatch for Ethereum address {eth_addr}" "Mismatch for Ethereum address {eth_addr}"
@@ -1008,7 +980,7 @@ mod tests {
fn eth_rpc_version_works() { fn eth_rpc_version_works() {
// Arrange // Arrange
let context = test_config(); let context = test_config();
let node = ZombienetNode::new( let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(), context.polkadot_parachain_configuration.path.clone(),
&context, &context,
); );
@@ -1027,7 +999,7 @@ mod tests {
fn version_works() { fn version_works() {
// Arrange // Arrange
let context = test_config(); let context = test_config();
let node = ZombienetNode::new( let node = ZombieNode::new(
context.polkadot_parachain_configuration.path.clone(), context.polkadot_parachain_configuration.path.clone(),
&context, &context,
); );
@@ -7,10 +7,6 @@ use alloy::{
transports::TransportResult, transports::TransportResult,
}; };
// Percentage padding applied to estimated gas (e.g. 120 = 20% padding)
const GAS_ESTIMATE_PADDING_NUMERATOR: u64 = 120;
const GAS_ESTIMATE_PADDING_DENOMINATOR: u64 = 100;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FallbackGasFiller { pub struct FallbackGasFiller {
inner: GasFiller, inner: GasFiller,
@@ -60,6 +56,8 @@ where
provider: &P, provider: &P,
tx: &<N as Network>::TransactionRequest, tx: &<N as Network>::TransactionRequest,
) -> TransportResult<Self::Fillable> { ) -> TransportResult<Self::Fillable> {
// Try to fetch GasFillers “fillable” (gas_price, base_fee, estimate_gas, …)
// If it errors (i.e. tx would revert under eth_estimateGas), swallow it.
match self.inner.prepare(provider, tx).await { match self.inner.prepare(provider, tx).await {
Ok(fill) => Ok(Some(fill)), Ok(fill) => Ok(Some(fill)),
Err(_) => Ok(None), Err(_) => Ok(None),
@@ -72,17 +70,8 @@ where
mut tx: alloy::providers::SendableTx<N>, mut tx: alloy::providers::SendableTx<N>,
) -> TransportResult<SendableTx<N>> { ) -> TransportResult<SendableTx<N>> {
if let Some(fill) = fillable { if let Some(fill) = fillable {
let mut tx = self.inner.fill(fill, tx).await?; // our inner GasFiller succeeded — use it
if let Some(builder) = tx.as_mut_builder() { self.inner.fill(fill, tx).await
if let Some(estimated) = builder.gas_limit() {
let padded = estimated
.checked_mul(GAS_ESTIMATE_PADDING_NUMERATOR)
.and_then(|v| v.checked_div(GAS_ESTIMATE_PADDING_DENOMINATOR))
.unwrap_or(u64::MAX);
builder.set_gas_limit(padded);
}
}
Ok(tx)
} else { } else {
if let Some(builder) = tx.as_mut_builder() { if let Some(builder) = tx.as_mut_builder() {
builder.set_gas_limit(self.default_gas_limit); builder.set_gas_limit(self.default_gas_limit);
+2 -2
View File
@@ -1,7 +1,7 @@
mod concurrency_limiter; mod concurrency_limiter;
mod fallback_gas_filler; mod fallback_gas_provider;
mod provider; mod provider;
pub use concurrency_limiter::*; pub use concurrency_limiter::*;
pub use fallback_gas_filler::*; pub use fallback_gas_provider::*;
pub use provider::*; pub use provider::*;
+3 -7
View File
@@ -10,7 +10,7 @@ use alloy::{
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use revive_dt_common::futures::{PollingWaitBehavior, poll}; use revive_dt_common::futures::{PollingWaitBehavior, poll};
use tracing::{Instrument, debug, info, info_span}; use tracing::debug;
use crate::provider_utils::{ConcurrencyLimiterLayer, FallbackGasFiller}; use crate::provider_utils::{ConcurrencyLimiterLayer, FallbackGasFiller};
@@ -44,7 +44,7 @@ where
// requests at any point of time and no more than that. This is done in an effort to stabilize // requests at any point of time and no more than that. This is done in an effort to stabilize
// the framework from some of the interment issues that we've been seeing related to RPC calls. // the framework from some of the interment issues that we've been seeing related to RPC calls.
static GLOBAL_CONCURRENCY_LIMITER_LAYER: LazyLock<ConcurrencyLimiterLayer> = static GLOBAL_CONCURRENCY_LIMITER_LAYER: LazyLock<ConcurrencyLimiterLayer> =
LazyLock::new(|| ConcurrencyLimiterLayer::new(500)); LazyLock::new(|| ConcurrencyLimiterLayer::new(10));
let client = ClientBuilder::default() let client = ClientBuilder::default()
.layer(GLOBAL_CONCURRENCY_LIMITER_LAYER.clone()) .layer(GLOBAL_CONCURRENCY_LIMITER_LAYER.clone())
@@ -117,16 +117,12 @@ where
async move { async move {
match provider.get_transaction_receipt(tx_hash).await { match provider.get_transaction_receipt(tx_hash).await {
Ok(Some(receipt)) => { Ok(Some(receipt)) => Ok(ControlFlow::Break(receipt)),
info!("Found the transaction receipt");
Ok(ControlFlow::Break(receipt))
}
_ => Ok(ControlFlow::Continue(())), _ => Ok(ControlFlow::Continue(())),
} }
} }
}, },
) )
.instrument(info_span!("Polling for receipt", %tx_hash))
.await .await
.context(format!("Polling for receipt failed for {tx_hash}")) .context(format!("Polling for receipt failed for {tx_hash}"))
} }
+4 -3
View File
@@ -93,16 +93,17 @@ echo ""
# Run the tool # Run the tool
cargo build --release; cargo build --release;
RUST_LOG="info,alloy_pubsub::service=error" ./target/release/retester test \ RUST_LOG="info,alloy_pubsub::service=error" ./target/release/retester test \
--platform geth-evm-solc \ --platform revive-dev-node-revm-solc \
--corpus "$CORPUS_FILE" \ --corpus "$CORPUS_FILE" \
--working-directory "$WORKDIR" \ --working-directory "$WORKDIR" \
--concurrency.number-of-nodes 10 \ --concurrency.number-of-nodes 10 \
--concurrency.number-of-threads 5 \ --concurrency.number-of-threads 5 \
--concurrency.ignore-concurrency-limit \ --concurrency.number-of-concurrent-tasks 1000 \
--wallet.additional-keys 100000 \ --wallet.additional-keys 100000 \
--kitchensink.path "$SUBSTRATE_NODE_BIN" \ --kitchensink.path "$SUBSTRATE_NODE_BIN" \
--revive-dev-node.path "$REVIVE_DEV_NODE_BIN" \ --revive-dev-node.path "$REVIVE_DEV_NODE_BIN" \
--eth-rpc.path "$ETH_RPC_BIN" \ --eth-rpc.path "$ETH_RPC_BIN" \
> logs.log > logs.log \
2> output.log
echo -e "${GREEN}=== Test run completed! ===${NC}" echo -e "${GREEN}=== Test run completed! ===${NC}"