From df0bcc16e758462f5417b3078b518d137e333db8 Mon Sep 17 00:00:00 2001 From: Alejandro Cabeza Romero Date: Thu, 7 May 2026 11:32:26 +0200 Subject: [PATCH] Add pre-commit hooks and workspace lint config for formatting, linting, and dependency auditing. --- .cargo-deny.toml | 32 +++ .pre-commit-config.yaml | 46 +++++ hooks/cargo-hack.sh | 3 + justfile | 6 + rust/Cargo.toml | 188 ++++++++++++++++-- .../Cargo.toml | 5 +- .../src/string.rs | 24 ++- .../Cargo.toml | 3 + .../build.rs | 52 +++-- .../src/ffi.rs | 2 +- .../src/lib.rs | 2 +- .../src/native.rs | 71 ++++--- .../Cargo.toml | 3 + .../build.rs | 52 +++-- .../src/ffi.rs | 2 +- .../src/lib.rs | 2 +- .../src/native.rs | 71 ++++--- .../Cargo.toml | 3 + .../build.rs | 52 +++-- .../src/ffi.rs | 2 +- .../src/lib.rs | 2 +- .../src/native.rs | 71 ++++--- .../Cargo.toml | 3 + .../build.rs | 52 +++-- .../src/ffi.rs | 2 +- .../src/lib.rs | 2 +- .../src/native.rs | 70 +++---- .../Cargo.toml | 3 + .../src/ffi/bytes.rs | 8 +- .../src/ffi/mod.rs | 4 +- .../src/ffi/status.rs | 14 +- .../src/ffi/witness_input.rs | 2 +- .../src/lib.rs | 2 +- .../src/native/bytes.rs | 9 +- .../src/native/circuit_witness_input.rs | 10 +- .../src/native/mod.rs | 6 +- .../src/native/status.rs | 26 +-- .../src/native/witness_input.rs | 20 +- 38 files changed, 630 insertions(+), 297 deletions(-) create mode 100644 .cargo-deny.toml create mode 100644 .pre-commit-config.yaml create mode 100755 hooks/cargo-hack.sh diff --git a/.cargo-deny.toml b/.cargo-deny.toml new file mode 100644 index 0000000..3429018 --- /dev/null +++ b/.cargo-deny.toml @@ -0,0 +1,32 @@ +# Config file reference can be found at https://embarkstudios.github.io/cargo-deny/checks/cfg.html. + +[graph] +all-features = true +exclude-dev = true +no-default-features = true + +[advisories] +ignore = [] +unused-ignored-advisory = "deny" +yanked = "deny" + +[bans] +allow-wildcard-paths = false +multiple-versions = "allow" + +[licenses] +allow = [ + "Apache-2.0 WITH LLVM-exception", + "Apache-2.0", + "BSD-3-Clause", + "CDLA-Permissive-2.0", + "ISC", + "MIT", + "Zlib", +] +private = { ignore = false } +unused-allowed-license = "deny" + +[sources] +unknown-git = "deny" +unknown-registry = "deny" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7a4a2a5 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,46 @@ +repos: + - repo: https://github.com/doublify/pre-commit-rust + rev: eeee35a89e69d5772bdee97db1a6a898467b686e # 1.0 + hooks: + - id: fmt + entry: cargo +nightly-2026-02-28 fmt --manifest-path rust/Cargo.toml --all + pass_filenames: false + - id: clippy + name: cargo clippy + entry: cargo clippy --manifest-path rust/Cargo.toml + args: + ["--all", "--all-targets", "--all-features", "--", "-D", "warnings"] + pass_filenames: false + - repo: https://github.com/EmbarkStudios/cargo-deny + rev: 09faadcea2d0d1742492e6872b743d1e4d151a27 # 0.19.0 + hooks: + - id: cargo-deny + args: + - --manifest-path + - rust/Cargo.toml + - --locked + - --all-features + - check + - --hide-inclusion-graph + - -c + - .cargo-deny.toml + - --show-stats + - -D + - warnings + - repo: https://github.com/ComPWA/taplo-pre-commit + rev: 6355f0e9a28a910b80c91f7f6521c03d0ea50fba # 0.9.3 # Can't update until: https://github.com/tamasfe/taplo/issues/805 + hooks: + - id: taplo-format + - id: taplo-lint + - repo: https://github.com/bnjbvr/cargo-machete + rev: 78beac95c8fd7c25bdfb194415128523e41512d5 # 0.19.1 + hooks: + - id: cargo-machete + args: ["rust/"] + - repo: local + hooks: + - id: cargo-hack-check + language: script + name: cargo hack check + entry: ./hooks/cargo-hack.sh + stages: [manual] diff --git a/hooks/cargo-hack.sh b/hooks/cargo-hack.sh new file mode 100755 index 0000000..5968b76 --- /dev/null +++ b/hooks/cargo-hack.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +RUSTFLAGS="-D warnings" cargo hack --feature-powerset --no-dev-deps --keep-going --all-targets check diff --git a/justfile b/justfile index ebfcfab..a81c249 100644 --- a/justfile +++ b/justfile @@ -1,3 +1,4 @@ +cargo_root := justfile_directory() + "/rust" src := justfile_directory() + "/src" ci_makefile := justfile_directory() + "/.github/resources/witness-generator/Makefile" circom_version := "2.2.2" # This version must match the version used in the CI @@ -5,6 +6,11 @@ circom_version := "2.2.2" # This version must match the version used in the CI os := `uname -s` sed_i := if os == "Darwin" { "sed -i ''" } else { "sed -i" } +# Shortcut to run cargo commands from the repo root (the Cargo workspace lives under ./rust/). +# Potentially temporary. +cargo +args: + cd {{cargo_root}} && cargo {{args}} + # Verify the installed circom matches the pinned version. check-circom: @circom --version | grep -qF "{{circom_version}}" || \ diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 6cebe43..df0632f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,32 +1,184 @@ [workspace.package] -categories = ["cryptography", "external-ffi-bindings"] +categories = ["cryptography", "external-ffi-bindings"] description = "Rust bindings for the Logos Blockchain Circuits, providing a safe and efficient interface for interacting with the underlying cryptographic circuits." -edition = "2024" -keywords = ["blockchain", "privacy"] -license = "MIT or Apache-2.0" -readme = "README.md" -repository = "https://github.com/logos-blockchain/logos-blockchain-circuits" -version = "0.4.2" +edition = "2024" +keywords = ["blockchain", "privacy"] +license = "MIT or Apache-2.0" +readme = "README.md" +repository = "https://github.com/logos-blockchain/logos-blockchain-circuits" +version = "0.4.2" [workspace] members = [ - "logos-blockchain-circuits-poc-sys", - "logos-blockchain-circuits-pol-sys", - "logos-blockchain-circuits-poq-sys", - "logos-blockchain-circuits-signature-sys", - "logos-blockchain-circuits-types", - "logos-blockchain-circuits-common" + "logos-blockchain-circuits-poc-sys", + "logos-blockchain-circuits-pol-sys", + "logos-blockchain-circuits-poq-sys", + "logos-blockchain-circuits-signature-sys", + "logos-blockchain-circuits-types", + "logos-blockchain-circuits-common", ] resolver = "3" +# Sourced from https://github.com/logos-co/nomos/blob/main/Cargo.toml +[workspace.lints.clippy] + +# Cargo and allowed cargo warnings (new lints will warn by default) +cargo = { level = "warn", priority = -1 } + +# TODO: We should tackle this lint at some point, to reduce dependencies duplication, reduce compilation times and bring down our binary size. +multiple_crate_versions = { level = "allow" } + +# Nursery and allowed nursery warnings (new lints will warn by default) +nursery = { level = "warn", priority = -1 } + +# Pedantic and allowed pedantic warnings (new lints will warn by default) +pedantic = { level = "warn", priority = -1 } + +similar_names = { level = "allow" } + +# Restriction and allowed restriction warnings (new lints will warn by default) +restriction = { level = "warn", priority = -1 } + +absolute_paths = { level = "allow" } +alloc_instead_of_core = { level = "allow" } +arbitrary_source_item_ordering = { level = "allow" } +big_endian_bytes = { level = "allow" } +blanket_clippy_restriction_lints = { level = "allow" } +decimal_literal_representation = { level = "allow" } +default_numeric_fallback = { level = "allow" } +deref_by_slicing = { level = "allow" } +else_if_without_else = { level = "allow" } +exhaustive_enums = { level = "allow" } +exhaustive_structs = { level = "allow" } +exit = { level = "allow" } +expect_used = { level = "allow" } +field_scoped_visibility_modifiers = { level = "allow" } +float_arithmetic = { level = "allow" } +get_unwrap = { level = "allow" } +host_endian_bytes = { level = "allow" } +implicit_return = { level = "allow" } +integer_division_remainder_used = { level = "allow" } +iter_over_hash_type = { level = "allow" } +let_underscore_must_use = { level = "allow" } +let_underscore_untyped = { level = "allow" } +little_endian_bytes = { level = "allow" } +map_err_ignore = { level = "allow" } +min_ident_chars = { level = "allow" } +missing_asserts_for_indexing = { level = "allow" } +missing_docs_in_private_items = { level = "allow" } +missing_inline_in_public_items = { level = "allow" } +missing_trait_methods = { level = "allow" } +mixed_read_write_in_expression = { level = "allow" } +mod_module_files = { level = "allow" } +module_name_repetitions = { level = "allow" } +modulo_arithmetic = { level = "allow" } +panic = { level = "allow" } +panic_in_result_fn = { level = "allow" } +partial_pub_fields = { level = "allow" } +print_stderr = { level = "allow" } +print_stdout = { level = "allow" } +pub_use = { level = "allow" } +pub_with_shorthand = { level = "allow" } +question_mark_used = { level = "allow" } +self_named_module_files = { level = "allow" } +semicolon_inside_block = { level = "allow" } +single_call_fn = { level = "allow" } +single_char_lifetime_names = { level = "allow" } +std_instead_of_alloc = { level = "allow" } +std_instead_of_core = { level = "allow" } +struct_field_names = { level = "allow" } +unseparated_literal_suffix = { level = "allow" } +use_debug = { level = "allow" } +wildcard_enum_match_arm = { level = "allow" } + +# TODO: Address these lints at some point. To allow them, move them to the section where they belong (e.g., pedantic), and keep allowing them. To enforce them, since all lints are "warn" by default, just remove them from this list. +arithmetic_side_effects = { level = "allow" } +as_conversions = { level = "allow" } +as_pointer_underscore = { level = "allow" } +as_underscore = { level = "allow" } +assertions_on_result_states = { level = "allow" } +cast_possible_truncation = { level = "allow" } +cast_possible_wrap = { level = "allow" } +cast_precision_loss = { level = "allow" } +cast_sign_loss = { level = "allow" } +doc_paragraphs_missing_punctuation = { level = "allow" } +error_impl_error = { level = "allow" } +impl_trait_in_params = { level = "allow" } +indexing_slicing = { level = "allow" } +infinite_loop = { level = "allow" } +integer_division = { level = "allow" } +large_stack_frames = { level = "allow" } +missing_assert_message = { level = "allow" } +missing_errors_doc = { level = "allow" } +missing_panics_doc = { level = "allow" } +pattern_type_mismatch = { level = "allow" } +redundant_test_prefix = { level = "allow" } +ref_patterns = { level = "allow" } +renamed_function_params = { level = "allow" } +same_name_method = { level = "allow" } +shadow_reuse = { level = "allow" } +shadow_same = { level = "allow" } +shadow_unrelated = { level = "allow" } +tests_outside_test_module = { level = "allow" } +todo = { level = "allow" } +unchecked_time_subtraction = { level = "allow" } +unimplemented = { level = "allow" } +unreachable = { level = "allow" } +unwrap_in_result = { level = "allow" } +unwrap_used = { level = "allow" } + +[workspace.lints.rust] + +# Explicitly allowed lints + +unused_crate_dependencies = { level = "allow" } # Too many false positives especially around benchmarking and binaries, which do not have their own `dependencies` section yet. Plus, we have cargo-machete checking unused deps. +unused_results = { level = "allow" } # We have Clippy lints to warn on unused `must_use` results. This is too pedantic as it complains on EVERY unused result. + +# Lints which are allow-by-default but have been changed to "warn" +ambiguous_negative_literals = { level = "warn" } +closure_returning_async_block = { level = "warn" } +deref_into_dyn_supertrait = { level = "warn" } +impl_trait_redundant_captures = { level = "warn" } +let_underscore_drop = { level = "warn" } +macro_use_extern_crate = { level = "warn" } +missing_unsafe_on_extern = { level = "warn" } +redundant_imports = { level = "warn" } +redundant_lifetimes = { level = "warn" } +single_use_lifetimes = { level = "warn" } +tail_expr_drop_order = { level = "warn" } +trivial_numeric_casts = { level = "warn" } +unit_bindings = { level = "warn" } +unsafe_attr_outside_unsafe = { level = "warn" } +unsafe_op_in_unsafe_fn = { level = "warn" } +unstable_features = { level = "warn" } +unused_extern_crates = { level = "warn" } +unused_import_braces = { level = "warn" } +unused_lifetimes = { level = "warn" } +unused_macro_rules = { level = "warn" } +unused_qualifications = { level = "warn" } + +# TODO: Address these allow-by-default Rustc lints at some point. To allow them, move them to the list of explicitly allowed lints. To enforce them, move them to the list of explicitly warned ones. +absolute_paths_not_starting_with_crate = { level = "allow" } +elided_lifetimes_in_paths = { level = "allow" } +ffi_unwind_calls = { level = "allow" } +impl_trait_overcaptures = { level = "allow" } +linker_messages = { level = "allow" } +missing_copy_implementations = { level = "allow" } +missing_debug_implementations = { level = "allow" } +missing_docs = { level = "allow" } +trivial_casts = { level = "allow" } +unreachable_pub = { level = "allow" } +unsafe_code = { level = "allow" } +variant_size_differences = { level = "allow" } + [workspace.dependencies] # Internal -lbc-poc-sys = { default-features = false, package = "logos-blockchain-circuits-poc-sys", path = "./logos-blockchain-circuits-poc-sys" } -lbc-pol-sys = { default-features = false, package = "logos-blockchain-circuits-pol-sys", path = "./logos-blockchain-circuits-pol-sys" } -lbc-poq-sys = { default-features = false, package = "logos-blockchain-circuits-poq-sys", path = "./logos-blockchain-circuits-poq-sys" } +lbc-poc-sys = { default-features = false, package = "logos-blockchain-circuits-poc-sys", path = "./logos-blockchain-circuits-poc-sys" } +lbc-pol-sys = { default-features = false, package = "logos-blockchain-circuits-pol-sys", path = "./logos-blockchain-circuits-pol-sys" } +lbc-poq-sys = { default-features = false, package = "logos-blockchain-circuits-poq-sys", path = "./logos-blockchain-circuits-poq-sys" } lbc-signature-sys = { default-features = false, package = "logos-blockchain-circuits-signature-sys", path = "./logos-blockchain-circuits-signature-sys" } -lbc-types = { default-features = false, package = "logos-blockchain-circuits-types", path = "./logos-blockchain-circuits-types" } -lbc-common = { default-features = false, package = "logos-blockchain-circuits-common", path = "./logos-blockchain-circuits-common" } +lbc-types = { default-features = false, package = "logos-blockchain-circuits-types", path = "./logos-blockchain-circuits-types" } +lbc-common = { default-features = false, package = "logos-blockchain-circuits-common", path = "./logos-blockchain-circuits-common" } # External flate2 = "^1" diff --git a/rust/logos-blockchain-circuits-common/Cargo.toml b/rust/logos-blockchain-circuits-common/Cargo.toml index edb780c..363c5ae 100644 --- a/rust/logos-blockchain-circuits-common/Cargo.toml +++ b/rust/logos-blockchain-circuits-common/Cargo.toml @@ -9,5 +9,8 @@ readme.workspace = true repository.workspace = true version.workspace = true +[lints] +workspace = true + [dependencies] -lbc-types = { workspace = true} +lbc-types = { workspace = true } diff --git a/rust/logos-blockchain-circuits-common/src/string.rs b/rust/logos-blockchain-circuits-common/src/string.rs index fbaeff5..77e99f9 100644 --- a/rust/logos-blockchain-circuits-common/src/string.rs +++ b/rust/logos-blockchain-circuits-common/src/string.rs @@ -1,16 +1,20 @@ -use std::path::Path; use lbc_types::native::Error; +use std::path::Path; -pub fn as_null_terminated_string( - string: &str, -) -> Result { - std::ffi::CString::new(string) - .map_err(|error| Error::InvalidInput(Some(format!("Could not convert string to CString: {error}")))) +pub fn as_null_terminated_string(string: &str) -> Result { + std::ffi::CString::new(string).map_err(|error| { + Error::InvalidInput(Some(format!( + "Could not convert string to CString: {error}" + ))) + }) } -pub fn path_as_null_terminated_string( - path: &Path, -) -> Result { - let path = path.to_str().ok_or(Error::InvalidInput(Some(format!("Could not convert the path to a string: {}", path.display()))))?; +pub fn path_as_null_terminated_string(path: &Path) -> Result { + let path = path.to_str().ok_or_else(|| { + Error::InvalidInput(Some(format!( + "Could not convert the path to a string: {}", + path.display() + ))) + })?; as_null_terminated_string(path) } diff --git a/rust/logos-blockchain-circuits-poc-sys/Cargo.toml b/rust/logos-blockchain-circuits-poc-sys/Cargo.toml index 8b0dc92..4302dd5 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 +[lints] +workspace = true + [dependencies] lbc-types = { workspace = true } lbc-common = { workspace = true } diff --git a/rust/logos-blockchain-circuits-poc-sys/build.rs b/rust/logos-blockchain-circuits-poc-sys/build.rs index 8b83fca..1401e8e 100644 --- a/rust/logos-blockchain-circuits-poc-sys/build.rs +++ b/rust/logos-blockchain-circuits-poc-sys/build.rs @@ -5,7 +5,6 @@ use ureq::http::Response; static CIRCUIT_NAME: &str = "poc"; static LIB_VAR_NAME: &str = "LBC_POC_LIB_DIR"; - fn get_artifact_name(version: &str, os: &str, arch: &str) -> String { format!("logos-blockchain-circuits-v{version}-{os}-{arch}") } @@ -29,17 +28,27 @@ fn fetch_library(version: &str, os: &str, arch: &str) -> Response { }) } -fn unpack_library(response: Response, version: &str, os: &str, arch: &str, output_dir: &Path) -> PathBuf { +fn unpack_library( + response: Response, + 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."); + archive + .unpack(output_dir) + .expect("Failed to unpack the downloaded archive."); let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch)); let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME); - if !unpacked_library_directory.exists() { - panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display()); - } + assert!( + unpacked_library_directory.is_dir(), + "Failed to find the unpacked library at {}", + unpacked_library_directory.display() + ); unpacked_library_directory } @@ -50,9 +59,14 @@ fn provision_library() -> PathBuf { 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(get_artifact_name(version, &os, &arch)).join(CIRCUIT_NAME); + let expected_library_directory = out_dir + .join(get_artifact_name(version, &os, &arch)) + .join(CIRCUIT_NAME); if expected_library_directory.exists() { - println!("Found an existing library at {}. Reusing it.", expected_library_directory.display()); + println!( + "Found an existing library at {}. Reusing it.", + expected_library_directory.display() + ); return expected_library_directory; } @@ -66,21 +80,23 @@ fn main() { println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-changed=build.rs"); - let lib_dir = std::env::var(LIB_VAR_NAME).map( + let lib_dir = std::env::var(LIB_VAR_NAME).map_or_else( + |_| provision_library(), |lib_dir| { println!("Using a library directory from {LIB_VAR_NAME}: {lib_dir}"); let lib_dir_path = PathBuf::from(lib_dir); - if !lib_dir_path.exists() { - panic!("The library directory specified in {LIB_VAR_NAME} at {} does not exist.", lib_dir_path.display()); - - } + assert!( + lib_dir_path.is_dir(), + "The library directory specified in {LIB_VAR_NAME} at {} does not exist.", + lib_dir_path.display() + ); lib_dir_path - } - ).unwrap_or_else(|_| { - provision_library() - }); + }, + ); - let lib_dir = lib_dir.to_str().expect("Failed to convert the library directory path to a string"); + let lib_dir = lib_dir + .to_str() + .expect("Failed to convert the library directory path to a string"); println!("cargo:rustc-env={LIB_VAR_NAME}={lib_dir}"); println!("cargo:rustc-link-search=native={lib_dir}"); println!("cargo:rustc-link-lib=static={CIRCUIT_NAME}"); diff --git a/rust/logos-blockchain-circuits-poc-sys/src/ffi.rs b/rust/logos-blockchain-circuits-poc-sys/src/ffi.rs index aeddef5..fb6b3e4 100644 --- a/rust/logos-blockchain-circuits-poc-sys/src/ffi.rs +++ b/rust/logos-blockchain-circuits-poc-sys/src/ffi.rs @@ -1,5 +1,5 @@ -use std::ffi::c_char; use lbc_types::ffi::{Bytes, Status, WitnessInput}; +use std::ffi::c_char; unsafe extern "C" { pub fn poc_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status; diff --git a/rust/logos-blockchain-circuits-poc-sys/src/lib.rs b/rust/logos-blockchain-circuits-poc-sys/src/lib.rs index aea275f..a90e5f1 100644 --- a/rust/logos-blockchain-circuits-poc-sys/src/lib.rs +++ b/rust/logos-blockchain-circuits-poc-sys/src/lib.rs @@ -1,4 +1,4 @@ mod ffi; pub mod native; -pub use native::{generate_witness, generate_witness_from_files, PocWitnessInput}; +pub use native::{PocWitnessInput, generate_witness, generate_witness_from_files}; diff --git a/rust/logos-blockchain-circuits-poc-sys/src/native.rs b/rust/logos-blockchain-circuits-poc-sys/src/native.rs index c0e2288..74d1130 100644 --- a/rust/logos-blockchain-circuits-poc-sys/src/native.rs +++ b/rust/logos-blockchain-circuits-poc-sys/src/native.rs @@ -1,9 +1,13 @@ -use std::path::Path; -use lbc_types::{ffi, native::{Bytes, Error}}; -use lbc_common::string::path_as_null_terminated_string; use crate::ffi::{poc_generate_witness, poc_generate_witness_from_files}; +use lbc_common::string::path_as_null_terminated_string; +use lbc_types::{ + ffi, + native::{Bytes, Error}, +}; +use std::path::Path; -static RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_POC_LIB_DIR"), "/witness_generator.dat")); +static RAW_CIRCUIT_DAT: &[u8] = + include_bytes!(concat!(env!("LBC_POC_LIB_DIR"), "/witness_generator.dat")); pub struct PocDat; impl<'dat> lbc_types::CircuitDat<'dat> for PocDat { @@ -12,74 +16,67 @@ impl<'dat> lbc_types::CircuitDat<'dat> for PocDat { pub type PocWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, PocDat>; -pub fn generate_witness( - input: PocWitnessInput, -) -> Result { +pub fn generate_witness(input: &PocWitnessInput) -> Result { let ffi_input_guard = input.as_ffi(); let ffi_input = ffi_input_guard.as_ref(); let mut ffi_output_bytes = ffi::Bytes::null(); - let status = unsafe { - poc_generate_witness( - ffi_input as *const ffi::WitnessInput, - &mut ffi_output_bytes as *mut ffi::Bytes - ) - }; + // SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes. + let status = + unsafe { poc_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes) }; - status.try_into().map(|()| { Bytes::from(ffi_output_bytes) }) + status.try_into().map(|()| Bytes::from(ffi_output_bytes)) } -pub fn generate_witness_from_files( - dat: &Path, - inputs: &Path, - output: &Path, -) -> Result<(), Error> { +pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> { let c_dat = path_as_null_terminated_string(dat)?; let c_inputs = path_as_null_terminated_string(inputs)?; let c_output = path_as_null_terminated_string(output)?; - unsafe { - poc_generate_witness_from_files( - c_dat.as_ptr(), - c_inputs.as_ptr(), - c_output.as_ptr(), - ) - }.try_into() + // SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call. + unsafe { poc_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr()) } + .try_into() } #[cfg(test)] mod tests { + use super::{PocWitnessInput, generate_witness, generate_witness_from_files}; use std::path::PathBuf; use std::sync::LazyLock; - use super::{generate_witness, generate_witness_from_files, PocWitnessInput}; static LIB_DIR: LazyLock = LazyLock::new(|| { const ENV_VAR: &str = "LBC_POC_LIB_DIR"; PathBuf::from( - std::env::var(ENV_VAR) - .expect(format!("Environment variable '{ENV_VAR}' must be available, as provided by the build script.").as_str()), + std::env::var(ENV_VAR).unwrap_or_else( + |_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."), + ) ) }); - static INPUTS: LazyLock = LazyLock::new(|| { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json") - }); + static INPUTS: LazyLock = + LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json")); #[test] fn test_generate_witness() { let dat = LIB_DIR.join("witness_generator"); let witness_output_path = std::env::temp_dir().join("poc_test_witness.wtns"); - generate_witness_from_files(&dat, &*INPUTS, &witness_output_path) + generate_witness_from_files(&dat, &INPUTS, &witness_output_path) .expect("generate_witness_from_files failed."); let inputs_json = std::fs::read_to_string(&*INPUTS) - .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); + .unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display())); - let input = PocWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); - let output = generate_witness(input).expect("generate_witness failed."); + let input = PocWitnessInput::new(inputs_json) + .expect("Failed to construct the input for the witness generator."); + let output = generate_witness(&input).expect("generate_witness failed."); - let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); + let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| { + panic!( + "Failed to read the generated witness from {}.", + witness_output_path.display() + ) + }); assert_eq!(output.as_slice(), expected.as_slice()); } } diff --git a/rust/logos-blockchain-circuits-pol-sys/Cargo.toml b/rust/logos-blockchain-circuits-pol-sys/Cargo.toml index 012be26..99d9de2 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 +[lints] +workspace = true + [dependencies] lbc-types = { workspace = true } lbc-common = { workspace = true } diff --git a/rust/logos-blockchain-circuits-pol-sys/build.rs b/rust/logos-blockchain-circuits-pol-sys/build.rs index 700d69e..a658ef8 100644 --- a/rust/logos-blockchain-circuits-pol-sys/build.rs +++ b/rust/logos-blockchain-circuits-pol-sys/build.rs @@ -5,7 +5,6 @@ use ureq::http::Response; static CIRCUIT_NAME: &str = "pol"; static LIB_VAR_NAME: &str = "LBC_POL_LIB_DIR"; - fn get_artifact_name(version: &str, os: &str, arch: &str) -> String { format!("logos-blockchain-circuits-v{version}-{os}-{arch}") } @@ -29,17 +28,27 @@ fn fetch_library(version: &str, os: &str, arch: &str) -> Response { }) } -fn unpack_library(response: Response, version: &str, os: &str, arch: &str, output_dir: &Path) -> PathBuf { +fn unpack_library( + response: Response, + 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."); + archive + .unpack(output_dir) + .expect("Failed to unpack the downloaded archive."); let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch)); let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME); - if !unpacked_library_directory.exists() { - panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display()); - } + assert!( + unpacked_library_directory.is_dir(), + "Failed to find the unpacked library at {}", + unpacked_library_directory.display() + ); unpacked_library_directory } @@ -50,9 +59,14 @@ fn provision_library() -> PathBuf { 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(get_artifact_name(version, &os, &arch)).join(CIRCUIT_NAME); + let expected_library_directory = out_dir + .join(get_artifact_name(version, &os, &arch)) + .join(CIRCUIT_NAME); if expected_library_directory.exists() { - println!("Found an existing library at {}. Reusing it.", expected_library_directory.display()); + println!( + "Found an existing library at {}. Reusing it.", + expected_library_directory.display() + ); return expected_library_directory; } @@ -66,21 +80,23 @@ fn main() { println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-changed=build.rs"); - let lib_dir = std::env::var(LIB_VAR_NAME).map( + let lib_dir = std::env::var(LIB_VAR_NAME).map_or_else( + |_| provision_library(), |lib_dir| { println!("Using a library directory from {LIB_VAR_NAME}: {lib_dir}"); let lib_dir_path = PathBuf::from(lib_dir); - if !lib_dir_path.exists() { - panic!("The library directory specified in {LIB_VAR_NAME} at {} does not exist.", lib_dir_path.display()); - - } + assert!( + lib_dir_path.is_dir(), + "The library directory specified in {LIB_VAR_NAME} at {} does not exist.", + lib_dir_path.display() + ); lib_dir_path - } - ).unwrap_or_else(|_| { - provision_library() - }); + }, + ); - let lib_dir = lib_dir.to_str().expect("Failed to convert the library directory path to a string"); + let lib_dir = lib_dir + .to_str() + .expect("Failed to convert the library directory path to a string"); println!("cargo:rustc-env={LIB_VAR_NAME}={lib_dir}"); println!("cargo:rustc-link-search=native={lib_dir}"); println!("cargo:rustc-link-lib=static={CIRCUIT_NAME}"); diff --git a/rust/logos-blockchain-circuits-pol-sys/src/ffi.rs b/rust/logos-blockchain-circuits-pol-sys/src/ffi.rs index e944990..2ce750a 100644 --- a/rust/logos-blockchain-circuits-pol-sys/src/ffi.rs +++ b/rust/logos-blockchain-circuits-pol-sys/src/ffi.rs @@ -1,5 +1,5 @@ -use std::ffi::c_char; use lbc_types::ffi::{Bytes, Status, WitnessInput}; +use std::ffi::c_char; unsafe extern "C" { pub fn pol_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status; diff --git a/rust/logos-blockchain-circuits-pol-sys/src/lib.rs b/rust/logos-blockchain-circuits-pol-sys/src/lib.rs index f425030..2c5a80c 100644 --- a/rust/logos-blockchain-circuits-pol-sys/src/lib.rs +++ b/rust/logos-blockchain-circuits-pol-sys/src/lib.rs @@ -1,4 +1,4 @@ mod ffi; pub mod native; -pub use native::{generate_witness, generate_witness_from_files, PolWitnessInput}; +pub use native::{PolWitnessInput, generate_witness, generate_witness_from_files}; diff --git a/rust/logos-blockchain-circuits-pol-sys/src/native.rs b/rust/logos-blockchain-circuits-pol-sys/src/native.rs index ab4b54e..9862c59 100644 --- a/rust/logos-blockchain-circuits-pol-sys/src/native.rs +++ b/rust/logos-blockchain-circuits-pol-sys/src/native.rs @@ -1,9 +1,13 @@ -use std::path::Path; -use lbc_types::{ffi, native::{Bytes, Error}}; -use lbc_common::string::path_as_null_terminated_string; use crate::ffi::{pol_generate_witness, pol_generate_witness_from_files}; +use lbc_common::string::path_as_null_terminated_string; +use lbc_types::{ + ffi, + native::{Bytes, Error}, +}; +use std::path::Path; -static RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_POL_LIB_DIR"), "/witness_generator.dat")); +static RAW_CIRCUIT_DAT: &[u8] = + include_bytes!(concat!(env!("LBC_POL_LIB_DIR"), "/witness_generator.dat")); pub struct PolDat; impl<'dat> lbc_types::CircuitDat<'dat> for PolDat { @@ -12,74 +16,67 @@ impl<'dat> lbc_types::CircuitDat<'dat> for PolDat { pub type PolWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, PolDat>; -pub fn generate_witness( - input: PolWitnessInput, -) -> Result { +pub fn generate_witness(input: &PolWitnessInput) -> Result { let ffi_input_guard = input.as_ffi(); let ffi_input = ffi_input_guard.as_ref(); let mut ffi_output_bytes = ffi::Bytes::null(); - let status = unsafe { - pol_generate_witness( - ffi_input as *const ffi::WitnessInput, - &mut ffi_output_bytes as *mut ffi::Bytes - ) - }; + // SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes. + let status = + unsafe { pol_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes) }; - status.try_into().map(|()| { Bytes::from(ffi_output_bytes) }) + status.try_into().map(|()| Bytes::from(ffi_output_bytes)) } -pub fn generate_witness_from_files( - dat: &Path, - inputs: &Path, - output: &Path, -) -> Result<(), Error> { +pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> { let c_dat = path_as_null_terminated_string(dat)?; let c_inputs = path_as_null_terminated_string(inputs)?; let c_output = path_as_null_terminated_string(output)?; - unsafe { - pol_generate_witness_from_files( - c_dat.as_ptr(), - c_inputs.as_ptr(), - c_output.as_ptr(), - ) - }.try_into() + // SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call. + unsafe { pol_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr()) } + .try_into() } #[cfg(test)] mod tests { + use super::{PolWitnessInput, generate_witness, generate_witness_from_files}; use std::path::PathBuf; use std::sync::LazyLock; - use super::{generate_witness, generate_witness_from_files, PolWitnessInput}; static LIB_DIR: LazyLock = LazyLock::new(|| { const ENV_VAR: &str = "LBC_POL_LIB_DIR"; PathBuf::from( - std::env::var(ENV_VAR) - .expect(format!("Environment variable '{ENV_VAR}' must be available, as provided by the build script.").as_str()), + std::env::var(ENV_VAR).unwrap_or_else( + |_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."), + ) ) }); - static INPUTS: LazyLock = LazyLock::new(|| { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json") - }); + static INPUTS: LazyLock = + LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json")); #[test] fn test_generate_witness() { let dat = LIB_DIR.join("witness_generator"); let witness_output_path = std::env::temp_dir().join("pol_test_witness.wtns"); - generate_witness_from_files(&dat, &*INPUTS, &witness_output_path) + generate_witness_from_files(&dat, &INPUTS, &witness_output_path) .expect("generate_witness_from_files failed."); let inputs_json = std::fs::read_to_string(&*INPUTS) - .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); + .unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display())); - let input = PolWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); - let output = generate_witness(input).expect("generate_witness failed."); + let input = PolWitnessInput::new(inputs_json) + .expect("Failed to construct the input for the witness generator."); + let output = generate_witness(&input).expect("generate_witness failed."); - let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); + let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| { + panic!( + "Failed to read the generated witness from {}.", + witness_output_path.display() + ) + }); assert_eq!(output.as_slice(), expected.as_slice()); } } diff --git a/rust/logos-blockchain-circuits-poq-sys/Cargo.toml b/rust/logos-blockchain-circuits-poq-sys/Cargo.toml index a3e919d..a9cdc2a 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 +[lints] +workspace = true + [dependencies] lbc-types = { workspace = true } lbc-common = { workspace = true } diff --git a/rust/logos-blockchain-circuits-poq-sys/build.rs b/rust/logos-blockchain-circuits-poq-sys/build.rs index efa8175..12dbdb5 100644 --- a/rust/logos-blockchain-circuits-poq-sys/build.rs +++ b/rust/logos-blockchain-circuits-poq-sys/build.rs @@ -5,7 +5,6 @@ use ureq::http::Response; static CIRCUIT_NAME: &str = "poq"; static LIB_VAR_NAME: &str = "LBC_POQ_LIB_DIR"; - fn get_artifact_name(version: &str, os: &str, arch: &str) -> String { format!("logos-blockchain-circuits-v{version}-{os}-{arch}") } @@ -29,17 +28,27 @@ fn fetch_library(version: &str, os: &str, arch: &str) -> Response { }) } -fn unpack_library(response: Response, version: &str, os: &str, arch: &str, output_dir: &Path) -> PathBuf { +fn unpack_library( + response: Response, + 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."); + archive + .unpack(output_dir) + .expect("Failed to unpack the downloaded archive."); let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch)); let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME); - if !unpacked_library_directory.exists() { - panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display()); - } + assert!( + unpacked_library_directory.is_dir(), + "Failed to find the unpacked library at {}", + unpacked_library_directory.display() + ); unpacked_library_directory } @@ -50,9 +59,14 @@ fn provision_library() -> PathBuf { 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(get_artifact_name(version, &os, &arch)).join(CIRCUIT_NAME); + let expected_library_directory = out_dir + .join(get_artifact_name(version, &os, &arch)) + .join(CIRCUIT_NAME); if expected_library_directory.exists() { - println!("Found an existing library at {}. Reusing it.", expected_library_directory.display()); + println!( + "Found an existing library at {}. Reusing it.", + expected_library_directory.display() + ); return expected_library_directory; } @@ -77,21 +91,23 @@ fn main() { println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-changed=build.rs"); - let lib_dir = std::env::var(LIB_VAR_NAME).map( + let lib_dir = std::env::var(LIB_VAR_NAME).map_or_else( + |_| provision_library(), |lib_dir| { println!("Using a library directory from {LIB_VAR_NAME}: {lib_dir}"); let lib_dir_path = PathBuf::from(lib_dir); - if !lib_dir_path.exists() { - panic!("The library directory specified in {LIB_VAR_NAME} at {} does not exist.", lib_dir_path.display()); - - } + assert!( + lib_dir_path.is_dir(), + "The library directory specified in {LIB_VAR_NAME} at {} does not exist.", + lib_dir_path.display() + ); lib_dir_path - } - ).unwrap_or_else(|_| { - provision_library() - }); + }, + ); - let lib_dir = lib_dir.to_str().expect("Failed to convert the library directory path to a string"); + let lib_dir = lib_dir + .to_str() + .expect("Failed to convert the library directory path to a string"); println!("cargo:rustc-env={LIB_VAR_NAME}={lib_dir}"); println!("cargo:rustc-link-search=native={lib_dir}"); println!("cargo:rustc-link-lib=static={CIRCUIT_NAME}"); diff --git a/rust/logos-blockchain-circuits-poq-sys/src/ffi.rs b/rust/logos-blockchain-circuits-poq-sys/src/ffi.rs index c414882..2ac1108 100644 --- a/rust/logos-blockchain-circuits-poq-sys/src/ffi.rs +++ b/rust/logos-blockchain-circuits-poq-sys/src/ffi.rs @@ -1,5 +1,5 @@ -use std::ffi::c_char; use lbc_types::ffi::{Bytes, Status, WitnessInput}; +use std::ffi::c_char; unsafe extern "C" { pub fn poq_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status; diff --git a/rust/logos-blockchain-circuits-poq-sys/src/lib.rs b/rust/logos-blockchain-circuits-poq-sys/src/lib.rs index efb86b6..b52fc8e 100644 --- a/rust/logos-blockchain-circuits-poq-sys/src/lib.rs +++ b/rust/logos-blockchain-circuits-poq-sys/src/lib.rs @@ -1,4 +1,4 @@ mod ffi; pub mod native; -pub use native::{generate_witness, generate_witness_from_files, PoqWitnessInput}; +pub use native::{PoqWitnessInput, generate_witness, generate_witness_from_files}; diff --git a/rust/logos-blockchain-circuits-poq-sys/src/native.rs b/rust/logos-blockchain-circuits-poq-sys/src/native.rs index 2e65bc4..8f504f1 100644 --- a/rust/logos-blockchain-circuits-poq-sys/src/native.rs +++ b/rust/logos-blockchain-circuits-poq-sys/src/native.rs @@ -1,9 +1,13 @@ -use std::path::Path; -use lbc_types::{ffi, native::{Bytes, Error}}; -use lbc_common::string::path_as_null_terminated_string; use crate::ffi::{poq_generate_witness, poq_generate_witness_from_files}; +use lbc_common::string::path_as_null_terminated_string; +use lbc_types::{ + ffi, + native::{Bytes, Error}, +}; +use std::path::Path; -static RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_POQ_LIB_DIR"), "/witness_generator.dat")); +static RAW_CIRCUIT_DAT: &[u8] = + include_bytes!(concat!(env!("LBC_POQ_LIB_DIR"), "/witness_generator.dat")); pub struct PoqDat; impl<'dat> lbc_types::CircuitDat<'dat> for PoqDat { @@ -12,74 +16,67 @@ impl<'dat> lbc_types::CircuitDat<'dat> for PoqDat { pub type PoqWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, PoqDat>; -pub fn generate_witness( - input: PoqWitnessInput, -) -> Result { +pub fn generate_witness(input: &PoqWitnessInput) -> Result { let ffi_input_guard = input.as_ffi(); let ffi_input = ffi_input_guard.as_ref(); let mut ffi_output_bytes = ffi::Bytes::null(); - let status = unsafe { - poq_generate_witness( - ffi_input as *const ffi::WitnessInput, - &mut ffi_output_bytes as *mut ffi::Bytes - ) - }; + // SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes. + let status = + unsafe { poq_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes) }; - status.try_into().map(|()| { Bytes::from(ffi_output_bytes) }) + status.try_into().map(|()| Bytes::from(ffi_output_bytes)) } -pub fn generate_witness_from_files( - dat: &Path, - inputs: &Path, - output: &Path, -) -> Result<(), Error> { +pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> { let c_dat = path_as_null_terminated_string(dat)?; let c_inputs = path_as_null_terminated_string(inputs)?; let c_output = path_as_null_terminated_string(output)?; - unsafe { - poq_generate_witness_from_files ( - c_dat.as_ptr(), - c_inputs.as_ptr(), - c_output.as_ptr(), - ) - }.try_into() + // SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call. + unsafe { poq_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr()) } + .try_into() } #[cfg(test)] mod tests { + use super::{PoqWitnessInput, generate_witness, generate_witness_from_files}; use std::path::PathBuf; use std::sync::LazyLock; - use super::{generate_witness, generate_witness_from_files, PoqWitnessInput}; static LIB_DIR: LazyLock = LazyLock::new(|| { const ENV_VAR: &str = "LBC_POQ_LIB_DIR"; PathBuf::from( - std::env::var(ENV_VAR) - .expect(format!("Environment variable '{ENV_VAR}' must be available, as provided by the build script.").as_str()), + std::env::var(ENV_VAR).unwrap_or_else( + |_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."), + ) ) }); - static INPUTS: LazyLock = LazyLock::new(|| { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json") - }); + static INPUTS: LazyLock = + LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json")); #[test] fn test_generate_witness() { let dat = LIB_DIR.join("witness_generator"); let witness_output_path = std::env::temp_dir().join("poq_test_witness.wtns"); - generate_witness_from_files(&dat, &*INPUTS, &witness_output_path) + generate_witness_from_files(&dat, &INPUTS, &witness_output_path) .expect("generate_witness_from_files failed."); let inputs_json = std::fs::read_to_string(&*INPUTS) - .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); + .unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display())); - let input = PoqWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); - let output = generate_witness(input).expect("generate_witness failed."); + let input = PoqWitnessInput::new(inputs_json) + .expect("Failed to construct the input for the witness generator."); + let output = generate_witness(&input).expect("generate_witness failed."); - let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); + let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| { + panic!( + "Failed to read the generated witness from {}.", + witness_output_path.display() + ) + }); assert_eq!(output.as_slice(), expected.as_slice()); } } diff --git a/rust/logos-blockchain-circuits-signature-sys/Cargo.toml b/rust/logos-blockchain-circuits-signature-sys/Cargo.toml index 81f8e8a..d055ba6 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 +[lints] +workspace = true + [dependencies] lbc-types = { workspace = true } lbc-common = { workspace = true } diff --git a/rust/logos-blockchain-circuits-signature-sys/build.rs b/rust/logos-blockchain-circuits-signature-sys/build.rs index 415d0ba..0aa8795 100644 --- a/rust/logos-blockchain-circuits-signature-sys/build.rs +++ b/rust/logos-blockchain-circuits-signature-sys/build.rs @@ -5,7 +5,6 @@ use ureq::http::Response; static CIRCUIT_NAME: &str = "signature"; static LIB_VAR_NAME: &str = "LBC_SIGNATURE_LIB_DIR"; - fn get_artifact_name(version: &str, os: &str, arch: &str) -> String { format!("logos-blockchain-circuits-v{version}-{os}-{arch}") } @@ -29,17 +28,27 @@ fn fetch_library(version: &str, os: &str, arch: &str) -> Response { }) } -fn unpack_library(response: Response, version: &str, os: &str, arch: &str, output_dir: &Path) -> PathBuf { +fn unpack_library( + response: Response, + 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."); + archive + .unpack(output_dir) + .expect("Failed to unpack the downloaded archive."); let unpacked_artifact_path = output_dir.join(get_artifact_name(version, os, arch)); let unpacked_library_directory = unpacked_artifact_path.join(CIRCUIT_NAME); - if !unpacked_library_directory.exists() { - panic!("Failed to find the unpacked library at {}.", unpacked_library_directory.display()); - } + assert!( + unpacked_library_directory.is_dir(), + "Failed to find the unpacked library at {}", + unpacked_library_directory.display() + ); unpacked_library_directory } @@ -50,9 +59,14 @@ fn provision_library() -> PathBuf { 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(get_artifact_name(version, &os, &arch)).join(CIRCUIT_NAME); + let expected_library_directory = out_dir + .join(get_artifact_name(version, &os, &arch)) + .join(CIRCUIT_NAME); if expected_library_directory.exists() { - println!("Found an existing library at {}. Reusing it.", expected_library_directory.display()); + println!( + "Found an existing library at {}. Reusing it.", + expected_library_directory.display() + ); return expected_library_directory; } @@ -66,21 +80,23 @@ fn main() { println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-changed=build.rs"); - let lib_dir = std::env::var(LIB_VAR_NAME).map( + let lib_dir = std::env::var(LIB_VAR_NAME).map_or_else( + |_| provision_library(), |lib_dir| { println!("Using a library directory from {LIB_VAR_NAME}: {lib_dir}"); let lib_dir_path = PathBuf::from(lib_dir); - if !lib_dir_path.exists() { - panic!("The library directory specified in {LIB_VAR_NAME} at {} does not exist.", lib_dir_path.display()); - - } + assert!( + lib_dir_path.is_dir(), + "The library directory specified in {LIB_VAR_NAME} at {} does not exist.", + lib_dir_path.display() + ); lib_dir_path - } - ).unwrap_or_else(|_| { - provision_library() - }); + }, + ); - let lib_dir = lib_dir.to_str().expect("Failed to convert the library directory path to a string"); + let lib_dir = lib_dir + .to_str() + .expect("Failed to convert the library directory path to a string"); println!("cargo:rustc-env={LIB_VAR_NAME}={lib_dir}"); println!("cargo:rustc-link-search=native={lib_dir}"); println!("cargo:rustc-link-lib=static={CIRCUIT_NAME}"); diff --git a/rust/logos-blockchain-circuits-signature-sys/src/ffi.rs b/rust/logos-blockchain-circuits-signature-sys/src/ffi.rs index 7469dae..9c15f07 100644 --- a/rust/logos-blockchain-circuits-signature-sys/src/ffi.rs +++ b/rust/logos-blockchain-circuits-signature-sys/src/ffi.rs @@ -1,5 +1,5 @@ -use std::ffi::c_char; use lbc_types::ffi::{Bytes, Status, WitnessInput}; +use std::ffi::c_char; unsafe extern "C" { pub fn signature_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status; diff --git a/rust/logos-blockchain-circuits-signature-sys/src/lib.rs b/rust/logos-blockchain-circuits-signature-sys/src/lib.rs index 6a74894..bad4692 100644 --- a/rust/logos-blockchain-circuits-signature-sys/src/lib.rs +++ b/rust/logos-blockchain-circuits-signature-sys/src/lib.rs @@ -1,4 +1,4 @@ mod ffi; pub mod native; -pub use native::{generate_witness, generate_witness_from_files, SignatureWitnessInput}; +pub use native::{SignatureWitnessInput, generate_witness, generate_witness_from_files}; diff --git a/rust/logos-blockchain-circuits-signature-sys/src/native.rs b/rust/logos-blockchain-circuits-signature-sys/src/native.rs index 40c8c3e..3f5d958 100644 --- a/rust/logos-blockchain-circuits-signature-sys/src/native.rs +++ b/rust/logos-blockchain-circuits-signature-sys/src/native.rs @@ -1,9 +1,15 @@ -use std::path::Path; -use lbc_types::{ffi, native::{Bytes, Error}}; -use lbc_common::string::path_as_null_terminated_string; use crate::ffi::{signature_generate_witness, signature_generate_witness_from_files}; +use lbc_common::string::path_as_null_terminated_string; +use lbc_types::{ + ffi, + native::{Bytes, Error}, +}; +use std::path::Path; -static RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(env!("LBC_SIGNATURE_LIB_DIR"), "/witness_generator.dat")); +static RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!( + env!("LBC_SIGNATURE_LIB_DIR"), + "/witness_generator.dat" +)); pub struct SignatureDat; impl<'dat> lbc_types::CircuitDat<'dat> for SignatureDat { @@ -12,74 +18,70 @@ impl<'dat> lbc_types::CircuitDat<'dat> for SignatureDat { pub type SignatureWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, SignatureDat>; -pub fn generate_witness( - input: SignatureWitnessInput, -) -> Result { +pub fn generate_witness(input: &SignatureWitnessInput) -> Result { let ffi_input_guard = input.as_ffi(); let ffi_input = ffi_input_guard.as_ref(); let mut ffi_output_bytes = ffi::Bytes::null(); + // SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes. let status = unsafe { - signature_generate_witness( - ffi_input as *const ffi::WitnessInput, - &mut ffi_output_bytes as *mut ffi::Bytes - ) + signature_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes) }; - status.try_into().map(|()| { Bytes::from(ffi_output_bytes) }) + status.try_into().map(|()| Bytes::from(ffi_output_bytes)) } -pub fn generate_witness_from_files( - dat: &Path, - inputs: &Path, - output: &Path, -) -> Result<(), Error> { +pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> { let c_dat = path_as_null_terminated_string(dat)?; let c_inputs = path_as_null_terminated_string(inputs)?; let c_output = path_as_null_terminated_string(output)?; + // SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call. unsafe { - signature_generate_witness_from_files( - c_dat.as_ptr(), - c_inputs.as_ptr(), - c_output.as_ptr(), - ) - }.try_into() + signature_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr()) + } + .try_into() } #[cfg(test)] mod tests { + use super::{SignatureWitnessInput, generate_witness, generate_witness_from_files}; use std::path::PathBuf; use std::sync::LazyLock; - use super::{generate_witness, generate_witness_from_files, SignatureWitnessInput}; static LIB_DIR: LazyLock = LazyLock::new(|| { const ENV_VAR: &str = "LBC_SIGNATURE_LIB_DIR"; PathBuf::from( - std::env::var(ENV_VAR) - .expect(format!("Environment variable '{ENV_VAR}' must be available, as provided by the build script.").as_str()), + std::env::var(ENV_VAR).unwrap_or_else( + |_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."), + ) ) }); - static INPUTS: LazyLock = LazyLock::new(|| { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json") - }); + static INPUTS: LazyLock = + LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json")); #[test] fn test_generate_witness() { let dat = LIB_DIR.join("witness_generator"); let witness_output_path = std::env::temp_dir().join("signature_test_witness.wtns"); - generate_witness_from_files(&dat, &*INPUTS, &witness_output_path) + generate_witness_from_files(&dat, &INPUTS, &witness_output_path) .expect("generate_witness_from_files failed."); let inputs_json = std::fs::read_to_string(&*INPUTS) - .expect(format!("Failed to read {}.", INPUTS.display()).as_str()); + .unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display())); - let input = SignatureWitnessInput::new(inputs_json).expect("Failed to construct the input for the witness generator."); - let output = generate_witness(input).expect("generate_witness failed."); + let input = SignatureWitnessInput::new(inputs_json) + .expect("Failed to construct the input for the witness generator."); + let output = generate_witness(&input).expect("generate_witness failed."); - let expected = std::fs::read(&witness_output_path).expect(format!("Failed to read the generated witness from {}.", witness_output_path.display()).as_str()); + let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| { + panic!( + "Failed to read the generated witness from {}.", + witness_output_path.display() + ) + }); assert_eq!(output.as_slice(), expected.as_slice()); } } diff --git a/rust/logos-blockchain-circuits-types/Cargo.toml b/rust/logos-blockchain-circuits-types/Cargo.toml index 898cb4f..73285b6 100644 --- a/rust/logos-blockchain-circuits-types/Cargo.toml +++ b/rust/logos-blockchain-circuits-types/Cargo.toml @@ -9,5 +9,8 @@ readme.workspace = true repository.workspace = true version.workspace = true +[lints] +workspace = true + [dependencies] libc = { workspace = true } diff --git a/rust/logos-blockchain-circuits-types/src/ffi/bytes.rs b/rust/logos-blockchain-circuits-types/src/ffi/bytes.rs index d0e37d0..6d96d3e 100644 --- a/rust/logos-blockchain-circuits-types/src/ffi/bytes.rs +++ b/rust/logos-blockchain-circuits-types/src/ffi/bytes.rs @@ -9,7 +9,7 @@ mod inner { } impl inner::Buffer<*const T> { - pub fn null() -> Self { + pub const fn null() -> Self { Self { data: std::ptr::null(), size: 0, @@ -18,7 +18,7 @@ impl inner::Buffer<*const T> { } impl inner::Buffer<*mut T> { - pub fn null() -> Self { + pub const fn null() -> Self { Self { data: std::ptr::null_mut(), size: 0, @@ -53,9 +53,11 @@ pub unsafe fn free_bytes(bytes: *mut Bytes) { return; } + // SAFETY: `bytes` is non-null (checked above). let bytes = unsafe { &mut *bytes }; if !bytes.data.is_null() { - unsafe { free(bytes.data as *mut libc::c_void) }; + // SAFETY: `bytes.data` is non-null (checked above). + unsafe { free(bytes.data.cast::()) }; } bytes.data = std::ptr::null_mut(); bytes.size = 0; diff --git a/rust/logos-blockchain-circuits-types/src/ffi/mod.rs b/rust/logos-blockchain-circuits-types/src/ffi/mod.rs index 9176c20..0261398 100644 --- a/rust/logos-blockchain-circuits-types/src/ffi/mod.rs +++ b/rust/logos-blockchain-circuits-types/src/ffi/mod.rs @@ -3,10 +3,10 @@ //! These types map directly to the C header structs and are used at the FFI boundary. Prefer the //! wrappers in [`crate::native`] for ordinary Rust code. -pub mod status; pub mod bytes; +pub mod status; pub mod witness_input; -pub use status::Status; pub use bytes::{Bytes, ConstBytes, free_bytes}; +pub use status::Status; pub use witness_input::WitnessInput; diff --git a/rust/logos-blockchain-circuits-types/src/ffi/status.rs b/rust/logos-blockchain-circuits-types/src/ffi/status.rs index 44ad026..bd94618 100644 --- a/rust/logos-blockchain-circuits-types/src/ffi/status.rs +++ b/rust/logos-blockchain-circuits-types/src/ffi/status.rs @@ -10,13 +10,13 @@ impl Code { pub const DYN_ERROR: Self = Self(1); pub const INVALID_INPUT: Self = Self(2); pub const OUT_OF_MEMORY: Self = Self(3); -} -impl Code { + #[must_use] pub fn is_ok(&self) -> bool { self == &Self::OK } + #[must_use] pub fn is_error(&self) -> bool { !self.is_ok() } @@ -30,22 +30,26 @@ pub struct Status { } impl Status { - pub fn ok() -> Self { - Status { + #[must_use] + pub const fn ok() -> Self { + Self { code: Code::OK, message: [0; 256], } } + #[must_use] pub fn is_ok(&self) -> bool { self.code.is_ok() } + #[must_use] pub fn is_error(&self) -> bool { self.code.is_error() } - pub fn has_message(&self) -> bool { + #[must_use] + pub const fn has_message(&self) -> bool { self.message[0] != 0 } } diff --git a/rust/logos-blockchain-circuits-types/src/ffi/witness_input.rs b/rust/logos-blockchain-circuits-types/src/ffi/witness_input.rs index 12741bd..5726243 100644 --- a/rust/logos-blockchain-circuits-types/src/ffi/witness_input.rs +++ b/rust/logos-blockchain-circuits-types/src/ffi/witness_input.rs @@ -1,5 +1,5 @@ -use std::ffi::c_char; use crate::ffi::ConstBytes; +use std::ffi::c_char; /// Input to a witness generator function. /// diff --git a/rust/logos-blockchain-circuits-types/src/lib.rs b/rust/logos-blockchain-circuits-types/src/lib.rs index f7a9b19..58235c8 100644 --- a/rust/logos-blockchain-circuits-types/src/lib.rs +++ b/rust/logos-blockchain-circuits-types/src/lib.rs @@ -4,7 +4,7 @@ //! [`native`] module re-exposes those through idiomatic Rust types that own their memory and //! convert FFI return values into [`Result`]s. -pub mod native; pub mod ffi; +pub mod native; pub use native::{CircuitDat, CircuitWitnessInput}; diff --git a/rust/logos-blockchain-circuits-types/src/native/bytes.rs b/rust/logos-blockchain-circuits-types/src/native/bytes.rs index 653e2b7..4375e47 100644 --- a/rust/logos-blockchain-circuits-types/src/native/bytes.rs +++ b/rust/logos-blockchain-circuits-types/src/native/bytes.rs @@ -41,11 +41,12 @@ impl From for Bytes { let vec = if ffi_value.size == 0 || ffi_value.data.is_null() { Vec::new() } else { - unsafe { - std::slice::from_raw_parts(ffi_value.data, ffi_value.size).to_vec() - } + // SAFETY: `ffi_value.data` is non-null and `ffi_value.size > 0` (checked above), + // pointing to a valid C-allocated buffer of at least `size` bytes. + unsafe { std::slice::from_raw_parts(ffi_value.data, ffi_value.size).to_vec() } }; - unsafe { ffi::free_bytes(&mut ffi_value) }; + // SAFETY: `ffi_value` is a local variable, so the raw pointer is valid for this call. + unsafe { ffi::free_bytes(&raw mut ffi_value) }; Self(vec) } } diff --git a/rust/logos-blockchain-circuits-types/src/native/circuit_witness_input.rs b/rust/logos-blockchain-circuits-types/src/native/circuit_witness_input.rs index 05c89c2..880562a 100644 --- a/rust/logos-blockchain-circuits-types/src/native/circuit_witness_input.rs +++ b/rust/logos-blockchain-circuits-types/src/native/circuit_witness_input.rs @@ -1,5 +1,6 @@ +use crate::native::{Error, WitnessInput}; +use std::marker::PhantomData; use std::ops::Deref; -use crate::native::{WitnessInput, Error}; pub trait CircuitDat<'dat> { const DAT: &'dat [u8]; @@ -7,13 +8,16 @@ pub trait CircuitDat<'dat> { pub struct CircuitWitnessInput<'dat, Dat> { inner: WitnessInput<'dat>, - _phantom: std::marker::PhantomData + _phantom: PhantomData, } impl<'dat, Dat: CircuitDat<'dat>> CircuitWitnessInput<'dat, Dat> { pub fn new(inputs_json: String) -> Result { let inner = WitnessInput::new(Dat::DAT, inputs_json)?; - Ok(Self { inner, _phantom: Default::default() }) + Ok(Self { + inner, + _phantom: PhantomData, + }) } } diff --git a/rust/logos-blockchain-circuits-types/src/native/mod.rs b/rust/logos-blockchain-circuits-types/src/native/mod.rs index 667ff1c..f5c4550 100644 --- a/rust/logos-blockchain-circuits-types/src/native/mod.rs +++ b/rust/logos-blockchain-circuits-types/src/native/mod.rs @@ -3,11 +3,11 @@ //! Use them in preference to the types in [`crate::ffi`]. pub mod bytes; +pub mod circuit_witness_input; pub mod status; pub mod witness_input; -pub mod circuit_witness_input; pub use bytes::Bytes; -pub use status::{Result, Error}; -pub use witness_input::WitnessInput; pub use circuit_witness_input::{CircuitDat, CircuitWitnessInput}; +pub use status::{Error, Result}; +pub use witness_input::WitnessInput; diff --git a/rust/logos-blockchain-circuits-types/src/native/status.rs b/rust/logos-blockchain-circuits-types/src/native/status.rs index 75c3c74..066b2e9 100644 --- a/rust/logos-blockchain-circuits-types/src/native/status.rs +++ b/rust/logos-blockchain-circuits-types/src/native/status.rs @@ -1,6 +1,6 @@ +use crate::ffi::status::Code as FfiStatusCode; use std::ffi::CStr; use std::fmt::Display; -use crate::ffi::status::Code as FfiStatusCode; pub type Result = std::result::Result; @@ -17,9 +17,9 @@ impl std::error::Error for Error {} impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let (kind, message) = match self { - Error::InvalidInput(msg) => ("Invalid input", msg), - Error::OutOfMemory(msg) => ("Out of memory", msg), - Error::Other(msg) => ("Other error", msg), + Self::InvalidInput(msg) => ("Invalid input", msg), + Self::OutOfMemory(msg) => ("Out of memory", msg), + Self::Other(msg) => ("Other error", msg), }; match message { Some(message) => write!(f, "{kind}: {message}"), @@ -32,21 +32,21 @@ impl TryFrom for () { type Error = Error; fn try_from(status: crate::ffi::Status) -> Result<()> { - let message: Option = if status.has_message() { - let status_message = unsafe { - CStr::from_ptr(status.message.as_ptr()) - }; - Some(status_message.to_string_lossy().into_owned()) - } else { - None - }; + let message: Option = status.has_message().then(|| { + // SAFETY: `status.message` is non-empty (checked by `has_message()`) and null-terminated as guaranteed by the C API. + let status_message = unsafe { CStr::from_ptr(status.message.as_ptr()) }; + status_message.to_string_lossy().into_owned() + }); match status.code { FfiStatusCode::OK => Ok(()), FfiStatusCode::DYN_ERROR => Err(Error::Other(message)), FfiStatusCode::INVALID_INPUT => Err(Error::InvalidInput(message)), FfiStatusCode::OUT_OF_MEMORY => Err(Error::OutOfMemory(message)), - other => Err(Error::Other(Some(format!("Unknown status code: {}", other.0)))) + other => Err(Error::Other(Some(format!( + "Unknown status code: {}", + other.0 + )))), } } } diff --git a/rust/logos-blockchain-circuits-types/src/native/witness_input.rs b/rust/logos-blockchain-circuits-types/src/native/witness_input.rs index 397ebfd..3fe2acb 100644 --- a/rust/logos-blockchain-circuits-types/src/native/witness_input.rs +++ b/rust/logos-blockchain-circuits-types/src/native/witness_input.rs @@ -1,6 +1,6 @@ -use std::ffi::CString; use crate::ffi; use crate::native::Error; +use std::ffi::CString; /// Input for witness generators pub struct WitnessInput<'dat> { @@ -12,15 +12,16 @@ pub struct WitnessInput<'dat> { impl<'dat> WitnessInput<'dat> { pub fn new(dat: &'dat [u8], inputs_json: String) -> Result { - let inputs_json = CString::new(inputs_json).map_err( - |error| Error::InvalidInput(Some( - format!("The parameter inputs_json could not be converted to CString: {}", error) - )) - )?; + let inputs_json = CString::new(inputs_json).map_err(|error| { + Error::InvalidInput(Some(format!( + "The parameter inputs_json could not be converted to C string: {error}" + ))) + })?; Ok(Self { dat, inputs_json }) } /// Borrows this value as a temporary FFI-compatible view. + #[must_use] pub fn as_ffi(&'_ self) -> WitnessInputFfiGuard<'_> { WitnessInputFfiGuard::new(self) } @@ -34,7 +35,10 @@ pub struct WitnessInputFfiGuard<'dat> { impl<'dat> WitnessInputFfiGuard<'dat> { fn new(inner: &'dat WitnessInput) -> Self { - let dat = ffi::ConstBytes { data: inner.dat.as_ptr(), size: inner.dat.len() }; + let dat = ffi::ConstBytes { + data: inner.dat.as_ptr(), + size: inner.dat.len(), + }; let inputs_json = inner.inputs_json.as_ptr(); let ffi = ffi::WitnessInput { dat, inputs_json }; Self { @@ -44,7 +48,7 @@ impl<'dat> WitnessInputFfiGuard<'dat> { } } -impl<'dat> AsRef for WitnessInputFfiGuard<'dat> { +impl AsRef for WitnessInputFfiGuard<'_> { fn as_ref(&self) -> &ffi::WitnessInput { &self.ffi }