mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-22 17:17:58 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 565b3803b7 | |||
| b5de071594 | |||
| 21e25f09e6 | |||
| 18ee331529 | |||
| 37a689a73e | |||
| 8c412dc924 | |||
| 6da3172581 | |||
| c6eb04b04e | |||
| e5114d31dc |
@@ -15,6 +15,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
POLKADOT_VERSION: polkadot-stable2506-2
|
||||
|
||||
jobs:
|
||||
cache-polkadot:
|
||||
@@ -65,6 +66,37 @@ jobs:
|
||||
run: |
|
||||
cd polkadot-sdk
|
||||
cargo install --path substrate/frame/revive/rpc --bin eth-rpc
|
||||
|
||||
- name: Cache downloaded Polkadot binaries
|
||||
id: cache-polkadot
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/polkadot-cache/polkadot
|
||||
~/polkadot-cache/polkadot-execute-worker
|
||||
~/polkadot-cache/polkadot-prepare-worker
|
||||
~/polkadot-cache/polkadot-parachain
|
||||
key: polkadot-downloaded-${{ matrix.os }}-${{ env.POLKADOT_VERSION }}
|
||||
|
||||
- name: Download Polkadot binaries on macOS
|
||||
if: matrix.os == 'macos-14' && steps.cache-polkadot.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p ~/polkadot-cache
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot-aarch64-apple-darwin -o ~/polkadot-cache/polkadot
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot-execute-worker-aarch64-apple-darwin -o ~/polkadot-cache/polkadot-execute-worker
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot-prepare-worker-aarch64-apple-darwin -o ~/polkadot-cache/polkadot-prepare-worker
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot-parachain-aarch64-apple-darwin -o ~/polkadot-cache/polkadot-parachain
|
||||
chmod +x ~/polkadot-cache/*
|
||||
|
||||
- name: Download Polkadot binaries on Ubuntu
|
||||
if: matrix.os == 'ubuntu-24.04' && steps.cache-polkadot.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p ~/polkadot-cache
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot -o ~/polkadot-cache/polkadot
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot-execute-worker -o ~/polkadot-cache/polkadot-execute-worker
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot-prepare-worker -o ~/polkadot-cache/polkadot-prepare-worker
|
||||
curl -sL https://github.com/paritytech/polkadot-sdk/releases/download/${{ env.POLKADOT_VERSION }}/polkadot-parachain -o ~/polkadot-cache/polkadot-parachain
|
||||
chmod +x ~/polkadot-cache/*
|
||||
|
||||
ci:
|
||||
name: CI on ${{ matrix.os }}
|
||||
@@ -86,6 +118,24 @@ jobs:
|
||||
~/.cargo/bin/eth-rpc
|
||||
key: polkadot-binaries-${{ matrix.os }}-${{ hashFiles('polkadot-sdk/.git') }}
|
||||
|
||||
- name: Restore downloaded Polkadot binaries from cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/polkadot-cache/polkadot
|
||||
~/polkadot-cache/polkadot-execute-worker
|
||||
~/polkadot-cache/polkadot-prepare-worker
|
||||
~/polkadot-cache/polkadot-parachain
|
||||
key: polkadot-downloaded-${{ matrix.os }}-${{ env.POLKADOT_VERSION }}
|
||||
|
||||
- name: Install Polkadot binaries
|
||||
run: |
|
||||
sudo cp ~/polkadot-cache/polkadot /usr/local/bin/
|
||||
sudo cp ~/polkadot-cache/polkadot-execute-worker /usr/local/bin/
|
||||
sudo cp ~/polkadot-cache/polkadot-prepare-worker /usr/local/bin/
|
||||
sudo cp ~/polkadot-cache/polkadot-parachain /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/polkadot*
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
@@ -170,6 +220,18 @@ jobs:
|
||||
- name: Check resolc version
|
||||
run: resolc --version
|
||||
|
||||
- name: Check polkadot version
|
||||
run: polkadot --version
|
||||
|
||||
- name: Check polkadot-parachain version
|
||||
run: polkadot-parachain --version
|
||||
|
||||
- name: Check polkadot-execute-worker version
|
||||
run: polkadot-execute-worker --version
|
||||
|
||||
- name: Check polkadot-prepare-worker version
|
||||
run: polkadot-prepare-worker --version
|
||||
|
||||
- name: Test Formatting
|
||||
run: make format
|
||||
|
||||
|
||||
Generated
+2137
-57
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,9 @@ revive-dt-node-pool = { version = "0.1.0", path = "crates/node-pool" }
|
||||
revive-dt-report = { version = "0.1.0", path = "crates/report" }
|
||||
revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
|
||||
|
||||
ansi_term = "0.12.1"
|
||||
anyhow = "1.0"
|
||||
async-stream = { version = "0.3.6" }
|
||||
bson = { version = "2.15.0" }
|
||||
cacache = { version = "13.1.0" }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
@@ -73,6 +75,8 @@ revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev
|
||||
revive-common = { 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" }
|
||||
|
||||
[workspace.dependencies.alloy]
|
||||
version = "1.0.37"
|
||||
default-features = false
|
||||
|
||||
@@ -39,6 +39,10 @@ pub enum PlatformIdentifier {
|
||||
ReviveDevNodePolkavmResolc,
|
||||
/// The revive dev node with the REVM backend with the solc compiler.
|
||||
ReviveDevNodeRevmSolc,
|
||||
/// A zombienet based Substrate/Polkadot node with the PolkaVM backend with the resolc compiler.
|
||||
ZombienetPolkavmResolc,
|
||||
/// A zombienet based Substrate/Polkadot node with the REVM backend with the solc compiler.
|
||||
ZombienetRevmSolc,
|
||||
}
|
||||
|
||||
/// An enum of the platform identifiers of all of the platforms supported by this framework.
|
||||
@@ -95,6 +99,8 @@ pub enum NodeIdentifier {
|
||||
Kitchensink,
|
||||
/// The revive dev node implementation.
|
||||
ReviveDevNode,
|
||||
/// A zombienet spawned nodes
|
||||
Zombienet,
|
||||
}
|
||||
|
||||
/// An enum representing the identifiers of the supported VMs.
|
||||
|
||||
@@ -7,7 +7,10 @@ pragma solidity >=0.6.9;
|
||||
import "./callable.sol";
|
||||
|
||||
contract Main {
|
||||
function main(uint[1] calldata p1, Callable callable) public returns(uint) {
|
||||
function main(
|
||||
uint[1] calldata p1,
|
||||
Callable callable
|
||||
) public pure returns (uint) {
|
||||
return callable.f(p1);
|
||||
}
|
||||
}
|
||||
|
||||
+102
-8
@@ -107,6 +107,16 @@ impl AsRef<KurtosisConfiguration> for Context {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PolkadotParachainConfiguration> for Context {
|
||||
fn as_ref(&self) -> &PolkadotParachainConfiguration {
|
||||
match self {
|
||||
Self::Test(context) => context.as_ref().as_ref(),
|
||||
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||
Self::ExportJsonSchema => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<KitchensinkConfiguration> for Context {
|
||||
fn as_ref(&self) -> &KitchensinkConfiguration {
|
||||
match self {
|
||||
@@ -192,6 +202,18 @@ impl AsRef<ReportConfiguration> for Context {
|
||||
|
||||
#[derive(Clone, Debug, Parser, Serialize)]
|
||||
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
|
||||
/// runtime.
|
||||
///
|
||||
@@ -205,14 +227,6 @@ pub struct TestExecutionContext {
|
||||
)]
|
||||
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.
|
||||
#[clap(flatten, next_help_heading = "Corpus Configuration")]
|
||||
pub corpus_configuration: CorpusConfiguration,
|
||||
@@ -225,6 +239,10 @@ pub struct TestExecutionContext {
|
||||
#[clap(flatten, next_help_heading = "Resolc Configuration")]
|
||||
pub resolc_configuration: ResolcConfiguration,
|
||||
|
||||
/// Configuration parameters for the Polkadot Parachain.
|
||||
#[clap(flatten, next_help_heading = "Polkadot Parachain Configuration")]
|
||||
pub polkadot_parachain_configuration: PolkadotParachainConfiguration,
|
||||
|
||||
/// Configuration parameters for the geth node.
|
||||
#[clap(flatten, next_help_heading = "Geth Configuration")]
|
||||
pub geth_configuration: GethConfiguration,
|
||||
@@ -318,6 +336,10 @@ pub struct BenchmarkingContext {
|
||||
#[clap(flatten, next_help_heading = "Kitchensink Configuration")]
|
||||
pub kitchensink_configuration: KitchensinkConfiguration,
|
||||
|
||||
/// Configuration parameters for the Polkadot Parachain.
|
||||
#[clap(flatten, next_help_heading = "Polkadot Parachain Configuration")]
|
||||
pub polkadot_parachain_configuration: PolkadotParachainConfiguration,
|
||||
|
||||
/// Configuration parameters for the Revive Dev Node.
|
||||
#[clap(flatten, next_help_heading = "Revive Dev Node Configuration")]
|
||||
pub revive_dev_node_configuration: ReviveDevNodeConfiguration,
|
||||
@@ -379,6 +401,12 @@ impl AsRef<GethConfiguration> for TestExecutionContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PolkadotParachainConfiguration> for TestExecutionContext {
|
||||
fn as_ref(&self) -> &PolkadotParachainConfiguration {
|
||||
&self.polkadot_parachain_configuration
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<KurtosisConfiguration> for TestExecutionContext {
|
||||
fn as_ref(&self) -> &KurtosisConfiguration {
|
||||
&self.lighthouse_configuration
|
||||
@@ -475,6 +503,12 @@ impl AsRef<KurtosisConfiguration> for BenchmarkingContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PolkadotParachainConfiguration> for BenchmarkingContext {
|
||||
fn as_ref(&self) -> &PolkadotParachainConfiguration {
|
||||
&self.polkadot_parachain_configuration
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<KitchensinkConfiguration> for BenchmarkingContext {
|
||||
fn as_ref(&self) -> &KitchensinkConfiguration {
|
||||
&self.kitchensink_configuration
|
||||
@@ -545,6 +579,30 @@ pub struct ResolcConfiguration {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
/// A set of configuration parameters for Polkadot Parachain.
|
||||
#[derive(Clone, Debug, Parser, Serialize)]
|
||||
pub struct PolkadotParachainConfiguration {
|
||||
/// Specifies the path of the polkadot-parachain node to be used by the tool.
|
||||
///
|
||||
/// If this is not specified, then the tool assumes that it should use the polkadot-parachain binary
|
||||
/// that's provided in the user's $PATH.
|
||||
#[clap(
|
||||
id = "polkadot-parachain.path",
|
||||
long = "polkadot-parachain.path",
|
||||
default_value = "polkadot-parachain"
|
||||
)]
|
||||
pub path: PathBuf,
|
||||
|
||||
/// The amount of time to wait upon startup before considering that the node timed out.
|
||||
#[clap(
|
||||
id = "polkadot-parachain.start-timeout-ms",
|
||||
long = "polkadot-parachain.start-timeout-ms",
|
||||
default_value = "5000",
|
||||
value_parser = parse_duration
|
||||
)]
|
||||
pub start_timeout_ms: Duration,
|
||||
}
|
||||
|
||||
/// A set of configuration parameters for Geth.
|
||||
#[derive(Clone, Debug, Parser, Serialize)]
|
||||
pub struct GethConfiguration {
|
||||
@@ -626,6 +684,14 @@ pub struct ReviveDevNodeConfiguration {
|
||||
value_parser = parse_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.
|
||||
@@ -893,4 +959,32 @@ pub enum TestingPlatform {
|
||||
Geth,
|
||||
/// The kitchensink runtime provides the PolkaVM (PVM) based node implementation.
|
||||
Kitchensink,
|
||||
/// A polkadot/Substrate based network
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ revive-dt-node = { workspace = true }
|
||||
revive-dt-node-interaction = { workspace = true }
|
||||
revive-dt-report = { workspace = true }
|
||||
|
||||
ansi_term = { workspace = true }
|
||||
alloy = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
bson = { workspace = true }
|
||||
|
||||
@@ -22,6 +22,7 @@ use alloy::{
|
||||
},
|
||||
};
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use futures::TryFutureExt;
|
||||
use indexmap::IndexMap;
|
||||
use revive_dt_common::{
|
||||
futures::{PollingWaitBehavior, poll},
|
||||
@@ -35,7 +36,7 @@ use revive_dt_format::{
|
||||
},
|
||||
traits::{ResolutionContext, ResolverApi},
|
||||
};
|
||||
use tokio::sync::{Mutex, mpsc::UnboundedSender};
|
||||
use tokio::sync::{Mutex, OnceCell, mpsc::UnboundedSender};
|
||||
use tracing::{Instrument, Span, debug, error, field::display, info, info_span, instrument};
|
||||
|
||||
use crate::{
|
||||
@@ -123,13 +124,7 @@ where
|
||||
&self.platform_information.reporter,
|
||||
)
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
error!(
|
||||
?err,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
"Pre-linking compilation failed"
|
||||
)
|
||||
})
|
||||
.inspect_err(|err| error!(?err, "Pre-linking compilation failed"))
|
||||
.context("Failed to produce the pre-linking compiled contracts")?;
|
||||
|
||||
let mut deployed_libraries = None::<HashMap<_, _>>;
|
||||
@@ -137,13 +132,7 @@ where
|
||||
.test_definition
|
||||
.metadata
|
||||
.contract_sources()
|
||||
.inspect_err(|err| {
|
||||
error!(
|
||||
?err,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
"Failed to retrieve contract sources from metadata"
|
||||
)
|
||||
})
|
||||
.inspect_err(|err| error!(?err, "Failed to retrieve contract sources from metadata"))
|
||||
.context("Failed to get the contract instances from the metadata file")?;
|
||||
for library_instance in self
|
||||
.test_definition
|
||||
@@ -191,20 +180,19 @@ where
|
||||
TransactionRequest::default().from(deployer_address),
|
||||
code,
|
||||
);
|
||||
let receipt = self.execute_transaction(tx).await.inspect_err(|err| {
|
||||
error!(
|
||||
?err,
|
||||
%library_instance,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
"Failed to deploy the library"
|
||||
)
|
||||
})?;
|
||||
let receipt = self
|
||||
.execute_transaction(tx)
|
||||
.and_then(|(_, receipt_fut)| receipt_fut)
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
error!(
|
||||
?err,
|
||||
%library_instance,
|
||||
"Failed to deploy the library"
|
||||
)
|
||||
})?;
|
||||
|
||||
debug!(
|
||||
?library_instance,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
"Deployed library"
|
||||
);
|
||||
debug!(?library_instance, "Deployed library");
|
||||
|
||||
let library_address = receipt
|
||||
.contract_address
|
||||
@@ -227,13 +215,7 @@ where
|
||||
&self.platform_information.reporter,
|
||||
)
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
error!(
|
||||
?err,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
"Post-linking compilation failed"
|
||||
)
|
||||
})
|
||||
.inspect_err(|err| error!(?err, "Post-linking compilation failed"))
|
||||
.context("Failed to compile the post-link contracts")?;
|
||||
|
||||
self.execution_state = ExecutionState::new(
|
||||
@@ -269,7 +251,6 @@ where
|
||||
skip_all,
|
||||
fields(
|
||||
driver_id = self.driver_id,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
%step_path,
|
||||
),
|
||||
err(Debug),
|
||||
@@ -305,15 +286,11 @@ where
|
||||
.handle_function_call_contract_deployment(step)
|
||||
.await
|
||||
.context("Failed to deploy contracts for the function call step")?;
|
||||
let execution_receipt = self
|
||||
let transaction_hash = self
|
||||
.handle_function_call_execution(step, deployment_receipts)
|
||||
.await
|
||||
.context("Failed to handle the function call execution")?;
|
||||
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)
|
||||
self.handle_function_call_variable_assignment(step, transaction_hash)
|
||||
.await
|
||||
.context("Failed to handle function call variable assignment")?;
|
||||
Ok(1)
|
||||
@@ -367,18 +344,19 @@ where
|
||||
&mut self,
|
||||
step: &FunctionCallStep,
|
||||
mut deployment_receipts: HashMap<ContractInstance, TransactionReceipt>,
|
||||
) -> Result<TransactionReceipt> {
|
||||
) -> Result<TxHash> {
|
||||
match step.method {
|
||||
// This step was already executed when `handle_step` was called. We just need to
|
||||
// lookup the transaction receipt in this case and continue on.
|
||||
Method::Deployer => deployment_receipts
|
||||
.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(_) => {
|
||||
let tx = step
|
||||
.as_transaction(self.resolver.as_ref(), self.default_resolution_context())
|
||||
.await?;
|
||||
self.execute_transaction(tx).await
|
||||
Ok(self.execute_transaction(tx).await?.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -417,15 +395,19 @@ where
|
||||
async fn handle_function_call_variable_assignment(
|
||||
&mut self,
|
||||
step: &FunctionCallStep,
|
||||
tracing_result: &CallFrame,
|
||||
tx_hash: TxHash,
|
||||
) -> Result<()> {
|
||||
let Some(ref assignments) = step.variable_assignments else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Handling the return data variable assignments.
|
||||
let callframe = OnceCell::new();
|
||||
for (variable_name, output_word) in assignments.return_data.iter().zip(
|
||||
tracing_result
|
||||
callframe
|
||||
.get_or_try_init(|| self.handle_function_call_call_frame_tracing(tx_hash))
|
||||
.await
|
||||
.context("Failed to get the callframe trace for transaction")?
|
||||
.output
|
||||
.as_ref()
|
||||
.unwrap_or_default()
|
||||
@@ -547,7 +529,6 @@ where
|
||||
skip_all,
|
||||
fields(
|
||||
driver_id = self.driver_id,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
%contract_instance,
|
||||
%deployer
|
||||
),
|
||||
@@ -590,7 +571,6 @@ where
|
||||
skip_all,
|
||||
fields(
|
||||
driver_id = self.driver_id,
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
%contract_instance,
|
||||
%deployer
|
||||
),
|
||||
@@ -660,7 +640,11 @@ where
|
||||
TransactionBuilder::<Ethereum>::with_deploy_code(tx, code)
|
||||
};
|
||||
|
||||
let receipt = match self.execute_transaction(tx).await {
|
||||
let receipt = match self
|
||||
.execute_transaction(tx)
|
||||
.and_then(|(_, receipt_fut)| receipt_fut)
|
||||
.await
|
||||
{
|
||||
Ok(receipt) => receipt,
|
||||
Err(error) => {
|
||||
tracing::error!(?error, "Contract deployment transaction failed.");
|
||||
@@ -734,7 +718,7 @@ where
|
||||
async fn execute_transaction(
|
||||
&self,
|
||||
transaction: TransactionRequest,
|
||||
) -> anyhow::Result<TransactionReceipt> {
|
||||
) -> anyhow::Result<(TxHash, impl Future<Output = Result<TransactionReceipt>>)> {
|
||||
let node = self.platform_information.node;
|
||||
let transaction_hash = node
|
||||
.submit_transaction(transaction)
|
||||
@@ -747,24 +731,28 @@ where
|
||||
.send(WatcherEvent::SubmittedTransaction { transaction_hash })
|
||||
.context("Failed to send the transaction hash to the watcher")?;
|
||||
|
||||
info!("Starting to poll for transaction receipt");
|
||||
poll(
|
||||
Duration::from_secs(30 * 60),
|
||||
PollingWaitBehavior::Constant(Duration::from_secs(1)),
|
||||
|| {
|
||||
async move {
|
||||
match node.get_receipt(transaction_hash).await {
|
||||
Ok(receipt) => {
|
||||
info!("Polling succeeded, receipt found");
|
||||
Ok(ControlFlow::Break(receipt))
|
||||
Ok((transaction_hash, async move {
|
||||
info!("Starting to poll for transaction receipt");
|
||||
poll(
|
||||
Duration::from_secs(30 * 60),
|
||||
PollingWaitBehavior::Constant(Duration::from_secs(1)),
|
||||
|| {
|
||||
async move {
|
||||
match node.get_receipt(transaction_hash).await {
|
||||
Ok(receipt) => {
|
||||
info!("Polling succeeded, receipt found");
|
||||
Ok(ControlFlow::Break(receipt))
|
||||
}
|
||||
Err(_) => Ok(ControlFlow::Continue(())),
|
||||
}
|
||||
Err(_) => Ok(ControlFlow::Continue(())),
|
||||
}
|
||||
}
|
||||
.instrument(info_span!("Polling for receipt"))
|
||||
},
|
||||
)
|
||||
.await
|
||||
.instrument(info_span!("Polling for receipt"))
|
||||
},
|
||||
)
|
||||
.instrument(info_span!("Polling for receipt", %transaction_hash))
|
||||
.await
|
||||
.inspect(|_| info!("Found the transaction receipt"))
|
||||
}))
|
||||
}
|
||||
// endregion:Transaction Execution
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use revive_dt_common::types::PrivateKeyAllocator;
|
||||
use revive_dt_core::Platform;
|
||||
use revive_dt_format::steps::{Step, StepIdx, StepPath};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{error, info, info_span, instrument, warn};
|
||||
use tracing::{Instrument, error, info, info_span, instrument, warn};
|
||||
|
||||
use revive_dt_config::{BenchmarkingContext, Context};
|
||||
use revive_dt_report::Reporter;
|
||||
@@ -159,12 +159,15 @@ pub async fn handle_differential_benchmarks(
|
||||
|
||||
futures::future::try_join(
|
||||
watcher.run(),
|
||||
driver.execute_all().inspect(|_| {
|
||||
info!("All transactions submitted - driver completed execution");
|
||||
watcher_tx
|
||||
.send(WatcherEvent::AllTransactionsSubmitted)
|
||||
.unwrap()
|
||||
}),
|
||||
driver
|
||||
.execute_all()
|
||||
.instrument(info_span!("Executing Benchmarks", %platform_identifier))
|
||||
.inspect(|_| {
|
||||
info!("All transactions submitted - driver completed execution");
|
||||
watcher_tx
|
||||
.send(WatcherEvent::AllTransactionsSubmitted)
|
||||
.unwrap()
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.context("Failed to run the driver and executor")
|
||||
|
||||
@@ -104,6 +104,10 @@ impl Watcher {
|
||||
async move {
|
||||
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 {
|
||||
// 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.
|
||||
@@ -118,8 +122,9 @@ impl Watcher {
|
||||
}
|
||||
|
||||
info!(
|
||||
remaining_transactions = watch_for_transaction_hashes.read().await.len(),
|
||||
block_number = block.block_number,
|
||||
block_tx_count = block.transaction_hashes.len(),
|
||||
remaining_transactions = watch_for_transaction_hashes.read().await.len(),
|
||||
"Observed a block"
|
||||
);
|
||||
|
||||
@@ -131,6 +136,20 @@ impl Watcher {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -139,41 +158,10 @@ impl Watcher {
|
||||
}
|
||||
};
|
||||
|
||||
let (_, mined_blocks_information) =
|
||||
let (_, _) =
|
||||
futures::future::join(watcher_event_watching_task, block_information_watching_task)
|
||||
.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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ use revive_dt_format::{
|
||||
traits::ResolutionContext,
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug, error, info, instrument};
|
||||
use tracing::{error, info, instrument};
|
||||
|
||||
use crate::{
|
||||
differential_tests::ExecutionState,
|
||||
@@ -109,7 +109,6 @@ impl<'a> Driver<'a, StepsIterator> {
|
||||
// endregion:Constructors
|
||||
|
||||
// region:Execution
|
||||
#[instrument(level = "info", skip_all)]
|
||||
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(
|
||||
@@ -218,8 +217,6 @@ where
|
||||
.flatten()
|
||||
.flat_map(|(_, map)| map.values())
|
||||
{
|
||||
debug!(%library_instance, "Deploying Library Instance");
|
||||
|
||||
let ContractPathAndIdent {
|
||||
contract_source_path: library_source_path,
|
||||
contract_ident: library_ident,
|
||||
@@ -268,12 +265,6 @@ where
|
||||
)
|
||||
})?;
|
||||
|
||||
debug!(
|
||||
?library_instance,
|
||||
platform_identifier = %platform_information.platform.platform_identifier(),
|
||||
"Deployed library"
|
||||
);
|
||||
|
||||
let library_address = receipt
|
||||
.contract_address
|
||||
.expect("Failed to deploy the library");
|
||||
@@ -312,7 +303,6 @@ where
|
||||
// endregion:Constructors & Initialization
|
||||
|
||||
// region:Step Handling
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub async fn execute_all(mut self) -> Result<usize> {
|
||||
while let Some(result) = self.execute_next_step().await {
|
||||
result?
|
||||
@@ -320,14 +310,6 @@ where
|
||||
Ok(self.steps_executed)
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
level = "info",
|
||||
skip_all,
|
||||
fields(
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
node_id = self.platform_information.node.id(),
|
||||
),
|
||||
)]
|
||||
pub async fn execute_next_step(&mut self) -> Option<Result<()>> {
|
||||
let (step_path, step) = self.steps_iterator.next()?;
|
||||
info!(%step_path, "Executing Step");
|
||||
@@ -344,6 +326,7 @@ where
|
||||
skip_all,
|
||||
fields(
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
node_id = self.platform_information.node.id(),
|
||||
%step_path,
|
||||
),
|
||||
err(Debug),
|
||||
@@ -402,6 +385,7 @@ where
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn handle_function_call_contract_deployment(
|
||||
&mut self,
|
||||
step: &FunctionCallStep,
|
||||
@@ -447,6 +431,7 @@ where
|
||||
Ok(receipts)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn handle_function_call_execution(
|
||||
&mut self,
|
||||
step: &FunctionCallStep,
|
||||
@@ -470,14 +455,12 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
match self.platform_information.node.execute_transaction(tx).await {
|
||||
Ok(receipt) => Ok(receipt),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
self.platform_information.node.execute_transaction(tx).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn handle_function_call_call_frame_tracing(
|
||||
&mut self,
|
||||
tx_hash: TxHash,
|
||||
@@ -509,6 +492,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn handle_function_call_variable_assignment(
|
||||
&mut self,
|
||||
step: &FunctionCallStep,
|
||||
@@ -541,6 +525,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn handle_function_call_assertions(
|
||||
&mut self,
|
||||
step: &FunctionCallStep,
|
||||
@@ -583,6 +568,7 @@ where
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn handle_function_call_assertion_item(
|
||||
&self,
|
||||
receipt: &TransactionReceipt,
|
||||
@@ -865,7 +851,6 @@ where
|
||||
level = "info",
|
||||
skip_all,
|
||||
fields(
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
%contract_instance,
|
||||
%deployer
|
||||
),
|
||||
@@ -907,7 +892,6 @@ where
|
||||
level = "info",
|
||||
skip_all,
|
||||
fields(
|
||||
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||
%contract_instance,
|
||||
%deployer
|
||||
),
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
//! The main entry point into differential testing.
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
io::{BufWriter, Write, stderr},
|
||||
sync::Arc,
|
||||
time::Instant,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use ansi_term::{ANSIStrings, Color};
|
||||
use anyhow::Context as _;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use revive_dt_common::types::PrivateKeyAllocator;
|
||||
use revive_dt_core::Platform;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::{Mutex, RwLock, Semaphore};
|
||||
use tracing::{Instrument, error, info, info_span, instrument};
|
||||
|
||||
use revive_dt_config::{Context, TestExecutionContext};
|
||||
use revive_dt_config::{Context, OutputFormat, TestExecutionContext};
|
||||
use revive_dt_report::{Reporter, ReporterEvent, TestCaseStatus};
|
||||
|
||||
use crate::{
|
||||
@@ -101,20 +102,40 @@ pub async fn handle_differential_tests(
|
||||
)));
|
||||
|
||||
// Creating the driver and executing all of the steps.
|
||||
let driver_task = futures::future::join_all(test_definitions.iter().map(|test_definition| {
|
||||
let private_key_allocator = private_key_allocator.clone();
|
||||
let cached_compiler = cached_compiler.clone();
|
||||
let mode = test_definition.mode.clone();
|
||||
let span = info_span!(
|
||||
"Executing Test Case",
|
||||
metadata_file_path = %test_definition.metadata_file_path.display(),
|
||||
case_idx = %test_definition.case_idx,
|
||||
mode = %mode
|
||||
);
|
||||
async move {
|
||||
let driver =
|
||||
match Driver::new_root(test_definition, private_key_allocator, &cached_compiler)
|
||||
.await
|
||||
let semaphore = context
|
||||
.concurrency_configuration
|
||||
.concurrency_limit()
|
||||
.map(Semaphore::new)
|
||||
.map(Arc::new);
|
||||
let running_task_list = Arc::new(RwLock::new(BTreeSet::<usize>::new()));
|
||||
let driver_task = futures::future::join_all(test_definitions.iter().enumerate().map(
|
||||
|(test_id, test_definition)| {
|
||||
let running_task_list = running_task_list.clone();
|
||||
let semaphore = semaphore.clone();
|
||||
|
||||
let private_key_allocator = private_key_allocator.clone();
|
||||
let cached_compiler = cached_compiler.clone();
|
||||
let mode = test_definition.mode.clone();
|
||||
let span = info_span!(
|
||||
"Executing Test Case",
|
||||
test_id,
|
||||
metadata_file_path = %test_definition.metadata_file_path.display(),
|
||||
case_idx = %test_definition.case_idx,
|
||||
mode = %mode,
|
||||
);
|
||||
async move {
|
||||
let permit = match semaphore.as_ref() {
|
||||
Some(semaphore) => Some(semaphore.acquire().await.expect("Can't fail")),
|
||||
None => None,
|
||||
};
|
||||
|
||||
running_task_list.write().await.insert(test_id);
|
||||
let driver = match Driver::new_root(
|
||||
test_definition,
|
||||
private_key_allocator,
|
||||
&cached_compiler,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(driver) => driver,
|
||||
Err(error) => {
|
||||
@@ -123,35 +144,52 @@ pub async fn handle_differential_tests(
|
||||
.report_test_failed_event(format!("{error:#}"))
|
||||
.expect("Can't fail");
|
||||
error!("Test Case Failed");
|
||||
drop(permit);
|
||||
running_task_list.write().await.remove(&test_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
info!("Created the driver for the test case");
|
||||
info!("Created the driver for the test case");
|
||||
|
||||
match driver.execute_all().await {
|
||||
Ok(steps_executed) => test_definition
|
||||
.reporter
|
||||
.report_test_succeeded_event(steps_executed)
|
||||
.expect("Can't fail"),
|
||||
Err(error) => {
|
||||
test_definition
|
||||
match driver.execute_all().await {
|
||||
Ok(steps_executed) => test_definition
|
||||
.reporter
|
||||
.report_test_failed_event(format!("{error:#}"))
|
||||
.expect("Can't fail");
|
||||
error!("Test Case Failed");
|
||||
}
|
||||
};
|
||||
info!("Finished the execution of the test case")
|
||||
}
|
||||
.instrument(span)
|
||||
}))
|
||||
.report_test_succeeded_event(steps_executed)
|
||||
.expect("Can't fail"),
|
||||
Err(error) => {
|
||||
test_definition
|
||||
.reporter
|
||||
.report_test_failed_event(format!("{error:#}"))
|
||||
.expect("Can't fail");
|
||||
error!("Test Case Failed");
|
||||
}
|
||||
};
|
||||
info!("Finished the execution of the test case");
|
||||
drop(permit);
|
||||
running_task_list.write().await.remove(&test_id);
|
||||
}
|
||||
.instrument(span)
|
||||
},
|
||||
))
|
||||
.inspect(|_| {
|
||||
info!("Finished executing all test cases");
|
||||
reporter_clone
|
||||
.report_completion_event()
|
||||
.expect("Can't fail")
|
||||
});
|
||||
let cli_reporting_task = start_cli_reporting_task(reporter);
|
||||
let cli_reporting_task = start_cli_reporting_task(context.output_format, reporter);
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
loop {
|
||||
let remaining_tasks = running_task_list.read().await;
|
||||
info!(
|
||||
count = remaining_tasks.len(),
|
||||
?remaining_tasks,
|
||||
"Remaining Tests"
|
||||
);
|
||||
tokio::time::sleep(Duration::from_secs(10)).await
|
||||
}
|
||||
});
|
||||
|
||||
futures::future::join(driver_task, cli_reporting_task).await;
|
||||
|
||||
@@ -159,21 +197,15 @@ pub async fn handle_differential_tests(
|
||||
}
|
||||
|
||||
#[allow(irrefutable_let_patterns, clippy::uninlined_format_args)]
|
||||
async fn start_cli_reporting_task(reporter: Reporter) {
|
||||
async fn start_cli_reporting_task(output_format: OutputFormat, reporter: Reporter) {
|
||||
let mut aggregator_events_rx = reporter.subscribe().await.expect("Can't fail");
|
||||
drop(reporter);
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
const GREEN: &str = "\x1B[32m";
|
||||
const RED: &str = "\x1B[31m";
|
||||
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 global_success_count = 0;
|
||||
let mut global_failure_count = 0;
|
||||
let mut global_ignore_count = 0;
|
||||
|
||||
let mut buf = BufWriter::new(stderr());
|
||||
while let Ok(event) = aggregator_events_rx.recv().await {
|
||||
@@ -186,55 +218,125 @@ async fn start_cli_reporting_task(reporter: Reporter) {
|
||||
continue;
|
||||
};
|
||||
|
||||
let _ = writeln!(buf, "{} - {}", mode, metadata_file_path.display());
|
||||
for (case_idx, case_status) in case_status.into_iter() {
|
||||
let _ = write!(buf, "\tCase Index {case_idx:>3}: ");
|
||||
let _ = match case_status {
|
||||
TestCaseStatus::Succeeded { steps_executed } => {
|
||||
number_of_successes += 1;
|
||||
writeln!(
|
||||
buf,
|
||||
"{}{}Case Succeeded{} - Steps Executed: {}{}",
|
||||
GREEN, BOLD, BOLD_RESET, steps_executed, COLOR_RESET
|
||||
)
|
||||
match output_format {
|
||||
OutputFormat::Legacy => {
|
||||
let _ = writeln!(buf, "{} - {}", mode, metadata_file_path.display());
|
||||
for (case_idx, case_status) in case_status.into_iter() {
|
||||
let _ = write!(buf, "\tCase Index {case_idx:>3}: ");
|
||||
let _ = match case_status {
|
||||
TestCaseStatus::Succeeded { steps_executed } => {
|
||||
global_success_count += 1;
|
||||
writeln!(
|
||||
buf,
|
||||
"{}",
|
||||
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())),
|
||||
])
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
TestCaseStatus::Failed { reason } => {
|
||||
number_of_failures += 1;
|
||||
writeln!(
|
||||
buf,
|
||||
"{}{}Case Failed{} - Reason: {}{}",
|
||||
RED,
|
||||
BOLD,
|
||||
BOLD_RESET,
|
||||
reason.trim(),
|
||||
COLOR_RESET,
|
||||
)
|
||||
}
|
||||
TestCaseStatus::Ignored { reason, .. } => writeln!(
|
||||
let _ = writeln!(buf);
|
||||
}
|
||||
OutputFormat::CargoTestLike => {
|
||||
writeln!(
|
||||
buf,
|
||||
"{}{}Case Ignored{} - Reason: {}{}",
|
||||
GREY,
|
||||
BOLD,
|
||||
BOLD_RESET,
|
||||
reason.trim(),
|
||||
COLOR_RESET,
|
||||
),
|
||||
};
|
||||
"\t{} {} - {}\n",
|
||||
Color::Green.paint("Running"),
|
||||
metadata_file_path.display(),
|
||||
mode
|
||||
)
|
||||
.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();
|
||||
|
||||
let status = if failure_count > 0 {
|
||||
Color::Red.paint("FAILED")
|
||||
} else {
|
||||
Color::Green.paint("ok")
|
||||
};
|
||||
writeln!(
|
||||
buf,
|
||||
"test result: {}. {} passed; {} failed; {} ignored",
|
||||
status, success_count, failure_count, ignored_count,
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(buf).unwrap()
|
||||
}
|
||||
}
|
||||
let _ = writeln!(buf);
|
||||
}
|
||||
|
||||
// Summary at the end.
|
||||
let _ = writeln!(
|
||||
buf,
|
||||
"{} cases: {}{}{} cases succeeded, {}{}{} cases failed in {} seconds",
|
||||
number_of_successes + number_of_failures,
|
||||
GREEN,
|
||||
number_of_successes,
|
||||
COLOR_RESET,
|
||||
RED,
|
||||
number_of_failures,
|
||||
COLOR_RESET,
|
||||
start.elapsed().as_secs()
|
||||
);
|
||||
match output_format {
|
||||
OutputFormat::Legacy => {
|
||||
writeln!(
|
||||
buf,
|
||||
"{} cases: {} cases succeeded, {} cases failed in {} seconds",
|
||||
global_success_count + global_failure_count + global_ignore_count,
|
||||
Color::Green.paint(global_success_count.to_string()),
|
||||
Color::Red.paint(global_failure_count.to_string()),
|
||||
start.elapsed().as_secs()
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ async fn compile_contracts(
|
||||
// Puts a limit on how many compilations we can perform at any given instance which helps us
|
||||
// with some of the errors we've been seeing with high concurrency on MacOS (we have not tried
|
||||
// it on Linux so we don't know if these issues also persist there or not.)
|
||||
static SPAWN_GATE: LazyLock<Semaphore> = LazyLock::new(|| Semaphore::new(100));
|
||||
static SPAWN_GATE: LazyLock<Semaphore> = LazyLock::new(|| Semaphore::new(5));
|
||||
let _permit = SPAWN_GATE.acquire().await?;
|
||||
|
||||
let all_sources_in_dir = FilesWithExtensionIterator::new(metadata_directory.as_ref())
|
||||
|
||||
+119
-7
@@ -16,7 +16,7 @@ use revive_dt_config::*;
|
||||
use revive_dt_node::{
|
||||
Node, node_implementations::geth::GethNode,
|
||||
node_implementations::lighthouse_geth::LighthouseGethNode,
|
||||
node_implementations::substrate::SubstrateNode,
|
||||
node_implementations::substrate::SubstrateNode, node_implementations::zombienet::ZombienetNode,
|
||||
};
|
||||
use revive_dt_node_interaction::EthereumNode;
|
||||
use tracing::info;
|
||||
@@ -184,6 +184,7 @@ impl Platform for KitchensinkPolkavmResolcPlatform {
|
||||
let node = SubstrateNode::new(
|
||||
kitchensink_path,
|
||||
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
|
||||
None,
|
||||
context,
|
||||
);
|
||||
let node = spawn_node(node, genesis)?;
|
||||
@@ -236,6 +237,7 @@ impl Platform for KitchensinkRevmSolcPlatform {
|
||||
let node = SubstrateNode::new(
|
||||
kitchensink_path,
|
||||
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
|
||||
None,
|
||||
context,
|
||||
);
|
||||
let node = spawn_node(node, genesis)?;
|
||||
@@ -280,14 +282,17 @@ 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_configuration = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context);
|
||||
|
||||
let revive_dev_node_path = revive_dev_node_configuration.path.clone();
|
||||
let revive_dev_node_consensus = revive_dev_node_configuration.consensus.clone();
|
||||
|
||||
let genesis = genesis_configuration.genesis()?.clone();
|
||||
Ok(thread::spawn(move || {
|
||||
let node = SubstrateNode::new(
|
||||
revive_dev_node_path,
|
||||
SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND,
|
||||
Some(revive_dev_node_consensus),
|
||||
context,
|
||||
);
|
||||
let node = spawn_node(node, genesis)?;
|
||||
@@ -332,14 +337,17 @@ 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_configuration = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context);
|
||||
|
||||
let revive_dev_node_path = revive_dev_node_configuration.path.clone();
|
||||
let revive_dev_node_consensus = revive_dev_node_configuration.consensus.clone();
|
||||
|
||||
let genesis = genesis_configuration.genesis()?.clone();
|
||||
Ok(thread::spawn(move || {
|
||||
let node = SubstrateNode::new(
|
||||
revive_dev_node_path,
|
||||
SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND,
|
||||
Some(revive_dev_node_consensus),
|
||||
context,
|
||||
);
|
||||
let node = spawn_node(node, genesis)?;
|
||||
@@ -359,6 +367,102 @@ impl Platform for ReviveDevNodeRevmSolcPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||
pub struct ZombienetPolkavmResolcPlatform;
|
||||
|
||||
impl Platform for ZombienetPolkavmResolcPlatform {
|
||||
fn platform_identifier(&self) -> PlatformIdentifier {
|
||||
PlatformIdentifier::ZombienetPolkavmResolc
|
||||
}
|
||||
|
||||
fn node_identifier(&self) -> NodeIdentifier {
|
||||
NodeIdentifier::Zombienet
|
||||
}
|
||||
|
||||
fn vm_identifier(&self) -> VmIdentifier {
|
||||
VmIdentifier::PolkaVM
|
||||
}
|
||||
|
||||
fn compiler_identifier(&self) -> CompilerIdentifier {
|
||||
CompilerIdentifier::Resolc
|
||||
}
|
||||
|
||||
fn new_node(
|
||||
&self,
|
||||
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 genesis = genesis_configuration.genesis()?.clone();
|
||||
Ok(thread::spawn(move || {
|
||||
let node = ZombienetNode::new(polkadot_parachain_path, context);
|
||||
let node = spawn_node(node, genesis)?;
|
||||
Ok(Box::new(node) as Box<_>)
|
||||
}))
|
||||
}
|
||||
|
||||
fn new_compiler(
|
||||
&self,
|
||||
context: Context,
|
||||
version: Option<VersionOrRequirement>,
|
||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn SolidityCompiler>>>>> {
|
||||
Box::pin(async move {
|
||||
let compiler = Resolc::new(context, version).await;
|
||||
compiler.map(|compiler| Box::new(compiler) as Box<dyn SolidityCompiler>)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||
pub struct ZombienetRevmSolcPlatform;
|
||||
|
||||
impl Platform for ZombienetRevmSolcPlatform {
|
||||
fn platform_identifier(&self) -> PlatformIdentifier {
|
||||
PlatformIdentifier::ZombienetRevmSolc
|
||||
}
|
||||
|
||||
fn node_identifier(&self) -> NodeIdentifier {
|
||||
NodeIdentifier::Zombienet
|
||||
}
|
||||
|
||||
fn vm_identifier(&self) -> VmIdentifier {
|
||||
VmIdentifier::Evm
|
||||
}
|
||||
|
||||
fn compiler_identifier(&self) -> CompilerIdentifier {
|
||||
CompilerIdentifier::Solc
|
||||
}
|
||||
|
||||
fn new_node(
|
||||
&self,
|
||||
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 genesis = genesis_configuration.genesis()?.clone();
|
||||
Ok(thread::spawn(move || {
|
||||
let node = ZombienetNode::new(polkadot_parachain_path, context);
|
||||
let node = spawn_node(node, genesis)?;
|
||||
Ok(Box::new(node) as Box<_>)
|
||||
}))
|
||||
}
|
||||
|
||||
fn new_compiler(
|
||||
&self,
|
||||
context: Context,
|
||||
version: Option<VersionOrRequirement>,
|
||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn SolidityCompiler>>>>> {
|
||||
Box::pin(async move {
|
||||
let compiler = Solc::new(context, version).await;
|
||||
compiler.map(|compiler| Box::new(compiler) as Box<dyn SolidityCompiler>)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PlatformIdentifier> for Box<dyn Platform> {
|
||||
fn from(value: PlatformIdentifier) -> Self {
|
||||
match value {
|
||||
@@ -378,6 +482,10 @@ impl From<PlatformIdentifier> for Box<dyn Platform> {
|
||||
PlatformIdentifier::ReviveDevNodeRevmSolc => {
|
||||
Box::new(ReviveDevNodeRevmSolcPlatform) as Box<_>
|
||||
}
|
||||
PlatformIdentifier::ZombienetPolkavmResolc => {
|
||||
Box::new(ZombienetPolkavmResolcPlatform) as Box<_>
|
||||
}
|
||||
PlatformIdentifier::ZombienetRevmSolc => Box::new(ZombienetRevmSolcPlatform) as Box<_>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,6 +509,10 @@ impl From<PlatformIdentifier> for &dyn Platform {
|
||||
PlatformIdentifier::ReviveDevNodeRevmSolc => {
|
||||
&ReviveDevNodeRevmSolcPlatform as &dyn Platform
|
||||
}
|
||||
PlatformIdentifier::ZombienetPolkavmResolc => {
|
||||
&ZombienetPolkavmResolcPlatform as &dyn Platform
|
||||
}
|
||||
PlatformIdentifier::ZombienetRevmSolc => &ZombienetRevmSolcPlatform as &dyn Platform,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ rust-version.workspace = true
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
alloy = { workspace = true }
|
||||
async-stream = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tower = { workspace = true }
|
||||
@@ -29,6 +30,7 @@ serde_yaml_ng = { workspace = true }
|
||||
|
||||
sp-core = { workspace = true }
|
||||
sp-runtime = { workspace = true }
|
||||
zombienet-sdk = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
temp-dir = { workspace = true }
|
||||
|
||||
@@ -17,7 +17,7 @@ use std::{
|
||||
pin::Pin,
|
||||
process::{Command, Stdio},
|
||||
sync::{
|
||||
Arc, LazyLock,
|
||||
Arc,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
@@ -47,7 +47,7 @@ use futures::{Stream, StreamExt};
|
||||
use revive_common::EVMVersion;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::serde_as;
|
||||
use tokio::sync::{OnceCell, Semaphore};
|
||||
use tokio::sync::OnceCell;
|
||||
use tracing::{Instrument, info, instrument};
|
||||
|
||||
use revive_dt_common::{
|
||||
@@ -105,7 +105,6 @@ pub struct LighthouseGethNode {
|
||||
|
||||
persistent_http_provider: OnceCell<ConcreteProvider<Ethereum, Arc<EthereumWallet>>>,
|
||||
persistent_ws_provider: OnceCell<ConcreteProvider<Ethereum, Arc<EthereumWallet>>>,
|
||||
http_provider_requests_semaphore: LazyLock<Semaphore>,
|
||||
}
|
||||
|
||||
impl LighthouseGethNode {
|
||||
@@ -176,7 +175,6 @@ impl LighthouseGethNode {
|
||||
nonce_manager: Default::default(),
|
||||
persistent_http_provider: OnceCell::const_new(),
|
||||
persistent_ws_provider: OnceCell::const_new(),
|
||||
http_provider_requests_semaphore: LazyLock::new(|| Semaphore::const_new(500)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,8 +564,6 @@ impl EthereumNode for LighthouseGethNode {
|
||||
transaction: TransactionRequest,
|
||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<TxHash>> + '_>> {
|
||||
Box::pin(async move {
|
||||
let _permit = self.http_provider_requests_semaphore.acquire().await;
|
||||
|
||||
let provider = self
|
||||
.http_provider()
|
||||
.await
|
||||
@@ -1135,6 +1131,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "Ignored since they take a long time to run"]
|
||||
async fn node_mines_simple_transfer_transaction_and_returns_receipt() {
|
||||
// Arrange
|
||||
let (context, node) = new_node();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod geth;
|
||||
pub mod lighthouse_geth;
|
||||
pub mod substrate;
|
||||
pub mod zombienet;
|
||||
|
||||
@@ -36,7 +36,8 @@ use alloy::{
|
||||
},
|
||||
};
|
||||
use anyhow::Context as _;
|
||||
use futures::{Stream, StreamExt};
|
||||
use async_stream::stream;
|
||||
use futures::Stream;
|
||||
use revive_common::EVMVersion;
|
||||
use revive_dt_common::fs::clear_directory;
|
||||
use revive_dt_format::traits::ResolverApi;
|
||||
@@ -54,7 +55,10 @@ use crate::{
|
||||
Node,
|
||||
constants::{CHAIN_ID, INITIAL_BALANCE},
|
||||
helpers::{Process, ProcessReadinessWaitBehavior},
|
||||
provider_utils::{ConcreteProvider, FallbackGasFiller, construct_concurrency_limited_provider},
|
||||
provider_utils::{
|
||||
ConcreteProvider, FallbackGasFiller, construct_concurrency_limited_provider,
|
||||
execute_transaction,
|
||||
},
|
||||
};
|
||||
|
||||
static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
@@ -77,10 +81,11 @@ pub struct SubstrateNode {
|
||||
wallet: Arc<EthereumWallet>,
|
||||
nonce_manager: CachedNonceManager,
|
||||
provider: OnceCell<ConcreteProvider<ReviveNetwork, Arc<EthereumWallet>>>,
|
||||
consensus: Option<String>,
|
||||
}
|
||||
|
||||
impl SubstrateNode {
|
||||
const BASE_DIRECTORY: &str = "Substrate";
|
||||
const BASE_DIRECTORY: &str = "substrate";
|
||||
const LOGS_DIRECTORY: &str = "logs";
|
||||
const DATA_DIRECTORY: &str = "chains";
|
||||
|
||||
@@ -99,6 +104,7 @@ impl SubstrateNode {
|
||||
pub fn new(
|
||||
node_path: PathBuf,
|
||||
export_chainspec_command: &str,
|
||||
consensus: Option<String>,
|
||||
context: impl AsRef<WorkingDirectoryConfiguration>
|
||||
+ AsRef<EthRpcConfiguration>
|
||||
+ AsRef<WalletConfiguration>,
|
||||
@@ -128,6 +134,7 @@ impl SubstrateNode {
|
||||
wallet: wallet.clone(),
|
||||
nonce_manager: Default::default(),
|
||||
provider: Default::default(),
|
||||
consensus,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +232,7 @@ impl SubstrateNode {
|
||||
self.logs_directory.as_path(),
|
||||
self.node_binary.as_path(),
|
||||
|command, stdout_file, stderr_file| {
|
||||
command
|
||||
let cmd = command
|
||||
.arg("--dev")
|
||||
.arg("--chain")
|
||||
.arg(chainspec_path)
|
||||
@@ -242,9 +249,16 @@ impl SubstrateNode {
|
||||
.arg("all")
|
||||
.arg("--rpc-max-connections")
|
||||
.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)
|
||||
.stdout(stdout_file)
|
||||
.stderr(stderr_file);
|
||||
if let Some(consensus) = self.consensus.as_ref() {
|
||||
cmd.arg("--consensus").arg(consensus.clone());
|
||||
}
|
||||
},
|
||||
ProcessReadinessWaitBehavior::TimeBoundedWaitFunction {
|
||||
max_wait_duration: Duration::from_secs(30),
|
||||
@@ -346,7 +360,7 @@ impl SubstrateNode {
|
||||
.get_or_try_init(|| async move {
|
||||
construct_concurrency_limited_provider::<ReviveNetwork, _>(
|
||||
self.rpc_url.as_str(),
|
||||
FallbackGasFiller::new(250_000_000, 5_000_000_000, 1_000_000_000),
|
||||
FallbackGasFiller::new(u64::MAX, 5_000_000_000, 1_000_000_000),
|
||||
ChainIdFiller::new(Some(CHAIN_ID)),
|
||||
NonceFiller::new(self.nonce_manager.clone()),
|
||||
self.wallet.clone(),
|
||||
@@ -408,23 +422,12 @@ impl EthereumNode for SubstrateNode {
|
||||
&self,
|
||||
transaction: TransactionRequest,
|
||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
||||
static SEMAPHORE: std::sync::LazyLock<tokio::sync::Semaphore> =
|
||||
std::sync::LazyLock::new(|| tokio::sync::Semaphore::new(500));
|
||||
|
||||
Box::pin(async move {
|
||||
let _permit = SEMAPHORE.acquire().await?;
|
||||
|
||||
let receipt = self
|
||||
let provider = self
|
||||
.provider()
|
||||
.await
|
||||
.context("Failed to create provider for transaction submission")?
|
||||
.send_transaction(transaction)
|
||||
.await
|
||||
.context("Failed to submit transaction to substrate proxy")?
|
||||
.get_receipt()
|
||||
.await
|
||||
.context("Failed to fetch transaction receipt from substrate proxy")?;
|
||||
Ok(receipt)
|
||||
.context("Failed to create the provider")?;
|
||||
execute_transaction(provider, transaction).await
|
||||
})
|
||||
}
|
||||
|
||||
@@ -516,37 +519,46 @@ 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 {
|
||||
let provider = self
|
||||
.provider()
|
||||
.await
|
||||
.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();
|
||||
.context("Failed to create the provider for a block subscription")?;
|
||||
|
||||
let mined_block_information_stream = block_stream.filter_map(|block| async {
|
||||
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(),
|
||||
})
|
||||
});
|
||||
let stream = Box::pin(create_stream(provider))
|
||||
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>;
|
||||
|
||||
Ok(Box::pin(mined_block_information_stream)
|
||||
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
|
||||
Ok(stream)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1178,6 +1190,7 @@ mod tests {
|
||||
let mut node = SubstrateNode::new(
|
||||
context.kitchensink_configuration.path.clone(),
|
||||
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
|
||||
None,
|
||||
&context,
|
||||
);
|
||||
node.init(context.genesis_configuration.genesis().unwrap().clone())
|
||||
@@ -1243,6 +1256,7 @@ mod tests {
|
||||
let mut dummy_node = SubstrateNode::new(
|
||||
context.kitchensink_configuration.path.clone(),
|
||||
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
|
||||
None,
|
||||
&context,
|
||||
);
|
||||
|
||||
@@ -1295,6 +1309,7 @@ mod tests {
|
||||
let node = SubstrateNode::new(
|
||||
context.kitchensink_configuration.path.clone(),
|
||||
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
|
||||
None,
|
||||
&context,
|
||||
);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+15
-4
@@ -7,6 +7,10 @@ use alloy::{
|
||||
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)]
|
||||
pub struct FallbackGasFiller {
|
||||
inner: GasFiller,
|
||||
@@ -56,8 +60,6 @@ where
|
||||
provider: &P,
|
||||
tx: &<N as Network>::TransactionRequest,
|
||||
) -> TransportResult<Self::Fillable> {
|
||||
// Try to fetch GasFiller’s “fillable” (gas_price, base_fee, estimate_gas, …)
|
||||
// If it errors (i.e. tx would revert under eth_estimateGas), swallow it.
|
||||
match self.inner.prepare(provider, tx).await {
|
||||
Ok(fill) => Ok(Some(fill)),
|
||||
Err(_) => Ok(None),
|
||||
@@ -70,8 +72,17 @@ where
|
||||
mut tx: alloy::providers::SendableTx<N>,
|
||||
) -> TransportResult<SendableTx<N>> {
|
||||
if let Some(fill) = fillable {
|
||||
// our inner GasFiller succeeded — use it
|
||||
self.inner.fill(fill, tx).await
|
||||
let mut tx = self.inner.fill(fill, tx).await?;
|
||||
if let Some(builder) = tx.as_mut_builder() {
|
||||
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 {
|
||||
if let Some(builder) = tx.as_mut_builder() {
|
||||
builder.set_gas_limit(self.default_gas_limit);
|
||||
@@ -1,7 +1,7 @@
|
||||
mod concurrency_limiter;
|
||||
mod fallback_gas_provider;
|
||||
mod fallback_gas_filler;
|
||||
mod provider;
|
||||
|
||||
pub use concurrency_limiter::*;
|
||||
pub use fallback_gas_provider::*;
|
||||
pub use fallback_gas_filler::*;
|
||||
pub use provider::*;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use std::sync::LazyLock;
|
||||
use std::{ops::ControlFlow, sync::LazyLock, time::Duration};
|
||||
|
||||
use alloy::{
|
||||
network::{Network, NetworkWallet, TransactionBuilder4844},
|
||||
network::{Ethereum, Network, NetworkWallet, TransactionBuilder4844},
|
||||
providers::{
|
||||
Identity, ProviderBuilder, RootProvider,
|
||||
Identity, PendingTransactionBuilder, Provider, ProviderBuilder, RootProvider,
|
||||
fillers::{ChainIdFiller, FillProvider, JoinFill, NonceFiller, TxFiller, WalletFiller},
|
||||
},
|
||||
rpc::client::ClientBuilder,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use revive_dt_common::futures::{PollingWaitBehavior, poll};
|
||||
use tracing::{Instrument, debug, info, info_span};
|
||||
|
||||
use crate::provider_utils::{ConcurrencyLimiterLayer, FallbackGasFiller};
|
||||
|
||||
@@ -42,7 +44,7 @@ where
|
||||
// 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.
|
||||
static GLOBAL_CONCURRENCY_LIMITER_LAYER: LazyLock<ConcurrencyLimiterLayer> =
|
||||
LazyLock::new(|| ConcurrencyLimiterLayer::new(10));
|
||||
LazyLock::new(|| ConcurrencyLimiterLayer::new(500));
|
||||
|
||||
let client = ClientBuilder::default()
|
||||
.layer(GLOBAL_CONCURRENCY_LIMITER_LAYER.clone())
|
||||
@@ -61,3 +63,70 @@ where
|
||||
|
||||
Ok(provider)
|
||||
}
|
||||
|
||||
pub async fn execute_transaction<N, W>(
|
||||
provider: ConcreteProvider<N, W>,
|
||||
transaction: N::TransactionRequest,
|
||||
) -> Result<N::ReceiptResponse>
|
||||
where
|
||||
N: Network<
|
||||
TransactionRequest: TransactionBuilder4844,
|
||||
TxEnvelope = <Ethereum as Network>::TxEnvelope,
|
||||
>,
|
||||
W: NetworkWallet<N>,
|
||||
Identity: TxFiller<N>,
|
||||
FallbackGasFiller: TxFiller<N>,
|
||||
ChainIdFiller: TxFiller<N>,
|
||||
NonceFiller: TxFiller<N>,
|
||||
WalletFiller<W>: TxFiller<N>,
|
||||
{
|
||||
let sendable_transaction = provider
|
||||
.fill(transaction)
|
||||
.await
|
||||
.context("Failed to fill transaction")?;
|
||||
|
||||
let transaction_envelope = sendable_transaction
|
||||
.try_into_envelope()
|
||||
.context("Failed to convert transaction into an envelope")?;
|
||||
let tx_hash = *transaction_envelope.tx_hash();
|
||||
|
||||
let mut pending_transaction = match provider.send_tx_envelope(transaction_envelope).await {
|
||||
Ok(pending_transaction) => pending_transaction,
|
||||
Err(error) => {
|
||||
let error_string = error.to_string();
|
||||
|
||||
if error_string.contains("Transaction Already Imported") {
|
||||
PendingTransactionBuilder::<N>::new(provider.root().clone(), tx_hash)
|
||||
} 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}"
|
||||
))?;
|
||||
|
||||
poll(
|
||||
Duration::from_secs(60),
|
||||
PollingWaitBehavior::Constant(Duration::from_secs(3)),
|
||||
|| {
|
||||
let provider = provider.clone();
|
||||
|
||||
async move {
|
||||
match provider.get_transaction_receipt(tx_hash).await {
|
||||
Ok(Some(receipt)) => {
|
||||
info!("Found the transaction receipt");
|
||||
Ok(ControlFlow::Break(receipt))
|
||||
}
|
||||
_ => Ok(ControlFlow::Continue(())),
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.instrument(info_span!("Polling for receipt", %tx_hash))
|
||||
.await
|
||||
.context(format!("Polling for receipt failed for {tx_hash}"))
|
||||
}
|
||||
|
||||
+4
-6
@@ -76,8 +76,6 @@ cat > "$CORPUS_FILE" << EOF
|
||||
{
|
||||
"name": "MatterLabs Solidity Simple, Complex, and Semantic Tests",
|
||||
"paths": [
|
||||
"$(realpath "$TEST_REPO_DIR/fixtures/solidity/translated_semantic_tests")",
|
||||
"$(realpath "$TEST_REPO_DIR/fixtures/solidity/complex")",
|
||||
"$(realpath "$TEST_REPO_DIR/fixtures/solidity/simple")"
|
||||
]
|
||||
}
|
||||
@@ -96,15 +94,15 @@ echo ""
|
||||
cargo build --release;
|
||||
RUST_LOG="info,alloy_pubsub::service=error" ./target/release/retester test \
|
||||
--platform geth-evm-solc \
|
||||
--platform revive-dev-node-revm-solc \
|
||||
--corpus "$CORPUS_FILE" \
|
||||
--working-directory "$WORKDIR" \
|
||||
--concurrency.number-of-nodes 5 \
|
||||
--concurrency.number-of-nodes 10 \
|
||||
--concurrency.number-of-threads 5 \
|
||||
--concurrency.ignore-concurrency-limit \
|
||||
--wallet.additional-keys 100000 \
|
||||
--kitchensink.path "$SUBSTRATE_NODE_BIN" \
|
||||
--revive-dev-node.path "$REVIVE_DEV_NODE_BIN" \
|
||||
--eth-rpc.path "$ETH_RPC_BIN" \
|
||||
> logs.log \
|
||||
2> output.log
|
||||
> logs.log
|
||||
|
||||
echo -e "${GREEN}=== Test run completed! ===${NC}"
|
||||
Reference in New Issue
Block a user