Better logging and fix concurrency issues

This commit is contained in:
Omar Abdulla
2025-08-18 16:16:33 +03:00
parent fb3959d345
commit 609ececea6
24 changed files with 433 additions and 534 deletions
+4
View File
@@ -31,9 +31,13 @@ indexmap = { workspace = true }
once_cell = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
tracing-appender = { workspace = true }
tracing-subscriber = { workspace = true }
semver = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
temp-dir = { workspace = true }
tempfile = { workspace = true }
[lints]
workspace = true
+56 -114
View File
@@ -16,26 +16,22 @@ 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 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 tracing::{Instrument, info, info_span, instrument};
use crate::Platform;
@@ -77,38 +73,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)
@@ -119,14 +115,13 @@ where
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
self.handle_input_diff(execution_receipt, node).await
}
#[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 +132,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 +147,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 +165,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 +191,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 +209,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,
@@ -275,6 +254,7 @@ where
})
}
#[instrument(level = "info", skip_all)]
fn handle_input_variable_assignment(
&mut self,
input: &Input,
@@ -305,6 +285,7 @@ where
Ok(())
}
#[instrument(level = "info", skip_all)]
async fn handle_input_expectations(
&mut self,
input: &Input,
@@ -353,6 +334,7 @@ where
Ok(())
}
#[instrument(level = "info", skip_all)]
async fn handle_input_expectation_item(
&mut self,
execution_receipt: &TransactionReceipt,
@@ -495,9 +477,9 @@ where
Ok(())
}
#[instrument(level = "info", skip_all)]
async fn handle_input_diff(
&mut self,
_: CaseIdx,
execution_receipt: TransactionReceipt,
node: &T::Blockchain,
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
@@ -515,6 +497,7 @@ where
Ok((execution_receipt, trace, diff))
}
#[instrument(level = "info", skip_all)]
pub async fn handle_balance_assertion_contract_deployment(
&mut self,
metadata: &Metadata,
@@ -540,6 +523,7 @@ where
Ok(())
}
#[instrument(level = "info", skip_all)]
pub async fn handle_balance_assertion_execution(
&mut self,
BalanceAssertion {
@@ -575,6 +559,7 @@ where
Ok(())
}
#[instrument(level = "info", skip_all)]
pub async fn handle_storage_empty_assertion_contract_deployment(
&mut self,
metadata: &Metadata,
@@ -600,6 +585,7 @@ where
Ok(())
}
#[instrument(level = "info", skip_all)]
pub async fn handle_storage_empty_assertion_execution(
&mut self,
StorageEmptyAssertion {
@@ -661,7 +647,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
@@ -674,11 +659,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
@@ -727,7 +707,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!(
@@ -754,7 +733,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>,
@@ -770,7 +748,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>,
@@ -779,7 +756,6 @@ where
Self {
metadata,
case,
case_idx: case_idx.into(),
leader_node,
follower_node,
leader_state,
@@ -787,79 +763,45 @@ 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 = self
.leader_state
.handle_step(self.metadata, self.case_idx, &step, self.leader_node)
.instrument(tracing_span.clone())
.handle_step(self.metadata, &step, self.leader_node)
.instrument(info_span!(
"Handling Step",
%step_idx,
target = "Leader",
))
.await?;
let follower_step_output = self
.follower_state
.handle_step(self.metadata, self.case_idx, &step, self.follower_node)
.instrument(tracing_span)
.handle_step(self.metadata, &step, self.follower_node)
.instrument(info_span!(
"Handling Step",
%step_idx,
target = "Follower",
))
.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) => {}
+133 -107
View File
@@ -20,7 +20,8 @@ use indexmap::IndexMap;
use revive_dt_node_interaction::EthereumNode;
use temp_dir::TempDir;
use tokio::sync::mpsc;
use tracing::{Instrument, Level};
use tracing::{debug, info, info_span, instrument};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{EnvFilter, FmtSubscriber};
use revive_dt_common::types::Mode;
@@ -34,10 +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;
@@ -45,8 +46,9 @@ 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, Debug)]
struct Test<'a> {
metadata: &'a Metadata,
metadata: &'a MetadataFile,
metadata_file_path: &'a Path,
mode: Mode,
case_idx: CaseIdx,
@@ -57,7 +59,15 @@ struct Test<'a> {
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)? {
@@ -79,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();
@@ -105,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);
}
@@ -156,7 +182,7 @@ where
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
{
let flattened_tests = metadata_files
let filtered_tests = metadata_files
.iter()
.flat_map(|metadata_file| {
metadata_file
@@ -165,14 +191,13 @@ where
.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)| {
let modes = match (metadata_file.modes.as_ref(), case.modes.as_ref()) {
(Some(_), Some(modes)) | (None, Some(modes)) | (Some(modes), None) => {
ParsedMode::many_to_modes(modes.iter()).collect::<Vec<_>>()
}
(None, None) => Mode::all().collect(),
};
modes
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))
})
@@ -180,8 +205,8 @@ where
IndexMap::<_, BTreeMap<_, Vec<_>>>::new(),
|mut map, (metadata_file, case_idx, case, mode)| {
let test = Test {
metadata: &metadata_file.content,
metadata_file_path: metadata_file.path.as_path(),
metadata: metadata_file,
metadata_file_path: metadata_file.metadata_file_path.as_path(),
mode: mode.clone(),
case_idx: CaseIdx::new(case_idx),
case,
@@ -193,18 +218,35 @@ where
.push(test);
map
},
);
let filtered_tests = flattened_tests
)
.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) {
tracing::warn!(
metadata_file_path = %test.metadata_file_path.display(),
"Ignoring test case since the metadata file is ignored"
debug!(
file_path = %test.metadata.relative_path().display(),
"Metadata file is ignored, throwing case out"
);
false
} else {
@@ -214,9 +256,10 @@ where
// Filter the test case if the case is ignored.
.filter(|test| {
if test.case.ignore.is_some_and(|ignore| ignore) {
tracing::warn!(
metadata_file_path = %test.metadata_file_path.display(),
"Ignoring test case since the case file is ignored"
debug!(
file_path = %test.metadata.relative_path().display(),
case_idx = %test.case_idx,
"Case is ignored, throwing case out"
);
false
} else {
@@ -226,18 +269,19 @@ where
// Filtering based on the EVM version compatibility
.filter(|test| {
if let Some(evm_version_requirement) = test.metadata.required_evm_version {
let is_allowed = evm_version_requirement
.matches(&<L::Blockchain as revive_dt_node::Node>::evm_version())
&& evm_version_requirement
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 = %test.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"
);
}
@@ -249,31 +293,27 @@ where
stream::iter(filtered_tests)
// Filter based on the compiler compatibility
.filter_map(|test| {
let args = args.clone();
.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;
async move {
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);
if !is_supported {
tracing::warn!(
metadata_file_path = %test.metadata_file_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"
);
}
is_supported.then_some(test)
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)
})
}
@@ -335,14 +375,6 @@ 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.metadata_file_path.display(),
case_idx = ?test.case_idx,
solc_mode = ?test.mode,
);
let result = handle_case_driver::<L, F>(
test.metadata_file_path,
test.metadata,
@@ -355,7 +387,6 @@ where
follower_node,
span,
)
.instrument(tracing_span)
.await;
report_tx
@@ -438,9 +469,22 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test<'_>, C
}
#[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,
@@ -476,6 +520,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,
@@ -495,24 +541,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)
}
};
@@ -542,43 +576,33 @@ where
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);
}
};
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(),
@@ -631,13 +655,15 @@ where
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(
@@ -687,7 +713,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,
@@ -698,7 +724,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,