mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-22 13:47:55 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed1a0a1dcd | |||
| 746f5db66f | |||
| 1400086794 | |||
| edba49b301 | |||
| 9980926d40 |
Generated
+1
@@ -4030,6 +4030,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"semver 1.0.26",
|
"semver 1.0.26",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ 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"] }
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
mod poll;
|
||||||
|
|
||||||
|
pub use poll::*;
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
//! 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;
|
||||||
|
|||||||
@@ -238,4 +238,25 @@ 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)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::marker::PhantomData;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use alloy::eips::BlockNumberOrTag;
|
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::{BlockNumber, U256};
|
||||||
@@ -242,6 +243,11 @@ 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(())
|
||||||
|
|||||||
+28
-3
@@ -162,6 +162,20 @@ where
|
|||||||
Some(false) | None => true,
|
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(
|
||||||
@@ -234,10 +248,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_name, case_status) in case_status.into_iter() {
|
for (case_idx, case_name, case_status) in case_status.into_iter() {
|
||||||
if case_status {
|
if case_status {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{GREEN} Case Succeeded:{RESET} {}",
|
" {GREEN}Case Succeeded:{RESET} {} - Case Idx: {case_idx}",
|
||||||
case_name
|
case_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|string| string.as_str())
|
.map(|string| string.as_str())
|
||||||
@@ -245,7 +259,7 @@ where
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{RED} Case Failed:{RESET} {}",
|
" {RED}Case Failed:{RESET} {} - Case Idx: {case_idx}",
|
||||||
case_name
|
case_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|string| string.as_str())
|
.map(|string| string.as_str())
|
||||||
@@ -463,6 +477,17 @@ 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");
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ 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 {
|
||||||
|
|||||||
+74
-55
@@ -3,9 +3,13 @@
|
|||||||
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::atomic::{AtomicU32, Ordering},
|
sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{AtomicU32, Ordering},
|
||||||
|
},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,11 +29,12 @@ use alloy::{
|
|||||||
},
|
},
|
||||||
signers::local::PrivateKeySigner,
|
signers::local::PrivateKeySigner,
|
||||||
};
|
};
|
||||||
use revive_dt_common::fs::clear_directory;
|
use tracing::{Instrument, Level};
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
@@ -77,6 +82,10 @@ 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))]
|
||||||
@@ -102,6 +111,8 @@ 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)
|
||||||
@@ -159,6 +170,12 @@ 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()?
|
||||||
@@ -248,22 +265,17 @@ impl GethNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EthereumNode for GethNode {
|
impl EthereumNode for GethNode {
|
||||||
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
|
#[tracing::instrument(level = "info", 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 outer_span = tracing::debug_span!("Submitting transaction", ?transaction);
|
let span = tracing::debug_span!("Submitting transaction", ?transaction);
|
||||||
let _outer_guard = outer_span.enter();
|
|
||||||
|
|
||||||
let provider = self.provider().await?;
|
|
||||||
|
|
||||||
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();
|
let _guard = span.enter();
|
||||||
|
|
||||||
|
let provider = Arc::new(self.provider().await?);
|
||||||
|
let transaction_hash = *provider.send_transaction(transaction).await?.tx_hash();
|
||||||
|
|
||||||
// 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
|
||||||
// https://github.com/ethereum/go-ethereum/issues/28877. To summarize what's going on,
|
// https://github.com/ethereum/go-ethereum/issues/28877. To summarize what's going on,
|
||||||
@@ -282,57 +294,64 @@ 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.
|
||||||
let mut retries = 0;
|
poll(
|
||||||
let mut total_wait_duration = Duration::from_secs(0);
|
Self::RECEIPT_POLLING_DURATION,
|
||||||
let max_allowed_wait_duration = Duration::from_secs(5 * 60);
|
Default::default(),
|
||||||
loop {
|
move || {
|
||||||
if total_wait_duration >= max_allowed_wait_duration {
|
let provider = provider.clone();
|
||||||
tracing::error!(
|
async move {
|
||||||
?total_wait_duration,
|
match provider.get_transaction_receipt(transaction_hash).await {
|
||||||
?max_allowed_wait_duration,
|
Ok(Some(receipt)) => Ok(ControlFlow::Break(receipt)),
|
||||||
retry_count = retries,
|
Ok(None) => Ok(ControlFlow::Continue(())),
|
||||||
"Failed to get receipt after polling for it"
|
Err(error) => {
|
||||||
);
|
let error_string = error.to_string();
|
||||||
anyhow::bail!(
|
match error_string.contains(Self::TRANSACTION_INDEXING_ERROR) {
|
||||||
"Polled for receipt for {total_wait_duration:?} but failed to get it"
|
true => Ok(ControlFlow::Continue(())),
|
||||||
);
|
false => Err(error.into()),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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))
|
.instrument(tracing::info_span!(
|
||||||
.min(max_allowed_wait_duration - total_wait_duration);
|
"Awaiting transaction receipt",
|
||||||
total_wait_duration += next_wait_duration;
|
?transaction_hash
|
||||||
retries += 1;
|
))
|
||||||
|
.await
|
||||||
tokio::time::sleep(next_wait_duration).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
|
#[tracing::instrument(level = "info", 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 tx_hash = transaction.transaction_hash;
|
let provider = Arc::new(self.provider().await?);
|
||||||
Ok(self
|
poll(
|
||||||
.provider()
|
Self::TRACE_POLLING_DURATION,
|
||||||
.await?
|
Default::default(),
|
||||||
.debug_trace_transaction(tx_hash, trace_options)
|
move || {
|
||||||
.await?)
|
let provider = provider.clone();
|
||||||
|
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))]
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ use std::{
|
|||||||
|
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::download::GHDownloader;
|
use crate::download::SolcDownloader;
|
||||||
|
|
||||||
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: &GHDownloader,
|
downloader: &SolcDownloader,
|
||||||
) -> 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: &GHDownloader) -> anyhow::Result<()> {
|
async fn download_to_file(path: &Path, downloader: &SolcDownloader) -> 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 {
|
||||||
|
|||||||
@@ -38,21 +38,21 @@ impl List {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Download solc binaries from GitHub releases (IPFS links aren't reliable).
|
/// Download solc binaries from the official SolidityLang site
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GHDownloader {
|
pub struct SolcDownloader {
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub target: &'static str,
|
pub target: &'static str,
|
||||||
pub list: &'static str,
|
pub list: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GHDownloader {
|
impl SolcDownloader {
|
||||||
pub const BASE_URL: &str = "https://github.com/ethereum/solidity/releases/download";
|
pub const BASE_URL: &str = "https://binaries.soliditylang.org";
|
||||||
|
|
||||||
pub const LINUX_NAME: &str = "solc-static-linux";
|
pub const LINUX_NAME: &str = "linux-amd64";
|
||||||
pub const MACOSX_NAME: &str = "solc-macos";
|
pub const MACOSX_NAME: &str = "macosx-amd64";
|
||||||
pub const WINDOWS_NAME: &str = "solc-windows.exe";
|
pub const WINDOWS_NAME: &str = "windows-amd64";
|
||||||
pub const WASM_NAME: &str = "soljson.js";
|
pub const WASM_NAME: &str = "wasm";
|
||||||
|
|
||||||
async fn new(
|
async fn new(
|
||||||
version: impl Into<VersionOrRequirement>,
|
version: impl Into<VersionOrRequirement>,
|
||||||
@@ -102,26 +102,27 @@ impl GHDownloader {
|
|||||||
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 expected_digest = List::download(self.list)
|
let builds = List::download(self.list).await?.builds;
|
||||||
.await?
|
let build = builds
|
||||||
.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 file = reqwest::get(self.url()).await?.bytes().await?.to_vec();
|
let path = build.path.clone();
|
||||||
|
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);
|
||||||
@@ -133,7 +134,7 @@ impl GHDownloader {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{download::GHDownloader, list::List};
|
use crate::{download::SolcDownloader, list::List};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn try_get_windows() {
|
async fn try_get_windows() {
|
||||||
@@ -141,7 +142,7 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.latest_release;
|
.latest_release;
|
||||||
GHDownloader::windows(version)
|
SolcDownloader::windows(version)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.download()
|
.download()
|
||||||
@@ -155,7 +156,7 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.latest_release;
|
.latest_release;
|
||||||
GHDownloader::macosx(version)
|
SolcDownloader::macosx(version)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.download()
|
.download()
|
||||||
@@ -169,7 +170,7 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.latest_release;
|
.latest_release;
|
||||||
GHDownloader::linux(version)
|
SolcDownloader::linux(version)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.download()
|
.download()
|
||||||
@@ -180,7 +181,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;
|
||||||
GHDownloader::wasm(version)
|
SolcDownloader::wasm(version)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.download()
|
.download()
|
||||||
|
|||||||
@@ -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::GHDownloader;
|
use download::SolcDownloader;
|
||||||
|
|
||||||
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 {
|
||||||
GHDownloader::wasm(version).await
|
SolcDownloader::wasm(version).await
|
||||||
} else if cfg!(target_os = "linux") {
|
} else if cfg!(target_os = "linux") {
|
||||||
GHDownloader::linux(version).await
|
SolcDownloader::linux(version).await
|
||||||
} else if cfg!(target_os = "macos") {
|
} else if cfg!(target_os = "macos") {
|
||||||
GHDownloader::macosx(version).await
|
SolcDownloader::macosx(version).await
|
||||||
} else if cfg!(target_os = "windows") {
|
} else if cfg!(target_os = "windows") {
|
||||||
GHDownloader::windows(version).await
|
SolcDownloader::windows(version).await
|
||||||
} else {
|
} else {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}?;
|
}?;
|
||||||
|
|||||||
Reference in New Issue
Block a user