Reproducible rust bindings (#243)

* modify directory structure to isolate the generated bindings

* add a lib.rs file

* move deref impls to the extension file

* remove unused types from generated bindings

* cleanup new lines to reduce diff noise

* reorder definitions to reduce diff noise

* move sync and send impls to the extension file

* generate bindings

* blacklist used bindings; create snapshots dir to ensure freshness in the future

* fix typo. Ty @pawanjay176

* run cargo build after merge

* custom impl for KZGCommitment and KZGProof

* final touches
This commit is contained in:
Divma 2023-03-27 09:47:24 -05:00 committed by GitHub
parent 88924c8aa7
commit f384175810
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1218 additions and 1023 deletions

View File

@ -1,2 +1,2 @@
src/consts.rs
src/bindings/generated.rs
target/

265
bindings/rust/Cargo.lock generated
View File

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

View File

@ -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
harness = false

View File

@ -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<P>(
field_elements_per_blob: usize,
root_dir: &Path,
bindings_out_path: P,
snapshot_path: P,
) where
P: AsRef<std::path::Path>,
{
use bindgen::Builder;
#[derive(Debug)]
struct Callbacks;
impl bindgen::callbacks::ParseCallbacks for Callbacks {
fn int_macro(&self, name: &str, _value: i64) -> Option<bindgen::callbacks::IntKind> {
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");
}

View File

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

View File

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

View File

@ -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<Self, Error> {
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::<KZGSettings>::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<Self, Error> {
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::<KZGSettings>::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<Self, Error> {
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, Error> {
Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap())
}
}
impl Bytes32 {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
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, Error> {
Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap())
}
}
impl Bytes48 {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
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, Error> {
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<Self, Error> {
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::<KZGProof>::uninit();
let mut y_out = MaybeUninit::<Bytes32>::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<Self, Error> {
let mut kzg_proof = MaybeUninit::<KZGProof>::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<bool, Error> {
let mut verified: MaybeUninit<bool> = 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<bool, Error> {
let mut verified: MaybeUninit<bool> = 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<bool, Error> {
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<bool> = 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<Self, Error> {
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<Self, Error> {
let mut kzg_commitment: MaybeUninit<KZGCommitment> = 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<Blob> = (0..num_blobs)
.map(|_| generate_random_blob(&mut rng))
.collect();
let commitments: Vec<Bytes48> = blobs
.iter()
.map(|blob| KZGCommitment::blob_to_kzg_commitment(blob.clone(), &kzg_settings).unwrap())
.map(|commitment| commitment.to_bytes())
.collect();
let proofs: Vec<Bytes48> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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()),
}
}
}
}

View File

@ -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<Self, Error> {
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::<KZGSettings>::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<Self, Error> {
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::<KZGSettings>::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<Self, Error> {
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, Error> {
Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap())
}
}
impl Bytes32 {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
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, Error> {
Self::from_bytes(&hex::decode(&hex_str[2..]).unwrap())
}
}
impl Bytes48 {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
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, Error> {
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<Self, Error> {
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::<KZGProof>::uninit();
let mut y_out = MaybeUninit::<Bytes32>::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<Self, Error> {
let mut kzg_proof = MaybeUninit::<KZGProof>::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<bool, Error> {
let mut verified: MaybeUninit<bool> = 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<bool, Error> {
let mut verified: MaybeUninit<bool> = 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<bool, Error> {
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<bool> = 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<Self, Error> {
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<Self, Error> {
let mut kzg_commitment: MaybeUninit<KZGCommitment> = 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<Blob> = (0..num_blobs)
.map(|_| generate_random_blob(&mut rng))
.collect();
let commitments: Vec<Bytes48> = blobs
.iter()
.map(|blob| KZGCommitment::blob_to_kzg_commitment(*blob, &kzg_settings).unwrap())
.map(|commitment| commitment.to_bytes())
.collect();
let proofs: Vec<Bytes48> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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<PathBuf> = 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};