From fd669bfa364e7f0194929f65a66b49923bf1ad26 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 6 Feb 2024 19:39:07 +0200 Subject: [PATCH] Make Rust `bindgen` build dependency optional (#382) --- .gitattributes | 1 + .github/workflows/rust-tests.yml | 6 +- bindings/rust/Cargo.lock | 12 +-- bindings/rust/Cargo.toml | 5 +- bindings/rust/build.rs | 66 +++++++++--- bindings/rust/src/bindings/generated.rs | 138 ++++++++++++++++++++++++ bindings/rust/src/bindings/mod.rs | 6 +- 7 files changed, 201 insertions(+), 33 deletions(-) create mode 100644 .gitattributes create mode 100644 bindings/rust/src/bindings/generated.rs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..52f429c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +bindings/rust/src/bindings/generated.rs linguist-generated diff --git a/.github/workflows/rust-tests.yml b/.github/workflows/rust-tests.yml index 2028edf..64f2885 100644 --- a/.github/workflows/rust-tests.yml +++ b/.github/workflows/rust-tests.yml @@ -23,6 +23,8 @@ jobs: - name: cargo hack working-directory: bindings/rust run: cargo hack check --feature-powerset --depth 2 + - name: Check that bindings are up to date + run: git diff --exit-code bindings/rust/src/bindings/generated.rs tests: runs-on: ${{ matrix.host }} @@ -58,7 +60,9 @@ jobs: - name: Build and Test working-directory: bindings/rust - run: cargo test --target ${{ matrix.target }} + run: cargo test --target ${{ matrix.target }} --features generate-bindings + - name: Check that bindings are up to date + run: git diff --exit-code bindings/rust/src/bindings/generated.rs - name: Benchmark working-directory: bindings/rust run: cargo bench --target ${{ matrix.target }} diff --git a/bindings/rust/Cargo.lock b/bindings/rust/Cargo.lock index 693f90d..9a8c4a9 100644 --- a/bindings/rust/Cargo.lock +++ b/bindings/rust/Cargo.lock @@ -31,17 +31,17 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bindgen" -version = "0.69.2" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c69fae65a523209d34240b60abe0c42d33d1045d445c0839d8a4894a736e2d" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ "bitflags", "cexpr", "clang-sys", + "itertools", "lazy_static", "lazycell", "log", - "peeking_take_while", "prettyplease", "proc-macro2", "quote", @@ -486,12 +486,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "plotters" version = "0.3.5" diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index a6f4a4a..012b223 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -11,6 +11,7 @@ links = "ckzg" default = ["std", "blst/portable"] std = ["hex/std", "libc/std", "serde?/std"] serde = ["dep:serde"] +generate-bindings = ["dep:bindgen"] # BLST Compilation: # Suppress multi-threading. @@ -18,13 +19,13 @@ serde = ["dep:serde"] no-threads = ["blst/no-threads"] [dependencies] +blst = { version = "0.3.11", default-features = false } hex = { version = "0.4.2", default-features = false, features = ["alloc"] } libc = { version = "0.2", default-features = false } serde = { version = "1.0", optional = true, default-features = false, features = [ "alloc", "derive", ] } -blst = { version = "0.3.11", default-features = false } [dev-dependencies] criterion = "0.5.1" @@ -35,7 +36,7 @@ serde_yaml = "0.9.17" serde_json = "1.0.105" [build-dependencies] -bindgen = "0.69" +bindgen = { version = "0.69", optional = true } cc = "1.0" [target.'cfg(target_env = "msvc")'.build-dependencies] diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 5148333..f121dd8 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -34,25 +34,23 @@ fn main() { cc.try_compile("ckzg").expect("Failed to compile ckzg"); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let bindings_out_path = out_dir.join("generated.rs"); - let header_file_path = c_src_dir.join("c_kzg_4844.h"); - let header_file = header_file_path.to_str().expect("valid header file"); - - make_bindings( - header_file, - &blst_headers_dir.to_string_lossy(), - bindings_out_path, - ); + #[cfg(feature = "generate-bindings")] + { + let header_path = c_src_dir.join("c_kzg_4844.h"); + let bindings_out_path = concat!(env!("CARGO_MANIFEST_DIR"), "/src/bindings/generated.rs"); + make_bindings( + header_path.to_str().expect("valid header path"), + blst_headers_dir.to_str().expect("valid blst header path"), + bindings_out_path.as_ref(), + ); + } // Finally, tell cargo this provides ckzg/ckzg_min println!("cargo:rustc-link-lib=ckzg"); } -fn make_bindings

(header_path: &str, blst_headers_dir: &str, bindings_out_path: P) -where - P: AsRef, -{ +#[cfg(feature = "generate-bindings")] +fn make_bindings(header_path: &str, blst_headers_dir: &str, bindings_out_path: &std::path::Path) { use bindgen::Builder; #[derive(Debug)] @@ -123,7 +121,41 @@ where .generate() .unwrap(); - bindings - .write_to_file(bindings_out_path) - .expect("Failed to write bindings"); + let mut bindings = bindings.to_string(); + bindings = replace_ckzg_ret_repr(bindings); + std::fs::write(bindings_out_path, bindings).expect("Failed to write bindings"); +} + +// Here we hardcode the C_KZG_RET enum to use C representation. Bindgen +// will use repr(u32) on Unix and repr(i32) on Windows. We would like to +// use the same generated bindings for all platforms. This can be removed +// if/when bindgen fixes this or allows us to choose our own representation +// for types. Using repr(C) is equivalent to repr(u*) for fieldless enums, +// so this should be safe to do. The alternative was to modify C_KZG_RET in +// C and we decided this was the lesser of two evils. There should be only +// one instance where repr(C) isn't used: C_KZG_RET. +// See: https://github.com/rust-lang/rust-bindgen/issues/1907 +#[cfg(feature = "generate-bindings")] +fn replace_ckzg_ret_repr(mut bindings: String) -> String { + let target = env::var("TARGET").unwrap_or_default(); + let repr_to_replace = if target.contains("windows") { + "#[repr(i32)]" + } else { + "#[repr(u32)]" + }; + + // Find `repr_to_replace` as an attribute of `enum C_KZG_RET`. + let ckzg_ret = bindings + .find("enum C_KZG_RET") + .expect("Could not find C_KZG_RET in bindings"); + let repr_start = bindings[..ckzg_ret] + .rfind(repr_to_replace) + .expect("Could not find repr to replace in bindings"); + + // Sanity check that it's an attribute of `C_KZG_RET` and not another type. + assert!(repr_start > bindings[..ckzg_ret].rfind('}').unwrap()); + + bindings.replace_range(repr_start..repr_start + repr_to_replace.len(), "#[repr(C)]"); + + bindings } diff --git a/bindings/rust/src/bindings/generated.rs b/bindings/rust/src/bindings/generated.rs new file mode 100644 index 0000000..2be3f7f --- /dev/null +++ b/bindings/rust/src/bindings/generated.rs @@ -0,0 +1,138 @@ +/* automatically generated by rust-bindgen 0.69.4 */ + +use libc::FILE; + +pub const BYTES_PER_COMMITMENT: usize = 48; +pub const BYTES_PER_PROOF: usize = 48; +pub const BYTES_PER_FIELD_ELEMENT: usize = 32; +pub const FIELD_ELEMENTS_PER_BLOB: usize = 4096; +pub const BYTES_PER_BLOB: usize = 131072; +pub type limb_t = u64; +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct blst_fr { + l: [limb_t; 4usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct blst_fp { + l: [limb_t; 6usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct blst_fp2 { + fp: [blst_fp; 2usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct blst_p1 { + x: blst_fp, + y: blst_fp, + z: blst_fp, +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct blst_p2 { + x: blst_fp2, + y: blst_fp2, + z: blst_fp2, +} +pub type g1_t = blst_p1; +pub type g2_t = blst_p2; +pub type fr_t = blst_fr; +#[doc = " An array of 32 bytes. Represents an untrusted\n (potentially invalid) field element."] +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Bytes32 { + bytes: [u8; 32usize], +} +#[doc = " An array of 48 bytes. Represents an untrusted\n (potentially invalid) commitment/proof."] +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Bytes48 { + bytes: [u8; 48usize], +} +#[doc = " A basic blob data."] +#[repr(C)] +#[derive(Debug, PartialEq, Eq)] +pub struct Blob { + bytes: [u8; 131072usize], +} +#[repr(C)] +#[doc = " The common return type for all routines in which something can go wrong."] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum C_KZG_RET { + #[doc = "< Success!"] + C_KZG_OK = 0, + #[doc = "< The supplied data is invalid in some way."] + C_KZG_BADARGS = 1, + #[doc = "< Internal error - this should never occur."] + C_KZG_ERROR = 2, + #[doc = "< Could not allocate memory."] + C_KZG_MALLOC = 3, +} +#[doc = " Stores the setup and parameters needed for computing KZG proofs."] +#[repr(C)] +#[derive(Debug, PartialEq, Eq)] +pub struct KZGSettings { + #[doc = " The length of `roots_of_unity`, a power of 2."] + max_width: u64, + #[doc = " Powers of the primitive root of unity determined by\n `SCALE2_ROOT_OF_UNITY` in bit-reversal permutation order,\n length `max_width`."] + roots_of_unity: *mut fr_t, + #[doc = " G1 group elements from the trusted setup,\n in Lagrange form bit-reversal permutation."] + g1_values: *mut g1_t, + #[doc = " G2 group elements from the trusted setup."] + g2_values: *mut g2_t, +} +extern "C" { + pub fn load_trusted_setup( + out: *mut KZGSettings, + g1_bytes: *const u8, + n1: usize, + g2_bytes: *const u8, + n2: usize, + ) -> C_KZG_RET; + pub fn load_trusted_setup_file(out: *mut KZGSettings, in_: *mut FILE) -> C_KZG_RET; + pub fn free_trusted_setup(s: *mut KZGSettings); + pub fn blob_to_kzg_commitment( + out: *mut KZGCommitment, + blob: *const Blob, + s: *const KZGSettings, + ) -> C_KZG_RET; + pub fn compute_kzg_proof( + proof_out: *mut KZGProof, + y_out: *mut Bytes32, + blob: *const Blob, + z_bytes: *const Bytes32, + s: *const KZGSettings, + ) -> C_KZG_RET; + pub fn compute_blob_kzg_proof( + out: *mut KZGProof, + blob: *const Blob, + commitment_bytes: *const Bytes48, + s: *const KZGSettings, + ) -> C_KZG_RET; + pub fn verify_kzg_proof( + ok: *mut bool, + commitment_bytes: *const Bytes48, + z_bytes: *const Bytes32, + y_bytes: *const Bytes32, + proof_bytes: *const Bytes48, + s: *const KZGSettings, + ) -> C_KZG_RET; + pub fn verify_blob_kzg_proof( + ok: *mut bool, + blob: *const Blob, + commitment_bytes: *const Bytes48, + proof_bytes: *const Bytes48, + s: *const KZGSettings, + ) -> C_KZG_RET; + pub fn verify_blob_kzg_proof_batch( + ok: *mut bool, + blobs: *const Blob, + commitments_bytes: *const Bytes48, + proofs_bytes: *const Bytes48, + n: usize, + s: *const KZGSettings, + ) -> C_KZG_RET; +} diff --git a/bindings/rust/src/bindings/mod.rs b/bindings/rust/src/bindings/mod.rs index ebeee8a..2fceebf 100644 --- a/bindings/rust/src/bindings/mod.rs +++ b/bindings/rust/src/bindings/mod.rs @@ -1,13 +1,11 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] +#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] #[cfg(feature = "serde")] mod serde; #[cfg(test)] mod test_formats; -include!(concat!(env!("OUT_DIR"), "/generated.rs")); +include!("./generated.rs"); use alloc::string::String; use alloc::vec::Vec;