diff --git a/Cargo.lock b/Cargo.lock index db4f1af..3916e3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2877,10 +2877,12 @@ name = "revive-dt-solc-binaries" version = "0.1.0" dependencies = [ "anyhow", + "hex", "once_cell", "reqwest", "semver 1.0.26", "serde", + "sha2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4a47853..b06ef33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ once_cell = "1.21" semver = {version = "1.0", features = ["serde"] } serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false, features = ["arbitrary_precision", "std"] } +sha2 = { version = "0.10.8" } tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } # revive compiler diff --git a/crates/compiler/src/revive_js.rs b/crates/compiler/src/revive_js.rs index fcd80aa..49c7f6d 100644 --- a/crates/compiler/src/revive_js.rs +++ b/crates/compiler/src/revive_js.rs @@ -1,2 +1,2 @@ -//! Implements the [SolidityCompiler] trait with revive Wasm for +//! Implements the [crate::SolidityCompiler] trait with revive Wasm for //! compiling contracts to PVM bytecode (via Wasm). diff --git a/crates/compiler/src/revive_resolc.rs b/crates/compiler/src/revive_resolc.rs index 7017d8b..ff76bc6 100644 --- a/crates/compiler/src/revive_resolc.rs +++ b/crates/compiler/src/revive_resolc.rs @@ -1,2 +1,2 @@ -//! Implements the [SolidityCompiler] trait with resolc for +//! Implements the [crate::SolidityCompiler] trait with resolc for //! compiling contracts to PVM bytecode. diff --git a/crates/solc-binaries/Cargo.toml b/crates/solc-binaries/Cargo.toml index b0c5506..f1a3a8b 100644 --- a/crates/solc-binaries/Cargo.toml +++ b/crates/solc-binaries/Cargo.toml @@ -14,7 +14,9 @@ download = ["reqwest"] [dependencies] anyhow = { workspace = true } +hex = { workspace = true } once_cell = { workspace = true } reqwest = { workspace = true, optional = true } semver = { workspace = true } serde = { workspace = true } +sha2 = { workspace = true } diff --git a/crates/solc-binaries/src/download.rs b/crates/solc-binaries/src/download.rs index aa2756e..baa7707 100644 --- a/crates/solc-binaries/src/download.rs +++ b/crates/solc-binaries/src/download.rs @@ -3,6 +3,8 @@ use std::{collections::HashMap, sync::Mutex}; use once_cell::sync::Lazy; +use semver::Version; +use sha2::{Digest, Sha256}; use crate::list::List; @@ -31,31 +33,113 @@ impl List { } } -#[cfg(test)] -mod tests { - use crate::list::List; +/// Download solc binaries from GitHub releases (IPFS links aren't reliable). +pub struct GHDownloader { + version: Version, + target: &'static str, + list: &'static str, +} - #[test] - fn try_get_windows_list() { - List::download(List::WINDOWS_URL).unwrap(); - List::download(List::WINDOWS_URL).unwrap(); +impl GHDownloader { + pub const BASE_URL: &str = "https://github.com/ethereum/solidity/releases/download"; + + pub const LINUX_NAME: &str = "solc-static-linux"; + pub const MACOSX_NAME: &str = "solc-macos"; + pub const WINDOWS_NAME: &str = "solc-windows.exe"; + pub const WASM_NAME: &str = "soljson.js"; + + pub fn linux(version: Version) -> Self { + Self { + version, + target: Self::LINUX_NAME, + list: List::LINUX_URL, + } } - #[test] - fn try_get_macosx_list() { - List::download(List::MACOSX_URL).unwrap(); - List::download(List::MACOSX_URL).unwrap(); + pub fn macosx(version: Version) -> Self { + Self { + version, + target: Self::MACOSX_NAME, + list: List::MACOSX_URL, + } } - #[test] - fn try_get_linux_list() { - List::download(List::LINUX_URL).unwrap(); - List::download(List::LINUX_URL).unwrap(); + pub fn windows(version: Version) -> Self { + Self { + version, + target: Self::WINDOWS_NAME, + list: List::WINDOWS_URL, + } } - #[test] - fn try_get_wasm_list() { - List::download(List::WASM_URL).unwrap(); - List::download(List::WASM_URL).unwrap(); + pub fn wasm(version: Version) -> Self { + Self { + version, + target: Self::WASM_NAME, + list: List::WASM_URL, + } + } + + /// Download the solc binary. + /// + /// Errors out if the download fails or the digest of the downloaded file + /// mismatches the expected digest from the release [List]. + pub fn download(&self) -> anyhow::Result> { + let expected_digest = List::download(self.list)? + .builds + .iter() + .find(|build| build.version == 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 url = format!("{}/v{}/{}", Self::BASE_URL, self.version, self.target); + let file = reqwest::blocking::get(&url)?.bytes()?.to_vec(); + + if hex::encode(Sha256::digest(&file)) != expected_digest { + anyhow::bail!("sha256 mismatch for solc version {}", self.version); + } + + Ok(file) + } +} + +#[cfg(test)] +mod tests { + use crate::{download::GHDownloader, list::List}; + + #[test] + fn try_get_windows() { + let version = List::download(List::WINDOWS_URL) + .unwrap() + .latest_release + .into(); + GHDownloader::windows(version).download().unwrap(); + } + + #[test] + fn try_get_macosx() { + let version = List::download(List::MACOSX_URL) + .unwrap() + .latest_release + .into(); + GHDownloader::macosx(version).download().unwrap(); + } + + #[test] + fn try_get_linux() { + let version = List::download(List::LINUX_URL) + .unwrap() + .latest_release + .into(); + GHDownloader::linux(version).download().unwrap(); + } + + #[test] + fn try_get_wasm() { + let version = List::download(List::WASM_URL) + .unwrap() + .latest_release + .into(); + GHDownloader::wasm(version).download().unwrap(); } } diff --git a/crates/solc-binaries/src/list.rs b/crates/solc-binaries/src/list.rs index c8b3abf..2287158 100644 --- a/crates/solc-binaries/src/list.rs +++ b/crates/solc-binaries/src/list.rs @@ -20,7 +20,7 @@ pub struct Build { pub build: String, #[serde(rename = "longVersion")] pub long_version: String, - keccak256: String, - sha256: String, - urls: Vec, + pub keccak256: String, + pub sha256: String, + pub urls: Vec, }