diff --git a/.github/workflows/rust-tests.yml b/.github/workflows/rust-tests.yml index 4034c9e..5dec120 100644 --- a/.github/workflows/rust-tests.yml +++ b/.github/workflows/rust-tests.yml @@ -9,7 +9,13 @@ on: jobs: tests: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - windows-latest + - macos-latest steps: - uses: actions/checkout@v3 with: diff --git a/bindings/rust/Cargo.lock b/bindings/rust/Cargo.lock index 11c88eb..2f521bf 100644 --- a/bindings/rust/Cargo.lock +++ b/bindings/rust/Cargo.lock @@ -42,7 +42,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.8", + "syn 2.0.13", "which", ] @@ -74,6 +74,7 @@ version = "0.1.0" dependencies = [ "bindgen", "blst", + "cc", "criterion", "glob", "hex", @@ -139,9 +140,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -309,9 +310,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -355,9 +356,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libloading" @@ -488,9 +489,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -558,9 +559,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "regex-syntax", ] @@ -600,29 +601,29 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa", "ryu", @@ -661,9 +662,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", @@ -846,5 +847,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index ed070eb..b9a53bd 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -2,6 +2,7 @@ name = "c-kzg" version = "0.1.0" edition = "2021" +links = "ckzg" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -24,6 +25,7 @@ serde_yaml = "0.9.17" [build-dependencies] bindgen = { git = "https://github.com/rust-lang/rust-bindgen" , rev = "0de11f0a521611ac8738b7b01d19dddaf3899e66" } +cc = "1.0" [[bench]] name = "kzg_benches" diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index bb5544b..9ec049c 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -1,18 +1,9 @@ use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; +use std::path::PathBuf; const MAINNET_FIELD_ELEMENTS_PER_BLOB: usize = 4096; const MINIMAL_FIELD_ELEMENTS_PER_BLOB: usize = 4; -fn move_file(src: &Path, dst: &Path) -> Result<(), String> { - std::fs::copy(src, dst) - .map_err(|_| format!("Failed to copy {} to {}", src.display(), dst.display()))?; - std::fs::remove_file(src) - .map_err(|_| format!("Failed to remove file {} from source", src.display()))?; - Ok(()) -} - fn main() { let cargo_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let root_dir = cargo_dir @@ -20,7 +11,6 @@ fn main() { .expect("rust dir is nested") .parent() .expect("bindings dir is nested"); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let field_elements_per_blob = if cfg!(feature = "minimal-spec") { MINIMAL_FIELD_ELEMENTS_PER_BLOB @@ -30,38 +20,24 @@ fn main() { eprintln!("Using FIELD_ELEMENTS_PER_BLOB={}", field_elements_per_blob); - // Deleting any existing assembly and object files to ensure that compiling with a different - // feature flag changes the final linked library file. - let obj_file = root_dir.join("src").join("c_kzg_4844.o"); - if obj_file.exists() { - std::fs::remove_file(obj_file).unwrap(); - } + // Obtain the header files exposed by blst-bindings' crate. + let blst_headers_dir = + std::env::var_os("DEP_BLST_BINDINGS").expect("BLST exposes header files for bindings"); - // Ensure libckzg exists in `OUT_DIR` - Command::new("make") - .current_dir(root_dir.join("src")) - .arg("c_kzg_4844.o") - .arg(format!( - "FIELD_ELEMENTS_PER_BLOB={}", - field_elements_per_blob - )) - .status() - .unwrap(); + let c_src_dir = root_dir.join("src"); - Command::new("ar") - .current_dir(&root_dir.join("src")) - .args(["crus", "libckzg.a", "c_kzg_4844.o"]) - .status() - .unwrap(); - move_file( - root_dir.join("src").join("libckzg.a").as_path(), - out_dir.join("libckzg.a").as_path(), - ) - .unwrap(); + let mut cc = cc::Build::new(); + + #[cfg(windows)] + cc.compiler("clang").flag("-D_CRT_SECURE_NO_WARNINGS"); + + cc.include(blst_headers_dir.clone()); + cc.warnings(false); + cc.flag(format!("-DFIELD_ELEMENTS_PER_BLOB={}", field_elements_per_blob).as_str()); + cc.file(c_src_dir.join("c_kzg_4844.c")); + + cc.try_compile("ckzg").expect("Failed to compile ckzg"); - println!("cargo:rustc-link-search={}", out_dir.display()); - println!("cargo:rustc-link-search={}", out_dir.display()); - println!("cargo:rustc-link-lib=static=ckzg"); // Tell cargo to search for the static blst exposed by the blst-bindings' crate. println!("cargo:rustc-link-lib=static=blst"); @@ -71,12 +47,9 @@ fn main() { "bindings_{build_target}_{field_elements_per_blob}.rs" )); - let header_file_path = root_dir.join("src").join("c_kzg_4844.h"); + let header_file_path = c_src_dir.join("c_kzg_4844.h"); let header_file = header_file_path.to_str().expect("valid header file"); - // Obtain the header files exposed by blst-bindings' crate. - let blst_headers_dir = - std::env::var_os("DEP_BLST_BINDINGS").expect("BLST exposes header files for bindings"); make_bindings( field_elements_per_blob, header_file, @@ -85,11 +58,8 @@ fn main() { snapshot_path, ); - // Cleanup - let obj_file = root_dir.join("src").join("c_kzg_4844.o"); - if obj_file.exists() { - std::fs::remove_file(obj_file).unwrap(); - } + // Finally, tell cargo this provides ckzg + println!("cargo:rustc-link-lib=ckzg"); } fn make_bindings

( diff --git a/bindings/rust/src/bindings/mod.rs b/bindings/rust/src/bindings/mod.rs index f385ca7..fbcce35 100644 --- a/bindings/rust/src/bindings/mod.rs +++ b/bindings/rust/src/bindings/mod.rs @@ -6,10 +6,8 @@ mod test_formats; include!("generated.rs"); -use libc::fopen; use std::ffi::CString; use std::mem::MaybeUninit; -use std::os::unix::prelude::OsStrExt; use std::path::PathBuf; pub const BYTES_PER_G1_POINT: usize = 48; @@ -102,12 +100,41 @@ impl KZGSettings { /// FIELD_ELEMENT_PER_BLOB g1 byte values /// 65 g2 byte values pub fn load_trusted_setup_file(file_path: PathBuf) -> Result { - let file_path = CString::new(file_path.as_os_str().as_bytes()).map_err(|e| { - Error::InvalidTrustedSetup(format!("Invalid trusted setup file: {:?}", e)) - })?; + // SAFETY: vec![b'r'] has no 0 bytes. + let mode = unsafe { CString::from_vec_unchecked(vec![b'r']) }; + + #[cfg(unix)] + let file_path_bytes = { + use std::os::unix::prelude::OsStrExt; + file_path.as_os_str().as_bytes() + }; + + #[cfg(windows)] + let file_path_bytes = { + file_path + .as_os_str() + .to_str() + .ok_or(Error::InvalidTrustedSetup(format!( + "Unsuported non unicode file path" + )))? + .as_bytes() + }; + + let file_path = CString::new(file_path_bytes) + .map_err(|e| Error::InvalidTrustedSetup(format!("Invalid trusted setup file: {e}")))?; + + // SAFETY: + // - .as_ptr(): pointer is not dangling because file_path has not been dropped. + // Usage or ptr: File will not be written to it by the c code. + let file_ptr = unsafe { libc::fopen(file_path.as_ptr(), mode.as_ptr()) }; + if file_ptr.is_null() { + let e = std::io::Error::last_os_error(); + return Err(Error::InvalidTrustedSetup(format!( + "Failed to open trusted setup file {e}" + ))); + } let mut kzg_settings = MaybeUninit::::uninit(); - unsafe { - let file_ptr = fopen(file_path.as_ptr(), &('r' as libc::c_char)); + let result = unsafe { let res = load_trusted_setup_file(kzg_settings.as_mut_ptr(), file_ptr); if let C_KZG_RET::C_KZG_OK = res { Ok(kzg_settings.assume_init()) @@ -117,7 +144,13 @@ impl KZGSettings { res ))) } - } + }; + + // We don't really care if this succeeds. + let _uncheched_close_result = unsafe { libc::fclose(file_ptr) }; + drop(file_path); + + result } } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 05bd6e9..57e5551 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -8,7 +8,7 @@ pub use bindings::{ // Expose the constants. pub use bindings::{ BYTES_PER_BLOB, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, BYTES_PER_PROOF, - FIELD_ELEMENTS_PER_BLOB, + FIELD_ELEMENTS_PER_BLOB, BYTES_PER_G1_POINT, BYTES_PER_G2_POINT }; // Expose the remaining relevant types. pub use bindings::{Blob, Bytes32, Bytes48, Error, FFTSettings};