Add a Quick Run Script (#152)

* Add a quick run script

* Add more context to errors

* Fix the issue with corpus directory canonicalization

* Update the quick run script

* Edit the runner script

* Support specifying the path of the polkadot sdk
This commit is contained in:
Omar
2025-08-26 00:03:28 +03:00
committed by GitHub
parent eb264fcc7b
commit 60328cd493
22 changed files with 730 additions and 246 deletions
+36 -15
View File
@@ -14,7 +14,7 @@ use revive_dt_config::Arguments;
use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata};
use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address};
use anyhow::{Error, Result};
use anyhow::{Context as _, Error, Result};
use once_cell::sync::Lazy;
use semver::Version;
use serde::{Deserialize, Serialize};
@@ -29,7 +29,10 @@ impl CachedCompiler {
pub async fn new(path: impl AsRef<Path>, invalidate_cache: bool) -> Result<Self> {
let mut cache = ArtifactsCache::new(path);
if invalidate_cache {
cache = cache.with_invalidated_cache().await?;
cache = cache
.with_invalidated_cache()
.await
.context("Failed to invalidate compilation cache directory")?;
}
Ok(Self(cache))
}
@@ -76,9 +79,8 @@ impl CachedCompiler {
compiler_version_or_requirement,
)
.await
.inspect_err(|err| {
compilation_failure_report_callback(None, None, None, err.to_string())
})?;
.inspect_err(|err| compilation_failure_report_callback(None, None, None, err.to_string()))
.context("Failed to obtain compiler executable path")?;
let compiler_version = <P::Compiler as SolidityCompiler>::new(compiler_path.clone())
.version()
.await
@@ -89,7 +91,8 @@ impl CachedCompiler {
None,
err.to_string(),
)
})?;
})
.context("Failed to query compiler version")?;
let cache_key = CacheKey {
platform_key: P::config_id().to_string(),
@@ -104,10 +107,14 @@ impl CachedCompiler {
let compilation_success_report_callback = compilation_success_report_callback.clone();
async move {
compile_contracts::<P>(
metadata.directory()?,
metadata
.directory()
.context("Failed to get metadata directory while preparing compilation")?,
compiler_path,
compiler_version,
metadata.files_to_compile()?,
metadata
.files_to_compile()
.context("Failed to enumerate files to compile from metadata")?,
mode,
deployed_libraries,
compilation_success_report_callback,
@@ -131,7 +138,10 @@ impl CachedCompiler {
Some(_) => {
debug!("Deployed libraries defined, recompilation must take place");
debug!("Cache miss");
compilation_callback().await?.compiler_output
compilation_callback()
.await
.context("Compilation callback for deployed libraries failed")?
.compiler_output
}
// If no deployed libraries are specified then we can follow the cached flow and attempt
// to lookup the compilation artifacts in the cache.
@@ -167,7 +177,12 @@ impl CachedCompiler {
);
cache_value.compiler_output
}
None => compilation_callback().await?.compiler_output,
None => {
compilation_callback()
.await
.context("Compilation callback failed (cache miss path)")?
.compiler_output
}
}
}
};
@@ -247,7 +262,8 @@ async fn compile_contracts<P: Platform>(
Some(compiler_input.clone()),
err.to_string(),
)
})?;
})
.context("Failed to configure compiler with sources and options")?;
compilation_success_report_callback(
compiler_version,
compiler_path.as_ref().to_path_buf(),
@@ -273,15 +289,20 @@ impl ArtifactsCache {
pub async fn with_invalidated_cache(self) -> Result<Self> {
cacache::clear(self.path.as_path())
.await
.map_err(Into::<Error>::into)?;
.map_err(Into::<Error>::into)
.with_context(|| format!("Failed to clear cache at {}", self.path.display()))?;
Ok(self)
}
#[instrument(level = "debug", skip_all, err)]
pub async fn insert(&self, key: &CacheKey, value: &CacheValue) -> Result<()> {
let key = bson::to_vec(key)?;
let value = bson::to_vec(value)?;
cacache::write(self.path.as_path(), key.encode_hex(), value).await?;
let key = bson::to_vec(key).context("Failed to serialize cache key (bson)")?;
let value = bson::to_vec(value).context("Failed to serialize cache value (bson)")?;
cacache::write(self.path.as_path(), key.encode_hex(), value)
.await
.with_context(|| {
format!("Failed to write cache entry under {}", self.path.display())
})?;
Ok(())
}
+41 -20
View File
@@ -86,18 +86,22 @@ where
) -> anyhow::Result<StepOutput> {
match step {
Step::FunctionCall(input) => {
let (receipt, geth_trace, diff_mode) =
self.handle_input(metadata, input, node).await?;
let (receipt, geth_trace, diff_mode) = self
.handle_input(metadata, input, node)
.await
.context("Failed to handle function call step")?;
Ok(StepOutput::FunctionCall(receipt, geth_trace, diff_mode))
}
Step::BalanceAssertion(balance_assertion) => {
self.handle_balance_assertion(metadata, balance_assertion, node)
.await?;
.await
.context("Failed to handle balance assertion step")?;
Ok(StepOutput::BalanceAssertion)
}
Step::StorageEmptyAssertion(storage_empty) => {
self.handle_storage_empty(metadata, storage_empty, node)
.await?;
.await
.context("Failed to handle storage empty assertion step")?;
Ok(StepOutput::StorageEmptyAssertion)
}
}
@@ -113,18 +117,23 @@ where
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
let deployment_receipts = self
.handle_input_contract_deployment(metadata, input, node)
.await?;
.await
.context("Failed during contract deployment phase of input handling")?;
let execution_receipt = self
.handle_input_execution(input, deployment_receipts, node)
.await?;
.await
.context("Failed during transaction execution phase of input handling")?;
let tracing_result = self
.handle_input_call_frame_tracing(&execution_receipt, node)
.await?;
self.handle_input_variable_assignment(input, &tracing_result)?;
.await
.context("Failed during callframe tracing phase of input handling")?;
self.handle_input_variable_assignment(input, &tracing_result)
.context("Failed to assign variables from callframe output")?;
let (_, (geth_trace, diff_mode)) = try_join!(
self.handle_input_expectations(input, &execution_receipt, node, &tracing_result),
self.handle_input_diff(&execution_receipt, node)
)?;
)
.context("Failed while evaluating expectations and diffs in parallel")?;
Ok((execution_receipt, geth_trace, diff_mode))
}
@@ -136,9 +145,11 @@ where
node: &T::Blockchain,
) -> anyhow::Result<()> {
self.handle_balance_assertion_contract_deployment(metadata, balance_assertion, node)
.await?;
.await
.context("Failed to deploy contract for balance assertion")?;
self.handle_balance_assertion_execution(balance_assertion, node)
.await?;
.await
.context("Failed to execute balance assertion")?;
Ok(())
}
@@ -150,9 +161,11 @@ where
node: &T::Blockchain,
) -> anyhow::Result<()> {
self.handle_storage_empty_assertion_contract_deployment(metadata, storage_empty, node)
.await?;
.await
.context("Failed to deploy contract for storage empty assertion")?;
self.handle_storage_empty_assertion_execution(storage_empty, node)
.await?;
.await
.context("Failed to execute storage empty assertion")?;
Ok(())
}
@@ -191,7 +204,8 @@ where
value,
node,
)
.await?
.await
.context("Failed to get or deploy contract instance during input execution")?
{
receipts.insert(instance.clone(), receipt);
}
@@ -213,7 +227,7 @@ where
// lookup the transaction receipt in this case and continue on.
Method::Deployer => deployment_receipts
.remove(&input.instance)
.context("Failed to find deployment receipt"),
.context("Failed to find deployment receipt for constructor call"),
Method::Fallback | Method::FunctionName(_) => {
let tx = match input
.legacy_transaction(node, self.default_resolution_context())
@@ -385,7 +399,8 @@ where
let actual = &tracing_result.output.as_ref().unwrap_or_default();
if !expected
.is_equivalent(actual, resolver, resolution_context)
.await?
.await
.context("Failed to resolve calldata equivalence for return data assertion")?
{
tracing::error!(
?execution_receipt,
@@ -448,7 +463,8 @@ where
let expected = Calldata::new_compound([expected]);
if !expected
.is_equivalent(&actual.0, resolver, resolution_context)
.await?
.await
.context("Failed to resolve event topic equivalence")?
{
tracing::error!(
event_idx,
@@ -468,7 +484,8 @@ where
let actual = &actual_event.data().data;
if !expected
.is_equivalent(&actual.0, resolver, resolution_context)
.await?
.await
.context("Failed to resolve event value equivalence")?
{
tracing::error!(
event_idx,
@@ -501,8 +518,12 @@ where
let trace = node
.trace_transaction(execution_receipt, trace_options)
.await?;
let diff = node.state_diff(execution_receipt).await?;
.await
.context("Failed to obtain geth prestate tracer output")?;
let diff = node
.state_diff(execution_receipt)
.await
.context("Failed to obtain state diff for transaction")?;
Ok((trace, diff))
}
+32 -13
View File
@@ -60,7 +60,7 @@ struct Test<'a> {
}
fn main() -> anyhow::Result<()> {
let (args, _guard) = init_cli()?;
let (args, _guard) = init_cli().context("Failed to initialize CLI and tracing subscriber")?;
info!(
leader = args.leader.to_string(),
follower = args.follower.to_string(),
@@ -74,7 +74,8 @@ fn main() -> anyhow::Result<()> {
let number_of_threads = args.number_of_threads;
let body = async move {
let tests = collect_corpora(&args)?
let tests = collect_corpora(&args)
.context("Failed to collect corpus files from provided arguments")?
.into_iter()
.inspect(|(corpus, _)| {
reporter
@@ -96,7 +97,9 @@ fn main() -> anyhow::Result<()> {
Some(platform) => {
compile_corpus(&args, &tests, platform, reporter, report_aggregator_task).await
}
None => execute_corpus(&args, &tests, reporter, report_aggregator_task).await?,
None => execute_corpus(&args, &tests, reporter, report_aggregator_task)
.await
.context("Failed to execute corpus")?,
}
Ok(())
};
@@ -183,7 +186,9 @@ where
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
{
let tests = prepare_tests::<L, F>(args, metadata_files, reporter.clone());
let driver_task = start_driver_task::<L, F>(args, tests).await?;
let driver_task = start_driver_task::<L, F>(args, tests)
.await
.context("Failed to start driver task")?;
let cli_reporting_task = start_cli_reporting_task(reporter);
let (_, _, rtn) = tokio::join!(cli_reporting_task, driver_task, report_aggregator_task);
@@ -419,9 +424,13 @@ async fn does_compiler_support_mode<P: Platform>(
mode: &Mode,
) -> anyhow::Result<bool> {
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().await?;
let compiler_path = P::Compiler::get_compiler_executable(args, compiler_version_or_requirement)
.await
.context("Failed to obtain compiler executable path")?;
let compiler_version = P::Compiler::new(compiler_path.clone())
.version()
.await
.context("Failed to query compiler version")?;
Ok(P::Compiler::supports_mode(
&compiler_version,
@@ -442,15 +451,20 @@ where
{
info!("Starting driver task");
let leader_nodes = Arc::new(NodePool::<L::Blockchain>::new(args)?);
let follower_nodes = Arc::new(NodePool::<F::Blockchain>::new(args)?);
let leader_nodes = Arc::new(
NodePool::<L::Blockchain>::new(args).context("Failed to initialize leader node pool")?,
);
let follower_nodes = Arc::new(
NodePool::<F::Blockchain>::new(args).context("Failed to initialize follower node pool")?,
);
let number_concurrent_tasks = args.number_of_concurrent_tasks();
let cached_compiler = Arc::new(
CachedCompiler::new(
args.directory().join("compilation_cache"),
args.invalidate_compilation_cache,
)
.await?,
.await
.context("Failed to initialize cached compiler")?,
);
Ok(tests.for_each_concurrent(
@@ -695,11 +709,15 @@ where
.expect("Can't fail")
}
)
)?;
)
.context("Failed to compile pre-link contracts for leader/follower in parallel")?;
let mut leader_deployed_libraries = None::<HashMap<_, _>>;
let mut follower_deployed_libraries = None::<HashMap<_, _>>;
let mut contract_sources = test.metadata.contract_sources()?;
let mut contract_sources = test
.metadata
.contract_sources()
.context("Failed to retrieve contract sources from metadata")?;
for library_instance in test
.metadata
.libraries
@@ -890,7 +908,8 @@ where
.expect("Can't fail")
}
)
)?;
)
.context("Failed to compile post-link contracts for leader/follower in parallel")?;
let leader_state = CaseState::<L>::new(
leader_compiler_version,