diff --git a/.gitignore b/.gitignore index 65983083..711f98d2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ Cargo.lock /halo2_ecc/src/bn254/data/ /halo2_ecc/src/secp256k1/data/ + +*.csv +*.srs \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..f836a1c2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2467 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "colored", + "num-traits", + "rand", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide 0.6.2", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bumpalo" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "ciborium" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef2b3ded6a26dfaec672a742c93c8cf6b689220324da509ec5caa20de55dc83" +dependencies = [ + "bitflags", + "clap_lex", + "indexmap", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "const-cstr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" + +[[package]] +name = "constant_time_eq" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "cpp_demangle" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c76f98bdfc7f66172e6c7065f981ebb576ffc903fe4c0561d9f0c2509226dc6" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-macro" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "288a8f36b28a19d7dbd572c76006c0a0eba1f3bf912a254dda211c6f665e7ffc" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.31", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "derive_builder" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" +dependencies = [ + "darling", + "derive_builder_core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "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 = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading", +] + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide 0.6.2", +] + +[[package]] +name = "float-ord" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "font-kit" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5" +dependencies = [ + "bitflags", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core", + "subtle", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "halo2-base" +version = "0.2.2" +dependencies = [ + "ark-std", + "criterion", + "criterion-macro", + "ff 0.13.0", + "halo2_proofs 0.2.0 (git+https://github.com/axiom-crypto/halo2.git?tag=v2023_01_17)", + "halo2_proofs 0.2.0 (git+https://github.com/scroll-tech/halo2.git?branch=v1.1)", + "itertools", + "jemallocator", + "mimalloc", + "num-bigint", + "num-integer", + "num-traits", + "plotters", + "pprof", + "rand", + "rand_chacha", + "rustc-hash", + "tabbycat", +] + +[[package]] +name = "halo2-ecc" +version = "0.2.2" +dependencies = [ + "ark-std", + "criterion", + "criterion-macro", + "ff 0.13.0", + "group 0.13.0", + "halo2-base", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "pprof", + "rand", + "rand_chacha", + "rand_core", + "serde", + "serde_json", +] + +[[package]] +name = "halo2_proofs" +version = "0.2.0" +source = "git+https://github.com/axiom-crypto/halo2.git?tag=v2023_01_17#475e45f52a0774ceb81304dd6a3a97dddd07662e" +dependencies = [ + "blake2b_simd", + "ff 0.12.1", + "group 0.12.1", + "halo2curves 0.3.1", + "plotters", + "rand_core", + "rayon", + "rustc-hash", + "tabbycat", + "tracing", +] + +[[package]] +name = "halo2_proofs" +version = "0.2.0" +source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.1#84003a202d6b9185c952ec91ea793dbbd6c5b200" +dependencies = [ + "ark-std", + "blake2b_simd", + "cfg-if 0.1.10", + "crossbeam", + "ff 0.13.0", + "group 0.13.0", + "halo2curves 0.1.0", + "log", + "num-bigint", + "num-integer", + "plotters", + "poseidon", + "rand_core", + "rayon", + "sha3", + "subtle", + "tabbycat", + "tracing", +] + +[[package]] +name = "halo2curves" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b1142bd1059aacde1b477e0c80c142910f1ceae67fc619311d6a17428007ab" +dependencies = [ + "blake2b_simd", + "ff 0.13.0", + "group 0.13.0", + "lazy_static", + "num-bigint", + "num-traits", + "pasta_curves 0.5.1", + "paste", + "rand", + "rand_core", + "serde", + "serde_arrays", + "static_assertions", + "subtle", +] + +[[package]] +name = "halo2curves" +version = "0.3.1" +source = "git+https://github.com/axiom-crypto/halo2.git?tag=v2023_01_17#475e45f52a0774ceb81304dd6a3a97dddd07662e" +dependencies = [ + "ff 0.12.1", + "group 0.12.1", + "lazy_static", + "num-bigint", + "num-traits", + "pasta_curves 0.4.1", + "rand", + "rand_core", + "serde", + "static_assertions", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inferno" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb7c1b80a1dfa604bb4a649a5c5aeef3d913f7c520cb42b40e534e8a61bcdfc" +dependencies = [ + "ahash", + "indexmap", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jemalloc-sys" +version = "0.5.3+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9bd5d616ea7ed58b571b2e209a65759664d7fb021a0819d7a790afc67e47ca1" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c2514137880c52b0b4822b563fadd38257c1f380858addb74a400889696ea6" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libmimalloc-sys" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ac0e912c8ef1b735e92369695618dc5b1819f5a7bf3f167301a3ba1cea515e" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mimalloc" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2894987a3459f3ffb755608bd82188f8ed00d0ae077f1edea29c068d639d98" +dependencies = [ + "libmimalloc-sys", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "libc", + "static_assertions", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "rand", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "pasta_curves" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" +dependencies = [ + "blake2b_simd", + "ff 0.12.1", + "group 0.12.1", + "lazy_static", + "rand", + "static_assertions", + "subtle", +] + +[[package]] +name = "pasta_curves" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" +dependencies = [ + "blake2b_simd", + "ff 0.13.0", + "group 0.13.0", + "lazy_static", + "rand", + "static_assertions", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pest" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "chrono", + "font-kit", + "image", + "lazy_static", + "num-traits", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-bitmap" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4a1f21490a6cf4a84c272ad20bd7844ed99a3178187a4c5ab7f2051295beef" +dependencies = [ + "gif", + "image", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "poseidon" +version = "0.2.0" +source = "git+https://github.com/scroll-tech/poseidon.git?branch=main#5787dd3d2ce7a9e9601a035c396ac0c03449b54d" +dependencies = [ + "halo2curves 0.1.0", + "subtle", +] + +[[package]] +name = "pprof" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196ded5d4be535690899a4631cc9f18cdc41b7ebf24a79400f46f48e49a11059" +dependencies = [ + "backtrace", + "cfg-if 1.0.0", + "criterion", + "findshlibs", + "inferno", + "libc", + "log", + "nix", + "once_cell", + "parking_lot", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "rgb" +version = "0.8.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0661814f891c57c930a610266415528da53c4933e6dea5fb350cbfe048a9ece" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[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-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_arrays" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38636132857f68ec3d5f3eb121166d2af33cb55174c4d5ff645db6165cbef0fd" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer", + "digest", + "keccak", + "opaque-debug", +] + +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "symbolic-common" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b55cdc318ede251d0957f07afe5fed912119b8c1bc5a7804151826db999e737" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79be897be8a483a81fff6a3a4e195b4ac838ef73ca42d348b3f722da9902e489" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + +[[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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tabbycat" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c45590f0f859197b4545be1b17b2bc3cc7bb075f7d1cc0ea1dc6521c0bf256a3" +dependencies = [ + "anyhow", + "derive_builder", + "regex", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tracing" +version = "0.1.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "uuid" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" +dependencies = [ + "const-cstr", + "dlib", + "once_cell", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 4f01110c..82e5a55a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,9 @@ members = [ "halo2-base", "halo2-ecc", - "hashes/zkevm-keccak", + # "hashes/zkevm-keccak", ] +resolver = "2" [profile.dev] opt-level = 3 diff --git a/halo2-base/Cargo.toml b/halo2-base/Cargo.toml index 0046f2e0..53ba0931 100644 --- a/halo2-base/Cargo.toml +++ b/halo2-base/Cargo.toml @@ -10,12 +10,12 @@ num-integer = "0.1" num-traits = "0.2" rand_chacha = "0.3" rustc-hash = "1.1" -ff = "0.12" +ff = "0.13" # Use Axiom's custom halo2 monorepo for faster proving when feature = "halo2-axiom" is on halo2_proofs_axiom = { git = "https://github.com/axiom-crypto/halo2.git", tag = "v2023_01_17", package = "halo2_proofs", optional = true } # Use PSE halo2 and halo2curves for compatibility when feature = "halo2-pse" is on -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_01_20", optional = true } +halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.1", optional = true } # plotting circuit layout plotters = { version = "0.3.0", optional = true } @@ -35,7 +35,7 @@ jemallocator = { version = "0.5", optional = true } mimalloc = { version = "0.1", default-features = false, optional = true } [features] -default = ["halo2-axiom", "display"] +default = ["halo2-pse", "display"] dev-graph = ["halo2_proofs?/dev-graph", "halo2_proofs_axiom?/dev-graph", "plotters"] halo2-pse = ["halo2_proofs"] halo2-axiom = ["halo2_proofs_axiom"] diff --git a/halo2-base/benches/mul.rs b/halo2-base/benches/mul.rs index 6698ae99..146db061 100644 --- a/halo2-base/benches/mul.rs +++ b/halo2-base/benches/mul.rs @@ -85,7 +85,7 @@ impl Circuit for MyCircuit { }; for _ in 0..120 { - config.mul(ctx, Existing(&c_cell), Existing(&b_cell)); + config.mul(ctx, Existing(c_cell), Existing(b_cell)); } Ok(()) diff --git a/halo2-base/example/inner_product.rs b/halo2-base/example/inner_product.rs new file mode 100644 index 00000000..7d38f785 --- /dev/null +++ b/halo2-base/example/inner_product.rs @@ -0,0 +1,97 @@ +//! halo2-lib v0.3.0 file. Not useful for current version. +//! Code included to minimize diffs between 0.2.2 and 0.3.0 +#![allow(unused_imports)] +#![allow(unused_variables)] +use halo2_base::gates::builder::{GateCircuitBuilder, GateThreadBuilder}; +use halo2_base::gates::flex_gate::{FlexGateConfig, GateChip, GateInstructions, GateStrategy}; +use halo2_base::halo2_proofs::{ + arithmetic::Field, + circuit::*, + dev::MockProver, + halo2curves::bn256::{Bn256, Fr, G1Affine}, + plonk::*, + poly::kzg::multiopen::VerifierSHPLONK, + poly::kzg::strategy::SingleStrategy, + poly::kzg::{ + commitment::{KZGCommitmentScheme, ParamsKZG}, + multiopen::ProverSHPLONK, + }, + transcript::{Blake2bRead, TranscriptReadBuffer}, + transcript::{Blake2bWrite, Challenge255, TranscriptWriterBuffer}, +}; +use halo2_base::utils::ScalarField; +use halo2_base::{ + Context, + QuantumCell::{Existing, Witness}, + SKIP_FIRST_PASS, +}; +use itertools::Itertools; +use rand::rngs::OsRng; +use std::marker::PhantomData; + +use criterion::{criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion}; + +use pprof::criterion::{Output, PProfProfiler}; +// Thanks to the example provided by @jebbow in his article +// https://www.jibbow.com/posts/criterion-flamegraphs/ + +const K: u32 = 19; + +fn inner_prod_bench(ctx: &mut Context, a: Vec, b: Vec) { + assert_eq!(a.len(), b.len()); + let a = ctx.assign_witnesses(a); + let b = ctx.assign_witnesses(b); + + let chip = GateChip::default(); + for _ in 0..(1 << K) / 16 - 10 { + chip.inner_product(ctx, a.clone(), b.clone().into_iter().map(Existing)); + } +} + +fn main() { + let k = 10u32; + // create circuit for keygen + let mut builder = GateThreadBuilder::new(false); + inner_prod_bench(builder.main(0), vec![Fr::zero(); 5], vec![Fr::zero(); 5]); + builder.config(k as usize, Some(20)); + let circuit = GateCircuitBuilder::mock(builder); + + // check the circuit is correct just in case + MockProver::run(k, &circuit, vec![]).unwrap().assert_satisfied(); + + let params = ParamsKZG::::setup(k, OsRng); + let vk = keygen_vk(¶ms, &circuit).expect("vk should not fail"); + let pk = keygen_pk(¶ms, vk, &circuit).expect("pk should not fail"); + + let break_points = circuit.break_points.take(); + + let mut builder = GateThreadBuilder::new(true); + let a = (0..5).map(|_| Fr::random(OsRng)).collect_vec(); + let b = (0..5).map(|_| Fr::random(OsRng)).collect_vec(); + inner_prod_bench(builder.main(0), a, b); + let circuit = GateCircuitBuilder::prover(builder, break_points); + + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof::< + KZGCommitmentScheme, + ProverSHPLONK<'_, Bn256>, + Challenge255, + _, + Blake2bWrite, G1Affine, Challenge255<_>>, + _, + >(¶ms, &pk, &[circuit], &[&[]], OsRng, &mut transcript) + .expect("prover should not fail"); + + let strategy = SingleStrategy::new(¶ms); + let proof = transcript.finalize(); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + verify_proof::< + KZGCommitmentScheme, + VerifierSHPLONK<'_, Bn256>, + Challenge255, + Blake2bRead<&[u8], G1Affine, Challenge255>, + _, + >(¶ms, pk.get_vk(), strategy, &[&[]], &mut transcript) + .unwrap(); +} \ No newline at end of file diff --git a/halo2-base/src/gates/builder.rs b/halo2-base/src/gates/builder.rs new file mode 100644 index 00000000..07f80e47 --- /dev/null +++ b/halo2-base/src/gates/builder.rs @@ -0,0 +1,601 @@ +//! halo2-lib v0.3.0 file. Not useful for current version. +//! Code included to minimize diffs between 0.2.2 and 0.3.0 +use super::{ + flex_gate::{FlexGateConfig, GateStrategy, MAX_PHASE}, + range::{RangeConfig, RangeStrategy}, +}; +use crate::{ + halo2_proofs::{ + circuit::{self, Layouter, Region, SimpleFloorPlanner, Value}, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Selector}, + }, + utils::ScalarField, + Context, SKIP_FIRST_PASS, +}; +use serde::{Deserialize, Serialize}; +use std::{ + cell::RefCell, + collections::{HashMap, HashSet}, +}; + +pub type ThreadBreakPoints = Vec; +pub type MultiPhaseThreadBreakPoints = Vec; + +#[derive(Clone, Debug, Default)] +pub struct KeygenAssignments { + pub assigned_advices: HashMap<(usize, usize), (circuit::Cell, usize)>, // (key = ContextCell, value = (circuit::Cell, row offset)) + pub assigned_constants: HashMap, // (key = constant, value = circuit::Cell) + pub break_points: MultiPhaseThreadBreakPoints, +} + +#[derive(Clone, Debug, Default)] +pub struct GateThreadBuilder { + /// Threads for each challenge phase + pub threads: [Vec>; MAX_PHASE], + thread_count: usize, + witness_gen_only: bool, + use_unknown: bool, +} + +impl GateThreadBuilder { + pub fn new(witness_gen_only: bool) -> Self { + let mut threads = [(); MAX_PHASE].map(|_| vec![]); + // start with a main thread in phase 0 + threads[0].push(Context::new(witness_gen_only, 0)); + Self { threads, thread_count: 1, witness_gen_only, use_unknown: false } + } + + pub fn mock() -> Self { + Self::new(false) + } + + pub fn keygen() -> Self { + Self::new(false) + } + + pub fn prover() -> Self { + Self::new(true) + } + + pub fn unknown(self, use_unknown: bool) -> Self { + Self { use_unknown, ..self } + } + + pub fn main(&mut self, phase: usize) -> &mut Context { + if self.threads[phase].is_empty() { + self.new_thread(phase) + } else { + self.threads[phase].last_mut().unwrap() + } + } + + pub fn witness_gen_only(&self) -> bool { + self.witness_gen_only + } + + pub fn use_unknown(&self) -> bool { + self.use_unknown + } + + pub fn thread_count(&self) -> usize { + self.thread_count + } + + pub fn get_new_thread_id(&mut self) -> usize { + let thread_id = self.thread_count; + self.thread_count += 1; + thread_id + } + + pub fn new_thread(&mut self, phase: usize) -> &mut Context { + let thread_id = self.thread_count; + self.thread_count += 1; + self.threads[phase].push(Context::new(self.witness_gen_only, thread_id)); + self.threads[phase].last_mut().unwrap() + } + + /// Auto-calculate configuration parameters for the circuit + pub fn config(&self, k: usize, minimum_rows: Option) -> FlexGateConfigParams { + let max_rows = (1 << k) - minimum_rows.unwrap_or(0); + let total_advice_per_phase = self + .threads + .iter() + .map(|threads| threads.iter().map(|ctx| ctx.advice.len()).sum::()) + .collect::>(); + // we do a rough estimate by taking ceil(advice_cells_per_phase / 2^k ) + // if this is too small, manual configuration will be needed + let num_advice_per_phase = total_advice_per_phase + .iter() + .map(|count| (count + max_rows - 1) / max_rows) + .collect::>(); + + let total_lookup_advice_per_phase = self + .threads + .iter() + .map(|threads| threads.iter().map(|ctx| ctx.cells_to_lookup.len()).sum::()) + .collect::>(); + let num_lookup_advice_per_phase = total_lookup_advice_per_phase + .iter() + .map(|count| (count + max_rows - 1) / max_rows) + .collect::>(); + + let total_fixed: usize = HashSet::::from_iter(self.threads.iter().flat_map(|threads| { + threads.iter().flat_map(|ctx| ctx.constant_equality_constraints.iter().map(|(c, _)| *c)) + })) + .len(); + let num_fixed = (total_fixed + (1 << k) - 1) >> k; + + let params = FlexGateConfigParams { + strategy: GateStrategy::Vertical, + num_advice_per_phase, + num_lookup_advice_per_phase, + num_fixed, + k, + }; + #[cfg(feature = "display")] + { + for phase in 0..MAX_PHASE { + if total_advice_per_phase[phase] != 0 || total_lookup_advice_per_phase[phase] != 0 { + println!( + "Gate Chip | Phase {}: {} advice cells , {} lookup advice cells", + phase, total_advice_per_phase[phase], total_lookup_advice_per_phase[phase], + ); + } + } + println!("Total {total_fixed} fixed cells"); + println!("Auto-calculated config params:\n {params:#?}"); + } + std::env::set_var("FLEX_GATE_CONFIG_PARAMS", serde_json::to_string(¶ms).unwrap()); + params + } + + /// Assigns all advice and fixed cells, turns on selectors, imposes equality constraints. + /// This should only be called during keygen. + pub fn assign_all( + &self, + config: &FlexGateConfig, + lookup_advice: &[Vec>], + q_lookup: &[Option], + region: &mut Region, + KeygenAssignments { + mut assigned_advices, + mut assigned_constants, + mut break_points + }: KeygenAssignments, + ) -> KeygenAssignments { + assert!(!self.witness_gen_only); + let use_unknown = self.use_unknown; + let max_rows = config.max_rows; + let mut fixed_col = 0; + let mut fixed_offset = 0; + for (phase, threads) in self.threads.iter().enumerate() { + let mut break_point = vec![]; + let mut gate_index = 0; + let mut row_offset = 0; + for ctx in threads { + let mut basic_gate = config.basic_gates[phase] + .get(gate_index) + .unwrap_or_else(|| panic!("NOT ENOUGH ADVICE COLUMNS IN PHASE {phase}. Perhaps blinding factors were not taken into account. The max non-poisoned rows is {max_rows}")); + assert_eq!(ctx.selector.len(), ctx.advice.len()); + + for (i, (advice, &q)) in ctx.advice.iter().zip(ctx.selector.iter()).enumerate() { + let column = basic_gate.value; + let value = if use_unknown { Value::unknown() } else { Value::known(advice) }; + #[cfg(feature = "halo2-axiom")] + let cell = *region.assign_advice(column, row_offset, value).cell(); + #[cfg(not(feature = "halo2-axiom"))] + let cell = region + .assign_advice(|| "", column, row_offset, || value.map(|v| *v)) + .unwrap() + .cell(); + assigned_advices.insert((ctx.context_id, i), (cell, row_offset)); + + if (q && row_offset + 4 > max_rows) || row_offset >= max_rows - 1 { + break_point.push(row_offset); + row_offset = 0; + gate_index += 1; + + // when there is a break point, because we may have two gates that overlap at the current cell, we must copy the current cell to the next column for safety + basic_gate = config.basic_gates[phase] + .get(gate_index) + .unwrap_or_else(|| panic!("NOT ENOUGH ADVICE COLUMNS IN PHASE {phase}. Perhaps blinding factors were not taken into account. The max non-poisoned rows is {max_rows}")); + let column = basic_gate.value; + + #[cfg(feature = "halo2-axiom")] + { + let ncell = region.assign_advice(column, row_offset, value); + region.constrain_equal(ncell.cell(), &cell); + } + #[cfg(not(feature = "halo2-axiom"))] + { + let ncell = region + .assign_advice(|| "", column, row_offset, || value.map(|v| *v)) + .unwrap() + .cell(); + region.constrain_equal(ncell, cell).unwrap(); + } + } + + if q { + basic_gate + .q_enable + .enable(region, row_offset) + .expect("enable selector should not fail"); + } + + row_offset += 1; + } + for (c, _) in ctx.constant_equality_constraints.iter() { + if assigned_constants.get(c).is_none() { + #[cfg(feature = "halo2-axiom")] + let cell = + region.assign_fixed(config.constants[fixed_col], fixed_offset, c); + #[cfg(not(feature = "halo2-axiom"))] + let cell = region + .assign_fixed( + || "", + config.constants[fixed_col], + fixed_offset, + || Value::known(*c), + ) + .unwrap() + .cell(); + assigned_constants.insert(*c, cell); + fixed_col += 1; + if fixed_col >= config.constants.len() { + fixed_col = 0; + fixed_offset += 1; + } + } + } + } + break_points.push(break_point); + } + // we constrain equality constraints in a separate loop in case context `i` contains references to context `j` for `j > i` + for (phase, threads) in self.threads.iter().enumerate() { + let mut lookup_offset = 0; + let mut lookup_col = 0; + for ctx in threads { + for (left, right) in &ctx.advice_equality_constraints { + let (left, _) = assigned_advices[&(left.context_id, left.offset)]; + let (right, _) = assigned_advices[&(right.context_id, right.offset)]; + #[cfg(feature = "halo2-axiom")] + region.constrain_equal(&left, &right); + #[cfg(not(feature = "halo2-axiom"))] + region.constrain_equal(left, right).unwrap(); + } + for (left, right) in &ctx.constant_equality_constraints { + let left = assigned_constants[left]; + let (right, _) = assigned_advices[&(right.context_id, right.offset)]; + #[cfg(feature = "halo2-axiom")] + region.constrain_equal(&left, &right); + #[cfg(not(feature = "halo2-axiom"))] + region.constrain_equal(left, right).unwrap(); + } + + for advice in &ctx.cells_to_lookup { + // if q_lookup is Some, that means there should be a single advice column and it has lookup enabled + let cell = advice.cell.unwrap(); + let (acell, row_offset) = assigned_advices[&(cell.context_id, cell.offset)]; + if let Some(q_lookup) = q_lookup[phase] { + assert_eq!(config.basic_gates[phase].len(), 1); + q_lookup.enable(region, row_offset).unwrap(); + continue; + } + // otherwise, we copy the advice value to the special lookup_advice columns + if lookup_offset >= max_rows { + lookup_offset = 0; + lookup_col += 1; + } + let value = advice.value; + let value = if use_unknown { Value::unknown() } else { Value::known(value) }; + let column = lookup_advice[phase][lookup_col]; + + #[cfg(feature = "halo2-axiom")] + { + let bcell = region.assign_advice(column, lookup_offset, value); + region.constrain_equal(&acell, bcell.cell()); + } + #[cfg(not(feature = "halo2-axiom"))] + { + let bcell = region + .assign_advice(|| "", column, lookup_offset, || value) + .expect("assign_advice should not fail") + .cell(); + region.constrain_equal(acell, bcell).unwrap(); + } + lookup_offset += 1; + } + } + } + KeygenAssignments { assigned_advices, assigned_constants, break_points } + } +} + +/// Pure advice witness assignment in a single phase. Uses preprocessed `break_points` to determine when +/// to split a thread into a new column. +pub fn assign_threads_in( + phase: usize, + threads: Vec>, + config: &FlexGateConfig, + lookup_advice: &[Column], + region: &mut Region, + break_points: ThreadBreakPoints, +) { + if config.basic_gates[phase].is_empty() { + assert!(threads.is_empty(), "Trying to assign threads in a phase with no columns"); + return; + } + + let mut break_points = break_points.into_iter(); + let mut break_point = break_points.next(); + + let mut gate_index = 0; + let mut column = config.basic_gates[phase][gate_index].value; + let mut row_offset = 0; + + let mut lookup_offset = 0; + let mut lookup_advice = lookup_advice.iter(); + let mut lookup_column = lookup_advice.next(); + for ctx in threads { + // if lookup_column is empty, that means there should be a single advice column and it has lookup enabled, so we don't need to copy to special lookup advice columns + if lookup_column.is_some() { + for advice in ctx.cells_to_lookup { + if lookup_offset >= config.max_rows { + lookup_offset = 0; + lookup_column = lookup_advice.next(); + } + let value = advice.value; + let lookup_column = *lookup_column.unwrap(); + #[cfg(feature = "halo2-axiom")] + region.assign_advice(lookup_column, lookup_offset, Value::known(value)); + #[cfg(not(feature = "halo2-axiom"))] + region + .assign_advice(|| "", lookup_column, lookup_offset, || Value::known(value)) + .unwrap(); + + lookup_offset += 1; + } + } + for advice in ctx.advice { + #[cfg(feature = "halo2-axiom")] + region.assign_advice(column, row_offset, Value::known(advice)); + #[cfg(not(feature = "halo2-axiom"))] + region.assign_advice(|| "", column, row_offset, || Value::known(advice)).unwrap(); + + if break_point == Some(row_offset) { + break_point = break_points.next(); + row_offset = 0; + gate_index += 1; + column = config.basic_gates[phase][gate_index].value; + + #[cfg(feature = "halo2-axiom")] + region.assign_advice(column, row_offset, Value::known(advice)); + #[cfg(not(feature = "halo2-axiom"))] + region.assign_advice(|| "", column, row_offset, || Value::known(advice)).unwrap(); + } + + row_offset += 1; + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct FlexGateConfigParams { + pub strategy: GateStrategy, + pub k: usize, + pub num_advice_per_phase: Vec, + pub num_lookup_advice_per_phase: Vec, + pub num_fixed: usize, +} + +/// A wrapper struct to auto-build a circuit from a `GateThreadBuilder`. +#[derive(Clone, Debug)] +pub struct GateCircuitBuilder { + pub builder: RefCell>, // `RefCell` is just to trick circuit `synthesize` to take ownership of the inner builder + pub break_points: RefCell, // `RefCell` allows the circuit to record break points in a keygen call of `synthesize` for use in later witness gen +} + +impl GateCircuitBuilder { + pub fn keygen(builder: GateThreadBuilder) -> Self { + Self { builder: RefCell::new(builder.unknown(true)), break_points: RefCell::new(vec![]) } + } + + pub fn mock(builder: GateThreadBuilder) -> Self { + Self { builder: RefCell::new(builder.unknown(false)), break_points: RefCell::new(vec![]) } + } + + pub fn prover( + builder: GateThreadBuilder, + break_points: MultiPhaseThreadBreakPoints, + ) -> Self { + Self { builder: RefCell::new(builder), break_points: RefCell::new(break_points) } + } +} + +impl Circuit for GateCircuitBuilder { + type Config = FlexGateConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> FlexGateConfig { + let FlexGateConfigParams { + strategy, + num_advice_per_phase, + num_lookup_advice_per_phase: _, + num_fixed, + k, + } = serde_json::from_str(&std::env::var("FLEX_GATE_CONFIG_PARAMS").unwrap()).unwrap(); + FlexGateConfig::configure(meta, strategy, &num_advice_per_phase, num_fixed, k) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let mut first_pass = SKIP_FIRST_PASS; + layouter.assign_region( + || "GateCircuitBuilder generated circuit", + |mut region| { + if first_pass { + first_pass = false; + return Ok(()); + } + // only support FirstPhase in this Builder because getting challenge value requires more specialized witness generation during synthesize + if !self.builder.borrow().witness_gen_only { + // clone the builder so we can re-use the circuit for both vk and pk gen + let builder = self.builder.borrow().clone(); + for threads in builder.threads.iter().skip(1) { + assert!( + threads.is_empty(), + "GateCircuitBuilder only supports FirstPhase for now" + ); + } + *self.break_points.borrow_mut() = builder + .assign_all(&config, &[], &[], &mut region, Default::default()) + .break_points; + } else { + let builder = self.builder.take(); + let break_points = self.break_points.take(); + for (phase, (threads, break_points)) in builder + .threads + .into_iter() + .zip(break_points.into_iter()) + .enumerate() + .take(1) + { + assign_threads_in(phase, threads, &config, &[], &mut region, break_points); + } + } + Ok(()) + }, + ) + } +} + +/// A wrapper struct to auto-build a circuit from a `GateThreadBuilder`. +#[derive(Clone, Debug)] +pub struct RangeCircuitBuilder(pub GateCircuitBuilder); + +impl RangeCircuitBuilder { + pub fn keygen(builder: GateThreadBuilder) -> Self { + Self(GateCircuitBuilder::keygen(builder)) + } + + pub fn mock(builder: GateThreadBuilder) -> Self { + Self(GateCircuitBuilder::mock(builder)) + } + + pub fn prover( + builder: GateThreadBuilder, + break_points: MultiPhaseThreadBreakPoints, + ) -> Self { + Self(GateCircuitBuilder::prover(builder, break_points)) + } +} + +impl Circuit for RangeCircuitBuilder { + type Config = RangeConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let FlexGateConfigParams { + strategy, + num_advice_per_phase, + num_lookup_advice_per_phase, + num_fixed, + k, + } = serde_json::from_str(&std::env::var("FLEX_GATE_CONFIG_PARAMS").unwrap()).unwrap(); + let strategy = match strategy { + GateStrategy::Vertical => RangeStrategy::Vertical, + }; + let lookup_bits = std::env::var("LOOKUP_BITS").unwrap().parse().unwrap(); + RangeConfig::configure( + meta, + strategy, + &num_advice_per_phase, + &num_lookup_advice_per_phase, + num_fixed, + lookup_bits, + k, + ) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + config.load_lookup_table(&mut layouter).expect("load lookup table should not fail"); + + let mut first_pass = SKIP_FIRST_PASS; + layouter.assign_region( + || "RangeCircuitBuilder generated circuit", + |mut region| { + if first_pass { + first_pass = false; + return Ok(()); + } + // only support FirstPhase in this Builder because getting challenge value requires more specialized witness generation during synthesize + if !self.0.builder.borrow().witness_gen_only { + // clone the builder so we can re-use the circuit for both vk and pk gen + let builder = self.0.builder.borrow().clone(); + for threads in builder.threads.iter().skip(1) { + assert!( + threads.is_empty(), + "GateCircuitBuilder only supports FirstPhase for now" + ); + } + *self.0.break_points.borrow_mut() = builder + .assign_all( + &config.gate, + &config.lookup_advice, + &config.q_lookup, + &mut region, + Default::default(), + ) + .break_points; + } else { + #[cfg(feature = "display")] + let start0 = std::time::Instant::now(); + let builder = self.0.builder.take(); + let break_points = self.0.break_points.take(); + for (phase, (threads, break_points)) in builder + .threads + .into_iter() + .zip(break_points.into_iter()) + .enumerate() + .take(1) + { + assign_threads_in( + phase, + threads, + &config.gate, + &config.lookup_advice[phase], + &mut region, + break_points, + ); + } + #[cfg(feature = "display")] + println!("assign threads in {:?}", start0.elapsed()); + } + Ok(()) + }, + ) + } +} + +#[derive(Clone, Copy, Debug)] +pub enum CircuitBuilderStage { + Keygen, + Prover, + Mock, +} \ No newline at end of file diff --git a/halo2-base/src/gates/flex_gate.rs b/halo2-base/src/gates/flex_gate.rs index fdbd8652..c8c1cb1d 100644 --- a/halo2-base/src/gates/flex_gate.rs +++ b/halo2-base/src/gates/flex_gate.rs @@ -1,7 +1,3 @@ -use super::{ - AssignedValue, Context, GateInstructions, - QuantumCell::{self, Constant, Existing, Witness}, -}; use crate::halo2_proofs::{ circuit::Value, plonk::{ @@ -11,6 +7,10 @@ use crate::halo2_proofs::{ poly::Rotation, }; use crate::utils::ScalarField; +use crate::{ + AssignedValue, Context, + QuantumCell::{self, Constant, Existing, Witness, WitnessFraction}, +}; use itertools::Itertools; use std::{ iter::{self, once}, @@ -147,7 +147,7 @@ impl FlexGateConfig { } let mut pow_of_two = Vec::with_capacity(F::NUM_BITS as usize); let two = F::from(2); - pow_of_two.push(F::one()); + pow_of_two.push(F::ONE); pow_of_two.push(two); for _ in 2..F::NUM_BITS { pow_of_two.push(two * pow_of_two.last().unwrap()); @@ -173,7 +173,7 @@ impl FlexGateConfig { strategy, gate_len: 4, context_id, - /// Warning: this needs to be updated if you create more advice columns after this `FlexGateConfig` is created + // Warning: this needs to be updated if you create more advice columns after this `FlexGateConfig` is created max_rows: (1 << circuit_degree) - meta.minimum_rows(), pow_of_two, field_element_cache, @@ -182,24 +182,24 @@ impl FlexGateConfig { } } - pub fn inner_product_simple<'a, 'b: 'a>( + pub fn inner_product_simple<'a>( &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> AssignedValue<'b, F> { + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> AssignedValue { let mut sum; let mut a = a.into_iter(); let mut b = b.into_iter().peekable(); - let cells = if matches!(b.peek(), Some(Constant(c)) if c == &F::one()) { + let cells = if matches!(b.peek(), Some(Constant(c)) if c == &F::ONE) { b.next(); let start_a = a.next().unwrap(); sum = start_a.value().copied(); iter::once(start_a) } else { - sum = Value::known(F::zero()); - iter::once(Constant(F::zero())) + sum = Value::known(F::ZERO); + iter::once(Constant(F::ZERO)) } .chain(a.zip(b).flat_map(|(a, b)| { sum = sum + a.value().zip(b.value()).map(|(a, b)| *a * b); @@ -213,24 +213,24 @@ impl FlexGateConfig { self.assign_region_last(ctx, cells, gate_offsets) } - pub fn inner_product_simple_with_assignments<'a, 'b: 'a>( + pub fn inner_product_simple_with_assignments<'a>( &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> (Vec>, AssignedValue<'b, F>) { + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> (Vec>, AssignedValue) { let mut sum; let mut a = a.into_iter(); let mut b = b.into_iter().peekable(); - let cells = if matches!(b.peek(), Some(Constant(c)) if c == &F::one()) { + let cells = if matches!(b.peek(), Some(Constant(c)) if c == &F::ONE) { b.next(); let start_a = a.next().unwrap(); sum = start_a.value().copied(); iter::once(start_a) } else { - sum = Value::known(F::zero()); - iter::once(Constant(F::zero())) + sum = Value::known(F::ZERO); + iter::once(Constant(F::ZERO)) } .chain(a.zip(b).flat_map(|(a, b)| { sum = sum + a.value().zip(b.value()).map(|(a, b)| *a * b); @@ -248,10 +248,10 @@ impl FlexGateConfig { fn inner_product_with_assignments<'a, 'b: 'a>( &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> (Vec>, AssignedValue<'b, F>) { + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> (Vec>, AssignedValue) { // we will do special handling of the cases where one of the vectors is all constants match self.strategy { GateStrategy::PlonkPlus => { @@ -274,7 +274,7 @@ impl FlexGateConfig { // we effect a small optimization if we know the constant b0 == 1: then instead of starting from 0 we can start from a0 // this is a peculiarity of our plonk-plus gate - let start_ida: usize = (vec_b[0] == F::one()).into(); + let start_ida: usize = (vec_b[0] == F::ONE).into(); if start_ida == 1 && k == 1 { // this is just a0 * 1 = a0; you're doing nothing, why are you calling this function? return (vec![], self.assign_region_last(ctx, vec_a, vec![])); @@ -283,14 +283,14 @@ impl FlexGateConfig { let mut cells = Vec::with_capacity(1 + (gate_segment + 1) * k_chunks); let mut gate_offsets = Vec::with_capacity(k_chunks); let mut running_sum = - if start_ida == 1 { vec_a[0].clone() } else { Constant(F::zero()) }; + if start_ida == 1 { vec_a[0].clone() } else { Constant(F::ZERO) }; cells.push(running_sum.clone()); for i in 0..k_chunks { let window = (start_ida + i * gate_segment) ..std::cmp::min(k, start_ida + (i + 1) * gate_segment); // we add a 0 at the start for q_mul = 0 - let mut c_window = [&[F::zero()], &vec_b[window.clone()]].concat(); - c_window.extend((c_window.len()..(gate_segment + 1)).map(|_| F::zero())); + let mut c_window = [&[F::ZERO], &vec_b[window.clone()]].concat(); + c_window.extend((c_window.len()..(gate_segment + 1)).map(|_| F::ZERO)); // c_window should have length gate_segment + 1 gate_offsets.push(( (i * (gate_segment + 1)) as isize, @@ -298,7 +298,7 @@ impl FlexGateConfig { )); cells.extend(window.clone().map(|j| vec_a[j].clone())); - cells.extend((window.len()..gate_segment).map(|_| Constant(F::zero()))); + cells.extend((window.len()..gate_segment).map(|_| Constant(F::ZERO))); running_sum = Witness( window.into_iter().fold(running_sum.value().copied(), |sum, j| { sum + Value::known(vec_b[j]) * vec_a[j].value() @@ -320,6 +320,672 @@ impl FlexGateConfig { } } +pub trait GateInstructions { + fn strategy(&self) -> GateStrategy; + fn context_id(&self) -> usize; + + fn pow_of_two(&self) -> &[F]; + fn get_field_element(&self, n: u64) -> F; + + fn assign_region<'a>( + &self, + ctx: &mut Context, + inputs: impl IntoIterator>, + gate_offsets: impl IntoIterator)>, + ) -> Vec> { + self.assign_region_in(ctx, inputs, gate_offsets, ctx.current_phase()) + } + + fn assign_region_in<'a>( + &self, + ctx: &mut Context, + inputs: impl IntoIterator>, + gate_offsets: impl IntoIterator)>, + phase: usize, + ) -> Vec>; + + /// Only returns the last assigned cell + /// + /// Does not collect the vec, saving heap allocation + fn assign_region_last<'a>( + &self, + ctx: &mut Context, + inputs: impl IntoIterator>, + gate_offsets: impl IntoIterator)>, + ) -> AssignedValue { + self.assign_region_last_in(ctx, inputs, gate_offsets, ctx.current_phase()) + } + + fn assign_region_last_in<'a>( + &self, + ctx: &mut Context, + inputs: impl IntoIterator>, + gate_offsets: impl IntoIterator)>, + phase: usize, + ) -> AssignedValue; + + /// Only call this if ctx.region is not in shape mode, i.e., if not using simple layouter or ctx.first_pass = false + /// + /// All indices in `gate_offsets`, `equality_offsets`, `external_equality` are with respect to `inputs` indices + /// - `gate_offsets` specifies indices to enable selector for the gate; assume `gate_offsets` is sorted in increasing order + /// - `equality_offsets` specifies pairs of indices to constrain equality + /// - `external_equality` specifies an existing cell to constrain equality with the cell at a certain index + fn assign_region_smart<'a>( + &self, + ctx: &mut Context, + inputs: impl IntoIterator>, + gate_offsets: impl IntoIterator, + equality_offsets: impl IntoIterator, + external_equality: Vec<(&AssignedValue, usize)>, + ) -> Vec> { + let assignments = + self.assign_region(ctx, inputs, gate_offsets.into_iter().map(|i| (i as isize, None))); + for (offset1, offset2) in equality_offsets.into_iter() { + ctx.region + .constrain_equal(assignments[offset1].cell(), assignments[offset2].cell()) + .unwrap(); + } + for (assigned, eq_offset) in external_equality.into_iter() { + ctx.region.constrain_equal(assigned.cell(), assignments[eq_offset].cell()).unwrap(); + } + assignments + } + + fn assign_witnesses( + &self, + ctx: &mut Context, + witnesses: impl IntoIterator>, + ) -> Vec> { + self.assign_region(ctx, witnesses.into_iter().map(Witness), []) + } + + fn load_witness(&self, ctx: &mut Context, witness: Value) -> AssignedValue { + self.assign_region_last(ctx, [Witness(witness)], []) + } + + fn load_constant(&self, ctx: &mut Context, c: F) -> AssignedValue { + self.assign_region_last(ctx, [Constant(c)], []) + } + + fn load_zero(&self, ctx: &mut Context) -> AssignedValue { + if let Some(zcell) = &ctx.zero_cell { + return zcell.clone(); + } + let zero_cell = self.assign_region_last(ctx, [Constant(F::ZERO)], []); + ctx.zero_cell = Some(zero_cell.clone()); + zero_cell + } + + /// Copies a, b and constrains `a + b * 1 = out` + // | a | b | 1 | a + b | + fn add( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + let out_val = a.value().zip(b.value()).map(|(a, b)| *a + b); + self.assign_region_last( + ctx, + vec![a, b, Constant(F::ONE), Witness(out_val)], + vec![(0, None)], + ) + } + + /// Copies a, b and constrains `a + b * (-1) = out` + // | a - b | b | 1 | a | + fn sub( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + + let out_val = a.value().zip(b.value()).map(|(a, b)| *a - b); + // slightly better to not have to compute -F::ONE since F::ONE is cached + let assigned_cells = self.assign_region( + ctx, + vec![Witness(out_val), b, Constant(F::ONE), a], + vec![(0, None)], + ); + assigned_cells.into_iter().next().unwrap() + } + + // | a | -a | 1 | 0 | + fn neg(&self, ctx: &mut Context, a: impl Into>) -> AssignedValue { + let a = a.into(); + let out_val = a.value().map(|v| -*v); + let assigned_cells = self.assign_region( + ctx, + vec![a, Witness(out_val), Constant(F::ONE), Constant(F::ZERO)], + vec![(0, None)], + ); + assigned_cells.into_iter().nth(1).unwrap() + } + + /// Copies a, b and constrains `0 + a * b = out` + // | 0 | a | b | a * b | + fn mul( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + let out_val = a.value().zip(b.value()).map(|(a, b)| *a * b); + self.assign_region_last( + ctx, + vec![Constant(F::ZERO), a, b, Witness(out_val)], + vec![(0, None)], + ) + } + + /// a * b + c + fn mul_add( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + c: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + let c = c.into(); + let out_val = a.value().zip(b.value()).map(|(a, b)| *a * b) + c.value(); + self.assign_region_last(ctx, vec![c, a, b, Witness(out_val)], vec![(0, None)]) + } + + /// (1 - a) * b = b - a * b + fn mul_not( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + + let out_val = a.value().zip(b.value()).map(|(a, b)| (F::ONE - a) * b); + let assignments = + self.assign_region(ctx, vec![Witness(out_val), a, b.clone(), b], vec![(0, None)]); + ctx.region.constrain_equal(assignments[2].cell(), assignments[3].cell()).unwrap(); + assignments.into_iter().next().unwrap() + } + + /// Constrain x is 0 or 1. + fn assert_bit(&self, ctx: &mut Context, x: AssignedValue) { + self.assign_region_last( + ctx, + [Constant(F::ZERO), Existing(x), Existing(x), Existing(x)], + [(0, None)], + ); + } + + fn div_unsafe( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + + // TODO: if really necessary, make `c` of type `Assigned` + // this would require the API using `Assigned` instead of `F` everywhere, so leave as last resort + let c = a.value().zip(b.value()).map(|(a, b)| b.invert().unwrap() * a); + let assignments = + self.assign_region(ctx, vec![Constant(F::ZERO), Witness(c), b, a], vec![(0, None)]); + assignments.into_iter().nth(1).unwrap() + } + + fn assert_equal(&self, ctx: &mut Context, a: QuantumCell, b: QuantumCell) { + if let (Existing(a), Existing(b)) = (&a, &b) { + ctx.region.constrain_equal(a.cell(), b.cell()).unwrap(); + } else { + self.assign_region_smart( + ctx, + vec![Constant(F::ZERO), a, Constant(F::ONE), b], + vec![0], + vec![], + vec![], + ); + } + } + + fn assert_is_const(&self, ctx: &mut Context, a: &AssignedValue, constant: F) { + let c_cell = ctx.assign_fixed(constant); + #[cfg(feature = "halo2-axiom")] + ctx.region.constrain_equal(a.cell(), &c_cell); + #[cfg(feature = "halo2-pse")] + ctx.region.constrain_equal(a.cell(), c_cell).unwrap(); + } + + /// Returns `(assignments, output)` where `output` is the inner product of `` + /// + /// `assignments` is for internal use + fn inner_product<'a>( + &self, + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> AssignedValue; + + /// very specialized for optimal range check, not for general consumption + /// - `a_assigned` is expected to have capacity a.len() + /// - we re-use `a_assigned` to save memory allocation + fn inner_product_left<'a>( + &self, + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + a_assigned: &mut Vec>, + ) -> AssignedValue; + + /// Returns an iterator with the partial sums `sum_{j=0..=i} a[j] * b[j]`. + fn inner_product_with_sums<'a>( + &self, + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> Box>>; + + fn sum<'a>( + &self, + ctx: &mut Context, + a: impl IntoIterator>, + ) -> AssignedValue { + let mut a = a.into_iter().peekable(); + let start = a.next(); + if start.is_none() { + return self.load_zero(ctx); + } + let start = start.unwrap(); + if a.peek().is_none() { + return self.assign_region_last(ctx, [start], []); + } + let (len, hi) = a.size_hint(); + debug_assert_eq!(Some(len), hi); + + let mut sum = start.value().copied(); + let cells = iter::once(start).chain(a.flat_map(|a| { + sum = sum + a.value(); + [a, Constant(F::ONE), Witness(sum)] + })); + self.assign_region_last(ctx, cells, (0..len).map(|i| (3 * i as isize, None))) + } + + /// Returns the assignment trace where `output[3 * i]` has the running sum `sum_{j=0..=i} a[j]` + fn sum_with_assignments<'a>( + &self, + ctx: &mut Context, + a: impl IntoIterator>, + ) -> Vec> { + let mut a = a.into_iter().peekable(); + let start = a.next(); + if start.is_none() { + return vec![self.load_zero(ctx)]; + } + let start = start.unwrap(); + if a.peek().is_none() { + return self.assign_region(ctx, [start], []); + } + let (len, hi) = a.size_hint(); + debug_assert_eq!(Some(len), hi); + + let mut sum = start.value().copied(); + let cells = iter::once(start).chain(a.flat_map(|a| { + sum = sum + a.value(); + [a, Constant(F::ONE), Witness(sum)] + })); + self.assign_region(ctx, cells, (0..len).map(|i| (3 * i as isize, None))) + } + + // requires b.len() == a.len() + 1 + // returns + // x_i = b_1 * (a_1...a_{i - 1}) + // + b_2 * (a_2...a_{i - 1}) + // + ... + // + b_i + // Returns [x_1, ..., x_{b.len()}] + fn accumulated_product( + &self, + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> Vec> { + let mut b = b.into_iter(); + let mut a = a.into_iter(); + let b_first = b.next(); + if let Some(b_first) = b_first { + let b_first = self.assign_region_last(ctx, [b_first], []); + std::iter::successors(Some(b_first), |&x| { + a.next().zip(b.next()).map(|(a, b)| self.mul_add(ctx, Existing(x), a, b)) + }) + .collect() + } else { + vec![] + } + } + + fn sum_products_with_coeff_and_var<'a, 'b: 'a>( + &self, + ctx: &mut Context, + values: impl IntoIterator, QuantumCell)>, + var: QuantumCell, + ) -> AssignedValue; + + // | 1 - b | 1 | b | 1 | b | a | 1 - b | out | + fn or(&self, ctx: &mut Context, a: QuantumCell, b: QuantumCell) -> AssignedValue { + let not_b_val = b.value().map(|x| F::ONE - x); + let out_val = a.value().zip(b.value()).map(|(a, b)| *a + b) + - a.value().zip(b.value()).map(|(a, b)| *a * b); + let cells = vec![ + Witness(not_b_val), + Constant(F::ONE), + b.clone(), + Constant(F::ONE), + b, + a, + Witness(not_b_val), + Witness(out_val), + ]; + let mut assigned_cells = + self.assign_region_smart(ctx, cells, vec![0, 4], vec![(0, 6), (2, 4)], vec![]); + assigned_cells.pop().unwrap() + } + + // | 0 | a | b | out | + fn and( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + ) -> AssignedValue { + self.mul(ctx, a, b) + } + + fn and_many( + &self, + ctx: &mut Context, + values: impl IntoIterator>, + ) -> AssignedValue { + let mut values = values.into_iter(); + let (len, hi) = values.size_hint(); + // TODO: is is safe to assume that len >= 2 (cc @rohit) + assert!(len >= 2, "at least 2 elements to perform AND operation"); + debug_assert_eq!(Some(len), hi); + + let a = values.next().unwrap(); + let b = values.next().unwrap(); + let and_many_acc = self.and(ctx, a, b); + values.fold(and_many_acc, |acc, b| self.and(ctx, Existing(acc), b)) + } + + fn not(&self, ctx: &mut Context, a: impl Into>) -> AssignedValue { + self.sub(ctx, Constant(F::ONE), a) + } + + fn select( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + sel: impl Into>, + ) -> AssignedValue; + + fn or_and( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + c: impl Into>, + ) -> AssignedValue; + + /// assume bits has boolean values + /// returns vec[idx] with vec[idx] = 1 if and only if bits == idx as a binary number + fn bits_to_indicator( + &self, + ctx: &mut Context, + bits: &[AssignedValue], + ) -> Vec> { + let k = bits.len(); + + let (inv_last_bit, last_bit) = { + let mut assignments = self + .assign_region( + ctx, + vec![ + Witness(bits[k - 1].value().map(|b| F::ONE - b)), + Existing(bits[k - 1]), + Constant(F::ONE), + Constant(F::ONE), + ], + vec![(0, None)], + ) + .into_iter(); + (assignments.next().unwrap(), assignments.next().unwrap()) + }; + let mut indicator = Vec::with_capacity(2 * (1 << k) - 2); + let mut offset = 0; + indicator.push(inv_last_bit); + indicator.push(last_bit); + for (idx, bit) in bits.iter().rev().enumerate().skip(1) { + for old_idx in 0..(1 << idx) { + let inv_prod_val = indicator[offset + old_idx] + .value() + .zip(bit.value()) + .map(|(a, b)| (F::ONE - b) * a); + let inv_prod = self + .assign_region_smart( + ctx, + vec![ + Witness(inv_prod_val), + Existing(indicator[offset + old_idx]), + Existing(*bit), + Existing(indicator[offset + old_idx]), + ], + vec![0], + vec![], + vec![], + ) + .into_iter() + .next() + .unwrap(); + indicator.push(inv_prod); + + let prod = self.mul(ctx, Existing(indicator[offset + old_idx]), Existing(*bit)); + indicator.push(prod); + } + offset += 1 << idx; + } + indicator.split_off((1 << k) - 2) + } + + // returns vec with vec.len() == len such that: + // vec[i] == 1{i == idx} + fn idx_to_indicator( + &self, + ctx: &mut Context, + idx: impl Into>, + len: usize, + ) -> Vec> { + let mut idx = idx.into(); + let ind = self.assign_region( + ctx, + (0..len).map(|i| { + Witness(idx.value().map(|x| { + if x.get_lower_32() == i as u32 { + F::ONE + } else { + F::ZERO + } + })) + }), + vec![], + ); + + // check ind[i] * (i - idx) == 0 + for (i, ind) in ind.iter().enumerate() { + let val = ind.value().zip(idx.value()).map(|(ind, idx)| *ind * idx); + let assignments = self.assign_region( + ctx, + vec![ + Constant(F::ZERO), + Existing(*ind), + idx, + Witness(val), + Constant(-F::from(i as u64)), + Existing(*ind), + Constant(F::ZERO), + ], + vec![(0, None), (3, None)], + ); + // need to use assigned idx after i > 0 so equality constraint holds + idx = Existing(assignments.into_iter().nth(2).unwrap()); + } + ind + } + + // performs inner product on a, indicator + // `indicator` values are all boolean + /// Assumes for witness generation that only one element of `indicator` has non-zero value and that value is `F::ONE`. + fn select_by_indicator( + &self, + ctx: &mut Context, + a: impl IntoIterator>, + indicator: impl IntoIterator>, + ) -> AssignedValue { + let mut sum = Value::known(F::ZERO); + let a = a.into_iter(); + let (len, hi) = a.size_hint(); + debug_assert_eq!(Some(len), hi); + + let cells = + std::iter::once(Constant(F::ZERO)).chain(a.zip(indicator).flat_map(|(a, ind)| { + sum = sum.zip(a.value().zip(ind.value())).map(|(sum, (a, ind))| { + if ind.is_zero_vartime() { + sum + } else { + *a + } + }); + [a, Existing(ind), Witness(sum)] + })); + self.assign_region_last(ctx, cells, (0..len).map(|i| (3 * i as isize, None))) + } + + fn select_from_idx<'a, 'v: 'a>( + &self, + ctx: &mut Context, + cells: impl IntoIterator>, + idx: impl Into>, + ) -> AssignedValue { + let cells = cells.into_iter(); + let (len, hi) = cells.size_hint(); + debug_assert_eq!(Some(len), hi); + + let ind = self.idx_to_indicator(ctx, idx, len); + let out = self.select_by_indicator(ctx, cells, ind); + out + } + + // | out | a | inv | 1 | 0 | a | out | 0 + fn is_zero(&self, ctx: &mut Context, a: &AssignedValue) -> AssignedValue { + let (is_zero, inv) = a + .value() + .map(|x| { + if x.is_zero_vartime() { + (F::ONE, Assigned::Trivial(F::ONE)) + } else { + (F::ZERO, Assigned::Rational(F::ONE, *x)) + } + }) + .unzip(); + + let cells = vec![ + Witness(is_zero), + Existing(*a), + WitnessFraction(inv), + Constant(F::ONE), + Constant(F::ZERO), + Existing(*a), + Witness(is_zero), + Constant(F::ZERO), + ]; + let assigned_cells = self.assign_region_smart(ctx, cells, vec![0, 4], vec![(0, 6)], vec![]); + assigned_cells.into_iter().next().unwrap() + } + + fn is_equal( + &self, + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + ) -> AssignedValue { + let diff = self.sub(ctx, a, b); + self.is_zero(ctx, &diff) + } + + // returns little-endian bit vectors + fn num_to_bits( + &self, + ctx: &mut Context, + a: &AssignedValue, + range_bits: usize, + ) -> Vec>; + + /// given pairs `coords[i] = (x_i, y_i)`, let `f` be the unique degree `len(coords)` polynomial such that `f(x_i) = y_i` for all `i`. + /// + /// input: coords, x + /// + /// output: (f(x), Prod_i (x - x_i)) + /// + /// constrains all x_i and x are distinct + fn lagrange_and_eval( + &self, + ctx: &mut Context, + coords: &[(AssignedValue, AssignedValue)], + x: AssignedValue, + ) -> (AssignedValue, AssignedValue) { + let mut z = self.sub(ctx, Existing(x), Existing(coords[0].0)); + for coord in coords.iter().skip(1) { + let sub = self.sub(ctx, Existing(x), Existing(coord.0)); + z = self.mul(ctx, Existing(z), Existing(sub)); + } + let mut eval = None; + for i in 0..coords.len() { + // compute (x - x_i) * Prod_{j != i} (x_i - x_j) + let mut denom = self.sub(ctx, Existing(x), Existing(coords[i].0)); + for j in 0..coords.len() { + if i == j { + continue; + } + let sub = self.sub(ctx, Existing(coords[i].0), Existing(coords[j].0)); + denom = self.mul(ctx, Existing(denom), Existing(sub)); + } + // TODO: batch inversion + let is_zero = self.is_zero(ctx, &denom); + self.assert_is_const(ctx, &is_zero, F::ZERO); + + // y_i / denom + let quot = self.div_unsafe(ctx, Existing(coords[i].1), Existing(denom)); + eval = if let Some(eval) = eval { + let eval = self.add(ctx, Existing(eval), Existing(quot)); + Some(eval) + } else { + Some(quot) + }; + } + let out = self.mul(ctx, Existing(eval.unwrap()), Existing(z)); + (out, z) + } +} + impl GateInstructions for FlexGateConfig { fn strategy(&self) -> GateStrategy { self.strategy @@ -346,13 +1012,13 @@ impl GateInstructions for FlexGateConfig { /// * gate_index can either be set if you know the specific column you want to assign to, or None if you want to auto-select index /// * only selects from advice columns in `ctx.current_phase` // same as `assign_region` except you can specify the `phase` to assign in - fn assign_region_in<'a, 'b: 'a>( + fn assign_region_in<'a>( &self, - ctx: &mut Context<'_, F>, - inputs: impl IntoIterator>, + ctx: &mut Context, + inputs: impl IntoIterator>, gate_offsets: impl IntoIterator)>, phase: usize, - ) -> Vec> { + ) -> Vec> { // We enforce the pattern that you should assign everything in current phase at once and then move onto next phase debug_assert_eq!(phase, ctx.current_phase()); @@ -398,7 +1064,7 @@ impl GateInstructions for FlexGateConfig { .expect("enable selector should not fail"); if self.strategy == GateStrategy::PlonkPlus { - let q_coeff = q_coeff.unwrap_or([F::one(), F::zero(), F::zero()]); + let q_coeff = q_coeff.unwrap_or([F::ONE, F::ZERO, F::ZERO]); for (j, q_coeff) in q_coeff.into_iter().enumerate() { #[cfg(feature = "halo2-axiom")] { @@ -433,13 +1099,13 @@ impl GateInstructions for FlexGateConfig { assignments } - fn assign_region_last_in<'a, 'b: 'a>( + fn assign_region_last_in<'a>( &self, - ctx: &mut Context<'_, F>, - inputs: impl IntoIterator>, + ctx: &mut Context, + inputs: impl IntoIterator>, gate_offsets: impl IntoIterator)>, phase: usize, - ) -> AssignedValue<'b, F> { + ) -> AssignedValue { // We enforce the pattern that you should assign everything in current phase at once and then move onto next phase debug_assert_eq!(phase, ctx.current_phase()); @@ -484,7 +1150,7 @@ impl GateInstructions for FlexGateConfig { .expect("selector enable should not fail"); if self.strategy == GateStrategy::PlonkPlus { - let q_coeff = q_coeff.unwrap_or([F::one(), F::zero(), F::zero()]); + let q_coeff = q_coeff.unwrap_or([F::ONE, F::ZERO, F::ZERO]); for (j, q_coeff) in q_coeff.into_iter().enumerate() { #[cfg(feature = "halo2-axiom")] { @@ -522,12 +1188,12 @@ impl GateInstructions for FlexGateConfig { // Takes two vectors of `QuantumCell` and constrains a witness output to the inner product of `` // outputs are (assignments except last, out_cell) // Currently the only places `assignments` is used are: `num_to_bits, range_check, carry_mod, check_carry_mod_to_zero` - fn inner_product<'a, 'b: 'a>( + fn inner_product<'a>( &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> AssignedValue<'b, F> { + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> AssignedValue { // we will do special handling of the cases where one of the vectors is all constants match self.strategy { GateStrategy::PlonkPlus => { @@ -538,14 +1204,14 @@ impl GateInstructions for FlexGateConfig { } } - fn inner_product_with_sums<'a, 'b: 'a>( + fn inner_product_with_sums<'a>( &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> Box> + 'b> { + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + ) -> Box>> { let mut b = b.into_iter().peekable(); - let flag = matches!(b.peek(), Some(&Constant(c)) if c == F::one()); + let flag = matches!(b.peek(), Some(&Constant(c)) if c == F::ONE); let (assignments_without_last, last) = self.inner_product_simple_with_assignments(ctx, a, b); if flag { @@ -556,13 +1222,13 @@ impl GateInstructions for FlexGateConfig { } } - fn inner_product_left<'a, 'b: 'a>( + fn inner_product_left<'a>( &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - a_assigned: &mut Vec>, - ) -> AssignedValue<'b, F> { + ctx: &mut Context, + a: impl IntoIterator>, + b: impl IntoIterator>, + a_assigned: &mut Vec>, + ) -> AssignedValue { match self.strategy { GateStrategy::PlonkPlus => { let a = a.into_iter(); @@ -608,12 +1274,12 @@ impl GateInstructions for FlexGateConfig { let mut right_one = false; let start = ctx.assign_cell( - if matches!(b.peek(), Some(&Constant(x)) if x == F::one()) { + if matches!(b.peek(), Some(&Constant(x)) if x == F::ONE) { right_one = true; b.next(); a.next().unwrap() } else { - Constant(F::zero()) + Constant(F::ZERO) }, column, #[cfg(feature = "display")] @@ -666,10 +1332,10 @@ impl GateInstructions for FlexGateConfig { fn sum_products_with_coeff_and_var<'a, 'b: 'a>( &self, - ctx: &mut Context<'_, F>, - values: impl IntoIterator, QuantumCell<'a, 'b, F>)>, - var: QuantumCell<'a, 'b, F>, - ) -> AssignedValue<'b, F> { + ctx: &mut Context, + values: impl IntoIterator, QuantumCell)>, + var: QuantumCell, + ) -> AssignedValue { // TODO: optimize match self.strategy { GateStrategy::PlonkPlus => { @@ -680,18 +1346,18 @@ impl GateInstructions for FlexGateConfig { for (i, (c, a, b)) in values.into_iter().enumerate() { acc = acc + Value::known(c) * a.value() * b.value(); cells.append(&mut vec![a, b, Witness(acc)]); - gate_offsets.push((3 * i as isize, Some([c, F::zero(), F::zero()]))); + gate_offsets.push((3 * i as isize, Some([c, F::ZERO, F::ZERO]))); } self.assign_region_last(ctx, cells, gate_offsets) } GateStrategy::Vertical => { - let (a, b): (Vec<_>, Vec<_>) = std::iter::once((var, Constant(F::one()))) + let (a, b): (Vec<_>, Vec<_>) = std::iter::once((var, Constant(F::ONE))) .chain(values.into_iter().filter_map(|(c, va, vb)| { - if c == F::one() { + if c == F::ONE { Some((va, vb)) - } else if c != F::zero() { + } else if c != F::ZERO { let prod = self.mul(ctx, va, vb); - Some((QuantumCell::ExistingOwned(prod), Constant(c))) + Some((QuantumCell::Existing(prod), Constant(c))) } else { None } @@ -705,13 +1371,17 @@ impl GateInstructions for FlexGateConfig { /// assumes sel is boolean /// returns /// a * sel + b * (1 - sel) - fn select<'v>( + fn select( &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - sel: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + sel: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + let sel = sel.into(); + let diff_val: Value = a.value().zip(b.value()).map(|(a, b)| *a - b); let out_val = diff_val * sel.value() + b.value(); match self.strategy { @@ -720,7 +1390,7 @@ impl GateInstructions for FlexGateConfig { GateStrategy::Vertical => { let cells = vec![ Witness(diff_val), - Constant(F::one()), + Constant(F::ONE), b.clone(), a, b, @@ -740,7 +1410,7 @@ impl GateInstructions for FlexGateConfig { let mut assignments = self.assign_region( ctx, vec![ - Constant(F::zero()), + Constant(F::ZERO), a, Witness(diff_val), b, @@ -748,9 +1418,9 @@ impl GateInstructions for FlexGateConfig { Witness(diff_val), Witness(out_val), ], - vec![(0, Some([F::zero(), F::one(), -F::one()])), (3, None)], + vec![(0, Some([F::ZERO, F::ONE, -F::ONE])), (3, None)], ); - ctx.region.constrain_equal(assignments[2].cell(), assignments[5].cell()); + ctx.region.constrain_equal(assignments[2].cell(), assignments[5].cell()).unwrap(); assignments.pop().unwrap() } } @@ -758,28 +1428,32 @@ impl GateInstructions for FlexGateConfig { /// returns: a || (b && c) // | 1 - b c | b | c | 1 | a - 1 | 1 - b c | out | a - 1 | 1 | 1 | a | - fn or_and<'v>( + fn or_and( &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - c: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: impl Into>, + b: impl Into>, + c: impl Into>, + ) -> AssignedValue { + let a = a.into(); + let b = b.into(); + let c = c.into(); + let bc_val = b.value().zip(c.value()).map(|(b, c)| *b * c); - let not_bc_val = bc_val.map(|x| F::one() - x); - let not_a_val = a.value().map(|x| *x - F::one()); + let not_bc_val = bc_val.map(|x| F::ONE - x); + let not_a_val = a.value().map(|x| *x - F::ONE); let out_val = bc_val + a.value() - bc_val * a.value(); let cells = vec![ Witness(not_bc_val), b, c, - Constant(F::one()), + Constant(F::ONE), Witness(not_a_val), Witness(not_bc_val), Witness(out_val), Witness(not_a_val), - Constant(F::one()), - Constant(F::one()), + Constant(F::ONE), + Constant(F::ONE), a, ]; let assigned_cells = @@ -788,12 +1462,12 @@ impl GateInstructions for FlexGateConfig { } // returns little-endian bit vectors - fn num_to_bits<'v>( + fn num_to_bits( &self, - ctx: &mut Context<'_, F>, - a: &AssignedValue<'v, F>, + ctx: &mut Context, + a: &AssignedValue, range_bits: usize, - ) -> Vec> { + ) -> Vec> { let bits = a .value() .map(|a| { @@ -815,16 +1489,16 @@ impl GateInstructions for FlexGateConfig { self.pow_of_two[..range_bits].iter().map(|c| Constant(*c)), &mut bit_cells, ); - ctx.region.constrain_equal(a.cell(), acc.cell()); + ctx.region.constrain_equal(a.cell(), acc.cell()).unwrap(); for bit_cell in &bit_cells { self.assign_region( ctx, vec![ - Constant(F::zero()), - Existing(bit_cell), - Existing(bit_cell), - Existing(bit_cell), + Constant(F::ZERO), + Existing(*bit_cell), + Existing(*bit_cell), + Existing(*bit_cell), ], vec![(0, None)], ); diff --git a/halo2-base/src/gates/mod.rs b/halo2-base/src/gates/mod.rs index 52706772..00c53fa2 100644 --- a/halo2-base/src/gates/mod.rs +++ b/halo2-base/src/gates/mod.rs @@ -1,864 +1,8 @@ -use self::{flex_gate::GateStrategy, range::RangeStrategy}; -use super::{ - utils::ScalarField, - AssignedValue, Context, - QuantumCell::{self, Constant, Existing, ExistingOwned, Witness, WitnessFraction}, -}; -use crate::{ - halo2_proofs::{circuit::Value, plonk::Assigned}, - utils::{biguint_to_fe, bit_length, fe_to_biguint, PrimeField}, -}; -use core::iter; -use num_bigint::BigUint; -use num_integer::Integer; -use num_traits::{One, Zero}; -use std::ops::Shl; - pub mod flex_gate; pub mod range; -pub trait GateInstructions { - fn strategy(&self) -> GateStrategy; - fn context_id(&self) -> usize; - - fn pow_of_two(&self) -> &[F]; - fn get_field_element(&self, n: u64) -> F; - - fn assign_region<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - inputs: impl IntoIterator>, - gate_offsets: impl IntoIterator)>, - ) -> Vec> { - self.assign_region_in(ctx, inputs, gate_offsets, ctx.current_phase()) - } - - fn assign_region_in<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - inputs: impl IntoIterator>, - gate_offsets: impl IntoIterator)>, - phase: usize, - ) -> Vec>; - - /// Only returns the last assigned cell - /// - /// Does not collect the vec, saving heap allocation - fn assign_region_last<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - inputs: impl IntoIterator>, - gate_offsets: impl IntoIterator)>, - ) -> AssignedValue<'b, F> { - self.assign_region_last_in(ctx, inputs, gate_offsets, ctx.current_phase()) - } - - fn assign_region_last_in<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - inputs: impl IntoIterator>, - gate_offsets: impl IntoIterator)>, - phase: usize, - ) -> AssignedValue<'b, F>; - - /// Only call this if ctx.region is not in shape mode, i.e., if not using simple layouter or ctx.first_pass = false - /// - /// All indices in `gate_offsets`, `equality_offsets`, `external_equality` are with respect to `inputs` indices - /// - `gate_offsets` specifies indices to enable selector for the gate; assume `gate_offsets` is sorted in increasing order - /// - `equality_offsets` specifies pairs of indices to constrain equality - /// - `external_equality` specifies an existing cell to constrain equality with the cell at a certain index - fn assign_region_smart<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - inputs: impl IntoIterator>, - gate_offsets: impl IntoIterator, - equality_offsets: impl IntoIterator, - external_equality: Vec<(&AssignedValue, usize)>, - ) -> Vec> { - let assignments = - self.assign_region(ctx, inputs, gate_offsets.into_iter().map(|i| (i as isize, None))); - for (offset1, offset2) in equality_offsets.into_iter() { - ctx.region.constrain_equal(assignments[offset1].cell(), assignments[offset2].cell()); - } - for (assigned, eq_offset) in external_equality.into_iter() { - ctx.region.constrain_equal(assigned.cell(), assignments[eq_offset].cell()); - } - assignments - } - - fn assign_witnesses<'v>( - &self, - ctx: &mut Context<'_, F>, - witnesses: impl IntoIterator>, - ) -> Vec> { - self.assign_region(ctx, witnesses.into_iter().map(Witness), []) - } - - fn load_witness<'v>( - &self, - ctx: &mut Context<'_, F>, - witness: Value, - ) -> AssignedValue<'v, F> { - self.assign_region_last(ctx, [Witness(witness)], []) - } - - fn load_constant<'a>(&self, ctx: &mut Context<'_, F>, c: F) -> AssignedValue<'a, F> { - self.assign_region_last(ctx, [Constant(c)], []) - } - - fn load_zero<'a>(&self, ctx: &mut Context<'a, F>) -> AssignedValue<'a, F> { - if let Some(zcell) = &ctx.zero_cell { - return zcell.clone(); - } - let zero_cell = self.assign_region_last(ctx, [Constant(F::zero())], []); - ctx.zero_cell = Some(zero_cell.clone()); - zero_cell - } - - /// Copies a, b and constrains `a + b * 1 = out` - // | a | b | 1 | a + b | - fn add<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let out_val = a.value().zip(b.value()).map(|(a, b)| *a + b); - self.assign_region_last( - ctx, - vec![a, b, Constant(F::one()), Witness(out_val)], - vec![(0, None)], - ) - } - - /// Copies a, b and constrains `a + b * (-1) = out` - // | a - b | b | 1 | a | - fn sub<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let out_val = a.value().zip(b.value()).map(|(a, b)| *a - b); - // slightly better to not have to compute -F::one() since F::one() is cached - let assigned_cells = self.assign_region( - ctx, - vec![Witness(out_val), b, Constant(F::one()), a], - vec![(0, None)], - ); - assigned_cells.into_iter().next().unwrap() - } - - // | a | -a | 1 | 0 | - fn neg<'v>(&self, ctx: &mut Context<'_, F>, a: QuantumCell<'_, 'v, F>) -> AssignedValue<'v, F> { - let out_val = a.value().map(|v| -*v); - let assigned_cells = self.assign_region( - ctx, - vec![a, Witness(out_val), Constant(F::one()), Constant(F::zero())], - vec![(0, None)], - ); - assigned_cells.into_iter().nth(1).unwrap() - } - - /// Copies a, b and constrains `0 + a * b = out` - // | 0 | a | b | a * b | - fn mul<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let out_val = a.value().zip(b.value()).map(|(a, b)| *a * b); - self.assign_region_last( - ctx, - vec![Constant(F::zero()), a, b, Witness(out_val)], - vec![(0, None)], - ) - } - - /// a * b + c - fn mul_add<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - c: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let out_val = a.value().zip(b.value()).map(|(a, b)| *a * b) + c.value(); - self.assign_region_last(ctx, vec![c, a, b, Witness(out_val)], vec![(0, None)]) - } - - /// (1 - a) * b = b - a * b - fn mul_not<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let out_val = a.value().zip(b.value()).map(|(a, b)| (F::one() - a) * b); - let assignments = - self.assign_region(ctx, vec![Witness(out_val), a, b.clone(), b], vec![(0, None)]); - ctx.region.constrain_equal(assignments[2].cell(), assignments[3].cell()); - assignments.into_iter().next().unwrap() - } - - /// Constrain x is 0 or 1. - fn assert_bit(&self, ctx: &mut Context<'_, F>, x: &AssignedValue) { - self.assign_region_last( - ctx, - [Constant(F::zero()), Existing(x), Existing(x), Existing(x)], - [(0, None)], - ); - } - - fn div_unsafe<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - // TODO: if really necessary, make `c` of type `Assigned` - // this would require the API using `Assigned` instead of `F` everywhere, so leave as last resort - let c = a.value().zip(b.value()).map(|(a, b)| b.invert().unwrap() * a); - let assignments = - self.assign_region(ctx, vec![Constant(F::zero()), Witness(c), b, a], vec![(0, None)]); - assignments.into_iter().nth(1).unwrap() - } - - fn assert_equal(&self, ctx: &mut Context<'_, F>, a: QuantumCell, b: QuantumCell) { - if let (Existing(a), Existing(b)) = (&a, &b) { - ctx.region.constrain_equal(a.cell(), b.cell()); - } else { - self.assign_region_smart( - ctx, - vec![Constant(F::zero()), a, Constant(F::one()), b], - vec![0], - vec![], - vec![], - ); - } - } - - fn assert_is_const(&self, ctx: &mut Context<'_, F>, a: &AssignedValue, constant: F) { - let c_cell = ctx.assign_fixed(constant); - #[cfg(feature = "halo2-axiom")] - ctx.region.constrain_equal(a.cell(), &c_cell); - #[cfg(feature = "halo2-pse")] - ctx.region.constrain_equal(a.cell(), c_cell).unwrap(); - } - - /// Returns `(assignments, output)` where `output` is the inner product of `` - /// - /// `assignments` is for internal use - fn inner_product<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> AssignedValue<'b, F>; - - /// very specialized for optimal range check, not for general consumption - /// - `a_assigned` is expected to have capacity a.len() - /// - we re-use `a_assigned` to save memory allocation - fn inner_product_left<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - a_assigned: &mut Vec>, - ) -> AssignedValue<'b, F>; - - /// Returns an iterator with the partial sums `sum_{j=0..=i} a[j] * b[j]`. - fn inner_product_with_sums<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> Box> + 'b>; - - fn sum<'a, 'b: 'a>( - &self, - ctx: &mut Context<'b, F>, - a: impl IntoIterator>, - ) -> AssignedValue<'b, F> { - let mut a = a.into_iter().peekable(); - let start = a.next(); - if start.is_none() { - return self.load_zero(ctx); - } - let start = start.unwrap(); - if a.peek().is_none() { - return self.assign_region_last(ctx, [start], []); - } - let (len, hi) = a.size_hint(); - debug_assert_eq!(Some(len), hi); - - let mut sum = start.value().copied(); - let cells = iter::once(start).chain(a.flat_map(|a| { - sum = sum + a.value(); - [a, Constant(F::one()), Witness(sum)] - })); - self.assign_region_last(ctx, cells, (0..len).map(|i| (3 * i as isize, None))) - } - - /// Returns the assignment trace where `output[3 * i]` has the running sum `sum_{j=0..=i} a[j]` - fn sum_with_assignments<'a, 'b: 'a>( - &self, - ctx: &mut Context<'b, F>, - a: impl IntoIterator>, - ) -> Vec> { - let mut a = a.into_iter().peekable(); - let start = a.next(); - if start.is_none() { - return vec![self.load_zero(ctx)]; - } - let start = start.unwrap(); - if a.peek().is_none() { - return self.assign_region(ctx, [start], []); - } - let (len, hi) = a.size_hint(); - debug_assert_eq!(Some(len), hi); - - let mut sum = start.value().copied(); - let cells = iter::once(start).chain(a.flat_map(|a| { - sum = sum + a.value(); - [a, Constant(F::one()), Witness(sum)] - })); - self.assign_region(ctx, cells, (0..len).map(|i| (3 * i as isize, None))) - } - - // requires b.len() == a.len() + 1 - // returns - // x_i = b_1 * (a_1...a_{i - 1}) - // + b_2 * (a_2...a_{i - 1}) - // + ... - // + b_i - // Returns [x_1, ..., x_{b.len()}] - fn accumulated_product<'a, 'v: 'a>( - &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - b: impl IntoIterator>, - ) -> Vec> { - let mut b = b.into_iter(); - let mut a = a.into_iter(); - let b_first = b.next(); - if let Some(b_first) = b_first { - let b_first = self.assign_region_last(ctx, [b_first], []); - std::iter::successors(Some(b_first), |x| { - a.next().zip(b.next()).map(|(a, b)| self.mul_add(ctx, Existing(x), a, b)) - }) - .collect() - } else { - vec![] - } - } - - fn sum_products_with_coeff_and_var<'a, 'b: 'a>( - &self, - ctx: &mut Context<'_, F>, - values: impl IntoIterator, QuantumCell<'a, 'b, F>)>, - var: QuantumCell<'a, 'b, F>, - ) -> AssignedValue<'b, F>; - - // | 1 - b | 1 | b | 1 | b | a | 1 - b | out | - fn or<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let not_b_val = b.value().map(|x| F::one() - x); - let out_val = a.value().zip(b.value()).map(|(a, b)| *a + b) - - a.value().zip(b.value()).map(|(a, b)| *a * b); - let cells = vec![ - Witness(not_b_val), - Constant(F::one()), - b.clone(), - Constant(F::one()), - b, - a, - Witness(not_b_val), - Witness(out_val), - ]; - let mut assigned_cells = - self.assign_region_smart(ctx, cells, vec![0, 4], vec![(0, 6), (2, 4)], vec![]); - assigned_cells.pop().unwrap() - } - - // | 0 | a | b | out | - fn and<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - self.mul(ctx, a, b) - } - - fn not<'v>(&self, ctx: &mut Context<'_, F>, a: QuantumCell<'_, 'v, F>) -> AssignedValue<'v, F> { - self.sub(ctx, Constant(F::one()), a) - } - - fn select<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - sel: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F>; - - fn or_and<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - c: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F>; - - /// assume bits has boolean values - /// returns vec[idx] with vec[idx] = 1 if and only if bits == idx as a binary number - fn bits_to_indicator<'v>( - &self, - ctx: &mut Context<'_, F>, - bits: &[AssignedValue<'v, F>], - ) -> Vec> { - let k = bits.len(); - - let (inv_last_bit, last_bit) = { - let mut assignments = self - .assign_region( - ctx, - vec![ - Witness(bits[k - 1].value().map(|b| F::one() - b)), - Existing(&bits[k - 1]), - Constant(F::one()), - Constant(F::one()), - ], - vec![(0, None)], - ) - .into_iter(); - (assignments.next().unwrap(), assignments.next().unwrap()) - }; - let mut indicator = Vec::with_capacity(2 * (1 << k) - 2); - let mut offset = 0; - indicator.push(inv_last_bit); - indicator.push(last_bit); - for (idx, bit) in bits.iter().rev().enumerate().skip(1) { - for old_idx in 0..(1 << idx) { - let inv_prod_val = indicator[offset + old_idx] - .value() - .zip(bit.value()) - .map(|(a, b)| (F::one() - b) * a); - let inv_prod = self - .assign_region_smart( - ctx, - vec![ - Witness(inv_prod_val), - Existing(&indicator[offset + old_idx]), - Existing(bit), - Existing(&indicator[offset + old_idx]), - ], - vec![0], - vec![], - vec![], - ) - .into_iter() - .next() - .unwrap(); - indicator.push(inv_prod); - - let prod = self.mul(ctx, Existing(&indicator[offset + old_idx]), Existing(bit)); - indicator.push(prod); - } - offset += 1 << idx; - } - indicator.split_off((1 << k) - 2) - } - - // returns vec with vec.len() == len such that: - // vec[i] == 1{i == idx} - fn idx_to_indicator<'v>( - &self, - ctx: &mut Context<'_, F>, - mut idx: QuantumCell<'_, 'v, F>, - len: usize, - ) -> Vec> { - let ind = self.assign_region( - ctx, - (0..len).map(|i| { - Witness(idx.value().map(|x| { - if x.get_lower_32() == i as u32 { - F::one() - } else { - F::zero() - } - })) - }), - vec![], - ); - - // check ind[i] * (i - idx) == 0 - for (i, ind) in ind.iter().enumerate() { - let val = ind.value().zip(idx.value()).map(|(ind, idx)| *ind * idx); - let assignments = self.assign_region( - ctx, - vec![ - Constant(F::zero()), - Existing(ind), - idx, - Witness(val), - Constant(-F::from(i as u64)), - Existing(ind), - Constant(F::zero()), - ], - vec![(0, None), (3, None)], - ); - // need to use assigned idx after i > 0 so equality constraint holds - idx = ExistingOwned(assignments.into_iter().nth(2).unwrap()); - } - ind - } - - // performs inner product on a, indicator - // `indicator` values are all boolean - /// Assumes for witness generation that only one element of `indicator` has non-zero value and that value is `F::one()`. - fn select_by_indicator<'a, 'i, 'b: 'a + 'i>( - &self, - ctx: &mut Context<'_, F>, - a: impl IntoIterator>, - indicator: impl IntoIterator>, - ) -> AssignedValue<'b, F> { - let mut sum = Value::known(F::zero()); - let a = a.into_iter(); - let (len, hi) = a.size_hint(); - debug_assert_eq!(Some(len), hi); - - let cells = - std::iter::once(Constant(F::zero())).chain(a.zip(indicator).flat_map(|(a, ind)| { - sum = sum.zip(a.value().zip(ind.value())).map(|(sum, (a, ind))| { - if ind.is_zero_vartime() { - sum - } else { - *a - } - }); - [a, Existing(ind), Witness(sum)] - })); - self.assign_region_last(ctx, cells, (0..len).map(|i| (3 * i as isize, None))) - } - - fn select_from_idx<'a, 'v: 'a>( - &self, - ctx: &mut Context<'_, F>, - cells: impl IntoIterator>, - idx: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let cells = cells.into_iter(); - let (len, hi) = cells.size_hint(); - debug_assert_eq!(Some(len), hi); - - let ind = self.idx_to_indicator(ctx, idx, len); - let out = self.select_by_indicator(ctx, cells, &ind); - out - } - - // | out | a | inv | 1 | 0 | a | out | 0 - fn is_zero<'v>( - &self, - ctx: &mut Context<'_, F>, - a: &AssignedValue<'v, F>, - ) -> AssignedValue<'v, F> { - let (is_zero, inv) = a - .value() - .map(|x| { - if x.is_zero_vartime() { - (F::one(), Assigned::Trivial(F::one())) - } else { - (F::zero(), Assigned::Rational(F::one(), *x)) - } - }) - .unzip(); - - let cells = vec![ - Witness(is_zero), - Existing(a), - WitnessFraction(inv), - Constant(F::one()), - Constant(F::zero()), - Existing(a), - Witness(is_zero), - Constant(F::zero()), - ]; - let assigned_cells = self.assign_region_smart(ctx, cells, vec![0, 4], vec![(0, 6)], vec![]); - assigned_cells.into_iter().next().unwrap() - } - - fn is_equal<'v>( - &self, - ctx: &mut Context<'_, F>, - a: QuantumCell<'_, 'v, F>, - b: QuantumCell<'_, 'v, F>, - ) -> AssignedValue<'v, F> { - let diff = self.sub(ctx, a, b); - self.is_zero(ctx, &diff) - } - - // returns little-endian bit vectors - fn num_to_bits<'v>( - &self, - ctx: &mut Context<'_, F>, - a: &AssignedValue<'v, F>, - range_bits: usize, - ) -> Vec>; - - /// given pairs `coords[i] = (x_i, y_i)`, let `f` be the unique degree `len(coords)` polynomial such that `f(x_i) = y_i` for all `i`. - /// - /// input: coords, x - /// - /// output: (f(x), Prod_i (x - x_i)) - /// - /// constrains all x_i and x are distinct - fn lagrange_and_eval<'v>( - &self, - ctx: &mut Context<'_, F>, - coords: &[(AssignedValue<'v, F>, AssignedValue<'v, F>)], - x: &AssignedValue<'v, F>, - ) -> (AssignedValue<'v, F>, AssignedValue<'v, F>) { - let mut z = self.sub(ctx, Existing(x), Existing(&coords[0].0)); - for coord in coords.iter().skip(1) { - let sub = self.sub(ctx, Existing(x), Existing(&coord.0)); - z = self.mul(ctx, Existing(&z), Existing(&sub)); - } - let mut eval = None; - for i in 0..coords.len() { - // compute (x - x_i) * Prod_{j != i} (x_i - x_j) - let mut denom = self.sub(ctx, Existing(x), Existing(&coords[i].0)); - for j in 0..coords.len() { - if i == j { - continue; - } - let sub = self.sub(ctx, Existing(&coords[i].0), Existing(&coords[j].0)); - denom = self.mul(ctx, Existing(&denom), Existing(&sub)); - } - // TODO: batch inversion - let is_zero = self.is_zero(ctx, &denom); - self.assert_is_const(ctx, &is_zero, F::zero()); - - // y_i / denom - let quot = self.div_unsafe(ctx, Existing(&coords[i].1), Existing(&denom)); - eval = if let Some(eval) = eval { - let eval = self.add(ctx, Existing(&eval), Existing(")); - Some(eval) - } else { - Some(quot) - }; - } - let out = self.mul(ctx, Existing(&eval.unwrap()), Existing(&z)); - (out, z) - } -} - -pub trait RangeInstructions { - type Gate: GateInstructions; - - fn gate(&self) -> &Self::Gate; - fn strategy(&self) -> RangeStrategy; - - fn lookup_bits(&self) -> usize; - - fn range_check<'a>( - &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, - range_bits: usize, - ); - - fn check_less_than<'a>( - &self, - ctx: &mut Context<'a, F>, - a: QuantumCell<'_, 'a, F>, - b: QuantumCell<'_, 'a, F>, - num_bits: usize, - ); - - /// Checks that `a` is in `[0, b)`. - /// - /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `bit_length(b)` bits. - fn check_less_than_safe<'a>(&self, ctx: &mut Context<'a, F>, a: &AssignedValue<'a, F>, b: u64) { - let range_bits = - (bit_length(b) + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); - - self.range_check(ctx, a, range_bits); - self.check_less_than( - ctx, - Existing(a), - Constant(self.gate().get_field_element(b)), - range_bits, - ) - } - - /// Checks that `a` is in `[0, b)`. - /// - /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `bit_length(b)` bits. - fn check_big_less_than_safe<'a>( - &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, - b: BigUint, - ) where - F: PrimeField, - { - let range_bits = - (b.bits() as usize + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); - - self.range_check(ctx, a, range_bits); - self.check_less_than(ctx, Existing(a), Constant(biguint_to_fe(&b)), range_bits) - } - - /// Returns whether `a` is in `[0, b)`. - /// - /// Warning: This may fail silently if `a` or `b` have more than `num_bits` bits - fn is_less_than<'a>( - &self, - ctx: &mut Context<'a, F>, - a: QuantumCell<'_, 'a, F>, - b: QuantumCell<'_, 'a, F>, - num_bits: usize, - ) -> AssignedValue<'a, F>; - - /// Returns whether `a` is in `[0, b)`. - /// - /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `range_bits` bits. - fn is_less_than_safe<'a>( - &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, - b: u64, - ) -> AssignedValue<'a, F> { - let range_bits = - (bit_length(b) + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); - - self.range_check(ctx, a, range_bits); - self.is_less_than(ctx, Existing(a), Constant(F::from(b)), range_bits) - } - - /// Returns whether `a` is in `[0, b)`. - /// - /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `range_bits` bits. - fn is_big_less_than_safe<'a>( - &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, - b: BigUint, - ) -> AssignedValue<'a, F> - where - F: PrimeField, - { - let range_bits = - (b.bits() as usize + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); - - self.range_check(ctx, a, range_bits); - self.is_less_than(ctx, Existing(a), Constant(biguint_to_fe(&b)), range_bits) - } - - /// Returns `(c, r)` such that `a = b * c + r`. - /// - /// Assumes that `b != 0`. - fn div_mod<'a>( - &self, - ctx: &mut Context<'a, F>, - a: QuantumCell<'_, 'a, F>, - b: impl Into, - a_num_bits: usize, - ) -> (AssignedValue<'a, F>, AssignedValue<'a, F>) - where - F: PrimeField, - { - let b = b.into(); - let mut a_val = BigUint::zero(); - a.value().map(|v| a_val = fe_to_biguint(v)); - let (div, rem) = a_val.div_mod_floor(&b); - let [div, rem] = [div, rem].map(|v| biguint_to_fe(&v)); - let assigned = self.gate().assign_region( - ctx, - vec![ - Witness(Value::known(rem)), - Constant(biguint_to_fe(&b)), - Witness(Value::known(div)), - a, - ], - vec![(0, None)], - ); - self.check_big_less_than_safe( - ctx, - &assigned[2], - BigUint::one().shl(a_num_bits as u32) / &b + BigUint::one(), - ); - self.check_big_less_than_safe(ctx, &assigned[0], b); - (assigned[2].clone(), assigned[0].clone()) - } - - /// Returns `(c, r)` such that `a = b * c + r`. - /// - /// Assumes that `b != 0`. - /// - /// Let `X = 2 ** b_num_bits`. - /// Write `a = a1 * X + a0` and `c = c1 * X + c0`. - /// If we write `b * c0 + r = d1 * X + d0` then - /// `b * c + r = (b * c1 + d1) * X + d0` - fn div_mod_var<'a>( - &self, - ctx: &mut Context<'a, F>, - a: QuantumCell<'_, 'a, F>, - b: QuantumCell<'_, 'a, F>, - a_num_bits: usize, - b_num_bits: usize, - ) -> (AssignedValue<'a, F>, AssignedValue<'a, F>) - where - F: PrimeField, - { - let mut a_val = BigUint::zero(); - a.value().map(|v| a_val = fe_to_biguint(v)); - let mut b_val = BigUint::one(); - b.value().map(|v| b_val = fe_to_biguint(v)); - let (div, rem) = a_val.div_mod_floor(&b_val); - let x = BigUint::one().shl(b_num_bits as u32); - let (div_hi, div_lo) = div.div_mod_floor(&x); - - let x_fe = self.gate().pow_of_two()[b_num_bits]; - let [div, div_hi, div_lo, rem] = [div, div_hi, div_lo, rem].map(|v| biguint_to_fe(&v)); - let assigned = self.gate().assign_region( - ctx, - vec![ - Witness(Value::known(div_lo)), - Witness(Value::known(div_hi)), - Constant(x_fe), - Witness(Value::known(div)), - Witness(Value::known(rem)), - ], - vec![(0, None)], - ); - self.range_check(ctx, &assigned[0], b_num_bits); - self.range_check(ctx, &assigned[1], a_num_bits.saturating_sub(b_num_bits)); - - let (bcr0_hi, bcr0_lo) = { - let bcr0 = - self.gate().mul_add(ctx, b.clone(), Existing(&assigned[0]), Existing(&assigned[4])); - self.div_mod(ctx, Existing(&bcr0), x.clone(), a_num_bits) - }; - let bcr_hi = - self.gate().mul_add(ctx, b.clone(), Existing(&assigned[1]), Existing(&bcr0_hi)); - - let (a_hi, a_lo) = self.div_mod(ctx, a, x, a_num_bits); - ctx.constrain_equal(&bcr_hi, &a_hi); - ctx.constrain_equal(&bcr0_lo, &a_lo); - - self.range_check(ctx, &assigned[4], b_num_bits); - self.check_less_than(ctx, Existing(&assigned[4]), b, b_num_bits); - (assigned[3].clone(), assigned[4].clone()) - } -} +pub use flex_gate::GateInstructions; +pub use range::RangeInstructions; #[cfg(test)] pub mod tests; diff --git a/halo2-base/src/gates/range.rs b/halo2-base/src/gates/range.rs index 07033ee7..40cf3985 100644 --- a/halo2-base/src/gates/range.rs +++ b/halo2-base/src/gates/range.rs @@ -1,3 +1,5 @@ +use crate::utils::{biguint_to_fe, bit_length, fe_to_biguint}; +use crate::Context; use crate::{ gates::{ flex_gate::{FlexGateConfig, GateStrategy, MAX_PHASE}, @@ -15,11 +17,13 @@ use crate::{ }, poly::Rotation, }, - utils::PrimeField, + utils::BigPrimeField, }; +use num_bigint::BigUint; +use num_integer::Integer; +use num_traits::{One, Zero}; use std::cmp::Ordering; - -use super::{Context, RangeInstructions}; +use std::ops::Shl; #[derive(Clone, Copy, Debug, PartialEq)] pub enum RangeStrategy { @@ -43,7 +47,7 @@ pub struct RangeConfig { pub q_lookup: Vec>, pub lookup: TableColumn, pub lookup_bits: usize, - pub limb_bases: Vec>, + pub limb_bases: Vec>, // selector for custom range gate // `q_range[k][i]` stores the selector for a custom range gate of length `k` // pub q_range: HashMap>, @@ -105,7 +109,7 @@ impl RangeConfig { let mut running_base = limb_base; let num_bases = F::NUM_BITS as usize / lookup_bits; let mut limb_bases = Vec::with_capacity(num_bases + 1); - limb_bases.extend([Constant(F::one()), Constant(running_base)]); + limb_bases.extend([Constant(F::ONE), Constant(running_base)]); for _ in 2..=num_bases { running_base *= &limb_base; limb_bases.push(Constant(running_base)); @@ -167,14 +171,14 @@ impl RangeConfig { /// Call this at the end of a phase to assign cells to special columns for lookup arguments /// /// returns total number of lookup cells assigned - pub fn finalize(&self, ctx: &mut Context<'_, F>) -> usize { + pub fn finalize(&self, ctx: &mut Context) -> usize { ctx.copy_and_lookup_cells(self.lookup_advice[ctx.current_phase].clone()) } /// assuming this is called when ctx.region is not in shape mode /// `offset` is the offset of the cell in `ctx.region` /// `offset` is only used if there is a single advice column - fn enable_lookup<'a>(&self, ctx: &mut Context<'a, F>, acell: AssignedValue<'a, F>) { + fn enable_lookup(&self, ctx: &mut Context, acell: AssignedValue) { let phase = ctx.current_phase(); if let Some(q) = &self.q_lookup[phase] { q.enable(&mut ctx.region, acell.row()).expect("enable selector should not fail"); @@ -184,12 +188,12 @@ impl RangeConfig { } // returns the limbs - fn range_check_simple<'a>( + fn range_check_simple( &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, + ctx: &mut Context, + a: &AssignedValue, range_bits: usize, - limbs_assigned: &mut Vec>, + limbs_assigned: &mut Vec>, ) { let k = (range_bits + self.lookup_bits - 1) / self.lookup_bits; // println!("range check {} bits {} len", range_bits, k); @@ -220,7 +224,7 @@ impl RangeConfig { ), }; // the inner product above must equal `a` - ctx.region.constrain_equal(a.cell(), acc.cell()); + ctx.region.constrain_equal(a.cell(), acc.cell()).unwrap(); }; assert_eq!(limbs_assigned.len(), k); @@ -235,15 +239,15 @@ impl RangeConfig { // we constrain x*(x-1) = 0 + x * x - x == 0 // | 0 | x | x | x | Ordering::Equal => { - self.gate.assert_bit(ctx, &limbs_assigned[k - 1]); + self.gate.assert_bit(ctx, limbs_assigned[k - 1]); } Ordering::Greater => { let mult_val = self.gate.get_field_element(1u64 << (self.lookup_bits - rem_bits)); let check = self.gate.assign_region_last( ctx, vec![ - Constant(F::zero()), - Existing(&limbs_assigned[k - 1]), + Constant(F::ZERO), + Existing(limbs_assigned[k - 1]), Constant(mult_val), Witness(limbs_assigned[k - 1].value().map(|limb| mult_val * limb)), ], @@ -258,14 +262,14 @@ impl RangeConfig { /// breaks up `a` into smaller pieces to lookup and stores them in `limbs_assigned` /// /// this is an internal function to avoid memory re-allocation of `limbs_assigned` - pub fn range_check_limbs<'a>( + pub fn range_check_limbs( &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, + ctx: &mut Context, + a: &AssignedValue, range_bits: usize, - limbs_assigned: &mut Vec>, + limbs_assigned: &mut Vec>, ) { - assert_ne!(range_bits, 0); + debug_assert_ne!(range_bits, 0); #[cfg(feature = "display")] { let key = format!( @@ -283,26 +287,26 @@ impl RangeConfig { } /// assume `a` has been range checked already to `limb_bits` bits - pub fn get_last_bit<'a>( + pub fn get_last_bit( &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, + ctx: &mut Context, + a: &AssignedValue, limb_bits: usize, - ) -> AssignedValue<'a, F> { + ) -> AssignedValue { let a_v = a.value(); let bit_v = a_v.map(|a| { let a = a.get_lower_32(); if a ^ 1 == 0 { - F::zero() + F::ZERO } else { - F::one() + F::ONE } }); let two = self.gate.get_field_element(2u64); let h_v = a.value().zip(bit_v).map(|(a, b)| (*a - b) * two.invert().unwrap()); let assignments = self.gate.assign_region_smart( ctx, - vec![Witness(bit_v), Witness(h_v), Constant(two), Existing(a)], + vec![Witness(bit_v), Witness(h_v), Constant(two), Existing(*a)], vec![0], vec![], vec![], @@ -313,6 +317,197 @@ impl RangeConfig { } } +pub trait RangeInstructions { + type Gate: GateInstructions; + + fn gate(&self) -> &Self::Gate; + fn strategy(&self) -> RangeStrategy; + + fn lookup_bits(&self) -> usize; + + fn range_check<'a>(&self, ctx: &mut Context<'a, F>, a: &AssignedValue, range_bits: usize); + + fn check_less_than<'a>( + &self, + ctx: &mut Context<'a, F>, + a: QuantumCell, + b: QuantumCell, + num_bits: usize, + ); + + /// Checks that `a` is in `[0, b)`. + /// + /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `bit_length(b)` bits. + fn check_less_than_safe(&self, ctx: &mut Context, a: &AssignedValue, b: u64) { + let range_bits = + (bit_length(b) + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); + + self.range_check(ctx, a, range_bits); + self.check_less_than( + ctx, + Existing(*a), + Constant(self.gate().get_field_element(b)), + range_bits, + ) + } + + /// Checks that `a` is in `[0, b)`. + /// + /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `bit_length(b)` bits. + fn check_big_less_than_safe(&self, ctx: &mut Context, a: &AssignedValue, b: BigUint) + where + F: BigPrimeField, + { + let range_bits = + (b.bits() as usize + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); + + self.range_check(ctx, a, range_bits); + self.check_less_than(ctx, Existing(*a), Constant(biguint_to_fe(&b)), range_bits) + } + + /// Returns whether `a` is in `[0, b)`. + /// + /// Warning: This may fail silently if `a` or `b` have more than `num_bits` bits + fn is_less_than( + &self, + ctx: &mut Context, + a: QuantumCell, + b: QuantumCell, + num_bits: usize, + ) -> AssignedValue; + + /// Returns whether `a` is in `[0, b)`. + /// + /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `range_bits` bits. + fn is_less_than_safe( + &self, + ctx: &mut Context, + a: AssignedValue, + b: u64, + ) -> AssignedValue { + let range_bits = + (bit_length(b) + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); + + self.range_check(ctx, &a, range_bits); + self.is_less_than(ctx, Existing(a), Constant(F::from(b)), range_bits) + } + + /// Returns whether `a` is in `[0, b)`. + /// + /// Does not require bit assumptions on `a, b` because we range check that `a` has at most `range_bits` bits. + fn is_big_less_than_safe<'a>( + &self, + ctx: &mut Context<'a, F>, + a: &AssignedValue, + b: BigUint, + ) -> AssignedValue + where + F: BigPrimeField, + { + let range_bits = + (b.bits() as usize + self.lookup_bits() - 1) / self.lookup_bits() * self.lookup_bits(); + + self.range_check(ctx, a, range_bits); + self.is_less_than(ctx, Existing(*a), Constant(biguint_to_fe(&b)), range_bits) + } + + /// Returns `(c, r)` such that `a = b * c + r`. + /// + /// Assumes that `b != 0`. + fn div_mod( + &self, + ctx: &mut Context, + a: QuantumCell, + b: impl Into, + a_num_bits: usize, + ) -> (AssignedValue, AssignedValue) + where + F: BigPrimeField, + { + let b = b.into(); + let mut a_val = BigUint::zero(); + a.value().map(|v| a_val = fe_to_biguint(v)); + let (div, rem) = a_val.div_mod_floor(&b); + let [div, rem] = [div, rem].map(|v| biguint_to_fe(&v)); + let assigned = self.gate().assign_region( + ctx, + vec![ + Witness(Value::known(rem)), + Constant(biguint_to_fe(&b)), + Witness(Value::known(div)), + a, + ], + vec![(0, None)], + ); + self.check_big_less_than_safe( + ctx, + &assigned[2], + BigUint::one().shl(a_num_bits as u32) / &b + BigUint::one(), + ); + self.check_big_less_than_safe(ctx, &assigned[0], b); + (assigned[2].clone(), assigned[0].clone()) + } + + /// Returns `(c, r)` such that `a = b * c + r`. + /// + /// Assumes that `b != 0`. + /// + /// Let `X = 2 ** b_num_bits`. + /// Write `a = a1 * X + a0` and `c = c1 * X + c0`. + /// If we write `b * c0 + r = d1 * X + d0` then + /// `b * c + r = (b * c1 + d1) * X + d0` + fn div_mod_var<'a>( + &self, + ctx: &mut Context<'a, F>, + a: QuantumCell, + b: QuantumCell, + a_num_bits: usize, + b_num_bits: usize, + ) -> (AssignedValue, AssignedValue) + where + F: BigPrimeField, + { + let mut a_val = BigUint::zero(); + a.value().map(|v| a_val = fe_to_biguint(v)); + let mut b_val = BigUint::one(); + b.value().map(|v| b_val = fe_to_biguint(v)); + let (div, rem) = a_val.div_mod_floor(&b_val); + let x = BigUint::one().shl(b_num_bits as u32); + let (div_hi, div_lo) = div.div_mod_floor(&x); + + let x_fe = self.gate().pow_of_two()[b_num_bits]; + let [div, div_hi, div_lo, rem] = [div, div_hi, div_lo, rem].map(|v| biguint_to_fe(&v)); + let assigned = self.gate().assign_region( + ctx, + vec![ + Witness(Value::known(div_lo)), + Witness(Value::known(div_hi)), + Constant(x_fe), + Witness(Value::known(div)), + Witness(Value::known(rem)), + ], + vec![(0, None)], + ); + self.range_check(ctx, &assigned[0], b_num_bits); + self.range_check(ctx, &assigned[1], a_num_bits.saturating_sub(b_num_bits)); + + let (bcr0_hi, bcr0_lo) = { + let bcr0 = + self.gate().mul_add(ctx, b.clone(), Existing(assigned[0]), Existing(assigned[4])); + self.div_mod(ctx, Existing(bcr0), x.clone(), a_num_bits) + }; + let bcr_hi = self.gate().mul_add(ctx, b.clone(), Existing(assigned[1]), Existing(bcr0_hi)); + + let (a_hi, a_lo) = self.div_mod(ctx, a, x, a_num_bits); + ctx.constrain_equal(&bcr_hi, &a_hi); + ctx.constrain_equal(&bcr0_lo, &a_lo); + + self.range_check(ctx, &assigned[4], b_num_bits); + self.check_less_than(ctx, Existing(assigned[4]), b, b_num_bits); + (assigned[3].clone(), assigned[4].clone()) + } +} + impl RangeInstructions for RangeConfig { type Gate = FlexGateConfig; @@ -327,22 +522,17 @@ impl RangeInstructions for RangeConfig { self.lookup_bits } - fn range_check<'a>( - &self, - ctx: &mut Context<'a, F>, - a: &AssignedValue<'a, F>, - range_bits: usize, - ) { + fn range_check(&self, ctx: &mut Context, a: &AssignedValue, range_bits: usize) { let tmp = ctx.preallocated_vec_to_assign(); self.range_check_limbs(ctx, a, range_bits, &mut tmp.as_ref().borrow_mut()); } /// Warning: This may fail silently if a or b have more than num_bits - fn check_less_than<'a>( + fn check_less_than( &self, - ctx: &mut Context<'a, F>, - a: QuantumCell<'_, 'a, F>, - b: QuantumCell<'_, 'a, F>, + ctx: &mut Context, + a: QuantumCell, + b: QuantumCell, num_bits: usize, ) { let pow_of_two = self.gate.pow_of_two[num_bits]; @@ -353,10 +543,10 @@ impl RangeInstructions for RangeConfig { let cells = vec![ Witness(shift_a_val - b.value()), b, - Constant(F::one()), + Constant(F::ONE), Witness(shift_a_val), Constant(-pow_of_two), - Constant(F::one()), + Constant(F::ONE), a, ]; let assigned_cells = @@ -371,8 +561,8 @@ impl RangeInstructions for RangeConfig { let out_val = Value::known(pow_of_two) + a.value() - b.value(); let assigned_cells = self.gate.assign_region( ctx, - vec![a, Constant(F::one()), b, Witness(out_val)], - vec![(0, Some([F::zero(), pow_of_two, -F::one()]))], + vec![a, Constant(F::ONE), b, Witness(out_val)], + vec![(0, Some([F::ZERO, pow_of_two, -F::ONE]))], ); assigned_cells.into_iter().nth(3).unwrap() } @@ -382,13 +572,13 @@ impl RangeInstructions for RangeConfig { } /// Warning: This may fail silently if a or b have more than num_bits - fn is_less_than<'a>( + fn is_less_than( &self, - ctx: &mut Context<'a, F>, - a: QuantumCell<'_, 'a, F>, - b: QuantumCell<'_, 'a, F>, + ctx: &mut Context, + a: QuantumCell, + b: QuantumCell, num_bits: usize, - ) -> AssignedValue<'a, F> { + ) -> AssignedValue { // TODO: optimize this for PlonkPlus strategy let k = (num_bits + self.lookup_bits - 1) / self.lookup_bits; let padded_bits = k * self.lookup_bits; @@ -403,10 +593,10 @@ impl RangeInstructions for RangeConfig { vec![ Witness(shifted_val), b, - Constant(F::one()), + Constant(F::ONE), Witness(shift_a_val), Constant(-pow_padded), - Constant(F::one()), + Constant(F::ONE), a, ], vec![0, 3], @@ -418,7 +608,7 @@ impl RangeInstructions for RangeConfig { RangeStrategy::PlonkPlus => self.gate.assign_region_last( ctx, vec![a, Constant(pow_padded), b, Witness(shifted_val)], - vec![(0, Some([F::zero(), F::one(), -F::one()]))], + vec![(0, Some([F::ZERO, F::ONE, -F::ONE]))], ), }; diff --git a/halo2-base/src/gates/tests.rs b/halo2-base/src/gates/tests.rs index c4e811a3..83e1730f 100644 --- a/halo2-base/src/gates/tests.rs +++ b/halo2-base/src/gates/tests.rs @@ -75,17 +75,17 @@ impl Circuit for MyCircuit { // test add { - config.add(ctx, Existing(&a_cell), Existing(&b_cell)); + config.add(ctx, Existing(a_cell), Existing(b_cell)); } // test sub { - config.sub(ctx, Existing(&a_cell), Existing(&b_cell)); + config.sub(ctx, Existing(a_cell), Existing(b_cell)); } // test multiply { - config.mul(ctx, Existing(&c_cell), Existing(&b_cell)); + config.mul(ctx, Existing(c_cell), Existing(b_cell)); } // test idx_to_indicator @@ -243,16 +243,16 @@ impl Circuit for RangeTestCircuit { config.range_check(ctx, &a, self.range_bits); } { - config.check_less_than(ctx, Existing(&a), Existing(&b), self.lt_bits); + config.check_less_than(ctx, Existing(a), Existing(b), self.lt_bits); } { - config.is_less_than(ctx, Existing(&a), Existing(&b), self.lt_bits); + config.is_less_than(ctx, Existing(a), Existing(b), self.lt_bits); } { - config.is_less_than(ctx, Existing(&b), Existing(&a), self.lt_bits); + config.is_less_than(ctx, Existing(b), Existing(a), self.lt_bits); } { - config.gate().is_equal(ctx, Existing(&b), Existing(&a)); + config.gate().is_equal(ctx, Existing(b), Existing(a)); } { config.gate().is_zero(ctx, &a); @@ -386,7 +386,7 @@ mod lagrange { config.lagrange_and_eval( ctx, &x.into_iter().zip(y.into_iter()).collect::>(), - &a, + a, ); #[cfg(feature = "display")] diff --git a/halo2-base/src/lib.rs b/halo2-base/src/lib.rs index 13fb664d..37f4eb97 100644 --- a/halo2-base/src/lib.rs +++ b/halo2-base/src/lib.rs @@ -31,7 +31,7 @@ pub use halo2_proofs; pub use halo2_proofs_axiom as halo2_proofs; use halo2_proofs::{ - circuit::{AssignedCell, Cell, Region, Value}, + circuit::{Cell, Region, Value}, plonk::{Advice, Assigned, Column, Fixed}, }; use rustc_hash::FxHashMap; @@ -50,19 +50,17 @@ pub const SKIP_FIRST_PASS: bool = false; pub const SKIP_FIRST_PASS: bool = true; #[derive(Clone, Debug)] -pub enum QuantumCell<'a, 'b: 'a, F: ScalarField> { - Existing(&'a AssignedValue<'b, F>), - ExistingOwned(AssignedValue<'b, F>), // this is similar to the Cow enum +pub enum QuantumCell { + Existing(AssignedValue), Witness(Value), WitnessFraction(Value>), Constant(F), } -impl QuantumCell<'_, '_, F> { +impl QuantumCell { pub fn value(&self) -> Value<&F> { match self { Self::Existing(a) => a.value(), - Self::ExistingOwned(a) => a.value(), Self::Witness(a) => a.as_ref(), Self::WitnessFraction(_) => { panic!("Trying to get value of a fraction before batch inversion") @@ -72,8 +70,8 @@ impl QuantumCell<'_, '_, F> { } } -#[derive(Clone, Debug)] -pub struct AssignedValue<'a, F: ScalarField> { +#[derive(Clone, Debug, Copy)] +pub struct AssignedValue { #[cfg(feature = "halo2-axiom")] pub cell: AssignedCell<&'a Assigned, F>, @@ -83,14 +81,12 @@ pub struct AssignedValue<'a, F: ScalarField> { pub value: Value, #[cfg(feature = "halo2-pse")] pub row_offset: usize, - #[cfg(feature = "halo2-pse")] - pub _marker: PhantomData<&'a F>, #[cfg(feature = "display")] pub context_id: usize, } -impl<'a, F: ScalarField> AssignedValue<'a, F> { +impl<'a, F: ScalarField> AssignedValue { #[cfg(feature = "display")] pub fn context_id(&self) -> usize { self.context_id @@ -148,7 +144,7 @@ impl<'a, F: ScalarField> AssignedValue<'a, F> { #[cfg(feature = "halo2-pse")] pub fn copy_advice( - &'a self, + &self, region: &mut Region<'_, F>, column: Column, offset: usize, @@ -185,7 +181,7 @@ pub struct Context<'a, F: ScalarField> { // To save time from re-allocating new temporary vectors that get quickly dropped (e.g., for some range checks), we keep a vector with high capacity around that we `clear` before use each time // Need to use RefCell to avoid borrow rules // Need to use Rc to borrow this and mutably borrow self at same time - preallocated_vec_to_assign: Rc>>>, + preallocated_vec_to_assign: Rc>>>, // `assigned_constants` is a HashMap keeping track of all constants that we use throughout // we assign them to fixed columns as we go, re-using a fixed cell if the constant value has been assigned previously @@ -199,10 +195,10 @@ pub struct Context<'a, F: ScalarField> { #[cfg(feature = "halo2-pse")] pub assigned_constants: FxHashMap, Cell>, - pub zero_cell: Option>, + pub zero_cell: Option>, // `cells_to_lookup` is a vector keeping track of all cells that we want to enable lookup for. When there is more than 1 advice column we will copy_advice all of these cells to the single lookup enabled column and do lookups there - pub cells_to_lookup: Vec>, + pub cells_to_lookup: Vec>, current_phase: usize, @@ -269,7 +265,7 @@ impl<'a, F: ScalarField> Context<'a, F> { } } - pub fn preallocated_vec_to_assign(&self) -> Rc>>> { + pub fn preallocated_vec_to_assign(&self) -> Rc>>> { Rc::clone(&self.preallocated_vec_to_assign) } @@ -366,7 +362,7 @@ impl<'a, F: ScalarField> Context<'a, F> { column: Column, #[cfg(feature = "display")] context_id: usize, row_offset: usize, - ) -> AssignedValue<'v, F> { + ) -> AssignedValue { match input { QuantumCell::Existing(acell) => { AssignedValue { @@ -425,14 +421,14 @@ impl<'a, F: ScalarField> Context<'a, F> { } #[cfg(feature = "halo2-pse")] - pub fn assign_cell<'v>( + pub fn assign_cell( &mut self, - input: QuantumCell<'_, 'v, F>, + input: QuantumCell, column: Column, #[cfg(feature = "display")] context_id: usize, row_offset: usize, - phase: u8, - ) -> AssignedValue<'v, F> { + _phase: u8, + ) -> AssignedValue { match input { QuantumCell::Existing(acell) => { AssignedValue { @@ -444,22 +440,6 @@ impl<'a, F: ScalarField> Context<'a, F> { ), value: acell.value, row_offset, - _marker: PhantomData, - #[cfg(feature = "display")] - context_id, - } - } - QuantumCell::ExistingOwned(acell) => { - AssignedValue { - cell: acell.copy_advice( - // || "gate: copy advice", - &mut self.region, - column, - row_offset, - ), - value: acell.value, - row_offset, - _marker: PhantomData, #[cfg(feature = "display")] context_id, } @@ -472,7 +452,6 @@ impl<'a, F: ScalarField> Context<'a, F> { .cell(), value, row_offset, - _marker: PhantomData, #[cfg(feature = "display")] context_id, }, @@ -484,7 +463,6 @@ impl<'a, F: ScalarField> Context<'a, F> { .cell(), value: Value::unknown(), row_offset, - _marker: PhantomData, #[cfg(feature = "display")] context_id, }, @@ -500,7 +478,6 @@ impl<'a, F: ScalarField> Context<'a, F> { cell: acell, value: Value::known(c), row_offset, - _marker: PhantomData, #[cfg(feature = "display")] context_id, } @@ -569,7 +546,7 @@ pub struct AssignedPrimitive<'a, T: Into + Copy, F: ScalarField> { #[cfg(feature = "halo2-pse")] pub cell: Cell, #[cfg(feature = "halo2-pse")] - row_offset: usize, + _row_offset: usize, #[cfg(feature = "halo2-pse")] _marker: PhantomData<&'a F>, } diff --git a/halo2-base/src/utils.rs b/halo2-base/src/utils.rs index bb07150a..55f95de1 100644 --- a/halo2-base/src/utils.rs +++ b/halo2-base/src/utils.rs @@ -1,21 +1,33 @@ -#[cfg(feature = "halo2-pse")] -use crate::halo2_proofs::arithmetic::CurveAffine; -use crate::halo2_proofs::{arithmetic::FieldExt, circuit::Value}; use core::hash::Hash; + +#[cfg(not(feature = "halo2-axiom"))] +use crate::halo2_proofs::arithmetic::CurveAffine; +use crate::halo2_proofs::circuit::Value; +#[cfg(feature = "halo2-axiom")] +pub use crate::halo2_proofs::halo2curves::CurveAffineExt; +use ff::{FromUniformBytes, PrimeField}; + use num_bigint::BigInt; use num_bigint::BigUint; use num_bigint::Sign; use num_traits::Signed; use num_traits::{One, Zero}; +/// Helper trait to convert to and from a [BigPrimeField] by converting a list of [u64] digits #[cfg(feature = "halo2-axiom")] pub trait BigPrimeField: ScalarField { + /// Converts a slice of [u64] to [BigPrimeField] + /// * `val`: the slice of u64 + /// + /// # Assumptions + /// * `val` has the correct length for the implementation + /// * The integer value of `val` is already less than the modulus of `Self` fn from_u64_digits(val: &[u64]) -> Self; } #[cfg(feature = "halo2-axiom")] impl BigPrimeField for F where - F: FieldExt + Hash + Into<[u64; 4]> + From<[u64; 4]>, + F: ScalarField + From<[u64; 4]>, // Assume [u64; 4] is little-endian. We only implement ScalarField when this is true. { #[inline(always)] fn from_u64_digits(val: &[u64]) -> Self { @@ -26,67 +38,103 @@ where } } -#[cfg(feature = "halo2-axiom")] -pub trait ScalarField: FieldExt + Hash { - /// Returns the base `2^bit_len` little endian representation of the prime field element - /// up to `num_limbs` number of limbs (truncates any extra limbs) - /// - /// Basically same as `to_repr` but does not go further into bytes +/// Helper trait to represent a field element that can be converted into [u64] limbs. +/// +/// Note: Since the number of bits necessary to represent a field element is larger than the number of bits in a u64, we decompose the integer representation of the field element into multiple [u64] values e.g. `limbs`. +pub trait ScalarField: PrimeField + FromUniformBytes<64> + From + Hash + Ord { + /// Returns the base `2bit_len` little endian representation of the [ScalarField] element up to `num_limbs` number of limbs (truncates any extra limbs). /// - /// Undefined behavior if `bit_len > 64` + /// Assumes `bit_len < 64`. + /// * `num_limbs`: number of limbs to return + /// * `bit_len`: number of bits in each limb fn to_u64_limbs(self, num_limbs: usize, bit_len: usize) -> Vec; -} -#[cfg(feature = "halo2-axiom")] -impl ScalarField for F -where - F: FieldExt + Hash + Into<[u64; 4]>, -{ - #[inline(always)] - fn to_u64_limbs(self, num_limbs: usize, bit_len: usize) -> Vec { - let tmp: [u64; 4] = self.into(); - decompose_u64_digits_to_limbs(tmp, num_limbs, bit_len) + + /// Returns the little endian byte representation of the element. + fn to_bytes_le(&self) -> Vec; + + /// Creates a field element from a little endian byte representation. + /// + /// The default implementation assumes that `PrimeField::from_repr` is implemented for little-endian. + /// It should be overriden if this is not the case. + fn from_bytes_le(bytes: &[u8]) -> Self { + let mut repr = Self::Repr::default(); + repr.as_mut()[..bytes.len()].copy_from_slice(bytes); + Self::from_repr(repr).unwrap() + } + + /// Gets the least significant 32 bits of the field element. + fn get_lower_32(&self) -> u32 { + let bytes = self.to_bytes_le(); + let mut lower_32 = 0u32; + for (i, byte) in bytes.into_iter().enumerate().take(4) { + lower_32 |= (byte as u32) << (i * 8); + } + lower_32 + } + + /// Gets the least significant 64 bits of the field element. + fn get_lower_64(&self) -> u64 { + let bytes = self.to_bytes_le(); + let mut lower_64 = 0u64; + for (i, byte) in bytes.into_iter().enumerate().take(8) { + lower_64 |= (byte as u64) << (i * 8); + } + lower_64 } } +// See below for implementations -// Later: will need to separate PrimeField from ScalarField when Goldilocks is introduced -#[cfg(feature = "halo2-axiom")] -pub trait PrimeField = BigPrimeField; -#[cfg(feature = "halo2-pse")] -pub trait PrimeField = FieldExt; +// Later: will need to separate BigPrimeField from ScalarField when Goldilocks is introduced +/// [ScalarField] that is ~256 bits long #[cfg(feature = "halo2-pse")] -pub trait ScalarField = FieldExt; +pub trait BigPrimeField = PrimeField + ScalarField; +/// Converts an [Iterator] of u64 digits into `number_of_limbs` limbs of `bit_len` bits returned as a [Vec]. +/// +/// Assumes: `bit_len < 64`. +/// * `e`: Iterator of [u64] digits +/// * `number_of_limbs`: number of limbs to return +/// * `bit_len`: number of bits in each limb #[inline(always)] pub(crate) fn decompose_u64_digits_to_limbs( e: impl IntoIterator, number_of_limbs: usize, bit_len: usize, ) -> Vec { - debug_assert!(bit_len <= 64); + debug_assert!(bit_len < 64); let mut e = e.into_iter(); + // Mask to extract the bits from each digit let mask: u64 = (1u64 << bit_len) - 1u64; let mut u64_digit = e.next().unwrap_or(0); let mut rem = 64; + + // For each digit, we extract its individual limbs by repeatedly masking and shifting the digit based on how many bits we have left to extract. (0..number_of_limbs) .map(|_| match rem.cmp(&bit_len) { + // If `rem` > `bit_len`, we mask the bits from the `u64_digit` to return the first limb. + // We shift the digit to the right by `bit_len` bits and subtract `bit_len` from `rem` core::cmp::Ordering::Greater => { let limb = u64_digit & mask; u64_digit >>= bit_len; rem -= bit_len; limb } + // If `rem` == `bit_len`, then we mask the bits from the `u64_digit` to return the first limb + // We retrieve the next digit and reset `rem` to 64 core::cmp::Ordering::Equal => { let limb = u64_digit & mask; u64_digit = e.next().unwrap_or(0); rem = 64; limb } + // If `rem` < `bit_len`, we retrieve the next digit, mask it, and shift left `rem` bits from the `u64_digit` to return the first limb. + // we shift the digit to the right by `bit_len` - `rem` bits to retrieve the start of the next limb and add 64 - bit_len to `rem` to get the remainder. core::cmp::Ordering::Less => { let mut limb = u64_digit; u64_digit = e.next().unwrap_or(0); - limb |= (u64_digit & ((1 << (bit_len - rem)) - 1)) << rem; + limb |= (u64_digit & ((1u64 << (bit_len - rem)) - 1u64)) << rem; u64_digit >>= bit_len - rem; rem += 64 - bit_len; limb @@ -95,24 +143,35 @@ pub(crate) fn decompose_u64_digits_to_limbs( .collect() } -pub fn bit_length(x: u64) -> usize { +/// Returns the number of bits needed to represent the value of `x`. +pub const fn bit_length(x: u64) -> usize { (u64::BITS - x.leading_zeros()) as usize } +/// Returns the ceiling of the base 2 logarithm of `x`. +/// +/// `log2_ceil(0)` returns 0. pub fn log2_ceil(x: u64) -> usize { - (u64::BITS - x.leading_zeros() - (x & (x - 1) == 0) as u32) as usize + (u64::BITS - x.leading_zeros()) as usize - usize::from(x.is_power_of_two()) } -pub fn modulus() -> BigUint { - fe_to_biguint(&-F::one()) + 1u64 +/// Returns the modulus of [BigPrimeField]. +pub fn modulus() -> BigUint { + fe_to_biguint(&-F::ONE) + 1u64 } -pub fn power_of_two(n: usize) -> F { +/// Returns the [BigPrimeField] element of 2n. +/// * `n`: the desired power of 2. +pub fn power_of_two(n: usize) -> F { biguint_to_fe(&(BigUint::one() << n)) } -/// assume `e` less than modulus of F -pub fn biguint_to_fe(e: &BigUint) -> F { +/// Converts an immutable reference to [BigUint] to a [BigPrimeField]. +/// * `e`: immutable reference to [BigUint] +/// +/// # Assumptions: +/// * `e` is less than the modulus of `F` +pub fn biguint_to_fe(e: &BigUint) -> F { #[cfg(feature = "halo2-axiom")] { F::from_u64_digits(&e.to_u64_digits()) @@ -120,15 +179,17 @@ pub fn biguint_to_fe(e: &BigUint) -> F { #[cfg(feature = "halo2-pse")] { - let mut repr = F::Repr::default(); let bytes = e.to_bytes_le(); - repr.as_mut()[..bytes.len()].copy_from_slice(&bytes); - F::from_repr(repr).unwrap() + F::from_bytes_le(&bytes) } } -/// assume `|e|` less than modulus of F -pub fn bigint_to_fe(e: &BigInt) -> F { +/// Converts an immutable reference to [BigInt] to a [BigPrimeField]. +/// * `e`: immutable reference to [BigInt] +/// +/// # Assumptions: +/// * The absolute value of `e` is less than the modulus of `F` +pub fn bigint_to_fe(e: &BigInt) -> F { #[cfg(feature = "halo2-axiom")] { let (sign, digits) = e.to_u64_digits(); @@ -141,9 +202,7 @@ pub fn bigint_to_fe(e: &BigInt) -> F { #[cfg(feature = "halo2-pse")] { let (sign, bytes) = e.to_bytes_le(); - let mut repr = F::Repr::default(); - repr.as_mut()[..bytes.len()].copy_from_slice(&bytes); - let f_abs = F::from_repr(repr).unwrap(); + let f_abs = F::from_bytes_le(&bytes); if sign == Sign::Minus { -f_abs } else { @@ -152,11 +211,18 @@ pub fn bigint_to_fe(e: &BigInt) -> F { } } -pub fn fe_to_biguint(fe: &F) -> BigUint { - BigUint::from_bytes_le(fe.to_repr().as_ref()) +/// Converts an immutable reference to an PrimeField element into a [BigUint] element. +/// * `fe`: immutable reference to PrimeField element to convert +pub fn fe_to_biguint(fe: &F) -> BigUint { + BigUint::from_bytes_le(fe.to_bytes_le().as_ref()) } -pub fn fe_to_bigint(fe: &F) -> BigInt { +/// Converts a [BigPrimeField] element into a [BigInt] element by sending `fe` in `[0, F::modulus())` to +/// ```ignore +/// fe, if fe < F::modulus() / 2 +/// fe - F::modulus(), otherwise +/// ``` +pub fn fe_to_bigint(fe: &F) -> BigInt { // TODO: `F` should just have modulus as lazy_static or something let modulus = modulus::(); let e = fe_to_biguint(fe); @@ -167,7 +233,13 @@ pub fn fe_to_bigint(fe: &F) -> BigInt { } } -pub fn decompose(e: &F, number_of_limbs: usize, bit_len: usize) -> Vec { +/// Decomposes an immutable reference to a [BigPrimeField] element into `number_of_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs. +/// +/// Assumes `bit_len < 128`. +/// * `e`: immutable reference to [BigPrimeField] element to decompose +/// * `number_of_limbs`: number of limbs to decompose `e` into +/// * `bit_len`: number of bits in each limb +pub fn decompose(e: &F, number_of_limbs: usize, bit_len: usize) -> Vec { if bit_len > 64 { decompose_biguint(&fe_to_biguint(e), number_of_limbs, bit_len) } else { @@ -175,7 +247,12 @@ pub fn decompose(e: &F, number_of_limbs: usize, bit_len: usize) - } } -/// Assumes `bit_len` <= 64 +/// Decomposes an immutable reference to a [ScalarField] element into `number_of_limbs` limbs of `bit_len` bits each and returns a [Vec] of [u64] represented by those limbs. +/// +/// Assumes `bit_len` < 64 +/// * `e`: immutable reference to [ScalarField] element to decompose +/// * `number_of_limbs`: number of limbs to decompose `e` into +/// * `bit_len`: number of bits in each limb pub fn decompose_fe_to_u64_limbs( e: &F, number_of_limbs: usize, @@ -192,29 +269,45 @@ pub fn decompose_fe_to_u64_limbs( } } -pub fn decompose_biguint(e: &BigUint, num_limbs: usize, bit_len: usize) -> Vec { - debug_assert!(bit_len > 64 && bit_len <= 128); +/// Decomposes an immutable reference to a [BigUint] into `num_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs. +/// +/// Assumes 64 <= `bit_len` < 128. +/// * `e`: immutable reference to [BigInt] to decompose +/// * `num_limbs`: number of limbs to decompose `e` into +/// * `bit_len`: number of bits in each limb +/// +/// Truncates to `num_limbs` limbs if `e` is too large. +pub fn decompose_biguint( + e: &BigUint, + num_limbs: usize, + bit_len: usize, +) -> Vec { + // bit_len must be between 64` and 128 + debug_assert!((64..128).contains(&bit_len)); let mut e = e.iter_u64_digits(); + // Grab first 128-bit limb from iterator let mut limb0 = e.next().unwrap_or(0) as u128; let mut rem = bit_len - 64; let mut u64_digit = e.next().unwrap_or(0); - limb0 |= ((u64_digit & ((1 << rem) - 1)) as u128) << 64; + // Extract second limb (bit length 64) from e + limb0 |= ((u64_digit & ((1u64 << rem) - 1u64)) as u128) << 64u32; u64_digit >>= rem; rem = 64 - rem; + // Convert `limb0` into field element `F` and create an iterator by chaining `limb0` with the computing the remaining limbs core::iter::once(F::from_u128(limb0)) .chain((1..num_limbs).map(|_| { - let mut limb: u128 = u64_digit.into(); + let mut limb = u64_digit as u128; let mut bits = rem; u64_digit = e.next().unwrap_or(0); - if bit_len - bits >= 64 { + if bit_len >= 64 + bits { limb |= (u64_digit as u128) << bits; u64_digit = e.next().unwrap_or(0); bits += 64; } rem = bit_len - bits; - limb |= ((u64_digit & ((1 << rem) - 1)) as u128) << bits; + limb |= ((u64_digit & ((1u64 << rem) - 1u64)) as u128) << bits; u64_digit >>= rem; rem = 64 - rem; F::from_u128(limb) @@ -222,7 +315,13 @@ pub fn decompose_biguint(e: &BigUint, num_limbs: usize, bit_len: .collect() } -pub fn decompose_bigint(e: &BigInt, num_limbs: usize, bit_len: usize) -> Vec { +/// Decomposes an immutable reference to a [BigInt] into `num_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs. +/// +/// Assumes `bit_len < 128`. +/// * `e`: immutable reference to `BigInt` to decompose +/// * `num_limbs`: number of limbs to decompose `e` into +/// * `bit_len`: number of bits in each limb +pub fn decompose_bigint(e: &BigInt, num_limbs: usize, bit_len: usize) -> Vec { if e.is_negative() { decompose_biguint::(e.magnitude(), num_limbs, bit_len).into_iter().map(|x| -x).collect() } else { @@ -230,7 +329,13 @@ pub fn decompose_bigint(e: &BigInt, num_limbs: usize, bit_len: us } } -pub fn decompose_bigint_option( +/// Decomposes an immutable reference to a [BigInt] into `num_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs wrapped in [Value]. +/// +/// Assumes `bit_len` < 128. +/// * `e`: immutable reference to `BigInt` to decompose +/// * `num_limbs`: number of limbs to decompose `e` into +/// * `bit_len`: number of bits in each limb +pub fn decompose_bigint_option( value: Value<&BigInt>, number_of_limbs: usize, bit_len: usize, @@ -238,6 +343,9 @@ pub fn decompose_bigint_option( value.map(|e| decompose_bigint(e, number_of_limbs, bit_len)).transpose_vec(number_of_limbs) } +/// Wraps the internal value of `value` in an [Option]. +/// If the value is [None], then the function returns [None]. +/// * `value`: Value to convert. pub fn value_to_option(value: Value) -> Option { let mut v = None; value.map(|val| { @@ -246,28 +354,19 @@ pub fn value_to_option(value: Value) -> Option { v } -/// Compute the represented value by a vector of values and a bit length. +/// Computes the value of an integer by passing as `input` a [Vec] of its limb values and the `bit_len` (bit length) used. /// -/// This function is used to compute the value of an integer -/// passing as input its limb values and the bit length used. -/// Returns the sum of all limbs scaled by 2^(bit_len * i) +/// Returns the sum of all limbs scaled by 2(bit_len * i) where i is the index of the limb. +/// * `input`: Limb values of the integer. +/// * `bit_len`: Length of limb in bits pub fn compose(input: Vec, bit_len: usize) -> BigUint { input.iter().rev().fold(BigUint::zero(), |acc, val| (acc << bit_len) + val) } -#[cfg(test)] -#[test] -fn test_signed_roundtrip() { - use crate::halo2_proofs::halo2curves::bn256::Fr; - assert_eq!(fe_to_bigint(&bigint_to_fe::(&-BigInt::one())), -BigInt::one()); -} - -#[cfg(feature = "halo2-axiom")] -pub use halo2_proofs_axiom::halo2curves::CurveAffineExt; - +/// Helper trait #[cfg(feature = "halo2-pse")] pub trait CurveAffineExt: CurveAffine { - /// Unlike the `Coordinates` trait, this just returns the raw affine coordinantes without checking `is_on_curve` + /// Returns the raw affine (X, Y) coordinantes fn into_coordinates(self) -> (Self::Base, Self::Base) { let coordinates = self.coordinates().unwrap(); (*coordinates.x(), *coordinates.y()) @@ -276,6 +375,85 @@ pub trait CurveAffineExt: CurveAffine { #[cfg(feature = "halo2-pse")] impl CurveAffineExt for C {} +mod scalar_field_impls { + use super::{decompose_u64_digits_to_limbs, ScalarField}; + use crate::halo2_proofs::halo2curves::{ + bls12_381::Scalar as blsScalar, + bn256::{Fq as bn254Fq, Fr as bn254Fr}, + secp256k1::{Fp as secpk1Fp, Fq as secpk1Fq}, + secp256r1::{Fp as secpr1Fp, Fq as secpr1Fq}, + }; + #[cfg(feature = "halo2-pse")] + use ff::PrimeField; + + /// To ensure `ScalarField` is only implemented for `ff:Field` where `Repr` is little endian, we use the following macro + /// to implement the trait for each field. + #[cfg(feature = "halo2-axiom")] + #[macro_export] + macro_rules! impl_scalar_field { + ($field:ident) => { + impl ScalarField for $field { + #[inline(always)] + fn to_u64_limbs(self, num_limbs: usize, bit_len: usize) -> Vec { + // Basically same as `to_repr` but does not go further into bytes + let tmp: [u64; 4] = self.into(); + decompose_u64_digits_to_limbs(tmp, num_limbs, bit_len) + } + + #[inline(always)] + fn to_bytes_le(&self) -> Vec { + let tmp: [u64; 4] = (*self).into(); + tmp.iter().flat_map(|x| x.to_le_bytes()).collect() + } + + #[inline(always)] + fn get_lower_32(&self) -> u32 { + let tmp: [u64; 4] = (*self).into(); + tmp[0] as u32 + } + + #[inline(always)] + fn get_lower_64(&self) -> u64 { + let tmp: [u64; 4] = (*self).into(); + tmp[0] + } + } + }; + } + + /// To ensure `ScalarField` is only implemented for `ff:Field` where `Repr` is little endian, we use the following macro + /// to implement the trait for each field. + #[cfg(feature = "halo2-pse")] + #[macro_export] + macro_rules! impl_scalar_field { + ($field:ident) => { + impl ScalarField for $field { + #[inline(always)] + fn to_u64_limbs(self, num_limbs: usize, bit_len: usize) -> Vec { + let bytes = self.to_repr(); + let digits = (0..4) + .map(|i| u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap())); + decompose_u64_digits_to_limbs(digits, num_limbs, bit_len) + } + + #[inline(always)] + fn to_bytes_le(&self) -> Vec { + self.to_repr().to_vec() + } + } + }; + } + + impl_scalar_field!(bn254Fr); + impl_scalar_field!(bn254Fq); + impl_scalar_field!(secpk1Fp); + impl_scalar_field!(secpk1Fq); + impl_scalar_field!(secpr1Fp); + impl_scalar_field!(secpr1Fq); + impl_scalar_field!(blsScalar); +} + +/// Module for reading parameters for Halo2 proving system from the file system. pub mod fs { use std::{ env::var, @@ -288,10 +466,15 @@ pub mod fs { bn256::{Bn256, G1Affine}, CurveAffine, }, - poly::{commitment::{Params, ParamsProver}, kzg::commitment::ParamsKZG}, + poly::{ + commitment::{Params, ParamsProver}, + kzg::commitment::ParamsKZG, + }, }; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; + /// Reads the srs from a file found in `./params/kzg_bn254_{k}.srs` or `{dir}/kzg_bn254_{k}.srs` if `PARAMS_DIR` env var is specified. + /// * `k`: degree that expresses the size of circuit (i.e., 2^k is the number of rows in the circuit) pub fn read_params(k: u32) -> ParamsKZG { let dir = var("PARAMS_DIR").unwrap_or_else(|_| "./params".to_string()); ParamsKZG::::read(&mut BufReader::new( @@ -301,6 +484,9 @@ pub mod fs { .unwrap() } + /// Attempts to read the srs from a file found in `./params/kzg_bn254_{k}.srs` or `{dir}/kzg_bn254_{k}.srs` if `PARAMS_DIR` env var is specified, creates a file it if it does not exist. + /// * `k`: degree that expresses the size of circuit (i.e., 2^k is the number of rows in the circuit) + /// * `setup`: a function that creates the srs pub fn read_or_create_srs<'a, C: CurveAffine, P: ParamsProver<'a, C>>( k: u32, setup: impl Fn(u32) -> P, @@ -325,9 +511,111 @@ pub mod fs { } } + /// Generates the SRS for the KZG scheme and writes it to a file found in "./params/kzg_bn2_{k}.srs` or `{dir}/kzg_bn254_{k}.srs` if `PARAMS_DIR` env var is specified, creates a file it if it does not exist" + /// * `k`: degree that expresses the size of circuit (i.e., 2^k is the number of rows in the circuit) pub fn gen_srs(k: u32) -> ParamsKZG { read_or_create_srs::(k, |k| { ParamsKZG::::setup(k, ChaCha20Rng::from_seed(Default::default())) }) } } + +#[cfg(test)] +mod tests { + use crate::halo2_proofs::halo2curves::bn256::Fr; + use num_bigint::RandomBits; + use rand::{ + rngs::{OsRng, StdRng}, + Rng, SeedableRng, + }; + use std::ops::Shl; + + use super::*; + + #[test] + fn test_signed_roundtrip() { + use crate::halo2_proofs::halo2curves::bn256::Fr; + assert_eq!(fe_to_bigint(&bigint_to_fe::(&-BigInt::one())), -BigInt::one()); + } + + #[test] + fn test_decompose_biguint() { + let mut rng = OsRng; + const MAX_LIMBS: u64 = 5; + for bit_len in 64..128usize { + for num_limbs in 1..=MAX_LIMBS { + for _ in 0..10_000usize { + let mut e: BigUint = rng.sample(RandomBits::new(num_limbs * bit_len as u64)); + let limbs = decompose_biguint::(&e, num_limbs as usize, bit_len); + + let limbs2 = { + let mut limbs = vec![]; + let mask = BigUint::one().shl(bit_len) - 1usize; + for _ in 0..num_limbs { + let limb = &e & &mask; + let mut bytes_le = limb.to_bytes_le(); + bytes_le.resize(32, 0u8); + limbs.push(Fr::from_bytes(&bytes_le.try_into().unwrap()).unwrap()); + e >>= bit_len; + } + limbs + }; + assert_eq!(limbs, limbs2); + } + } + } + } + + #[test] + fn test_decompose_u64_digits_to_limbs() { + let mut rng = OsRng; + const MAX_LIMBS: u64 = 5; + for bit_len in 0..64usize { + for num_limbs in 1..=MAX_LIMBS { + for _ in 0..10_000usize { + let mut e: BigUint = rng.sample(RandomBits::new(num_limbs * bit_len as u64)); + let limbs = decompose_u64_digits_to_limbs( + e.to_u64_digits(), + num_limbs as usize, + bit_len, + ); + let limbs2 = { + let mut limbs = vec![]; + let mask = BigUint::one().shl(bit_len) - 1usize; + for _ in 0..num_limbs { + let limb = &e & &mask; + limbs.push(u64::try_from(limb).unwrap()); + e >>= bit_len; + } + limbs + }; + assert_eq!(limbs, limbs2); + } + } + } + } + + #[test] + fn test_log2_ceil_zero() { + assert_eq!(log2_ceil(0), 0); + } + + #[test] + fn test_get_lower_32() { + let mut rng = StdRng::seed_from_u64(0); + for _ in 0..10_000usize { + let e: u32 = rng.gen_range(0..u32::MAX); + assert_eq!(Fr::from(e as u64).get_lower_32(), e); + } + assert_eq!(Fr::from((1u64 << 32_i32) + 1).get_lower_32(), 1); + } + + #[test] + fn test_get_lower_64() { + let mut rng = StdRng::seed_from_u64(0); + for _ in 0..10_000usize { + let e: u64 = rng.gen_range(0..u64::MAX); + assert_eq!(Fr::from(e).get_lower_64(), e); + } + } +} diff --git a/halo2-ecc/Cargo.toml b/halo2-ecc/Cargo.toml index a142200d..7498ecf4 100644 --- a/halo2-ecc/Cargo.toml +++ b/halo2-ecc/Cargo.toml @@ -15,8 +15,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" # arithmetic -ff = "0.12" -group = "0.12" +ff = "0.13" +group = "0.13" halo2-base = { path = "../halo2-base", default-features = false } @@ -27,7 +27,7 @@ criterion = "0.4" criterion-macro = "0.4" [features] -default = ["jemallocator", "halo2-axiom", "display"] +default = ["jemallocator", "halo2-pse", "display"] dev-graph = ["halo2-base/dev-graph"] display = ["halo2-base/display"] halo2-pse = ["halo2-base/halo2-pse"] diff --git a/halo2-ecc/benches/fixed_base_msm.rs b/halo2-ecc/benches/fixed_base_msm.rs index 0bdf7e12..00bd8695 100644 --- a/halo2-ecc/benches/fixed_base_msm.rs +++ b/halo2-ecc/benches/fixed_base_msm.rs @@ -12,6 +12,7 @@ use rand_core::OsRng; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; +use halo2_base::gates::GateInstructions; use halo2_base::halo2_proofs::{ arithmetic::Field, circuit::{Layouter, SimpleFloorPlanner, Value}, @@ -24,7 +25,7 @@ use halo2_base::halo2_proofs::{ transcript::TranscriptWriterBuffer, transcript::{Blake2bWrite, Challenge255}, }; -use halo2_base::{gates::GateInstructions, utils::PrimeField}; +use halo2_ecc::fields::PrimeField; use halo2_ecc::{ ecc::EccChip, fields::fp::{FpConfig, FpStrategy}, diff --git a/halo2-ecc/benches/fp_mul.rs b/halo2-ecc/benches/fp_mul.rs index d49162e0..80ae8bf1 100644 --- a/halo2-ecc/benches/fp_mul.rs +++ b/halo2-ecc/benches/fp_mul.rs @@ -11,14 +11,14 @@ use halo2_base::halo2_proofs::{ }, transcript::{Blake2bWrite, Challenge255, TranscriptWriterBuffer}, }; -use rand::rngs::OsRng; - use halo2_base::{ - utils::{fe_to_bigint, modulus, PrimeField}, + utils::{fe_to_bigint, modulus}, SKIP_FIRST_PASS, }; use halo2_ecc::fields::fp::{FpConfig, FpStrategy}; use halo2_ecc::fields::FieldChip; +use halo2_ecc::fields::PrimeField; +use rand::rngs::OsRng; use criterion::{criterion_group, criterion_main}; use criterion::{BenchmarkId, Criterion}; diff --git a/halo2-ecc/benches/msm.rs b/halo2-ecc/benches/msm.rs index 22be806e..0ac98097 100644 --- a/halo2-ecc/benches/msm.rs +++ b/halo2-ecc/benches/msm.rs @@ -24,9 +24,10 @@ use halo2_base::halo2_proofs::{ }; use halo2_base::{ gates::GateInstructions, - utils::{biguint_to_fe, fe_to_biguint, PrimeField}, + utils::{biguint_to_fe, fe_to_biguint}, QuantumCell::Witness, }; +use halo2_ecc::fields::PrimeField; use halo2_ecc::{ ecc::EccChip, fields::fp::{FpConfig, FpStrategy}, diff --git a/halo2-ecc/src/bigint/add_no_carry.rs b/halo2-ecc/src/bigint/add_no_carry.rs index 8cc687d4..0300494f 100644 --- a/halo2-ecc/src/bigint/add_no_carry.rs +++ b/halo2-ecc/src/bigint/add_no_carry.rs @@ -1,34 +1,35 @@ use super::{CRTInteger, OverflowInteger}; -use halo2_base::{gates::GateInstructions, utils::PrimeField, Context, QuantumCell::Existing}; +use crate::fields::PrimeField; +use halo2_base::{gates::GateInstructions, Context, QuantumCell::Existing}; use std::cmp::max; -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, - b: &OverflowInteger<'v, F>, -) -> OverflowInteger<'v, F> { + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, +) -> OverflowInteger { assert_eq!(a.limbs.len(), b.limbs.len()); let out_limbs = a .limbs .iter() .zip(b.limbs.iter()) - .map(|(a_limb, b_limb)| gate.add(ctx, Existing(a_limb), Existing(b_limb))) + .map(|(a_limb, b_limb)| gate.add(ctx, Existing(*a_limb), Existing(*b_limb))) .collect(); OverflowInteger::construct(out_limbs, max(a.max_limb_bits, b.max_limb_bits) + 1) } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, -) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, +) -> CRTInteger { assert_eq!(a.truncation.limbs.len(), b.truncation.limbs.len()); let out_trunc = assign::(gate, ctx, &a.truncation, &b.truncation); - let out_native = gate.add(ctx, Existing(&a.native), Existing(&b.native)); + let out_native = gate.add(ctx, Existing(a.native), Existing(b.native)); let out_val = a.value.as_ref().zip(b.value.as_ref()).map(|(a, b)| a + b); CRTInteger::construct(out_trunc, out_native, out_val) } diff --git a/halo2-ecc/src/bigint/big_is_equal.rs b/halo2-ecc/src/bigint/big_is_equal.rs index f963937f..d67b30b3 100644 --- a/halo2-ecc/src/bigint/big_is_equal.rs +++ b/halo2-ecc/src/bigint/big_is_equal.rs @@ -1,47 +1,46 @@ use super::{CRTInteger, OverflowInteger}; -use halo2_base::{ - gates::GateInstructions, utils::PrimeField, AssignedValue, Context, QuantumCell::Existing, -}; +use crate::fields::PrimeField; +use halo2_base::{gates::GateInstructions, AssignedValue, Context, QuantumCell::Existing}; -// given OverflowInteger's `a` and `b` of the same shape, -// returns whether `a == b` -pub fn assign<'v, F: PrimeField>( +/// Given OverflowInteger's `a` and `b` of the same shape, +/// returns whether `a == b` +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, - b: &OverflowInteger<'v, F>, -) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, +) -> AssignedValue { let k = a.limbs.len(); assert_eq!(k, b.limbs.len()); - assert_ne!(k, 0); + debug_assert_ne!(k, 0); let mut a_limbs = a.limbs.iter(); let mut b_limbs = b.limbs.iter(); let mut partial = - gate.is_equal(ctx, Existing(a_limbs.next().unwrap()), Existing(b_limbs.next().unwrap())); + gate.is_equal(ctx, Existing(*a_limbs.next().unwrap()), Existing(*b_limbs.next().unwrap())); for (a_limb, b_limb) in a_limbs.zip(b_limbs) { - let eq_limb = gate.is_equal(ctx, Existing(a_limb), Existing(b_limb)); - partial = gate.and(ctx, Existing(&eq_limb), Existing(&partial)); + let eq_limb = gate.is_equal(ctx, Existing(*a_limb), Existing(*b_limb)); + partial = gate.and(ctx, Existing(eq_limb), Existing(partial)); } partial } -pub fn wrapper<'v, F: PrimeField>( +pub fn wrapper( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, -) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, +) -> AssignedValue { assign(gate, ctx, &a.truncation, &b.truncation) } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, -) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, +) -> AssignedValue { let out_trunc = assign::(gate, ctx, &a.truncation, &b.truncation); - let out_native = gate.is_equal(ctx, Existing(&a.native), Existing(&b.native)); - gate.and(ctx, Existing(&out_trunc), Existing(&out_native)) + let out_native = gate.is_equal(ctx, Existing(a.native), Existing(b.native)); + gate.and(ctx, Existing(out_trunc), Existing(out_native)) } diff --git a/halo2-ecc/src/bigint/big_is_zero.rs b/halo2-ecc/src/bigint/big_is_zero.rs index 4ab84fa3..4a35561a 100644 --- a/halo2-ecc/src/bigint/big_is_zero.rs +++ b/halo2-ecc/src/bigint/big_is_zero.rs @@ -1,46 +1,45 @@ use super::{CRTInteger, OverflowInteger}; -use halo2_base::{ - gates::GateInstructions, utils::PrimeField, AssignedValue, Context, QuantumCell::Existing, -}; +use crate::fields::PrimeField; +use halo2_base::{gates::GateInstructions, AssignedValue, Context, QuantumCell::Existing}; /// assume you know that the limbs of `a` are all in [0, 2^{a.max_limb_bits}) -pub fn positive<'v, F: PrimeField>( +pub fn positive( gate: &impl GateInstructions, - ctx: &mut Context<'v, F>, - a: &OverflowInteger<'v, F>, -) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &OverflowInteger, +) -> AssignedValue { let k = a.limbs.len(); - assert_ne!(k, 0); + debug_assert_ne!(k, 0); debug_assert!(a.max_limb_bits as u32 + k.ilog2() < F::CAPACITY); - let sum = gate.sum(ctx, a.limbs.iter().map(Existing)); + let sum = gate.sum(ctx, a.limbs.iter().copied().map(Existing)); gate.is_zero(ctx, &sum) } // given OverflowInteger `a`, returns whether `a == 0` -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, -) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &OverflowInteger, +) -> AssignedValue { let k = a.limbs.len(); - assert_ne!(k, 0); + debug_assert_ne!(k, 0); let mut a_limbs = a.limbs.iter(); let mut partial = gate.is_zero(ctx, a_limbs.next().unwrap()); for a_limb in a_limbs { let limb_is_zero = gate.is_zero(ctx, a_limb); - partial = gate.and(ctx, Existing(&limb_is_zero), Existing(&partial)); + partial = gate.and(ctx, Existing(limb_is_zero), Existing(partial)); } partial } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, -) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &CRTInteger, +) -> AssignedValue { let out_trunc = assign::(gate, ctx, &a.truncation); let out_native = gate.is_zero(ctx, &a.native); - gate.and(ctx, Existing(&out_trunc), Existing(&out_native)) + gate.and(ctx, Existing(out_trunc), Existing(out_native)) } diff --git a/halo2-ecc/src/bigint/big_less_than.rs b/halo2-ecc/src/bigint/big_less_than.rs index 52528870..9d0bdc92 100644 --- a/halo2-ecc/src/bigint/big_less_than.rs +++ b/halo2-ecc/src/bigint/big_less_than.rs @@ -1,16 +1,17 @@ use super::OverflowInteger; -use halo2_base::{gates::RangeInstructions, utils::PrimeField, AssignedValue, Context}; +use crate::fields::PrimeField; +use halo2_base::{gates::RangeInstructions, AssignedValue, Context}; // given OverflowInteger's `a` and `b` of the same shape, // returns whether `a < b` -pub fn assign<'a, F: PrimeField>( +pub fn assign( range: &impl RangeInstructions, - ctx: &mut Context<'a, F>, - a: &OverflowInteger<'a, F>, - b: &OverflowInteger<'a, F>, + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, limb_bits: usize, limb_base: F, -) -> AssignedValue<'a, F> { +) -> AssignedValue { // a < b iff a - b has underflow let (_, underflow) = super::sub::assign::(range, ctx, a, b, limb_bits, limb_base); underflow diff --git a/halo2-ecc/src/bigint/carry_mod.rs b/halo2-ecc/src/bigint/carry_mod.rs index 111f31d5..74468d3c 100644 --- a/halo2-ecc/src/bigint/carry_mod.rs +++ b/halo2-ecc/src/bigint/carry_mod.rs @@ -1,8 +1,9 @@ use super::{check_carry_to_zero, CRTInteger, OverflowInteger}; +use crate::fields::PrimeField; use crate::halo2_proofs::circuit::Value; use halo2_base::{ gates::{range::RangeStrategy, GateInstructions, RangeInstructions}, - utils::{biguint_to_fe, decompose_bigint_option, value_to_option, PrimeField}, + utils::{biguint_to_fe, decompose_bigint_option, value_to_option}, AssignedValue, Context, QuantumCell::{Constant, Existing, Witness}, }; @@ -20,11 +21,11 @@ use std::{cmp::max, iter}; // We constrain `a = out + modulus * quotient` and range check `out` and `quotient` // // Assumption: the leading two bits (in big endian) are 1, and `abs(a) <= 2^{n * k - 1 + F::NUM_BITS - 2}` (A weaker assumption is also enough, but this is good enough for forseeable use cases) -pub fn crt<'a, F: PrimeField>( +pub fn crt( range: &impl RangeInstructions, // chip: &BigIntConfig, - ctx: &mut Context<'a, F>, - a: &CRTInteger<'a, F>, + ctx: &mut Context, + a: &CRTInteger, k_bits: usize, // = a.len().bits() modulus: &BigInt, mod_vec: &[F], @@ -32,7 +33,7 @@ pub fn crt<'a, F: PrimeField>( limb_bits: usize, limb_bases: &[F], limb_base_big: &BigInt, -) -> CRTInteger<'a, F> { +) -> CRTInteger { let n = limb_bits; let k = a.truncation.limbs.len(); let trunc_len = n * k; @@ -118,7 +119,7 @@ pub fn crt<'a, F: PrimeField>( let (quot_cell, out_cell, check_cell) = { let prod = range.gate().inner_product_left( ctx, - quot_assigned.iter().map(|a| Existing(a)).chain(iter::once(Witness(quot_v))), + quot_assigned.iter().map(|a| Existing(*a)).chain(iter::once(Witness(quot_v))), mod_vec[..=i].iter().rev().map(|c| Constant(*c)), &mut tmp_assigned, ); @@ -138,7 +139,7 @@ pub fn crt<'a, F: PrimeField>( // dbg!(*alloc); alloc.1 = 0; alloc.0 += 1; - range.gate().assign_region_last(ctx, [Existing(&prod)], []); + range.gate().assign_region_last(ctx, [Existing(prod)], []); } match range.strategy() { RangeStrategy::Vertical => { @@ -148,10 +149,10 @@ pub fn crt<'a, F: PrimeField>( let mut assignments = range.gate().assign_region( ctx, [ - Constant(-F::one()), - Existing(a_limb), + Constant(-F::ONE), + Existing(*a_limb), Witness(temp1), - Constant(F::one()), + Constant(F::ONE), Witness(out_v), Witness(check_val), ], @@ -167,8 +168,8 @@ pub fn crt<'a, F: PrimeField>( // | 0 | -1| 1 | let mut assignments = range.gate().assign_region( ctx, - [Existing(a_limb), Witness(out_v), Witness(check_val)], - [(-1, Some([F::zero(), -F::one(), F::one()]))], + [Existing(*a_limb), Witness(out_v), Witness(check_val)], + [(-1, Some([F::ZERO, -F::ONE, F::ONE]))], ); check_cell = assignments.pop().unwrap(); out_cell = assignments.pop().unwrap(); @@ -204,7 +205,7 @@ pub fn crt<'a, F: PrimeField>( // | quot_cell | 2^n | 1 | quot_cell + 2^n | range.gate().assign_region_last( ctx, - [Existing(quot_cell), Constant(limb_base), Constant(F::one()), Witness(out_val)], + [Existing(*quot_cell), Constant(limb_base), Constant(F::ONE), Witness(out_val)], [(0, None)], ) }; @@ -249,10 +250,10 @@ pub fn crt<'a, F: PrimeField>( let _native_computation = range.gate().assign_region_last( ctx, [ - Existing(&out_native_assigned), + Existing(out_native_assigned), Constant(mod_native), - Existing("_native_assigned), - Existing(&a.native), + Existing(quot_native_assigned), + Existing(a.native), ], [(0, None)], ); diff --git a/halo2-ecc/src/bigint/check_carry_mod_to_zero.rs b/halo2-ecc/src/bigint/check_carry_mod_to_zero.rs index 38453da0..8e16ecaf 100644 --- a/halo2-ecc/src/bigint/check_carry_mod_to_zero.rs +++ b/halo2-ecc/src/bigint/check_carry_mod_to_zero.rs @@ -1,8 +1,9 @@ use super::{check_carry_to_zero, CRTInteger, OverflowInteger}; +use crate::fields::PrimeField; use crate::halo2_proofs::circuit::Value; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::{biguint_to_fe, decompose_bigint_option, value_to_option, PrimeField}, + utils::{biguint_to_fe, decompose_bigint_option, value_to_option}, AssignedValue, Context, QuantumCell::{Constant, Existing, Witness}, }; @@ -14,11 +15,11 @@ use std::{cmp::max, iter}; // same as carry_mod::crt but `out = 0` so no need to range check // // Assumption: the leading two bits (in big endian) are 1, and `a.max_size <= 2^{n * k - 1 + F::NUM_BITS - 2}` (A weaker assumption is also enough) -pub fn crt<'a, F: PrimeField>( +pub fn crt( range: &impl RangeInstructions, // chip: &BigIntConfig, - ctx: &mut Context<'a, F>, - a: &CRTInteger<'a, F>, + ctx: &mut Context, + a: &CRTInteger, k_bits: usize, // = a.len().bits() modulus: &BigInt, mod_vec: &[F], @@ -89,7 +90,7 @@ pub fn crt<'a, F: PrimeField>( let (quot_cell, check_cell) = { let prod = range.gate().inner_product_left( ctx, - quot_assigned.iter().map(Existing).chain(iter::once(Witness(quot_v))), + quot_assigned.iter().copied().map(Existing).chain(iter::once(Witness(quot_v))), mod_vec[0..=i].iter().rev().map(|c| Constant(*c)), &mut tmp_assigned, ); @@ -105,13 +106,13 @@ pub fn crt<'a, F: PrimeField>( // edge case, we need to copy the last `prod` cell alloc.1 = 0; alloc.0 += 1; - range.gate().assign_region_last(ctx, vec![Existing(&prod)], vec![]); + range.gate().assign_region_last(ctx, vec![Existing(prod)], vec![]); } let check_val = prod.value().zip(a_limb.value()).map(|(prod, a)| *prod - a); let check_cell = range.gate().assign_region_last( ctx, - vec![Constant(-F::one()), Existing(a_limb), Witness(check_val)], + vec![Constant(-F::ONE), Existing(*a_limb), Witness(check_val)], vec![(-1, None)], ); @@ -139,12 +140,7 @@ pub fn crt<'a, F: PrimeField>( // | quot_cell | 2^n | 1 | quot_cell + 2^n | range.gate().assign_region_last( ctx, - vec![ - Existing(quot_cell), - Constant(limb_base), - Constant(F::one()), - Witness(out_val), - ], + vec![Existing(*quot_cell), Constant(limb_base), Constant(F::ONE), Witness(out_val)], vec![(0, None)], ) }; @@ -179,10 +175,10 @@ pub fn crt<'a, F: PrimeField>( let _native_computation = range.gate().assign_region( ctx, vec![ - Constant(F::zero()), + Constant(F::ZERO), Constant(mod_native), - Existing("_native_assigned), - Existing(&a.native), + Existing(quot_native_assigned), + Existing(a.native), ], vec![(0, None)], ); diff --git a/halo2-ecc/src/bigint/check_carry_to_zero.rs b/halo2-ecc/src/bigint/check_carry_to_zero.rs index e718b128..4fcd7a9b 100644 --- a/halo2-ecc/src/bigint/check_carry_to_zero.rs +++ b/halo2-ecc/src/bigint/check_carry_to_zero.rs @@ -1,8 +1,9 @@ use super::OverflowInteger; +use crate::fields::PrimeField; use crate::halo2_proofs::circuit::Value; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::{bigint_to_fe, biguint_to_fe, fe_to_bigint, value_to_option, PrimeField}, + utils::{bigint_to_fe, biguint_to_fe, fe_to_bigint, value_to_option}, Context, QuantumCell::{Constant, Existing, Witness}, }; @@ -26,10 +27,10 @@ use num_traits::One; // a_i * 2^{n*w} + a_{i - 1} * 2^{n*(w-1)} + ... + a_{i - w} + c_{i - w - 1} = c_i * 2^{n*(w+1)} // which is valid as long as `(m - n + EPSILON) + n * (w+1) < native_modulus::().bits() - 1` // so we only need to range check `c_i` every `w + 1` steps, starting with `i = w` -pub fn truncate<'a, F: PrimeField>( +pub fn truncate( range: &impl RangeInstructions, - ctx: &mut Context<'a, F>, - a: &OverflowInteger<'a, F>, + ctx: &mut Context, + a: &OverflowInteger, limb_bits: usize, limb_base: F, limb_base_big: &BigInt, @@ -82,10 +83,10 @@ pub fn truncate<'a, F: PrimeField>( .assign_region( ctx, vec![ - Existing(a_limb), + Existing(*a_limb), Witness(neg_carry_val), Constant(limb_base), - previous.as_ref().map(Existing).unwrap_or_else(|| Constant(F::zero())), + previous.as_ref().copied().map(Existing).unwrap_or_else(|| Constant(F::ZERO)), ], vec![(0, None)], ) @@ -99,8 +100,8 @@ pub fn truncate<'a, F: PrimeField>( let shifted_carry = { let shift_carry_val = Value::known(shift_val) + neg_carry.value(); let cells = vec![ - Existing(&neg_carry), - Constant(F::one()), + Existing(neg_carry), + Constant(F::ONE), Constant(shift_val), Witness(shift_carry_val), ]; diff --git a/halo2-ecc/src/bigint/mod.rs b/halo2-ecc/src/bigint/mod.rs index 44b65a0b..425de2b6 100644 --- a/halo2-ecc/src/bigint/mod.rs +++ b/halo2-ecc/src/bigint/mod.rs @@ -1,10 +1,11 @@ +use crate::fields::PrimeField; use crate::halo2_proofs::{ circuit::{Cell, Value}, plonk::ConstraintSystem, }; use halo2_base::{ gates::{flex_gate::FlexGateConfig, GateInstructions}, - utils::{biguint_to_fe, decompose_biguint, fe_to_biguint, PrimeField}, + utils::{biguint_to_fe, decompose_biguint, fe_to_biguint}, AssignedValue, Context, QuantumCell::{Constant, Existing, Witness}, }; @@ -29,8 +30,7 @@ pub mod select_by_indicator; pub mod sub; pub mod sub_no_carry; -#[derive(Clone, Debug, PartialEq)] -#[derive(Default)] +#[derive(Clone, Debug, PartialEq, Default)] pub enum BigIntStrategy { // use existing gates #[default] @@ -40,19 +40,17 @@ pub enum BigIntStrategy { // CustomVerticalShort, } - - #[derive(Clone, Debug)] -pub struct OverflowInteger<'v, F: PrimeField> { - pub limbs: Vec>, +pub struct OverflowInteger { + pub limbs: Vec>, // max bits of a limb, ignoring sign pub max_limb_bits: usize, // the standard limb bit that we use for pow of two limb base - to reduce overhead we just assume this is inferred from context (e.g., the chip stores it), so we stop storing it here // pub limb_bits: usize, } -impl<'v, F: PrimeField> OverflowInteger<'v, F> { - pub fn construct(limbs: Vec>, max_limb_bits: usize) -> Self { +impl<'v, F: PrimeField> OverflowInteger { + pub fn construct(limbs: Vec>, max_limb_bits: usize) -> Self { Self { limbs, max_limb_bits } } @@ -69,14 +67,14 @@ impl<'v, F: PrimeField> OverflowInteger<'v, F> { pub fn evaluate( gate: &impl GateInstructions, // chip: &BigIntConfig, - ctx: &mut Context<'_, F>, - limbs: &[AssignedValue<'v, F>], + ctx: &mut Context, + limbs: &[AssignedValue], limb_bases: impl IntoIterator, - ) -> AssignedValue<'v, F> { + ) -> AssignedValue { // Constrain `out_native = sum_i out_assigned[i] * 2^{n*i}` in `F` gate.inner_product( ctx, - limbs.iter().map(|a| Existing(a)), + limbs.iter().map(|a| Existing(*a)), limb_bases.into_iter().map(|c| Constant(c)), ) } @@ -107,30 +105,30 @@ impl FixedOverflowInteger { .fold(BigUint::zero(), |acc, x| (acc << limb_bits) + fe_to_biguint(x)) } - pub fn assign<'v>( + pub fn assign( self, gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, + ctx: &mut Context, limb_bits: usize, - ) -> OverflowInteger<'v, F> { + ) -> OverflowInteger { let assigned_limbs = gate.assign_region(ctx, self.limbs.into_iter().map(Constant), vec![]); OverflowInteger::construct(assigned_limbs, limb_bits) } /// only use case is when coeffs has only a single 1, rest are 0 - pub fn select_by_indicator<'v>( + pub fn select_by_indicator( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, + ctx: &mut Context, a: &[Self], - coeffs: &[AssignedValue<'v, F>], + coeffs: &[AssignedValue], limb_bits: usize, - ) -> OverflowInteger<'v, F> { + ) -> OverflowInteger { let k = a[0].limbs.len(); let out_limbs = (0..k) .map(|idx| { let int_limbs = a.iter().map(|a| Constant(a.limbs[idx])); - gate.select_by_indicator(ctx, int_limbs, coeffs.iter()) + gate.select_by_indicator(ctx, int_limbs, coeffs.iter().copied()) }) .collect(); @@ -139,7 +137,7 @@ impl FixedOverflowInteger { } #[derive(Clone, Debug)] -pub struct CRTInteger<'v, F: PrimeField> { +pub struct CRTInteger { // keep track of an integer `a` using CRT as `a mod 2^t` and `a mod n` // where `t = truncation.limbs.len() * truncation.limb_bits` // `n = modulus::` @@ -151,25 +149,25 @@ pub struct CRTInteger<'v, F: PrimeField> { // the IMPLICIT ASSUMPTION: `value (mod 2^t) = truncation` && `value (mod n) = native` // this struct should only be used if the implicit assumption above is satisfied - pub truncation: OverflowInteger<'v, F>, - pub native: AssignedValue<'v, F>, + pub truncation: OverflowInteger, + pub native: AssignedValue, pub value: Value, } -impl<'v, F: PrimeField> CRTInteger<'v, F> { +impl CRTInteger { pub fn construct( - truncation: OverflowInteger<'v, F>, - native: AssignedValue<'v, F>, + truncation: OverflowInteger, + native: AssignedValue, value: Value, ) -> Self { Self { truncation, native, value } } - pub fn native(&self) -> &AssignedValue<'v, F> { + pub fn native(&self) -> &AssignedValue { &self.native } - pub fn limbs(&self) -> &[AssignedValue<'v, F>] { + pub fn limbs(&self) -> &[AssignedValue] { self.truncation.limbs.as_slice() } } @@ -210,13 +208,13 @@ impl FixedCRTInteger { Self { truncation, value } } - pub fn assign<'a>( + pub fn assign( self, gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, + ctx: &mut Context, limb_bits: usize, native_modulus: &BigUint, - ) -> CRTInteger<'a, F> { + ) -> CRTInteger { let assigned_truncation = self.truncation.assign(gate, ctx, limb_bits); let assigned_native = { let native_cells = vec![Constant(biguint_to_fe(&(&self.value % native_modulus)))]; @@ -225,13 +223,13 @@ impl FixedCRTInteger { CRTInteger::construct(assigned_truncation, assigned_native, Value::known(self.value.into())) } - pub fn assign_without_caching<'a>( + pub fn assign_without_caching( self, gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, + ctx: &mut Context, limb_bits: usize, native_modulus: &BigUint, - ) -> CRTInteger<'a, F> { + ) -> CRTInteger { let fixed_cells = self .truncation .limbs diff --git a/halo2-ecc/src/bigint/mul_no_carry.rs b/halo2-ecc/src/bigint/mul_no_carry.rs index 637c17e6..01bd882d 100644 --- a/halo2-ecc/src/bigint/mul_no_carry.rs +++ b/halo2-ecc/src/bigint/mul_no_carry.rs @@ -1,14 +1,15 @@ use super::{CRTInteger, OverflowInteger}; -use halo2_base::{gates::GateInstructions, utils::PrimeField, Context, QuantumCell::Existing}; +use crate::fields::PrimeField; +use halo2_base::{gates::GateInstructions, Context, QuantumCell::Existing}; -pub fn truncate<'v, F: PrimeField>( +pub fn truncate( gate: &impl GateInstructions, // _chip: &BigIntConfig, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, - b: &OverflowInteger<'v, F>, + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, num_limbs_log2_ceil: usize, -) -> OverflowInteger<'v, F> { +) -> OverflowInteger { let k = a.limbs.len(); assert!(k > 0); assert_eq!(k, b.limbs.len()); @@ -28,8 +29,8 @@ pub fn truncate<'v, F: PrimeField>( .map(|i| { gate.inner_product( ctx, - a.limbs[..=i].iter().map(Existing), - b.limbs[..=i].iter().rev().map(Existing), + a.limbs[..=i].iter().copied().map(Existing), + b.limbs[..=i].iter().copied().rev().map(Existing), ) }) .collect(); @@ -37,16 +38,16 @@ pub fn truncate<'v, F: PrimeField>( OverflowInteger::construct(out_limbs, num_limbs_log2_ceil + a.max_limb_bits + b.max_limb_bits) } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, // chip: &BigIntConfig, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, num_limbs_log2_ceil: usize, -) -> CRTInteger<'v, F> { +) -> CRTInteger { let out_trunc = truncate::(gate, ctx, &a.truncation, &b.truncation, num_limbs_log2_ceil); - let out_native = gate.mul(ctx, Existing(&a.native), Existing(&b.native)); + let out_native = gate.mul(ctx, Existing(a.native), Existing(b.native)); let out_val = a.value.as_ref() * b.value.as_ref(); CRTInteger::construct(out_trunc, out_native, out_val) diff --git a/halo2-ecc/src/bigint/negative.rs b/halo2-ecc/src/bigint/negative.rs index 60183c3f..6294b702 100644 --- a/halo2-ecc/src/bigint/negative.rs +++ b/halo2-ecc/src/bigint/negative.rs @@ -1,11 +1,12 @@ use super::OverflowInteger; -use halo2_base::{gates::GateInstructions, utils::PrimeField, Context, QuantumCell::Existing}; +use crate::fields::PrimeField; +use halo2_base::{gates::GateInstructions, Context, QuantumCell::Existing}; -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, -) -> OverflowInteger<'v, F> { - let out_limbs = a.limbs.iter().map(|limb| gate.neg(ctx, Existing(limb))).collect(); + ctx: &mut Context, + a: &OverflowInteger, +) -> OverflowInteger { + let out_limbs = a.limbs.iter().map(|limb| gate.neg(ctx, Existing(*limb))).collect(); OverflowInteger::construct(out_limbs, a.max_limb_bits) } diff --git a/halo2-ecc/src/bigint/scalar_mul_and_add_no_carry.rs b/halo2-ecc/src/bigint/scalar_mul_and_add_no_carry.rs index 1c64e24f..9888d454 100644 --- a/halo2-ecc/src/bigint/scalar_mul_and_add_no_carry.rs +++ b/halo2-ecc/src/bigint/scalar_mul_and_add_no_carry.rs @@ -1,7 +1,8 @@ use super::{CRTInteger, OverflowInteger}; +use crate::fields::PrimeField; use halo2_base::{ gates::GateInstructions, - utils::{log2_ceil, PrimeField}, + utils::log2_ceil, Context, QuantumCell::{Constant, Existing, Witness}, }; @@ -9,14 +10,14 @@ use std::cmp::max; /// compute a * c + b = b + a * c // this is uniquely suited for our simple gate -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, - b: &OverflowInteger<'v, F>, + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, c_f: F, c_log2_ceil: usize, -) -> OverflowInteger<'v, F> { +) -> OverflowInteger { assert_eq!(a.limbs.len(), b.limbs.len()); let out_limbs = a @@ -27,7 +28,7 @@ pub fn assign<'v, F: PrimeField>( let out_val = a_limb.value().zip(b_limb.value()).map(|(a, b)| c_f * a + b); gate.assign_region_last( ctx, - vec![Existing(b_limb), Existing(a_limb), Constant(c_f), Witness(out_val)], + vec![Existing(*b_limb), Existing(*a_limb), Constant(c_f), Witness(out_val)], vec![(0, None)], ) }) @@ -36,13 +37,13 @@ pub fn assign<'v, F: PrimeField>( OverflowInteger::construct(out_limbs, max(a.max_limb_bits + c_log2_ceil, b.max_limb_bits) + 1) } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, c: i64, -) -> CRTInteger<'v, F> { +) -> CRTInteger { assert_eq!(a.truncation.limbs.len(), b.truncation.limbs.len()); let (c_f, c_abs) = if c >= 0 { @@ -58,7 +59,7 @@ pub fn crt<'v, F: PrimeField>( let out_val = b.native.value().zip(a.native.value()).map(|(b, a)| c_f * a + b); gate.assign_region_last( ctx, - vec![Existing(&b.native), Existing(&a.native), Constant(c_f), Witness(out_val)], + vec![Existing(b.native), Existing(a.native), Constant(c_f), Witness(out_val)], vec![(0, None)], ) }; diff --git a/halo2-ecc/src/bigint/scalar_mul_no_carry.rs b/halo2-ecc/src/bigint/scalar_mul_no_carry.rs index 4aff4b0c..3f58588e 100644 --- a/halo2-ecc/src/bigint/scalar_mul_no_carry.rs +++ b/halo2-ecc/src/bigint/scalar_mul_no_carry.rs @@ -1,29 +1,30 @@ use super::{CRTInteger, OverflowInteger}; +use crate::fields::PrimeField; use halo2_base::{ gates::GateInstructions, - utils::{log2_ceil, PrimeField}, + utils::log2_ceil, Context, QuantumCell::{Constant, Existing}, }; -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, + ctx: &mut Context, + a: &OverflowInteger, c_f: F, c_log2_ceil: usize, -) -> OverflowInteger<'v, F> { +) -> OverflowInteger { let out_limbs = - a.limbs.iter().map(|limb| gate.mul(ctx, Existing(limb), Constant(c_f))).collect(); + a.limbs.iter().map(|limb| gate.mul(ctx, Existing(*limb), Constant(c_f))).collect(); OverflowInteger::construct(out_limbs, a.max_limb_bits + c_log2_ceil) } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, + ctx: &mut Context, + a: &CRTInteger, c: i64, -) -> CRTInteger<'v, F> { +) -> CRTInteger { let (c_f, c_abs) = if c >= 0 { let c_abs = u64::try_from(c).unwrap(); (F::from(c_abs), c_abs) @@ -36,10 +37,10 @@ pub fn crt<'v, F: PrimeField>( .truncation .limbs .iter() - .map(|limb| gate.mul(ctx, Existing(limb), Constant(c_f))) + .map(|limb| gate.mul(ctx, Existing(*limb), Constant(c_f))) .collect(); - let out_native = gate.mul(ctx, Existing(&a.native), Constant(c_f)); + let out_native = gate.mul(ctx, Existing(a.native), Constant(c_f)); let out_val = a.value.as_ref().map(|a| a * c); CRTInteger::construct( diff --git a/halo2-ecc/src/bigint/select.rs b/halo2-ecc/src/bigint/select.rs index aa296164..1c76a808 100644 --- a/halo2-ecc/src/bigint/select.rs +++ b/halo2-ecc/src/bigint/select.rs @@ -1,41 +1,44 @@ use super::{CRTInteger, OverflowInteger}; -use halo2_base::{ - gates::GateInstructions, utils::PrimeField, AssignedValue, Context, QuantumCell::Existing, -}; +use crate::fields::PrimeField; +use halo2_base::{gates::GateInstructions, AssignedValue, Context, QuantumCell::Existing}; use std::cmp::max; -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, - b: &OverflowInteger<'v, F>, - sel: &AssignedValue<'v, F>, -) -> OverflowInteger<'v, F> { + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, + sel: &AssignedValue, +) -> OverflowInteger { assert_eq!(a.limbs.len(), b.limbs.len()); let out_limbs = a .limbs .iter() .zip(b.limbs.iter()) - .map(|(a_limb, b_limb)| gate.select(ctx, Existing(a_limb), Existing(b_limb), Existing(sel))) + .map(|(a_limb, b_limb)| { + gate.select(ctx, Existing(*a_limb), Existing(*b_limb), Existing(*sel)) + }) .collect(); OverflowInteger::construct(out_limbs, max(a.max_limb_bits, b.max_limb_bits)) } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, - sel: &AssignedValue<'v, F>, -) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, + sel: &AssignedValue, +) -> CRTInteger { assert_eq!(a.truncation.limbs.len(), b.truncation.limbs.len()); let out_limbs = a .truncation .limbs .iter() .zip(b.truncation.limbs.iter()) - .map(|(a_limb, b_limb)| gate.select(ctx, Existing(a_limb), Existing(b_limb), Existing(sel))) + .map(|(a_limb, b_limb)| { + gate.select(ctx, Existing(*a_limb), Existing(*b_limb), Existing(*sel)) + }) .collect(); let out_trunc = OverflowInteger::construct( @@ -43,7 +46,7 @@ pub fn crt<'v, F: PrimeField>( max(a.truncation.max_limb_bits, b.truncation.max_limb_bits), ); - let out_native = gate.select(ctx, Existing(&a.native), Existing(&b.native), Existing(sel)); + let out_native = gate.select(ctx, Existing(a.native), Existing(b.native), Existing(*sel)); let out_val = a.value.as_ref().zip(b.value.as_ref()).zip(sel.value()).map(|((a, b), s)| { if s.is_zero_vartime() { b.clone() diff --git a/halo2-ecc/src/bigint/select_by_indicator.rs b/halo2-ecc/src/bigint/select_by_indicator.rs index 87597804..c5301a12 100644 --- a/halo2-ecc/src/bigint/select_by_indicator.rs +++ b/halo2-ecc/src/bigint/select_by_indicator.rs @@ -1,25 +1,24 @@ use super::{CRTInteger, OverflowInteger}; +use crate::fields::PrimeField; use crate::halo2_proofs::circuit::Value; -use halo2_base::{ - gates::GateInstructions, utils::PrimeField, AssignedValue, Context, QuantumCell::Existing, -}; +use halo2_base::{gates::GateInstructions, AssignedValue, Context, QuantumCell::Existing}; use num_bigint::BigInt; use num_traits::Zero; use std::cmp::max; /// only use case is when coeffs has only a single 1, rest are 0 -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &[OverflowInteger<'v, F>], - coeffs: &[AssignedValue<'v, F>], -) -> OverflowInteger<'v, F> { + ctx: &mut Context, + a: &[OverflowInteger], + coeffs: &[AssignedValue], +) -> OverflowInteger { let k = a[0].limbs.len(); let out_limbs = (0..k) .map(|idx| { - let int_limbs = a.iter().map(|a| Existing(&a.limbs[idx])); - gate.select_by_indicator(ctx, int_limbs, coeffs.iter()) + let int_limbs = a.iter().map(|a| Existing(a.limbs[idx])); + gate.select_by_indicator(ctx, int_limbs, coeffs.iter().copied()) }) .collect(); @@ -29,20 +28,20 @@ pub fn assign<'v, F: PrimeField>( } /// only use case is when coeffs has only a single 1, rest are 0 -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &[CRTInteger<'v, F>], - coeffs: &[AssignedValue<'v, F>], + ctx: &mut Context, + a: &[CRTInteger], + coeffs: &[AssignedValue], limb_bases: &[F], -) -> CRTInteger<'v, F> { +) -> CRTInteger { assert_eq!(a.len(), coeffs.len()); let k = a[0].truncation.limbs.len(); let out_limbs = (0..k) .map(|idx| { - let int_limbs = a.iter().map(|a| Existing(&a.truncation.limbs[idx])); - gate.select_by_indicator(ctx, int_limbs, coeffs.iter()) + let int_limbs = a.iter().map(|a| Existing(a.truncation.limbs[idx])); + gate.select_by_indicator(ctx, int_limbs, coeffs.iter().copied()) }) .collect(); @@ -52,8 +51,8 @@ pub fn crt<'v, F: PrimeField>( let out_native = if a.len() > k { OverflowInteger::::evaluate(gate, ctx, &out_trunc.limbs, limb_bases[..k].iter().cloned()) } else { - let a_native = a.iter().map(|x| Existing(&x.native)); - gate.select_by_indicator(ctx, a_native, coeffs.iter()) + let a_native = a.iter().map(|x| Existing(x.native)); + gate.select_by_indicator(ctx, a_native, coeffs.iter().copied()) }; let out_val = a.iter().zip(coeffs.iter()).fold(Value::known(BigInt::zero()), |acc, (x, y)| { acc.zip(x.value.as_ref()).zip(y.value()).map(|((a, x), y)| { diff --git a/halo2-ecc/src/bigint/sub.rs b/halo2-ecc/src/bigint/sub.rs index 5e987f0c..f8870df0 100644 --- a/halo2-ecc/src/bigint/sub.rs +++ b/halo2-ecc/src/bigint/sub.rs @@ -1,20 +1,20 @@ use super::{CRTInteger, OverflowInteger}; +use crate::fields::PrimeField; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::PrimeField, AssignedValue, Context, QuantumCell::{Constant, Existing, Witness}, }; /// Should only be called on integers a, b in proper representation with all limbs having at most `limb_bits` number of bits -pub fn assign<'a, F: PrimeField>( +pub fn assign( range: &impl RangeInstructions, - ctx: &mut Context<'a, F>, - a: &OverflowInteger<'a, F>, - b: &OverflowInteger<'a, F>, + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, limb_bits: usize, limb_base: F, -) -> (OverflowInteger<'a, F>, AssignedValue<'a, F>) { +) -> (OverflowInteger, AssignedValue) { assert!(a.max_limb_bits <= limb_bits); assert!(b.max_limb_bits <= limb_bits); assert_eq!(a.limbs.len(), b.limbs.len()); @@ -25,15 +25,15 @@ pub fn assign<'a, F: PrimeField>( for (a_limb, b_limb) in a.limbs.iter().zip(b.limbs.iter()) { let (bottom, lt) = match borrow { None => { - let lt = range.is_less_than(ctx, Existing(a_limb), Existing(b_limb), limb_bits); + let lt = range.is_less_than(ctx, Existing(*a_limb), Existing(*b_limb), limb_bits); (b_limb.clone(), lt) } Some(borrow) => { - let b_plus_borrow = range.gate().add(ctx, Existing(b_limb), Existing(&borrow)); + let b_plus_borrow = range.gate().add(ctx, Existing(*b_limb), Existing(borrow)); let lt = range.is_less_than( ctx, - Existing(a_limb), - Existing(&b_plus_borrow), + Existing(*a_limb), + Existing(b_plus_borrow), limb_bits + 1, ); (b_plus_borrow, lt) @@ -47,12 +47,12 @@ pub fn assign<'a, F: PrimeField>( range.gate().assign_region_last( ctx, vec![ - Existing(a_limb), - Existing(<), + Existing(*a_limb), + Existing(lt), Constant(limb_base), Witness(a_with_borrow_val), - Constant(-F::one()), - Existing(&bottom), + Constant(-F::ONE), + Existing(bottom), Witness(out_val), ], vec![(0, None), (3, None)], @@ -65,17 +65,17 @@ pub fn assign<'a, F: PrimeField>( } // returns (a-b, underflow), where underflow is nonzero iff a < b -pub fn crt<'a, F: PrimeField>( +pub fn crt( range: &impl RangeInstructions, - ctx: &mut Context<'a, F>, - a: &CRTInteger<'a, F>, - b: &CRTInteger<'a, F>, + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, limb_bits: usize, limb_base: F, -) -> (CRTInteger<'a, F>, AssignedValue<'a, F>) { +) -> (CRTInteger, AssignedValue) { let (out_trunc, underflow) = assign::(range, ctx, &a.truncation, &b.truncation, limb_bits, limb_base); - let out_native = range.gate().sub(ctx, Existing(&a.native), Existing(&b.native)); + let out_native = range.gate().sub(ctx, Existing(a.native), Existing(b.native)); let out_val = a.value.as_ref().zip(b.value.as_ref()).map(|(a, b)| a - b); (CRTInteger::construct(out_trunc, out_native, out_val), underflow) } diff --git a/halo2-ecc/src/bigint/sub_no_carry.rs b/halo2-ecc/src/bigint/sub_no_carry.rs index 2226027d..78ac4577 100644 --- a/halo2-ecc/src/bigint/sub_no_carry.rs +++ b/halo2-ecc/src/bigint/sub_no_carry.rs @@ -1,32 +1,33 @@ use super::{CRTInteger, OverflowInteger}; -use halo2_base::{gates::GateInstructions, utils::PrimeField, Context, QuantumCell::Existing}; +use crate::fields::PrimeField; +use halo2_base::{gates::GateInstructions, Context, QuantumCell::Existing}; use std::cmp::max; -pub fn assign<'v, F: PrimeField>( +pub fn assign( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &OverflowInteger<'v, F>, - b: &OverflowInteger<'v, F>, -) -> OverflowInteger<'v, F> { + ctx: &mut Context, + a: &OverflowInteger, + b: &OverflowInteger, +) -> OverflowInteger { assert_eq!(a.limbs.len(), b.limbs.len()); let out_limbs = a .limbs .iter() .zip(b.limbs.iter()) - .map(|(a_limb, b_limb)| gate.sub(ctx, Existing(a_limb), Existing(b_limb))) + .map(|(a_limb, b_limb)| gate.sub(ctx, Existing(*a_limb), Existing(*b_limb))) .collect(); OverflowInteger::construct(out_limbs, max(a.max_limb_bits, b.max_limb_bits) + 1) } -pub fn crt<'v, F: PrimeField>( +pub fn crt( gate: &impl GateInstructions, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, -) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, +) -> CRTInteger { let out_trunc = assign::(gate, ctx, &a.truncation, &b.truncation); - let out_native = gate.sub(ctx, Existing(&a.native), Existing(&b.native)); + let out_native = gate.sub(ctx, Existing(a.native), Existing(b.native)); let out_val = a.value.as_ref().zip(b.value.as_ref()).map(|(a, b)| a - b); CRTInteger::construct(out_trunc, out_native, out_val) } diff --git a/halo2-ecc/src/bn254/configs/bench_ec_add.config b/halo2-ecc/src/bn254/configs/bench_ec_add.config index ab65ccee..34597284 100644 --- a/halo2-ecc/src/bn254/configs/bench_ec_add.config +++ b/halo2-ecc/src/bn254/configs/bench_ec_add.config @@ -2,4 +2,4 @@ {"strategy":"Simple","degree":16,"num_advice":5,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":15,"limb_bits":90,"num_limbs":3,"batch_size":100} {"strategy":"Simple","degree":17,"num_advice":3,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":16,"limb_bits":88,"num_limbs":3,"batch_size":100} {"strategy":"Simple","degree":18,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":17,"limb_bits":88,"num_limbs":3,"batch_size":100} -{"strategy":"Simple","degree":19,"num_advice":1,"num_lookup_advice":0,"num_fixed":1,"lookup_bits":18,"limb_bits":90,"num_limbs":3,"batch_size":100} \ No newline at end of file +{"strategy":"Simple","degree":19,"num_advice":1,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":90,"num_limbs":3,"batch_size":100} \ No newline at end of file diff --git a/halo2-ecc/src/bn254/configs/ec_add_circuit.tmp.config b/halo2-ecc/src/bn254/configs/ec_add_circuit.tmp.config new file mode 100644 index 00000000..aa7ffe86 --- /dev/null +++ b/halo2-ecc/src/bn254/configs/ec_add_circuit.tmp.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":15,"num_advice":10,"num_lookup_advice":2,"num_fixed":1,"lookup_bits":14,"limb_bits":88,"num_limbs":3,"batch_size":100} \ No newline at end of file diff --git a/halo2-ecc/src/bn254/configs/fixed_msm_circuit.tmp.config b/halo2-ecc/src/bn254/configs/fixed_msm_circuit.tmp.config new file mode 100644 index 00000000..bd27f264 --- /dev/null +++ b/halo2-ecc/src/bn254/configs/fixed_msm_circuit.tmp.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":17,"num_advice":83,"num_lookup_advice":9,"num_fixed":7,"lookup_bits":16,"limb_bits":88,"num_limbs":3,"batch_size":100,"radix":0,"clump_factor":4} \ No newline at end of file diff --git a/halo2-ecc/src/bn254/configs/msm_circuit.tmp.config b/halo2-ecc/src/bn254/configs/msm_circuit.tmp.config new file mode 100644 index 00000000..f66f6077 --- /dev/null +++ b/halo2-ecc/src/bn254/configs/msm_circuit.tmp.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":17,"num_advice":84,"num_lookup_advice":11,"num_fixed":1,"lookup_bits":16,"limb_bits":88,"num_limbs":3,"batch_size":100,"window_bits":4} \ No newline at end of file diff --git a/halo2-ecc/src/bn254/configs/pairing_circuit.tmp.config b/halo2-ecc/src/bn254/configs/pairing_circuit.tmp.config new file mode 100644 index 00000000..3aaac67f --- /dev/null +++ b/halo2-ecc/src/bn254/configs/pairing_circuit.tmp.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":14,"num_advice":211,"num_lookup_advice":27,"num_fixed":1,"lookup_bits":13,"limb_bits":91,"num_limbs":3} \ No newline at end of file diff --git a/halo2-ecc/src/bn254/final_exp.rs b/halo2-ecc/src/bn254/final_exp.rs index e131f7d5..6db32b49 100644 --- a/halo2-ecc/src/bn254/final_exp.rs +++ b/halo2-ecc/src/bn254/final_exp.rs @@ -1,15 +1,14 @@ use super::{Fp12Chip, Fp2Chip, FpChip, FpPoint}; -use crate::halo2_proofs::{ - arithmetic::Field, - halo2curves::bn256::{Fq, Fq2, BN_X, FROBENIUS_COEFF_FQ12_C1}, -}; +use crate::fields::PrimeField; +use crate::halo2_proofs::halo2curves::bn256::{Fq, Fq2, BN_X, FROBENIUS_COEFF_FQ12_C1}; use crate::{ ecc::get_naf, fields::{fp12::mul_no_carry_w6, FieldChip, FieldExtPoint}, }; +use ff::Field; use halo2_base::{ gates::GateInstructions, - utils::{fe_to_biguint, modulus, PrimeField}, + utils::{fe_to_biguint, modulus}, Context, QuantumCell::{Constant, Existing}, }; @@ -17,22 +16,22 @@ use num_bigint::BigUint; const XI_0: i64 = 9; -impl<'a, F: PrimeField> Fp12Chip<'a, F> { +impl Fp12Chip { // computes a ** (p ** power) // only works for p = 3 (mod 4) and p = 1 (mod 6) - pub fn frobenius_map<'v>( + pub fn frobenius_map( &self, - ctx: &mut Context<'v, F>, - a: &>::FieldPoint<'v>, + ctx: &mut Context, + a: &>::FieldPoint, power: usize, - ) -> >::FieldPoint<'v> { + ) -> >::FieldPoint { assert_eq!(modulus::() % 4u64, BigUint::from(3u64)); assert_eq!(modulus::() % 6u64, BigUint::from(1u64)); assert_eq!(a.coeffs.len(), 12); let pow = power % 12; let mut out_fp2 = Vec::with_capacity(6); - let fp2_chip = Fp2Chip::::construct(self.fp_chip); + let fp2_chip = Fp2Chip::::construct(self.fp_chip.clone()); for i in 0..6 { let frob_coeff = FROBENIUS_COEFF_FQ12_C1[pow].pow_vartime([i as u64]); // possible optimization (not implemented): load `frob_coeff` as we multiply instead of loading first @@ -68,13 +67,13 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { } // exp is in little-endian - pub fn pow<'v>( + pub fn pow( &self, - ctx: &mut Context<'v, F>, - a: &>::FieldPoint<'v>, + ctx: &mut Context, + a: &>::FieldPoint, exp: Vec, - ) -> >::FieldPoint<'v> { - let mut res = a.clone(); + ) -> >::FieldPoint { + let mut res = (*a).clone(); let mut is_started = false; let naf = get_naf(exp); @@ -86,7 +85,12 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { if z != 0 { assert!(z == 1 || z == -1); if is_started { - res = if z == 1 { self.mul(ctx, &res, a) } else { self.divide(ctx, &res, a) }; + res = if z == 1 { + self.mul(ctx, &res, a) + } else { + // todo: double check that + self.divide_unsafe(ctx, &res, a) + }; } else { assert_eq!(z, 1); is_started = true; @@ -106,10 +110,10 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { /// in = g0 + g2 w + g4 w^2 + g1 w^3 + g3 w^4 + g5 w^5 where g_i = g_i0 + g_i1 * u are elements of Fp2 /// out = Compress(in) = [ g2, g3, g4, g5 ] - pub fn cyclotomic_compress<'v>( + pub fn cyclotomic_compress( &self, - a: &FieldExtPoint>, - ) -> Vec>> { + a: &FieldExtPoint>, + ) -> Vec>> { let g2 = FieldExtPoint::construct(vec![a.coeffs[1].clone(), a.coeffs[1 + 6].clone()]); let g3 = FieldExtPoint::construct(vec![a.coeffs[4].clone(), a.coeffs[4 + 6].clone()]); let g4 = FieldExtPoint::construct(vec![a.coeffs[2].clone(), a.coeffs[2 + 6].clone()]); @@ -129,16 +133,16 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { /// if g2 = 0: /// g1 = (2 g4 * g5)/g3 /// g0 = (2 g1^2 - 3 g3 * g4) * c + 1 - pub fn cyclotomic_decompress<'v>( + pub fn cyclotomic_decompress( &self, - ctx: &mut Context<'v, F>, - compression: Vec>>, - ) -> FieldExtPoint> { - let [g2, g3, g4, g5]: [FieldExtPoint>; 4] = compression.try_into().unwrap(); + ctx: &mut Context, + compression: Vec>>, + ) -> FieldExtPoint> { + let [g2, g3, g4, g5]: [FieldExtPoint>; 4] = compression.try_into().unwrap(); - let fp2_chip = Fp2Chip::::construct(self.fp_chip); + let fp2_chip = Fp2Chip::::construct(self.fp_chip.clone()); let g5_sq = fp2_chip.mul_no_carry(ctx, &g5, &g5); - let g5_sq_c = mul_no_carry_w6::, XI_0>(fp2_chip.fp_chip, ctx, &g5_sq); + let g5_sq_c = mul_no_carry_w6::, XI_0>(&fp2_chip.fp_chip, ctx, &g5_sq); let g4_sq = fp2_chip.mul_no_carry(ctx, &g4, &g4); let g4_sq_3 = fp2_chip.scalar_mul_no_carry(ctx, &g4_sq, 3); @@ -148,11 +152,11 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { g1_num = fp2_chip.sub_no_carry(ctx, &g1_num, &g3_2); // can divide without carrying g1_num or g1_denom (I think) let g2_4 = fp2_chip.scalar_mul_no_carry(ctx, &g2, 4); - let g1_1 = fp2_chip.divide(ctx, &g1_num, &g2_4); + let g1_1 = fp2_chip.divide_unsafe(ctx, &g1_num, &g2_4); let g4_g5 = fp2_chip.mul_no_carry(ctx, &g4, &g5); let g1_num = fp2_chip.scalar_mul_no_carry(ctx, &g4_g5, 2); - let g1_0 = fp2_chip.divide(ctx, &g1_num, &g3); + let g1_0 = fp2_chip.divide_unsafe(ctx, &g1_num, &g3); let g2_is_zero = fp2_chip.is_zero(ctx, &g2); // resulting `g1` is already in "carried" format (witness is in `[0, p)`) @@ -168,16 +172,16 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { let temp = fp2_chip.add_no_carry(ctx, &g1_sq_2, &g2_g5); let temp = fp2_chip.select(ctx, &g1_sq_2, &temp, &g2_is_zero); let temp = fp2_chip.sub_no_carry(ctx, &temp, &g3_g4_3); - let mut g0 = mul_no_carry_w6::, XI_0>(fp2_chip.fp_chip, ctx, &temp); + let mut g0 = mul_no_carry_w6::, XI_0>(&fp2_chip.fp_chip, ctx, &temp); // compute `g0 + 1` g0.coeffs[0].truncation.limbs[0] = fp2_chip.range().gate.add( ctx, - Existing(&g0.coeffs[0].truncation.limbs[0]), - Constant(F::one()), + Existing(g0.coeffs[0].truncation.limbs[0]), + Constant(F::ONE), ); g0.coeffs[0].native = - fp2_chip.range().gate.add(ctx, Existing(&g0.coeffs[0].native), Constant(F::one())); + fp2_chip.range().gate.add(ctx, Existing(g0.coeffs[0].native), Constant(F::ONE)); g0.coeffs[0].truncation.max_limb_bits += 1; g0.coeffs[0].value = g0.coeffs[0].value.as_ref().map(|v| v + 1usize); @@ -217,32 +221,32 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { // A_ij = (g_i + g_j)(g_i + c g_j) // B_ij = g_i g_j - pub fn cyclotomic_square<'v>( + pub fn cyclotomic_square( &self, - ctx: &mut Context<'v, F>, - compression: &[FieldExtPoint>], - ) -> Vec>> { + ctx: &mut Context, + compression: &[FieldExtPoint>], + ) -> Vec>> { assert_eq!(compression.len(), 4); let g2 = &compression[0]; let g3 = &compression[1]; let g4 = &compression[2]; let g5 = &compression[3]; - let fp2_chip = Fp2Chip::::construct(self.fp_chip); + let fp2_chip = Fp2Chip::::construct(self.fp_chip.clone()); let g2_plus_g3 = fp2_chip.add_no_carry(ctx, g2, g3); - let cg3 = mul_no_carry_w6::, XI_0>(fp2_chip.fp_chip, ctx, g3); + let cg3 = mul_no_carry_w6::, XI_0>(&fp2_chip.fp_chip, ctx, g3); let g2_plus_cg3 = fp2_chip.add_no_carry(ctx, g2, &cg3); let a23 = fp2_chip.mul_no_carry(ctx, &g2_plus_g3, &g2_plus_cg3); let g4_plus_g5 = fp2_chip.add_no_carry(ctx, g4, g5); - let cg5 = mul_no_carry_w6::, XI_0>(fp2_chip.fp_chip, ctx, g5); + let cg5 = mul_no_carry_w6::, XI_0>(&fp2_chip.fp_chip, ctx, g5); let g4_plus_cg5 = fp2_chip.add_no_carry(ctx, g4, &cg5); let a45 = fp2_chip.mul_no_carry(ctx, &g4_plus_g5, &g4_plus_cg5); let b23 = fp2_chip.mul_no_carry(ctx, g2, g3); let b45 = fp2_chip.mul_no_carry(ctx, g4, g5); - let b45_c = mul_no_carry_w6::, XI_0>(fp2_chip.fp_chip, ctx, &b45); + let b45_c = mul_no_carry_w6::, XI_0>(&fp2_chip.fp_chip, ctx, &b45); let mut temp = fp2_chip.scalar_mul_and_add_no_carry(ctx, &b45_c, g2, 3); let h2 = fp2_chip.scalar_mul_no_carry(ctx, &temp, 2); @@ -254,7 +258,7 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { const XI0_PLUS_1: i64 = XI_0 + 1; // (c + 1) = (XI_0 + 1) + u - temp = mul_no_carry_w6::, XI0_PLUS_1>(fp2_chip.fp_chip, ctx, &b23); + temp = mul_no_carry_w6::, XI0_PLUS_1>(&fp2_chip.fp_chip, ctx, &b23); temp = fp2_chip.sub_no_carry(ctx, &a23, &temp); temp = fp2_chip.scalar_mul_no_carry(ctx, &temp, 3); let h4 = fp2_chip.scalar_mul_and_add_no_carry(ctx, g4, &temp, -2); @@ -266,12 +270,12 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { } // exp is in little-endian - pub fn cyclotomic_pow<'v>( + pub fn cyclotomic_pow( &self, - ctx: &mut Context<'v, F>, - a: FieldExtPoint>, + ctx: &mut Context, + a: FieldExtPoint>, exp: Vec, - ) -> FieldExtPoint> { + ) -> FieldExtPoint> { let mut compression = self.cyclotomic_compress(&a); let mut out = None; let mut is_started = false; @@ -285,7 +289,11 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { assert!(z == 1 || z == -1); if is_started { let mut res = self.cyclotomic_decompress(ctx, compression); - res = if z == 1 { self.mul(ctx, &res, &a) } else { self.divide(ctx, &res, &a) }; + res = if z == 1 { + self.mul(ctx, &res, &a) + } else { + self.divide_unsafe(ctx, &res, &a) + }; // compression is free, so it doesn't hurt (except possibly witness generation runtime) to do it // TODO: alternatively we go from small bits to large to avoid this compression compression = self.cyclotomic_compress(&res); @@ -304,11 +312,11 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { #[allow(non_snake_case)] // use equation for (p^4 - p^2 + 1)/r in Section 5 of https://eprint.iacr.org/2008/490.pdf for BN curves - pub fn hard_part_BN<'v>( + pub fn hard_part_BN( &self, - ctx: &mut Context<'v, F>, - m: >::FieldPoint<'v>, - ) -> >::FieldPoint<'v> { + ctx: &mut Context, + m: >::FieldPoint, + ) -> >::FieldPoint { // x = BN_X // m^p @@ -325,12 +333,12 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { let y1 = self.conjugate(ctx, &m); // m^x - let mx = self.cyclotomic_pow(ctx, m, vec![BN_X]); + let mx = self.pow(ctx, &m, vec![BN_X]); // (m^x)^p let mxp = self.frobenius_map(ctx, &mx, 1); // m^{x^2} - let mx2 = self.cyclotomic_pow(ctx, mx.clone(), vec![BN_X]); + let mx2 = self.pow(ctx, &mx, vec![BN_X]); // (m^{x^2})^p let mx2p = self.frobenius_map(ctx, &mx2, 1); // y2 = (m^{x^2})^{p^2} @@ -339,7 +347,7 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { // y5 = 1/mx2 let y5 = self.conjugate(ctx, &mx2); - let mx3 = self.cyclotomic_pow(ctx, mx2, vec![BN_X]); + let mx3 = self.pow(ctx, &mx2, vec![BN_X]); // (m^{x^3})^p let mx3p = self.frobenius_map(ctx, &mx3, 1); @@ -372,25 +380,25 @@ impl<'a, F: PrimeField> Fp12Chip<'a, F> { } // out = in^{ (q^6 - 1)*(q^2 + 1) } - pub fn easy_part<'v>( + pub fn easy_part( &self, - ctx: &mut Context<'v, F>, - a: &>::FieldPoint<'v>, - ) -> >::FieldPoint<'v> { + ctx: &mut Context, + a: &>::FieldPoint, + ) -> >::FieldPoint { // a^{q^6} = conjugate of a let f1 = self.conjugate(ctx, a); - let f2 = self.divide(ctx, &f1, a); + let f2 = self.divide_unsafe(ctx, &f1, a); let f3 = self.frobenius_map(ctx, &f2, 2); let f = self.mul(ctx, &f3, &f2); f } // out = in^{(q^12 - 1)/r} - pub fn final_exp<'v>( + pub fn final_exp( &self, - ctx: &mut Context<'v, F>, - a: &>::FieldPoint<'v>, - ) -> >::FieldPoint<'v> { + ctx: &mut Context, + a: &>::FieldPoint, + ) -> >::FieldPoint { let f0 = self.easy_part(ctx, a); let f = self.hard_part_BN(ctx, f0); f diff --git a/halo2-ecc/src/bn254/mod.rs b/halo2-ecc/src/bn254/mod.rs index 5f5db57b..992f10ba 100644 --- a/halo2-ecc/src/bn254/mod.rs +++ b/halo2-ecc/src/bn254/mod.rs @@ -8,10 +8,10 @@ pub mod final_exp; pub mod pairing; type FpChip = fp::FpConfig; -type FpPoint<'v, F> = CRTInteger<'v, F>; -type FqPoint<'v, F> = FieldExtPoint>; -type Fp2Chip<'a, F> = fp2::Fp2Chip<'a, F, FpChip, Fq2>; -type Fp12Chip<'a, F> = fp12::Fp12Chip<'a, F, FpChip, Fq12, 9>; +type FpPoint = CRTInteger; +type FqPoint = FieldExtPoint>; +type Fp2Chip = fp2::Fp2Chip, Fq2>; +type Fp12Chip = fp12::Fp12Chip, Fq12, 9>; #[cfg(test)] pub(crate) mod tests; diff --git a/halo2-ecc/src/bn254/pairing.rs b/halo2-ecc/src/bn254/pairing.rs index 2502ea48..8fded38c 100644 --- a/halo2-ecc/src/bn254/pairing.rs +++ b/halo2-ecc/src/bn254/pairing.rs @@ -1,6 +1,8 @@ #![allow(non_snake_case)] use super::{Fp12Chip, Fp2Chip, FpChip, FpPoint, FqPoint}; +use crate::fields::PrimeField; use crate::halo2_proofs::{ + arithmetic::CurveAffine, circuit::Value, halo2curves::bn256::{self, G1Affine, G2Affine, SIX_U_PLUS_2_NAF}, halo2curves::bn256::{Fq, Fq2, FROBENIUS_COEFF_FQ12_C1}, @@ -12,7 +14,7 @@ use crate::{ fields::{FieldChip, FieldExtPoint}, }; use halo2_base::{ - utils::{biguint_to_fe, fe_to_biguint, PrimeField}, + utils::{biguint_to_fe, fe_to_biguint}, Context, }; use num_bigint::BigUint; @@ -27,12 +29,12 @@ const XI_0: i64 = 9; // line_{Psi(Q0), Psi(Q1)}(P) where Psi(x,y) = (w^2 x, w^3 y) // - equals w^3 (y_1 - y_2) X + w^2 (x_2 - x_1) Y + w^5 (x_1 y_2 - x_2 y_1) =: out3 * w^3 + out2 * w^2 + out5 * w^5 where out2, out3, out5 are Fp2 points // Output is [None, None, out2, out3, None, out5] as vector of `Option`s -pub fn sparse_line_function_unequal<'a, F: PrimeField>( +pub fn sparse_line_function_unequal( fp2_chip: &Fp2Chip, - ctx: &mut Context<'a, F>, - Q: (&EcPoint>, &EcPoint>), - P: &EcPoint>, -) -> Vec>> { + ctx: &mut Context, + Q: (&EcPoint>, &EcPoint>), + P: &EcPoint>, +) -> Vec>> { let (x_1, y_1) = (&Q.0.x, &Q.0.y); let (x_2, y_2) = (&Q.1.x, &Q.1.y); let (X, Y) = (&P.x, &P.y); @@ -66,12 +68,12 @@ pub fn sparse_line_function_unequal<'a, F: PrimeField>( // line_{Psi(Q), Psi(Q)}(P) where Psi(x,y) = (w^2 x, w^3 y) // - equals (3x^3 - 2y^2)(XI_0 + u) + w^4 (-3 x^2 * Q.x) + w^3 (2 y * Q.y) =: out0 + out4 * w^4 + out3 * w^3 where out0, out3, out4 are Fp2 points // Output is [out0, None, None, out3, out4, None] as vector of `Option`s -pub fn sparse_line_function_equal<'a, F: PrimeField>( +pub fn sparse_line_function_equal( fp2_chip: &Fp2Chip, - ctx: &mut Context<'a, F>, - Q: &EcPoint>, - P: &EcPoint>, -) -> Vec>> { + ctx: &mut Context, + Q: &EcPoint>, + P: &EcPoint>, +) -> Vec>> { let (x, y) = (&Q.x, &Q.y); assert_eq!(x.coeffs.len(), 2); assert_eq!(y.coeffs.len(), 2); @@ -83,7 +85,7 @@ pub fn sparse_line_function_equal<'a, F: PrimeField>( let y_sq = fp2_chip.mul_no_carry(ctx, y, y); let two_y_sq = fp2_chip.scalar_mul_no_carry(ctx, &y_sq, 2); let out0_left = fp2_chip.sub_no_carry(ctx, &three_x_cu, &two_y_sq); - let out0 = mul_no_carry_w6::, XI_0>(fp2_chip.fp_chip, ctx, &out0_left); + let out0 = mul_no_carry_w6::, XI_0>(&fp2_chip.fp_chip, ctx, &out0_left); let x_sq_Px = fp2_chip.fp_mul_no_carry(ctx, &x_sq, &P.x); let out4 = fp2_chip.scalar_mul_no_carry(ctx, &x_sq_Px, -3); @@ -101,12 +103,12 @@ pub fn sparse_line_function_equal<'a, F: PrimeField>( // multiply Fp12 point `a` with Fp12 point `b` where `b` is len 6 vector of Fp2 points, where some are `None` to represent zero. // Assumes `b` is not vector of all `None`s -pub fn sparse_fp12_multiply<'a, F: PrimeField>( +pub fn sparse_fp12_multiply( fp2_chip: &Fp2Chip, - ctx: &mut Context<'a, F>, - a: &FqPoint<'a, F>, - b_fp2_coeffs: &Vec>>, -) -> FieldExtPoint> { + ctx: &mut Context, + a: &FqPoint, + b_fp2_coeffs: &Vec>>, +) -> FieldExtPoint> { assert_eq!(a.coeffs.len(), 12); assert_eq!(b_fp2_coeffs.len(), 6); let mut a_fp2_coeffs = Vec::with_capacity(6); @@ -139,7 +141,7 @@ pub fn sparse_fp12_multiply<'a, F: PrimeField>( let prod_nocarry = if i != 5 { let eval_w6 = prod_2d[i + 6] .as_ref() - .map(|a| mul_no_carry_w6::, XI_0>(fp2_chip.fp_chip, ctx, a)); + .map(|a| mul_no_carry_w6::, XI_0>(&fp2_chip.fp_chip, ctx, a)); match (prod_2d[i].as_ref(), eval_w6) { (None, b) => b.unwrap(), // Our current use cases of 235 and 034 sparse multiplication always result in non-None value (Some(a), None) => a.clone(), @@ -168,13 +170,13 @@ pub fn sparse_fp12_multiply<'a, F: PrimeField>( // - P is point in E(Fp) // Output: // - out = g * l_{Psi(Q0), Psi(Q1)}(P) as Fp12 point -pub fn fp12_multiply_with_line_unequal<'a, F: PrimeField>( +pub fn fp12_multiply_with_line_unequal( fp2_chip: &Fp2Chip, - ctx: &mut Context<'a, F>, - g: &FqPoint<'a, F>, - Q: (&EcPoint>, &EcPoint>), - P: &EcPoint>, -) -> FqPoint<'a, F> { + ctx: &mut Context, + g: &FqPoint, + Q: (&EcPoint>, &EcPoint>), + P: &EcPoint>, +) -> FqPoint { let line = sparse_line_function_unequal::(fp2_chip, ctx, Q, P); sparse_fp12_multiply::(fp2_chip, ctx, g, &line) } @@ -185,13 +187,13 @@ pub fn fp12_multiply_with_line_unequal<'a, F: PrimeField>( // - P is point in E(Fp) // Output: // - out = g * l_{Psi(Q), Psi(Q)}(P) as Fp12 point -pub fn fp12_multiply_with_line_equal<'a, F: PrimeField>( +pub fn fp12_multiply_with_line_equal( fp2_chip: &Fp2Chip, - ctx: &mut Context<'a, F>, - g: &FqPoint<'a, F>, - Q: &EcPoint>, - P: &EcPoint>, -) -> FqPoint<'a, F> { + ctx: &mut Context, + g: &FqPoint, + Q: &EcPoint>, + P: &EcPoint>, +) -> FqPoint { let line = sparse_line_function_equal::(fp2_chip, ctx, Q, P); sparse_fp12_multiply::(fp2_chip, ctx, g, &line) } @@ -214,13 +216,16 @@ pub fn fp12_multiply_with_line_equal<'a, F: PrimeField>( // - `0 <= loop_count < r` and `loop_count < p` (to avoid [loop_count]Q' = Frob_p(Q')) // - x^3 + b = 0 has no solution in Fp2, i.e., the y-coordinate of Q cannot be 0. -pub fn miller_loop_BN<'a, 'b, F: PrimeField>( - ecc_chip: &EccChip>, - ctx: &mut Context<'b, F>, - Q: &EcPoint>, - P: &EcPoint>, +pub fn miller_loop_BN( + ecc_chip: &EccChip>, + ctx: &mut Context, + Q: &EcPoint>, + P: &EcPoint>, pseudo_binary_encoding: &[i8], -) -> FqPoint<'b, F> { +) -> FqPoint +where + C: CurveAffine, +{ let mut i = pseudo_binary_encoding.len() - 1; while pseudo_binary_encoding[i] == 0 { i -= 1; @@ -229,7 +234,7 @@ pub fn miller_loop_BN<'a, 'b, F: PrimeField>( let neg_Q = ecc_chip.negate(ctx, Q); assert!(pseudo_binary_encoding[i] == 1 || pseudo_binary_encoding[i] == -1); - let mut R = if pseudo_binary_encoding[i] == 1 { Q.clone() } else { neg_Q.clone() }; + let mut R = if pseudo_binary_encoding[i] == 1 { (*Q).clone() } else { neg_Q.clone() }; i -= 1; // initialize the first line function into Fq12 point @@ -257,11 +262,11 @@ pub fn miller_loop_BN<'a, 'b, F: PrimeField>( loop { if i != last_index - 1 { - let fp12_chip = Fp12Chip::::construct(ecc_chip.field_chip.fp_chip); + let fp12_chip = Fp12Chip::::construct(ecc_chip.field_chip.fp_chip.clone()); let f_sq = fp12_chip.mul(ctx, &f, &f); f = fp12_multiply_with_line_equal::(ecc_chip.field_chip(), ctx, &f_sq, &R, P); } - R = ecc_chip.double(ctx, &R); + R = ecc_chip.double::(ctx, &R); assert!(pseudo_binary_encoding[i] <= 1 && pseudo_binary_encoding[i] >= -1); if pseudo_binary_encoding[i] != 0 { @@ -299,12 +304,15 @@ pub fn miller_loop_BN<'a, 'b, F: PrimeField>( // let pairs = [(a_i, b_i)], a_i in G_1, b_i in G_2 // output is Prod_i e'(a_i, b_i), where e'(a_i, b_i) is the output of `miller_loop_BN(b_i, a_i)` -pub fn multi_miller_loop_BN<'a, 'b, F: PrimeField>( - ecc_chip: &EccChip>, - ctx: &mut Context<'b, F>, - pairs: Vec<(&EcPoint>, &EcPoint>)>, +pub fn multi_miller_loop_BN( + ecc_chip: &EccChip>, + ctx: &mut Context, + pairs: Vec<(&EcPoint>, &EcPoint>)>, pseudo_binary_encoding: &[i8], -) -> FqPoint<'b, F> { +) -> FqPoint +where + C: CurveAffine, +{ let mut i = pseudo_binary_encoding.len() - 1; while pseudo_binary_encoding[i] == 0 { i -= 1; @@ -343,8 +351,8 @@ pub fn multi_miller_loop_BN<'a, 'b, F: PrimeField>( } i -= 1; - let mut r = pairs.iter().map(|pair| pair.1.clone()).collect::>(); - let fp12_chip = Fp12Chip::::construct(ecc_chip.field_chip.fp_chip); + let mut r = pairs.iter().map(|pair| (*pair.1).clone()).collect::>(); + let fp12_chip = Fp12Chip::::construct(ecc_chip.field_chip.fp_chip.clone()); loop { if i != last_index - 1 { f = fp12_chip.mul(ctx, &f, &f); @@ -353,7 +361,7 @@ pub fn multi_miller_loop_BN<'a, 'b, F: PrimeField>( } } for r in r.iter_mut() { - *r = ecc_chip.double(ctx, r); + *r = ecc_chip.double::(ctx, &r); } assert!(pseudo_binary_encoding[i] <= 1 && pseudo_binary_encoding[i] >= -1); @@ -401,13 +409,13 @@ pub fn multi_miller_loop_BN<'a, 'b, F: PrimeField>( // - coeff[1][2], coeff[1][3] as assigned cells: this is an optimization to avoid loading new constants // Output: // - (coeff[1][2] * x^p, coeff[1][3] * y^p) point in E(Fp2) -pub fn twisted_frobenius<'a, 'b, F: PrimeField>( - ecc_chip: &EccChip>, - ctx: &mut Context<'b, F>, - Q: &EcPoint>, - c2: &FqPoint<'b, F>, - c3: &FqPoint<'b, F>, -) -> EcPoint> { +pub fn twisted_frobenius( + ecc_chip: &EccChip>, + ctx: &mut Context, + Q: &EcPoint>, + c2: &FqPoint, + c3: &FqPoint, +) -> EcPoint> { assert_eq!(c2.coeffs.len(), 2); assert_eq!(c3.coeffs.len(), 2); @@ -424,13 +432,13 @@ pub fn twisted_frobenius<'a, 'b, F: PrimeField>( // - Q = (x, y) point in E(Fp2) // Output: // - (coeff[1][2] * x^p, coeff[1][3] * -y^p) point in E(Fp2) -pub fn neg_twisted_frobenius<'a, 'b, F: PrimeField>( - ecc_chip: &EccChip>, - ctx: &mut Context<'b, F>, - Q: &EcPoint>, - c2: &FqPoint<'b, F>, - c3: &FqPoint<'b, F>, -) -> EcPoint> { +pub fn neg_twisted_frobenius( + ecc_chip: &EccChip>, + ctx: &mut Context, + Q: &EcPoint>, + c2: &FqPoint, + c3: &FqPoint, +) -> EcPoint> { assert_eq!(c2.coeffs.len(), 2); assert_eq!(c3.coeffs.len(), 2); @@ -442,12 +450,12 @@ pub fn neg_twisted_frobenius<'a, 'b, F: PrimeField>( } // To avoid issues with mutably borrowing twice (not allowed in Rust), we only store fp_chip and construct g2_chip and fp12_chip in scope when needed for temporary mutable borrows -pub struct PairingChip<'a, F: PrimeField> { - pub fp_chip: &'a FpChip, +pub struct PairingChip { + pub fp_chip: FpChip, } -impl<'a, F: PrimeField> PairingChip<'a, F> { - pub fn construct(fp_chip: &'a FpChip) -> Self { +impl PairingChip { + pub fn construct(fp_chip: FpChip) -> Self { Self { fp_chip } } @@ -478,11 +486,11 @@ impl<'a, F: PrimeField> PairingChip<'a, F> { ) } - pub fn load_private_g1<'v>( + pub fn load_private_g1( &self, - ctx: &mut Context<'_, F>, + ctx: &mut Context, point: Value, - ) -> EcPoint> { + ) -> EcPoint> { // go from pse/pairing::bn256::Fq to forked Fq let convert_fp = |x: bn256::Fq| biguint_to_fe(&fe_to_biguint(&x)); let g1_chip = EccChip::construct(self.fp_chip.clone()); @@ -490,12 +498,12 @@ impl<'a, F: PrimeField> PairingChip<'a, F> { .load_private(ctx, (point.map(|pt| convert_fp(pt.x)), point.map(|pt| convert_fp(pt.y)))) } - pub fn load_private_g2<'v>( + pub fn load_private_g2( &self, - ctx: &mut Context<'_, F>, + ctx: &mut Context, point: Value, - ) -> EcPoint>> { - let fp2_chip = Fp2Chip::::construct(self.fp_chip); + ) -> EcPoint>> { + let fp2_chip = Fp2Chip::::construct(self.fp_chip.clone()); let g2_chip = EccChip::construct(fp2_chip); // go from pse/pairing::bn256::Fq2 to forked public Fq2 let convert_fp2 = |c0: bn256::Fq, c1: bn256::Fq| Fq2 { @@ -508,15 +516,15 @@ impl<'a, F: PrimeField> PairingChip<'a, F> { g2_chip.load_private(ctx, (x, y)) } - pub fn miller_loop<'v>( + pub fn miller_loop( &self, - ctx: &mut Context<'v, F>, - Q: &EcPoint>, - P: &EcPoint>, - ) -> FqPoint<'v, F> { - let fp2_chip = Fp2Chip::::construct(self.fp_chip); + ctx: &mut Context, + Q: &EcPoint>, + P: &EcPoint>, + ) -> FqPoint { + let fp2_chip = Fp2Chip::::construct(self.fp_chip.clone()); let g2_chip = EccChip::construct(fp2_chip); - miller_loop_BN::( + miller_loop_BN::( &g2_chip, ctx, Q, @@ -525,14 +533,14 @@ impl<'a, F: PrimeField> PairingChip<'a, F> { ) } - pub fn multi_miller_loop<'v>( + pub fn multi_miller_loop( &self, - ctx: &mut Context<'v, F>, - pairs: Vec<(&EcPoint>, &EcPoint>)>, - ) -> FqPoint<'v, F> { - let fp2_chip = Fp2Chip::::construct(self.fp_chip); + ctx: &mut Context, + pairs: Vec<(&EcPoint>, &EcPoint>)>, + ) -> FqPoint { + let fp2_chip = Fp2Chip::::construct(self.fp_chip.clone()); let g2_chip = EccChip::construct(fp2_chip); - multi_miller_loop_BN::( + multi_miller_loop_BN::( &g2_chip, ctx, pairs, @@ -540,20 +548,20 @@ impl<'a, F: PrimeField> PairingChip<'a, F> { ) } - pub fn final_exp<'v>(&self, ctx: &mut Context<'v, F>, f: &FqPoint<'v, F>) -> FqPoint<'v, F> { - let fp12_chip = Fp12Chip::::construct(self.fp_chip); + pub fn final_exp(&self, ctx: &mut Context, f: &FqPoint) -> FqPoint { + let fp12_chip = Fp12Chip::::construct(self.fp_chip.clone()); fp12_chip.final_exp(ctx, f) } // optimal Ate pairing - pub fn pairing<'v>( + pub fn pairing( &self, - ctx: &mut Context<'v, F>, - Q: &EcPoint>, - P: &EcPoint>, - ) -> FqPoint<'v, F> { + ctx: &mut Context, + Q: &EcPoint>, + P: &EcPoint>, + ) -> FqPoint { let f0 = self.miller_loop(ctx, Q, P); - let fp12_chip = Fp12Chip::::construct(self.fp_chip); + let fp12_chip = Fp12Chip::::construct(self.fp_chip.clone()); // final_exp implemented in final_exp module fp12_chip.final_exp(ctx, &f0) } diff --git a/halo2-ecc/src/bn254/tests/ec_add.rs b/halo2-ecc/src/bn254/tests/ec_add.rs index 08dc9fb1..25fca4b5 100644 --- a/halo2-ecc/src/bn254/tests/ec_add.rs +++ b/halo2-ecc/src/bn254/tests/ec_add.rs @@ -4,7 +4,7 @@ use std::{env::var, fs::File}; use super::*; use crate::fields::FieldChip; -use crate::halo2_proofs::halo2curves::{bn256::G2Affine, FieldExt}; +use crate::halo2_proofs::halo2curves::bn256::G2Affine; use group::cofactor::CofactorCurveAffine; use halo2_base::SKIP_FIRST_PASS; use rand_core::OsRng; @@ -116,7 +116,7 @@ impl Circuit for EcAddCircuit { assert_eq!(config.batch_size, self.points.len()); config.fp_chip.load_lookup_table(&mut layouter)?; - let fp2_chip = Fp2Chip::::construct(&config.fp_chip); + let fp2_chip = Fp2Chip::::construct(config.fp_chip.clone()); let g2_chip = EccChip::construct(fp2_chip.clone()); let mut first_pass = SKIP_FIRST_PASS; @@ -140,7 +140,7 @@ impl Circuit for EcAddCircuit { }) .collect::>(); - let acc = g2_chip.sum::(ctx, points.iter()); + let acc = g2_chip.sum::(ctx, points.iter().cloned()); #[cfg(feature = "display")] if display { diff --git a/halo2-ecc/src/bn254/tests/fixed_base_msm.rs b/halo2-ecc/src/bn254/tests/fixed_base_msm.rs index c7239d9d..424acacf 100644 --- a/halo2-ecc/src/bn254/tests/fixed_base_msm.rs +++ b/halo2-ecc/src/bn254/tests/fixed_base_msm.rs @@ -1,3 +1,4 @@ +use ff::Field; use std::{env::var, fs::File}; #[allow(unused_imports)] diff --git a/halo2-ecc/src/bn254/tests/mod.rs b/halo2-ecc/src/bn254/tests/mod.rs index 763bd127..e5516788 100644 --- a/halo2-ecc/src/bn254/tests/mod.rs +++ b/halo2-ecc/src/bn254/tests/mod.rs @@ -7,6 +7,7 @@ use std::marker::PhantomData; use super::pairing::PairingChip; use super::*; +use crate::fields::PrimeField; use crate::halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, @@ -24,7 +25,7 @@ use crate::halo2_proofs::{ use crate::{ecc::EccChip, fields::fp::FpStrategy}; use halo2_base::{ gates::GateInstructions, - utils::{biguint_to_fe, fe_to_biguint, value_to_option, PrimeField}, + utils::{biguint_to_fe, fe_to_biguint, value_to_option}, QuantumCell::Witness, }; use num_bigint::BigUint; diff --git a/halo2-ecc/src/bn254/tests/msm.rs b/halo2-ecc/src/bn254/tests/msm.rs index 4195c0f8..4e68e6f7 100644 --- a/halo2-ecc/src/bn254/tests/msm.rs +++ b/halo2-ecc/src/bn254/tests/msm.rs @@ -1,7 +1,6 @@ -use std::{env::var, fs::File}; - -use crate::halo2_proofs::arithmetic::FieldExt; +use ff::Field; use halo2_base::SKIP_FIRST_PASS; +use std::{env::var, fs::File}; use super::*; diff --git a/halo2-ecc/src/bn254/tests/pairing.rs b/halo2-ecc/src/bn254/tests/pairing.rs index f71f6cdd..c114ba6c 100644 --- a/halo2-ecc/src/bn254/tests/pairing.rs +++ b/halo2-ecc/src/bn254/tests/pairing.rs @@ -63,7 +63,7 @@ impl Circuit for PairingCircuit { mut layouter: impl Layouter, ) -> Result<(), Error> { config.range.load_lookup_table(&mut layouter)?; - let chip = PairingChip::::construct(&config); + let chip = PairingChip::::construct(config.clone()); let mut first_pass = SKIP_FIRST_PASS; diff --git a/halo2-ecc/src/ecc/ecdsa.rs b/halo2-ecc/src/ecc/ecdsa.rs index 005f5c39..e81ea664 100644 --- a/halo2-ecc/src/ecc/ecdsa.rs +++ b/halo2-ecc/src/ecc/ecdsa.rs @@ -1,8 +1,10 @@ use crate::bigint::{big_less_than, CRTInteger}; +use crate::fields::PrimeField; use crate::fields::{fp::FpConfig, FieldChip}; +use halo2_base::QuantumCell; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::{modulus, CurveAffineExt, PrimeField}, + utils::{modulus, CurveAffineExt}, AssignedValue, Context, QuantumCell::Existing, }; @@ -14,16 +16,16 @@ use super::{ec_add_unequal, scalar_multiply, EcPoint}; // p = coordinate field modulus // n = scalar field modulus // Only valid when p is very close to n in size (e.g. for Secp256k1) -pub fn ecdsa_verify_no_pubkey_check<'v, F: PrimeField, CF: PrimeField, SF: PrimeField, GA>( +pub fn ecdsa_verify_no_pubkey_check( base_chip: &FpConfig, - ctx: &mut Context<'v, F>, - pubkey: &EcPoint as FieldChip>::FieldPoint<'v>>, - r: &CRTInteger<'v, F>, - s: &CRTInteger<'v, F>, - msghash: &CRTInteger<'v, F>, + ctx: &mut Context, + pubkey: &EcPoint as FieldChip>::FieldPoint>, + r: &CRTInteger, + s: &CRTInteger, + msghash: &CRTInteger, var_window_bits: usize, fixed_window_bits: usize, -) -> AssignedValue<'v, F> +) -> AssignedValue where GA: CurveAffineExt, { @@ -40,8 +42,8 @@ where let s_valid = scalar_chip.is_soft_nonzero(ctx, s); // compute u1 = m s^{-1} mod n and u2 = r s^{-1} mod n - let u1 = scalar_chip.divide(ctx, msghash, s); - let u2 = scalar_chip.divide(ctx, r, s); + let u1 = scalar_chip.divide_unsafe(ctx, msghash, s); + let u2 = scalar_chip.divide_unsafe(ctx, r, s); //let r_crt = scalar_chip.to_crt(ctx, r)?; @@ -54,7 +56,7 @@ where base_chip.limb_bits, fixed_window_bits, ); - let u2_mul = scalar_multiply::( + let u2_mul = scalar_multiply::( base_chip, ctx, pubkey, @@ -63,13 +65,15 @@ where var_window_bits, ); - // check u1 * G and u2 * pubkey are not negatives and not equal - // TODO: Technically they could be equal for a valid signature, but this happens with vanishing probability - // for an ECDSA signature constructed in a standard way + // check u1 * G != -(u2 * pubkey) but allow u1 * G == u2 * pubkey + // check (u1 * G).x != (u2 * pubkey).x or (u1 * G).y == (u2 * pubkey).y // coordinates of u1_mul and u2_mul are in proper bigint form, and lie in but are not constrained to [0, n) // we therefore need hard inequality here - let u1_u2_x_eq = base_chip.is_equal(ctx, &u1_mul.x, &u2_mul.x); - let u1_u2_not_neg = base_chip.range.gate().not(ctx, Existing(&u1_u2_x_eq)); + let x_eq = base_chip.is_equal(ctx, &u1_mul.x, &u2_mul.x); + let x_neq = base_chip.gate().not(ctx, QuantumCell::Existing(x_eq)); + let y_eq = base_chip.is_equal(ctx, &u1_mul.y, &u2_mul.y); + let u1g_u2pk_not_neg = + base_chip.gate().or(ctx, QuantumCell::Existing(x_neq), QuantumCell::Existing(y_eq)); // compute (x1, y1) = u1 * G + u2 * pubkey and check (r mod n) == x1 as integers // WARNING: For optimization reasons, does not reduce x1 mod n, which is @@ -98,10 +102,10 @@ where ); // check (r in [1, n - 1]) and (s in [1, n - 1]) and (u1_mul != - u2_mul) and (r == x1 mod n) - let res1 = base_chip.range.gate().and(ctx, Existing(&r_valid), Existing(&s_valid)); - let res2 = base_chip.range.gate().and(ctx, Existing(&res1), Existing(&u1_small)); - let res3 = base_chip.range.gate().and(ctx, Existing(&res2), Existing(&u2_small)); - let res4 = base_chip.range.gate().and(ctx, Existing(&res3), Existing(&u1_u2_not_neg)); - let res5 = base_chip.range.gate().and(ctx, Existing(&res4), Existing(&equal_check)); + let res1 = base_chip.range.gate().and(ctx, Existing(r_valid), Existing(s_valid)); + let res2 = base_chip.range.gate().and(ctx, Existing(res1), Existing(u1_small)); + let res3 = base_chip.range.gate().and(ctx, Existing(res2), Existing(u2_small)); + let res4 = base_chip.range.gate().and(ctx, Existing(res3), Existing(u1g_u2pk_not_neg)); + let res5 = base_chip.range.gate().and(ctx, Existing(res4), Existing(equal_check)); res5 } diff --git a/halo2-ecc/src/ecc/fixed_base.rs b/halo2-ecc/src/ecc/fixed_base.rs index 64168c96..da5b31c5 100644 --- a/halo2-ecc/src/ecc/fixed_base.rs +++ b/halo2-ecc/src/ecc/fixed_base.rs @@ -1,5 +1,7 @@ #![allow(non_snake_case)] use super::{ec_add_unequal, ec_select, ec_select_from_bits, EcPoint, EccChip}; +use crate::ecc::{ec_sub_unequal, load_random_point}; +use crate::fields::PrimeField; use crate::halo2_proofs::arithmetic::CurveAffine; use crate::{ bigint::{CRTInteger, FixedCRTInteger}, @@ -8,7 +10,7 @@ use crate::{ use group::Curve; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::{fe_to_biguint, CurveAffineExt, PrimeField}, + utils::{fe_to_biguint, CurveAffineExt}, AssignedValue, Context, QuantumCell::Existing, }; @@ -42,11 +44,11 @@ where pub fn assign<'v, FC>( self, chip: &FC, - ctx: &mut Context<'_, F>, + ctx: &mut Context, native_modulus: &BigUint, - ) -> EcPoint> + ) -> EcPoint where - FC: PrimeFieldChip = CRTInteger<'v, F>>, + FC: PrimeFieldChip>, { let assigned_x = self.x.assign(chip.range().gate(), ctx, chip.limb_bits(), native_modulus); let assigned_y = self.y.assign(chip.range().gate(), ctx, chip.limb_bits(), native_modulus); @@ -56,11 +58,11 @@ where pub fn assign_without_caching<'v, FC>( self, chip: &FC, - ctx: &mut Context<'_, F>, + ctx: &mut Context, native_modulus: &BigUint, - ) -> EcPoint> + ) -> EcPoint where - FC: PrimeFieldChip = CRTInteger<'v, F>>, + FC: PrimeFieldChip>, { let assigned_x = self.x.assign_without_caching( chip.range().gate(), @@ -86,20 +88,20 @@ where // - `scalar_i < 2^{max_bits} for all i` (constrained by num_to_bits) // - `max_bits <= modulus::.bits()` -pub fn scalar_multiply<'v, F, FC, C>( +pub fn scalar_multiply( chip: &FC, - ctx: &mut Context<'v, F>, + ctx: &mut Context, point: &C, - scalar: &[AssignedValue<'v, F>], + scalar: &[AssignedValue], max_bits: usize, window_bits: usize, -) -> EcPoint> +) -> EcPoint where F: PrimeField, C: CurveAffineExt, C::Base: PrimeField, - FC: PrimeFieldChip = CRTInteger<'v, F>> - + Selectable = FC::FieldPoint<'v>>, + FC: PrimeFieldChip> + + Selectable, { if point.is_identity().into() { let point = FixedEcPoint::from_curve(*point, chip.num_limbs(), chip.limb_bits()); @@ -153,52 +155,39 @@ where let cached_point_window_rev = cached_points.chunks(1usize << window_bits).rev(); let bit_window_rev = bits.chunks(window_bits).rev(); - let mut curr_point = None; + + let random_point = load_random_point::(chip, ctx); + let mut curr_point = random_point.clone(); // `is_started` is just a way to deal with if `curr_point` is actually identity - let mut is_started = chip.gate().load_zero(ctx); for (cached_point_window, bit_window) in cached_point_window_rev.zip(bit_window_rev) { - let bit_sum = chip.gate().sum(ctx, bit_window.iter().map(Existing)); + let bit_sum = chip.gate().sum(ctx, bit_window.iter().copied().map(Existing)); // are we just adding a window of all 0s? if so, skip let is_zero_window = chip.gate().is_zero(ctx, &bit_sum); - let add_point = ec_select_from_bits::(chip, ctx, cached_point_window, bit_window); - curr_point = if let Some(curr_point) = curr_point { - let sum = ec_add_unequal(chip, ctx, &curr_point, &add_point, false); - let zero_sum = ec_select(chip, ctx, &curr_point, &sum, &is_zero_window); - Some(ec_select(chip, ctx, &zero_sum, &add_point, &is_started)) - } else { - Some(add_point) - }; - is_started = { - // is_started || !is_zero_window - // (a || !b) = (1-b) + a*b - let not_zero_window = chip.gate().not(ctx, Existing(&is_zero_window)); - chip.gate().mul_add( - ctx, - Existing(&is_started), - Existing(&is_zero_window), - Existing(¬_zero_window), - ) + curr_point = { + let add_point = ec_select_from_bits(chip, ctx, cached_point_window, bit_window); + let sum = ec_add_unequal(chip, ctx, &curr_point, &add_point, true); + ec_select(chip, ctx, &curr_point, &sum, &is_zero_window) }; } - curr_point.unwrap() + ec_sub_unequal(chip, ctx, &curr_point, &random_point, false) } // basically just adding up individual fixed_base::scalar_multiply except that we do all batched normalization of cached points at once to further save inversion time during witness generation // we also use the random accumulator for some extra efficiency (which also works in scalar multiply case but that is TODO) -pub fn msm<'v, F, FC, C>( +pub fn msm( chip: &EccChip, - ctx: &mut Context<'v, F>, + ctx: &mut Context, points: &[C], - scalars: &[Vec>], + scalars: &[Vec>], max_scalar_bits_per_cell: usize, window_bits: usize, -) -> EcPoint> +) -> EcPoint where F: PrimeField, C: CurveAffineExt, C::Base: PrimeField, - FC: PrimeFieldChip = CRTInteger<'v, F>> - + Selectable = FC::FieldPoint<'v>>, + FC: PrimeFieldChip> + + Selectable, { assert!((max_scalar_bits_per_cell as u32) <= F::NUM_BITS); let scalar_len = scalars[0].len(); @@ -263,15 +252,14 @@ where .chunks(cached_points.len() / points.len()) .zip(bits.chunks(total_bits)) .map(|(cached_points, bits)| { - let cached_point_window_rev = - cached_points.chunks(1usize << window_bits).rev(); + let cached_point_window_rev = cached_points.chunks(1usize << window_bits).rev(); let bit_window_rev = bits.chunks(window_bits).rev(); let mut curr_point = None; // `is_started` is just a way to deal with if `curr_point` is actually identity let mut is_started = field_chip.gate().load_zero(ctx); for (cached_point_window, bit_window) in cached_point_window_rev.zip(bit_window_rev) { let is_zero_window = { - let sum = field_chip.gate().sum(ctx, bit_window.iter().map(Existing)); + let sum = field_chip.gate().sum(ctx, bit_window.iter().copied().map(Existing)); field_chip.gate().is_zero(ctx, &sum) }; let add_point = @@ -287,17 +275,17 @@ where // is_started || !is_zero_window // (a || !b) = (1-b) + a*b let not_zero_window = - field_chip.range().gate().not(ctx, Existing(&is_zero_window)); + field_chip.range().gate().not(ctx, Existing(is_zero_window)); field_chip.range().gate().mul_add( ctx, - Existing(&is_started), - Existing(&is_zero_window), - Existing(¬_zero_window), + Existing(is_started), + Existing(is_zero_window), + Existing(not_zero_window), ) }; } curr_point.unwrap() }) .collect_vec(); - chip.sum::(ctx, sm.iter()) + chip.sum::(ctx, sm.iter().cloned()) } diff --git a/halo2-ecc/src/ecc/fixed_base_pippenger.rs b/halo2-ecc/src/ecc/fixed_base_pippenger.rs index 1e36bfd1..b21d7760 100644 --- a/halo2-ecc/src/ecc/fixed_base_pippenger.rs +++ b/halo2-ecc/src/ecc/fixed_base_pippenger.rs @@ -20,14 +20,14 @@ use rand_chacha::ChaCha20Rng; // Output: // * new_points: length `points.len() * radix` // * new_bool_scalars: 2d array `ceil(scalar_bits / radix)` by `points.len() * radix` -pub fn decompose<'v, F, C>( +pub fn decompose( gate: &impl GateInstructions, - ctx: &mut Context<'v, F>, + ctx: &mut Context< F>, points: &[C], - scalars: &Vec>>, + scalars: &Vec>>, max_scalar_bits_per_cell: usize, radix: usize, -) -> (Vec, Vec>>) +) -> (Vec, Vec>>) where F: PrimeField, C: CurveAffine, @@ -66,15 +66,15 @@ where // Given points[i] and bool_scalars[j][i], // compute G'[j] = sum_{i=0..points.len()} points[i] * bool_scalars[j][i] // output is [ G'[j] + rand_point ]_{j=0..bool_scalars.len()}, rand_point -pub fn multi_product<'v, F: PrimeField, FC, C>( +pub fn multi_product( chip: &FC, - ctx: &mut Context<'v, F>, + ctx: &mut Context< F>, points: Vec, - bool_scalars: Vec>>, + bool_scalars: Vec>>, clumping_factor: usize, -) -> (Vec>>, EcPoint>) +) -> (Vec>, EcPoint) where - FC: PrimeFieldChip = CRTInteger<'v, F>>, + FC: PrimeFieldChip>, FC::FieldType: PrimeField, C: CurveAffine, { @@ -187,17 +187,17 @@ where (acc, rand_point) } -pub fn multi_exp<'v, F: PrimeField, FC, C>( +pub fn multi_exp( chip: &FC, - ctx: &mut Context<'v, F>, + ctx: &mut Context< F>, points: &[C], - scalars: &Vec>>, + scalars: &Vec>>, max_scalar_bits_per_cell: usize, radix: usize, clump_factor: usize, -) -> EcPoint> +) -> EcPoint where - FC: PrimeFieldChip = CRTInteger<'v, F>>, + FC: PrimeFieldChip>, FC::FieldType: PrimeField, C: CurveAffine, { diff --git a/halo2-ecc/src/ecc/mod.rs b/halo2-ecc/src/ecc/mod.rs index 2b9cedf6..3b1586b2 100644 --- a/halo2-ecc/src/ecc/mod.rs +++ b/halo2-ecc/src/ecc/mod.rs @@ -1,11 +1,12 @@ #![allow(non_snake_case)] use crate::bigint::CRTInteger; +use crate::fields::PrimeField; use crate::fields::{fp::FpConfig, FieldChip, PrimeFieldChip, Selectable}; use crate::halo2_proofs::{arithmetic::CurveAffine, circuit::Value}; use group::{Curve, Group}; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::{modulus, CurveAffineExt, PrimeField}, + utils::{modulus, CurveAffineExt}, AssignedValue, Context, QuantumCell::Existing, }; @@ -20,19 +21,13 @@ pub mod fixed_base; pub mod pippenger; // EcPoint and EccChip take in a generic `FieldChip` to implement generic elliptic curve operations on arbitrary field extensions (provided chip exists) for short Weierstrass curves (currently further assuming a4 = 0 for optimization purposes) -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EcPoint { pub x: FieldPoint, pub y: FieldPoint, _marker: PhantomData, } -impl Clone for EcPoint { - fn clone(&self) -> Self { - Self { x: self.x.clone(), y: self.y.clone(), _marker: PhantomData } - } -} - impl EcPoint { pub fn construct(x: FieldPoint, y: FieldPoint) -> Self { Self { x, y, _marker: PhantomData } @@ -58,22 +53,22 @@ impl EcPoint { // y_3 = lambda (x_1 - x_3) - y_1 mod p // /// For optimization reasons, we assume that if you are using this with `is_strict = true`, then you have already called `chip.enforce_less_than_p` on both `P.x` and `P.y` -pub fn ec_add_unequal<'v, F: PrimeField, FC: FieldChip>( +pub fn ec_add_unequal>( chip: &FC, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - Q: &EcPoint>, + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, is_strict: bool, -) -> EcPoint> { +) -> EcPoint { if is_strict { // constrains that P.x != Q.x let x_is_equal = chip.is_equal_unenforced(ctx, &P.x, &Q.x); - chip.range().gate().assert_is_const(ctx, &x_is_equal, F::zero()); + chip.range().gate().assert_is_const(ctx, &x_is_equal, F::ZERO); } let dx = chip.sub_no_carry(ctx, &Q.x, &P.x); let dy = chip.sub_no_carry(ctx, &Q.y, &P.y); - let lambda = chip.divide(ctx, &dy, &dx); + let lambda = chip.divide_unsafe(ctx, &dy, &dx); // x_3 = lambda^2 - x_1 - x_2 (mod p) let lambda_sq = chip.mul_no_carry(ctx, &lambda, &lambda); @@ -100,23 +95,23 @@ pub fn ec_add_unequal<'v, F: PrimeField, FC: FieldChip>( // Assumes that P !=Q and Q != (P - Q) // /// For optimization reasons, we assume that if you are using this with `is_strict = true`, then you have already called `chip.enforce_less_than_p` on both `P.x` and `P.y` -pub fn ec_sub_unequal<'v, F: PrimeField, FC: FieldChip>( +pub fn ec_sub_unequal>( chip: &FC, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - Q: &EcPoint>, + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, is_strict: bool, -) -> EcPoint> { +) -> EcPoint { if is_strict { // constrains that P.x != Q.x let x_is_equal = chip.is_equal_unenforced(ctx, &P.x, &Q.x); - chip.range().gate().assert_is_const(ctx, &x_is_equal, F::zero()); + chip.range().gate().assert_is_const(ctx, &x_is_equal, F::ZERO); } let dx = chip.sub_no_carry(ctx, &Q.x, &P.x); let dy = chip.add_no_carry(ctx, &Q.y, &P.y); - let lambda = chip.neg_divide(ctx, &dy, &dx); + let lambda = chip.neg_divide_unsafe(ctx, &dy, &dx); // (x_2 - x_1) * lambda + y_2 + y_1 = 0 (mod p) let lambda_dx = chip.mul_no_carry(ctx, &lambda, &dx); @@ -143,23 +138,31 @@ pub fn ec_sub_unequal<'v, F: PrimeField, FC: FieldChip>( // formula from https://crypto.stanford.edu/pbc/notes/elliptic/explicit.html // assume y != 0 (otherwise 2P = O) -// lamb = 3x^2 / (2 y) % p +// lamb = 3x^2 + a / (2 y) % p // x_3 = out[0] = lambda^2 - 2 x % p // y_3 = out[1] = lambda (x - x_3) - y % p -// we precompute lambda and constrain (2y) * lambda = 3 x^2 (mod p) +// we precompute lambda and constrain (2y) * lambda = 3 x^2 + a(mod p) // then we compute x_3 = lambda^2 - 2 x (mod p) // y_3 = lambda (x - x_3) - y (mod p) -pub fn ec_double<'v, F: PrimeField, FC: FieldChip>( +pub fn ec_double, C>( chip: &FC, - ctx: &mut Context<'v, F>, - P: &EcPoint>, -) -> EcPoint> { + ctx: &mut Context, + P: &EcPoint, +) -> EcPoint +where + C: CurveAffine, +{ // removed optimization that computes `2 * lambda` while assigning witness to `lambda` simultaneously, in favor of readability. The difference is just copying `lambda` once let two_y = chip.scalar_mul_no_carry(ctx, &P.y, 2); let three_x = chip.scalar_mul_no_carry(ctx, &P.x, 3); let three_x_sq = chip.mul_no_carry(ctx, &three_x, &P.x); - let lambda = chip.divide(ctx, &three_x_sq, &two_y); + + // add a, for secp256k1 a = 0, for secp256r1, a > 0 + let a_const = FC::fe_to_constant(C::a()); + let three_x_plus_a = chip.add_constant_no_carry(ctx, &three_x_sq, a_const); + + let lambda = chip.divide_unsafe(ctx, &three_x_plus_a, &two_y); // x_3 = lambda^2 - 2 x % p let lambda_sq = chip.mul_no_carry(ctx, &lambda, &lambda); @@ -176,31 +179,31 @@ pub fn ec_double<'v, F: PrimeField, FC: FieldChip>( EcPoint::construct(x_3, y_3) } -pub fn ec_select<'v, F: PrimeField, FC>( +pub fn ec_select( chip: &FC, - ctx: &mut Context<'_, F>, - P: &EcPoint>, - Q: &EcPoint>, - sel: &AssignedValue<'v, F>, -) -> EcPoint> + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, + sel: &AssignedValue, +) -> EcPoint where - FC: FieldChip + Selectable = FC::FieldPoint<'v>>, + FC: FieldChip + Selectable, { let Rx = chip.select(ctx, &P.x, &Q.x, sel); let Ry = chip.select(ctx, &P.y, &Q.y, sel); EcPoint::construct(Rx, Ry) } -// takes the dot product of points with sel, where each is intepreted as +// takes the dot product of points with sel, where each is interpreted as // a _vector_ -pub fn ec_select_by_indicator<'v, F: PrimeField, FC>( +pub fn ec_select_by_indicator( chip: &FC, - ctx: &mut Context<'_, F>, - points: &[EcPoint>], - coeffs: &[AssignedValue<'v, F>], -) -> EcPoint> + ctx: &mut Context, + points: &[EcPoint], + coeffs: &[AssignedValue], +) -> EcPoint where - FC: FieldChip + Selectable = FC::FieldPoint<'v>>, + FC: FieldChip + Selectable, { let x_coords = points.iter().map(|P| P.x.clone()).collect::>(); let y_coords = points.iter().map(|P| P.y.clone()).collect::>(); @@ -210,14 +213,14 @@ where } // `sel` is little-endian binary -pub fn ec_select_from_bits<'v, F: PrimeField, FC>( +pub fn ec_select_from_bits( chip: &FC, - ctx: &mut Context<'_, F>, - points: &[EcPoint>], - sel: &[AssignedValue<'v, F>], -) -> EcPoint> + ctx: &mut Context, + points: &[EcPoint], + sel: &[AssignedValue], +) -> EcPoint where - FC: FieldChip + Selectable = FC::FieldPoint<'v>>, + FC: FieldChip + Selectable, { let w = sel.len(); let num_points = points.len(); @@ -234,16 +237,17 @@ where // - `scalar_i < 2^{max_bits} for all i` (constrained by num_to_bits) // - `max_bits <= modulus::.bits()` // * P has order given by the scalar field modulus -pub fn scalar_multiply<'v, F: PrimeField, FC>( +pub fn scalar_multiply( chip: &FC, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - scalar: &Vec>, + ctx: &mut Context, + P: &EcPoint, + scalar: &Vec>, max_bits: usize, window_bits: usize, -) -> EcPoint> +) -> EcPoint where - FC: FieldChip + Selectable = FC::FieldPoint<'v>>, + FC: FieldChip + Selectable, + C: CurveAffineExt, { assert!(!scalar.is_empty()); assert!((max_bits as u64) <= modulus::().bits()); @@ -272,8 +276,8 @@ where for idx in 1..total_bits { let or = chip.gate().or( ctx, - Existing(&is_started[rounded_bitlen - total_bits + idx - 1]), - Existing(&rounded_bits[total_bits - idx]), + Existing(is_started[rounded_bitlen - total_bits + idx - 1]), + Existing(rounded_bits[total_bits - idx]), ); is_started.push(or.clone()); } @@ -284,7 +288,7 @@ where let temp_bits = rounded_bits [rounded_bitlen - window_bits * (idx + 1)..rounded_bitlen - window_bits * idx] .iter() - .map(|x| Existing(x)); + .map(|x| Existing(*x)); let bit_sum = chip.gate().sum(ctx, temp_bits); let is_zero = chip.gate().is_zero(ctx, &bit_sum); is_zero_window.push(is_zero.clone()); @@ -297,7 +301,7 @@ where cached_points.push(P.clone()); for idx in 2..cache_size { if idx == 2 { - let double = ec_double(chip, ctx, P /*, b*/); + let double = ec_double::(chip, ctx, P /*, b*/); cached_points.push(double.clone()); } else { let new_point = ec_add_unequal(chip, ctx, &cached_points[idx - 1], P, false); @@ -316,7 +320,7 @@ where for idx in 1..num_windows { let mut mult_point = curr_point.clone(); for _ in 0..window_bits { - mult_point = ec_double(chip, ctx, &mult_point); + mult_point = ec_double::(chip, ctx, &mult_point); } let add_point = ec_select_from_bits::( chip, @@ -335,29 +339,36 @@ where curr_point } -pub fn is_on_curve<'v, F, FC, C>( - chip: &FC, - ctx: &mut Context<'v, F>, - P: &EcPoint>, -) where +pub fn is_on_curve(chip: &FC, ctx: &mut Context, P: &EcPoint) +where F: PrimeField, FC: FieldChip, C: CurveAffine, { + // calculate y^2 let lhs = chip.mul_no_carry(ctx, &P.y, &P.y); + // calculate x^2 let mut rhs = chip.mul(ctx, &P.x, &P.x); + // calculate x^3 rhs = chip.mul_no_carry(ctx, &rhs, &P.x); + let a_const = FC::fe_to_constant(C::a()); + let a = chip.load_constant(ctx, a_const); let b = FC::fe_to_constant(C::b()); + // calculate x^3 + b rhs = chip.add_constant_no_carry(ctx, &rhs, b); + // calculate `a*x` part of the whole equation: `y^2= x^3 + a*x + b`, if a = 0 (for secp256k1), + // `a*x` is zero. + // add chip.mul_constant helper ? + let ax = chip.mul(ctx, &P.x, &a); + // calculate x^3 + a*x + b + rhs = chip.add_no_carry(ctx, &rhs, &ax); + let diff = chip.sub_no_carry(ctx, &lhs, &rhs); chip.check_carry_mod_to_zero(ctx, &diff) } -pub fn load_random_point<'v, F, FC, C>( - chip: &FC, - ctx: &mut Context<'v, F>, -) -> EcPoint> +pub fn load_random_point(chip: &FC, ctx: &mut Context) -> EcPoint where F: PrimeField, FC: FieldChip, @@ -379,25 +390,25 @@ where // need to supply an extra generic `C` implementing `CurveAffine` trait in order to generate random witness points on the curve in question // Using Simultaneous 2^w-Ary Method, see https://www.bmoeller.de/pdf/multiexp-sac2001.pdf -// Random Accumlation point trick learned from halo2wrong: https://hackmd.io/ncuKqRXzR-Cw-Au2fGzsMg?view +// Random Accumulation point trick learned from halo2wrong: https://hackmd.io/ncuKqRXzR-Cw-Au2fGzsMg?view // Input: // - `scalars` is vector of same length as `P` // - each `scalar` in `scalars` satisfies same assumptions as in `scalar_multiply` above -pub fn multi_scalar_multiply<'v, F: PrimeField, FC, C>( +pub fn multi_scalar_multiply( chip: &FC, - ctx: &mut Context<'v, F>, - P: &[EcPoint>], - scalars: &[Vec>], + ctx: &mut Context, + P: &[EcPoint], + scalars: &[Vec>], max_bits: usize, window_bits: usize, -) -> EcPoint> +) -> EcPoint where - FC: FieldChip + Selectable = FC::FieldPoint<'v>>, + FC: FieldChip + Selectable, C: CurveAffineExt, { let k = P.len(); assert_eq!(k, scalars.len()); - assert_ne!(k, 0); + debug_assert_ne!(k, 0); assert!(!scalars[0].is_empty()); assert!((max_bits as u32) <= F::NUM_BITS); @@ -428,7 +439,7 @@ where let mut rand_start_vec = Vec::with_capacity(k + window_bits); rand_start_vec.push(base); for idx in 1..(k + window_bits) { - let base_mult = ec_double(chip, ctx, &rand_start_vec[idx - 1]); + let base_mult = ec_double::(chip, ctx, &rand_start_vec[idx - 1]); rand_start_vec.push(base_mult); } assert!(rand_start_vec.len() >= k + window_bits); @@ -479,11 +490,10 @@ where // compute \sum_i x_i P_i + (2^{k + 1} - 1) * A for idx in 0..num_windows { for _ in 0..window_bits { - curr_point = ec_double(chip, ctx, &curr_point); + curr_point = ec_double::(chip, ctx, &curr_point); } - for (cached_points, rounded_bits) in cached_points - .chunks(cache_size) - .zip(rounded_bits.chunks(rounded_bitlen)) + for (cached_points, rounded_bits) in + cached_points.chunks(cache_size).zip(rounded_bits.chunks(rounded_bitlen)) { let add_point = ec_select_from_bits::( chip, @@ -566,11 +576,11 @@ impl> EccChip { &self.field_chip } - pub fn load_private<'v>( + pub fn load_private( &self, - ctx: &mut Context<'_, F>, + ctx: &mut Context, point: (Value, Value), - ) -> EcPoint> { + ) -> EcPoint { let (x, y) = (FC::fe_to_witness(&point.0), FC::fe_to_witness(&point.1)); let x_assigned = self.field_chip.load_private(ctx, x); @@ -580,11 +590,7 @@ impl> EccChip { } /// Does not constrain witness to lie on curve - pub fn assign_point<'v, C>( - &self, - ctx: &mut Context<'_, F>, - g: Value, - ) -> EcPoint> + pub fn assign_point(&self, ctx: &mut Context, g: Value) -> EcPoint where C: CurveAffineExt, { @@ -592,11 +598,7 @@ impl> EccChip { self.load_private(ctx, (x, y)) } - pub fn assign_constant_point<'v, C>( - &self, - ctx: &mut Context<'_, F>, - g: C, - ) -> EcPoint> + pub fn assign_constant_point(&self, ctx: &mut Context, g: C) -> EcPoint where C: CurveAffineExt, { @@ -608,31 +610,25 @@ impl> EccChip { EcPoint::construct(x, y) } - pub fn load_random_point<'v, C>( - &self, - ctx: &mut Context<'v, F>, - ) -> EcPoint> + pub fn load_random_point(&self, ctx: &mut Context) -> EcPoint where C: CurveAffineExt, { load_random_point::(self.field_chip(), ctx) } - pub fn assert_is_on_curve<'v, C>( - &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - ) where + pub fn assert_is_on_curve(&self, ctx: &mut Context, P: &EcPoint) + where C: CurveAffine, { is_on_curve::(&self.field_chip, ctx, P) } - pub fn is_on_curve_or_infinity<'v, C>( + pub fn is_on_curve_or_infinity( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - ) -> AssignedValue<'v, F> + ctx: &mut Context, + P: &EcPoint, + ) -> AssignedValue where C: CurveAffine, C::Base: ff::PrimeField, @@ -641,8 +637,17 @@ impl> EccChip { let mut rhs = self.field_chip.mul(ctx, &P.x, &P.x); rhs = self.field_chip.mul_no_carry(ctx, &rhs, &P.x); + let a_const = FC::fe_to_constant(C::a()); + let a = self.field_chip.load_constant(ctx, a_const); + let b = FC::fe_to_constant(C::b()); rhs = self.field_chip.add_constant_no_carry(ctx, &rhs, b); + // calculate `a*x` part of the whole equation: `y^2= x^3 + a*x + b`, if a = 0 (for secp256k1), + // `a*x` is zero. + // add chip.mul_constant helper ? + let ax = self.field_chip.mul(ctx, &P.x, &a); + // calculate x^3 + a*x + b + rhs = self.field_chip.add_no_carry(ctx, &rhs, &ax); let mut diff = self.field_chip.sub_no_carry(ctx, &lhs, &rhs); diff = self.field_chip.carry_mod(ctx, &diff); @@ -653,89 +658,91 @@ impl> EccChip { self.field_chip.range().gate().or_and( ctx, - Existing(&is_on_curve), - Existing(&x_is_zero), - Existing(&y_is_zero), + Existing(is_on_curve), + Existing(x_is_zero), + Existing(y_is_zero), ) } - pub fn negate<'v>( + pub fn negate( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - ) -> EcPoint> { + ctx: &mut Context, + P: &EcPoint, + ) -> EcPoint { EcPoint::construct(P.x.clone(), self.field_chip.negate(ctx, &P.y)) } /// Assumes that P.x != Q.x /// If `is_strict == true`, then actually constrains that `P.x != Q.x` - pub fn add_unequal<'v>( + pub fn add_unequal( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - Q: &EcPoint>, + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, is_strict: bool, - ) -> EcPoint> { + ) -> EcPoint { ec_add_unequal(&self.field_chip, ctx, P, Q, is_strict) } /// Assumes that P.x != Q.x /// Otherwise will panic - pub fn sub_unequal<'v>( + pub fn sub_unequal( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - Q: &EcPoint>, + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, is_strict: bool, - ) -> EcPoint> { + ) -> EcPoint { ec_sub_unequal(&self.field_chip, ctx, P, Q, is_strict) } - pub fn double<'v>( + pub fn double( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - ) -> EcPoint> { - ec_double(&self.field_chip, ctx, P) + ctx: &mut Context, + P: &EcPoint, + ) -> EcPoint + where + C: CurveAffine, + { + ec_double::(&self.field_chip, ctx, P) } - pub fn is_equal<'v>( + pub fn is_equal( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - Q: &EcPoint>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, + ) -> AssignedValue { // TODO: optimize let x_is_equal = self.field_chip.is_equal(ctx, &P.x, &Q.x); let y_is_equal = self.field_chip.is_equal(ctx, &P.y, &Q.y); - self.field_chip.range().gate().and(ctx, Existing(&x_is_equal), Existing(&y_is_equal)) + self.field_chip.range().gate().and(ctx, Existing(x_is_equal), Existing(y_is_equal)) } - pub fn assert_equal<'v>( + pub fn assert_equal( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - Q: &EcPoint>, + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, ) { self.field_chip.assert_equal(ctx, &P.x, &Q.x); self.field_chip.assert_equal(ctx, &P.y, &Q.y); } - pub fn sum<'b, 'v: 'b, C>( + pub fn sum( &self, - ctx: &mut Context<'v, F>, - points: impl Iterator>>, - ) -> EcPoint> + ctx: &mut Context, + points: impl Iterator>, + ) -> EcPoint where C: CurveAffineExt, - FC::FieldPoint<'v>: 'b, { let rand_point = self.load_random_point::(ctx); self.field_chip.enforce_less_than(ctx, rand_point.x()); let mut acc = rand_point.clone(); for point in points { self.field_chip.enforce_less_than(ctx, point.x()); - acc = self.add_unequal(ctx, &acc, point, true); + acc = self.add_unequal(ctx, &acc, &point, true); self.field_chip.enforce_less_than(ctx, acc.x()); } self.sub_unequal(ctx, &acc, &rand_point, true) @@ -744,38 +751,41 @@ impl> EccChip { impl> EccChip where - for<'v> FC: Selectable = FC::FieldPoint<'v>>, + FC: Selectable, { - pub fn select<'v>( + pub fn select( &self, - ctx: &mut Context<'_, F>, - P: &EcPoint>, - Q: &EcPoint>, - condition: &AssignedValue<'v, F>, - ) -> EcPoint> { + ctx: &mut Context, + P: &EcPoint, + Q: &EcPoint, + condition: &AssignedValue, + ) -> EcPoint { ec_select(&self.field_chip, ctx, P, Q, condition) } - pub fn scalar_mult<'v>( + pub fn scalar_mult( &self, - ctx: &mut Context<'v, F>, - P: &EcPoint>, - scalar: &Vec>, + ctx: &mut Context, + P: &EcPoint, + scalar: &Vec>, max_bits: usize, window_bits: usize, - ) -> EcPoint> { - scalar_multiply::(&self.field_chip, ctx, P, scalar, max_bits, window_bits) + ) -> EcPoint + where + C: CurveAffine, + { + scalar_multiply::(&self.field_chip, ctx, P, scalar, max_bits, window_bits) } // TODO: put a check in place that scalar is < modulus of C::Scalar - pub fn variable_base_msm<'v, C>( + pub fn variable_base_msm( &self, - ctx: &mut Context<'v, F>, - P: &[EcPoint>], - scalars: &[Vec>], + ctx: &mut Context, + P: &[EcPoint], + scalars: &[Vec>], max_bits: usize, window_bits: usize, - ) -> EcPoint> + ) -> EcPoint where C: CurveAffineExt, C::Base: ff::PrimeField, @@ -819,18 +829,18 @@ where FC::FieldType: PrimeField, { // TODO: put a check in place that scalar is < modulus of C::Scalar - pub fn fixed_base_scalar_mult<'v, C>( + pub fn fixed_base_scalar_mult( &self, - ctx: &mut Context<'v, F>, + ctx: &mut Context, point: &C, - scalar: &[AssignedValue<'v, F>], + scalar: &[AssignedValue], max_bits: usize, window_bits: usize, - ) -> EcPoint> + ) -> EcPoint where C: CurveAffineExt, - FC: PrimeFieldChip = CRTInteger<'v, F>> - + Selectable = FC::FieldPoint<'v>>, + FC: PrimeFieldChip> + + Selectable, { fixed_base::scalar_multiply::( &self.field_chip, @@ -847,19 +857,19 @@ where /// `clump_factor = 0` means auto-calculate /// /// The user should filter out base points that are identity beforehand; we do not separately do this here - pub fn fixed_base_msm<'v, C>( + pub fn fixed_base_msm( &self, - ctx: &mut Context<'v, F>, + ctx: &mut Context, points: &[C], - scalars: &[Vec>], + scalars: &[Vec>], max_scalar_bits_per_cell: usize, _radix: usize, clump_factor: usize, - ) -> EcPoint> + ) -> EcPoint where C: CurveAffineExt, - FC: PrimeFieldChip = CRTInteger<'v, F>> - + Selectable = FC::FieldPoint<'v>>, + FC: PrimeFieldChip> + + Selectable, { assert_eq!(points.len(), scalars.len()); #[cfg(feature = "display")] diff --git a/halo2-ecc/src/ecc/pippenger.rs b/halo2-ecc/src/ecc/pippenger.rs index 4598ab1a..2871421c 100644 --- a/halo2-ecc/src/ecc/pippenger.rs +++ b/halo2-ecc/src/ecc/pippenger.rs @@ -2,12 +2,9 @@ use super::{ ec_add_unequal, ec_double, ec_select, ec_select_from_bits, ec_sub_unequal, load_random_point, EcPoint, }; +use crate::fields::PrimeField; use crate::fields::{FieldChip, Selectable}; -use halo2_base::{ - gates::GateInstructions, - utils::{CurveAffineExt, PrimeField}, - AssignedValue, Context, -}; +use halo2_base::{gates::GateInstructions, utils::CurveAffineExt, AssignedValue, Context}; // Reference: https://jbootle.github.io/Misc/pippenger.pdf @@ -15,17 +12,18 @@ use halo2_base::{ // Output: // * new_points: length `points.len() * radix` // * new_bool_scalars: 2d array `ceil(scalar_bits / radix)` by `points.len() * radix` -pub fn decompose<'v, F, FC>( +pub fn decompose( chip: &FC, - ctx: &mut Context<'v, F>, - points: &[EcPoint>], - scalars: &[Vec>], + ctx: &mut Context, + points: &[EcPoint], + scalars: &[Vec>], max_scalar_bits_per_cell: usize, radix: usize, -) -> (Vec>>, Vec>>) +) -> (Vec>, Vec>>) where F: PrimeField, FC: FieldChip, + C: CurveAffineExt, { assert_eq!(points.len(), scalars.len()); let scalar_bits = max_scalar_bits_per_cell * scalars[0].len(); @@ -37,11 +35,11 @@ where let zero_cell = chip.gate().load_zero(ctx); for (point, scalar) in points.iter().zip(scalars.iter()) { assert_eq!(scalars[0].len(), scalar.len()); - let mut g = point.clone(); + let mut g = (*point).clone(); new_points.push(g); for _ in 1..radix { // if radix > 1, this does not work if `points` contains identity point - g = ec_double(chip, ctx, new_points.last().unwrap()); + g = ec_double::(chip, ctx, new_points.last().unwrap()); new_points.push(g); } let mut bits = Vec::with_capacity(scalar_bits); @@ -62,15 +60,15 @@ where // Given points[i] and bool_scalars[j][i], // compute G'[j] = sum_{i=0..points.len()} points[i] * bool_scalars[j][i] // output is [ G'[j] + rand_point ]_{j=0..bool_scalars.len()}, rand_point -pub fn multi_product<'v, F: PrimeField, FC, C>( +pub fn multi_product( chip: &FC, - ctx: &mut Context<'v, F>, - points: &[EcPoint>], - bool_scalars: &[Vec>], + ctx: &mut Context, + points: &[EcPoint], + bool_scalars: &[Vec>], clumping_factor: usize, -) -> (Vec>>, EcPoint>) +) -> (Vec>, EcPoint) where - FC: FieldChip + Selectable = FC::FieldPoint<'v>>, + FC: FieldChip + Selectable, C: CurveAffineExt, { let c = clumping_factor; // this is `b` in Section 3 of Bootle @@ -91,7 +89,7 @@ where // for later addition collision-prevension, we need a different random point per round // we take 2^round * rand_base if round > 0 { - rand_point = ec_double(chip, ctx, &rand_point); + rand_point = ec_double::(chip, ctx, &rand_point); } // stores { rand_point, rand_point + points[0], rand_point + points[1], rand_point + points[0] + points[1] , ... } // since rand_point is random, we can always use add_unequal (with strict constraint checking that the points are indeed unequal and not negative of each other) @@ -132,27 +130,27 @@ where } // we have acc[j] = G'[j] + (2^num_rounds - 1) * rand_base - rand_point = ec_double(chip, ctx, &rand_point); + rand_point = ec_double::(chip, ctx, &rand_point); rand_point = ec_sub_unequal(chip, ctx, &rand_point, &rand_base, false); (acc, rand_point) } -pub fn multi_exp<'v, F: PrimeField, FC, C>( +pub fn multi_exp( chip: &FC, - ctx: &mut Context<'v, F>, - points: &[EcPoint>], - scalars: &[Vec>], + ctx: &mut Context, + points: &[EcPoint], + scalars: &[Vec>], max_scalar_bits_per_cell: usize, radix: usize, clump_factor: usize, -) -> EcPoint> +) -> EcPoint where - FC: FieldChip + Selectable = FC::FieldPoint<'v>>, + FC: FieldChip + Selectable, C: CurveAffineExt, { let (points, bool_scalars) = - decompose::(chip, ctx, points, scalars, max_scalar_bits_per_cell, radix); + decompose::(chip, ctx, points, scalars, max_scalar_bits_per_cell, radix); /* let t = bool_scalars.len(); @@ -182,8 +180,8 @@ where let mut rand_sum = rand_point.clone(); for g in agg.iter().rev() { for _ in 0..radix { - sum = ec_double(chip, ctx, &sum); - rand_sum = ec_double(chip, ctx, &rand_sum); + sum = ec_double::(chip, ctx, &sum); + rand_sum = ec_double::(chip, ctx, &rand_sum); } sum = ec_add_unequal(chip, ctx, &sum, g, true); chip.enforce_less_than(ctx, sum.x()); @@ -195,7 +193,7 @@ where } if radix == 1 { - rand_sum = ec_double(chip, ctx, &rand_sum); + rand_sum = ec_double::(chip, ctx, &rand_sum); // assume 2^t != +-1 mod modulus::() rand_sum = ec_sub_unequal(chip, ctx, &rand_sum, &rand_point, false); } diff --git a/halo2-ecc/src/ecc/tests.rs b/halo2-ecc/src/ecc/tests.rs index fa9d6ed5..1b5589f5 100644 --- a/halo2-ecc/src/ecc/tests.rs +++ b/halo2-ecc/src/ecc/tests.rs @@ -2,6 +2,7 @@ use super::*; use crate::fields::fp::{FpConfig, FpStrategy}; use crate::fields::fp2::Fp2Chip; +use crate::fields::PrimeField; use crate::halo2_proofs::{ circuit::*, dev::MockProver, @@ -11,9 +12,7 @@ use crate::halo2_proofs::{ use group::Group; use halo2_base::utils::bigint_to_fe; use halo2_base::SKIP_FIRST_PASS; -use halo2_base::{ - gates::range::RangeStrategy, utils::value_to_option, utils::PrimeField, ContextParams, -}; +use halo2_base::{gates::range::RangeStrategy, utils::value_to_option, ContextParams}; use num_bigint::{BigInt, RandBigInt}; use std::marker::PhantomData; use std::ops::Neg; @@ -111,7 +110,7 @@ impl Circuit for MyCircuit { // test double { - let doub = chip.double(ctx, &P_assigned); + let doub = chip.double::(ctx, &P_assigned); assert_eq!( value_to_option(doub.x.truncation.to_bigint(config.limb_bits)), value_to_option(doub.x.value.clone()) diff --git a/halo2-ecc/src/fields/fp.rs b/halo2-ecc/src/fields/fp.rs index 1329726a..6a0966bc 100644 --- a/halo2-ecc/src/fields/fp.rs +++ b/halo2-ecc/src/fields/fp.rs @@ -4,6 +4,7 @@ use crate::bigint::{ scalar_mul_and_add_no_carry, scalar_mul_no_carry, select, select_by_indicator, sub, sub_no_carry, CRTInteger, FixedCRTInteger, OverflowInteger, }; +use crate::fields::PrimeField; use crate::halo2_proofs::{ circuit::{Layouter, Region, Value}, halo2curves::CurveAffine, @@ -16,7 +17,7 @@ use halo2_base::{ }, utils::{ bigint_to_fe, biguint_to_fe, bit_length, decompose_bigint_option, decompose_biguint, - fe_to_biguint, modulus, PrimeField, + fe_to_biguint, modulus, }, AssignedValue, Context, ContextParams, QuantumCell::{Constant, Existing}, @@ -100,7 +101,7 @@ impl FpConfig { let limb_base = biguint_to_fe::(&(BigUint::one() << limb_bits)); let mut limb_bases = Vec::with_capacity(num_limbs); - limb_bases.push(F::one()); + limb_bases.push(F::ONE); while limb_bases.len() != num_limbs { limb_bases.push(limb_base * limb_bases.last().unwrap()); } @@ -138,35 +139,46 @@ impl FpConfig { self.range.load_lookup_table(layouter) } - pub fn enforce_less_than_p<'v>(&self, ctx: &mut Context<'v, F>, a: &CRTInteger<'v, F>) { + pub fn enforce_less_than_p(&self, ctx: &mut Context, a: &CRTInteger) { // a < p iff a - p has underflow + let borrow = self.minus_p(ctx, a); + self.range.gate.assert_is_const(ctx, &borrow, F::ONE) + } + + pub fn is_less_than_p(&self, ctx: &mut Context, a: &CRTInteger) -> AssignedValue { + // a < p iff a - p has underflow + let borrow = self.minus_p(ctx, a); + let one = self.range.gate.load_constant(ctx, F::ONE); + self.range.gate.is_equal(ctx, Existing(borrow), Existing(one)) + } + + pub fn finalize(&self, ctx: &mut Context) -> usize { + self.range.finalize(ctx) + } + + fn minus_p(&self, ctx: &mut Context, a: &CRTInteger) -> AssignedValue { let mut borrow: Option> = None; for (p_limb, a_limb) in self.p_limbs.iter().zip(a.truncation.limbs.iter()) { let lt = match borrow { None => self.range.is_less_than( ctx, - Existing(a_limb), + Existing(*a_limb), Constant(*p_limb), self.limb_bits, ), Some(borrow) => { - let plus_borrow = - self.range.gate.add(ctx, Constant(*p_limb), Existing(&borrow)); + let plus_borrow = self.range.gate.add(ctx, Constant(*p_limb), Existing(borrow)); self.range.is_less_than( ctx, - Existing(a_limb), - Existing(&plus_borrow), + Existing(*a_limb), + Existing(plus_borrow), self.limb_bits, ) } }; borrow = Some(lt); } - self.range.gate.assert_is_const(ctx, &borrow.unwrap(), F::one()) - } - - pub fn finalize(&self, ctx: &mut Context<'_, F>) -> usize { - self.range.finalize(ctx) + borrow.unwrap() } } @@ -186,7 +198,7 @@ impl FieldChip for FpConfig { const PRIME_FIELD_NUM_BITS: u32 = Fp::NUM_BITS; type ConstantType = BigUint; type WitnessType = Value; - type FieldPoint<'v> = CRTInteger<'v, F>; + type FieldPoint = CRTInteger; type FieldType = Fp; type RangeChip = RangeConfig; @@ -212,7 +224,7 @@ impl FieldChip for FpConfig { x.map(|x| BigInt::from(fe_to_biguint(&x))) } - fn load_private<'v>(&self, ctx: &mut Context<'_, F>, a: Value) -> CRTInteger<'v, F> { + fn load_private(&self, ctx: &mut Context, a: Value) -> CRTInteger { let a_vec = decompose_bigint_option::(a.as_ref(), self.num_limbs, self.limb_bits); let limbs = self.range.gate().assign_witnesses(ctx, a_vec); @@ -232,7 +244,7 @@ impl FieldChip for FpConfig { a_loaded } - fn load_constant<'v>(&self, ctx: &mut Context<'_, F>, a: BigUint) -> CRTInteger<'v, F> { + fn load_constant(&self, ctx: &mut Context, a: BigUint) -> CRTInteger { let a_native = self.range.gate.assign_region_last( ctx, vec![Constant(biguint_to_fe(&(&a % modulus::())))], @@ -252,29 +264,29 @@ impl FieldChip for FpConfig { } // signed overflow BigInt functions - fn add_no_carry<'v>( + fn add_no_carry( &self, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, - ) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, + ) -> CRTInteger { add_no_carry::crt::(self.range.gate(), ctx, a, b) } - fn add_constant_no_carry<'v>( + fn add_constant_no_carry( &self, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, + ctx: &mut Context, + a: &CRTInteger, c: BigUint, - ) -> CRTInteger<'v, F> { + ) -> CRTInteger { let c = FixedCRTInteger::from_native(c, self.num_limbs, self.limb_bits); let c_native = biguint_to_fe::(&(&c.value % modulus::())); let mut limbs = Vec::with_capacity(a.truncation.limbs.len()); for (a_limb, c_limb) in a.truncation.limbs.iter().zip(c.truncation.limbs.into_iter()) { - let limb = self.range.gate.add(ctx, Existing(a_limb), Constant(c_limb)); + let limb = self.range.gate.add(ctx, Existing(*a_limb), Constant(c_limb)); limbs.push(limb); } - let native = self.range.gate.add(ctx, Existing(&a.native), Constant(c_native)); + let native = self.range.gate.add(ctx, Existing(a.native), Constant(c_native)); let trunc = OverflowInteger::construct(limbs, max(a.truncation.max_limb_bits, self.limb_bits) + 1); let value = a.value.as_ref().map(|a| a + BigInt::from(c.value)); @@ -282,12 +294,12 @@ impl FieldChip for FpConfig { CRTInteger::construct(trunc, native, value) } - fn sub_no_carry<'v>( + fn sub_no_carry( &self, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, - ) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, + ) -> CRTInteger { sub_no_carry::crt::(self.range.gate(), ctx, a, b) } @@ -295,47 +307,47 @@ impl FieldChip for FpConfig { // Output: p - a if a != 0, else a // Assume the actual value of `a` equals `a.truncation` // Constrains a.truncation <= p using subtraction with carries - fn negate<'v>(&self, ctx: &mut Context<'v, F>, a: &CRTInteger<'v, F>) -> CRTInteger<'v, F> { + fn negate(&self, ctx: &mut Context, a: &CRTInteger) -> CRTInteger { // Compute p - a.truncation using carries let p = self.load_constant(ctx, self.p.to_biguint().unwrap()); let (out_or_p, underflow) = sub::crt::(self.range(), ctx, &p, a, self.limb_bits, self.limb_bases[1]); // constrain underflow to equal 0 - self.range.gate.assert_is_const(ctx, &underflow, F::zero()); + self.range.gate.assert_is_const(ctx, &underflow, F::ZERO); let a_is_zero = big_is_zero::assign::(self.gate(), ctx, &a.truncation); select::crt::(self.range.gate(), ctx, a, &out_or_p, &a_is_zero) } - fn scalar_mul_no_carry<'v>( + fn scalar_mul_no_carry( &self, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, + ctx: &mut Context, + a: &CRTInteger, c: i64, - ) -> CRTInteger<'v, F> { + ) -> CRTInteger { scalar_mul_no_carry::crt::(self.range.gate(), ctx, a, c) } - fn scalar_mul_and_add_no_carry<'v>( + fn scalar_mul_and_add_no_carry( &self, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, c: i64, - ) -> CRTInteger<'v, F> { + ) -> CRTInteger { scalar_mul_and_add_no_carry::crt::(self.range.gate(), ctx, a, b, c) } - fn mul_no_carry<'v>( + fn mul_no_carry( &self, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, - ) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, + ) -> CRTInteger { mul_no_carry::crt::(self.range.gate(), ctx, a, b, self.num_limbs_log2_ceil) } - fn check_carry_mod_to_zero<'v>(&self, ctx: &mut Context<'v, F>, a: &CRTInteger<'v, F>) { + fn check_carry_mod_to_zero(&self, ctx: &mut Context, a: &CRTInteger) { check_carry_mod_to_zero::crt::( self.range(), // &self.bigint_chip, @@ -351,7 +363,7 @@ impl FieldChip for FpConfig { ) } - fn carry_mod<'v>(&self, ctx: &mut Context<'v, F>, a: &CRTInteger<'v, F>) -> CRTInteger<'v, F> { + fn carry_mod(&self, ctx: &mut Context, a: &CRTInteger) -> CRTInteger { carry_mod::crt::( self.range(), // &self.bigint_chip, @@ -367,10 +379,10 @@ impl FieldChip for FpConfig { ) } - fn range_check<'v>( + fn range_check( &self, - ctx: &mut Context<'v, F>, - a: &CRTInteger<'v, F>, + ctx: &mut Context, + a: &CRTInteger, max_bits: usize, // the maximum bits that a.value could take ) { let n = self.limb_bits; @@ -390,15 +402,11 @@ impl FieldChip for FpConfig { } } - fn enforce_less_than<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>) { + fn enforce_less_than(&self, ctx: &mut Context, a: &Self::FieldPoint) { self.enforce_less_than_p(ctx, a) } - fn is_soft_zero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &CRTInteger<'v, F>, - ) -> AssignedValue<'v, F> { + fn is_soft_zero(&self, ctx: &mut Context, a: &CRTInteger) -> AssignedValue { let is_zero = big_is_zero::crt::(self.gate(), ctx, a); // underflow != 0 iff carry < p @@ -406,84 +414,97 @@ impl FieldChip for FpConfig { let (_, underflow) = sub::crt::(self.range(), ctx, a, &p, self.limb_bits, self.limb_bases[1]); let is_underflow_zero = self.gate().is_zero(ctx, &underflow); - let range_check = self.gate().not(ctx, Existing(&is_underflow_zero)); + let range_check = self.gate().not(ctx, Existing(is_underflow_zero)); - self.gate().and(ctx, Existing(&is_zero), Existing(&range_check)) + self.gate().and(ctx, Existing(is_zero), Existing(range_check)) } - fn is_soft_nonzero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &CRTInteger<'v, F>, - ) -> AssignedValue<'v, F> { + fn is_soft_nonzero(&self, ctx: &mut Context, a: &CRTInteger) -> AssignedValue { let is_zero = big_is_zero::crt::(self.gate(), ctx, a); - let is_nonzero = self.gate().not(ctx, Existing(&is_zero)); + let is_nonzero = self.gate().not(ctx, Existing(is_zero)); // underflow != 0 iff carry < p let p = self.load_constant(ctx, self.p.to_biguint().unwrap()); let (_, underflow) = sub::crt::(self.range(), ctx, a, &p, self.limb_bits, self.limb_bases[1]); let is_underflow_zero = self.gate().is_zero(ctx, &underflow); - let range_check = self.gate().not(ctx, Existing(&is_underflow_zero)); + let range_check = self.gate().not(ctx, Existing(is_underflow_zero)); - self.gate().and(ctx, Existing(&is_nonzero), Existing(&range_check)) + self.gate().and(ctx, Existing(is_nonzero), Existing(range_check)) } // assuming `a` has been range checked to be a proper BigInt // constrain the witness `a` to be `< p` // then check if `a` is 0 - fn is_zero<'v>(&self, ctx: &mut Context<'v, F>, a: &CRTInteger<'v, F>) -> AssignedValue<'v, F> { + fn is_zero(&self, ctx: &mut Context, a: &CRTInteger) -> AssignedValue { self.enforce_less_than_p(ctx, a); // just check truncated limbs are all 0 since they determine the native value big_is_zero::positive::(self.gate(), ctx, &a.truncation) } - fn is_equal_unenforced<'v>( + fn is_equal_unenforced( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> AssignedValue { big_is_equal::assign::(self.gate(), ctx, &a.truncation, &b.truncation) } // assuming `a, b` have been range checked to be a proper BigInt // constrain the witnesses `a, b` to be `< p` // then assert `a == b` as BigInts - fn assert_equal<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) { + fn assert_equal(&self, ctx: &mut Context, a: &Self::FieldPoint, b: &Self::FieldPoint) { self.enforce_less_than_p(ctx, a); self.enforce_less_than_p(ctx, b); // a.native and b.native are derived from `a.truncation, b.truncation`, so no need to check if they're equal - for (limb_a, limb_b) in a.truncation.limbs.iter().zip(a.truncation.limbs.iter()) { - self.range.gate.assert_equal(ctx, Existing(limb_a), Existing(limb_b)); + for (limb_a, limb_b) in a.truncation.limbs.iter().zip(b.truncation.limbs.iter()) { + self.range.gate.assert_equal(ctx, Existing(*limb_a), Existing(*limb_b)); } } + + fn divide( + &self, + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { + let quotient = self.divide_unsafe(ctx, a, b); + let b_is_zero = self.is_zero(ctx, b); + self.select(ctx, b, "ient, &b_is_zero) + } + + fn neg_divide( + &self, + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { + let quotient = self.neg_divide_unsafe(ctx, a, b); + let b_is_zero = self.is_zero(ctx, b); + self.select(ctx, b, "ient, &b_is_zero) + } } impl Selectable for FpConfig { - type Point<'v> = CRTInteger<'v, F>; + type Point = CRTInteger; - fn select<'v>( + fn select( &self, - ctx: &mut Context<'_, F>, - a: &CRTInteger<'v, F>, - b: &CRTInteger<'v, F>, - sel: &AssignedValue<'v, F>, - ) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &CRTInteger, + b: &CRTInteger, + sel: &AssignedValue, + ) -> CRTInteger { select::crt::(self.range.gate(), ctx, a, b, sel) } - fn select_by_indicator<'v>( + fn select_by_indicator( &self, - ctx: &mut Context<'_, F>, - a: &[CRTInteger<'v, F>], - coeffs: &[AssignedValue<'v, F>], - ) -> CRTInteger<'v, F> { + ctx: &mut Context, + a: &[CRTInteger], + coeffs: &[AssignedValue], + ) -> CRTInteger { select_by_indicator::crt::(self.range.gate(), ctx, a, coeffs, &self.limb_bases) } } diff --git a/halo2-ecc/src/fields/fp12.rs b/halo2-ecc/src/fields/fp12.rs index f130fd52..16e65cf9 100644 --- a/halo2-ecc/src/fields/fp12.rs +++ b/halo2-ecc/src/fields/fp12.rs @@ -1,8 +1,10 @@ use super::{FieldChip, FieldExtConstructor, FieldExtPoint, PrimeFieldChip}; +use crate::fields::PrimeField; use crate::halo2_proofs::{arithmetic::Field, circuit::Value}; +use ff::PrimeField as _; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::{fe_to_biguint, value_to_option, PrimeField}, + utils::{fe_to_biguint, value_to_option}, AssignedValue, Context, QuantumCell::Existing, }; @@ -15,18 +17,18 @@ use std::marker::PhantomData; /// be irreducible over Fp; i.e., in order for -1 to not be a square (quadratic residue) in Fp /// This means we store an Fp12 point as `\sum_{i = 0}^6 (a_{i0} + a_{i1} * u) * w^i` /// This is encoded in an FqPoint of degree 12 as `(a_{00}, ..., a_{50}, a_{01}, ..., a_{51})` -pub struct Fp12Chip<'a, F: PrimeField, FpChip: PrimeFieldChip, Fp12: Field, const XI_0: i64> +pub struct Fp12Chip, Fp12: Field, const XI_0: i64> where FpChip::FieldType: PrimeField, { // for historical reasons, leaving this as a reference // for the current implementation we could also just use the de-referenced version: `fp_chip: FpChip` - pub fp_chip: &'a FpChip, + pub fp_chip: FpChip, _f: PhantomData, _fp12: PhantomData, } -impl<'a, F, FpChip, Fp12, const XI_0: i64> Fp12Chip<'a, F, FpChip, Fp12, XI_0> +impl Fp12Chip where F: PrimeField, FpChip: PrimeFieldChip, @@ -34,16 +36,16 @@ where Fp12: Field + FieldExtConstructor, { /// User must construct an `FpChip` first using a config. This is intended so everything shares a single `FlexGateChip`, which is needed for the column allocation to work. - pub fn construct(fp_chip: &'a FpChip) -> Self { + pub fn construct(fp_chip: FpChip) -> Self { Self { fp_chip, _f: PhantomData, _fp12: PhantomData } } - pub fn fp2_mul_no_carry<'v>( + pub fn fp2_mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &FieldExtPoint>, - fp2_pt: &FieldExtPoint>, - ) -> FieldExtPoint> { + ctx: &mut Context, + a: &FieldExtPoint, + fp2_pt: &FieldExtPoint, + ) -> FieldExtPoint { assert_eq!(a.coeffs.len(), 12); assert_eq!(fp2_pt.coeffs.len(), 2); @@ -64,11 +66,11 @@ where } // for \sum_i (a_i + b_i u) w^i, returns \sum_i (-1)^i (a_i + b_i u) w^i - pub fn conjugate<'v>( + pub fn conjugate( &self, - ctx: &mut Context<'v, F>, - a: &FieldExtPoint>, - ) -> FieldExtPoint> { + ctx: &mut Context, + a: &FieldExtPoint, + ) -> FieldExtPoint { assert_eq!(a.coeffs.len(), 12); let coeffs = a @@ -82,11 +84,11 @@ where } /// multiply (a0 + a1 * u) * (XI0 + u) without carry -pub fn mul_no_carry_w6<'v, F: PrimeField, FC: FieldChip, const XI_0: i64>( +pub fn mul_no_carry_w6, const XI_0: i64>( fp_chip: &FC, - ctx: &mut Context<'v, F>, - a: &FieldExtPoint>, -) -> FieldExtPoint> { + ctx: &mut Context, + a: &FieldExtPoint, +) -> FieldExtPoint { assert_eq!(a.coeffs.len(), 2); let (a0, a1) = (&a.coeffs[0], &a.coeffs[1]); // (a0 + a1 u) * (XI_0 + u) = (a0 * XI_0 - a1) + (a1 * XI_0 + a0) u with u^2 = -1 @@ -97,7 +99,7 @@ pub fn mul_no_carry_w6<'v, F: PrimeField, FC: FieldChip, const XI_0: i64>( FieldExtPoint::construct(vec![out0_0_nocarry, out0_1_nocarry]) } -impl<'a, F, FpChip, Fp12, const XI_0: i64> FieldChip for Fp12Chip<'a, F, FpChip, Fp12, XI_0> +impl FieldChip for Fp12Chip where F: PrimeField, FpChip: PrimeFieldChip, ConstantType = BigUint>, @@ -107,7 +109,7 @@ where const PRIME_FIELD_NUM_BITS: u32 = FpChip::FieldType::NUM_BITS; type ConstantType = Fp12; type WitnessType = Vec>; - type FieldPoint<'v> = FieldExtPoint>; + type FieldPoint = FieldExtPoint; type FieldType = Fp12; type RangeChip = FpChip::RangeChip; @@ -122,7 +124,7 @@ where self.fp_chip.limb_bits() } - fn get_assigned_value(&self, x: &Self::FieldPoint<'_>) -> Value { + fn get_assigned_value(&self, x: &Self::FieldPoint) -> Value { assert_eq!(x.coeffs.len(), 12); let values = x.coeffs.iter().map(|v| self.fp_chip.get_assigned_value(v)); let values_collected: Value> = values.into_iter().collect(); @@ -141,11 +143,7 @@ where } } - fn load_private<'v>( - &self, - ctx: &mut Context<'_, F>, - coeffs: Vec>, - ) -> Self::FieldPoint<'v> { + fn load_private(&self, ctx: &mut Context, coeffs: Vec>) -> Self::FieldPoint { assert_eq!(coeffs.len(), 12); let mut assigned_coeffs = Vec::with_capacity(12); for a in coeffs { @@ -155,7 +153,7 @@ where Self::FieldPoint::construct(assigned_coeffs) } - fn load_constant<'v>(&self, ctx: &mut Context<'_, F>, c: Fp12) -> Self::FieldPoint<'v> { + fn load_constant(&self, ctx: &mut Context, c: Fp12) -> Self::FieldPoint { let mut assigned_coeffs = Vec::with_capacity(12); for a in &c.coeffs() { let assigned_coeff = self.fp_chip.load_constant(ctx, fe_to_biguint(a)); @@ -165,12 +163,12 @@ where } // signed overflow BigInt functions - fn add_no_carry<'v>( + fn add_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { assert_eq!(a.coeffs.len(), b.coeffs.len()); let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { @@ -180,12 +178,12 @@ where Self::FieldPoint::construct(out_coeffs) } - fn add_constant_no_carry<'v>( + fn add_constant_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, c: Self::ConstantType, - ) -> Self::FieldPoint<'v> { + ) -> Self::FieldPoint { let c_coeffs = c.coeffs(); assert_eq!(a.coeffs.len(), c_coeffs.len()); let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); @@ -196,12 +194,12 @@ where Self::FieldPoint::construct(out_coeffs) } - fn sub_no_carry<'v>( + fn sub_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { assert_eq!(a.coeffs.len(), b.coeffs.len()); let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { @@ -211,11 +209,7 @@ where Self::FieldPoint::construct(out_coeffs) } - fn negate<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + fn negate(&self, ctx: &mut Context, a: &Self::FieldPoint) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for a_coeff in &a.coeffs { let out_coeff = self.fp_chip.negate(ctx, a_coeff); @@ -224,12 +218,12 @@ where Self::FieldPoint::construct(out_coeffs) } - fn scalar_mul_no_carry<'v>( + fn scalar_mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, c: i64, - ) -> Self::FieldPoint<'v> { + ) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { let coeff = self.fp_chip.scalar_mul_no_carry(ctx, &a.coeffs[i], c); @@ -238,13 +232,13 @@ where Self::FieldPoint::construct(out_coeffs) } - fn scalar_mul_and_add_no_carry<'v>( + fn scalar_mul_and_add_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, c: i64, - ) -> Self::FieldPoint<'v> { + ) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { let coeff = @@ -255,12 +249,12 @@ where } // w^6 = u + xi for xi = 9 - fn mul_no_carry<'v>( + fn mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { assert_eq!(a.coeffs.len(), 12); assert_eq!(b.coeffs.len(), 12); @@ -341,17 +335,13 @@ where Self::FieldPoint::construct(out_coeffs) } - fn check_carry_mod_to_zero<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>) { + fn check_carry_mod_to_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) { for coeff in &a.coeffs { self.fp_chip.check_carry_mod_to_zero(ctx, coeff); } } - fn carry_mod<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + fn carry_mod(&self, ctx: &mut Context, a: &Self::FieldPoint) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for a_coeff in &a.coeffs { let coeff = self.fp_chip.carry_mod(ctx, a_coeff); @@ -360,28 +350,24 @@ where Self::FieldPoint::construct(out_coeffs) } - fn range_check<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>, max_bits: usize) { + fn range_check(&self, ctx: &mut Context, a: &Self::FieldPoint, max_bits: usize) { for a_coeff in &a.coeffs { self.fp_chip.range_check(ctx, a_coeff, max_bits); } } - fn enforce_less_than<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>) { + fn enforce_less_than(&self, ctx: &mut Context, a: &Self::FieldPoint) { for a_coeff in &a.coeffs { self.fp_chip.enforce_less_than(ctx, a_coeff) } } - fn is_soft_zero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + fn is_soft_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue { let mut prev = None; for a_coeff in &a.coeffs { let coeff = self.fp_chip.is_soft_zero(ctx, a_coeff); if let Some(p) = prev { - let new = self.fp_chip.range().gate().and(ctx, Existing(&coeff), Existing(&p)); + let new = self.fp_chip.range().gate().and(ctx, Existing(coeff), Existing(p)); prev = Some(new); } else { prev = Some(coeff); @@ -390,16 +376,12 @@ where prev.unwrap() } - fn is_soft_nonzero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + fn is_soft_nonzero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue { let mut prev = None; for a_coeff in &a.coeffs { let coeff = self.fp_chip.is_soft_nonzero(ctx, a_coeff); if let Some(p) = prev { - let new = self.fp_chip.range().gate().or(ctx, Existing(&coeff), Existing(&p)); + let new = self.fp_chip.range().gate().or(ctx, Existing(coeff), Existing(p)); prev = Some(new); } else { prev = Some(coeff); @@ -408,16 +390,12 @@ where prev.unwrap() } - fn is_zero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + fn is_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue { let mut prev = None; for a_coeff in &a.coeffs { let coeff = self.fp_chip.is_zero(ctx, a_coeff); if let Some(p) = prev { - let new = self.fp_chip.range().gate().and(ctx, Existing(&coeff), Existing(&p)); + let new = self.fp_chip.range().gate().and(ctx, Existing(coeff), Existing(p)); prev = Some(new); } else { prev = Some(coeff); @@ -426,17 +404,17 @@ where prev.unwrap() } - fn is_equal<'v>( + fn is_equal( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> AssignedValue { let mut acc = None; for (a_coeff, b_coeff) in a.coeffs.iter().zip(b.coeffs.iter()) { let coeff = self.fp_chip.is_equal(ctx, a_coeff, b_coeff); if let Some(c) = acc { - acc = Some(self.fp_chip.range().gate().and(ctx, Existing(&coeff), Existing(&c))); + acc = Some(self.fp_chip.range().gate().and(ctx, Existing(coeff), Existing(c))); } else { acc = Some(coeff); } @@ -444,17 +422,17 @@ where acc.unwrap() } - fn is_equal_unenforced<'v>( + fn is_equal_unenforced( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> AssignedValue { let mut acc = None; for (a_coeff, b_coeff) in a.coeffs.iter().zip(b.coeffs.iter()) { let coeff = self.fp_chip.is_equal_unenforced(ctx, a_coeff, b_coeff); if let Some(c) = acc { - acc = Some(self.fp_chip.range().gate().and(ctx, Existing(&coeff), Existing(&c))); + acc = Some(self.fp_chip.range().gate().and(ctx, Existing(coeff), Existing(c))); } else { acc = Some(coeff); } @@ -462,16 +440,29 @@ where acc.unwrap() } - fn assert_equal<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) { + fn assert_equal(&self, ctx: &mut Context, a: &Self::FieldPoint, b: &Self::FieldPoint) { for (a_coeff, b_coeff) in a.coeffs.iter().zip(b.coeffs.iter()) { self.fp_chip.assert_equal(ctx, a_coeff, b_coeff); } } + + fn divide( + &self, + _ctx: &mut Context, + _a: &Self::FieldPoint, + _b: &Self::FieldPoint, + ) -> Self::FieldPoint { + unimplemented!() + } + + fn neg_divide( + &self, + _ctx: &mut Context, + _a: &Self::FieldPoint, + _b: &Self::FieldPoint, + ) -> Self::FieldPoint { + unimplemented!() + } } mod bn254 { diff --git a/halo2-ecc/src/fields/fp2.rs b/halo2-ecc/src/fields/fp2.rs index 633ae6fa..329152ad 100644 --- a/halo2-ecc/src/fields/fp2.rs +++ b/halo2-ecc/src/fields/fp2.rs @@ -1,8 +1,10 @@ use super::{FieldChip, FieldExtConstructor, FieldExtPoint, PrimeFieldChip, Selectable}; +use crate::fields::PrimeField; use crate::halo2_proofs::{arithmetic::Field, circuit::Value}; +use ff::PrimeField as _; use halo2_base::{ gates::{GateInstructions, RangeInstructions}, - utils::{fe_to_biguint, value_to_option, PrimeField}, + utils::{fe_to_biguint, value_to_option}, AssignedValue, Context, QuantumCell::Existing, }; @@ -14,18 +16,18 @@ use std::marker::PhantomData; /// This implementation assumes p = 3 (mod 4) in order for the polynomial u^2 + 1 to be irreducible over Fp; i.e., in order for -1 to not be a square (quadratic residue) in Fp /// This means we store an Fp2 point as `a_0 + a_1 * u` where `a_0, a_1 in Fp` #[derive(Clone, Debug)] -pub struct Fp2Chip<'a, F: PrimeField, FpChip: PrimeFieldChip, Fp2: Field> +pub struct Fp2Chip, Fp2: Field> where FpChip::FieldType: PrimeField, { // for historical reasons, leaving this as a reference // for the current implementation we could also just use the de-referenced version: `fp_chip: FpChip` - pub fp_chip: &'a FpChip, + pub fp_chip: FpChip, _f: PhantomData, _fp2: PhantomData, } -impl<'a, F, FpChip, Fp2> Fp2Chip<'a, F, FpChip, Fp2> +impl Fp2Chip where F: PrimeField, FpChip: PrimeFieldChip, @@ -33,16 +35,16 @@ where Fp2: Field + FieldExtConstructor, { /// User must construct an `FpChip` first using a config. This is intended so everything shares a single `FlexGateChip`, which is needed for the column allocation to work. - pub fn construct(fp_chip: &'a FpChip) -> Self { + pub fn construct(fp_chip: FpChip) -> Self { Self { fp_chip, _f: PhantomData, _fp2: PhantomData } } - pub fn fp_mul_no_carry<'v>( + pub fn fp_mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &FieldExtPoint>, - fp_point: &FpChip::FieldPoint<'v>, - ) -> FieldExtPoint> { + ctx: &mut Context, + a: &FieldExtPoint, + fp_point: &FpChip::FieldPoint, + ) -> FieldExtPoint { assert_eq!(a.coeffs.len(), 2); let mut out_coeffs = Vec::with_capacity(2); @@ -53,37 +55,37 @@ where FieldExtPoint::construct(out_coeffs) } - pub fn conjugate<'v>( + pub fn conjugate( &self, - ctx: &mut Context<'v, F>, - a: &FieldExtPoint>, - ) -> FieldExtPoint> { + ctx: &mut Context, + a: &FieldExtPoint, + ) -> FieldExtPoint { assert_eq!(a.coeffs.len(), 2); let neg_a1 = self.fp_chip.negate(ctx, &a.coeffs[1]); FieldExtPoint::construct(vec![a.coeffs[0].clone(), neg_a1]) } - pub fn neg_conjugate<'v>( + pub fn neg_conjugate( &self, - ctx: &mut Context<'v, F>, - a: &FieldExtPoint>, - ) -> FieldExtPoint> { + ctx: &mut Context, + a: &FieldExtPoint, + ) -> FieldExtPoint { assert_eq!(a.coeffs.len(), 2); let neg_a0 = self.fp_chip.negate(ctx, &a.coeffs[0]); FieldExtPoint::construct(vec![neg_a0, a.coeffs[1].clone()]) } - pub fn select<'v>( + pub fn select( &self, - ctx: &mut Context<'_, F>, - a: &FieldExtPoint>, - b: &FieldExtPoint>, - sel: &AssignedValue<'v, F>, - ) -> FieldExtPoint> + ctx: &mut Context, + a: &FieldExtPoint, + b: &FieldExtPoint, + sel: &AssignedValue, + ) -> FieldExtPoint where - FpChip: Selectable = FpChip::FieldPoint<'v>>, + FpChip: Selectable, { let coeffs: Vec<_> = a .coeffs @@ -95,7 +97,7 @@ where } } -impl<'a, F, FpChip, Fp2> FieldChip for Fp2Chip<'a, F, FpChip, Fp2> +impl FieldChip for Fp2Chip where F: PrimeField, FpChip::FieldType: PrimeField, @@ -105,7 +107,7 @@ where const PRIME_FIELD_NUM_BITS: u32 = FpChip::FieldType::NUM_BITS; type ConstantType = Fp2; type WitnessType = Vec>; - type FieldPoint<'v> = FieldExtPoint>; + type FieldPoint = FieldExtPoint; type FieldType = Fp2; type RangeChip = FpChip::RangeChip; @@ -120,7 +122,7 @@ where self.fp_chip.limb_bits() } - fn get_assigned_value(&self, x: &Self::FieldPoint<'_>) -> Value { + fn get_assigned_value(&self, x: &Self::FieldPoint) -> Value { assert_eq!(x.coeffs.len(), 2); let c0 = self.fp_chip.get_assigned_value(&x.coeffs[0]); let c1 = self.fp_chip.get_assigned_value(&x.coeffs[1]); @@ -142,11 +144,7 @@ where } } - fn load_private<'v>( - &self, - ctx: &mut Context<'_, F>, - coeffs: Vec>, - ) -> Self::FieldPoint<'v> { + fn load_private(&self, ctx: &mut Context, coeffs: Vec>) -> Self::FieldPoint { assert_eq!(coeffs.len(), 2); let mut assigned_coeffs = Vec::with_capacity(2); for a in coeffs { @@ -156,7 +154,7 @@ where Self::FieldPoint::construct(assigned_coeffs) } - fn load_constant<'v>(&self, ctx: &mut Context<'_, F>, c: Fp2) -> Self::FieldPoint<'v> { + fn load_constant(&self, ctx: &mut Context, c: Fp2) -> Self::FieldPoint { let mut assigned_coeffs = Vec::with_capacity(2); for a in &c.coeffs() { let assigned_coeff = self.fp_chip.load_constant(ctx, fe_to_biguint(a)); @@ -166,12 +164,12 @@ where } // signed overflow BigInt functions - fn add_no_carry<'v>( + fn add_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { assert_eq!(a.coeffs.len(), b.coeffs.len()); let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { @@ -181,12 +179,12 @@ where Self::FieldPoint::construct(out_coeffs) } - fn add_constant_no_carry<'v>( + fn add_constant_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, c: Self::ConstantType, - ) -> Self::FieldPoint<'v> { + ) -> Self::FieldPoint { let c_coeffs = c.coeffs(); assert_eq!(a.coeffs.len(), c_coeffs.len()); let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); @@ -197,12 +195,12 @@ where Self::FieldPoint::construct(out_coeffs) } - fn sub_no_carry<'v>( + fn sub_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { assert_eq!(a.coeffs.len(), b.coeffs.len()); let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { @@ -212,11 +210,7 @@ where Self::FieldPoint::construct(out_coeffs) } - fn negate<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + fn negate(&self, ctx: &mut Context, a: &Self::FieldPoint) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for a_coeff in &a.coeffs { let out_coeff = self.fp_chip.negate(ctx, a_coeff); @@ -225,12 +219,12 @@ where Self::FieldPoint::construct(out_coeffs) } - fn scalar_mul_no_carry<'v>( + fn scalar_mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, c: i64, - ) -> Self::FieldPoint<'v> { + ) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { let coeff = self.fp_chip.scalar_mul_no_carry(ctx, &a.coeffs[i], c); @@ -239,13 +233,13 @@ where Self::FieldPoint::construct(out_coeffs) } - fn scalar_mul_and_add_no_carry<'v>( + fn scalar_mul_and_add_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, c: i64, - ) -> Self::FieldPoint<'v> { + ) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for i in 0..a.coeffs.len() { let coeff = @@ -255,12 +249,12 @@ where Self::FieldPoint::construct(out_coeffs) } - fn mul_no_carry<'v>( + fn mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { assert_eq!(a.coeffs.len(), b.coeffs.len()); // (a_0 + a_1 * u) * (b_0 + b_1 * u) = (a_0 b_0 - a_1 b_1) + (a_0 b_1 + a_1 b_0) * u let mut ab_coeffs = Vec::with_capacity(a.coeffs.len() * b.coeffs.len()); @@ -282,17 +276,13 @@ where Self::FieldPoint::construct(out_coeffs) } - fn check_carry_mod_to_zero<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>) { + fn check_carry_mod_to_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) { for coeff in &a.coeffs { self.fp_chip.check_carry_mod_to_zero(ctx, coeff); } } - fn carry_mod<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + fn carry_mod(&self, ctx: &mut Context, a: &Self::FieldPoint) -> Self::FieldPoint { let mut out_coeffs = Vec::with_capacity(a.coeffs.len()); for a_coeff in &a.coeffs { let coeff = self.fp_chip.carry_mod(ctx, a_coeff); @@ -301,28 +291,24 @@ where Self::FieldPoint::construct(out_coeffs) } - fn range_check<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>, max_bits: usize) { + fn range_check(&self, ctx: &mut Context, a: &Self::FieldPoint, max_bits: usize) { for a_coeff in &a.coeffs { self.fp_chip.range_check(ctx, a_coeff, max_bits); } } - fn enforce_less_than<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>) { + fn enforce_less_than(&self, ctx: &mut Context, a: &Self::FieldPoint) { for a_coeff in &a.coeffs { self.fp_chip.enforce_less_than(ctx, a_coeff) } } - fn is_soft_zero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + fn is_soft_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue { let mut prev = None; for a_coeff in &a.coeffs { let coeff = self.fp_chip.is_soft_zero(ctx, a_coeff); if let Some(p) = prev { - let new = self.fp_chip.range().gate().and(ctx, Existing(&coeff), Existing(&p)); + let new = self.fp_chip.range().gate().and(ctx, Existing(coeff), Existing(p)); prev = Some(new); } else { prev = Some(coeff); @@ -331,16 +317,12 @@ where prev.unwrap() } - fn is_soft_nonzero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + fn is_soft_nonzero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue { let mut prev = None; for a_coeff in &a.coeffs { let coeff = self.fp_chip.is_soft_nonzero(ctx, a_coeff); if let Some(p) = prev { - let new = self.fp_chip.range().gate().or(ctx, Existing(&coeff), Existing(&p)); + let new = self.fp_chip.range().gate().or(ctx, Existing(coeff), Existing(p)); prev = Some(new); } else { prev = Some(coeff); @@ -349,16 +331,12 @@ where prev.unwrap() } - fn is_zero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + fn is_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue { let mut prev = None; for a_coeff in &a.coeffs { let coeff = self.fp_chip.is_zero(ctx, a_coeff); if let Some(p) = prev { - let new = self.fp_chip.range().gate().and(ctx, Existing(&coeff), Existing(&p)); + let new = self.fp_chip.range().gate().and(ctx, Existing(coeff), Existing(p)); prev = Some(new); } else { prev = Some(coeff); @@ -367,17 +345,17 @@ where prev.unwrap() } - fn is_equal_unenforced<'v>( + fn is_equal_unenforced( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> AssignedValue { let mut acc = None; for (a_coeff, b_coeff) in a.coeffs.iter().zip(b.coeffs.iter()) { let coeff = self.fp_chip.is_equal_unenforced(ctx, a_coeff, b_coeff); if let Some(c) = acc { - acc = Some(self.fp_chip.range().gate().and(ctx, Existing(&coeff), Existing(&c))); + acc = Some(self.fp_chip.range().gate().and(ctx, Existing(coeff), Existing(c))); } else { acc = Some(coeff); } @@ -385,16 +363,29 @@ where acc.unwrap() } - fn assert_equal<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) { + fn assert_equal(&self, ctx: &mut Context, a: &Self::FieldPoint, b: &Self::FieldPoint) { for (a_coeff, b_coeff) in a.coeffs.iter().zip(b.coeffs.iter()) { self.fp_chip.assert_equal(ctx, a_coeff, b_coeff) } } + + fn divide( + &self, + _ctx: &mut Context, + _a: &Self::FieldPoint, + _b: &Self::FieldPoint, + ) -> Self::FieldPoint { + unimplemented!() + } + + fn neg_divide( + &self, + _ctx: &mut Context, + _a: &Self::FieldPoint, + _b: &Self::FieldPoint, + ) -> Self::FieldPoint { + unimplemented!() + } } mod bn254 { diff --git a/halo2-ecc/src/fields/mod.rs b/halo2-ecc/src/fields/mod.rs index e5e65f16..94a0b304 100644 --- a/halo2-ecc/src/fields/mod.rs +++ b/halo2-ecc/src/fields/mod.rs @@ -1,5 +1,5 @@ use crate::halo2_proofs::{arithmetic::Field, circuit::Value}; -use halo2_base::{gates::RangeInstructions, utils::PrimeField, AssignedValue, Context}; +use halo2_base::{gates::RangeInstructions, utils::BigPrimeField, AssignedValue, Context}; use num_bigint::BigUint; use std::fmt::Debug; @@ -10,6 +10,8 @@ pub mod fp2; #[cfg(test)] mod tests; +pub trait PrimeField = BigPrimeField; + #[derive(Clone, Debug)] pub struct FieldExtPoint { // `F_q` field extension of `F_p` where `q = p^degree` @@ -33,7 +35,7 @@ pub trait FieldChip { type ConstantType: Debug; type WitnessType: Debug; - type FieldPoint<'v>: Clone + Debug; + type FieldPoint: Clone + Debug; // a type implementing `Field` trait to help with witness generation (for example with inverse) type FieldType: Field; type RangeChip: RangeInstructions; @@ -45,155 +47,135 @@ pub trait FieldChip { fn range(&self) -> &Self::RangeChip; fn limb_bits(&self) -> usize; - fn get_assigned_value(&self, x: &Self::FieldPoint<'_>) -> Value; + fn get_assigned_value(&self, x: &Self::FieldPoint) -> Value; fn fe_to_constant(x: Self::FieldType) -> Self::ConstantType; fn fe_to_witness(x: &Value) -> Self::WitnessType; - fn load_private<'v>( - &self, - ctx: &mut Context<'_, F>, - coeffs: Self::WitnessType, - ) -> Self::FieldPoint<'v>; + fn load_private(&self, ctx: &mut Context, coeffs: Self::WitnessType) -> Self::FieldPoint; - fn load_constant<'v>( - &self, - ctx: &mut Context<'_, F>, - coeffs: Self::ConstantType, - ) -> Self::FieldPoint<'v>; + fn load_constant(&self, ctx: &mut Context, coeffs: Self::ConstantType) -> Self::FieldPoint; - fn add_no_carry<'v>( + fn add_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v>; + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint; /// output: `a + c` - fn add_constant_no_carry<'v>( + fn add_constant_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, c: Self::ConstantType, - ) -> Self::FieldPoint<'v>; + ) -> Self::FieldPoint; - fn sub_no_carry<'v>( + fn sub_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v>; + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint; - fn negate<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v>; + fn negate(&self, ctx: &mut Context, a: &Self::FieldPoint) -> Self::FieldPoint; /// a * c - fn scalar_mul_no_carry<'v>( + fn scalar_mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, c: i64, - ) -> Self::FieldPoint<'v>; + ) -> Self::FieldPoint; /// a * c + b - fn scalar_mul_and_add_no_carry<'v>( + fn scalar_mul_and_add_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, c: i64, - ) -> Self::FieldPoint<'v>; + ) -> Self::FieldPoint; - fn mul_no_carry<'v>( + fn mul_no_carry( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v>; + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint; - fn check_carry_mod_to_zero<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>); + fn check_carry_mod_to_zero(&self, ctx: &mut Context, a: &Self::FieldPoint); - fn carry_mod<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v>; + fn carry_mod(&self, ctx: &mut Context, a: &Self::FieldPoint) -> Self::FieldPoint; - fn range_check<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>, max_bits: usize); + fn range_check(&self, ctx: &mut Context, a: &Self::FieldPoint, max_bits: usize); - fn enforce_less_than<'v>(&self, ctx: &mut Context<'v, F>, a: &Self::FieldPoint<'v>); + fn enforce_less_than(&self, ctx: &mut Context, a: &Self::FieldPoint); // Assumes the witness for a is 0 // Constrains that the underlying big integer is 0 and < p. // For field extensions, checks coordinate-wise. - fn is_soft_zero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F>; + fn is_soft_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue; // Constrains that the underlying big integer is in [1, p - 1]. // For field extensions, checks coordinate-wise. - fn is_soft_nonzero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F>; + fn is_soft_nonzero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue; - fn is_zero<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F>; + fn is_zero(&self, ctx: &mut Context, a: &Self::FieldPoint) -> AssignedValue; // assuming `a, b` have been range checked to be a proper BigInt // constrain the witnesses `a, b` to be `< p` // then check `a == b` as BigInts - fn is_equal<'v>( + fn is_equal( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> AssignedValue { self.enforce_less_than(ctx, a); self.enforce_less_than(ctx, b); // a.native and b.native are derived from `a.truncation, b.truncation`, so no need to check if they're equal self.is_equal_unenforced(ctx, a, b) } - fn is_equal_unenforced<'v>( + fn is_equal_unenforced( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> AssignedValue<'v, F>; + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> AssignedValue; - fn assert_equal<'v>( - &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ); + fn assert_equal(&self, ctx: &mut Context, a: &Self::FieldPoint, b: &Self::FieldPoint); - fn mul<'v>( + fn mul( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { let no_carry = self.mul_no_carry(ctx, a, b); self.carry_mod(ctx, &no_carry) } - fn divide<'v>( + // constrains safe division. + // - if b==0 => undefined behavior + // - else => return quot * b - a = 0 mod p + fn divide( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint; + + // constrains unsafe division. + // - if b==0 => undefined behavior + // - else => return quot * b - a = 0 mod p + fn divide_unsafe( + &self, + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { let a_val = self.get_assigned_value(a); let b_val = self.get_assigned_value(b); let b_inv = b_val.map(|bv| bv.invert().unwrap()); @@ -211,12 +193,22 @@ pub trait FieldChip { // constrain and output -a / b // this is usually cheaper constraint-wise than computing -a and then (-a) / b separately - fn neg_divide<'v>( + fn neg_divide( + &self, + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint; + + // constrains unsafe division. + // constrain and output -a / b + // this is usually cheaper constraint-wise than computing -a and then (-a) / b separately + fn neg_divide_unsafe( &self, - ctx: &mut Context<'v, F>, - a: &Self::FieldPoint<'v>, - b: &Self::FieldPoint<'v>, - ) -> Self::FieldPoint<'v> { + ctx: &mut Context, + a: &Self::FieldPoint, + b: &Self::FieldPoint, + ) -> Self::FieldPoint { let a_val = self.get_assigned_value(a); let b_val = self.get_assigned_value(b); let b_inv = b_val.map(|bv| bv.invert().unwrap()); @@ -235,22 +227,22 @@ pub trait FieldChip { } pub trait Selectable { - type Point<'v>; + type Point; - fn select<'v>( + fn select( &self, - ctx: &mut Context<'_, F>, - a: &Self::Point<'v>, - b: &Self::Point<'v>, - sel: &AssignedValue<'v, F>, - ) -> Self::Point<'v>; + ctx: &mut Context, + a: &Self::Point, + b: &Self::Point, + sel: &AssignedValue, + ) -> Self::Point; - fn select_by_indicator<'v>( + fn select_by_indicator( &self, - ctx: &mut Context<'_, F>, - a: &[Self::Point<'v>], - coeffs: &[AssignedValue<'v, F>], - ) -> Self::Point<'v>; + ctx: &mut Context, + a: &[Self::Point], + coeffs: &[AssignedValue], + ) -> Self::Point; } // Common functionality for prime field chips diff --git a/halo2-ecc/src/fields/tests.rs b/halo2-ecc/src/fields/tests.rs index 36398e65..26c1f778 100644 --- a/halo2-ecc/src/fields/tests.rs +++ b/halo2-ecc/src/fields/tests.rs @@ -1,4 +1,5 @@ mod fp { + use crate::fields::PrimeField; use crate::fields::{ fp::{FpConfig, FpStrategy}, FieldChip, @@ -9,9 +10,9 @@ mod fp { halo2curves::bn256::{Fq, Fr}, plonk::*, }; - use group::ff::Field; + use ff::Field; use halo2_base::{ - utils::{fe_to_biguint, modulus, PrimeField}, + utils::{fe_to_biguint, modulus}, SKIP_FIRST_PASS, }; use num_bigint::BigInt; @@ -89,9 +90,7 @@ mod fp { #[cfg(feature = "display")] { - println!( - "Using {NUM_ADVICE} advice columns and {NUM_FIXED} fixed columns" - ); + println!("Using {NUM_ADVICE} advice columns and {NUM_FIXED} fixed columns"); println!("total cells: {}", ctx.total_advice); let (const_rows, _) = ctx.fixed_stats(); @@ -131,6 +130,7 @@ mod fp { } mod fp12 { + use crate::fields::PrimeField; use crate::fields::{ fp::{FpConfig, FpStrategy}, fp12::*, @@ -142,8 +142,9 @@ mod fp12 { halo2curves::bn256::{Fq, Fq12, Fr}, plonk::*, }; + use ff::Field; use halo2_base::utils::modulus; - use halo2_base::{utils::PrimeField, SKIP_FIRST_PASS}; + use halo2_base::SKIP_FIRST_PASS; use std::marker::PhantomData; #[derive(Default)] @@ -187,7 +188,7 @@ mod fp12 { mut layouter: impl Layouter, ) -> Result<(), Error> { config.load_lookup_table(&mut layouter)?; - let chip = Fp12Chip::, Fq12, XI_0>::construct(&config); + let chip = Fp12Chip::, Fq12, XI_0>::construct(config.clone()); let mut first_pass = SKIP_FIRST_PASS; @@ -222,9 +223,7 @@ mod fp12 { #[cfg(feature = "display")] { - println!( - "Using {NUM_ADVICE} advice columns and {NUM_FIXED} fixed columns" - ); + println!("Using {NUM_ADVICE} advice columns and {NUM_FIXED} fixed columns"); println!("total advice cells: {}", ctx.total_advice); let (const_rows, _) = ctx.fixed_stats(); diff --git a/halo2-ecc/src/lib.rs b/halo2-ecc/src/lib.rs index ddf2763d..db01f828 100644 --- a/halo2-ecc/src/lib.rs +++ b/halo2-ecc/src/lib.rs @@ -1,7 +1,10 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::op_ref)] #![allow(clippy::type_complexity)] +#![allow(unused_imports)] +#![allow(stable_features)] #![feature(int_log)] +#![feature(trait_alias)] pub mod bigint; pub mod ecc; diff --git a/halo2-ecc/src/secp256k1/tests/ecdsa.rs b/halo2-ecc/src/secp256k1/tests/ecdsa.rs index 73389d79..ad9a869f 100644 --- a/halo2-ecc/src/secp256k1/tests/ecdsa.rs +++ b/halo2-ecc/src/secp256k1/tests/ecdsa.rs @@ -1,6 +1,8 @@ #![allow(non_snake_case)] +use crate::fields::PrimeField; use ark_std::{end_timer, start_timer}; -use halo2_base::{utils::PrimeField, SKIP_FIRST_PASS}; +use ff::Field; +use halo2_base::SKIP_FIRST_PASS; use serde::{Deserialize, Serialize}; use std::fs::File; use std::marker::PhantomData; diff --git a/hashes/zkevm-keccak/src/keccak_packed_multi.rs b/hashes/zkevm-keccak/src/keccak_packed_multi.rs index 085ff9c6..50af5b62 100644 --- a/hashes/zkevm-keccak/src/keccak_packed_multi.rs +++ b/hashes/zkevm-keccak/src/keccak_packed_multi.rs @@ -114,7 +114,7 @@ impl KeccakRow { q_round_last: false, q_padding: false, q_padding_last: false, - round_cst: F::zero(), + round_cst: F::ZERO, is_final: false, cell_values: Vec::new(), }) @@ -154,7 +154,7 @@ impl KeccakRegion { } let row = &mut self.rows[offset]; while column >= row.len() { - row.push(F::zero()); + row.push(F::ZERO); } row[column] = value; } @@ -387,7 +387,7 @@ pub fn assign_advice_custom<'v, F: Field>( column: Column, offset: usize, value: Value, -) -> AssignedValue<'v, F> { +) -> AssignedValue { #[cfg(feature = "halo2-axiom")] { AssignedValue { @@ -448,7 +448,7 @@ mod decode { } pub(crate) fn value(parts: Vec>) -> F { - parts.iter().rev().fold(F::zero(), |acc, part| { + parts.iter().rev().fold(F::ZERO, |acc, part| { acc * F::from(1u64 << (BIT_COUNT * part.num_bits)) + part.value }) } @@ -1604,14 +1604,14 @@ pub fn keccak_phase1<'v, F: Field>( keccak_table: &KeccakTable, bytes: &[u8], challenge: Value, - input_rlcs: &mut Vec>, + input_rlcs: &mut Vec>, offset: &mut usize, ) { let num_chunks = get_num_keccak_f(bytes.len()); let num_rows_per_round = get_num_rows_per_round(); let mut byte_idx = 0; - let mut data_rlc = Value::known(F::zero()); + let mut data_rlc = Value::known(F::ZERO); for _ in 0..num_chunks { for round in 0..NUM_ROUNDS + 1 { @@ -1648,7 +1648,7 @@ pub fn keccak_phase0( bytes: &[u8], ) { let mut bits = into_bits(bytes); - let mut s = [[F::zero(); 5]; 5]; + let mut s = [[F::ZERO; 5]; 5]; let absorb_positions = get_absorb_positions(); let num_bytes_in_last_block = bytes.len() % RATE; let num_rows_per_round = get_num_rows_per_round(); @@ -1666,7 +1666,7 @@ pub fn keccak_phase0( let mut cell_managers = Vec::with_capacity(NUM_ROUNDS + 1); let mut regions = Vec::with_capacity(NUM_ROUNDS + 1); - let mut hash_words = [F::zero(); NUM_WORDS_TO_SQUEEZE]; + let mut hash_words = [F::ZERO; NUM_WORDS_TO_SQUEEZE]; for (idx, chunk) in chunks.enumerate() { let is_final_block = idx == num_chunks - 1; @@ -1771,7 +1771,7 @@ pub fn keccak_phase0( bc.push(bc_norm); } cell_manager.start_region(); - let mut os = [[F::zero(); 5]; 5]; + let mut os = [[F::ZERO; 5]; 5]; for i in 0..5 { let t = decode::value(bc[(i + 4) % 5].clone()) + decode::value(rotate(bc[(i + 1) % 5].clone(), 1, part_size)); @@ -1834,7 +1834,7 @@ pub fn keccak_phase0( // Chi let part_size_base = get_num_bits_per_base_chi_lookup(); let three_packed = pack::(&vec![3u8; part_size_base]); - let mut os = [[F::zero(); 5]; 5]; + let mut os = [[F::ZERO; 5]; 5]; for j in 0..5 { for i in 0..5 { let mut s_parts = Vec::new(); @@ -1967,14 +1967,14 @@ pub fn multi_keccak_phase1<'a, 'v, F: Field>( bytes: impl IntoIterator, challenge: Value, squeeze_digests: Vec<[F; NUM_WORDS_TO_SQUEEZE]>, -) -> (Vec>, Vec>) { +) -> (Vec>, Vec>) { let mut input_rlcs = Vec::with_capacity(squeeze_digests.len()); let mut output_rlcs = Vec::with_capacity(squeeze_digests.len()); let num_rows_per_round = get_num_rows_per_round(); for idx in 0..num_rows_per_round { [keccak_table.input_rlc, keccak_table.output_rlc] - .map(|column| assign_advice_custom(region, column, idx, Value::known(F::zero()))); + .map(|column| assign_advice_custom(region, column, idx, Value::known(F::ZERO))); } let mut offset = num_rows_per_round; diff --git a/hashes/zkevm-keccak/src/util.rs b/hashes/zkevm-keccak/src/util.rs index 868c366c..472142a3 100644 --- a/hashes/zkevm-keccak/src/util.rs +++ b/hashes/zkevm-keccak/src/util.rs @@ -169,7 +169,7 @@ pub fn pack(bits: &[u8]) -> F { /// specified bit base pub fn pack_with_base(bits: &[u8], base: usize) -> F { let base = F::from(base as u64); - bits.iter().rev().fold(F::zero(), |acc, &bit| acc * base + F::from(bit as u64)) + bits.iter().rev().fold(F::ZERO, |acc, &bit| acc * base + F::from(bit as u64)) } /// Decodes the bits using the position data found in the part info diff --git a/hashes/zkevm-keccak/src/util/expression.rs b/hashes/zkevm-keccak/src/util/expression.rs index fa0ee216..d254ed0a 100644 --- a/hashes/zkevm-keccak/src/util/expression.rs +++ b/hashes/zkevm-keccak/src/util/expression.rs @@ -11,7 +11,7 @@ pub mod sum { /// Returns the sum of the given list of values within the field. pub fn value(values: &[u8]) -> F { - values.iter().fold(F::zero(), |acc, value| acc + F::from(*value as u64)) + values.iter().fold(F::ZERO, |acc, value| acc + F::from(*value as u64)) } } @@ -28,7 +28,7 @@ pub mod and { /// Returns the product of all given values. pub fn value(inputs: Vec) -> F { - inputs.iter().fold(F::one(), |acc, input| acc * input) + inputs.iter().fold(F::ONE, |acc, input| acc * input) } } @@ -62,7 +62,7 @@ pub mod not { /// Returns a value that represents the NOT of the given value. pub fn value(b: F) -> F { - F::one() - b + F::ONE - b } } @@ -100,7 +100,7 @@ pub mod select { /// Returns the `when_true` value when the selector is true, else returns /// the `when_false` value. pub fn value(selector: F, when_true: F, when_false: F) -> F { - selector * when_true + (F::one() - selector) * when_false + selector * when_true + (F::ONE - selector) * when_false } /// Returns the `when_true` word when selector is true, else returns the @@ -110,7 +110,7 @@ pub mod select { when_true: [u8; 32], when_false: [u8; 32], ) -> [u8; 32] { - if selector == F::one() { + if selector == F::ONE { when_true } else { when_false @@ -170,7 +170,7 @@ impl Expr for i32 { fn expr(&self) -> Expression { Expression::Constant( F::from(self.unsigned_abs() as u64) - * if self.is_negative() { -F::one() } else { F::one() }, + * if self.is_negative() { -F::ONE } else { F::ONE }, ) } } @@ -179,7 +179,7 @@ impl Expr for i32 { /// single expression. pub fn expr_from_bytes>(bytes: &[E]) -> Expression { let mut value = 0.expr(); - let mut multiplier = F::one(); + let mut multiplier = F::ONE; for byte in bytes.iter() { value = value + byte.expr() * multiplier; multiplier *= F::from(256); diff --git a/rust-toolchain b/rust-toolchain index 51ab4759..27c108be 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2022-10-28 \ No newline at end of file +nightly-2023-12-03