diff --git a/bindings/rust/.gitignore b/bindings/rust/.gitignore index 685e955..af69316 100644 --- a/bindings/rust/.gitignore +++ b/bindings/rust/.gitignore @@ -1,2 +1,2 @@ -src/consts.rs +src/bindings/generated.rs target/ diff --git a/bindings/rust/Cargo.lock b/bindings/rust/Cargo.lock index 05bc121..b100348 100644 --- a/bindings/rust/Cargo.lock +++ b/bindings/rust/Cargo.lock @@ -25,6 +25,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bindgen" +version = "0.64.0" +source = "git+https://github.com/rust-lang/rust-bindgen?rev=0de11f0a521611ac8738b7b01d19dddaf3899e66#0de11f0a521611ac8738b7b01d19dddaf3899e66" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.8", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -33,14 +54,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "c-kzg" version = "0.1.0" dependencies = [ + "bindgen", "criterion", "glob", "hex", @@ -56,6 +78,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -89,6 +120,17 @@ dependencies = [ "half", ] +[[package]] +name = "clang-sys" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "3.2.23" @@ -148,9 +190,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -158,9 +200,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -169,9 +211,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if", @@ -182,18 +224,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "getrandom" @@ -269,15 +311,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -289,10 +331,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "libc" -version = "0.2.137" +name = "lazycell" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] [[package]] name = "log" @@ -304,14 +362,36 @@ dependencies = [ ] [[package]] -name = "memoffset" -version = "0.7.1" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -333,9 +413,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -345,9 +425,15 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "plotters" @@ -385,18 +471,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -433,9 +519,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -443,9 +529,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -455,24 +541,30 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" @@ -491,29 +583,29 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.150" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.150" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -522,9 +614,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.17" +version = "0.9.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" +checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" dependencies = [ "indexmap", "itoa", @@ -534,10 +626,27 @@ dependencies = [ ] [[package]] -name = "syn" -version = "1.0.107" +name = "shlex" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" dependencies = [ "proc-macro2", "quote", @@ -562,24 +671,23 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unsafe-libyaml" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" +checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -591,9 +699,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -601,24 +709,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -626,33 +734,44 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index fc48b51..11accb6 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -21,6 +21,9 @@ glob = "0.3.1" rand = "0.8.5" serde_yaml = "0.9.17" +[build-dependencies] +bindgen = { git = "https://github.com/rust-lang/rust-bindgen" , rev = "0de11f0a521611ac8738b7b01d19dddaf3899e66" } + [[bench]] name = "kzg_benches" -harness = false \ No newline at end of file +harness = false diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 06f341e..a39e0b7 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -14,7 +14,8 @@ fn move_file(src: &Path, dst: &Path) -> Result<(), String> { } fn main() { - let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../../"); + let cargo_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let root_dir = cargo_dir.join("../../"); let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); // Ensure libblst exists in `OUT_DIR` @@ -72,16 +73,17 @@ fn main() { println!("cargo:rustc-link-lib=static=ckzg"); println!("cargo:rustc-link-lib=static=blst"); - // Write the compile time variable to a consts.rs file to be imported to the bindings module. - let const_file = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("src/consts.rs"); - std::fs::write( - const_file, - format!( - "pub const FIELD_ELEMENTS_PER_BLOB: usize = {};", - field_elements_per_blob - ), - ) - .unwrap(); + let bindings_out_path = cargo_dir.join("src/bindings/generated.rs"); + let build_target = env::var("TARGET").unwrap(); + let snapshot_path = cargo_dir.join("snapshots").join(format!( + "bindings_{build_target}_{field_elements_per_blob}.rs" + )); + make_bindings( + field_elements_per_blob, + &root_dir, + bindings_out_path, + snapshot_path, + ); // Cleanup let obj_file = root_dir.join("src/c_kzg_4844.o"); @@ -89,3 +91,103 @@ fn main() { std::fs::remove_file(obj_file).unwrap(); } } + +fn make_bindings

( + field_elements_per_blob: usize, + root_dir: &Path, + bindings_out_path: P, + snapshot_path: P, +) where + P: AsRef, +{ + use bindgen::Builder; + + #[derive(Debug)] + struct Callbacks; + impl bindgen::callbacks::ParseCallbacks for Callbacks { + fn int_macro(&self, name: &str, _value: i64) -> Option { + match name { + "FIELD_ELEMENTS_PER_BLOB" + | "BYTES_PER_COMMITMENT" + | "BYTES_PER_PROOF" + | "BYTES_PER_FIELD_ELEMENT" + | "BYTES_PER_BLOB" => Some(bindgen::callbacks::IntKind::Custom { + name: "usize", + is_signed: false, + }), + _ => None, + } + } + } + + let header_file_path = root_dir.join("src/c_kzg_4844.h"); + let header_file = header_file_path.to_str().expect("valid header file"); + + let inc_dir_path = root_dir.join("inc"); + let inc_dir = inc_dir_path.to_str().expect("valid inc dir"); + + let bindings = Builder::default() + /* + * Header definitions. + */ + // Inject the constant as C code so that the C compiler can use it. + // -D is not supported by bindgen https://github.com/rust-lang/rust-bindgen/issues/2394 + .header_contents( + "consts", + &format!("#define FIELD_ELEMENTS_PER_BLOB {field_elements_per_blob}"), + ) + .header(header_file) + .clang_args([format!("-I{inc_dir}")]) + // Since this is not part of the header file, needs to be allowed explicitly. + .allowlist_var("FIELD_ELEMENTS_PER_BLOB") + // Get bindings only for the header file. + .allowlist_file(".*/c_kzg_4844.h") + /* + * Cleanup instructions. + */ + // Remove stdio definitions related to FILE. + .opaque_type("FILE") + // Remove the definition of FILE to use the libc one, which is more convenient. + .blocklist_type("FILE") + // Inject rust code using libc's FILE + .raw_line("use libc::FILE;") + // Do no generate layout tests. + .layout_tests(false) + // Extern functions do not need individual extern blocks. + .merge_extern_blocks(true) + // We implement Drop for this type. Copy is not allowed for types with destructors. + .no_copy("KZGSettings") + /* + * API improvements. + */ + // Do not create individual constants for enum variants. + .rustified_enum("C_KZG_RET") + // Make constants used as sizes `usize`. + .parse_callbacks(Box::new(Callbacks)) + // Add PartialEq and Eq impls to types. + .derive_eq(true) + // Blobs are big, we don't want rust to liberally copy them around. + .no_copy("Blob") + // Do not make fields public. If we want to modify them we can create setters/mutable + // getters when necessary. + .default_visibility(bindgen::FieldVisibilityKind::Private) + // Blocklist this type alias to use a custom implementation. If this stops being a type + // alias this line needs to be removed. + .blocklist_type("KZGCommitment") + // Blocklist this type alias to use a custom implementation. If this stops being a type + // alias this line needs to be removed. + .blocklist_type("KZGProof") + /* + * Re-build instructions + */ + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .generate() + .unwrap(); + + bindings + .write_to_file(bindings_out_path) + .expect("Failed to write bindings"); + bindings + .write_to_file(snapshot_path) + .expect("Failed to write snapshot"); +} diff --git a/bindings/rust/snapshots/bindings_x86_64-unknown-linux-gnu_4096.rs b/bindings/rust/snapshots/bindings_x86_64-unknown-linux-gnu_4096.rs new file mode 100644 index 0000000..1c62647 --- /dev/null +++ b/bindings/rust/snapshots/bindings_x86_64-unknown-linux-gnu_4096.rs @@ -0,0 +1,149 @@ +/* automatically generated by rust-bindgen 0.64.0 */ + +use libc::FILE; + +pub const FIELD_ELEMENTS_PER_BLOB: usize = 4096; +pub const BYTES_PER_COMMITMENT: usize = 48; +pub const BYTES_PER_PROOF: usize = 48; +pub const BYTES_PER_FIELD_ELEMENT: usize = 32; +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(u32)] +#[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 performing FFTs."] +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct FFTSettings { + #[doc = " The maximum size of FFT these settings support, a power of 2."] + max_width: u64, + #[doc = " Ascending powers of the root of unity, length `max_width + 1`."] + expanded_roots_of_unity: *mut fr_t, + #[doc = " Descending powers of the root of unity, length `max_width + 1`."] + reverse_roots_of_unity: *mut fr_t, + #[doc = " Powers of the root of unity in bit-reversal permutation order, length\n `max_width`."] + roots_of_unity: *mut fr_t, +} +#[doc = " Stores the setup and parameters needed for computing KZG proofs."] +#[repr(C)] +#[derive(Debug, PartialEq, Eq)] +pub struct KZGSettings { + #[doc = " The corresponding settings for performing FFTs."] + fs: *mut FFTSettings, + #[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;\n both arrays have `FIELD_ELEMENTS_PER_BLOB` elements."] + 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.rs b/bindings/rust/src/bindings.rs deleted file mode 100644 index 7e3bb18..0000000 --- a/bindings/rust/src/bindings.rs +++ /dev/null @@ -1,262 +0,0 @@ -/* automatically generated by rust-bindgen 0.61.0 */ - -include!("./consts.rs"); - -use std::ops::{Deref, DerefMut}; - -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 BYTES_PER_BLOB: usize = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT; - -type byte = u8; -type limb_t = u64; -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_scalar { - b: [byte; 32usize], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_fr { - l: [limb_t; 4usize], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_fp { - l: [limb_t; 6usize], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_fp2 { - fp: [blst_fp; 2usize], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_fp6 { - fp2: [blst_fp2; 3usize], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_fp12 { - fp6: [blst_fp6; 2usize], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_p1 { - x: blst_fp, - y: blst_fp, - z: blst_fp, -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_p1_affine { - x: blst_fp, - y: blst_fp, -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_p2 { - x: blst_fp2, - y: blst_fp2, - z: blst_fp2, -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct blst_p2_affine { - x: blst_fp2, - y: blst_fp2, -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct Bytes32 { - bytes: [u8; 32], -} - -impl Deref for Bytes32 { - type Target = [u8; 32]; - fn deref(&self) -> &Self::Target { - &self.bytes - } -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct Bytes48 { - bytes: [u8; 48], -} - -impl Deref for Bytes48 { - type Target = [u8; 48]; - fn deref(&self) -> &Self::Target { - &self.bytes - } -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct Blob { - bytes: [u8; BYTES_PER_BLOB], -} - -impl Deref for Blob { - type Target = [u8; BYTES_PER_BLOB]; - fn deref(&self) -> &Self::Target { - &self.bytes - } -} - -impl DerefMut for Blob { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.bytes - } -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct KZGProof { - bytes: [u8; BYTES_PER_PROOF], -} - -impl Deref for KZGProof { - type Target = [u8; BYTES_PER_PROOF]; - fn deref(&self) -> &Self::Target { - &self.bytes - } -} - -type g1_t = blst_p1; -type g2_t = blst_p2; -type fr_t = blst_fr; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct KZGCommitment { - bytes: [u8; BYTES_PER_COMMITMENT], -} - -impl Deref for KZGCommitment { - type Target = [u8; BYTES_PER_COMMITMENT]; - fn deref(&self) -> &Self::Target { - &self.bytes - } -} - -#[must_use] -#[repr(u32)] -#[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 and may indicate a bug in the library."] - C_KZG_ERROR = 2, - #[doc = "< Could not allocate memory."] - C_KZG_MALLOC = 3, -} -#[doc = " Stores the setup and parameters needed for performing FFTs."] -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct FFTSettings { - #[doc = "< The maximum size of FFT these settings support, a power of 2."] - max_width: u64, - #[doc = "< Ascending powers of the root of unity, size `width + 1`."] - expanded_roots_of_unity: *const fr_t, - #[doc = "< Descending powers of the root of unity, size `width + 1`."] - reverse_roots_of_unity: *const fr_t, - #[doc = "< Powers of the root of unity in bit-reversal permutation, size `width`."] - roots_of_unity: *const fr_t, -} - -#[doc = " Stores the setup and parameters needed for computing KZG proofs."] -#[repr(C)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct KZGSettings { - #[doc = "< The corresponding settings for performing FFTs."] - fs: *const FFTSettings, - #[doc = "< G1 group elements from the trusted setup, in Lagrange form bit-reversal permutation."] - g1_values: *const g1_t, - #[doc = "< G2 group elements from the trusted setup; both arrays have FIELD_ELEMENTS_PER_BLOB elements."] - g2_values: *const g2_t, -} - -/// Safety: FFTSettings is initialized once on calling `load_trusted_setup`. After -/// that, the struct is never modified. The memory for the arrays within `FFTSettings` and -/// `g1_values` and `g2_values` are only freed on calling `free_trusted_setup` which only happens -/// when we drop the struct. -unsafe impl Sync for KZGSettings {} -unsafe impl Send for KZGSettings {} - -extern "C" { - pub fn load_trusted_setup( - out: *mut KZGSettings, - g1_bytes: *const u8, /* n1 * 48 bytes */ - n1: usize, - g2_bytes: *const u8, /* n2 * 96 bytes */ - 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( - out: *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( - out: *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( - out: *mut bool, - blobs: *const Blob, - commitments_bytes: *const Bytes48, - proofs_bytes: *const Bytes48, - count: usize, - s: *const KZGSettings, - ) -> C_KZG_RET; -} diff --git a/bindings/rust/src/bindings/mod.rs b/bindings/rust/src/bindings/mod.rs new file mode 100644 index 0000000..f385ca7 --- /dev/null +++ b/bindings/rust/src/bindings/mod.rs @@ -0,0 +1,745 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +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; +pub const BYTES_PER_G2_POINT: usize = 96; + +/// Number of G2 points required for the kzg trusted setup. +/// 65 is fixed and is used for providing multiproofs up to 64 field elements. +const NUM_G2_POINTS: usize = 65; + +/// A trusted (valid) KZG commitment. +// NOTE: this is a type alias to the struct Bytes48, same as [`KZGProof`] in the C header files. To +// facilitate type safety: proofs and commitments should not be interchangeable, we use a +// custom implementation. +#[repr(C)] +pub struct KZGCommitment { + bytes: [u8; BYTES_PER_COMMITMENT], +} + +/// A trusted (valid) KZG proof. +// NOTE: this is a type alias to the struct Bytes48, same as [`KZGCommitment`] in the C header +// files. To facilitate type safety: proofs and commitments should not be interchangeable, we +// use a custom implementation. +#[repr(C)] +pub struct KZGProof { + bytes: [u8; BYTES_PER_PROOF], +} + +#[derive(Debug)] +pub enum Error { + /// Wrong number of bytes. + InvalidBytesLength(String), + /// The KZG proof is invalid. + InvalidKzgProof(String), + /// The KZG commitment is invalid. + InvalidKzgCommitment(String), + /// The provided trusted setup is invalid. + InvalidTrustedSetup(String), + /// Paired arguments have different lengths. + MismatchLength(String), + /// The underlying c-kzg library returned an error. + CError(C_KZG_RET), +} + +/// Holds the parameters of a kzg trusted setup ceremony. +impl KZGSettings { + /// Initializes a trusted setup from `FIELD_ELEMENTS_PER_BLOB` g1 points + /// and 65 g2 points in byte format. + pub fn load_trusted_setup( + g1_bytes: Vec<[u8; BYTES_PER_G1_POINT]>, + g2_bytes: Vec<[u8; BYTES_PER_G2_POINT]>, + ) -> Result { + if g1_bytes.len() != FIELD_ELEMENTS_PER_BLOB { + return Err(Error::InvalidTrustedSetup(format!( + "Invalid number of g1 points in trusted setup. Expected {} got {}", + FIELD_ELEMENTS_PER_BLOB, + g1_bytes.len() + ))); + } + if g2_bytes.len() != NUM_G2_POINTS { + return Err(Error::InvalidTrustedSetup(format!( + "Invalid number of g2 points in trusted setup. Expected {} got {}", + NUM_G2_POINTS, + g2_bytes.len() + ))); + } + let mut kzg_settings = MaybeUninit::::uninit(); + unsafe { + let res = load_trusted_setup( + kzg_settings.as_mut_ptr(), + g1_bytes.as_ptr() as *const u8, + g1_bytes.len(), + g2_bytes.as_ptr() as *const u8, + g2_bytes.len(), + ); + if let C_KZG_RET::C_KZG_OK = res { + Ok(kzg_settings.assume_init()) + } else { + Err(Error::InvalidTrustedSetup(format!( + "Invalid trusted setup: {:?}", + res + ))) + } + } + } + + /// Loads the trusted setup parameters from a file. The file format is as follows: + /// + /// FIELD_ELEMENTS_PER_BLOB + /// 65 # This is fixed and is used for providing multiproofs up to 64 field elements. + /// 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)) + })?; + let mut kzg_settings = MaybeUninit::::uninit(); + unsafe { + let file_ptr = fopen(file_path.as_ptr(), &('r' as libc::c_char)); + 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()) + } else { + Err(Error::InvalidTrustedSetup(format!( + "Invalid trusted setup: {:?}", + res + ))) + } + } + } +} + +impl Drop for KZGSettings { + fn drop(&mut self) { + unsafe { free_trusted_setup(self) } + } +} + +impl Blob { + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != BYTES_PER_BLOB { + return Err(Error::InvalidBytesLength(format!( + "Invalid byte length. Expected {} got {}", + BYTES_PER_BLOB, + bytes.len(), + ))); + } + let mut new_bytes = [0; BYTES_PER_BLOB]; + new_bytes.copy_from_slice(bytes); + Ok(Self { bytes: new_bytes }) + } + + pub fn from_hex(hex_str: &str) -> Result { + Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap()) + } +} + +impl Bytes32 { + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 32 { + return Err(Error::InvalidBytesLength(format!( + "Invalid byte length. Expected {} got {}", + 32, + bytes.len(), + ))); + } + let mut new_bytes = [0; 32]; + new_bytes.copy_from_slice(bytes); + Ok(Self { bytes: new_bytes }) + } + + pub fn from_hex(hex_str: &str) -> Result { + Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap()) + } +} + +impl Bytes48 { + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 48 { + return Err(Error::InvalidBytesLength(format!( + "Invalid byte length. Expected {} got {}", + 48, + bytes.len(), + ))); + } + let mut new_bytes = [0; 48]; + new_bytes.copy_from_slice(bytes); + Ok(Self { bytes: new_bytes }) + } + + pub fn from_hex(hex_str: &str) -> Result { + Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap()) + } + + pub fn into_inner(self) -> [u8; 48] { + self.bytes + } +} + +impl KZGProof { + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != BYTES_PER_PROOF { + return Err(Error::InvalidKzgProof(format!( + "Invalid byte length. Expected {} got {}", + BYTES_PER_PROOF, + bytes.len(), + ))); + } + let mut proof_bytes = [0; BYTES_PER_PROOF]; + proof_bytes.copy_from_slice(bytes); + Ok(Self { bytes: proof_bytes }) + } + + pub fn to_bytes(&self) -> Bytes48 { + Bytes48 { bytes: self.bytes } + } + + pub fn as_hex_string(&self) -> String { + hex::encode(self.bytes) + } + + pub fn compute_kzg_proof( + blob: Blob, + z_bytes: Bytes32, + kzg_settings: &KZGSettings, + ) -> Result<(Self, Bytes32), Error> { + let mut kzg_proof = MaybeUninit::::uninit(); + let mut y_out = MaybeUninit::::uninit(); + unsafe { + let res = compute_kzg_proof( + kzg_proof.as_mut_ptr(), + y_out.as_mut_ptr(), + &blob, + &z_bytes, + kzg_settings, + ); + if let C_KZG_RET::C_KZG_OK = res { + Ok((kzg_proof.assume_init(), y_out.assume_init())) + } else { + Err(Error::CError(res)) + } + } + } + + pub fn compute_blob_kzg_proof( + blob: Blob, + commitment_bytes: Bytes48, + kzg_settings: &KZGSettings, + ) -> Result { + let mut kzg_proof = MaybeUninit::::uninit(); + unsafe { + let res = compute_blob_kzg_proof( + kzg_proof.as_mut_ptr(), + &blob, + &commitment_bytes, + kzg_settings, + ); + if let C_KZG_RET::C_KZG_OK = res { + Ok(kzg_proof.assume_init()) + } else { + Err(Error::CError(res)) + } + } + } + + pub fn verify_kzg_proof( + commitment_bytes: Bytes48, + z_bytes: Bytes32, + y_bytes: Bytes32, + proof_bytes: Bytes48, + kzg_settings: &KZGSettings, + ) -> Result { + let mut verified: MaybeUninit = MaybeUninit::uninit(); + unsafe { + let res = verify_kzg_proof( + verified.as_mut_ptr(), + &commitment_bytes, + &z_bytes, + &y_bytes, + &proof_bytes, + kzg_settings, + ); + if let C_KZG_RET::C_KZG_OK = res { + Ok(verified.assume_init()) + } else { + Err(Error::CError(res)) + } + } + } + + pub fn verify_blob_kzg_proof( + blob: Blob, + commitment_bytes: Bytes48, + proof_bytes: Bytes48, + kzg_settings: &KZGSettings, + ) -> Result { + let mut verified: MaybeUninit = MaybeUninit::uninit(); + unsafe { + let res = verify_blob_kzg_proof( + verified.as_mut_ptr(), + &blob, + &commitment_bytes, + &proof_bytes, + kzg_settings, + ); + if let C_KZG_RET::C_KZG_OK = res { + Ok(verified.assume_init()) + } else { + Err(Error::CError(res)) + } + } + } + + pub fn verify_blob_kzg_proof_batch( + blobs: &[Blob], + commitments_bytes: &[Bytes48], + proofs_bytes: &[Bytes48], + kzg_settings: &KZGSettings, + ) -> Result { + if blobs.len() != commitments_bytes.len() { + return Err(Error::MismatchLength(format!( + "There are {} blobs and {} commitments", + blobs.len(), + commitments_bytes.len() + ))); + } + if blobs.len() != proofs_bytes.len() { + return Err(Error::MismatchLength(format!( + "There are {} blobs and {} proofs", + blobs.len(), + proofs_bytes.len() + ))); + } + let mut verified: MaybeUninit = MaybeUninit::uninit(); + unsafe { + let res = verify_blob_kzg_proof_batch( + verified.as_mut_ptr(), + blobs.as_ptr(), + commitments_bytes.as_ptr(), + proofs_bytes.as_ptr(), + blobs.len(), + kzg_settings, + ); + if let C_KZG_RET::C_KZG_OK = res { + Ok(verified.assume_init()) + } else { + Err(Error::CError(res)) + } + } + } +} + +impl KZGCommitment { + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != BYTES_PER_COMMITMENT { + return Err(Error::InvalidKzgCommitment(format!( + "Invalid byte length. Expected {} got {}", + BYTES_PER_PROOF, + bytes.len(), + ))); + } + let mut commitment = [0; BYTES_PER_COMMITMENT]; + commitment.copy_from_slice(bytes); + Ok(Self { bytes: commitment }) + } + + pub fn to_bytes(&self) -> Bytes48 { + Bytes48 { bytes: self.bytes } + } + + pub fn as_hex_string(&self) -> String { + hex::encode(self.bytes) + } + + pub fn blob_to_kzg_commitment(blob: Blob, kzg_settings: &KZGSettings) -> Result { + let mut kzg_commitment: MaybeUninit = MaybeUninit::uninit(); + unsafe { + let res = blob_to_kzg_commitment( + kzg_commitment.as_mut_ptr(), + blob.as_ptr() as *const Blob, + kzg_settings, + ); + if let C_KZG_RET::C_KZG_OK = res { + Ok(kzg_commitment.assume_init()) + } else { + Err(Error::CError(res)) + } + } + } +} + +impl From<[u8; BYTES_PER_COMMITMENT]> for KZGCommitment { + fn from(value: [u8; BYTES_PER_COMMITMENT]) -> Self { + Self { bytes: value } + } +} + +impl From<[u8; BYTES_PER_PROOF]> for KZGProof { + fn from(value: [u8; BYTES_PER_PROOF]) -> Self { + Self { bytes: value } + } +} + +impl From<[u8; BYTES_PER_BLOB]> for Blob { + fn from(value: [u8; BYTES_PER_BLOB]) -> Self { + Self { bytes: value } + } +} + +impl From<[u8; 32]> for Bytes32 { + fn from(value: [u8; 32]) -> Self { + Self { bytes: value } + } +} + +impl From<[u8; 48]> for Bytes48 { + fn from(value: [u8; 48]) -> Self { + Self { bytes: value } + } +} + +use std::ops::{Deref, DerefMut}; + +impl Deref for Bytes32 { + type Target = [u8; 32]; + fn deref(&self) -> &Self::Target { + &self.bytes + } +} + +impl Deref for Bytes48 { + type Target = [u8; 48]; + fn deref(&self) -> &Self::Target { + &self.bytes + } +} + +impl Deref for Blob { + type Target = [u8; BYTES_PER_BLOB]; + fn deref(&self) -> &Self::Target { + &self.bytes + } +} + +impl DerefMut for Blob { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.bytes + } +} + +impl Clone for Blob { + fn clone(&self) -> Self { + Blob { bytes: self.bytes } + } +} + +impl Deref for KZGProof { + type Target = [u8; BYTES_PER_PROOF]; + fn deref(&self) -> &Self::Target { + &self.bytes + } +} + +impl Deref for KZGCommitment { + type Target = [u8; BYTES_PER_COMMITMENT]; + fn deref(&self) -> &Self::Target { + &self.bytes + } +} + +/// Safety: FFTSettings is initialized once on calling `load_trusted_setup`. After +/// that, the struct is never modified. The memory for the arrays within `FFTSettings` and +/// `g1_values` and `g2_values` are only freed on calling `free_trusted_setup` which only happens +/// when we drop the struct. +unsafe impl Sync for KZGSettings {} +unsafe impl Send for KZGSettings {} + +#[cfg(test)] +mod tests { + use super::*; + use rand::{rngs::ThreadRng, Rng}; + use std::fs; + + use test_formats::{ + blob_to_kzg_commitment_test, compute_blob_kzg_proof, compute_kzg_proof, + verify_blob_kzg_proof, verify_blob_kzg_proof_batch, verify_kzg_proof, + }; + + fn generate_random_blob(rng: &mut ThreadRng) -> Blob { + let mut arr = [0u8; BYTES_PER_BLOB]; + rng.fill(&mut arr[..]); + // Ensure that the blob is canonical by ensuring that + // each field element contained in the blob is < BLS_MODULUS + for i in 0..FIELD_ELEMENTS_PER_BLOB { + arr[i * BYTES_PER_FIELD_ELEMENT + BYTES_PER_FIELD_ELEMENT - 1] = 0; + } + arr.into() + } + + fn test_simple(trusted_setup_file: PathBuf) { + let mut rng = rand::thread_rng(); + assert!(trusted_setup_file.exists()); + let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); + + let num_blobs: usize = rng.gen_range(1..16); + let mut blobs: Vec = (0..num_blobs) + .map(|_| generate_random_blob(&mut rng)) + .collect(); + + let commitments: Vec = blobs + .iter() + .map(|blob| KZGCommitment::blob_to_kzg_commitment(blob.clone(), &kzg_settings).unwrap()) + .map(|commitment| commitment.to_bytes()) + .collect(); + + let proofs: Vec = blobs + .iter() + .zip(commitments.iter()) + .map(|(blob, commitment)| { + KZGProof::compute_blob_kzg_proof(blob.clone(), *commitment, &kzg_settings).unwrap() + }) + .map(|proof| proof.to_bytes()) + .collect(); + + assert!(KZGProof::verify_blob_kzg_proof_batch( + &blobs, + &commitments, + &proofs, + &kzg_settings + ) + .unwrap()); + + blobs.pop(); + + let error = + KZGProof::verify_blob_kzg_proof_batch(&blobs, &commitments, &proofs, &kzg_settings) + .unwrap_err(); + assert!(matches!(error, Error::MismatchLength(_))); + + let incorrect_blob = generate_random_blob(&mut rng); + blobs.push(incorrect_blob); + + assert!(!KZGProof::verify_blob_kzg_proof_batch( + &blobs, + &commitments, + &proofs, + &kzg_settings + ) + .unwrap()); + } + + #[test] + fn test_end_to_end() { + let trusted_setup_file = if cfg!(feature = "minimal-spec") { + PathBuf::from("../../src/trusted_setup_4.txt") + } else { + PathBuf::from("../../src/trusted_setup.txt") + }; + test_simple(trusted_setup_file); + } + + const BLOB_TO_KZG_COMMITMENT_TESTS: &str = "../../tests/blob_to_kzg_commitment/*/*/*"; + const COMPUTE_KZG_PROOF_TESTS: &str = "../../tests/compute_kzg_proof/*/*/*"; + const COMPUTE_BLOB_KZG_PROOF_TESTS: &str = "../../tests/compute_blob_kzg_proof/*/*/*"; + const VERIFY_KZG_PROOF_TESTS: &str = "../../tests/verify_kzg_proof/*/*/*"; + const VERIFY_BLOB_KZG_PROOF_TESTS: &str = "../../tests/verify_blob_kzg_proof/*/*/*"; + const VERIFY_BLOB_KZG_PROOF_BATCH_TESTS: &str = "../../tests/verify_blob_kzg_proof_batch/*/*/*"; + + #[cfg(not(feature = "minimal-spec"))] + #[test] + fn test_blob_to_kzg_commitment() { + let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); + assert!(trusted_setup_file.exists()); + let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); + let test_files: Vec = glob::glob(BLOB_TO_KZG_COMMITMENT_TESTS) + .unwrap() + .map(Result::unwrap) + .collect(); + assert!(!test_files.is_empty()); + + for test_file in test_files { + let yaml_data = fs::read_to_string(test_file).unwrap(); + let test: blob_to_kzg_commitment_test::Test = serde_yaml::from_str(&yaml_data).unwrap(); + let Ok(blob) = test.input.get_blob() else { + assert!(test.get_output().is_none()); + continue; + }; + + match KZGCommitment::blob_to_kzg_commitment(blob, &kzg_settings) { + Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes), + _ => assert!(test.get_output().is_none()), + } + } + } + + #[cfg(not(feature = "minimal-spec"))] + #[test] + fn test_compute_kzg_proof() { + let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); + assert!(trusted_setup_file.exists()); + let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); + let test_files: Vec = glob::glob(COMPUTE_KZG_PROOF_TESTS) + .unwrap() + .map(Result::unwrap) + .collect(); + assert!(!test_files.is_empty()); + + for test_file in test_files { + let yaml_data = fs::read_to_string(test_file).unwrap(); + let test: compute_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); + let (Ok(blob), Ok(z)) = (test.input.get_blob(), test.input.get_z()) else { + assert!(test.get_output().is_none()); + continue; + }; + + match KZGProof::compute_kzg_proof(blob, z, &kzg_settings) { + Ok((proof, y)) => { + assert_eq!(proof.bytes, test.get_output().unwrap().0.bytes); + assert_eq!(y.bytes, test.get_output().unwrap().1.bytes); + } + _ => assert!(test.get_output().is_none()), + } + } + } + + #[cfg(not(feature = "minimal-spec"))] + #[test] + fn test_compute_blob_kzg_proof() { + let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); + assert!(trusted_setup_file.exists()); + let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); + let test_files: Vec = glob::glob(COMPUTE_BLOB_KZG_PROOF_TESTS) + .unwrap() + .map(Result::unwrap) + .collect(); + assert!(!test_files.is_empty()); + + for test_file in test_files { + let yaml_data = fs::read_to_string(test_file).unwrap(); + let test: compute_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); + let (Ok(blob), Ok(commitment)) = ( + test.input.get_blob(), + test.input.get_commitment() + ) else { + assert!(test.get_output().is_none()); + continue; + }; + + match KZGProof::compute_blob_kzg_proof(blob, commitment, &kzg_settings) { + Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes), + _ => assert!(test.get_output().is_none()), + } + } + } + + #[cfg(not(feature = "minimal-spec"))] + #[test] + fn test_verify_kzg_proof() { + let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); + assert!(trusted_setup_file.exists()); + let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); + let test_files: Vec = glob::glob(VERIFY_KZG_PROOF_TESTS) + .unwrap() + .map(Result::unwrap) + .collect(); + assert!(!test_files.is_empty()); + + for test_file in test_files { + let yaml_data = fs::read_to_string(test_file).unwrap(); + let test: verify_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); + let (Ok(commitment), Ok(z), Ok(y), Ok(proof)) = ( + test.input.get_commitment(), + test.input.get_z(), + test.input.get_y(), + test.input.get_proof() + ) else { + assert!(test.get_output().is_none()); + continue; + }; + + match KZGProof::verify_kzg_proof(commitment, z, y, proof, &kzg_settings) { + Ok(res) => assert_eq!(res, test.get_output().unwrap()), + _ => assert!(test.get_output().is_none()), + } + } + } + + #[cfg(not(feature = "minimal-spec"))] + #[test] + fn test_verify_blob_kzg_proof() { + let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); + assert!(trusted_setup_file.exists()); + let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); + let test_files: Vec = glob::glob(VERIFY_BLOB_KZG_PROOF_TESTS) + .unwrap() + .map(Result::unwrap) + .collect(); + assert!(!test_files.is_empty()); + + for test_file in test_files { + let yaml_data = fs::read_to_string(test_file).unwrap(); + let test: verify_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); + let (Ok(blob), Ok(commitment), Ok(proof)) = ( + test.input.get_blob(), + test.input.get_commitment(), + test.input.get_proof() + ) else { + assert!(test.get_output().is_none()); + continue; + }; + + match KZGProof::verify_blob_kzg_proof(blob, commitment, proof, &kzg_settings) { + Ok(res) => assert_eq!(res, test.get_output().unwrap()), + _ => assert!(test.get_output().is_none()), + } + } + } + + #[cfg(not(feature = "minimal-spec"))] + #[test] + fn test_verify_blob_kzg_proof_batch() { + let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); + assert!(trusted_setup_file.exists()); + let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); + let test_files: Vec = glob::glob(VERIFY_BLOB_KZG_PROOF_BATCH_TESTS) + .unwrap() + .map(Result::unwrap) + .collect(); + assert!(!test_files.is_empty()); + + for test_file in test_files { + let yaml_data = fs::read_to_string(test_file).unwrap(); + let test: verify_blob_kzg_proof_batch::Test = serde_yaml::from_str(&yaml_data).unwrap(); + let (Ok(blobs), Ok(commitments), Ok(proofs)) = ( + test.input.get_blobs(), + test.input.get_commitments(), + test.input.get_proofs() + ) else { + assert!(test.get_output().is_none()); + continue; + }; + + match KZGProof::verify_blob_kzg_proof_batch( + &blobs, + &commitments, + &proofs, + &kzg_settings, + ) { + Ok(res) => assert_eq!(res, test.get_output().unwrap()), + _ => assert!(test.get_output().is_none()), + } + } + } +} diff --git a/bindings/rust/src/test_formats/blob_to_kzg_commitment_test.rs b/bindings/rust/src/bindings/test_formats/blob_to_kzg_commitment_test.rs similarity index 100% rename from bindings/rust/src/test_formats/blob_to_kzg_commitment_test.rs rename to bindings/rust/src/bindings/test_formats/blob_to_kzg_commitment_test.rs diff --git a/bindings/rust/src/test_formats/compute_blob_kzg_proof.rs b/bindings/rust/src/bindings/test_formats/compute_blob_kzg_proof.rs similarity index 100% rename from bindings/rust/src/test_formats/compute_blob_kzg_proof.rs rename to bindings/rust/src/bindings/test_formats/compute_blob_kzg_proof.rs diff --git a/bindings/rust/src/test_formats/compute_kzg_proof.rs b/bindings/rust/src/bindings/test_formats/compute_kzg_proof.rs similarity index 100% rename from bindings/rust/src/test_formats/compute_kzg_proof.rs rename to bindings/rust/src/bindings/test_formats/compute_kzg_proof.rs diff --git a/bindings/rust/src/test_formats/mod.rs b/bindings/rust/src/bindings/test_formats/mod.rs similarity index 100% rename from bindings/rust/src/test_formats/mod.rs rename to bindings/rust/src/bindings/test_formats/mod.rs diff --git a/bindings/rust/src/test_formats/verify_blob_kzg_proof.rs b/bindings/rust/src/bindings/test_formats/verify_blob_kzg_proof.rs similarity index 100% rename from bindings/rust/src/test_formats/verify_blob_kzg_proof.rs rename to bindings/rust/src/bindings/test_formats/verify_blob_kzg_proof.rs diff --git a/bindings/rust/src/test_formats/verify_blob_kzg_proof_batch.rs b/bindings/rust/src/bindings/test_formats/verify_blob_kzg_proof_batch.rs similarity index 100% rename from bindings/rust/src/test_formats/verify_blob_kzg_proof_batch.rs rename to bindings/rust/src/bindings/test_formats/verify_blob_kzg_proof_batch.rs diff --git a/bindings/rust/src/test_formats/verify_kzg_proof.rs b/bindings/rust/src/bindings/test_formats/verify_kzg_proof.rs similarity index 100% rename from bindings/rust/src/test_formats/verify_kzg_proof.rs rename to bindings/rust/src/bindings/test_formats/verify_kzg_proof.rs diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index a9c236d..05bd6e9 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -1,675 +1,14 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -mod test_formats; - -include!("bindings.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; -pub const BYTES_PER_G2_POINT: usize = 96; - -/// Number of G2 points required for the kzg trusted setup. -/// 65 is fixed and is used for providing multiproofs up to 64 field elements. -const NUM_G2_POINTS: usize = 65; - -#[derive(Debug)] -pub enum Error { - /// Wrong number of bytes. - InvalidBytesLength(String), - /// The KZG proof is invalid. - InvalidKzgProof(String), - /// The KZG commitment is invalid. - InvalidKzgCommitment(String), - /// The provided trusted setup is invalid. - InvalidTrustedSetup(String), - /// Paired arguments have different lengths. - MismatchLength(String), - /// The underlying c-kzg library returned an error. - CError(C_KZG_RET), -} - -/// Holds the parameters of a kzg trusted setup ceremony. -impl KZGSettings { - /// Initializes a trusted setup from `FIELD_ELEMENTS_PER_BLOB` g1 points - /// and 65 g2 points in byte format. - pub fn load_trusted_setup( - g1_bytes: Vec<[u8; BYTES_PER_G1_POINT]>, - g2_bytes: Vec<[u8; BYTES_PER_G2_POINT]>, - ) -> Result { - if g1_bytes.len() != FIELD_ELEMENTS_PER_BLOB { - return Err(Error::InvalidTrustedSetup(format!( - "Invalid number of g1 points in trusted setup. Expected {} got {}", - FIELD_ELEMENTS_PER_BLOB, - g1_bytes.len() - ))); - } - if g2_bytes.len() != NUM_G2_POINTS { - return Err(Error::InvalidTrustedSetup(format!( - "Invalid number of g2 points in trusted setup. Expected {} got {}", - NUM_G2_POINTS, - g2_bytes.len() - ))); - } - let mut kzg_settings = MaybeUninit::::uninit(); - unsafe { - let res = load_trusted_setup( - kzg_settings.as_mut_ptr(), - g1_bytes.as_ptr() as *const u8, - g1_bytes.len(), - g2_bytes.as_ptr() as *const u8, - g2_bytes.len(), - ); - if let C_KZG_RET::C_KZG_OK = res { - Ok(kzg_settings.assume_init()) - } else { - Err(Error::InvalidTrustedSetup(format!( - "Invalid trusted setup: {:?}", - res - ))) - } - } - } - - /// Loads the trusted setup parameters from a file. The file format is as follows: - /// - /// FIELD_ELEMENTS_PER_BLOB - /// 65 # This is fixed and is used for providing multiproofs up to 64 field elements. - /// 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)) - })?; - let mut kzg_settings = MaybeUninit::::uninit(); - unsafe { - let file_ptr = fopen(file_path.as_ptr(), &('r' as libc::c_char)); - 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()) - } else { - Err(Error::InvalidTrustedSetup(format!( - "Invalid trusted setup: {:?}", - res - ))) - } - } - } -} - -impl Drop for KZGSettings { - fn drop(&mut self) { - unsafe { free_trusted_setup(self) } - } -} - -impl Blob { - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != BYTES_PER_BLOB { - return Err(Error::InvalidBytesLength(format!( - "Invalid byte length. Expected {} got {}", - BYTES_PER_BLOB, - bytes.len(), - ))); - } - let mut new_bytes = [0; BYTES_PER_BLOB]; - new_bytes.copy_from_slice(bytes); - Ok(Self { bytes: new_bytes }) - } - - pub fn from_hex(hex_str: &str) -> Result { - Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap()) - } -} - -impl Bytes32 { - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != 32 { - return Err(Error::InvalidBytesLength(format!( - "Invalid byte length. Expected {} got {}", - 32, - bytes.len(), - ))); - } - let mut new_bytes = [0; 32]; - new_bytes.copy_from_slice(bytes); - Ok(Self { bytes: new_bytes }) - } - - pub fn from_hex(hex_str: &str) -> Result { - Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap()) - } -} - -impl Bytes48 { - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != 48 { - return Err(Error::InvalidBytesLength(format!( - "Invalid byte length. Expected {} got {}", - 48, - bytes.len(), - ))); - } - let mut new_bytes = [0; 48]; - new_bytes.copy_from_slice(bytes); - Ok(Self { bytes: new_bytes }) - } - - pub fn from_hex(hex_str: &str) -> Result { - Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap()) - } - - pub fn into_inner(self) -> [u8; 48] { - self.bytes - } -} - -impl KZGProof { - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != BYTES_PER_PROOF { - return Err(Error::InvalidKzgProof(format!( - "Invalid byte length. Expected {} got {}", - BYTES_PER_PROOF, - bytes.len(), - ))); - } - let mut proof_bytes = [0; BYTES_PER_PROOF]; - proof_bytes.copy_from_slice(bytes); - Ok(Self { bytes: proof_bytes }) - } - - pub fn to_bytes(&self) -> Bytes48 { - let mut bytes = [0; 48]; - bytes.copy_from_slice(&self.bytes); - Bytes48 { bytes } - } - - pub fn as_hex_string(&self) -> String { - hex::encode(self.bytes) - } - - pub fn compute_kzg_proof( - blob: Blob, - z_bytes: Bytes32, - kzg_settings: &KZGSettings, - ) -> Result<(Self, Bytes32), Error> { - let mut kzg_proof = MaybeUninit::::uninit(); - let mut y_out = MaybeUninit::::uninit(); - unsafe { - let res = compute_kzg_proof( - kzg_proof.as_mut_ptr(), - y_out.as_mut_ptr(), - &blob, - &z_bytes, - kzg_settings, - ); - if let C_KZG_RET::C_KZG_OK = res { - Ok((kzg_proof.assume_init(), y_out.assume_init())) - } else { - Err(Error::CError(res)) - } - } - } - - pub fn compute_blob_kzg_proof( - blob: Blob, - commitment_bytes: Bytes48, - kzg_settings: &KZGSettings, - ) -> Result { - let mut kzg_proof = MaybeUninit::::uninit(); - unsafe { - let res = compute_blob_kzg_proof( - kzg_proof.as_mut_ptr(), - &blob, - &commitment_bytes, - kzg_settings, - ); - if let C_KZG_RET::C_KZG_OK = res { - Ok(kzg_proof.assume_init()) - } else { - Err(Error::CError(res)) - } - } - } - - pub fn verify_kzg_proof( - commitment_bytes: Bytes48, - z_bytes: Bytes32, - y_bytes: Bytes32, - proof_bytes: Bytes48, - kzg_settings: &KZGSettings, - ) -> Result { - let mut verified: MaybeUninit = MaybeUninit::uninit(); - unsafe { - let res = verify_kzg_proof( - verified.as_mut_ptr(), - &commitment_bytes, - &z_bytes, - &y_bytes, - &proof_bytes, - kzg_settings, - ); - if let C_KZG_RET::C_KZG_OK = res { - Ok(verified.assume_init()) - } else { - Err(Error::CError(res)) - } - } - } - - pub fn verify_blob_kzg_proof( - blob: Blob, - commitment_bytes: Bytes48, - proof_bytes: Bytes48, - kzg_settings: &KZGSettings, - ) -> Result { - let mut verified: MaybeUninit = MaybeUninit::uninit(); - unsafe { - let res = verify_blob_kzg_proof( - verified.as_mut_ptr(), - &blob, - &commitment_bytes, - &proof_bytes, - kzg_settings, - ); - if let C_KZG_RET::C_KZG_OK = res { - Ok(verified.assume_init()) - } else { - Err(Error::CError(res)) - } - } - } - - pub fn verify_blob_kzg_proof_batch( - blobs: &[Blob], - commitments_bytes: &[Bytes48], - proofs_bytes: &[Bytes48], - kzg_settings: &KZGSettings, - ) -> Result { - if blobs.len() != commitments_bytes.len() { - return Err(Error::MismatchLength(format!( - "There are {} blobs and {} commitments", - blobs.len(), - commitments_bytes.len() - ))); - } - if blobs.len() != proofs_bytes.len() { - return Err(Error::MismatchLength(format!( - "There are {} blobs and {} proofs", - blobs.len(), - proofs_bytes.len() - ))); - } - let mut verified: MaybeUninit = MaybeUninit::uninit(); - unsafe { - let res = verify_blob_kzg_proof_batch( - verified.as_mut_ptr(), - blobs.as_ptr(), - commitments_bytes.as_ptr(), - proofs_bytes.as_ptr(), - blobs.len(), - kzg_settings, - ); - if let C_KZG_RET::C_KZG_OK = res { - Ok(verified.assume_init()) - } else { - Err(Error::CError(res)) - } - } - } -} - -impl KZGCommitment { - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != BYTES_PER_COMMITMENT { - return Err(Error::InvalidKzgCommitment(format!( - "Invalid byte length. Expected {} got {}", - BYTES_PER_PROOF, - bytes.len(), - ))); - } - let mut commitment = [0; BYTES_PER_COMMITMENT]; - commitment.copy_from_slice(bytes); - Ok(Self { bytes: commitment }) - } - - pub fn to_bytes(&self) -> Bytes48 { - let mut bytes = [0; 48]; - bytes.copy_from_slice(&self.bytes); - Bytes48 { bytes } - } - - pub fn as_hex_string(&self) -> String { - hex::encode(self.bytes) - } - - pub fn blob_to_kzg_commitment(blob: Blob, kzg_settings: &KZGSettings) -> Result { - let mut kzg_commitment: MaybeUninit = MaybeUninit::uninit(); - unsafe { - let res = blob_to_kzg_commitment( - kzg_commitment.as_mut_ptr(), - blob.as_ptr() as *const Blob, - kzg_settings, - ); - if let C_KZG_RET::C_KZG_OK = res { - Ok(kzg_commitment.assume_init()) - } else { - Err(Error::CError(res)) - } - } - } -} - -impl From<[u8; BYTES_PER_COMMITMENT]> for KZGCommitment { - fn from(value: [u8; BYTES_PER_COMMITMENT]) -> Self { - Self { bytes: value } - } -} - -impl From<[u8; BYTES_PER_PROOF]> for KZGProof { - fn from(value: [u8; BYTES_PER_PROOF]) -> Self { - Self { bytes: value } - } -} - -impl From<[u8; BYTES_PER_BLOB]> for Blob { - fn from(value: [u8; BYTES_PER_BLOB]) -> Self { - Self { bytes: value } - } -} - -impl From<[u8; 32]> for Bytes32 { - fn from(value: [u8; 32]) -> Self { - Self { bytes: value } - } -} - -impl From<[u8; 48]> for Bytes48 { - fn from(value: [u8; 48]) -> Self { - Self { bytes: value } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::{rngs::ThreadRng, Rng}; - use std::fs; - - use crate::test_formats::{ - blob_to_kzg_commitment_test, compute_blob_kzg_proof, compute_kzg_proof, - verify_blob_kzg_proof, verify_blob_kzg_proof_batch, verify_kzg_proof, - }; - - fn generate_random_blob(rng: &mut ThreadRng) -> Blob { - let mut arr = [0u8; BYTES_PER_BLOB]; - rng.fill(&mut arr[..]); - // Ensure that the blob is canonical by ensuring that - // each field element contained in the blob is < BLS_MODULUS - for i in 0..FIELD_ELEMENTS_PER_BLOB { - arr[i * BYTES_PER_FIELD_ELEMENT + BYTES_PER_FIELD_ELEMENT - 1] = 0; - } - arr.into() - } - - fn test_simple(trusted_setup_file: PathBuf) { - let mut rng = rand::thread_rng(); - assert!(trusted_setup_file.exists()); - let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); - - let num_blobs: usize = rng.gen_range(1..16); - let mut blobs: Vec = (0..num_blobs) - .map(|_| generate_random_blob(&mut rng)) - .collect(); - - let commitments: Vec = blobs - .iter() - .map(|blob| KZGCommitment::blob_to_kzg_commitment(*blob, &kzg_settings).unwrap()) - .map(|commitment| commitment.to_bytes()) - .collect(); - - let proofs: Vec = blobs - .iter() - .zip(commitments.iter()) - .map(|(blob, commitment)| { - KZGProof::compute_blob_kzg_proof(*blob, *commitment, &kzg_settings).unwrap() - }) - .map(|proof| proof.to_bytes()) - .collect(); - - assert!(KZGProof::verify_blob_kzg_proof_batch( - &blobs, - &commitments, - &proofs, - &kzg_settings - ) - .unwrap()); - - blobs.pop(); - - let error = - KZGProof::verify_blob_kzg_proof_batch(&blobs, &commitments, &proofs, &kzg_settings) - .unwrap_err(); - assert!(matches!(error, Error::MismatchLength(_))); - - let incorrect_blob = generate_random_blob(&mut rng); - blobs.push(incorrect_blob); - - assert!(!KZGProof::verify_blob_kzg_proof_batch( - &blobs, - &commitments, - &proofs, - &kzg_settings - ) - .unwrap()); - } - - #[test] - fn test_end_to_end() { - let trusted_setup_file = if cfg!(feature = "minimal-spec") { - PathBuf::from("../../src/trusted_setup_4.txt") - } else { - PathBuf::from("../../src/trusted_setup.txt") - }; - test_simple(trusted_setup_file); - } - - const BLOB_TO_KZG_COMMITMENT_TESTS: &str = "../../tests/blob_to_kzg_commitment/*/*/*"; - const COMPUTE_KZG_PROOF_TESTS: &str = "../../tests/compute_kzg_proof/*/*/*"; - const COMPUTE_BLOB_KZG_PROOF_TESTS: &str = "../../tests/compute_blob_kzg_proof/*/*/*"; - const VERIFY_KZG_PROOF_TESTS: &str = "../../tests/verify_kzg_proof/*/*/*"; - const VERIFY_BLOB_KZG_PROOF_TESTS: &str = "../../tests/verify_blob_kzg_proof/*/*/*"; - const VERIFY_BLOB_KZG_PROOF_BATCH_TESTS: &str = "../../tests/verify_blob_kzg_proof_batch/*/*/*"; - - #[cfg(not(feature = "minimal-spec"))] - #[test] - fn test_blob_to_kzg_commitment() { - let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); - assert!(trusted_setup_file.exists()); - let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); - let test_files: Vec = glob::glob(BLOB_TO_KZG_COMMITMENT_TESTS) - .unwrap() - .map(Result::unwrap) - .collect(); - assert!(!test_files.is_empty()); - - for test_file in test_files { - let yaml_data = fs::read_to_string(test_file).unwrap(); - let test: blob_to_kzg_commitment_test::Test = serde_yaml::from_str(&yaml_data).unwrap(); - let Ok(blob) = test.input.get_blob() else { - assert!(test.get_output().is_none()); - continue; - }; - - match KZGCommitment::blob_to_kzg_commitment(blob, &kzg_settings) { - Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes), - _ => assert!(test.get_output().is_none()), - } - } - } - - #[cfg(not(feature = "minimal-spec"))] - #[test] - fn test_compute_kzg_proof() { - let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); - assert!(trusted_setup_file.exists()); - let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); - let test_files: Vec = glob::glob(COMPUTE_KZG_PROOF_TESTS) - .unwrap() - .map(Result::unwrap) - .collect(); - assert!(!test_files.is_empty()); - - for test_file in test_files { - let yaml_data = fs::read_to_string(test_file).unwrap(); - let test: compute_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); - let (Ok(blob), Ok(z)) = (test.input.get_blob(), test.input.get_z()) else { - assert!(test.get_output().is_none()); - continue; - }; - - match KZGProof::compute_kzg_proof(blob, z, &kzg_settings) { - Ok((proof, y)) => { - assert_eq!(proof.bytes, test.get_output().unwrap().0.bytes); - assert_eq!(y.bytes, test.get_output().unwrap().1.bytes); - } - _ => assert!(test.get_output().is_none()), - } - } - } - - #[cfg(not(feature = "minimal-spec"))] - #[test] - fn test_compute_blob_kzg_proof() { - let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); - assert!(trusted_setup_file.exists()); - let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); - let test_files: Vec = glob::glob(COMPUTE_BLOB_KZG_PROOF_TESTS) - .unwrap() - .map(Result::unwrap) - .collect(); - assert!(!test_files.is_empty()); - - for test_file in test_files { - let yaml_data = fs::read_to_string(test_file).unwrap(); - let test: compute_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); - let (Ok(blob), Ok(commitment)) = ( - test.input.get_blob(), - test.input.get_commitment() - ) else { - assert!(test.get_output().is_none()); - continue; - }; - - match KZGProof::compute_blob_kzg_proof(blob, commitment, &kzg_settings) { - Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes), - _ => assert!(test.get_output().is_none()), - } - } - } - - #[cfg(not(feature = "minimal-spec"))] - #[test] - fn test_verify_kzg_proof() { - let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); - assert!(trusted_setup_file.exists()); - let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); - let test_files: Vec = glob::glob(VERIFY_KZG_PROOF_TESTS) - .unwrap() - .map(Result::unwrap) - .collect(); - assert!(!test_files.is_empty()); - - for test_file in test_files { - let yaml_data = fs::read_to_string(test_file).unwrap(); - let test: verify_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); - let (Ok(commitment), Ok(z), Ok(y), Ok(proof)) = ( - test.input.get_commitment(), - test.input.get_z(), - test.input.get_y(), - test.input.get_proof() - ) else { - assert!(test.get_output().is_none()); - continue; - }; - - match KZGProof::verify_kzg_proof(commitment, z, y, proof, &kzg_settings) { - Ok(res) => assert_eq!(res, test.get_output().unwrap()), - _ => assert!(test.get_output().is_none()), - } - } - } - - #[cfg(not(feature = "minimal-spec"))] - #[test] - fn test_verify_blob_kzg_proof() { - let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); - assert!(trusted_setup_file.exists()); - let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); - let test_files: Vec = glob::glob(VERIFY_BLOB_KZG_PROOF_TESTS) - .unwrap() - .map(Result::unwrap) - .collect(); - assert!(!test_files.is_empty()); - - for test_file in test_files { - let yaml_data = fs::read_to_string(test_file).unwrap(); - let test: verify_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap(); - let (Ok(blob), Ok(commitment), Ok(proof)) = ( - test.input.get_blob(), - test.input.get_commitment(), - test.input.get_proof() - ) else { - assert!(test.get_output().is_none()); - continue; - }; - - match KZGProof::verify_blob_kzg_proof(blob, commitment, proof, &kzg_settings) { - Ok(res) => assert_eq!(res, test.get_output().unwrap()), - _ => assert!(test.get_output().is_none()), - } - } - } - - #[cfg(not(feature = "minimal-spec"))] - #[test] - fn test_verify_blob_kzg_proof_batch() { - let trusted_setup_file = PathBuf::from("../../src/trusted_setup.txt"); - assert!(trusted_setup_file.exists()); - let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap(); - let test_files: Vec = glob::glob(VERIFY_BLOB_KZG_PROOF_BATCH_TESTS) - .unwrap() - .map(Result::unwrap) - .collect(); - assert!(!test_files.is_empty()); - - for test_file in test_files { - let yaml_data = fs::read_to_string(test_file).unwrap(); - let test: verify_blob_kzg_proof_batch::Test = serde_yaml::from_str(&yaml_data).unwrap(); - let (Ok(blobs), Ok(commitments), Ok(proofs)) = ( - test.input.get_blobs(), - test.input.get_commitments(), - test.input.get_proofs() - ) else { - assert!(test.get_output().is_none()); - continue; - }; - - match KZGProof::verify_blob_kzg_proof_batch( - &blobs, - &commitments, - &proofs, - &kzg_settings, - ) { - Ok(res) => assert_eq!(res, test.get_output().unwrap()), - _ => assert!(test.get_output().is_none()), - } - } - } -} +mod bindings; + +// Expose relevant types with idiomatic names. +pub use bindings::{ + KZGCommitment as KzgCommitment, KZGProof as KzgProof, KZGSettings as KzgSettings, + C_KZG_RET as CkzgError, +}; +// Expose the constants. +pub use bindings::{ + BYTES_PER_BLOB, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, BYTES_PER_PROOF, + FIELD_ELEMENTS_PER_BLOB, +}; +// Expose the remaining relevant types. +pub use bindings::{Blob, Bytes32, Bytes48, Error, FFTSettings};