diff --git a/.cargo-deny.toml b/.cargo-deny.toml index 3429018..0b91841 100644 --- a/.cargo-deny.toml +++ b/.cargo-deny.toml @@ -22,6 +22,8 @@ allow = [ "CDLA-Permissive-2.0", "ISC", "MIT", + "MPL-2.0", + "Unicode-3.0", "Zlib", ] private = { ignore = false } diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 36fbde4..edc9d92 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -51,6 +51,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + [[package]] name = "errno" version = "0.3.14" @@ -58,7 +79,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -155,6 +176,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" name = "logos-blockchain-circuits-build" version = "0.5.0" dependencies = [ + "dirs", "flate2", "tar", "ureq", @@ -227,6 +249,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -239,6 +267,24 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + [[package]] name = "redox_syscall" version = "0.7.4" @@ -248,6 +294,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "ring" version = "0.17.14" @@ -259,7 +316,7 @@ dependencies = [ "getrandom", "libc", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -272,7 +329,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -328,6 +385,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tar" version = "0.4.45" @@ -339,6 +407,32 @@ dependencies = [ "xattr", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + [[package]] name = "untrusted" version = "0.9.0" @@ -395,6 +489,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.52.0" @@ -404,6 +504,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e91f7bc..68d5c41 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -31,6 +31,7 @@ lbc-signature-sys = { default-features = false, package = "logos-blockchain-circ lbc-types = { default-features = false, package = "logos-blockchain-circuits-types", path = "./logos-blockchain-circuits-types" } # External +dirs = "^6" flate2 = "^1.1" libc = "^0.2" tar = "^0.4" diff --git a/rust/logos-blockchain-circuits-build/Cargo.toml b/rust/logos-blockchain-circuits-build/Cargo.toml index fff522c..bd03025 100644 --- a/rust/logos-blockchain-circuits-build/Cargo.toml +++ b/rust/logos-blockchain-circuits-build/Cargo.toml @@ -9,10 +9,14 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +prebuilt = ["dep:dirs", "dep:flate2", "dep:tar", "dep:ureq"] + [lints] workspace = true [dependencies] -flate2 = { workspace = true } -tar = { workspace = true } -ureq = { workspace = true } +dirs = { workspace = true, optional = true } +flate2 = { workspace = true, optional = true } +tar = { workspace = true, optional = true } +ureq = { workspace = true, optional = true } diff --git a/rust/logos-blockchain-circuits-build/src/lib.rs b/rust/logos-blockchain-circuits-build/src/lib.rs index 9e8909e..205dfd7 100644 --- a/rust/logos-blockchain-circuits-build/src/lib.rs +++ b/rust/logos-blockchain-circuits-build/src/lib.rs @@ -1,80 +1,102 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; -use ureq::Body; -use ureq::http::Response; +#[cfg(feature = "prebuilt")] +mod prebuilt { + use std::path::{Path, PathBuf}; + use ureq::Body; + use ureq::http::Response; -static REPO: &str = "logos-blockchain/logos-blockchain-circuits"; -static ARTIFACT_PREFIX: &str = "logos-blockchain-circuits"; + static REPO: &str = "logos-blockchain/logos-blockchain-circuits"; + static ARTIFACT_PREFIX: &str = "logos-blockchain-circuits"; -fn build_artifact_name(version: &str, os: &str, arch: &str) -> String { - format!("{ARTIFACT_PREFIX}-v{version}-{os}-{arch}") -} - -fn build_artifact_url(version: &str, os: &str, arch: &str) -> String { - let artifact = build_artifact_name(version, os, arch); - let artifact_tar_gz = format!("{artifact}.tar.gz"); - format!("https://github.com/{REPO}/releases/download/v{version}/{artifact_tar_gz}") -} - -fn fetch_library(version: &str, os: &str, arch: &str, lib_var_name: &str) -> Response { - let url = build_artifact_url(version, os, arch); - // We skip checksum verification intentionally. - // Hardcoded hashes would protect against a silently replaced release asset, but require a - // two-step release (build → hash → commit → tag) which feels overkill for a first-party - // library. - ureq::get(&url).call().unwrap_or_else(|error| { - panic!( - "Failed to download a prebuilt library for {os}-{arch} v{version}: {error}. \ - Set {lib_var_name} to point to a local build instead." - ) - }) -} - -fn unpack_library( - response: Response, - circuit_name: &str, - version: &str, - os: &str, - arch: &str, - output_dir: &Path, -) -> PathBuf { - let gz_decoder = flate2::read::GzDecoder::new(response.into_body().into_reader()); - let mut archive = tar::Archive::new(gz_decoder); - archive - .unpack(output_dir) - .expect("Failed to unpack the downloaded archive."); - - let unpacked_artifact_path = output_dir.join(build_artifact_name(version, os, arch)); - let unpacked_library_directory = unpacked_artifact_path.join(circuit_name); - - assert!( - unpacked_library_directory.is_dir(), - "Failed to find the unpacked library at {}", - unpacked_library_directory.display() - ); - - unpacked_library_directory -} - -fn provision_library(circuit_name: &str, lib_var_name: &str) -> PathBuf { - let version = env!("CARGO_PKG_VERSION"); - let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); - let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - - let expected_library_directory = out_dir - .join(build_artifact_name(version, &os, &arch)) - .join(circuit_name); - if expected_library_directory.is_dir() { - println!( - "Found an existing library at {}. Reusing it.", - expected_library_directory.display() - ); - return expected_library_directory; + fn build_artifact_name(version: &str, os: &str, arch: &str) -> String { + format!("{ARTIFACT_PREFIX}-v{version}-{os}-{arch}") } - let response = fetch_library(version, &os, &arch, lib_var_name); - unpack_library(response, circuit_name, version, &os, &arch, &out_dir) + fn build_artifact_url(version: &str, os: &str, arch: &str) -> String { + let artifact = build_artifact_name(version, os, arch); + let artifact_tar_gz = format!("{artifact}.tar.gz"); + format!("https://github.com/{REPO}/releases/download/v{version}/{artifact_tar_gz}") + } + + fn fetch_library(version: &str, os: &str, arch: &str, lib_var_name: &str) -> Response { + let url = build_artifact_url(version, os, arch); + // We skip checksum verification intentionally. + // Hardcoded hashes would protect against a silently replaced release asset but require a + // two-step release (build → hash → commit → tag) which feels overkill for a first-party + // library. + ureq::get(&url).call().unwrap_or_else(|error| { + panic!( + "Failed to download a prebuilt library for {os}-{arch} v{version}: {error}. \ + Set {lib_var_name} to point to a local build instead." + ) + }) + } + + fn unpack_library( + response: Response, + circuit_name: &str, + version: &str, + os: &str, + arch: &str, + output_dir: &Path, + ) -> PathBuf { + let gz_decoder = flate2::read::GzDecoder::new(response.into_body().into_reader()); + let mut archive = tar::Archive::new(gz_decoder); + archive + .unpack(output_dir) + .expect("Failed to unpack the downloaded archive."); + + let unpacked_artifact_path = output_dir.join(build_artifact_name(version, os, arch)); + let unpacked_library_directory = unpacked_artifact_path.join(circuit_name); + + assert!( + unpacked_library_directory.is_dir(), + "Failed to find the unpacked library at {}", + unpacked_library_directory.display() + ); + + unpacked_library_directory + } + + fn cache_dir() -> PathBuf { + dirs::cache_dir() + .expect("Could not determine the cache directory for this platform.") + .join("logos") + .join("blockchain") + } + + pub fn provision_library(circuit_name: &str, lib_var_name: &str) -> PathBuf { + let version = env!("CARGO_PKG_VERSION"); + let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + + let cache = cache_dir(); + // The tarball unpacks to a top-level `{artifact_name}/` dir, so the circuit lives at + // `cache/{artifact_name}/{circuit_name}/`. + let circuit_dir = cache + .join(build_artifact_name(version, &os, &arch)) + .join(circuit_name); + + if circuit_dir.is_dir() { + println!( + "Found a cached {circuit_name} library at {}, reusing.", + circuit_dir.display() + ); + return circuit_dir; + } + + std::fs::create_dir_all(&cache).expect("Failed to create cache directory."); + + println!( + "No cached download found, downloading {circuit_name} v{version} for {os}-{arch}..." + ); + let response = fetch_library(version, &os, &arch, lib_var_name); + println!("Download complete, unpacking..."); + let lib_dir = unpack_library(response, circuit_name, version, &os, &arch, &cache); + println!("Ready, {circuit_name} library at {}.", lib_dir.display()); + lib_dir + } } pub fn build(circuit_name: &str, lib_var_name: &str) { @@ -84,9 +106,22 @@ pub fn build(circuit_name: &str, lib_var_name: &str) { println!("cargo:rerun-if-changed=build.rs"); let lib_dir = std::env::var(lib_var_name).map_or_else( - |_| provision_library(circuit_name, lib_var_name), + |_| { + #[cfg(not(feature = "prebuilt"))] + panic!( + "{lib_var_name} is not set. Either:\n\ + - Set {lib_var_name} to point at a local build, or\n\ + - Enable the `prebuilt` feature to download from GitHub Releases." + ); + + #[cfg(feature = "prebuilt")] + { + println!("{lib_var_name} not set, falling back to prebuilt download."); + prebuilt::provision_library(circuit_name, lib_var_name) + } + }, |lib_dir| { - println!("Using a library directory from {lib_var_name}: {lib_dir}"); + println!("Found {lib_var_name}, using local library at {lib_dir}."); let lib_dir_path = PathBuf::from(lib_dir); assert!( lib_dir_path.is_dir(), diff --git a/rust/logos-blockchain-circuits-poc-sys/Cargo.toml b/rust/logos-blockchain-circuits-poc-sys/Cargo.toml index 01fa797..a3824ed 100644 --- a/rust/logos-blockchain-circuits-poc-sys/Cargo.toml +++ b/rust/logos-blockchain-circuits-poc-sys/Cargo.toml @@ -9,6 +9,9 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +prebuilt = ["lbc-build/prebuilt"] + [lints] workspace = true diff --git a/rust/logos-blockchain-circuits-pol-sys/Cargo.toml b/rust/logos-blockchain-circuits-pol-sys/Cargo.toml index 7bb520e..08481ff 100644 --- a/rust/logos-blockchain-circuits-pol-sys/Cargo.toml +++ b/rust/logos-blockchain-circuits-pol-sys/Cargo.toml @@ -9,6 +9,9 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +prebuilt = ["lbc-build/prebuilt"] + [lints] workspace = true diff --git a/rust/logos-blockchain-circuits-poq-sys/Cargo.toml b/rust/logos-blockchain-circuits-poq-sys/Cargo.toml index d3b8324..3e29099 100644 --- a/rust/logos-blockchain-circuits-poq-sys/Cargo.toml +++ b/rust/logos-blockchain-circuits-poq-sys/Cargo.toml @@ -9,6 +9,9 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +prebuilt = ["lbc-build/prebuilt"] + [lints] workspace = true diff --git a/rust/logos-blockchain-circuits-signature-sys/Cargo.toml b/rust/logos-blockchain-circuits-signature-sys/Cargo.toml index 0a4ec14..04c3e14 100644 --- a/rust/logos-blockchain-circuits-signature-sys/Cargo.toml +++ b/rust/logos-blockchain-circuits-signature-sys/Cargo.toml @@ -9,6 +9,9 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +prebuilt = ["lbc-build/prebuilt"] + [lints] workspace = true