From e39f156fff2b2e7be64b075ad5e794585887569d Mon Sep 17 00:00:00 2001 From: Aleksei Vambol <77882392+AlekseiVambol@users.noreply.github.com> Date: Wed, 25 Dec 2024 12:38:35 +0200 Subject: [PATCH] Replace the ark-zkey witness calculator with the one of iden3 (#273) * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Delete rln/resources/tree_height_20/rln.wasm * Changes in accordance with the results of the first round of reviewing. * Formatting * Acknowledgements and a readme fix * Minor change: Vec memory allocation --- Cargo.lock | 324 +++++++-- README.md | 2 + rln-cli/Cargo.toml | 3 + rln-cli/src/main.rs | 27 +- rln/Cargo.toml | 3 + rln/Makefile.toml | 2 +- rln/README.md | 14 +- rln/resources/tree_height_20/graph.bin | Bin 0 -> 280250 bytes rln/src/circuit.rs | 34 +- rln/src/ffi.rs | 9 +- rln/src/iden3calc.rs | 73 ++ rln/src/iden3calc/graph.rs | 947 +++++++++++++++++++++++++ rln/src/iden3calc/proto.rs | 117 +++ rln/src/iden3calc/storage.rs | 496 +++++++++++++ rln/src/lib.rs | 1 + rln/src/protocol.rs | 46 +- rln/src/public.rs | 53 +- rln/tests/ffi.rs | 11 - rln/tests/protocol.rs | 8 +- 19 files changed, 1981 insertions(+), 189 deletions(-) create mode 100644 rln/resources/tree_height_20/graph.bin create mode 100644 rln/src/iden3calc.rs create mode 100644 rln/src/iden3calc/graph.rs create mode 100644 rln/src/iden3calc/proto.rs create mode 100644 rln/src/iden3calc/storage.rs diff --git a/Cargo.lock b/Cargo.lock index 6301484..44516b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,6 +48,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-rlp" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097" +dependencies = [ + "arrayvec", + "bytes", +] + [[package]] name = "anes" version = "0.1.6" @@ -103,6 +113,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "anyhow" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" + [[package]] name = "ark-bn254" version = "0.4.0" @@ -110,8 +126,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" dependencies = [ "ark-ec", - "ark-ff", - "ark-std", + "ark-ff 0.4.1", + "ark-std 0.4.0", ] [[package]] @@ -123,12 +139,12 @@ dependencies = [ "ark-bn254", "ark-crypto-primitives", "ark-ec", - "ark-ff", + "ark-ff 0.4.1", "ark-groth16", "ark-poly", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.1", + "ark-std 0.4.0", "byteorder", "cfg-if", "color-eyre", @@ -150,14 +166,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.4.1", "ark-relations", - "ark-serialize", + "ark-serialize 0.4.1", "ark-snark", - "ark-std", + "ark-std 0.4.0", "blake2", "derivative", - "digest", + "digest 0.10.6", "rayon", "sha2", ] @@ -168,10 +184,10 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c60370a92f8e1a5f053cad73a862e1b99bc642333cd676fa11c0c39f80f4ac2" dependencies = [ - "ark-ff", + "ark-ff 0.4.1", "ark-poly", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.1", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", "itertools", @@ -180,27 +196,55 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c2d42532524bee1da5a4f6f733eb4907301baa480829557adcff5dfaeee1d9a" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.1", + "ark-std 0.4.0", "derivative", - "digest", + "digest 0.10.6", "itertools", "num-bigint", "num-traits", "paste", "rayon", - "rustc_version", + "rustc_version 0.4.0", "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.4.2" @@ -211,6 +255,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.4.2" @@ -232,11 +288,11 @@ checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" dependencies = [ "ark-crypto-primitives", "ark-ec", - "ark-ff", + "ark-ff 0.4.1", "ark-poly", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.1", + "ark-std 0.4.0", "rayon", ] @@ -246,9 +302,9 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f6ec811462cabe265cfe1b102fcfe3df79d7d2929c2425673648ee9abfd0272" dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.4.1", + "ark-serialize 0.4.1", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", "rayon", @@ -260,12 +316,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" dependencies = [ - "ark-ff", - "ark-std", + "ark-ff 0.4.1", + "ark-std 0.4.0", "tracing", "tracing-subscriber 0.2.25", ] +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + [[package]] name = "ark-serialize" version = "0.4.1" @@ -273,8 +339,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7e735959bc173ea4baf13327b19c22d452b8e9e8e8f7b7fc34e6bf0e316c33e" dependencies = [ "ark-serialize-derive", - "ark-std", - "digest", + "ark-std 0.4.0", + "digest 0.10.6", "num-bigint", ] @@ -295,10 +361,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" dependencies = [ - "ark-ff", + "ark-ff 0.4.1", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.1", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", ] [[package]] @@ -321,10 +397,10 @@ dependencies = [ "ark-bn254", "ark-circom", "ark-ec", - "ark-ff", + "ark-ff 0.4.1", "ark-groth16", "ark-relations", - "ark-serialize", + "ark-serialize 0.4.1", "color-eyre", "flame", "flamer", @@ -422,7 +498,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.6", ] [[package]] @@ -981,6 +1057,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.6" @@ -1009,7 +1094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" dependencies = [ "der", - "digest", + "digest 0.10.6", "elliptic-curve", "rfc6979", "signature", @@ -1030,7 +1115,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.6", "ff", "generic-array", "group", @@ -1206,6 +1291,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.0" @@ -1422,7 +1518,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.6", ] [[package]] @@ -1965,6 +2061,17 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -2082,6 +2189,45 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +dependencies = [ + "bitflags 2.4.1", + "lazy_static 1.4.0", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.8.5", + "unarray", +] + +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.16", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -2147,6 +2293,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + [[package]] name = "rayon" version = "1.7.0" @@ -2212,7 +2367,7 @@ checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -2221,6 +2376,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "region" version = "3.0.0" @@ -2287,12 +2448,13 @@ dependencies = [ "ark-bn254", "ark-circom", "ark-ec", - "ark-ff", + "ark-ff 0.4.1", "ark-groth16", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.1", + "ark-std 0.4.0", "ark-zkey", + "byteorder", "cfg-if", "color-eyre", "criterion 0.4.0", @@ -2301,8 +2463,10 @@ dependencies = [ "num-bigint", "num-traits", "once_cell", + "prost", "rand", "rand_chacha", + "ruint", "serde", "serde_json", "sled", @@ -2365,6 +2529,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.1", + "bytes", + "fastrlp", + "num-bigint", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2383,13 +2576,22 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.17", ] [[package]] @@ -2496,12 +2698,30 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.163" @@ -2571,7 +2791,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.6", ] [[package]] @@ -2580,7 +2800,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.6", "keccak", ] @@ -2599,7 +2819,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ - "digest", + "digest 0.10.6", "rand_core", ] @@ -2922,6 +3142,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uint" version = "0.9.5" @@ -2934,6 +3160,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.8" @@ -3562,7 +3794,7 @@ name = "zerokit_utils" version = "0.5.1" dependencies = [ "ark-bn254", - "ark-ff", + "ark-ff 0.4.1", "color-eyre", "criterion 0.4.0", "hex", diff --git a/README.md b/README.md index 1b74561..b6941f8 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ in Rust. - [semaphore-rs](https://github.com/worldcoin/semaphore-rs) written in Rust based on ark-circom. +- The circom witness calculation code of the rln crate is based on [circom-witnesscalc](https://github.com/iden3/circom-witnesscalc) by iden3. The execution graph file used by this code has been generated by means of the same iden3 software. + ## Users Zerokit is used by - diff --git a/rln-cli/Cargo.toml b/rln-cli/Cargo.toml index 86f168e..9d1db53 100644 --- a/rln-cli/Cargo.toml +++ b/rln-cli/Cargo.toml @@ -11,3 +11,6 @@ color-eyre = "=0.6.2" # serialization serde_json = "1.0.48" serde = { version = "1.0.130", features = ["derive"] } + +[features] +arkzkey = [] diff --git a/rln-cli/src/main.rs b/rln-cli/src/main.rs index c8931ff..dd7563e 100644 --- a/rln-cli/src/main.rs +++ b/rln-cli/src/main.rs @@ -27,7 +27,7 @@ fn main() -> Result<()> { tree_height, config, }) => { - let resources = File::open(&config)?; + let resources = File::open(config)?; state.rln = Some(RLN::new(*tree_height, resources)?); Ok(()) } @@ -38,9 +38,9 @@ fn main() -> Result<()> { }) => { let mut resources: Vec> = Vec::new(); #[cfg(feature = "arkzkey")] - let filenames = ["rln.wasm", "rln_final.arkzkey", "verification_key.arkvkey"]; + let filenames = ["rln_final.arkzkey", "verification_key.arkvkey"]; #[cfg(not(feature = "arkzkey"))] - let filenames = ["rln.wasm", "rln_final.zkey", "verification_key.arkvkey"]; + let filenames = ["rln_final.zkey", "verification_key.arkvkey"]; for filename in filenames { let fullpath = config.join(Path::new(filename)); let mut file = File::open(&fullpath)?; @@ -49,12 +49,11 @@ fn main() -> Result<()> { file.read_exact(&mut buffer)?; resources.push(buffer); } - let tree_config_input_file = File::open(&tree_config_input)?; + let tree_config_input_file = File::open(tree_config_input)?; state.rln = Some(RLN::new_with_params( *tree_height, resources[0].clone(), resources[1].clone(), - resources[2].clone(), tree_config_input_file, )?); Ok(()) @@ -67,7 +66,7 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::SetLeaf { index, file }) => { - let input_data = File::open(&file)?; + let input_data = File::open(file)?; state .rln .ok_or(Report::msg("no RLN instance initialized"))? @@ -75,7 +74,7 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::SetMultipleLeaves { index, file }) => { - let input_data = File::open(&file)?; + let input_data = File::open(file)?; state .rln .ok_or(Report::msg("no RLN instance initialized"))? @@ -83,7 +82,7 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::ResetMultipleLeaves { file }) => { - let input_data = File::open(&file)?; + let input_data = File::open(file)?; state .rln .ok_or(Report::msg("no RLN instance initialized"))? @@ -91,7 +90,7 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::SetNextLeaf { file }) => { - let input_data = File::open(&file)?; + let input_data = File::open(file)?; state .rln .ok_or(Report::msg("no RLN instance initialized"))? @@ -122,7 +121,7 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::Prove { input }) => { - let input_data = File::open(&input)?; + let input_data = File::open(input)?; let writer = std::io::stdout(); state .rln @@ -131,7 +130,7 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::Verify { file }) => { - let input_data = File::open(&file)?; + let input_data = File::open(file)?; state .rln .ok_or(Report::msg("no RLN instance initialized"))? @@ -139,7 +138,7 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::GenerateProof { input }) => { - let input_data = File::open(&input)?; + let input_data = File::open(input)?; let writer = std::io::stdout(); state .rln @@ -148,8 +147,8 @@ fn main() -> Result<()> { Ok(()) } Some(Commands::VerifyWithRoots { input, roots }) => { - let input_data = File::open(&input)?; - let roots_data = File::open(&roots)?; + let input_data = File::open(input)?; + let roots_data = File::open(roots)?; state .rln .ok_or(Report::msg("no RLN instance initialized"))? diff --git a/rln/Cargo.toml b/rln/Cargo.toml index c24461f..6288aca 100644 --- a/rln/Cargo.toml +++ b/rln/Cargo.toml @@ -42,6 +42,7 @@ color-eyre = "=0.6.2" thiserror = "=1.0.39" # utilities +byteorder = "1.4.3" cfg-if = "=1.0" num-bigint = { version = "=0.4.3", default-features = false, features = [ "rand", @@ -51,11 +52,13 @@ once_cell = "=1.17.1" lazy_static = "=1.4.0" rand = "=0.8.5" rand_chacha = "=0.3.1" +ruint = { version = "1.10.0", features = ["rand", "serde", "ark-ff-04", "num-bigint"] } tiny-keccak = { version = "=2.0.2", features = ["keccak"] } utils = { package = "zerokit_utils", version = "=0.5.1", path = "../utils/", default-features = false } # serialization +prost = "0.13.1" serde_json = "=1.0.96" serde = { version = "=1.0.163", features = ["derive"] } diff --git a/rln/Makefile.toml b/rln/Makefile.toml index 9c7aba1..e353c14 100644 --- a/rln/Makefile.toml +++ b/rln/Makefile.toml @@ -4,7 +4,7 @@ args = ["build", "--release"] [tasks.test_default] command = "cargo" -args = ["test", "--release"] +args = ["test", "--release", "--", "--nocapture"] [tasks.test_stateless] command = "cargo" diff --git a/rln/README.md b/rln/README.md index ae38ee4..77aaf37 100644 --- a/rln/README.md +++ b/rln/README.md @@ -12,14 +12,18 @@ git clone https://github.com/vacp2p/zerokit.git cd zerokit/rln ``` -### Build and Test + ### Build and Test -To build and test, run the following commands within the module folder + To build and test, run the following commands within the module folder -```bash -cargo make build -cargo make test +``` bash + cargo make build + cargo make test_{mode} ``` +The {mode} placeholder should be replaced with +* **default** for the default tests; +* **arkzkey** for the tests with the arkzkey feature; +* **stateless** for the tests with the stateless feature. ### Compile ZK circuits diff --git a/rln/resources/tree_height_20/graph.bin b/rln/resources/tree_height_20/graph.bin new file mode 100644 index 0000000000000000000000000000000000000000..1e335b7eb6e1b84f9da3d69d5cd95a999f1b43b0 GIT binary patch literal 280250 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 = vk_from_ark_serialized(VK_BYTES).expect("Failed to read vk"); - - #[cfg(not(target_arch = "wasm32"))] - static ref WITNESS_CALCULATOR: Arc> = { - circom_from_raw(WASM_BYTES).expect("Failed to create witness calculator") - }; } pub const TEST_TREE_HEIGHT: usize = 20; @@ -92,6 +84,10 @@ pub fn zkey_from_raw(zkey_data: &[u8]) -> Result<(ProvingKey, ConstraintM Ok(proving_key_and_matrices) } +pub fn calculate_rln_witness)>>(inputs: I) -> Vec { + calc_witness(inputs, GRAPH_BYTES) +} + // Loads the proving key #[cfg(not(target_arch = "wasm32"))] pub fn zkey_from_folder() -> &'static (ProvingKey, ConstraintMatrices) { @@ -118,20 +114,6 @@ pub fn vk_from_folder() -> &'static VerifyingKey { &VK } -// Initializes the witness calculator using a bytes vector -#[cfg(not(target_arch = "wasm32"))] -pub fn circom_from_raw(wasm_buffer: &[u8]) -> Result>> { - let module = Module::new(&Store::default(), wasm_buffer)?; - let result = WitnessCalculator::from_module(module)?; - Ok(Arc::new(Mutex::new(result))) -} - -// Initializes the witness calculator -#[cfg(not(target_arch = "wasm32"))] -pub fn circom_from_folder() -> &'static Arc> { - &WITNESS_CALCULATOR -} - // Computes the verification key from a bytes vector containing pre-processed ark-serialized verification key // uncompressed, unchecked pub fn vk_from_ark_serialized(data: &[u8]) -> Result> { diff --git a/rln/src/ffi.rs b/rln/src/ffi.rs index 0d2cd70..da1660a 100644 --- a/rln/src/ffi.rs +++ b/rln/src/ffi.rs @@ -228,7 +228,6 @@ pub extern "C" fn new(ctx: *mut *mut RLN) -> bool { #[no_mangle] pub extern "C" fn new_with_params( tree_height: usize, - circom_buffer: *const Buffer, zkey_buffer: *const Buffer, vk_buffer: *const Buffer, tree_config: *const Buffer, @@ -236,7 +235,6 @@ pub extern "C" fn new_with_params( ) -> bool { match RLN::new_with_params( tree_height, - circom_buffer.process().to_vec(), zkey_buffer.process().to_vec(), vk_buffer.process().to_vec(), tree_config.process(), @@ -256,16 +254,11 @@ pub extern "C" fn new_with_params( #[cfg(feature = "stateless")] #[no_mangle] pub extern "C" fn new_with_params( - circom_buffer: *const Buffer, zkey_buffer: *const Buffer, vk_buffer: *const Buffer, ctx: *mut *mut RLN, ) -> bool { - match RLN::new_with_params( - circom_buffer.process().to_vec(), - zkey_buffer.process().to_vec(), - vk_buffer.process().to_vec(), - ) { + match RLN::new_with_params(zkey_buffer.process().to_vec(), vk_buffer.process().to_vec()) { Ok(rln) => { unsafe { *ctx = Box::into_raw(Box::new(rln)) }; true diff --git a/rln/src/iden3calc.rs b/rln/src/iden3calc.rs new file mode 100644 index 0000000..056582d --- /dev/null +++ b/rln/src/iden3calc.rs @@ -0,0 +1,73 @@ +// This file is based on the code by iden3. Its preimage can be found here: +// https://github.com/iden3/circom-witnesscalc/blob/5cb365b6e4d9052ecc69d4567fcf5bc061c20e94/src/lib.rs + +pub mod graph; +pub mod proto; +pub mod storage; + +use ark_bn254::Fr; +use graph::Node; +use num_bigint::BigInt; +use ruint::aliases::U256; +use std::collections::HashMap; +use storage::deserialize_witnesscalc_graph; + +pub type InputSignalsInfo = HashMap; + +pub fn calc_witness)>>( + inputs: I, + graph_data: &[u8], +) -> Vec { + let inputs: HashMap> = inputs + .into_iter() + .map(|(key, value)| (key, value.iter().map(|v| U256::from(v)).collect())) + .collect(); + + let (nodes, signals, input_mapping): (Vec, Vec, InputSignalsInfo) = + deserialize_witnesscalc_graph(std::io::Cursor::new(graph_data)).unwrap(); + + let mut inputs_buffer = get_inputs_buffer(get_inputs_size(&nodes)); + populate_inputs(&inputs, &input_mapping, &mut inputs_buffer); + + graph::evaluate(&nodes, inputs_buffer.as_slice(), &signals) +} + +fn get_inputs_size(nodes: &[Node]) -> usize { + let mut start = false; + let mut max_index = 0usize; + for &node in nodes.iter() { + if let Node::Input(i) = node { + if i > max_index { + max_index = i; + } + start = true + } else if start { + break; + } + } + max_index + 1 +} + +fn populate_inputs( + input_list: &HashMap>, + inputs_info: &InputSignalsInfo, + input_buffer: &mut [U256], +) { + for (key, value) in input_list { + let (offset, len) = inputs_info[key]; + if len != value.len() { + panic!("Invalid input length for {}", key); + } + + for (i, v) in value.iter().enumerate() { + input_buffer[offset + i] = *v; + } + } +} + +/// Allocates inputs vec with position 0 set to 1 +fn get_inputs_buffer(size: usize) -> Vec { + let mut inputs = vec![U256::ZERO; size]; + inputs[0] = U256::from(1); + inputs +} diff --git a/rln/src/iden3calc/graph.rs b/rln/src/iden3calc/graph.rs new file mode 100644 index 0000000..ab66b8e --- /dev/null +++ b/rln/src/iden3calc/graph.rs @@ -0,0 +1,947 @@ +// This file is based on the code by iden3. Its preimage can be found here: +// https://github.com/iden3/circom-witnesscalc/blob/5cb365b6e4d9052ecc69d4567fcf5bc061c20e94/src/graph.rs + +use crate::iden3calc::proto; +use ark_bn254::Fr; +use ark_ff::{BigInt, BigInteger, One, PrimeField, Zero}; +use rand::Rng; +use ruint::aliases::U256; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::error::Error; +use std::ops::{BitOr, BitXor, Deref}; +use std::{ + collections::HashMap, + ops::{BitAnd, Shl, Shr}, +}; + +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate}; +use ruint::uint; + +pub const M: U256 = + uint!(21888242871839275222246405745257275088548364400416034343698204186575808495617_U256); + +fn ark_se(a: &A, s: S) -> Result +where + S: serde::Serializer, +{ + let mut bytes = vec![]; + a.serialize_with_mode(&mut bytes, Compress::Yes) + .map_err(serde::ser::Error::custom)?; + s.serialize_bytes(&bytes) +} + +fn ark_de<'de, D, A: CanonicalDeserialize>(data: D) -> Result +where + D: serde::de::Deserializer<'de>, +{ + let s: Vec = serde::de::Deserialize::deserialize(data)?; + let a = A::deserialize_with_mode(s.as_slice(), Compress::Yes, Validate::Yes); + a.map_err(serde::de::Error::custom) +} + +#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)] +pub enum Operation { + Mul, + Div, + Add, + Sub, + Pow, + Idiv, + Mod, + Eq, + Neq, + Lt, + Gt, + Leq, + Geq, + Land, + Lor, + Shl, + Shr, + Bor, + Band, + Bxor, +} + +impl Operation { + // TODO: rewrite to &U256 type + pub fn eval(&self, a: U256, b: U256) -> U256 { + use Operation::*; + match self { + Mul => a.mul_mod(b, M), + Div => { + if b == U256::ZERO { + // as we are simulating a circuit execution with signals + // values all equal to 0, just return 0 here in case of + // division by zero + U256::ZERO + } else { + a.mul_mod(b.inv_mod(M).unwrap(), M) + } + } + Add => a.add_mod(b, M), + Sub => a.add_mod(M - b, M), + Pow => a.pow_mod(b, M), + Mod => a.div_rem(b).1, + Eq => U256::from(a == b), + Neq => U256::from(a != b), + Lt => u_lt(&a, &b), + Gt => u_gt(&a, &b), + Leq => u_lte(&a, &b), + Geq => u_gte(&a, &b), + Land => U256::from(a != U256::ZERO && b != U256::ZERO), + Lor => U256::from(a != U256::ZERO || b != U256::ZERO), + Shl => compute_shl_uint(a, b), + Shr => compute_shr_uint(a, b), + // TODO test with conner case when it is possible to get the number + // bigger then modulus + Bor => a.bitor(b), + Band => a.bitand(b), + // TODO test with conner case when it is possible to get the number + // bigger then modulus + Bxor => a.bitxor(b), + Idiv => a / b, + } + } + + pub fn eval_fr(&self, a: Fr, b: Fr) -> Fr { + use Operation::*; + match self { + Mul => a * b, + // We always should return something on the circuit execution. + // So in case of division by 0 we would return 0. And the proof + // should be invalid in the end. + Div => { + if b.is_zero() { + Fr::zero() + } else { + a / b + } + } + Add => a + b, + Sub => a - b, + Idiv => { + if b.is_zero() { + Fr::zero() + } else { + Fr::new((Into::::into(a) / Into::::into(b)).into()) + } + } + Mod => { + if b.is_zero() { + Fr::zero() + } else { + Fr::new((Into::::into(a) % Into::::into(b)).into()) + } + } + Eq => match a.cmp(&b) { + Ordering::Equal => Fr::one(), + _ => Fr::zero(), + }, + Neq => match a.cmp(&b) { + Ordering::Equal => Fr::zero(), + _ => Fr::one(), + }, + Lt => Fr::new(u_lt(&a.into(), &b.into()).into()), + Gt => Fr::new(u_gt(&a.into(), &b.into()).into()), + Leq => Fr::new(u_lte(&a.into(), &b.into()).into()), + Geq => Fr::new(u_gte(&a.into(), &b.into()).into()), + Land => { + if a.is_zero() || b.is_zero() { + Fr::zero() + } else { + Fr::one() + } + } + Lor => { + if a.is_zero() && b.is_zero() { + Fr::zero() + } else { + Fr::one() + } + } + Shl => shl(a, b), + Shr => shr(a, b), + Bor => bit_or(a, b), + Band => bit_and(a, b), + Bxor => bit_xor(a, b), + // TODO implement other operators + _ => unimplemented!("operator {:?} not implemented for Montgomery", self), + } + } +} + +impl From<&Operation> for proto::DuoOp { + fn from(v: &Operation) -> Self { + match v { + Operation::Mul => proto::DuoOp::Mul, + Operation::Div => proto::DuoOp::Div, + Operation::Add => proto::DuoOp::Add, + Operation::Sub => proto::DuoOp::Sub, + Operation::Pow => proto::DuoOp::Pow, + Operation::Idiv => proto::DuoOp::Idiv, + Operation::Mod => proto::DuoOp::Mod, + Operation::Eq => proto::DuoOp::Eq, + Operation::Neq => proto::DuoOp::Neq, + Operation::Lt => proto::DuoOp::Lt, + Operation::Gt => proto::DuoOp::Gt, + Operation::Leq => proto::DuoOp::Leq, + Operation::Geq => proto::DuoOp::Geq, + Operation::Land => proto::DuoOp::Land, + Operation::Lor => proto::DuoOp::Lor, + Operation::Shl => proto::DuoOp::Shl, + Operation::Shr => proto::DuoOp::Shr, + Operation::Bor => proto::DuoOp::Bor, + Operation::Band => proto::DuoOp::Band, + Operation::Bxor => proto::DuoOp::Bxor, + } + } +} + +#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)] +pub enum UnoOperation { + Neg, + Id, // identity - just return self +} + +impl UnoOperation { + pub fn eval(&self, a: U256) -> U256 { + match self { + UnoOperation::Neg => { + if a == U256::ZERO { + U256::ZERO + } else { + M - a + } + } + UnoOperation::Id => a, + } + } + + pub fn eval_fr(&self, a: Fr) -> Fr { + match self { + UnoOperation::Neg => { + if a.is_zero() { + Fr::zero() + } else { + let mut x = Fr::MODULUS; + x.sub_with_borrow(&a.into_bigint()); + Fr::from_bigint(x).unwrap() + } + } + _ => unimplemented!("uno operator {:?} not implemented for Montgomery", self), + } + } +} + +impl From<&UnoOperation> for proto::UnoOp { + fn from(v: &UnoOperation) -> Self { + match v { + UnoOperation::Neg => proto::UnoOp::Neg, + UnoOperation::Id => proto::UnoOp::Id, + } + } +} + +#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)] +pub enum TresOperation { + TernCond, +} + +impl TresOperation { + pub fn eval(&self, a: U256, b: U256, c: U256) -> U256 { + match self { + TresOperation::TernCond => { + if a == U256::ZERO { + c + } else { + b + } + } + } + } + + pub fn eval_fr(&self, a: Fr, b: Fr, c: Fr) -> Fr { + match self { + TresOperation::TernCond => { + if a.is_zero() { + c + } else { + b + } + } + } + } +} + +impl From<&TresOperation> for proto::TresOp { + fn from(v: &TresOperation) -> Self { + match v { + TresOperation::TernCond => proto::TresOp::TernCond, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum Node { + Input(usize), + Constant(U256), + #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")] + MontConstant(Fr), + UnoOp(UnoOperation, usize), + Op(Operation, usize, usize), + TresOp(TresOperation, usize, usize, usize), +} + +// TODO remove pub from Vec +#[derive(Default)] +pub struct Nodes(pub Vec); + +impl Nodes { + pub fn new() -> Self { + Nodes(Vec::new()) + } + + pub fn to_const(&self, idx: NodeIdx) -> Result { + let me = self.0.get(idx.0).ok_or(NodeConstErr::EmptyNode(idx))?; + match me { + Node::Constant(v) => Ok(*v), + Node::UnoOp(op, a) => Ok(op.eval(self.to_const(NodeIdx(*a))?)), + Node::Op(op, a, b) => { + Ok(op.eval(self.to_const(NodeIdx(*a))?, self.to_const(NodeIdx(*b))?)) + } + Node::TresOp(op, a, b, c) => Ok(op.eval( + self.to_const(NodeIdx(*a))?, + self.to_const(NodeIdx(*b))?, + self.to_const(NodeIdx(*c))?, + )), + Node::Input(_) => Err(NodeConstErr::InputSignal), + Node::MontConstant(_) => { + panic!("MontConstant should not be used here") + } + } + } + + pub fn push(&mut self, n: Node) -> NodeIdx { + self.0.push(n); + NodeIdx(self.0.len() - 1) + } + + pub fn get(&self, idx: NodeIdx) -> Option<&Node> { + self.0.get(idx.0) + } +} + +impl Deref for Nodes { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Copy, Clone)] +pub struct NodeIdx(pub usize); + +impl From for NodeIdx { + fn from(v: usize) -> Self { + NodeIdx(v) + } +} + +#[derive(Debug)] +pub enum NodeConstErr { + EmptyNode(NodeIdx), + InputSignal, +} + +impl std::fmt::Display for NodeConstErr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + NodeConstErr::EmptyNode(idx) => { + write!(f, "empty node at index {}", idx.0) + } + NodeConstErr::InputSignal => { + write!(f, "input signal is not a constant") + } + } + } +} + +impl Error for NodeConstErr {} + +fn compute_shl_uint(a: U256, b: U256) -> U256 { + debug_assert!(b.lt(&U256::from(256))); + let ls_limb = b.as_limbs()[0]; + a.shl(ls_limb as usize) +} + +fn compute_shr_uint(a: U256, b: U256) -> U256 { + debug_assert!(b.lt(&U256::from(256))); + let ls_limb = b.as_limbs()[0]; + a.shr(ls_limb as usize) +} + +/// All references must be backwards. +fn assert_valid(nodes: &[Node]) { + for (i, &node) in nodes.iter().enumerate() { + if let Node::Op(_, a, b) = node { + assert!(a < i); + assert!(b < i); + } else if let Node::UnoOp(_, a) = node { + assert!(a < i); + } else if let Node::TresOp(_, a, b, c) = node { + assert!(a < i); + assert!(b < i); + assert!(c < i); + } + } +} + +pub fn optimize(nodes: &mut Vec, outputs: &mut [usize]) { + tree_shake(nodes, outputs); + propagate(nodes); + value_numbering(nodes, outputs); + constants(nodes); + tree_shake(nodes, outputs); + montgomery_form(nodes); +} + +pub fn evaluate(nodes: &[Node], inputs: &[U256], outputs: &[usize]) -> Vec { + // assert_valid(nodes); + + // Evaluate the graph. + let mut values = Vec::with_capacity(nodes.len()); + for &node in nodes.iter() { + let value = match node { + Node::Constant(c) => Fr::new(c.into()), + Node::MontConstant(c) => c, + Node::Input(i) => Fr::new(inputs[i].into()), + Node::Op(op, a, b) => op.eval_fr(values[a], values[b]), + Node::UnoOp(op, a) => op.eval_fr(values[a]), + Node::TresOp(op, a, b, c) => op.eval_fr(values[a], values[b], values[c]), + }; + values.push(value); + } + + // Convert from Montgomery form and return the outputs. + let mut out = vec![Fr::from(0); outputs.len()]; + for i in 0..outputs.len() { + out[i] = values[outputs[i]]; + } + + out +} + +/// Constant propagation +pub fn propagate(nodes: &mut [Node]) { + assert_valid(nodes); + for i in 0..nodes.len() { + if let Node::Op(op, a, b) = nodes[i] { + if let (Node::Constant(va), Node::Constant(vb)) = (nodes[a], nodes[b]) { + nodes[i] = Node::Constant(op.eval(va, vb)); + } else if a == b { + // Not constant but equal + use Operation::*; + if let Some(c) = match op { + Eq | Leq | Geq => Some(true), + Neq | Lt | Gt => Some(false), + _ => None, + } { + nodes[i] = Node::Constant(U256::from(c)); + } + } + } else if let Node::UnoOp(op, a) = nodes[i] { + if let Node::Constant(va) = nodes[a] { + nodes[i] = Node::Constant(op.eval(va)); + } + } else if let Node::TresOp(op, a, b, c) = nodes[i] { + if let (Node::Constant(va), Node::Constant(vb), Node::Constant(vc)) = + (nodes[a], nodes[b], nodes[c]) + { + nodes[i] = Node::Constant(op.eval(va, vb, vc)); + } + } + } +} + +/// Remove unused nodes +pub fn tree_shake(nodes: &mut Vec, outputs: &mut [usize]) { + assert_valid(nodes); + + // Mark all nodes that are used. + let mut used = vec![false; nodes.len()]; + for &i in outputs.iter() { + used[i] = true; + } + + // Work backwards from end as all references are backwards. + for i in (0..nodes.len()).rev() { + if used[i] { + if let Node::Op(_, a, b) = nodes[i] { + used[a] = true; + used[b] = true; + } + if let Node::UnoOp(_, a) = nodes[i] { + used[a] = true; + } + if let Node::TresOp(_, a, b, c) = nodes[i] { + used[a] = true; + used[b] = true; + used[c] = true; + } + } + } + + // Remove unused nodes + let n = nodes.len(); + let mut retain = used.iter(); + nodes.retain(|_| *retain.next().unwrap()); + + // Renumber references. + let mut renumber = vec![None; n]; + let mut index = 0; + for (i, &used) in used.iter().enumerate() { + if used { + renumber[i] = Some(index); + index += 1; + } + } + assert_eq!(index, nodes.len()); + for (&used, renumber) in used.iter().zip(renumber.iter()) { + assert_eq!(used, renumber.is_some()); + } + + // Renumber references. + for node in nodes.iter_mut() { + if let Node::Op(_, a, b) = node { + *a = renumber[*a].unwrap(); + *b = renumber[*b].unwrap(); + } + if let Node::UnoOp(_, a) = node { + *a = renumber[*a].unwrap(); + } + if let Node::TresOp(_, a, b, c) = node { + *a = renumber[*a].unwrap(); + *b = renumber[*b].unwrap(); + *c = renumber[*c].unwrap(); + } + } + for output in outputs.iter_mut() { + *output = renumber[*output].unwrap(); + } +} + +/// Randomly evaluate the graph +fn random_eval(nodes: &mut [Node]) -> Vec { + let mut rng = rand::thread_rng(); + let mut values = Vec::with_capacity(nodes.len()); + let mut inputs = HashMap::new(); + let mut prfs = HashMap::new(); + let mut prfs_uno = HashMap::new(); + let mut prfs_tres = HashMap::new(); + for node in nodes.iter() { + use Operation::*; + let value = match node { + // Constants evaluate to themselves + Node::Constant(c) => *c, + + Node::MontConstant(_) => unimplemented!("should not be used"), + + // Algebraic Ops are evaluated directly + // Since the field is large, by Swartz-Zippel if + // two values are the same then they are likely algebraically equal. + Node::Op(op @ (Add | Sub | Mul), a, b) => op.eval(values[*a], values[*b]), + + // Input and non-algebraic ops are random functions + // TODO: https://github.com/recmo/uint/issues/95 and use .gen_range(..M) + Node::Input(i) => *inputs.entry(*i).or_insert_with(|| rng.gen::() % M), + Node::Op(op, a, b) => *prfs + .entry((*op, values[*a], values[*b])) + .or_insert_with(|| rng.gen::() % M), + Node::UnoOp(op, a) => *prfs_uno + .entry((*op, values[*a])) + .or_insert_with(|| rng.gen::() % M), + Node::TresOp(op, a, b, c) => *prfs_tres + .entry((*op, values[*a], values[*b], values[*c])) + .or_insert_with(|| rng.gen::() % M), + }; + values.push(value); + } + values +} + +/// Value numbering +pub fn value_numbering(nodes: &mut [Node], outputs: &mut [usize]) { + assert_valid(nodes); + + // Evaluate the graph in random field elements. + let values = random_eval(nodes); + + // Find all nodes with the same value. + let mut value_map = HashMap::new(); + for (i, &value) in values.iter().enumerate() { + value_map.entry(value).or_insert_with(Vec::new).push(i); + } + + // For nodes that are the same, pick the first index. + let renumber: Vec<_> = values.into_iter().map(|v| value_map[&v][0]).collect(); + + // Renumber references. + for node in nodes.iter_mut() { + if let Node::Op(_, a, b) = node { + *a = renumber[*a]; + *b = renumber[*b]; + } + if let Node::UnoOp(_, a) = node { + *a = renumber[*a]; + } + if let Node::TresOp(_, a, b, c) = node { + *a = renumber[*a]; + *b = renumber[*b]; + *c = renumber[*c]; + } + } + for output in outputs.iter_mut() { + *output = renumber[*output]; + } +} + +/// Probabilistic constant determination +pub fn constants(nodes: &mut [Node]) { + assert_valid(nodes); + + // Evaluate the graph in random field elements. + let values_a = random_eval(nodes); + let values_b = random_eval(nodes); + + // Find all nodes with the same value. + for i in 0..nodes.len() { + if let Node::Constant(_) = nodes[i] { + continue; + } + if values_a[i] == values_b[i] { + nodes[i] = Node::Constant(values_a[i]); + } + } +} + +/// Convert to Montgomery form +pub fn montgomery_form(nodes: &mut [Node]) { + for node in nodes.iter_mut() { + use Node::*; + use Operation::*; + match node { + Constant(c) => *node = MontConstant(Fr::new((*c).into())), + MontConstant(..) => (), + Input(..) => (), + Op( + Mul | Div | Add | Sub | Idiv | Mod | Eq | Neq | Lt | Gt | Leq | Geq | Land | Lor + | Shl | Shr | Bor | Band | Bxor, + .., + ) => (), + Op(op @ Pow, ..) => unimplemented!("Operators Montgomery form: {:?}", op), + UnoOp(UnoOperation::Neg, ..) => (), + UnoOp(op, ..) => unimplemented!("Uno Operators Montgomery form: {:?}", op), + TresOp(TresOperation::TernCond, ..) => (), + } + } +} + +fn shl(a: Fr, b: Fr) -> Fr { + if b.is_zero() { + return a; + } + + if b.cmp(&Fr::from(Fr::MODULUS_BIT_SIZE)).is_ge() { + return Fr::zero(); + } + + let n = b.into_bigint().0[0] as u32; + + let mut a = a.into_bigint(); + a.muln(n); + Fr::from_bigint(a).unwrap() +} + +fn shr(a: Fr, b: Fr) -> Fr { + if b.is_zero() { + return a; + } + + match b.cmp(&Fr::from(254u64)) { + Ordering::Equal => return Fr::zero(), + Ordering::Greater => return Fr::zero(), + _ => (), + }; + + let mut n = b.into_bigint().to_bytes_le()[0]; + let mut result = a.into_bigint(); + let c = result.as_mut(); + while n >= 64 { + for i in 0..3 { + c[i as usize] = c[(i + 1) as usize]; + } + c[3] = 0; + n -= 64; + } + + if n == 0 { + return Fr::from_bigint(result).unwrap(); + } + + let mask: u64 = (1 << n) - 1; + let mut carrier: u64 = c[3] & mask; + c[3] >>= n; + for i in (0..3).rev() { + let new_carrier = c[i] & mask; + c[i] = (c[i] >> n) | (carrier << (64 - n)); + carrier = new_carrier; + } + Fr::from_bigint(result).unwrap() +} + +fn bit_and(a: Fr, b: Fr) -> Fr { + let a = a.into_bigint(); + let b = b.into_bigint(); + let c: [u64; 4] = [ + a.0[0] & b.0[0], + a.0[1] & b.0[1], + a.0[2] & b.0[2], + a.0[3] & b.0[3], + ]; + let mut d: BigInt<4> = BigInt::new(c); + if d > Fr::MODULUS { + d.sub_with_borrow(&Fr::MODULUS); + } + + Fr::from_bigint(d).unwrap() +} + +fn bit_or(a: Fr, b: Fr) -> Fr { + let a = a.into_bigint(); + let b = b.into_bigint(); + let c: [u64; 4] = [ + a.0[0] | b.0[0], + a.0[1] | b.0[1], + a.0[2] | b.0[2], + a.0[3] | b.0[3], + ]; + let mut d: BigInt<4> = BigInt::new(c); + if d > Fr::MODULUS { + d.sub_with_borrow(&Fr::MODULUS); + } + + Fr::from_bigint(d).unwrap() +} + +fn bit_xor(a: Fr, b: Fr) -> Fr { + let a = a.into_bigint(); + let b = b.into_bigint(); + let c: [u64; 4] = [ + a.0[0] ^ b.0[0], + a.0[1] ^ b.0[1], + a.0[2] ^ b.0[2], + a.0[3] ^ b.0[3], + ]; + let mut d: BigInt<4> = BigInt::new(c); + if d > Fr::MODULUS { + d.sub_with_borrow(&Fr::MODULUS); + } + + Fr::from_bigint(d).unwrap() +} + +// M / 2 +const HALF_M: U256 = + uint!(10944121435919637611123202872628637544274182200208017171849102093287904247808_U256); + +fn u_gte(a: &U256, b: &U256) -> U256 { + let a_neg = &HALF_M < a; + let b_neg = &HALF_M < b; + + match (a_neg, b_neg) { + (false, false) => U256::from(a >= b), + (true, false) => uint!(0_U256), + (false, true) => uint!(1_U256), + (true, true) => U256::from(a >= b), + } +} + +fn u_lte(a: &U256, b: &U256) -> U256 { + let a_neg = &HALF_M < a; + let b_neg = &HALF_M < b; + + match (a_neg, b_neg) { + (false, false) => U256::from(a <= b), + (true, false) => uint!(1_U256), + (false, true) => uint!(0_U256), + (true, true) => U256::from(a <= b), + } +} + +fn u_gt(a: &U256, b: &U256) -> U256 { + let a_neg = &HALF_M < a; + let b_neg = &HALF_M < b; + + match (a_neg, b_neg) { + (false, false) => U256::from(a > b), + (true, false) => uint!(0_U256), + (false, true) => uint!(1_U256), + (true, true) => U256::from(a > b), + } +} + +fn u_lt(a: &U256, b: &U256) -> U256 { + let a_neg = &HALF_M < a; + let b_neg = &HALF_M < b; + + match (a_neg, b_neg) { + (false, false) => U256::from(a < b), + (true, false) => uint!(1_U256), + (false, true) => uint!(0_U256), + (true, true) => U256::from(a < b), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ruint::uint; + use std::ops::Div; + use std::str::FromStr; + + #[test] + fn test_ok() { + let a = Fr::from(4u64); + let b = Fr::from(2u64); + let c = shl(a, b); + assert_eq!(c.cmp(&Fr::from(16u64)), Ordering::Equal) + } + + #[test] + fn test_div() { + assert_eq!( + Operation::Div.eval_fr(Fr::from(2u64), Fr::from(3u64)), + Fr::from_str( + "7296080957279758407415468581752425029516121466805344781232734728858602831873" + ) + .unwrap() + ); + + assert_eq!( + Operation::Div.eval_fr(Fr::from(6u64), Fr::from(2u64)), + Fr::from_str("3").unwrap() + ); + + assert_eq!( + Operation::Div.eval_fr(Fr::from(7u64), Fr::from(2u64)), + Fr::from_str( + "10944121435919637611123202872628637544274182200208017171849102093287904247812" + ) + .unwrap() + ); + } + + #[test] + fn test_idiv() { + assert_eq!( + Operation::Idiv.eval_fr(Fr::from(2u64), Fr::from(3u64)), + Fr::from_str("0").unwrap() + ); + + assert_eq!( + Operation::Idiv.eval_fr(Fr::from(6u64), Fr::from(2u64)), + Fr::from_str("3").unwrap() + ); + + assert_eq!( + Operation::Idiv.eval_fr(Fr::from(7u64), Fr::from(2u64)), + Fr::from_str("3").unwrap() + ); + } + + #[test] + fn test_fr_mod() { + assert_eq!( + Operation::Mod.eval_fr(Fr::from(7u64), Fr::from(2u64)), + Fr::from_str("1").unwrap() + ); + + assert_eq!( + Operation::Mod.eval_fr(Fr::from(7u64), Fr::from(9u64)), + Fr::from_str("7").unwrap() + ); + } + + #[test] + fn test_u_gte() { + let result = u_gte(&uint!(10_U256), &uint!(3_U256)); + assert_eq!(result, uint!(1_U256)); + + let result = u_gte(&uint!(3_U256), &uint!(3_U256)); + assert_eq!(result, uint!(1_U256)); + + let result = u_gte(&uint!(2_U256), &uint!(3_U256)); + assert_eq!(result, uint!(0_U256)); + + // -1 >= 3 => 0 + let result = u_gte( + &uint!( + 21888242871839275222246405745257275088548364400416034343698204186575808495616_U256 + ), + &uint!(3_U256), + ); + assert_eq!(result, uint!(0_U256)); + + // -1 >= -2 => 1 + let result = u_gte( + &uint!( + 21888242871839275222246405745257275088548364400416034343698204186575808495616_U256 + ), + &uint!( + 21888242871839275222246405745257275088548364400416034343698204186575808495615_U256 + ), + ); + assert_eq!(result, uint!(1_U256)); + + // -2 >= -1 => 0 + let result = u_gte( + &uint!( + 21888242871839275222246405745257275088548364400416034343698204186575808495615_U256 + ), + &uint!( + 21888242871839275222246405745257275088548364400416034343698204186575808495616_U256 + ), + ); + assert_eq!(result, uint!(0_U256)); + + // -2 == -2 => 1 + let result = u_gte( + &uint!( + 21888242871839275222246405745257275088548364400416034343698204186575808495615_U256 + ), + &uint!( + 21888242871839275222246405745257275088548364400416034343698204186575808495615_U256 + ), + ); + assert_eq!(result, uint!(1_U256)); + } + + #[test] + fn test_x() { + let x = M.div(uint!(2_U256)); + + println!("x: {:?}", x.as_limbs()); + println!("x: {}", M); + } + + #[test] + fn test_2() { + let nodes: Vec = vec![]; + // let node = nodes[0]; + let node = nodes.get(0); + println!("{:?}", node); + } +} diff --git a/rln/src/iden3calc/proto.rs b/rln/src/iden3calc/proto.rs new file mode 100644 index 0000000..99886fb --- /dev/null +++ b/rln/src/iden3calc/proto.rs @@ -0,0 +1,117 @@ +// This file has been generated by prost-build during compilation of the code by iden3 +// and modified manually. The *.proto file used to generate this on can be found here: +// https://github.com/iden3/circom-witnesscalc/blob/5cb365b6e4d9052ecc69d4567fcf5bc061c20e94/protos/messages.proto + +use std::collections::HashMap; + +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BigUInt { + #[prost(bytes = "vec", tag = "1")] + pub value_le: Vec, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct InputNode { + #[prost(uint32, tag = "1")] + pub idx: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConstantNode { + #[prost(message, optional, tag = "1")] + pub value: Option, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct UnoOpNode { + #[prost(enumeration = "UnoOp", tag = "1")] + pub op: i32, + #[prost(uint32, tag = "2")] + pub a_idx: u32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct DuoOpNode { + #[prost(enumeration = "DuoOp", tag = "1")] + pub op: i32, + #[prost(uint32, tag = "2")] + pub a_idx: u32, + #[prost(uint32, tag = "3")] + pub b_idx: u32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct TresOpNode { + #[prost(enumeration = "TresOp", tag = "1")] + pub op: i32, + #[prost(uint32, tag = "2")] + pub a_idx: u32, + #[prost(uint32, tag = "3")] + pub b_idx: u32, + #[prost(uint32, tag = "4")] + pub c_idx: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Node { + #[prost(oneof = "node::Node", tags = "1, 2, 3, 4, 5")] + pub node: Option, +} +/// Nested message and enum types in `Node`. +pub mod node { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Node { + #[prost(message, tag = "1")] + Input(super::InputNode), + #[prost(message, tag = "2")] + Constant(super::ConstantNode), + #[prost(message, tag = "3")] + UnoOp(super::UnoOpNode), + #[prost(message, tag = "4")] + DuoOp(super::DuoOpNode), + #[prost(message, tag = "5")] + TresOp(super::TresOpNode), + } +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct SignalDescription { + #[prost(uint32, tag = "1")] + pub offset: u32, + #[prost(uint32, tag = "2")] + pub len: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GraphMetadata { + #[prost(uint32, repeated, tag = "1")] + pub witness_signals: Vec, + #[prost(map = "string, message", tag = "2")] + pub inputs: HashMap, +} +#[derive(Clone, Copy, Debug, PartialEq, ::prost::Enumeration)] +pub enum DuoOp { + Mul = 0, + Div = 1, + Add = 2, + Sub = 3, + Pow = 4, + Idiv = 5, + Mod = 6, + Eq = 7, + Neq = 8, + Lt = 9, + Gt = 10, + Leq = 11, + Geq = 12, + Land = 13, + Lor = 14, + Shl = 15, + Shr = 16, + Bor = 17, + Band = 18, + Bxor = 19, +} + +#[derive(Clone, Copy, Debug, PartialEq, ::prost::Enumeration)] +pub enum UnoOp { + Neg = 0, + Id = 1, +} + +#[derive(Clone, Copy, Debug, PartialEq, ::prost::Enumeration)] +pub enum TresOp { + TernCond = 0, +} diff --git a/rln/src/iden3calc/storage.rs b/rln/src/iden3calc/storage.rs new file mode 100644 index 0000000..d00c994 --- /dev/null +++ b/rln/src/iden3calc/storage.rs @@ -0,0 +1,496 @@ +// This file is based on the code by iden3. Its preimage can be found here: +// https://github.com/iden3/circom-witnesscalc/blob/5cb365b6e4d9052ecc69d4567fcf5bc061c20e94/src/storage.rs + +use crate::iden3calc::{ + graph, + graph::{Operation, TresOperation, UnoOperation}, + proto, InputSignalsInfo, +}; +use ark_bn254::Fr; +use ark_ff::PrimeField; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use prost::Message; +use std::io::{Read, Write}; + +// format of the wtns.graph file: +// + magic line: wtns.graph.001 +// + 4 bytes unsigned LE 32-bit integer: number of nodes +// + series of protobuf serialized nodes. Each node prefixed by varint length +// + protobuf serialized GraphMetadata +// + 8 bytes unsigned LE 64-bit integer: offset of GraphMetadata message + +const WITNESSCALC_GRAPH_MAGIC: &[u8] = b"wtns.graph.001"; + +const MAX_VARINT_LENGTH: usize = 10; + +impl From for graph::Node { + fn from(value: proto::Node) -> Self { + match value.node.unwrap() { + proto::node::Node::Input(input_node) => graph::Node::Input(input_node.idx as usize), + proto::node::Node::Constant(constant_node) => { + let i = constant_node.value.unwrap(); + graph::Node::MontConstant(Fr::from_le_bytes_mod_order(i.value_le.as_slice())) + } + proto::node::Node::UnoOp(uno_op_node) => { + let op = proto::UnoOp::try_from(uno_op_node.op).unwrap(); + graph::Node::UnoOp(op.into(), uno_op_node.a_idx as usize) + } + proto::node::Node::DuoOp(duo_op_node) => { + let op = proto::DuoOp::try_from(duo_op_node.op).unwrap(); + graph::Node::Op( + op.into(), + duo_op_node.a_idx as usize, + duo_op_node.b_idx as usize, + ) + } + proto::node::Node::TresOp(tres_op_node) => { + let op = proto::TresOp::try_from(tres_op_node.op).unwrap(); + graph::Node::TresOp( + op.into(), + tres_op_node.a_idx as usize, + tres_op_node.b_idx as usize, + tres_op_node.c_idx as usize, + ) + } + } + } +} + +impl From<&graph::Node> for proto::node::Node { + fn from(node: &graph::Node) -> Self { + match node { + graph::Node::Input(i) => proto::node::Node::Input(proto::InputNode { idx: *i as u32 }), + graph::Node::Constant(_) => { + panic!("We are not supposed to write Constant to the witnesscalc graph. All Constant should be converted to MontConstant."); + } + graph::Node::UnoOp(op, a) => { + let op = proto::UnoOp::from(op); + proto::node::Node::UnoOp(proto::UnoOpNode { + op: op as i32, + a_idx: *a as u32, + }) + } + graph::Node::Op(op, a, b) => proto::node::Node::DuoOp(proto::DuoOpNode { + op: proto::DuoOp::from(op) as i32, + a_idx: *a as u32, + b_idx: *b as u32, + }), + graph::Node::TresOp(op, a, b, c) => proto::node::Node::TresOp(proto::TresOpNode { + op: proto::TresOp::from(op) as i32, + a_idx: *a as u32, + b_idx: *b as u32, + c_idx: *c as u32, + }), + graph::Node::MontConstant(c) => { + let bi = Into::::into(*c); + let i = proto::BigUInt { + value_le: bi.to_bytes_le(), + }; + proto::node::Node::Constant(proto::ConstantNode { value: Some(i) }) + } + } + } +} + +impl From for UnoOperation { + fn from(value: proto::UnoOp) -> Self { + match value { + proto::UnoOp::Neg => UnoOperation::Neg, + proto::UnoOp::Id => UnoOperation::Id, + } + } +} + +impl From for Operation { + fn from(value: proto::DuoOp) -> Self { + match value { + proto::DuoOp::Mul => Operation::Mul, + proto::DuoOp::Div => Operation::Div, + proto::DuoOp::Add => Operation::Add, + proto::DuoOp::Sub => Operation::Sub, + proto::DuoOp::Pow => Operation::Pow, + proto::DuoOp::Idiv => Operation::Idiv, + proto::DuoOp::Mod => Operation::Mod, + proto::DuoOp::Eq => Operation::Eq, + proto::DuoOp::Neq => Operation::Neq, + proto::DuoOp::Lt => Operation::Lt, + proto::DuoOp::Gt => Operation::Gt, + proto::DuoOp::Leq => Operation::Leq, + proto::DuoOp::Geq => Operation::Geq, + proto::DuoOp::Land => Operation::Land, + proto::DuoOp::Lor => Operation::Lor, + proto::DuoOp::Shl => Operation::Shl, + proto::DuoOp::Shr => Operation::Shr, + proto::DuoOp::Bor => Operation::Bor, + proto::DuoOp::Band => Operation::Band, + proto::DuoOp::Bxor => Operation::Bxor, + } + } +} + +impl From for graph::TresOperation { + fn from(value: proto::TresOp) -> Self { + match value { + proto::TresOp::TernCond => TresOperation::TernCond, + } + } +} + +pub fn serialize_witnesscalc_graph( + mut w: T, + nodes: &Vec, + witness_signals: &[usize], + input_signals: &InputSignalsInfo, +) -> std::io::Result<()> { + let mut ptr = 0usize; + w.write_all(WITNESSCALC_GRAPH_MAGIC).unwrap(); + ptr += WITNESSCALC_GRAPH_MAGIC.len(); + + w.write_u64::(nodes.len() as u64)?; + ptr += 8; + + let metadata = proto::GraphMetadata { + witness_signals: witness_signals + .iter() + .map(|x| *x as u32) + .collect::>(), + inputs: input_signals + .iter() + .map(|(k, v)| { + let sig = proto::SignalDescription { + offset: v.0 as u32, + len: v.1 as u32, + }; + (k.clone(), sig) + }) + .collect(), + }; + + // capacity of buf should be enough to hold the largest message + 10 bytes + // of varint length + let mut buf = Vec::with_capacity(metadata.encoded_len() + MAX_VARINT_LENGTH); + + for node in nodes { + let node_pb = proto::Node { + node: Some(proto::node::Node::from(node)), + }; + + assert_eq!(buf.len(), 0); + node_pb.encode_length_delimited(&mut buf)?; + ptr += buf.len(); + + w.write_all(&buf)?; + buf.clear(); + } + + metadata.encode_length_delimited(&mut buf)?; + w.write_all(&buf)?; + buf.clear(); + + w.write_u64::(ptr as u64)?; + + Ok(()) +} + +fn read_message_length(rw: &mut WriteBackReader) -> std::io::Result { + let mut buf = [0u8; MAX_VARINT_LENGTH]; + let bytes_read = rw.read(&mut buf)?; + if bytes_read == 0 { + return Err(std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "Unexpected EOF", + )); + } + + let len_delimiter = prost::decode_length_delimiter(buf.as_ref())?; + + let lnln = prost::length_delimiter_len(len_delimiter); + + if lnln < bytes_read { + rw.write_all(&buf[lnln..bytes_read])?; + } + + Ok(len_delimiter) +} + +fn read_message( + rw: &mut WriteBackReader, +) -> std::io::Result { + let ln = read_message_length(rw)?; + let mut buf = vec![0u8; ln]; + let bytes_read = rw.read(&mut buf)?; + if bytes_read != ln { + return Err(std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "Unexpected EOF", + )); + } + + let msg = prost::Message::decode(&buf[..])?; + + Ok(msg) +} + +pub fn deserialize_witnesscalc_graph( + r: impl Read, +) -> std::io::Result<(Vec, Vec, InputSignalsInfo)> { + let mut br = WriteBackReader::new(r); + let mut magic = [0u8; WITNESSCALC_GRAPH_MAGIC.len()]; + + br.read_exact(&mut magic)?; + + if !magic.eq(WITNESSCALC_GRAPH_MAGIC) { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid magic", + )); + } + + let nodes_num = br.read_u64::()?; + let mut nodes = Vec::with_capacity(nodes_num as usize); + for _ in 0..nodes_num { + let n: proto::Node = read_message(&mut br)?; + let n2: graph::Node = n.into(); + nodes.push(n2); + } + + let md: proto::GraphMetadata = read_message(&mut br)?; + + let witness_signals = md + .witness_signals + .iter() + .map(|x| *x as usize) + .collect::>(); + + let input_signals = md + .inputs + .iter() + .map(|(k, v)| (k.clone(), (v.offset as usize, v.len as usize))) + .collect::(); + + Ok((nodes, witness_signals, input_signals)) +} + +struct WriteBackReader { + reader: R, + buffer: Vec, +} + +impl WriteBackReader { + fn new(reader: R) -> Self { + WriteBackReader { + reader, + buffer: Vec::new(), + } + } +} + +impl Read for WriteBackReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + if buf.is_empty() { + return Ok(0); + } + + let mut n = 0usize; + + if !self.buffer.is_empty() { + n = std::cmp::min(buf.len(), self.buffer.len()); + self.buffer[self.buffer.len() - n..] + .iter() + .rev() + .enumerate() + .for_each(|(i, x)| { + buf[i] = *x; + }); + self.buffer.truncate(self.buffer.len() - n); + } + + while n < buf.len() { + let m = self.reader.read(&mut buf[n..])?; + if m == 0 { + break; + } + n += m; + } + + Ok(n) + } +} + +impl Write for WriteBackReader { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buffer.reserve(buf.len()); + self.buffer.extend(buf.iter().rev()); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use byteorder::ByteOrder; + use core::str::FromStr; + use graph::{Operation, TresOperation, UnoOperation}; + use std::collections::HashMap; + + #[test] + fn test_read_message() { + let mut buf = Vec::new(); + let n1 = proto::Node { + node: Some(proto::node::Node::Input(proto::InputNode { idx: 1 })), + }; + n1.encode_length_delimited(&mut buf).unwrap(); + + let n2 = proto::Node { + node: Some(proto::node::Node::Input(proto::InputNode { idx: 2 })), + }; + n2.encode_length_delimited(&mut buf).unwrap(); + + let mut reader = std::io::Cursor::new(&buf); + + let mut rw = WriteBackReader::new(&mut reader); + + let got_n1: proto::Node = read_message(&mut rw).unwrap(); + assert!(n1.eq(&got_n1)); + + let got_n2: proto::Node = read_message(&mut rw).unwrap(); + assert!(n2.eq(&got_n2)); + + assert_eq!(reader.position(), buf.len() as u64); + } + + #[test] + fn test_read_message_variant() { + let nodes = vec![ + proto::Node { + node: Some(proto::node::Node::from(&graph::Node::Input(0))), + }, + proto::Node { + node: Some(proto::node::Node::from(&graph::Node::MontConstant( + Fr::from_str("1").unwrap(), + ))), + }, + proto::Node { + node: Some(proto::node::Node::from(&graph::Node::UnoOp( + UnoOperation::Id, + 4, + ))), + }, + proto::Node { + node: Some(proto::node::Node::from(&graph::Node::Op( + Operation::Mul, + 5, + 6, + ))), + }, + proto::Node { + node: Some(proto::node::Node::from(&graph::Node::TresOp( + TresOperation::TernCond, + 7, + 8, + 9, + ))), + }, + ]; + + let mut buf = Vec::new(); + for n in &nodes { + n.encode_length_delimited(&mut buf).unwrap(); + } + + let mut nodes_got: Vec = Vec::new(); + let mut reader = std::io::Cursor::new(&buf); + let mut rw = WriteBackReader::new(&mut reader); + for _ in 0..nodes.len() { + nodes_got.push(read_message(&mut rw).unwrap()); + } + + assert_eq!(nodes, nodes_got); + } + + #[test] + fn test_write_back_reader() { + let data = [1u8, 2, 3, 4, 5, 6]; + let mut r = WriteBackReader::new(std::io::Cursor::new(&data)); + + let buf = &mut [0u8; 5]; + r.read(buf).unwrap(); + assert_eq!(buf, &[1, 2, 3, 4, 5]); + + // return [4, 5] to reader + r.write(&buf[3..]).unwrap(); + // return [2, 3] to reader + r.write(&buf[1..3]).unwrap(); + + buf.fill(0); + + // read 3 bytes, expect [2, 3, 4] after returns + let mut n = r.read(&mut buf[..3]).unwrap(); + assert_eq!(n, 3); + assert_eq!(buf, &[2, 3, 4, 0, 0]); + + buf.fill(0); + + // read everything left in reader + n = r.read(buf).unwrap(); + assert_eq!(n, 2); + assert_eq!(buf, &[5, 6, 0, 0, 0]); + } + + #[test] + fn test_deserialize_inputs() { + let nodes = vec![ + graph::Node::Input(0), + graph::Node::MontConstant(Fr::from_str("1").unwrap()), + graph::Node::UnoOp(UnoOperation::Id, 4), + graph::Node::Op(Operation::Mul, 5, 6), + graph::Node::TresOp(TresOperation::TernCond, 7, 8, 9), + ]; + + let witness_signals = vec![4, 1]; + + let mut input_signals: InputSignalsInfo = HashMap::new(); + input_signals.insert("sig1".to_string(), (1, 3)); + input_signals.insert("sig2".to_string(), (5, 1)); + + let mut tmp = Vec::new(); + serialize_witnesscalc_graph(&mut tmp, &nodes, &witness_signals, &input_signals).unwrap(); + + let mut reader = std::io::Cursor::new(&tmp); + + let (nodes_res, witness_signals_res, input_signals_res) = + deserialize_witnesscalc_graph(&mut reader).unwrap(); + + assert_eq!(nodes, nodes_res); + assert_eq!(input_signals, input_signals_res); + assert_eq!(witness_signals, witness_signals_res); + + let metadata_start = LittleEndian::read_u64(&tmp[tmp.len() - 8..]); + + let mt_reader = std::io::Cursor::new(&tmp[metadata_start as usize..]); + let mut rw = WriteBackReader::new(mt_reader); + let metadata: proto::GraphMetadata = read_message(&mut rw).unwrap(); + + let metadata_want = proto::GraphMetadata { + witness_signals: vec![4, 1], + inputs: input_signals + .iter() + .map(|(k, v)| { + ( + k.clone(), + proto::SignalDescription { + offset: v.0 as u32, + len: v.1 as u32, + }, + ) + }) + .collect(), + }; + + assert_eq!(metadata, metadata_want); + } +} diff --git a/rln/src/lib.rs b/rln/src/lib.rs index 292db57..06d8d4a 100644 --- a/rln/src/lib.rs +++ b/rln/src/lib.rs @@ -2,6 +2,7 @@ pub mod circuit; pub mod hashers; +pub mod iden3calc; #[cfg(feature = "pmtree-ft")] pub mod pm_tree_adapter; pub mod poseidon_tree; diff --git a/rln/src/protocol.rs b/rln/src/protocol.rs index 46aa853..7818aed 100644 --- a/rln/src/protocol.rs +++ b/rln/src/protocol.rs @@ -1,6 +1,6 @@ // This crate collects all the underlying primitives used to implement RLN -use ark_circom::{CircomReduction, WitnessCalculator}; +use ark_circom::CircomReduction; use ark_groth16::{prepare_verifying_key, Groth16, Proof as ArkProof, ProvingKey, VerifyingKey}; use ark_relations::r1cs::ConstraintMatrices; use ark_relations::r1cs::SynthesisError; @@ -11,20 +11,17 @@ use num_bigint::BigInt; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; use serde::{Deserialize, Serialize}; -#[cfg(not(target_arch = "wasm32"))] -use std::sync::Mutex; -#[cfg(debug_assertions)] +#[cfg(test)] use std::time::Instant; use thiserror::Error; use tiny_keccak::{Hasher as _, Keccak}; -use crate::circuit::{Curve, Fr}; +use crate::circuit::{calculate_rln_witness, Curve, Fr}; use crate::hashers::hash_to_field; use crate::hashers::poseidon_hash; use crate::poseidon_tree::*; use crate::public::RLN_IDENTIFIER; use crate::utils::*; -use cfg_if::cfg_if; use utils::{ZerokitMerkleProof, ZerokitMerkleTree}; /////////////////////////////////////////////////////// @@ -544,13 +541,13 @@ pub fn generate_proof_with_witness( proving_key: &(ProvingKey, ConstraintMatrices), ) -> Result, ProofError> { // If in debug mode, we measure and later print time take to compute witness - #[cfg(debug_assertions)] + #[cfg(test)] let now = Instant::now(); let full_assignment = calculate_witness_element::(witness).map_err(ProofError::WitnessError)?; - #[cfg(debug_assertions)] + #[cfg(test)] println!("witness generation took: {:.2?}", now.elapsed()); // Random Values @@ -559,7 +556,7 @@ pub fn generate_proof_with_witness( let s = Fr::rand(&mut rng); // If in debug mode, we measure and later print time take to compute proof - #[cfg(debug_assertions)] + #[cfg(test)] let now = Instant::now(); let proof = Groth16::<_, CircomReduction>::create_proof_with_reduction_and_matrices( @@ -572,7 +569,7 @@ pub fn generate_proof_with_witness( full_assignment.as_slice(), )?; - #[cfg(debug_assertions)] + #[cfg(test)] println!("proof generation took: {:.2?}", now.elapsed()); Ok(proof) @@ -628,8 +625,6 @@ pub fn inputs_for_witness_calculation( /// /// Returns a [`ProofError`] if proving fails. pub fn generate_proof( - #[cfg(not(target_arch = "wasm32"))] witness_calculator: &Mutex, - #[cfg(target_arch = "wasm32")] witness_calculator: &mut WitnessCalculator, proving_key: &(ProvingKey, ConstraintMatrices), rln_witness: &RLNWitnessInput, ) -> Result, ProofError> { @@ -638,24 +633,11 @@ pub fn generate_proof( .map(|(name, values)| (name.to_string(), values)); // If in debug mode, we measure and later print time take to compute witness - #[cfg(debug_assertions)] + #[cfg(test)] let now = Instant::now(); + let full_assignment = calculate_rln_witness(inputs); - cfg_if! { - if #[cfg(target_arch = "wasm32")] { - let full_assignment = witness_calculator - .calculate_witness_element::(inputs, false) - .map_err(ProofError::WitnessError)?; - } else { - let full_assignment = witness_calculator - .lock() - .expect("witness_calculator mutex should not get poisoned") - .calculate_witness_element::(inputs, false) - .map_err(ProofError::WitnessError)?; - } - } - - #[cfg(debug_assertions)] + #[cfg(test)] println!("witness generation took: {:.2?}", now.elapsed()); // Random Values @@ -664,7 +646,7 @@ pub fn generate_proof( let s = Fr::rand(&mut rng); // If in debug mode, we measure and later print time take to compute proof - #[cfg(debug_assertions)] + #[cfg(test)] let now = Instant::now(); let proof = Groth16::<_, CircomReduction>::create_proof_with_reduction_and_matrices( &proving_key.0, @@ -676,7 +658,7 @@ pub fn generate_proof( full_assignment.as_slice(), )?; - #[cfg(debug_assertions)] + #[cfg(test)] println!("proof generation took: {:.2?}", now.elapsed()); Ok(proof) @@ -707,12 +689,12 @@ pub fn verify_proof( //let pr: ArkProof = (*proof).into(); // If in debug mode, we measure and later print time take to verify proof - #[cfg(debug_assertions)] + #[cfg(test)] let now = Instant::now(); let verified = Groth16::<_, CircomReduction>::verify_proof(&pvk, proof, &inputs)?; - #[cfg(debug_assertions)] + #[cfg(test)] println!("verify took: {:.2?}", now.elapsed()); Ok(verified) diff --git a/rln/src/public.rs b/rln/src/public.rs index ca575d4..b9bf19a 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -16,13 +16,10 @@ use utils::{ZerokitMerkleProof, ZerokitMerkleTree}; cfg_if! { if #[cfg(not(target_arch = "wasm32"))] { use std::default::Default; - use std::sync::Mutex; - use crate::circuit::{circom_from_folder, vk_from_folder, circom_from_raw, zkey_from_folder, TEST_TREE_HEIGHT}; - use ark_circom::WitnessCalculator; + use crate::circuit::{vk_from_folder, zkey_from_folder, TEST_TREE_HEIGHT}; use crate::poseidon_tree::PoseidonTree; use serde_json::{json, Value}; - use utils::{Hasher}; - use std::sync::Arc; + use utils::Hasher; use std::str::FromStr; } else { use std::marker::*; @@ -45,12 +42,6 @@ pub struct RLN { pub(crate) verification_key: VerifyingKey, #[cfg(not(feature = "stateless"))] pub(crate) tree: PoseidonTree, - - // The witness calculator can't be loaded in zerokit. Since this struct - // contains a lifetime, a PhantomData is necessary to avoid a compiler - // error since the lifetime is not being used - #[cfg(not(target_arch = "wasm32"))] - pub(crate) witness_calculator: Arc>, #[cfg(target_arch = "wasm32")] _marker: PhantomData<()>, } @@ -81,7 +72,6 @@ impl RLN { let rln_config: Value = serde_json::from_str(&String::from_utf8(input)?)?; let tree_config = rln_config["tree_config"].to_string(); - let witness_calculator = circom_from_folder(); let proving_key = zkey_from_folder(); let verification_key = vk_from_folder(); @@ -100,7 +90,6 @@ impl RLN { )?; Ok(RLN { - witness_calculator: witness_calculator.to_owned(), proving_key: proving_key.to_owned(), verification_key: verification_key.to_owned(), #[cfg(not(feature = "stateless"))] @@ -122,12 +111,10 @@ impl RLN { #[cfg(all(not(target_arch = "wasm32"), feature = "stateless"))] pub fn new() -> Result { #[cfg(not(target_arch = "wasm32"))] - let witness_calculator = circom_from_folder(); let proving_key = zkey_from_folder(); let verification_key = vk_from_folder(); Ok(RLN { - witness_calculator: witness_calculator.to_owned(), proving_key: proving_key.to_owned(), verification_key: verification_key.to_owned(), #[cfg(target_arch = "wasm32")] @@ -139,7 +126,6 @@ impl RLN { /// /// Input parameters are /// - `tree_height`: the height of the internal Merkle tree - /// - `circom_vec`: a byte vector containing the ZK circuit (`rln.wasm`) as binary file /// - `zkey_vec`: a byte vector containing to the proving key (`rln_final.zkey`) or (`rln_final.arkzkey`) as binary file /// - `vk_vec`: a byte vector containing to the verification key (`verification_key.arkvkey`) as binary file /// - `tree_config_input`: a reader for a string containing a json with the merkle tree configuration @@ -153,36 +139,33 @@ impl RLN { /// let resources_folder = "./resources/tree_height_20/"; /// /// let mut resources: Vec> = Vec::new(); - /// for filename in ["rln.wasm", "rln_final.zkey", "verification_key.arkvkey"] { + /// for filename in ["rln_final.zkey", "verification_key.arkvkey"] { /// let fullpath = format!("{resources_folder}{filename}"); /// let mut file = File::open(&fullpath).expect("no file found"); /// let metadata = std::fs::metadata(&fullpath).expect("unable to read metadata"); /// let mut buffer = vec![0; metadata.len() as usize]; /// file.read_exact(&mut buffer).expect("buffer overflow"); /// resources.push(buffer); - /// let tree_config = "{}".to_string(); - /// let tree_config_input = &Buffer::from(tree_config.as_bytes()); /// } /// + /// let tree_config = "".to_string(); + /// let tree_config_buffer = &Buffer::from(tree_config.as_bytes()); + /// /// let mut rln = RLN::new_with_params( /// tree_height, /// resources[0].clone(), /// resources[1].clone(), - /// resources[2].clone(), - /// tree_config_input, + /// tree_config_buffer, /// ); /// ``` #[cfg(all(not(target_arch = "wasm32"), not(feature = "stateless")))] pub fn new_with_params( tree_height: usize, - circom_vec: Vec, zkey_vec: Vec, vk_vec: Vec, mut tree_config_input: R, ) -> Result { #[cfg(not(target_arch = "wasm32"))] - let witness_calculator = circom_from_raw(&circom_vec)?; - let proving_key = zkey_from_raw(&zkey_vec)?; let verification_key = vk_from_raw(&vk_vec, &zkey_vec)?; @@ -204,7 +187,6 @@ impl RLN { )?; Ok(RLN { - witness_calculator, proving_key, verification_key, #[cfg(not(feature = "stateless"))] @@ -217,7 +199,6 @@ impl RLN { /// Creates a new stateless RLN object by passing circuit resources as byte vectors. /// /// Input parameters are - /// - `circom_vec`: a byte vector containing the ZK circuit (`rln.wasm`) as binary file /// - `zkey_vec`: a byte vector containing to the proving key (`rln_final.zkey`) or (`rln_final.arkzkey`) as binary file /// - `vk_vec`: a byte vector containing to the verification key (`verification_key.arkvkey`) as binary file /// @@ -229,7 +210,7 @@ impl RLN { /// let resources_folder = "./resources/tree_height_20/"; /// /// let mut resources: Vec> = Vec::new(); - /// for filename in ["rln.wasm", "rln_final.zkey", "verification_key.arkvkey"] { + /// for filename in ["rln_final.zkey", "verification_key.arkvkey"] { /// let fullpath = format!("{resources_folder}{filename}"); /// let mut file = File::open(&fullpath).expect("no file found"); /// let metadata = std::fs::metadata(&fullpath).expect("unable to read metadata"); @@ -241,18 +222,14 @@ impl RLN { /// let mut rln = RLN::new_with_params( /// resources[0].clone(), /// resources[1].clone(), - /// resources[2].clone(), /// ); /// ``` #[cfg(all(not(target_arch = "wasm32"), feature = "stateless"))] - pub fn new_with_params(circom_vec: Vec, zkey_vec: Vec, vk_vec: Vec) -> Result { - let witness_calculator = circom_from_raw(&circom_vec)?; - + pub fn new_with_params(zkey_vec: Vec, vk_vec: Vec) -> Result { let proving_key = zkey_from_raw(&zkey_vec)?; let verification_key = vk_from_raw(&vk_vec, &zkey_vec)?; Ok(RLN { - witness_calculator, proving_key, verification_key, }) @@ -784,13 +761,7 @@ impl RLN { input_data.read_to_end(&mut serialized)?; let (rln_witness, _) = deserialize_witness(&serialized)?; - /* - if self.witness_calculator.is_none() { - self.witness_calculator = CIRCOM(&self.resources_folder); - } - */ - - let proof = generate_proof(&self.witness_calculator, &self.proving_key, &rln_witness)?; + let proof = generate_proof(&self.proving_key, &rln_witness)?; // Note: we export a serialization of ark-groth16::Proof not semaphore::Proof proof.serialize_compressed(&mut output_data)?; @@ -913,7 +884,7 @@ impl RLN { let (rln_witness, _) = proof_inputs_to_rln_witness(&mut self.tree, &witness_byte)?; let proof_values = proof_values_from_witness(&rln_witness)?; - let proof = generate_proof(&self.witness_calculator, &self.proving_key, &rln_witness)?; + let proof = generate_proof(&self.proving_key, &rln_witness)?; // Note: we export a serialization of ark-groth16::Proof not semaphore::Proof // This proof is compressed, i.e. 128 bytes long @@ -961,7 +932,7 @@ impl RLN { let (rln_witness, _) = deserialize_witness(&witness_byte)?; let proof_values = proof_values_from_witness(&rln_witness)?; - let proof = generate_proof(&self.witness_calculator, &self.proving_key, &rln_witness)?; + let proof = generate_proof(&self.proving_key, &rln_witness)?; // Note: we export a serialization of ark-groth16::Proof not semaphore::Proof // This proof is compressed, i.e. 128 bytes long diff --git a/rln/tests/ffi.rs b/rln/tests/ffi.rs index b116148..dd6ad71 100644 --- a/rln/tests/ffi.rs +++ b/rln/tests/ffi.rs @@ -408,15 +408,6 @@ mod test { // We obtain the root from the RLN instance let root_rln_folder = get_tree_root(rln_pointer); - // Reading the raw data from the files required for instantiating a RLN instance using raw data - let circom_path = "./resources/tree_height_20/rln.wasm"; - let mut circom_file = File::open(&circom_path).expect("no file found"); - let metadata = std::fs::metadata(&circom_path).expect("unable to read metadata"); - let mut circom_buffer = vec![0; metadata.len() as usize]; - circom_file - .read_exact(&mut circom_buffer) - .expect("buffer overflow"); - #[cfg(feature = "arkzkey")] let zkey_path = "./resources/tree_height_20/rln_final.arkzkey"; #[cfg(not(feature = "arkzkey"))] @@ -434,7 +425,6 @@ mod test { let mut vk_buffer = vec![0; metadata.len() as usize]; vk_file.read_exact(&mut vk_buffer).expect("buffer overflow"); - let circom_data = &Buffer::from(&circom_buffer[..]); let zkey_data = &Buffer::from(&zkey_buffer[..]); let vk_data = &Buffer::from(&vk_buffer[..]); @@ -444,7 +434,6 @@ mod test { let tree_config_buffer = &Buffer::from(tree_config.as_bytes()); let success = new_with_params( TEST_TREE_HEIGHT, - circom_data, zkey_data, vk_data, tree_config_buffer, diff --git a/rln/tests/protocol.rs b/rln/tests/protocol.rs index 9a5144a..26cea1a 100644 --- a/rln/tests/protocol.rs +++ b/rln/tests/protocol.rs @@ -2,7 +2,7 @@ mod test { use ark_ff::BigInt; use rln::circuit::zkey_from_folder; - use rln::circuit::{circom_from_folder, vk_from_folder, Fr, TEST_TREE_HEIGHT}; + use rln::circuit::{vk_from_folder, Fr, TEST_TREE_HEIGHT}; use rln::hashers::{hash_to_field, poseidon_hash}; use rln::poseidon_tree::PoseidonTree; use rln::protocol::*; @@ -129,7 +129,6 @@ mod test { // We generate all relevant keys let proving_key = zkey_from_folder(); let verification_key = vk_from_folder(); - let builder = circom_from_folder(); // We compute witness from the json input let rln_witness = get_test_witness(); @@ -138,7 +137,7 @@ mod test { assert_eq!(rln_witness_deser, rln_witness); // Let's generate a zkSNARK proof - let proof = generate_proof(builder, &proving_key, &rln_witness_deser).unwrap(); + let proof = generate_proof(&proving_key, &rln_witness_deser).unwrap(); let proof_values = proof_values_from_witness(&rln_witness_deser).unwrap(); // Let's verify the proof @@ -158,10 +157,9 @@ mod test { // We generate all relevant keys let proving_key = zkey_from_folder(); let verification_key = vk_from_folder(); - let builder = circom_from_folder(); // Let's generate a zkSNARK proof - let proof = generate_proof(builder, &proving_key, &rln_witness_deser).unwrap(); + let proof = generate_proof(&proving_key, &rln_witness_deser).unwrap(); let proof_values = proof_values_from_witness(&rln_witness_deser).unwrap();