mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-28 17:48:00 +00:00
Fix concurrency issues (#142)
* Fix the OS FD error * Cache the compiler versions * Allow for auto display impl in declare wrapper type macro * Better logging and fix concurrency issues * Fix tests * Format * Make the code even more concurrent
This commit is contained in:
@@ -62,8 +62,9 @@ impl CachedCompiler {
|
||||
compiler_version_or_requirement,
|
||||
)
|
||||
.await?;
|
||||
let compiler_version =
|
||||
<P::Compiler as SolidityCompiler>::new(compiler_path.clone()).version()?;
|
||||
let compiler_version = <P::Compiler as SolidityCompiler>::new(compiler_path.clone())
|
||||
.version()
|
||||
.await?;
|
||||
|
||||
let cache_key = CacheKey {
|
||||
platform_key: P::config_id().to_string(),
|
||||
|
||||
+90
-142
@@ -16,26 +16,24 @@ use alloy::rpc::types::trace::geth::{
|
||||
};
|
||||
use alloy::{
|
||||
primitives::Address,
|
||||
rpc::types::{
|
||||
TransactionRequest,
|
||||
trace::geth::{AccountState, DiffMode},
|
||||
},
|
||||
rpc::types::{TransactionRequest, trace::geth::DiffMode},
|
||||
};
|
||||
use anyhow::Context;
|
||||
use futures::TryStreamExt;
|
||||
use indexmap::IndexMap;
|
||||
use revive_dt_format::traits::{ResolutionContext, ResolverApi};
|
||||
use semver::Version;
|
||||
|
||||
use revive_dt_format::case::{Case, CaseIdx};
|
||||
use revive_dt_format::case::Case;
|
||||
use revive_dt_format::input::{
|
||||
BalanceAssertion, Calldata, EtherValue, Expected, ExpectedOutput, Input, Method,
|
||||
BalanceAssertion, Calldata, EtherValue, Expected, ExpectedOutput, Input, Method, StepIdx,
|
||||
StorageEmptyAssertion,
|
||||
};
|
||||
use revive_dt_format::metadata::{ContractIdent, ContractInstance, ContractPathAndIdent};
|
||||
use revive_dt_format::{input::Step, metadata::Metadata};
|
||||
use revive_dt_node::Node;
|
||||
use revive_dt_node_interaction::EthereumNode;
|
||||
use tracing::Instrument;
|
||||
use tokio::try_join;
|
||||
use tracing::{Instrument, info, info_span, instrument};
|
||||
|
||||
use crate::Platform;
|
||||
|
||||
@@ -77,38 +75,38 @@ where
|
||||
pub async fn handle_step(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
case_idx: CaseIdx,
|
||||
step: &Step,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<StepOutput> {
|
||||
match step {
|
||||
Step::FunctionCall(input) => {
|
||||
let (receipt, geth_trace, diff_mode) =
|
||||
self.handle_input(metadata, case_idx, input, node).await?;
|
||||
self.handle_input(metadata, input, node).await?;
|
||||
Ok(StepOutput::FunctionCall(receipt, geth_trace, diff_mode))
|
||||
}
|
||||
Step::BalanceAssertion(balance_assertion) => {
|
||||
self.handle_balance_assertion(metadata, case_idx, balance_assertion, node)
|
||||
self.handle_balance_assertion(metadata, balance_assertion, node)
|
||||
.await?;
|
||||
Ok(StepOutput::BalanceAssertion)
|
||||
}
|
||||
Step::StorageEmptyAssertion(storage_empty) => {
|
||||
self.handle_storage_empty(metadata, case_idx, storage_empty, node)
|
||||
self.handle_storage_empty(metadata, storage_empty, node)
|
||||
.await?;
|
||||
Ok(StepOutput::StorageEmptyAssertion)
|
||||
}
|
||||
}
|
||||
.inspect(|_| info!("Step Succeeded"))
|
||||
}
|
||||
|
||||
#[instrument(level = "info", name = "Handling Input", skip_all)]
|
||||
pub async fn handle_input(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
case_idx: CaseIdx,
|
||||
input: &Input,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
||||
let deployment_receipts = self
|
||||
.handle_input_contract_deployment(metadata, case_idx, input, node)
|
||||
.handle_input_contract_deployment(metadata, input, node)
|
||||
.await?;
|
||||
let execution_receipt = self
|
||||
.handle_input_execution(input, deployment_receipts, node)
|
||||
@@ -117,16 +115,17 @@ where
|
||||
.handle_input_call_frame_tracing(&execution_receipt, node)
|
||||
.await?;
|
||||
self.handle_input_variable_assignment(input, &tracing_result)?;
|
||||
self.handle_input_expectations(input, &execution_receipt, node, &tracing_result)
|
||||
.await?;
|
||||
self.handle_input_diff(case_idx, execution_receipt, node)
|
||||
.await
|
||||
let (_, (geth_trace, diff_mode)) = try_join!(
|
||||
self.handle_input_expectations(input, &execution_receipt, node, &tracing_result),
|
||||
self.handle_input_diff(&execution_receipt, node)
|
||||
)?;
|
||||
Ok((execution_receipt, geth_trace, diff_mode))
|
||||
}
|
||||
|
||||
#[instrument(level = "info", name = "Handling Balance Assertion", skip_all)]
|
||||
pub async fn handle_balance_assertion(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
_: CaseIdx,
|
||||
balance_assertion: &BalanceAssertion,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -137,10 +136,10 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "info", name = "Handling Storage Assertion", skip_all)]
|
||||
pub async fn handle_storage_empty(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
_: CaseIdx,
|
||||
storage_empty: &StorageEmptyAssertion,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -152,10 +151,10 @@ where
|
||||
}
|
||||
|
||||
/// Handles the contract deployment for a given input performing it if it needs to be performed.
|
||||
#[instrument(level = "info", skip_all)]
|
||||
async fn handle_input_contract_deployment(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
_: CaseIdx,
|
||||
input: &Input,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<HashMap<ContractInstance, TransactionReceipt>> {
|
||||
@@ -170,11 +169,6 @@ where
|
||||
instances_we_must_deploy.insert(input.instance.clone(), true);
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
instances_to_deploy = instances_we_must_deploy.len(),
|
||||
"Computed the number of required deployments for input"
|
||||
);
|
||||
|
||||
let mut receipts = HashMap::new();
|
||||
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
|
||||
let calldata = deploy_with_constructor_arguments.then_some(&input.calldata);
|
||||
@@ -201,6 +195,7 @@ where
|
||||
}
|
||||
|
||||
/// Handles the execution of the input in terms of the calls that need to be made.
|
||||
#[instrument(level = "info", skip_all)]
|
||||
async fn handle_input_execution(
|
||||
&mut self,
|
||||
input: &Input,
|
||||
@@ -218,33 +213,21 @@ where
|
||||
.legacy_transaction(node, self.default_resolution_context())
|
||||
.await
|
||||
{
|
||||
Ok(tx) => {
|
||||
tracing::debug!("Legacy transaction data: {tx:#?}");
|
||||
tx
|
||||
}
|
||||
Ok(tx) => tx,
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to construct legacy transaction: {err:?}");
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
tracing::trace!("Executing transaction for input: {input:?}");
|
||||
|
||||
match node.execute_transaction(tx).await {
|
||||
Ok(receipt) => Ok(receipt),
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
"Failed to execute transaction when executing the contract: {}, {:?}",
|
||||
&*input.instance,
|
||||
err
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
async fn handle_input_call_frame_tracing(
|
||||
&self,
|
||||
execution_receipt: &TransactionReceipt,
|
||||
@@ -259,7 +242,10 @@ where
|
||||
tracer_config: GethDebugTracerConfig(serde_json::json! {{
|
||||
"onlyTopCall": true,
|
||||
"withLog": false,
|
||||
"withReturnData": false
|
||||
"withStorage": false,
|
||||
"withMemory": false,
|
||||
"withStack": false,
|
||||
"withReturnData": true
|
||||
}}),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -272,6 +258,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
fn handle_input_variable_assignment(
|
||||
&mut self,
|
||||
input: &Input,
|
||||
@@ -302,8 +289,9 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
async fn handle_input_expectations(
|
||||
&mut self,
|
||||
&self,
|
||||
input: &Input,
|
||||
execution_receipt: &TransactionReceipt,
|
||||
resolver: &impl ResolverApi,
|
||||
@@ -337,24 +325,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
for expectation in expectations.iter() {
|
||||
self.handle_input_expectation_item(
|
||||
execution_receipt,
|
||||
resolver,
|
||||
expectation,
|
||||
tracing_result,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
futures::stream::iter(expectations.into_iter().map(Ok))
|
||||
.try_for_each_concurrent(None, |expectation| async move {
|
||||
self.handle_input_expectation_item(
|
||||
execution_receipt,
|
||||
resolver,
|
||||
expectation,
|
||||
tracing_result,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
async fn handle_input_expectation_item(
|
||||
&mut self,
|
||||
&self,
|
||||
execution_receipt: &TransactionReceipt,
|
||||
resolver: &impl ResolverApi,
|
||||
expectation: &ExpectedOutput,
|
||||
expectation: ExpectedOutput,
|
||||
tracing_result: &CallFrame,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(ref version_requirement) = expectation.compiler_version {
|
||||
@@ -492,12 +481,12 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
async fn handle_input_diff(
|
||||
&mut self,
|
||||
_: CaseIdx,
|
||||
execution_receipt: TransactionReceipt,
|
||||
&self,
|
||||
execution_receipt: &TransactionReceipt,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
||||
) -> anyhow::Result<(GethTrace, DiffMode)> {
|
||||
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
|
||||
diff_mode: Some(true),
|
||||
disable_code: None,
|
||||
@@ -505,13 +494,14 @@ where
|
||||
});
|
||||
|
||||
let trace = node
|
||||
.trace_transaction(&execution_receipt, trace_options)
|
||||
.trace_transaction(execution_receipt, trace_options)
|
||||
.await?;
|
||||
let diff = node.state_diff(&execution_receipt).await?;
|
||||
let diff = node.state_diff(execution_receipt).await?;
|
||||
|
||||
Ok((execution_receipt, trace, diff))
|
||||
Ok((trace, diff))
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub async fn handle_balance_assertion_contract_deployment(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
@@ -537,6 +527,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub async fn handle_balance_assertion_execution(
|
||||
&mut self,
|
||||
BalanceAssertion {
|
||||
@@ -572,6 +563,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub async fn handle_storage_empty_assertion_contract_deployment(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
@@ -597,6 +589,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub async fn handle_storage_empty_assertion_execution(
|
||||
&mut self,
|
||||
StorageEmptyAssertion {
|
||||
@@ -658,7 +651,6 @@ where
|
||||
contract_ident,
|
||||
}) = metadata.contract_sources()?.remove(contract_instance)
|
||||
else {
|
||||
tracing::error!("Contract source not found for instance");
|
||||
anyhow::bail!(
|
||||
"Contract source not found for instance {:?}",
|
||||
contract_instance
|
||||
@@ -671,11 +663,6 @@ where
|
||||
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
|
||||
.cloned()
|
||||
else {
|
||||
tracing::error!(
|
||||
contract_source_path = contract_source_path.display().to_string(),
|
||||
contract_ident = contract_ident.as_ref(),
|
||||
"Failed to find information for contract"
|
||||
);
|
||||
anyhow::bail!(
|
||||
"Failed to find information for contract {:?}",
|
||||
contract_instance
|
||||
@@ -724,7 +711,6 @@ where
|
||||
};
|
||||
|
||||
let Some(address) = receipt.contract_address else {
|
||||
tracing::error!("Contract deployment transaction didn't return an address");
|
||||
anyhow::bail!("Contract deployment didn't return an address");
|
||||
};
|
||||
tracing::info!(
|
||||
@@ -751,7 +737,6 @@ where
|
||||
pub struct CaseDriver<'a, Leader: Platform, Follower: Platform> {
|
||||
metadata: &'a Metadata,
|
||||
case: &'a Case,
|
||||
case_idx: CaseIdx,
|
||||
leader_node: &'a Leader::Blockchain,
|
||||
follower_node: &'a Follower::Blockchain,
|
||||
leader_state: CaseState<Leader>,
|
||||
@@ -767,7 +752,6 @@ where
|
||||
pub fn new(
|
||||
metadata: &'a Metadata,
|
||||
case: &'a Case,
|
||||
case_idx: impl Into<CaseIdx>,
|
||||
leader_node: &'a L::Blockchain,
|
||||
follower_node: &'a F::Blockchain,
|
||||
leader_state: CaseState<L>,
|
||||
@@ -776,7 +760,6 @@ where
|
||||
Self {
|
||||
metadata,
|
||||
case,
|
||||
case_idx: case_idx.into(),
|
||||
leader_node,
|
||||
follower_node,
|
||||
leader_state,
|
||||
@@ -784,79 +767,44 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trace_diff_mode(label: &str, diff: &DiffMode) {
|
||||
tracing::trace!("{label} - PRE STATE:");
|
||||
for (addr, state) in &diff.pre {
|
||||
Self::trace_account_state(" [pre]", addr, state);
|
||||
}
|
||||
|
||||
tracing::trace!("{label} - POST STATE:");
|
||||
for (addr, state) in &diff.post {
|
||||
Self::trace_account_state(" [post]", addr, state);
|
||||
}
|
||||
}
|
||||
|
||||
fn trace_account_state(prefix: &str, addr: &Address, state: &AccountState) {
|
||||
tracing::trace!("{prefix} 0x{addr:x}");
|
||||
|
||||
if let Some(balance) = &state.balance {
|
||||
tracing::trace!("{prefix} balance: {balance}");
|
||||
}
|
||||
if let Some(nonce) = &state.nonce {
|
||||
tracing::trace!("{prefix} nonce: {nonce}");
|
||||
}
|
||||
if let Some(code) = &state.code {
|
||||
tracing::trace!("{prefix} code: {code}");
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "info", name = "Executing Case", skip_all)]
|
||||
pub async fn execute(&mut self) -> anyhow::Result<usize> {
|
||||
if !self
|
||||
.leader_node
|
||||
.matches_target(self.metadata.targets.as_deref())
|
||||
|| !self
|
||||
.follower_node
|
||||
.matches_target(self.metadata.targets.as_deref())
|
||||
{
|
||||
tracing::warn!(
|
||||
targets = ?self.metadata.targets,
|
||||
"Either the leader or follower node do not support the targets of the file"
|
||||
);
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut steps_executed = 0;
|
||||
for (step_idx, step) in self.case.steps_iterator().enumerate() {
|
||||
let tracing_span = tracing::info_span!("Handling input", step_idx);
|
||||
for (step_idx, step) in self
|
||||
.case
|
||||
.steps_iterator()
|
||||
.enumerate()
|
||||
.map(|(idx, v)| (StepIdx::new(idx), v))
|
||||
{
|
||||
let (leader_step_output, follower_step_output) = try_join!(
|
||||
self.leader_state
|
||||
.handle_step(self.metadata, &step, self.leader_node)
|
||||
.instrument(info_span!(
|
||||
"Handling Step",
|
||||
%step_idx,
|
||||
target = "Leader",
|
||||
)),
|
||||
self.follower_state
|
||||
.handle_step(self.metadata, &step, self.follower_node)
|
||||
.instrument(info_span!(
|
||||
"Handling Step",
|
||||
%step_idx,
|
||||
target = "Follower",
|
||||
))
|
||||
)?;
|
||||
|
||||
let leader_step_output = self
|
||||
.leader_state
|
||||
.handle_step(self.metadata, self.case_idx, &step, self.leader_node)
|
||||
.instrument(tracing_span.clone())
|
||||
.await?;
|
||||
let follower_step_output = self
|
||||
.follower_state
|
||||
.handle_step(self.metadata, self.case_idx, &step, self.follower_node)
|
||||
.instrument(tracing_span)
|
||||
.await?;
|
||||
match (leader_step_output, follower_step_output) {
|
||||
(
|
||||
StepOutput::FunctionCall(leader_receipt, _, leader_diff),
|
||||
StepOutput::FunctionCall(follower_receipt, _, follower_diff),
|
||||
) => {
|
||||
if leader_diff == follower_diff {
|
||||
tracing::debug!("State diffs match between leader and follower.");
|
||||
} else {
|
||||
tracing::debug!("State diffs mismatch between leader and follower.");
|
||||
Self::trace_diff_mode("Leader", &leader_diff);
|
||||
Self::trace_diff_mode("Follower", &follower_diff);
|
||||
}
|
||||
|
||||
if leader_receipt.logs() != follower_receipt.logs() {
|
||||
tracing::debug!("Log/event mismatch between leader and follower.");
|
||||
tracing::trace!("Leader logs: {:?}", leader_receipt.logs());
|
||||
tracing::trace!("Follower logs: {:?}", follower_receipt.logs());
|
||||
}
|
||||
(StepOutput::FunctionCall(..), StepOutput::FunctionCall(..)) => {
|
||||
// TODO: We need to actually work out how/if we will compare the diff between
|
||||
// the leader and the follower. The diffs are almost guaranteed to be different
|
||||
// from leader and follower and therefore without an actual strategy for this
|
||||
// we have something that's guaranteed to fail. Even a simple call to some
|
||||
// contract will produce two non-equal diffs because on the leader the contract
|
||||
// has address X and on the follower it has address Y. On the leader contract X
|
||||
// contains address A in the state and on the follower it contains address B. So
|
||||
// this isn't exactly a straightforward thing to do and I'm not even sure that
|
||||
// it's possible to do. Once we have an actual strategy for doing the diffs we
|
||||
// will implement it here. Until then, this remains empty.
|
||||
}
|
||||
(StepOutput::BalanceAssertion, StepOutput::BalanceAssertion) => {}
|
||||
(StepOutput::StorageEmptyAssertion, StepOutput::StorageEmptyAssertion) => {}
|
||||
|
||||
+268
-202
@@ -1,8 +1,9 @@
|
||||
mod cached_compiler;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
collections::{BTreeMap, HashMap},
|
||||
io::{BufWriter, Write, stderr},
|
||||
path::Path,
|
||||
sync::{Arc, LazyLock},
|
||||
time::Instant,
|
||||
};
|
||||
@@ -13,16 +14,18 @@ use alloy::{
|
||||
};
|
||||
use anyhow::Context;
|
||||
use clap::Parser;
|
||||
use futures::stream::futures_unordered::FuturesUnordered;
|
||||
use futures::stream;
|
||||
use futures::{Stream, StreamExt};
|
||||
use indexmap::IndexMap;
|
||||
use revive_dt_node_interaction::EthereumNode;
|
||||
use temp_dir::TempDir;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{Instrument, Level};
|
||||
use tokio::{sync::mpsc, try_join};
|
||||
use tracing::{debug, info, info_span, instrument};
|
||||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::{EnvFilter, FmtSubscriber};
|
||||
|
||||
use revive_dt_common::types::Mode;
|
||||
use revive_dt_compiler::SolidityCompiler;
|
||||
use revive_dt_compiler::{CompilerOutput, SolidityCompiler};
|
||||
use revive_dt_config::*;
|
||||
use revive_dt_core::{
|
||||
Geth, Kitchensink, Platform,
|
||||
@@ -32,9 +35,10 @@ use revive_dt_format::{
|
||||
case::{Case, CaseIdx},
|
||||
corpus::Corpus,
|
||||
input::{Input, Step},
|
||||
metadata::{ContractPathAndIdent, Metadata, MetadataFile},
|
||||
metadata::{ContractPathAndIdent, MetadataFile},
|
||||
mode::ParsedMode,
|
||||
};
|
||||
use revive_dt_node::pool::NodePool;
|
||||
use revive_dt_node::{Node, pool::NodePool};
|
||||
use revive_dt_report::reporter::{Report, Span};
|
||||
|
||||
use crate::cached_compiler::CachedCompiler;
|
||||
@@ -42,20 +46,28 @@ use crate::cached_compiler::CachedCompiler;
|
||||
static TEMP_DIR: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
|
||||
|
||||
/// this represents a single "test"; a mode, path and collection of cases.
|
||||
#[derive(Clone)]
|
||||
struct Test {
|
||||
metadata: Metadata,
|
||||
path: PathBuf,
|
||||
#[derive(Clone, Debug)]
|
||||
struct Test<'a> {
|
||||
metadata: &'a MetadataFile,
|
||||
metadata_file_path: &'a Path,
|
||||
mode: Mode,
|
||||
case_idx: CaseIdx,
|
||||
case: Case,
|
||||
case: &'a Case,
|
||||
}
|
||||
|
||||
/// This represents the results that we gather from running test cases.
|
||||
type CaseResult = Result<usize, anyhow::Error>;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = init_cli()?;
|
||||
let (args, _guard) = init_cli()?;
|
||||
info!(
|
||||
leader = args.leader.to_string(),
|
||||
follower = args.follower.to_string(),
|
||||
working_directory = %args.directory().display(),
|
||||
number_of_nodes = args.number_of_nodes,
|
||||
invalidate_compilation_cache = args.invalidate_compilation_cache,
|
||||
"Differential testing tool has been initialized"
|
||||
);
|
||||
|
||||
let body = async {
|
||||
for (corpus, tests) in collect_corpora(&args)? {
|
||||
@@ -77,15 +89,25 @@ fn main() -> anyhow::Result<()> {
|
||||
.block_on(body)
|
||||
}
|
||||
|
||||
fn init_cli() -> anyhow::Result<Arguments> {
|
||||
fn init_cli() -> anyhow::Result<(Arguments, WorkerGuard)> {
|
||||
let (writer, guard) = tracing_appender::non_blocking::NonBlockingBuilder::default()
|
||||
.lossy(false)
|
||||
// Assuming that each line contains 255 characters and that each character is one byte, then
|
||||
// this means that our buffer is about 4GBs large.
|
||||
.buffered_lines_limit(0x1000000)
|
||||
.thread_name("buffered writer")
|
||||
.finish(std::io::stdout());
|
||||
|
||||
let subscriber = FmtSubscriber::builder()
|
||||
.with_thread_ids(true)
|
||||
.with_thread_names(true)
|
||||
.with_writer(writer)
|
||||
.with_thread_ids(false)
|
||||
.with_thread_names(false)
|
||||
.with_env_filter(EnvFilter::from_default_env())
|
||||
.with_ansi(false)
|
||||
.pretty()
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
info!("Differential testing tool is starting");
|
||||
|
||||
let mut args = Arguments::parse();
|
||||
|
||||
@@ -103,19 +125,25 @@ fn init_cli() -> anyhow::Result<Arguments> {
|
||||
args.temp_dir = Some(&TEMP_DIR);
|
||||
}
|
||||
}
|
||||
tracing::info!("workdir: {}", args.directory().display());
|
||||
|
||||
Ok(args)
|
||||
Ok((args, guard))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "Collecting Corpora", skip_all)]
|
||||
fn collect_corpora(args: &Arguments) -> anyhow::Result<HashMap<Corpus, Vec<MetadataFile>>> {
|
||||
let mut corpora = HashMap::new();
|
||||
|
||||
for path in &args.corpus {
|
||||
let span = info_span!("Processing corpus file", path = %path.display());
|
||||
let _guard = span.enter();
|
||||
|
||||
let corpus = Corpus::try_from_path(path)?;
|
||||
tracing::info!("found corpus: {}", path.display());
|
||||
info!(
|
||||
name = corpus.name(),
|
||||
number_of_contained_paths = corpus.path_count(),
|
||||
"Deserialized corpus file"
|
||||
);
|
||||
let tests = corpus.enumerate_tests();
|
||||
tracing::info!("corpus '{}' contains {} tests", &corpus.name(), tests.len());
|
||||
corpora.insert(corpus, tests);
|
||||
}
|
||||
|
||||
@@ -133,7 +161,7 @@ where
|
||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
{
|
||||
let (report_tx, report_rx) = mpsc::unbounded_channel::<(Test, CaseResult)>();
|
||||
let (report_tx, report_rx) = mpsc::unbounded_channel::<(Test<'_>, CaseResult)>();
|
||||
|
||||
let tests = prepare_tests::<L, F>(args, metadata_files);
|
||||
let driver_task = start_driver_task::<L, F>(args, tests, span, report_tx).await?;
|
||||
@@ -144,111 +172,148 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_tests<L, F>(
|
||||
fn prepare_tests<'a, L, F>(
|
||||
args: &Arguments,
|
||||
metadata_files: &[MetadataFile],
|
||||
) -> impl Stream<Item = Test>
|
||||
metadata_files: &'a [MetadataFile],
|
||||
) -> impl Stream<Item = Test<'a>>
|
||||
where
|
||||
L: Platform,
|
||||
F: Platform,
|
||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
{
|
||||
metadata_files
|
||||
let filtered_tests = metadata_files
|
||||
.iter()
|
||||
.flat_map(
|
||||
|MetadataFile {
|
||||
path,
|
||||
content: metadata,
|
||||
}| {
|
||||
metadata
|
||||
.cases
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(move |(case_idx, case)| {
|
||||
metadata
|
||||
.solc_modes()
|
||||
.into_iter()
|
||||
.map(move |solc_mode| (path, metadata, case_idx, case, solc_mode))
|
||||
})
|
||||
.flat_map(|metadata_file| {
|
||||
metadata_file
|
||||
.cases
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(move |(case_idx, case)| (metadata_file, case_idx, case))
|
||||
})
|
||||
// Flatten over the modes, prefer the case modes over the metadata file modes.
|
||||
.flat_map(|(metadata_file, case_idx, case)| {
|
||||
case.modes
|
||||
.as_ref()
|
||||
.or(metadata_file.modes.as_ref())
|
||||
.map(|modes| ParsedMode::many_to_modes(modes.iter()).collect::<Vec<_>>())
|
||||
.unwrap_or(Mode::all().collect())
|
||||
.into_iter()
|
||||
.map(move |mode| (metadata_file, case_idx, case, mode))
|
||||
})
|
||||
.fold(
|
||||
IndexMap::<_, BTreeMap<_, Vec<_>>>::new(),
|
||||
|mut map, (metadata_file, case_idx, case, mode)| {
|
||||
let test = Test {
|
||||
metadata: metadata_file,
|
||||
metadata_file_path: metadata_file.metadata_file_path.as_path(),
|
||||
mode: mode.clone(),
|
||||
case_idx: CaseIdx::new(case_idx),
|
||||
case,
|
||||
};
|
||||
map.entry(mode)
|
||||
.or_default()
|
||||
.entry(test.case_idx)
|
||||
.or_default()
|
||||
.push(test);
|
||||
map
|
||||
},
|
||||
)
|
||||
.filter(
|
||||
|(metadata_file_path, metadata, _, _, _)| match metadata.ignore {
|
||||
Some(true) => {
|
||||
tracing::warn!(
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
"Ignoring metadata file"
|
||||
);
|
||||
false
|
||||
}
|
||||
Some(false) | None => true,
|
||||
},
|
||||
)
|
||||
.filter(
|
||||
|(metadata_file_path, _, case_idx, case, _)| match case.ignore {
|
||||
Some(true) => {
|
||||
tracing::warn!(
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
case_idx,
|
||||
case_name = ?case.name,
|
||||
"Ignoring case"
|
||||
);
|
||||
false
|
||||
}
|
||||
Some(false) | None => true,
|
||||
},
|
||||
)
|
||||
.filter(|(metadata_file_path, metadata, ..)| match metadata.required_evm_version {
|
||||
Some(evm_version_requirement) => {
|
||||
let is_allowed = evm_version_requirement
|
||||
.matches(&<L::Blockchain as revive_dt_node::Node>::evm_version())
|
||||
&& evm_version_requirement
|
||||
.matches(&<F::Blockchain as revive_dt_node::Node>::evm_version());
|
||||
.into_values()
|
||||
.flatten()
|
||||
.flat_map(|(_, value)| value.into_iter())
|
||||
// Filter the test out if the leader and follower do not support the target.
|
||||
.filter(|test| {
|
||||
let leader_support =
|
||||
<L::Blockchain as Node>::matches_target(test.metadata.targets.as_deref());
|
||||
let follower_support =
|
||||
<F::Blockchain as Node>::matches_target(test.metadata.targets.as_deref());
|
||||
let is_allowed = leader_support && follower_support;
|
||||
|
||||
if !is_allowed {
|
||||
debug!(
|
||||
file_path = %test.metadata.relative_path().display(),
|
||||
leader_support,
|
||||
follower_support,
|
||||
"Target is not supported, throwing metadata file out"
|
||||
)
|
||||
}
|
||||
|
||||
is_allowed
|
||||
})
|
||||
// Filter the test out if the metadata file is ignored.
|
||||
.filter(|test| {
|
||||
if test.metadata.ignore.is_some_and(|ignore| ignore) {
|
||||
debug!(
|
||||
file_path = %test.metadata.relative_path().display(),
|
||||
"Metadata file is ignored, throwing case out"
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
// Filter the test case if the case is ignored.
|
||||
.filter(|test| {
|
||||
if test.case.ignore.is_some_and(|ignore| ignore) {
|
||||
debug!(
|
||||
file_path = %test.metadata.relative_path().display(),
|
||||
case_idx = %test.case_idx,
|
||||
"Case is ignored, throwing case out"
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
// Filtering based on the EVM version compatibility
|
||||
.filter(|test| {
|
||||
if let Some(evm_version_requirement) = test.metadata.required_evm_version {
|
||||
let leader_compatibility = evm_version_requirement
|
||||
.matches(&<L::Blockchain as revive_dt_node::Node>::evm_version());
|
||||
let follower_compatibility = evm_version_requirement
|
||||
.matches(&<F::Blockchain as revive_dt_node::Node>::evm_version());
|
||||
let is_allowed = leader_compatibility && follower_compatibility;
|
||||
|
||||
if !is_allowed {
|
||||
tracing::warn!(
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
leader_evm_version = %<L::Blockchain as revive_dt_node::Node>::evm_version(),
|
||||
follower_evm_version = %<F::Blockchain as revive_dt_node::Node>::evm_version(),
|
||||
version_requirement = %evm_version_requirement,
|
||||
"Skipped test since the EVM version requirement was not fulfilled."
|
||||
debug!(
|
||||
file_path = %test.metadata.relative_path().display(),
|
||||
case_idx = %test.case_idx,
|
||||
leader_compatibility,
|
||||
follower_compatibility,
|
||||
"EVM Version is incompatible, throwing case out"
|
||||
);
|
||||
}
|
||||
|
||||
is_allowed
|
||||
}
|
||||
None => true,
|
||||
})
|
||||
.map(|(metadata_file_path, metadata, case_idx, case, solc_mode)| {
|
||||
Test {
|
||||
metadata: metadata.clone(),
|
||||
path: metadata_file_path.to_path_buf(),
|
||||
mode: solc_mode,
|
||||
case_idx: case_idx.into(),
|
||||
case: case.clone(),
|
||||
}
|
||||
})
|
||||
.map(async |test| test)
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.filter_map(async move |test| {
|
||||
// Check that both compilers support this test, else we skip it
|
||||
let is_supported = does_compiler_support_mode::<L>(args, &test.mode).await.ok().unwrap_or(false) &&
|
||||
does_compiler_support_mode::<F>(args, &test.mode).await.ok().unwrap_or(false);
|
||||
|
||||
// We filter_map to avoid needing to clone `test`, but return it as-is.
|
||||
if is_supported {
|
||||
Some(test)
|
||||
} else {
|
||||
tracing::warn!(
|
||||
metadata_file_path = %test.path.display(),
|
||||
case_idx = %test.case_idx,
|
||||
case_name = ?test.case.name,
|
||||
mode = %test.mode,
|
||||
"Skipping test as one or both of the compilers don't support it"
|
||||
);
|
||||
None
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
stream::iter(filtered_tests)
|
||||
// Filter based on the compiler compatibility
|
||||
.filter_map(move |test| async move {
|
||||
let leader_support = does_compiler_support_mode::<L>(args, &test.mode)
|
||||
.await
|
||||
.ok()
|
||||
.unwrap_or(false);
|
||||
let follower_support = does_compiler_support_mode::<F>(args, &test.mode)
|
||||
.await
|
||||
.ok()
|
||||
.unwrap_or(false);
|
||||
let is_allowed = leader_support && follower_support;
|
||||
|
||||
if !is_allowed {
|
||||
debug!(
|
||||
file_path = %test.metadata.relative_path().display(),
|
||||
leader_support,
|
||||
follower_support,
|
||||
"Compilers do not support this, throwing case out"
|
||||
);
|
||||
}
|
||||
|
||||
is_allowed.then_some(test)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -259,7 +324,7 @@ async fn does_compiler_support_mode<P: Platform>(
|
||||
let compiler_version_or_requirement = mode.compiler_version_to_use(args.solc.clone());
|
||||
let compiler_path =
|
||||
P::Compiler::get_compiler_executable(args, compiler_version_or_requirement).await?;
|
||||
let compiler_version = P::Compiler::new(compiler_path.clone()).version()?;
|
||||
let compiler_version = P::Compiler::new(compiler_path.clone()).version().await?;
|
||||
|
||||
Ok(P::Compiler::supports_mode(
|
||||
&compiler_version,
|
||||
@@ -268,11 +333,11 @@ async fn does_compiler_support_mode<P: Platform>(
|
||||
))
|
||||
}
|
||||
|
||||
async fn start_driver_task<L, F>(
|
||||
async fn start_driver_task<'a, L, F>(
|
||||
args: &Arguments,
|
||||
tests: impl Stream<Item = Test>,
|
||||
tests: impl Stream<Item = Test<'a>>,
|
||||
span: Span,
|
||||
report_tx: mpsc::UnboundedSender<(Test, CaseResult)>,
|
||||
report_tx: mpsc::UnboundedSender<(Test<'a>, CaseResult)>,
|
||||
) -> anyhow::Result<impl Future<Output = ()>>
|
||||
where
|
||||
L: Platform,
|
||||
@@ -310,19 +375,11 @@ where
|
||||
let leader_node = leader_nodes.round_robbin();
|
||||
let follower_node = follower_nodes.round_robbin();
|
||||
|
||||
let tracing_span = tracing::span!(
|
||||
Level::INFO,
|
||||
"Running driver",
|
||||
metadata_file_path = %test.path.display(),
|
||||
case_idx = ?test.case_idx,
|
||||
solc_mode = ?test.mode,
|
||||
);
|
||||
|
||||
let result = handle_case_driver::<L, F>(
|
||||
&test.path,
|
||||
&test.metadata,
|
||||
test.metadata_file_path,
|
||||
test.metadata,
|
||||
test.case_idx,
|
||||
&test.case,
|
||||
test.case,
|
||||
test.mode.clone(),
|
||||
args,
|
||||
cached_compiler,
|
||||
@@ -330,7 +387,6 @@ where
|
||||
follower_node,
|
||||
span,
|
||||
)
|
||||
.instrument(tracing_span)
|
||||
.await;
|
||||
|
||||
report_tx
|
||||
@@ -341,7 +397,7 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseResult)>) {
|
||||
async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test<'_>, CaseResult)>) {
|
||||
let start = Instant::now();
|
||||
|
||||
const GREEN: &str = "\x1B[32m";
|
||||
@@ -355,22 +411,25 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseR
|
||||
let mut failures = vec![];
|
||||
|
||||
// Wait for reports to come from our test runner. When the channel closes, this ends.
|
||||
let mut buf = BufWriter::new(stderr());
|
||||
while let Some((test, case_result)) = report_rx.recv().await {
|
||||
let case_name = test.case.name.as_deref().unwrap_or("unnamed_case");
|
||||
let case_idx = test.case_idx;
|
||||
let test_path = test.path.display();
|
||||
let test_path = test.metadata_file_path.display();
|
||||
let test_mode = test.mode.clone();
|
||||
|
||||
match case_result {
|
||||
Ok(_inputs) => {
|
||||
number_of_successes += 1;
|
||||
eprintln!(
|
||||
let _ = writeln!(
|
||||
buf,
|
||||
"{GREEN}Case Succeeded:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})"
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
number_of_failures += 1;
|
||||
eprintln!(
|
||||
let _ = writeln!(
|
||||
buf,
|
||||
"{RED}Case Failed:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})"
|
||||
);
|
||||
failures.push((test, err));
|
||||
@@ -378,29 +437,31 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseR
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!();
|
||||
let _ = writeln!(buf,);
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
// Now, log the failures with more complete errors at the bottom, like `cargo test` does, so
|
||||
// that we don't have to scroll through the entire output to find them.
|
||||
if !failures.is_empty() {
|
||||
eprintln!("{BOLD}Failures:{BOLD_RESET}\n");
|
||||
let _ = writeln!(buf, "{BOLD}Failures:{BOLD_RESET}\n");
|
||||
|
||||
for failure in failures {
|
||||
let (test, err) = failure;
|
||||
let case_name = test.case.name.as_deref().unwrap_or("unnamed_case");
|
||||
let case_idx = test.case_idx;
|
||||
let test_path = test.path.display();
|
||||
let test_path = test.metadata_file_path.display();
|
||||
let test_mode = test.mode.clone();
|
||||
|
||||
eprintln!(
|
||||
let _ = writeln!(
|
||||
buf,
|
||||
"---- {RED}Case Failed:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode}) ----\n\n{err}\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Summary at the end.
|
||||
eprintln!(
|
||||
let _ = writeln!(
|
||||
buf,
|
||||
"{} cases: {GREEN}{number_of_successes}{COLOUR_RESET} cases succeeded, {RED}{number_of_failures}{COLOUR_RESET} cases failed in {} seconds",
|
||||
number_of_successes + number_of_failures,
|
||||
elapsed.as_secs()
|
||||
@@ -408,9 +469,22 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseR
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[instrument(
|
||||
level = "info",
|
||||
name = "Handling Case"
|
||||
skip_all,
|
||||
fields(
|
||||
metadata_file_path = %metadata.relative_path().display(),
|
||||
mode = %mode,
|
||||
%case_idx,
|
||||
case_name = case.name.as_deref().unwrap_or("Unnamed Case"),
|
||||
leader_node = leader_node.id(),
|
||||
follower_node = follower_node.id(),
|
||||
)
|
||||
)]
|
||||
async fn handle_case_driver<L, F>(
|
||||
metadata_file_path: &Path,
|
||||
metadata: &Metadata,
|
||||
metadata: &MetadataFile,
|
||||
case_idx: CaseIdx,
|
||||
case: &Case,
|
||||
mode: Mode,
|
||||
@@ -426,16 +500,23 @@ where
|
||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
{
|
||||
let leader_pre_link_contracts = cached_compiler
|
||||
.compile_contracts::<L>(metadata, metadata_file_path, &mode, config, None)
|
||||
.await?
|
||||
.0
|
||||
.contracts;
|
||||
let follower_pre_link_contracts = cached_compiler
|
||||
.compile_contracts::<F>(metadata, metadata_file_path, &mode, config, None)
|
||||
.await?
|
||||
.0
|
||||
.contracts;
|
||||
let (
|
||||
(
|
||||
CompilerOutput {
|
||||
contracts: leader_pre_link_contracts,
|
||||
},
|
||||
_,
|
||||
),
|
||||
(
|
||||
CompilerOutput {
|
||||
contracts: follower_pre_link_contracts,
|
||||
},
|
||||
_,
|
||||
),
|
||||
) = try_join!(
|
||||
cached_compiler.compile_contracts::<L>(metadata, metadata_file_path, &mode, config, None),
|
||||
cached_compiler.compile_contracts::<F>(metadata, metadata_file_path, &mode, config, None)
|
||||
)?;
|
||||
|
||||
let mut leader_deployed_libraries = None::<HashMap<_, _>>;
|
||||
let mut follower_deployed_libraries = None::<HashMap<_, _>>;
|
||||
@@ -446,6 +527,8 @@ 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,
|
||||
@@ -465,24 +548,12 @@ where
|
||||
let leader_code = match alloy::hex::decode(leader_code) {
|
||||
Ok(code) => code,
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
?error,
|
||||
contract_source_path = library_source_path.display().to_string(),
|
||||
contract_ident = library_ident.as_ref(),
|
||||
"Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking"
|
||||
);
|
||||
anyhow::bail!("Failed to hex-decode the byte code {}", error)
|
||||
}
|
||||
};
|
||||
let follower_code = match alloy::hex::decode(follower_code) {
|
||||
Ok(code) => code,
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
?error,
|
||||
contract_source_path = library_source_path.display().to_string(),
|
||||
contract_ident = library_ident.as_ref(),
|
||||
"Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking"
|
||||
);
|
||||
anyhow::bail!("Failed to hex-decode the byte code {}", error)
|
||||
}
|
||||
};
|
||||
@@ -509,46 +580,28 @@ where
|
||||
follower_code,
|
||||
);
|
||||
|
||||
let leader_receipt = match leader_node.execute_transaction(leader_tx).await {
|
||||
Ok(receipt) => receipt,
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
node = std::any::type_name::<L>(),
|
||||
?error,
|
||||
"Contract deployment transaction failed."
|
||||
);
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let follower_receipt = match follower_node.execute_transaction(follower_tx).await {
|
||||
Ok(receipt) => receipt,
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
node = std::any::type_name::<F>(),
|
||||
?error,
|
||||
"Contract deployment transaction failed."
|
||||
);
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let (leader_receipt, follower_receipt) = try_join!(
|
||||
leader_node.execute_transaction(leader_tx),
|
||||
follower_node.execute_transaction(follower_tx)
|
||||
)?;
|
||||
|
||||
tracing::info!(
|
||||
debug!(
|
||||
?library_instance,
|
||||
library_address = ?leader_receipt.contract_address,
|
||||
"Deployed library to leader"
|
||||
);
|
||||
tracing::info!(
|
||||
debug!(
|
||||
?library_instance,
|
||||
library_address = ?follower_receipt.contract_address,
|
||||
"Deployed library to follower"
|
||||
);
|
||||
|
||||
let Some(leader_library_address) = leader_receipt.contract_address else {
|
||||
anyhow::bail!("Contract deployment didn't return an address");
|
||||
};
|
||||
let Some(follower_library_address) = follower_receipt.contract_address else {
|
||||
anyhow::bail!("Contract deployment didn't return an address");
|
||||
};
|
||||
let leader_library_address = leader_receipt
|
||||
.contract_address
|
||||
.context("Contract deployment didn't return an address")?;
|
||||
let follower_library_address = follower_receipt
|
||||
.contract_address
|
||||
.context("Contract deployment didn't return an address")?;
|
||||
|
||||
leader_deployed_libraries.get_or_insert_default().insert(
|
||||
library_instance.clone(),
|
||||
@@ -568,46 +621,59 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
let (leader_post_link_contracts, leader_compiler_version) = cached_compiler
|
||||
.compile_contracts::<L>(
|
||||
let (
|
||||
(
|
||||
CompilerOutput {
|
||||
contracts: leader_post_link_contracts,
|
||||
},
|
||||
leader_compiler_version,
|
||||
),
|
||||
(
|
||||
CompilerOutput {
|
||||
contracts: follower_post_link_contracts,
|
||||
},
|
||||
follower_compiler_version,
|
||||
),
|
||||
) = try_join!(
|
||||
cached_compiler.compile_contracts::<L>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
leader_deployed_libraries.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
let (follower_post_link_contracts, follower_compiler_version) = cached_compiler
|
||||
.compile_contracts::<F>(
|
||||
leader_deployed_libraries.as_ref()
|
||||
),
|
||||
cached_compiler.compile_contracts::<F>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
follower_deployed_libraries.as_ref(),
|
||||
follower_deployed_libraries.as_ref()
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
let leader_state = CaseState::<L>::new(
|
||||
leader_compiler_version,
|
||||
leader_post_link_contracts.contracts,
|
||||
leader_post_link_contracts,
|
||||
leader_deployed_libraries.unwrap_or_default(),
|
||||
);
|
||||
let follower_state = CaseState::<F>::new(
|
||||
follower_compiler_version,
|
||||
follower_post_link_contracts.contracts,
|
||||
follower_post_link_contracts,
|
||||
follower_deployed_libraries.unwrap_or_default(),
|
||||
);
|
||||
|
||||
let mut driver = CaseDriver::<L, F>::new(
|
||||
metadata,
|
||||
case,
|
||||
case_idx,
|
||||
leader_node,
|
||||
follower_node,
|
||||
leader_state,
|
||||
follower_state,
|
||||
);
|
||||
driver.execute().await
|
||||
driver
|
||||
.execute()
|
||||
.await
|
||||
.inspect(|steps_executed| info!(steps_executed, "Case succeeded"))
|
||||
}
|
||||
|
||||
async fn execute_corpus(
|
||||
@@ -657,7 +723,7 @@ async fn compile_corpus(
|
||||
let _ = cached_compiler
|
||||
.compile_contracts::<Geth>(
|
||||
metadata,
|
||||
metadata.path.as_path(),
|
||||
metadata.metadata_file_path.as_path(),
|
||||
&mode,
|
||||
config,
|
||||
None,
|
||||
@@ -668,7 +734,7 @@ async fn compile_corpus(
|
||||
let _ = cached_compiler
|
||||
.compile_contracts::<Kitchensink>(
|
||||
metadata,
|
||||
metadata.path.as_path(),
|
||||
metadata.metadata_file_path.as_path(),
|
||||
&mode,
|
||||
config,
|
||||
None,
|
||||
|
||||
Reference in New Issue
Block a user