Gate prebuilt mode behind feature flag and use a shared cache.

This commit is contained in:
Alejandro Cabeza Romero 2026-05-14 13:26:49 +02:00
parent 4fccb3927b
commit 9d6ab94d5f
No known key found for this signature in database
GPG Key ID: DA3D14AE478030FD
9 changed files with 243 additions and 80 deletions

View File

@ -22,6 +22,8 @@ allow = [
"CDLA-Permissive-2.0",
"ISC",
"MIT",
"MPL-2.0",
"Unicode-3.0",
"Zlib",
]
private = { ignore = false }

115
rust/Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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 }

View File

@ -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<Body> {
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<Body>,
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<Body> {
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<Body>,
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(),

View File

@ -9,6 +9,9 @@ readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true

View File

@ -9,6 +9,9 @@ readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true

View File

@ -9,6 +9,9 @@ readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true

View File

@ -9,6 +9,9 @@ readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true