Compare commits

..

3 Commits

Author SHA1 Message Date
Omar Abdulla 9598bdbe35 Print to the stderr and print logs to stdout 2025-08-03 11:03:00 +03:00
Omar Abdulla 1b9eff1205 Add some waiting period to the printing task 2025-08-02 22:07:39 +03:00
Omar Abdulla c0ca716163 Added basic console reporting 2025-08-02 22:01:43 +03:00
16 changed files with 175 additions and 419 deletions
Generated
-1
View File
@@ -4030,7 +4030,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"semver 1.0.26", "semver 1.0.26",
"tokio",
] ]
[[package]] [[package]]
-1
View File
@@ -11,4 +11,3 @@ rust-version.workspace = true
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
semver = { workspace = true } semver = { workspace = true }
tokio = { workspace = true, default-features = false, features = ["time"] }
-3
View File
@@ -1,3 +0,0 @@
mod poll;
pub use poll::*;
-69
View File
@@ -1,69 +0,0 @@
use std::ops::ControlFlow;
use std::time::Duration;
use anyhow::{Result, anyhow};
const EXPONENTIAL_BACKOFF_MAX_WAIT_DURATION: Duration = Duration::from_secs(60);
/// A function that polls for a fallible future for some period of time and errors if it fails to
/// get a result after polling.
///
/// Given a future that returns a [`Result<ControlFlow<O, ()>>`], this function calls the future
/// repeatedly (with some wait period) until the future returns a [`ControlFlow::Break`] or until it
/// returns an [`Err`] in which case the function stops polling and returns the error.
///
/// If the future keeps returning [`ControlFlow::Continue`] and fails to return a [`Break`] within
/// the permitted polling duration then this function returns an [`Err`]
///
/// [`Break`]: ControlFlow::Break
/// [`Continue`]: ControlFlow::Continue
pub async fn poll<F, O>(
polling_duration: Duration,
polling_wait_behavior: PollingWaitBehavior,
mut future: impl FnMut() -> F,
) -> Result<O>
where
F: Future<Output = Result<ControlFlow<O, ()>>>,
{
let mut retries = 0;
let mut total_wait_duration = Duration::ZERO;
let max_allowed_wait_duration = polling_duration;
loop {
if total_wait_duration >= max_allowed_wait_duration {
break Err(anyhow!(
"Polling failed after {} retries and a total of {:?} of wait time",
retries,
total_wait_duration
));
}
match future().await? {
ControlFlow::Continue(()) => {
let next_wait_duration = match polling_wait_behavior {
PollingWaitBehavior::Constant(duration) => duration,
PollingWaitBehavior::ExponentialBackoff => {
Duration::from_secs(2u64.pow(retries))
.min(EXPONENTIAL_BACKOFF_MAX_WAIT_DURATION)
}
};
let next_wait_duration =
next_wait_duration.min(max_allowed_wait_duration - total_wait_duration);
total_wait_duration += next_wait_duration;
retries += 1;
tokio::time::sleep(next_wait_duration).await;
}
ControlFlow::Break(output) => {
break Ok(output);
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum PollingWaitBehavior {
Constant(Duration),
#[default]
ExponentialBackoff,
}
-1
View File
@@ -2,7 +2,6 @@
//! the workspace can benefit from. //! the workspace can benefit from.
pub mod fs; pub mod fs;
pub mod futures;
pub mod iterators; pub mod iterators;
pub mod macros; pub mod macros;
pub mod types; pub mod types;
-21
View File
@@ -238,25 +238,4 @@ mod test {
Version::new(0, 7, 6) Version::new(0, 7, 6)
) )
} }
#[tokio::test]
async fn compiler_version_can_be_obtained1() {
// Arrange
let args = Arguments::default();
println!("Getting compiler path");
let path = Solc::get_compiler_executable(&args, Version::new(0, 4, 21))
.await
.unwrap();
println!("Got compiler path");
let compiler = Solc::new(path);
// Act
let version = compiler.version();
// Assert
assert_eq!(
version.expect("Failed to get version"),
Version::new(0, 4, 21)
)
}
} }
+1 -1
View File
@@ -77,7 +77,7 @@ pub struct Arguments {
/// This argument controls which private keys the nodes should have access to and be added to /// This argument controls which private keys the nodes should have access to and be added to
/// its wallet signers. With a value of N, private keys (0, N] will be added to the signer set /// its wallet signers. With a value of N, private keys (0, N] will be added to the signer set
/// of the node. /// of the node.
#[arg(long = "private-keys-count", default_value_t = 100_000)] #[arg(long = "private-keys-count", default_value_t = 15_000)]
pub private_keys_to_add: usize, pub private_keys_to_add: usize,
/// The differential testing leader node implementation. /// The differential testing leader node implementation.
+28 -97
View File
@@ -4,11 +4,9 @@ use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::path::PathBuf; use std::path::PathBuf;
use alloy::eips::BlockNumberOrTag;
use alloy::hex;
use alloy::json_abi::JsonAbi; use alloy::json_abi::JsonAbi;
use alloy::network::{Ethereum, TransactionBuilder}; use alloy::network::{Ethereum, TransactionBuilder};
use alloy::primitives::{BlockNumber, U256}; use alloy::primitives::U256;
use alloy::rpc::types::TransactionReceipt; use alloy::rpc::types::TransactionReceipt;
use alloy::rpc::types::trace::geth::{ use alloy::rpc::types::trace::geth::{
CallFrame, GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, GethTrace, CallFrame, GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, GethTrace,
@@ -23,7 +21,6 @@ use alloy::{
}; };
use anyhow::Context; use anyhow::Context;
use indexmap::IndexMap; use indexmap::IndexMap;
use revive_dt_format::traits::ResolverApi;
use semver::Version; use semver::Version;
use revive_dt_format::case::{Case, CaseIdx}; use revive_dt_format::case::{Case, CaseIdx};
@@ -32,7 +29,6 @@ use revive_dt_format::metadata::{ContractInstance, ContractPathAndIdent};
use revive_dt_format::{input::Input, metadata::Metadata}; use revive_dt_format::{input::Input, metadata::Metadata};
use revive_dt_node::Node; use revive_dt_node::Node;
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use tracing::Instrument;
use crate::Platform; use crate::Platform;
@@ -88,13 +84,7 @@ where
.handle_input_call_frame_tracing(&execution_receipt, node) .handle_input_call_frame_tracing(&execution_receipt, node)
.await?; .await?;
self.handle_input_variable_assignment(input, &tracing_result)?; self.handle_input_variable_assignment(input, &tracing_result)?;
let resolver = BlockPinnedResolver::<'_, T> { self.handle_input_expectations(input, &execution_receipt, node, &tracing_result)
node,
block_number: execution_receipt
.block_number
.context("Transaction was not included in a block")?,
};
self.handle_input_expectations(input, &execution_receipt, &resolver, &tracing_result)
.await?; .await?;
self.handle_input_diff(case_idx, execution_receipt, node) self.handle_input_diff(case_idx, execution_receipt, node)
.await .await
@@ -243,11 +233,6 @@ where
) { ) {
let value = U256::from_be_slice(output_word); let value = U256::from_be_slice(output_word);
self.variables.insert(variable_name.clone(), value); self.variables.insert(variable_name.clone(), value);
tracing::info!(
variable_name,
variable_value = hex::encode(value.to_be_bytes::<32>()),
"Assigned variable"
);
} }
Ok(()) Ok(())
@@ -257,7 +242,7 @@ where
&mut self, &mut self,
input: &Input, input: &Input,
execution_receipt: &TransactionReceipt, execution_receipt: &TransactionReceipt,
resolver: &impl ResolverApi, node: &T::Blockchain,
tracing_result: &CallFrame, tracing_result: &CallFrame,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let span = tracing::info_span!("Handling input expectations"); let span = tracing::info_span!("Handling input expectations");
@@ -294,7 +279,7 @@ where
for expectation in expectations.iter() { for expectation in expectations.iter() {
self.handle_input_expectation_item( self.handle_input_expectation_item(
execution_receipt, execution_receipt,
resolver, node,
expectation, expectation,
tracing_result, tracing_result,
) )
@@ -307,7 +292,7 @@ where
async fn handle_input_expectation_item( async fn handle_input_expectation_item(
&mut self, &mut self,
execution_receipt: &TransactionReceipt, execution_receipt: &TransactionReceipt,
resolver: &impl ResolverApi, node: &T::Blockchain,
expectation: &ExpectedOutput, expectation: &ExpectedOutput,
tracing_result: &CallFrame, tracing_result: &CallFrame,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -319,6 +304,7 @@ where
let deployed_contracts = &mut self.deployed_contracts; let deployed_contracts = &mut self.deployed_contracts;
let variables = &mut self.variables; let variables = &mut self.variables;
let chain_state_provider = node;
// Handling the receipt state assertion. // Handling the receipt state assertion.
let expected = !expectation.exception; let expected = !expectation.exception;
@@ -341,7 +327,12 @@ where
let expected = expected_calldata; let expected = expected_calldata;
let actual = &tracing_result.output.as_ref().unwrap_or_default(); let actual = &tracing_result.output.as_ref().unwrap_or_default();
if !expected if !expected
.is_equivalent(actual, deployed_contracts, &*variables, resolver) .is_equivalent(
actual,
deployed_contracts,
&*variables,
chain_state_provider,
)
.await? .await?
{ {
tracing::error!( tracing::error!(
@@ -367,16 +358,14 @@ where
} }
// Handling the events assertion. // Handling the events assertion.
for (event_idx, (expected_event, actual_event)) in expected_events for (expected_event, actual_event) in
.iter() expected_events.iter().zip(execution_receipt.logs())
.zip(execution_receipt.logs())
.enumerate()
{ {
// Handling the emitter assertion. // Handling the emitter assertion.
if let Some(ref expected_address) = expected_event.address { if let Some(ref expected_address) = expected_event.address {
let expected = Address::from_slice( let expected = Address::from_slice(
Calldata::new_compound([expected_address]) Calldata::new_compound([expected_address])
.calldata(deployed_contracts, &*variables, resolver) .calldata(deployed_contracts, &*variables, node)
.await? .await?
.get(12..32) .get(12..32)
.expect("Can't fail"), .expect("Can't fail"),
@@ -384,7 +373,6 @@ where
let actual = actual_event.address(); let actual = actual_event.address();
if actual != expected { if actual != expected {
tracing::error!( tracing::error!(
event_idx,
%expected, %expected,
%actual, %actual,
"Event emitter assertion failed", "Event emitter assertion failed",
@@ -404,11 +392,15 @@ where
{ {
let expected = Calldata::new_compound([expected]); let expected = Calldata::new_compound([expected]);
if !expected if !expected
.is_equivalent(&actual.0, deployed_contracts, &*variables, resolver) .is_equivalent(
&actual.0,
deployed_contracts,
&*variables,
chain_state_provider,
)
.await? .await?
{ {
tracing::error!( tracing::error!(
event_idx,
?execution_receipt, ?execution_receipt,
?expected, ?expected,
?actual, ?actual,
@@ -424,11 +416,15 @@ where
let expected = &expected_event.values; let expected = &expected_event.values;
let actual = &actual_event.data().data; let actual = &actual_event.data().data;
if !expected if !expected
.is_equivalent(&actual.0, deployed_contracts, &*variables, resolver) .is_equivalent(
&actual.0,
deployed_contracts,
&*variables,
chain_state_provider,
)
.await? .await?
{ {
tracing::error!( tracing::error!(
event_idx,
?execution_receipt, ?execution_receipt,
?expected, ?expected,
?actual, ?actual,
@@ -653,16 +649,15 @@ where
let mut inputs_executed = 0; let mut inputs_executed = 0;
for (input_idx, input) in self.case.inputs_iterator().enumerate() { for (input_idx, input) in self.case.inputs_iterator().enumerate() {
let tracing_span = tracing::info_span!("Handling input", input_idx); let tracing_span = tracing::info_span!("Handling input", input_idx);
let _guard = tracing_span.enter();
let (leader_receipt, _, leader_diff) = self let (leader_receipt, _, leader_diff) = self
.leader_state .leader_state
.handle_input(self.metadata, self.case_idx, &input, self.leader_node) .handle_input(self.metadata, self.case_idx, &input, self.leader_node)
.instrument(tracing_span.clone())
.await?; .await?;
let (follower_receipt, _, follower_diff) = self let (follower_receipt, _, follower_diff) = self
.follower_state .follower_state
.handle_input(self.metadata, self.case_idx, &input, self.follower_node) .handle_input(self.metadata, self.case_idx, &input, self.follower_node)
.instrument(tracing_span)
.await?; .await?;
if leader_diff == follower_diff { if leader_diff == follower_diff {
@@ -685,67 +680,3 @@ where
Ok(inputs_executed) Ok(inputs_executed)
} }
} }
pub struct BlockPinnedResolver<'a, T: Platform> {
block_number: BlockNumber,
node: &'a T::Blockchain,
}
impl<'a, T: Platform> ResolverApi for BlockPinnedResolver<'a, T> {
async fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> {
self.node.chain_id().await
}
async fn block_gas_limit(&self, number: BlockNumberOrTag) -> anyhow::Result<u128> {
self.node
.block_gas_limit(self.resolve_block_number_or_tag(number))
.await
}
async fn block_coinbase(&self, number: BlockNumberOrTag) -> anyhow::Result<Address> {
self.node
.block_coinbase(self.resolve_block_number_or_tag(number))
.await
}
async fn block_difficulty(&self, number: BlockNumberOrTag) -> anyhow::Result<U256> {
self.node
.block_difficulty(self.resolve_block_number_or_tag(number))
.await
}
async fn block_hash(
&self,
number: BlockNumberOrTag,
) -> anyhow::Result<alloy::primitives::BlockHash> {
self.node
.block_hash(self.resolve_block_number_or_tag(number))
.await
}
async fn block_timestamp(
&self,
number: BlockNumberOrTag,
) -> anyhow::Result<alloy::primitives::BlockTimestamp> {
self.node
.block_timestamp(self.resolve_block_number_or_tag(number))
.await
}
async fn last_block_number(&self) -> anyhow::Result<alloy::primitives::BlockNumber> {
Ok(self.block_number)
}
}
impl<'a, T: Platform> BlockPinnedResolver<'a, T> {
fn resolve_block_number_or_tag(&self, number: BlockNumberOrTag) -> BlockNumberOrTag {
match number {
BlockNumberOrTag::Latest => BlockNumberOrTag::Number(self.block_number),
n @ BlockNumberOrTag::Finalized
| n @ BlockNumberOrTag::Safe
| n @ BlockNumberOrTag::Earliest
| n @ BlockNumberOrTag::Pending
| n @ BlockNumberOrTag::Number(_) => n,
}
}
}
+12 -89
View File
@@ -150,32 +150,6 @@ where
}) })
}, },
) )
.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,
},
)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let metadata_case_status = Arc::new(RwLock::new(test_cases.iter().fold( let metadata_case_status = Arc::new(RwLock::new(test_cases.iter().fold(
@@ -195,8 +169,6 @@ where
const RESET: &str = "\x1B[0m"; const RESET: &str = "\x1B[0m";
let mut entries_to_delete = Vec::new(); let mut entries_to_delete = Vec::new();
let mut number_of_successes = 0;
let mut number_of_failures = 0;
loop { loop {
let metadata_case_status_read = metadata_case_status.read().await; let metadata_case_status_read = metadata_case_status.read().await;
if metadata_case_status_read.is_empty() { if metadata_case_status_read.is_empty() {
@@ -232,15 +204,6 @@ where
) )
}; };
number_of_successes += case_status
.values()
.filter(|value| value.is_some_and(|value| value))
.count();
number_of_failures += case_status
.values()
.filter(|value| value.is_some_and(|value| !value))
.count();
let mut case_status = case_status let mut case_status = case_status
.iter() .iter()
.map(|((case_idx, case_name), case_status)| { .map(|((case_idx, case_name), case_status)| {
@@ -248,10 +211,10 @@ where
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
case_status.sort_by(|a, b| a.0.cmp(&b.0)); case_status.sort_by(|a, b| a.0.cmp(&b.0));
for (case_idx, case_name, case_status) in case_status.into_iter() { for (_, case_name, case_status) in case_status.into_iter() {
if case_status { if case_status {
eprintln!( eprintln!(
" {GREEN}Case Succeeded:{RESET} {} - Case Idx: {case_idx}", "{GREEN} Case Succeeded:{RESET} {}",
case_name case_name
.as_ref() .as_ref()
.map(|string| string.as_str()) .map(|string| string.as_str())
@@ -259,7 +222,7 @@ where
) )
} else { } else {
eprintln!( eprintln!(
" {RED}Case Failed:{RESET} {} - Case Idx: {case_idx}", "{RED} Case Failed:{RESET} {}",
case_name case_name
.as_ref() .as_ref()
.map(|string| string.as_str()) .map(|string| string.as_str())
@@ -280,10 +243,6 @@ where
tokio::time::sleep(std::time::Duration::from_secs(3)).await; tokio::time::sleep(std::time::Duration::from_secs(3)).await;
} }
eprintln!(
"{GREEN}{number_of_successes}{RESET} cases succeeded, {RED}{number_of_failures}{RESET} cases failed"
);
} }
}; };
@@ -330,7 +289,7 @@ where
.entry((metadata_file_path.clone(), solc_mode)) .entry((metadata_file_path.clone(), solc_mode))
.or_default() .or_default()
.insert((CaseIdx::new(case_idx), case.name.clone()), Some(false)); .insert((CaseIdx::new(case_idx), case.name.clone()), Some(false));
tracing::error!(%error, "Execution failed") tracing::info!(%error, "Execution failed")
} }
} }
tracing::info!("Execution completed"); tracing::info!("Execution completed");
@@ -477,17 +436,6 @@ where
} }
}; };
tracing::info!(
?library_instance,
library_address = ?leader_receipt.contract_address,
"Deployed library to leader"
);
tracing::info!(
?library_instance,
library_address = ?follower_receipt.contract_address,
"Deployed library to follower"
);
let Some(leader_library_address) = leader_receipt.contract_address else { let Some(leader_library_address) = leader_receipt.contract_address else {
tracing::error!("Contract deployment transaction didn't return an address"); tracing::error!("Contract deployment transaction didn't return an address");
anyhow::bail!("Contract deployment didn't return an address"); anyhow::bail!("Contract deployment didn't return an address");
@@ -597,14 +545,7 @@ async fn get_or_build_contracts<'a, P: Platform>(
None => { None => {
tracing::debug!(?key, "Compiled contracts cache miss"); tracing::debug!(?key, "Compiled contracts cache miss");
let compiled_contracts = Arc::new( let compiled_contracts = Arc::new(
compile_contracts::<P>( compile_contracts::<P>(metadata, &mode, config, deployed_libraries).await?,
metadata,
metadata_file_path,
&mode,
config,
deployed_libraries,
)
.await?,
); );
*compilation_artifact = Some(compiled_contracts.clone()); *compilation_artifact = Some(compiled_contracts.clone());
return Ok(compiled_contracts.clone()); return Ok(compiled_contracts.clone());
@@ -620,23 +561,14 @@ async fn get_or_build_contracts<'a, P: Platform>(
mutex mutex
}; };
let mut compilation_artifact = mutex.lock().await; let mut compilation_artifact = mutex.lock().await;
let compiled_contracts = Arc::new( let compiled_contracts =
compile_contracts::<P>( Arc::new(compile_contracts::<P>(metadata, &mode, config, deployed_libraries).await?);
metadata,
metadata_file_path,
&mode,
config,
deployed_libraries,
)
.await?,
);
*compilation_artifact = Some(compiled_contracts.clone()); *compilation_artifact = Some(compiled_contracts.clone());
Ok(compiled_contracts.clone()) Ok(compiled_contracts.clone())
} }
async fn compile_contracts<P: Platform>( async fn compile_contracts<P: Platform>(
metadata: &Metadata, metadata: &Metadata,
metadata_file_path: &Path,
mode: &SolcMode, mode: &SolcMode,
config: &Arguments, config: &Arguments,
deployed_libraries: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_libraries: &HashMap<ContractInstance, (Address, JsonAbi)>,
@@ -646,13 +578,6 @@ async fn compile_contracts<P: Platform>(
P::Compiler::get_compiler_executable(config, compiler_version_or_requirement).await?; P::Compiler::get_compiler_executable(config, 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()?;
tracing::info!(
%compiler_version,
metadata_file_path = %metadata_file_path.display(),
mode = ?mode,
"Compiling contracts"
);
let compiler = Compiler::<P::Compiler>::new() let compiler = Compiler::<P::Compiler>::new()
.with_allow_path(metadata.directory()?) .with_allow_path(metadata.directory()?)
.with_optimization(mode.solc_optimize()); .with_optimization(mode.solc_optimize());
@@ -667,11 +592,11 @@ async fn compile_contracts<P: Platform>(
.expect("Impossible for library to not be found in contracts") .expect("Impossible for library to not be found in contracts")
.contract_ident; .contract_ident;
// Note the following: we need to tell solc which files require the libraries to be linked // Note the following: we need to tell solc which files require the libraries to be
// into them. We do not have access to this information and therefore we choose an easier, // linked into them. We do not have access to this information and therefore we choose
// yet more compute intensive route, of telling solc that all of the files need to link the // an easier, yet more compute intensive route, of telling solc that all of the files
// library and it will only perform the linking for the files that do actually need the // need to link the library and it will only perform the linking for the files that do
// library. // actually need the library.
compiler = FilesWithExtensionIterator::new(metadata.directory()?) compiler = FilesWithExtensionIterator::new(metadata.directory()?)
.with_allowed_extension("sol") .with_allowed_extension("sol")
.fold(compiler, |compiler, path| { .fold(compiler, |compiler, path| {
@@ -721,7 +646,6 @@ async fn compile_corpus(
TestingPlatform::Geth => { TestingPlatform::Geth => {
let _ = compile_contracts::<Geth>( let _ = compile_contracts::<Geth>(
&metadata.content, &metadata.content,
&metadata.path,
&mode, &mode,
config, config,
&Default::default(), &Default::default(),
@@ -731,7 +655,6 @@ async fn compile_corpus(
TestingPlatform::Kitchensink => { TestingPlatform::Kitchensink => {
let _ = compile_contracts::<Geth>( let _ = compile_contracts::<Geth>(
&metadata.content, &metadata.content,
&metadata.path,
&mode, &mode,
config, config,
&Default::default(), &Default::default(),
-1
View File
@@ -15,7 +15,6 @@ pub struct Case {
pub inputs: Vec<Input>, pub inputs: Vec<Input>,
pub group: Option<String>, pub group: Option<String>,
pub expected: Option<Expected>, pub expected: Option<Expected>,
pub ignore: Option<bool>,
} }
impl Case { impl Case {
+44 -25
View File
@@ -202,13 +202,13 @@ impl Input {
&'a self, &'a self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone, variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<Bytes> { ) -> anyhow::Result<Bytes> {
match self.method { match self.method {
Method::Deployer | Method::Fallback => { Method::Deployer | Method::Fallback => {
let calldata = self let calldata = self
.calldata .calldata
.calldata(deployed_contracts, variables, resolver) .calldata(deployed_contracts, variables, chain_state_provider)
.await?; .await?;
Ok(calldata.into()) Ok(calldata.into())
@@ -256,7 +256,12 @@ impl Input {
let mut calldata = Vec::<u8>::with_capacity(4 + self.calldata.size_requirement()); let mut calldata = Vec::<u8>::with_capacity(4 + self.calldata.size_requirement());
calldata.extend(function.selector().0); calldata.extend(function.selector().0);
self.calldata self.calldata
.calldata_into_slice(&mut calldata, deployed_contracts, variables, resolver) .calldata_into_slice(
&mut calldata,
deployed_contracts,
variables,
chain_state_provider,
)
.await?; .await?;
Ok(calldata.into()) Ok(calldata.into())
@@ -269,10 +274,10 @@ impl Input {
&'a self, &'a self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone, variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<TransactionRequest> { ) -> anyhow::Result<TransactionRequest> {
let input_data = self let input_data = self
.encoded_input(deployed_contracts, variables, resolver) .encoded_input(deployed_contracts, variables, chain_state_provider)
.await?; .await?;
let transaction_request = TransactionRequest::default().from(self.caller).value( let transaction_request = TransactionRequest::default().from(self.caller).value(
self.value self.value
@@ -355,10 +360,15 @@ impl Calldata {
&'a self, &'a self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone, variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<Vec<u8>> { ) -> anyhow::Result<Vec<u8>> {
let mut buffer = Vec::<u8>::with_capacity(self.size_requirement()); let mut buffer = Vec::<u8>::with_capacity(self.size_requirement());
self.calldata_into_slice(&mut buffer, deployed_contracts, variables, resolver) self.calldata_into_slice(
&mut buffer,
deployed_contracts,
variables,
chain_state_provider,
)
.await?; .await?;
Ok(buffer) Ok(buffer)
} }
@@ -368,7 +378,7 @@ impl Calldata {
buffer: &mut Vec<u8>, buffer: &mut Vec<u8>,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone, variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
match self { match self {
Calldata::Single(bytes) => { Calldata::Single(bytes) => {
@@ -377,7 +387,7 @@ impl Calldata {
Calldata::Compound(items) => { Calldata::Compound(items) => {
for (arg_idx, arg) in items.iter().enumerate() { for (arg_idx, arg) in items.iter().enumerate() {
match arg match arg
.resolve(deployed_contracts, variables.clone(), resolver) .resolve(deployed_contracts, variables.clone(), chain_state_provider)
.await .await
{ {
Ok(resolved) => { Ok(resolved) => {
@@ -407,7 +417,7 @@ impl Calldata {
other: &[u8], other: &[u8],
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone, variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<bool> { ) -> anyhow::Result<bool> {
match self { match self {
Calldata::Single(calldata) => Ok(calldata == other), Calldata::Single(calldata) => Ok(calldata == other),
@@ -430,7 +440,7 @@ impl Calldata {
}; };
let this = this let this = this
.resolve(deployed_contracts, variables.clone(), resolver) .resolve(deployed_contracts, variables.clone(), chain_state_provider)
.await?; .await?;
let other = U256::from_be_slice(&other); let other = U256::from_be_slice(&other);
if this != other { if this != other {
@@ -448,13 +458,13 @@ impl CalldataItem {
&'a self, &'a self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone, variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<U256> { ) -> anyhow::Result<U256> {
let mut stack = Vec::<CalldataToken<U256>>::new(); let mut stack = Vec::<CalldataToken<U256>>::new();
for token in self for token in self
.calldata_tokens() .calldata_tokens()
.map(|token| token.resolve(deployed_contracts, variables.clone(), resolver)) .map(|token| token.resolve(deployed_contracts, variables.clone(), chain_state_provider))
{ {
let token = token.await?; let token = token.await?;
let new_token = match token { let new_token = match token {
@@ -559,7 +569,7 @@ impl<T: AsRef<str>> CalldataToken<T> {
self, self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone, variables: impl Into<Option<&'a HashMap<String, U256>>> + Clone,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<CalldataToken<U256>> { ) -> anyhow::Result<CalldataToken<U256>> {
match self { match self {
Self::Item(item) => { Self::Item(item) => {
@@ -589,17 +599,22 @@ impl<T: AsRef<str>> CalldataToken<T> {
anyhow::anyhow!("Invalid hexadecimal literal: {}", error) anyhow::anyhow!("Invalid hexadecimal literal: {}", error)
})?) })?)
} else if item == Self::CHAIN_VARIABLE { } else if item == Self::CHAIN_VARIABLE {
let chain_id = resolver.chain_id().await?; let chain_id = chain_state_provider.chain_id().await?;
Ok(U256::from(chain_id)) Ok(U256::from(chain_id))
} else if item == Self::GAS_LIMIT_VARIABLE { } else if item == Self::GAS_LIMIT_VARIABLE {
let gas_limit = resolver.block_gas_limit(BlockNumberOrTag::Latest).await?; let gas_limit = chain_state_provider
.block_gas_limit(BlockNumberOrTag::Latest)
.await?;
Ok(U256::from(gas_limit)) Ok(U256::from(gas_limit))
} else if item == Self::COINBASE_VARIABLE { } else if item == Self::COINBASE_VARIABLE {
let coinbase = resolver.block_coinbase(BlockNumberOrTag::Latest).await?; let coinbase = chain_state_provider
.block_coinbase(BlockNumberOrTag::Latest)
.await?;
Ok(U256::from_be_slice(coinbase.as_ref())) Ok(U256::from_be_slice(coinbase.as_ref()))
} else if item == Self::DIFFICULTY_VARIABLE { } else if item == Self::DIFFICULTY_VARIABLE {
let block_difficulty = let block_difficulty = chain_state_provider
resolver.block_difficulty(BlockNumberOrTag::Latest).await?; .block_difficulty(BlockNumberOrTag::Latest)
.await?;
Ok(block_difficulty) Ok(block_difficulty)
} else if item.starts_with(Self::BLOCK_HASH_VARIABLE_PREFIX) { } else if item.starts_with(Self::BLOCK_HASH_VARIABLE_PREFIX) {
let offset: u64 = item let offset: u64 = item
@@ -608,17 +623,21 @@ impl<T: AsRef<str>> CalldataToken<T> {
.and_then(|value| value.parse().ok()) .and_then(|value| value.parse().ok())
.unwrap_or_default(); .unwrap_or_default();
let current_block_number = resolver.last_block_number().await?; let current_block_number = chain_state_provider.last_block_number().await?;
let desired_block_number = current_block_number - offset; let desired_block_number = current_block_number - offset;
let block_hash = resolver.block_hash(desired_block_number.into()).await?; let block_hash = chain_state_provider
.block_hash(desired_block_number.into())
.await?;
Ok(U256::from_be_bytes(block_hash.0)) Ok(U256::from_be_bytes(block_hash.0))
} else if item == Self::BLOCK_NUMBER_VARIABLE { } else if item == Self::BLOCK_NUMBER_VARIABLE {
let current_block_number = resolver.last_block_number().await?; let current_block_number = chain_state_provider.last_block_number().await?;
Ok(U256::from(current_block_number)) Ok(U256::from(current_block_number))
} else if item == Self::BLOCK_TIMESTAMP_VARIABLE { } else if item == Self::BLOCK_TIMESTAMP_VARIABLE {
let timestamp = resolver.block_timestamp(BlockNumberOrTag::Latest).await?; let timestamp = chain_state_provider
.block_timestamp(BlockNumberOrTag::Latest)
.await?;
Ok(U256::from(timestamp)) Ok(U256::from(timestamp))
} else if let Some(variable_name) = item.strip_prefix(Self::VARIABLE_PREFIX) { } else if let Some(variable_name) = item.strip_prefix(Self::VARIABLE_PREFIX) {
let Some(variables) = variables.into() else { let Some(variables) = variables.into() else {
@@ -863,10 +882,10 @@ mod tests {
async fn resolve_calldata_item( async fn resolve_calldata_item(
input: &str, input: &str,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
resolver: &impl ResolverApi, chain_state_provider: &impl ResolverApi,
) -> anyhow::Result<U256> { ) -> anyhow::Result<U256> {
CalldataItem::new(input) CalldataItem::new(input)
.resolve(deployed_contracts, None, resolver) .resolve(deployed_contracts, None, chain_state_provider)
.await .await
} }
+57 -76
View File
@@ -3,13 +3,9 @@
use std::{ use std::{
fs::{File, OpenOptions, create_dir_all, remove_dir_all}, fs::{File, OpenOptions, create_dir_all, remove_dir_all},
io::{BufRead, BufReader, Read, Write}, io::{BufRead, BufReader, Read, Write},
ops::ControlFlow,
path::PathBuf, path::PathBuf,
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
sync::{ sync::atomic::{AtomicU32, Ordering},
Arc,
atomic::{AtomicU32, Ordering},
},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@@ -29,12 +25,11 @@ use alloy::{
}, },
signers::local::PrivateKeySigner, signers::local::PrivateKeySigner,
}; };
use tracing::{Instrument, Level}; use revive_dt_common::fs::clear_directory;
use revive_dt_common::{fs::clear_directory, futures::poll};
use revive_dt_config::Arguments; use revive_dt_config::Arguments;
use revive_dt_format::traits::ResolverApi; use revive_dt_format::traits::ResolverApi;
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use tracing::Level;
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE}; use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
@@ -82,10 +77,6 @@ impl GethNode {
const GETH_STDERR_LOG_FILE_NAME: &str = "node_stderr.log"; const GETH_STDERR_LOG_FILE_NAME: &str = "node_stderr.log";
const TRANSACTION_INDEXING_ERROR: &str = "transaction indexing is in progress"; const TRANSACTION_INDEXING_ERROR: &str = "transaction indexing is in progress";
const TRANSACTION_TRACING_ERROR: &str = "historical state not available in path scheme yet";
const RECEIPT_POLLING_DURATION: Duration = Duration::from_secs(5 * 60);
const TRACE_POLLING_DURATION: Duration = Duration::from_secs(60);
/// Create the node directory and call `geth init` to configure the genesis. /// Create the node directory and call `geth init` to configure the genesis.
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))] #[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
@@ -111,8 +102,6 @@ impl GethNode {
serde_json::to_writer(File::create(&genesis_path)?, &genesis)?; serde_json::to_writer(File::create(&genesis_path)?, &genesis)?;
let mut child = Command::new(&self.geth) let mut child = Command::new(&self.geth)
.arg("--state.scheme")
.arg("hash")
.arg("init") .arg("init")
.arg("--datadir") .arg("--datadir")
.arg(&self.data_directory) .arg(&self.data_directory)
@@ -170,12 +159,6 @@ impl GethNode {
.arg("0") .arg("0")
.arg("--cache.blocklogs") .arg("--cache.blocklogs")
.arg("512") .arg("512")
.arg("--state.scheme")
.arg("hash")
.arg("--syncmode")
.arg("full")
.arg("--gcmode")
.arg("archive")
.stderr(stderr_logs_file.try_clone()?) .stderr(stderr_logs_file.try_clone()?)
.stdout(stdout_logs_file.try_clone()?) .stdout(stdout_logs_file.try_clone()?)
.spawn()? .spawn()?
@@ -265,16 +248,21 @@ impl GethNode {
} }
impl EthereumNode for GethNode { impl EthereumNode for GethNode {
#[tracing::instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
async fn execute_transaction( async fn execute_transaction(
&self, &self,
transaction: TransactionRequest, transaction: TransactionRequest,
) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> { ) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> {
let span = tracing::debug_span!("Submitting transaction", ?transaction); let outer_span = tracing::debug_span!("Submitting transaction", ?transaction);
let _guard = span.enter(); let _outer_guard = outer_span.enter();
let provider = Arc::new(self.provider().await?); let provider = self.provider().await?;
let transaction_hash = *provider.send_transaction(transaction).await?.tx_hash();
let pending_transaction = provider.send_transaction(transaction).await?;
let transaction_hash = pending_transaction.tx_hash();
let span = tracing::info_span!("Awaiting transaction receipt", ?transaction_hash);
let _guard = span.enter();
// The following is a fix for the "transaction indexing is in progress" error that we // The following is a fix for the "transaction indexing is in progress" error that we
// used to get. You can find more information on this in the following GH issue in geth // used to get. You can find more information on this in the following GH issue in geth
@@ -294,64 +282,57 @@ impl EthereumNode for GethNode {
// allow for a larger wait time. Therefore, in here we allow for 5 minutes of waiting // allow for a larger wait time. Therefore, in here we allow for 5 minutes of waiting
// with exponential backoff each time we attempt to get the receipt and find that it's // with exponential backoff each time we attempt to get the receipt and find that it's
// not available. // not available.
poll( let mut retries = 0;
Self::RECEIPT_POLLING_DURATION, let mut total_wait_duration = Duration::from_secs(0);
Default::default(), let max_allowed_wait_duration = Duration::from_secs(5 * 60);
move || { loop {
let provider = provider.clone(); if total_wait_duration >= max_allowed_wait_duration {
async move { tracing::error!(
match provider.get_transaction_receipt(transaction_hash).await { ?total_wait_duration,
Ok(Some(receipt)) => Ok(ControlFlow::Break(receipt)), ?max_allowed_wait_duration,
Ok(None) => Ok(ControlFlow::Continue(())), retry_count = retries,
Err(error) => { "Failed to get receipt after polling for it"
let error_string = error.to_string(); );
match error_string.contains(Self::TRANSACTION_INDEXING_ERROR) { anyhow::bail!(
true => Ok(ControlFlow::Continue(())), "Polled for receipt for {total_wait_duration:?} but failed to get it"
false => Err(error.into()), );
}
}
}
}
},
)
.instrument(tracing::info_span!(
"Awaiting transaction receipt",
?transaction_hash
))
.await
} }
#[tracing::instrument(level = "info", skip_all, fields(geth_node_id = self.id))] match provider.get_transaction_receipt(*transaction_hash).await {
Ok(Some(receipt)) => {
tracing::info!(?total_wait_duration, "Found receipt");
break Ok(receipt);
}
Ok(None) => {}
Err(error) => {
let error_string = error.to_string();
if !error_string.contains(Self::TRANSACTION_INDEXING_ERROR) {
break Err(error.into());
}
}
};
let next_wait_duration = Duration::from_secs(2u64.pow(retries))
.min(max_allowed_wait_duration - total_wait_duration);
total_wait_duration += next_wait_duration;
retries += 1;
tokio::time::sleep(next_wait_duration).await;
}
}
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
async fn trace_transaction( async fn trace_transaction(
&self, &self,
transaction: &TransactionReceipt, transaction: &TransactionReceipt,
trace_options: GethDebugTracingOptions, trace_options: GethDebugTracingOptions,
) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> { ) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> {
let provider = Arc::new(self.provider().await?); let tx_hash = transaction.transaction_hash;
poll( Ok(self
Self::TRACE_POLLING_DURATION, .provider()
Default::default(), .await?
move || { .debug_trace_transaction(tx_hash, trace_options)
let provider = provider.clone(); .await?)
let trace_options = trace_options.clone();
async move {
match provider
.debug_trace_transaction(transaction.transaction_hash, trace_options)
.await
{
Ok(trace) => Ok(ControlFlow::Break(trace)),
Err(error) => {
let error_string = error.to_string();
match error_string.contains(Self::TRANSACTION_TRACING_ERROR) {
true => Ok(ControlFlow::Continue(())),
false => Err(error.into()),
}
}
}
}
},
)
.await
} }
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))] #[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
+2 -2
View File
@@ -209,7 +209,7 @@ impl KitchensinkNode {
if let Err(error) = Self::wait_ready( if let Err(error) = Self::wait_ready(
self.kitchensink_stderr_log_file_path().as_path(), self.kitchensink_stderr_log_file_path().as_path(),
Self::SUBSTRATE_READY_MARKER, Self::SUBSTRATE_READY_MARKER,
Duration::from_secs(60), Duration::from_secs(30),
) { ) {
tracing::error!( tracing::error!(
?error, ?error,
@@ -238,7 +238,7 @@ impl KitchensinkNode {
if let Err(error) = Self::wait_ready( if let Err(error) = Self::wait_ready(
self.proxy_stderr_log_file_path().as_path(), self.proxy_stderr_log_file_path().as_path(),
Self::ETH_PROXY_READY_MARKER, Self::ETH_PROXY_READY_MARKER,
Duration::from_secs(60), Duration::from_secs(30),
) { ) {
tracing::error!(?error, "Failed to start proxy, shutting down gracefully"); tracing::error!(?error, "Failed to start proxy, shutting down gracefully");
self.shutdown()?; self.shutdown()?;
+3 -3
View File
@@ -11,14 +11,14 @@ use std::{
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::download::SolcDownloader; use crate::download::GHDownloader;
pub const SOLC_CACHE_DIRECTORY: &str = "solc"; pub const SOLC_CACHE_DIRECTORY: &str = "solc";
pub(crate) static SOLC_CACHER: LazyLock<Mutex<HashSet<PathBuf>>> = LazyLock::new(Default::default); pub(crate) static SOLC_CACHER: LazyLock<Mutex<HashSet<PathBuf>>> = LazyLock::new(Default::default);
pub(crate) async fn get_or_download( pub(crate) async fn get_or_download(
working_directory: &Path, working_directory: &Path,
downloader: &SolcDownloader, downloader: &GHDownloader,
) -> anyhow::Result<PathBuf> { ) -> anyhow::Result<PathBuf> {
let target_directory = working_directory let target_directory = working_directory
.join(SOLC_CACHE_DIRECTORY) .join(SOLC_CACHE_DIRECTORY)
@@ -38,7 +38,7 @@ pub(crate) async fn get_or_download(
Ok(target_file) Ok(target_file)
} }
async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> anyhow::Result<()> { async fn download_to_file(path: &Path, downloader: &GHDownloader) -> anyhow::Result<()> {
tracing::info!("caching file: {}", path.display()); tracing::info!("caching file: {}", path.display());
let Ok(file) = File::create_new(path) else { let Ok(file) = File::create_new(path) else {
+24 -25
View File
@@ -38,21 +38,21 @@ impl List {
} }
} }
/// Download solc binaries from the official SolidityLang site /// Download solc binaries from GitHub releases (IPFS links aren't reliable).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SolcDownloader { pub struct GHDownloader {
pub version: Version, pub version: Version,
pub target: &'static str, pub target: &'static str,
pub list: &'static str, pub list: &'static str,
} }
impl SolcDownloader { impl GHDownloader {
pub const BASE_URL: &str = "https://binaries.soliditylang.org"; pub const BASE_URL: &str = "https://github.com/ethereum/solidity/releases/download";
pub const LINUX_NAME: &str = "linux-amd64"; pub const LINUX_NAME: &str = "solc-static-linux";
pub const MACOSX_NAME: &str = "macosx-amd64"; pub const MACOSX_NAME: &str = "solc-macos";
pub const WINDOWS_NAME: &str = "windows-amd64"; pub const WINDOWS_NAME: &str = "solc-windows.exe";
pub const WASM_NAME: &str = "wasm"; pub const WASM_NAME: &str = "soljson.js";
async fn new( async fn new(
version: impl Into<VersionOrRequirement>, version: impl Into<VersionOrRequirement>,
@@ -102,27 +102,26 @@ impl SolcDownloader {
Self::new(version, Self::WASM_NAME, List::WASM_URL).await Self::new(version, Self::WASM_NAME, List::WASM_URL).await
} }
/// Returns the download link.
pub fn url(&self) -> String {
format!("{}/v{}/{}", Self::BASE_URL, &self.version, &self.target)
}
/// Download the solc binary. /// Download the solc binary.
/// ///
/// Errors out if the download fails or the digest of the downloaded file /// Errors out if the download fails or the digest of the downloaded file
/// mismatches the expected digest from the release [List]. /// mismatches the expected digest from the release [List].
pub async fn download(&self) -> anyhow::Result<Vec<u8>> { pub async fn download(&self) -> anyhow::Result<Vec<u8>> {
tracing::info!("downloading solc: {self:?}"); tracing::info!("downloading solc: {self:?}");
let builds = List::download(self.list).await?.builds; let expected_digest = List::download(self.list)
let build = builds .await?
.builds
.iter() .iter()
.find(|build| build.version == self.version) .find(|build| build.version == self.version)
.ok_or_else(|| anyhow::anyhow!("solc v{} not found builds", self.version))?; .ok_or_else(|| anyhow::anyhow!("solc v{} not found builds", self.version))
.map(|b| b.sha256.strip_prefix("0x").unwrap_or(&b.sha256).to_string())?;
let path = build.path.clone(); let file = reqwest::get(self.url()).await?.bytes().await?.to_vec();
let expected_digest = build
.sha256
.strip_prefix("0x")
.unwrap_or(&build.sha256)
.to_string();
let url = format!("{}/{}/{}", Self::BASE_URL, self.target, path.display());
let file = reqwest::get(url).await?.bytes().await?.to_vec();
if hex::encode(Sha256::digest(&file)) != expected_digest { if hex::encode(Sha256::digest(&file)) != expected_digest {
anyhow::bail!("sha256 mismatch for solc version {}", self.version); anyhow::bail!("sha256 mismatch for solc version {}", self.version);
@@ -134,7 +133,7 @@ impl SolcDownloader {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{download::SolcDownloader, list::List}; use crate::{download::GHDownloader, list::List};
#[tokio::test] #[tokio::test]
async fn try_get_windows() { async fn try_get_windows() {
@@ -142,7 +141,7 @@ mod tests {
.await .await
.unwrap() .unwrap()
.latest_release; .latest_release;
SolcDownloader::windows(version) GHDownloader::windows(version)
.await .await
.unwrap() .unwrap()
.download() .download()
@@ -156,7 +155,7 @@ mod tests {
.await .await
.unwrap() .unwrap()
.latest_release; .latest_release;
SolcDownloader::macosx(version) GHDownloader::macosx(version)
.await .await
.unwrap() .unwrap()
.download() .download()
@@ -170,7 +169,7 @@ mod tests {
.await .await
.unwrap() .unwrap()
.latest_release; .latest_release;
SolcDownloader::linux(version) GHDownloader::linux(version)
.await .await
.unwrap() .unwrap()
.download() .download()
@@ -181,7 +180,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn try_get_wasm() { async fn try_get_wasm() {
let version = List::download(List::WASM_URL).await.unwrap().latest_release; let version = List::download(List::WASM_URL).await.unwrap().latest_release;
SolcDownloader::wasm(version) GHDownloader::wasm(version)
.await .await
.unwrap() .unwrap()
.download() .download()
+5 -5
View File
@@ -6,7 +6,7 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use cache::get_or_download; use cache::get_or_download;
use download::SolcDownloader; use download::GHDownloader;
use revive_dt_common::types::VersionOrRequirement; use revive_dt_common::types::VersionOrRequirement;
@@ -25,13 +25,13 @@ pub async fn download_solc(
wasm: bool, wasm: bool,
) -> anyhow::Result<PathBuf> { ) -> anyhow::Result<PathBuf> {
let downloader = if wasm { let downloader = if wasm {
SolcDownloader::wasm(version).await GHDownloader::wasm(version).await
} else if cfg!(target_os = "linux") { } else if cfg!(target_os = "linux") {
SolcDownloader::linux(version).await GHDownloader::linux(version).await
} else if cfg!(target_os = "macos") { } else if cfg!(target_os = "macos") {
SolcDownloader::macosx(version).await GHDownloader::macosx(version).await
} else if cfg!(target_os = "windows") { } else if cfg!(target_os = "windows") {
SolcDownloader::windows(version).await GHDownloader::windows(version).await
} else { } else {
unimplemented!() unimplemented!()
}?; }?;