diff --git a/.gitignore b/.gitignore index 31a5ddfa..21ad0a9e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ /layouts .vscode .cargo -Cargo.lock -*.png \ No newline at end of file +*.png diff --git a/Cargo.lock.current b/Cargo.lock similarity index 90% rename from Cargo.lock.current rename to Cargo.lock index cf31c6e0..e53bb878 100644 --- a/Cargo.lock.current +++ b/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -73,13 +73,13 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.38", ] [[package]] @@ -88,7 +88,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -125,9 +125,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -155,9 +155,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bitvec" @@ -213,15 +213,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.10.7", + "sha2 0.10.8", "tinyvec", ] [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-slice-cast" @@ -237,9 +237,9 @@ checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -273,9 +273,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", @@ -316,7 +316,7 @@ dependencies = [ "hmac 0.12.1", "k256", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "thiserror", ] @@ -332,7 +332,7 @@ dependencies = [ "once_cell", "pbkdf2 0.12.2", "rand", - "sha2 0.10.7", + "sha2 0.10.8", "thiserror", ] @@ -342,7 +342,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bech32", "bs58", "digest 0.10.7", @@ -351,7 +351,7 @@ dependencies = [ "ripemd", "serde", "serde_derive", - "sha2 0.10.7", + "sha2 0.10.8", "sha3 0.10.8", "thiserror", ] @@ -434,9 +434,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" dependencies = [ "libc", ] @@ -765,9 +765,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ "base16ct", "crypto-bigint", @@ -803,25 +803,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] -[[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 = "eth-keystore" version = "0.5.0" @@ -838,7 +827,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", "sha3 0.10.8", "thiserror", "uuid", @@ -847,7 +836,7 @@ dependencies = [ [[package]] name = "eth-types" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git#0afa0d9e148b914eda1d1a701547f62ee1844b24" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?rev=7d9bc181953cfc6e7baf82ff0ce651281fd70a8a#7d9bc181953cfc6e7baf82ff0ce651281fd70a8a" dependencies = [ "ethers-core", "ethers-signers", @@ -858,6 +847,7 @@ dependencies = [ "libsecp256k1", "num", "num-bigint", + "once_cell", "poseidon-circuit", "regex", "serde", @@ -958,16 +948,16 @@ dependencies = [ "ethers-core", "hex", "rand", - "sha2 0.10.7", + "sha2 0.10.8", "thiserror", "tracing", ] [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fdeflate" @@ -1013,9 +1003,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -1181,6 +1171,7 @@ dependencies = [ "poseidon-circuit", "rand", "rand_chacha", + "rayon", "serde", "serde_json", "strum", @@ -1192,7 +1183,7 @@ dependencies = [ [[package]] name = "halo2-mpt-circuits" version = "0.1.0" -source = "git+https://github.com/scroll-tech/mpt-circuit.git?tag=v0.6.3#a42263eeb38f48b3008abea95993423604497c6a" +source = "git+https://github.com/scroll-tech/mpt-circuit.git?tag=v0.7.0#578c210ceb88d3c143ee2a013ad836d19285d9c1" dependencies = [ "ethers-core", "halo2_proofs", @@ -1214,7 +1205,7 @@ dependencies = [ [[package]] name = "halo2_proofs" version = "0.2.0" -source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#aa86c107aeb62282d81ebce5c4930ec0c0aa540b" +source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#92fe9b3e2420ee51336b39513e397df7c8175cf9" dependencies = [ "ark-std", "blake2b_simd", @@ -1275,9 +1266,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -1294,12 +1285,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - [[package]] name = "hex" version = "0.4.3" @@ -1344,16 +1329,16 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1426,9 +1411,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", "hashbrown", @@ -1483,7 +1468,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.7", + "sha2 0.10.8", "signature", ] @@ -1504,15 +1489,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if 1.0.0", "windows-sys", @@ -1568,9 +1553,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" @@ -1580,9 +1565,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -1606,10 +1591,10 @@ dependencies = [ [[package]] name = "mpt-zktrie" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git#0afa0d9e148b914eda1d1a701547f62ee1844b24" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?rev=7d9bc181953cfc6e7baf82ff0ce651281fd70a8a#7d9bc181953cfc6e7baf82ff0ce651281fd70a8a" dependencies = [ "eth-types", - "halo2-mpt-circuits 0.1.0 (git+https://github.com/scroll-tech/mpt-circuit.git?tag=v0.6.3)", + "halo2-mpt-circuits 0.1.0 (git+https://github.com/scroll-tech/mpt-circuit.git?tag=v0.7.0)", "halo2_proofs", "hex", "lazy_static", @@ -1689,23 +1674,13 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - [[package]] name = "num_enum" version = "0.6.1" @@ -1724,7 +1699,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.38", ] [[package]] @@ -1823,9 +1798,9 @@ dependencies = [ [[package]] name = "pathfinder_simd" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" +checksum = "0444332826c70dc47be74a7c6a5fc44e23a7905ad6858d4162b658320455ef93" dependencies = [ "rustc_version", ] @@ -1849,17 +1824,6 @@ dependencies = [ "hmac 0.12.1", ] -[[package]] -name = "pest" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1973,9 +1937,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primitive-types" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", @@ -2021,9 +1985,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -2084,9 +2048,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -2094,14 +2058,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -2135,9 +2097,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -2147,9 +2109,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -2158,9 +2120,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rfc6979" @@ -2211,20 +2173,20 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.13" +version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -2263,9 +2225,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "cfg-if 1.0.0", "derive_more", @@ -2275,9 +2237,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2300,7 +2262,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -2319,47 +2281,35 @@ dependencies = [ [[package]] name = "semver" -version = "0.11.0" +version = "1.0.20" 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", -] +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2403,9 +2353,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -2519,9 +2469,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -2560,31 +2510,31 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.38", ] [[package]] @@ -2613,9 +2563,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" [[package]] name = "toml_edit" @@ -2630,11 +2580,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if 1.0.0", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2642,20 +2591,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -2668,15 +2617,9 @@ checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" [[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.6" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "uint" @@ -2692,9 +2635,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-xid" @@ -2755,7 +2698,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -2777,7 +2720,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2822,9 +2765,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -2836,10 +2779,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets", ] @@ -2912,9 +2855,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" dependencies = [ "memchr", ] @@ -2958,7 +2901,7 @@ checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zktrie" version = "0.2.0" -source = "git+https://github.com/scroll-tech/zktrie.git?branch=v0.6#83318659773604fa565e2ebeb810a6d3746f0af4" +source = "git+https://github.com/scroll-tech/zktrie.git?branch=v0.7#a130ea543d291d4b71724f91cb8a49745c593a0c" dependencies = [ "gobuild", ] diff --git a/Cargo.toml b/Cargo.toml index 65f5f3e1..f07ec310 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ num-bigint = "0.4" hex = "0.4" thiserror = "1.0" log = "0.4" +rayon = "1" [patch."https://github.com/privacy-scaling-explorations/halo2.git"] halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" } @@ -32,7 +33,7 @@ ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = " print_layout = ["halo2_proofs/dev-graph"] [dev-dependencies] -mpt-zktrie = { git = "https://github.com/scroll-tech/zkevm-circuits.git" } +mpt-zktrie = { git = "https://github.com/scroll-tech/zkevm-circuits.git", rev = "7d9bc181953cfc6e7baf82ff0ce651281fd70a8a" } # mpt-zktrie = { path = "../scroll-circuits/zktrie" } rand_chacha = "0.3.0" plotters = "0.3" diff --git a/Makefile b/Makefile index beb636f4..cf38b795 100644 --- a/Makefile +++ b/Makefile @@ -5,4 +5,4 @@ fmt: @cargo fmt clippy: - @cargo clippy --all-features + @cargo clippy --all-features -- -D warnings diff --git a/src/assignment_map.rs b/src/assignment_map.rs new file mode 100644 index 00000000..9723af67 --- /dev/null +++ b/src/assignment_map.rs @@ -0,0 +1,94 @@ +use crate::constraint_builder::{ + AdviceColumn, FixedColumn, SecondPhaseAdviceColumn, SelectorColumn, +}; +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Region, Value}, + plonk::Error, +}; +use itertools::Itertools; +use rayon::prelude::*; +use std::collections::BTreeMap; + +#[derive(Clone, Default)] +pub struct AssignmentMap(BTreeMap)>>); + +impl AssignmentMap { + pub fn new(stream: impl ParallelIterator)>) -> Self { + let mut sorted_by_offset: Vec<_> = stream + .map(|((column, offset), value)| (offset, column, value)) + .collect(); + sorted_by_offset.sort_by(|x, y| x.0.cmp(&y.0)); + let grouped_by_offset = sorted_by_offset.iter().group_by(|(offset, _, _)| offset); + let y: BTreeMap<_, _> = grouped_by_offset + .into_iter() + .map(|(offset, group)| { + ( + *offset, + group + .map(|(_offset, column, value)| (*column, *value)) + .collect(), + ) + }) + .collect(); + Self(y) + } + + pub fn into_vec(self) -> Vec) -> Result<(), Error>> { + self.0 + .into_values() + .map(|column_assignments| { + move |mut region: Region<'_, F>| { + for (column, value) in column_assignments.iter() { + match *column { + Column::Selector(s) => { + region.assign_fixed(|| "selector", s.0, 0, || *value) + } + Column::Fixed(s) => region.assign_fixed(|| "fixed", s.0, 0, || *value), + Column::Advice(s) => { + region.assign_advice(|| "advice", s.0, 0, || *value) + } + Column::SecondPhaseAdvice(s) => { + region.assign_advice(|| "second phase advice", s.0, 0, || *value) + } + } + .unwrap(); + } + Ok(()) + } + }) + .collect() + } +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub enum Column { + Selector(SelectorColumn), + Fixed(FixedColumn), + Advice(AdviceColumn), + SecondPhaseAdvice(SecondPhaseAdviceColumn), +} + +impl From for Column { + fn from(c: SelectorColumn) -> Self { + Self::Selector(c) + } +} + +impl From for Column { + fn from(c: FixedColumn) -> Self { + Self::Fixed(c) + } +} + +impl From for Column { + fn from(c: AdviceColumn) -> Self { + Self::Advice(c) + } +} + +impl From for Column { + fn from(c: SecondPhaseAdviceColumn) -> Self { + Self::SecondPhaseAdvice(c) + } +} diff --git a/src/constraint_builder/binary_column.rs b/src/constraint_builder/binary_column.rs index c414850f..44056b9b 100644 --- a/src/constraint_builder/binary_column.rs +++ b/src/constraint_builder/binary_column.rs @@ -1,4 +1,5 @@ -use super::{BinaryQuery, ConstraintBuilder, Query}; +use super::{AdviceColumn, BinaryQuery, ConstraintBuilder, Query}; +use crate::assignment_map::Column as ColumnEnum; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, @@ -43,4 +44,15 @@ impl BinaryColumn { .assign_advice(|| "binary", self.0, offset, || Value::known(F::from(value))) .expect("failed assign_advice"); } + + pub fn assignment( + &self, + offset: usize, + value: bool, + ) -> ((ColumnEnum, usize), Value) { + ( + (ColumnEnum::Advice(AdviceColumn(self.0)), offset), + Value::known(if value { F::one() } else { F::zero() }), + ) + } } diff --git a/src/constraint_builder/column.rs b/src/constraint_builder/column.rs index b3dbfa03..f59eafae 100644 --- a/src/constraint_builder/column.rs +++ b/src/constraint_builder/column.rs @@ -1,4 +1,5 @@ use super::{BinaryQuery, Query}; +use crate::assignment_map::Column as ColumnEnum; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, @@ -6,7 +7,7 @@ use halo2_proofs::{ }; use std::fmt::Debug; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct SelectorColumn(pub Column); impl SelectorColumn { @@ -23,9 +24,20 @@ impl SelectorColumn { .assign_fixed(|| "selector", self.0, offset, || Value::known(F::one())) .expect("failed enable selector"); } + + pub fn assignment( + &self, + offset: usize, + enable: bool, + ) -> ((ColumnEnum, usize), Value) { + ( + (ColumnEnum::from(*self), offset), + Value::known(if enable { F::one() } else { F::zero() }), + ) + } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct FixedColumn(pub Column); impl FixedColumn { @@ -51,16 +63,30 @@ impl FixedColumn { { region .assign_fixed( - || "asdfasdfawe", + || "fixed", self.0, offset, || Value::known(value.try_into().unwrap()), ) .expect("failed assign_fixed"); } + + pub fn assignment>( + &self, + offset: usize, + value: T, + ) -> ((ColumnEnum, usize), Value) + where + >::Error: Debug, + { + ( + (ColumnEnum::from(*self), offset), + Value::known(value.try_into().unwrap()), + ) + } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct AdviceColumn(pub Column); impl AdviceColumn { @@ -101,9 +127,23 @@ impl AdviceColumn { ) .expect("failed assign_advice"); } + + pub fn assignment>( + &self, + offset: usize, + value: T, + ) -> ((ColumnEnum, usize), Value) + where + >::Error: Debug, + { + ( + (ColumnEnum::from(*self), offset), + Value::known(value.try_into().unwrap()), + ) + } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct SecondPhaseAdviceColumn(pub Column); impl SecondPhaseAdviceColumn { @@ -124,4 +164,12 @@ impl SecondPhaseAdviceColumn { .assign_advice(|| "second phase advice", self.0, offset, || value) .expect("failed assign_advice"); } + + pub fn assignment( + &self, + offset: usize, + value: Value, + ) -> ((ColumnEnum, usize), Value) { + ((ColumnEnum::from(*self), offset), value) + } } diff --git a/src/gadgets/byte_bit.rs b/src/gadgets/byte_bit.rs index 60474732..5537531a 100644 --- a/src/gadgets/byte_bit.rs +++ b/src/gadgets/byte_bit.rs @@ -1,5 +1,11 @@ use super::super::constraint_builder::{ConstraintBuilder, FixedColumn, Query}; -use halo2_proofs::{arithmetic::FieldExt, circuit::Region, plonk::ConstraintSystem}; +use crate::assignment_map::Column; +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Region, Value}, + plonk::ConstraintSystem, +}; +use rayon::prelude::*; // TODO: fix name to configggggggg #[derive(Clone)] @@ -31,16 +37,37 @@ impl ByteBitGadget { } pub fn assign(&self, region: &mut Region<'_, F>) { - let mut offset = 0; - for byte in 0..256 { - for index in 0..8 { - self.byte.assign(region, offset, byte); - self.index.assign(region, offset, index); - self.bit.assign(region, offset, byte & (1 << index) != 0); - offset += 1; + let assignments: Vec<_> = self.assignments::().collect(); + for ((column, offset), value) in assignments.into_iter() { + match column { + Column::Fixed(s) => region.assign_fixed(|| "fixed", s.0, offset, || value), + _ => unreachable!(), } + .unwrap(); } } + + pub fn assignments( + &self, + ) -> impl ParallelIterator)> + '_ { + (0..256u64).into_par_iter().flat_map(move |byte| { + let starting_offset = byte * 8; + (0..8u64).into_par_iter().flat_map_iter(move |index| { + let offset = usize::try_from(1 + starting_offset + index).unwrap(); + [ + self.byte.assignment(offset, byte), + self.index.assignment(offset, index), + self.bit.assignment(offset, byte & (1 << index) != 0), + ] + .into_iter() + }) + }) + } + + pub fn n_rows_required() -> usize { + // +1 because assigment starts on offset = 1 instead of offset = 0. + 256 * 8 + 1 + } } impl RangeCheck8Lookup for ByteBitGadget { diff --git a/src/gadgets/byte_representation.rs b/src/gadgets/byte_representation.rs index 4ce4a331..53f6ce11 100644 --- a/src/gadgets/byte_representation.rs +++ b/src/gadgets/byte_representation.rs @@ -1,14 +1,18 @@ use super::{byte_bit::RangeCheck256Lookup, is_zero::IsZeroGadget, rlc_randomness::RlcRandomness}; -use crate::constraint_builder::{ - AdviceColumn, ConstraintBuilder, Query, SecondPhaseAdviceColumn, SelectorColumn, +use crate::{ + assignment_map::Column, + constraint_builder::{ + AdviceColumn, ConstraintBuilder, Query, SecondPhaseAdviceColumn, SelectorColumn, + }, }; -use ethers_core::types::{Address, H256}; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, halo2curves::bn256::Fr, plonk::ConstraintSystem, }; +use rayon::prelude::*; +use std::iter::repeat; pub trait RlcLookup { fn lookup(&self) -> [Query; 3]; @@ -93,45 +97,87 @@ impl ByteRepresentationConfig { } } - // can this we done with an Iterator instead? pub fn assign( &self, region: &mut Region<'_, F>, - u32s: &[u32], - u64s: &[u64], - u128s: &[u128], - frs: &[Fr], + u32s: Vec, + u64s: Vec, + u128s: Vec, + frs: Vec, randomness: Value, ) { - self.is_first.enable(region, 0); - let byte_representations = u32s - .iter() - .map(u32_to_big_endian) - .chain(u64s.iter().map(u64_to_big_endian)) - .chain(u128s.iter().map(u128_to_big_endian)) - .chain(frs.iter().map(fr_to_big_endian)); - - let mut offset = 0; - for byte_representation in byte_representations { - let mut value = F::zero(); - let mut rlc = Value::known(F::zero()); - for (index, byte) in byte_representation.iter().enumerate() { - let byte = F::from(u64::from(*byte)); - self.byte.assign(region, offset, byte); - - value = value * F::from(256) + byte; - self.value.assign(region, offset, value); + let assignments: Vec<_> = self + .assignments::(u32s, u64s, u128s, frs, randomness) + .collect(); + for ((column, offset), value) in assignments.into_iter() { + match column { + Column::Selector(s) => region.assign_fixed(|| "fixed", s.0, offset, || value), + Column::Advice(s) => region.assign_advice(|| "advice", s.0, offset, || value), + Column::SecondPhaseAdvice(s) => { + region.assign_advice(|| "second phase advice", s.0, offset, || value) + } + _ => unreachable!(), + } + .unwrap(); + } + } - rlc = rlc * randomness + Value::known(byte); - self.rlc.assign(region, offset, rlc); + pub fn assignments( + &self, + u32s: Vec, + u64s: Vec, + u128s: Vec, + frs: Vec, + randomness: Value, + ) -> impl ParallelIterator)> + '_ { + let starting_offsets: Vec<_> = repeat(4) + .take(u32s.len()) + .chain(repeat(8).take(u64s.len())) + .chain(repeat(16).take(u128s.len())) + .chain(repeat(31).take(frs.len())) + .scan(1, |cumulative_sum, n_rows| { + let result = Some(*cumulative_sum); + *cumulative_sum += n_rows; + result + }) + .collect(); + u32s.into_par_iter() + .map(|x| u32_to_big_endian(&x)) + .chain(u64s.into_par_iter().map(|x| u64_to_big_endian(&x))) + .chain(u128s.into_par_iter().map(|x| u128_to_big_endian(&x))) + .chain(frs.into_par_iter().map(|x| fr_to_big_endian(&x))) + .zip_eq(starting_offsets.into_par_iter()) + .enumerate() + .flat_map_iter(move |(i, (bytes, starting_offset))| { + let mut assignments = vec![]; + if i == 0 { + assignments.push(self.is_first.assignment(starting_offset, true)); + } + let mut value = F::zero(); + let mut rlc = Value::known(F::zero()); + for (index, byte) in bytes.iter().enumerate() { + let byte = F::from(u64::from(*byte)); + value = value * F::from(256) + byte; + rlc = rlc * randomness + Value::known(byte); - let index = u64::try_from(index).unwrap(); - self.index.assign(region, offset, index); - self.index_is_zero.assign(region, offset, index); + let offset = starting_offset + index; + assignments.extend(vec![ + self.byte.assignment(offset, byte), + self.value.assignment(offset, value), + self.rlc.assignment(offset, rlc), + ]); + assignments.extend( + self.index_is_zero + .assignments(offset, u64::try_from(index).unwrap()), + ); + } + assignments.into_iter() + }) + } - offset += 1; - } - } + pub fn n_rows_required(u32s: &[u32], u64s: &[u64], u128s: &[u128], frs: &[Fr]) -> usize { + // +1 because assigment starts on offset = 1 instead of offset = 0. + 1 + u32s.len() * 4 + u64s.len() * 8 + u128s.len() * 16 + frs.len() * 31 } } @@ -146,14 +192,6 @@ fn u128_to_big_endian(x: &u128) -> Vec { x.to_be_bytes().to_vec() } -fn address_to_big_endian(x: &Address) -> Vec { - x.0.to_vec() -} - -fn h256_to_big_endian(x: &H256) -> Vec { - x.0.to_vec() -} - fn fr_to_big_endian(x: &Fr) -> Vec { let mut bytes = x.to_bytes(); bytes.reverse(); @@ -225,10 +263,10 @@ mod test { byte_bit.assign(&mut region); byte_representation.assign( &mut region, - &self.u32s, - &self.u64s, - &self.u128s, - &self.frs, + self.u32s.clone(), + self.u64s.clone(), + self.u128s.clone(), + self.frs.clone(), randomness, ); Ok(()) diff --git a/src/gadgets/canonical_representation.rs b/src/gadgets/canonical_representation.rs index 7e2ae9b5..6829c0fb 100644 --- a/src/gadgets/canonical_representation.rs +++ b/src/gadgets/canonical_representation.rs @@ -3,6 +3,7 @@ use super::super::constraint_builder::{ SelectorColumn, }; use super::{byte_bit::RangeCheck256Lookup, is_zero::IsZeroGadget, rlc_randomness::RlcRandomness}; +use crate::assignment_map::Column; use ethers_core::types::U256; use halo2_proofs::{ arithmetic::{Field, FieldExt}, @@ -12,6 +13,7 @@ use halo2_proofs::{ }; use itertools::Itertools; use num_traits::Zero; +use rayon::prelude::*; pub trait CanonicalRepresentationLookup { fn lookup(&self) -> [Query; 3]; @@ -130,55 +132,112 @@ impl CanonicalRepresentationConfig { } } - pub fn assign<'a>( + pub fn assign( &self, region: &mut Region<'_, Fr>, randomness: Value, - values: impl IntoIterator, + values: Vec, + n_rows: usize, ) { + let assignments: Vec<_> = self.assignments(values, n_rows, randomness).collect(); + for ((column, offset), value) in assignments.into_iter() { + match column { + Column::Selector(s) => region.assign_fixed(|| "selector", s.0, offset, || value), + Column::Fixed(s) => region.assign_fixed(|| "fixed", s.0, offset, || value), + Column::Advice(s) => region.assign_advice(|| "advice", s.0, offset, || value), + Column::SecondPhaseAdvice(s) => { + region.assign_advice(|| "second phase advice", s.0, offset, || value) + } + } + .unwrap(); + } + } + + pub fn assignments( + &self, + values: Vec, + n_rows: usize, + randomness: Value, + ) -> impl ParallelIterator)> + '_ { let modulus = U256::from_str_radix(Fr::MODULUS, 16).unwrap(); let mut modulus_bytes = [0u8; 32]; modulus.to_big_endian(&mut modulus_bytes); - let mut offset = 0; - // TODO: we add a final Fr::zero() to handle the always enabled selector. Add a default assignment instead? - for value in values.into_iter().copied().chain([Fr::zero()]) { - let mut bytes = value.to_bytes(); - bytes.reverse(); - let mut differences_are_zero_so_far = true; - let mut rlc = Value::known(Fr::zero()); - for (index, (byte, modulus_byte)) in bytes.iter().zip_eq(&modulus_bytes).enumerate() { - self.byte.assign(region, offset, u64::from(*byte)); - self.modulus_byte - .assign(region, offset, u64::from(*modulus_byte)); + let n_values = values.len(); + values + .into_par_iter() + .enumerate() + .flat_map_iter(move |(i, value)| { + let mut assignments = vec![]; + let mut offset = 1 + 32 * i; - self.index - .assign(region, offset, u64::try_from(index).unwrap()); - if index.is_zero() { - self.index_is_zero.enable(region, offset); - } else if index == 31 { - self.index_is_31.enable(region, offset); - } + let mut bytes = value.to_bytes(); + bytes.reverse(); + let mut differences_are_zero_so_far = true; + let mut rlc = Value::known(Fr::zero()); + for (index, (byte, modulus_byte)) in bytes.iter().zip_eq(&modulus_bytes).enumerate() + { + let difference = + Fr::from(u64::from(*modulus_byte)) - Fr::from(u64::from(*byte)); + rlc = rlc * randomness + Value::known(Fr::from(u64::from(*byte))); - let difference = Fr::from(u64::from(*modulus_byte)) - Fr::from(u64::from(*byte)); - self.difference.assign(region, offset, difference); - self.difference_is_zero.assign(region, offset, difference); + assignments.extend([ + self.byte.assignment(offset, u64::from(*byte)), + self.modulus_byte + .assignment(offset, u64::from(*modulus_byte)), + self.index.assignment(offset, u64::try_from(index).unwrap()), + self.differences_are_zero_so_far + .assignment(offset, differences_are_zero_so_far), + self.value.assignment(offset, value), + self.rlc.assignment(offset, rlc), + ]); + assignments.extend(self.difference_is_zero.assignments(offset, difference)); + if index.is_zero() { + assignments.push(self.index_is_zero.assignment(offset, true)); + } else if index == 31 { + assignments.push(self.index_is_31.assignment(offset, true)); + } - self.differences_are_zero_so_far.assign( - region, - offset, - differences_are_zero_so_far, - ); - differences_are_zero_so_far &= difference.is_zero_vartime(); + differences_are_zero_so_far &= difference.is_zero_vartime(); + offset += 1 + } - self.value.assign(region, offset, value); + assignments.into_iter() + }) + .chain( + (n_values..n_rows / 32) + .into_par_iter() + .flat_map_iter(move |i| { + let mut assignments = vec![]; + for (index, modulus_byte) in modulus_bytes.iter().enumerate() { + let offset = 1 + 32 * i + index; + assignments.extend([ + self.modulus_byte + .assignment(offset, u64::from(*modulus_byte)), + self.index.assignment(offset, u64::try_from(index).unwrap()), + ]); + assignments.extend( + self.difference_is_zero + .assignments(offset, u64::from(*modulus_byte)), + ); - rlc = rlc * randomness + Value::known(Fr::from(u64::from(*byte))); - self.rlc.assign(region, offset, rlc); + if index.is_zero() { + assignments.extend([ + self.index_is_zero.assignment(offset, true), + self.differences_are_zero_so_far.assignment(offset, true), + ]); + } else if index == 31 { + assignments.push(self.index_is_31.assignment(offset, true)); + } + } + assignments.into_iter() + }), + ) + } - offset += 1 - } - } + pub fn n_rows_required(values: &[Fr]) -> usize { + // +1 because assigment starts on offset = 1 instead of offset = 0. + values.len() * 32 + 1 } } @@ -250,11 +309,16 @@ mod test { layouter.assign_region( || "", |mut region| { - for offset in 0..(8 * 256) { + for offset in 1..(1 + 8 * 256) { selector.enable(&mut region, offset); } byte_bit.assign(&mut region); - canonical_representation.assign(&mut region, randomness, &self.values); + canonical_representation.assign( + &mut region, + randomness, + self.values.clone(), + 256, + ); Ok(()) }, ) diff --git a/src/gadgets/is_zero.rs b/src/gadgets/is_zero.rs index 9fe7163d..88efc926 100644 --- a/src/gadgets/is_zero.rs +++ b/src/gadgets/is_zero.rs @@ -1,5 +1,10 @@ +use crate::assignment_map::Column; use crate::constraint_builder::{AdviceColumn, BinaryQuery, ConstraintBuilder, Query}; -use halo2_proofs::{arithmetic::FieldExt, circuit::Region, plonk::ConstraintSystem}; +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Region, Value}, + plonk::ConstraintSystem, +}; use std::fmt::Debug; #[derive(Clone, Copy)] @@ -32,6 +37,23 @@ impl IsZeroGadget { ); } + pub fn assignments>( + &self, + offset: usize, + value: T, + ) -> [((Column, usize), Value); 2] + where + >::Error: Debug, + { + [ + self.value.assignment(offset, value), + self.inverse_or_zero.assignment( + offset, + value.try_into().unwrap().invert().unwrap_or(F::zero()), + ), + ] + } + // TODO: get rid of assign method in favor of it. pub fn assign_value_and_inverse>( &self, diff --git a/src/gadgets/key_bit.rs b/src/gadgets/key_bit.rs index 8891665d..6d13d418 100644 --- a/src/gadgets/key_bit.rs +++ b/src/gadgets/key_bit.rs @@ -2,10 +2,17 @@ use super::{ byte_bit::{ByteBitLookup, RangeCheck256Lookup, RangeCheck8Lookup}, canonical_representation::CanonicalRepresentationLookup, }; -use crate::constraint_builder::{AdviceColumn, ConstraintBuilder, Query, SelectorColumn}; +use crate::{ + assignment_map::Column, + constraint_builder::{AdviceColumn, ConstraintBuilder, Query, SelectorColumn}, +}; use halo2_proofs::{ - arithmetic::FieldExt, circuit::Region, halo2curves::bn256::Fr, plonk::ConstraintSystem, + arithmetic::FieldExt, + circuit::{Region, Value}, + halo2curves::bn256::Fr, + plonk::ConstraintSystem, }; +use rayon::prelude::*; pub trait KeyBitLookup { fn lookup(&self) -> [Query; 3]; @@ -86,28 +93,48 @@ impl KeyBitConfig { } } - pub fn assign(&self, region: &mut Region<'_, Fr>, lookups: &[(Fr, usize, bool)]) { - // TODO; dedup lookups - for (offset, (value, index, bit)) in lookups.iter().enumerate() { - let bytes = value.to_bytes(); - - let index_div_8 = index / 8; // index = (31 - index/8) * 8 - let index_mod_8 = index % 8; - let byte = bytes[index_div_8]; - // sanity check. TODO: Get rid of bit in the assign fn? - assert_eq!(*bit, byte & 1 << index_mod_8 != 0); - - self.value.assign(region, offset, *value); - self.index - .assign(region, offset, u64::try_from(*index).unwrap()); - self.bit.assign(region, offset, *bit); - self.index_div_8 - .assign(region, offset, u64::try_from(index_div_8).unwrap()); - self.index_mod_8 - .assign(region, offset, u64::try_from(index_mod_8).unwrap()); - self.byte.assign(region, offset, u64::from(byte)); + pub fn assign(&self, region: &mut Region<'_, Fr>, lookups: Vec<(Fr, usize, bool)>) { + let assignments: Vec<_> = self.assignments(lookups).collect(); + for ((column, offset), value) in assignments.into_iter() { + match column { + Column::Advice(s) => region + .assign_advice(|| "advice", s.0, offset, || value) + .unwrap(), + _ => unreachable!(), + }; } } + + pub fn assignments( + &self, + lookups: Vec<(Fr, usize, bool)>, + ) -> impl ParallelIterator)> + '_ { + lookups + .into_par_iter() + .enumerate() + .flat_map_iter(|(i, (value, index, bit))| { + let offset = i + 1; + let index_div_8 = index / 8; + let index_mod_8 = index % 8; + let byte = value.to_bytes()[index_div_8]; + [ + self.value.assignment::(offset, value), + self.index.assignment(offset, u64::try_from(index).unwrap()), + self.bit.assignment(offset, bit), + self.index_div_8 + .assignment(offset, u64::try_from(index_div_8).unwrap()), + self.index_mod_8 + .assignment(offset, u64::try_from(index_mod_8).unwrap()), + self.byte.assignment(offset, u64::from(byte)), + ] + .into_iter() + }) + } + + pub fn n_rows_required(lookups: &[(Fr, usize, bool)]) -> usize { + // +1 because assigment starts on offset = 1 instead of offset = 0. + 1 + lookups.len() + } } impl KeyBitLookup for KeyBitConfig { @@ -191,13 +218,13 @@ mod test { layouter.assign_region( || "", |mut region| { - for offset in 0..(8 * 256) { + for offset in 1..(1 + 8 * 256) { selector.enable(&mut region, offset); } - key_bit.assign(&mut region, &self.lookups); + key_bit.assign(&mut region, self.lookups.clone()); byte_bit.assign(&mut region); - canonical_representation.assign(&mut region, randomness, &keys); + canonical_representation.assign(&mut region, randomness, keys.clone(), 256); Ok(()) }, ) diff --git a/src/gadgets/mpt_update.rs b/src/gadgets/mpt_update.rs index 500537e7..d0442e65 100644 --- a/src/gadgets/mpt_update.rs +++ b/src/gadgets/mpt_update.rs @@ -1,3 +1,4 @@ +mod assign; mod nonexistence_proof; mod path; mod segment; @@ -19,23 +20,19 @@ use crate::{ constraint_builder::{ AdviceColumn, BinaryQuery, ConstraintBuilder, Query, SecondPhaseAdviceColumn, }, - types::{ - storage::{StorageLeaf, StorageProof}, - trie::{next_domain, TrieRows}, - ClaimKind, HashDomain, Proof, - }, - util::{account_key, domain_hash, lagrange_polynomial, rlc, u256_hi_lo, u256_to_big_endian}, + types::{storage::StorageProof, HashDomain, Proof}, + util::{account_key, domain_hash, lagrange_polynomial, u256_hi_lo}, MPTProofType, }; use ethers_core::types::Address; use halo2_proofs::{ - arithmetic::{Field, FieldExt}, + arithmetic::FieldExt, circuit::{Region, Value}, halo2curves::{bn256::Fr, group::ff::PrimeField}, - plonk::ConstraintSystem, + plonk::{ConstraintSystem, Error}, }; -use itertools::izip; use lazy_static::lazy_static; +use std::iter::{once, repeat}; use strum::IntoEnumIterator; lazy_static! { @@ -342,531 +339,61 @@ impl MptUpdateConfig { config } - /// Valid assignment proving that the address 0 doesn't exist in an empty MPT. - pub fn assign_padding_row(&self, region: &mut Region<'_, Fr>, offset: usize) { - self.proof_type - .assign(region, offset, MPTProofType::AccountDoesNotExist); - self.key.assign(region, offset, *ZERO_PAIR_HASH); - self.other_key.assign(region, offset, *ZERO_PAIR_HASH); - self.domain.assign(region, offset, HashDomain::Pair); - } - - /// .. pub fn assign( &self, region: &mut Region<'_, Fr>, proofs: &[Proof], + n_rows: usize, randomness: Value, - ) -> usize { - let mut n_rows = 0; - let mut offset = 1; // selector on first row is disabled. + ) { + let mut n_rows_used = 1; // selector on first row is disabled. for proof in proofs { - let proof_type = MPTProofType::from(proof.claim); - let storage_key = - randomness.map(|r| rlc(&u256_to_big_endian(&proof.claim.storage_key()), r)); - let old_value = randomness.map(|r| proof.claim.old_value_assignment(r)); - let new_value = randomness.map(|r| proof.claim.new_value_assignment(r)); - - for i in 0..proof.n_rows() { - self.proof_type.assign(region, offset + i, proof_type); - self.storage_key_rlc.assign(region, offset + i, storage_key); - self.old_value.assign(region, offset + i, old_value); - self.new_value.assign(region, offset + i, new_value); - } - - let key = account_key(proof.claim.address); - let (other_key, other_leaf_data_hash) = - // checking if type 1 or type 2 - if proof.old.key != key { - assert!(proof.new.key == key || proof.new.key == proof.old.key); - (proof.old.key, proof.old.leaf_data_hash.unwrap()) - } else if proof.new.key != key { - assert!(proof.old.key == key); - (proof.new.key, proof.new.leaf_data_hash.unwrap()) - } else { - // neither is a type 1 path - // handle type 0 and type 2 paths here: - (proof.old.key, proof.new.leaf_data_hash.unwrap_or_default()) - }; - // Assign start row - self.segment_type.assign(region, offset, SegmentType::Start); - self.path_type.assign(region, offset, PathType::Start); - self.old_hash.assign(region, offset, proof.claim.old_root); - self.new_hash.assign(region, offset, proof.claim.new_root); - - self.key.assign(region, offset, key); - self.other_key.assign(region, offset, other_key); - self.domain.assign(region, offset, HashDomain::Pair); - - self.intermediate_values[0].assign( - region, - offset, - Fr::from_u128(address_high(proof.claim.address)), - ); - self.intermediate_values[1].assign( - region, - offset, - u64::from(address_low(proof.claim.address)), - ); - - let rlc_fr = |x: Fr| { - let mut bytes = x.to_bytes(); - bytes.reverse(); - randomness.map(|r| rlc(&bytes, r)) - }; - - self.second_phase_intermediate_values[0].assign( - region, - offset, - rlc_fr(proof.claim.old_root), - ); - self.second_phase_intermediate_values[1].assign( - region, - offset, - rlc_fr(proof.claim.new_root), - ); - - offset += 1; - - let n_account_trie_rows = - self.assign_account_trie_rows(region, offset, &proof.account_trie_rows); - for i in 0..n_account_trie_rows { - self.key.assign(region, offset + i, key); - self.other_key.assign(region, offset + i, other_key); - } - offset += n_account_trie_rows; - - let final_path_type = proof - .address_hash_traces - .first() - .map(|(_, _, _, _, _, is_padding_open, is_padding_close)| { - match (*is_padding_open, *is_padding_close) { - (false, false) => PathType::Common, - (false, true) => PathType::ExtensionOld, - (true, false) => PathType::ExtensionNew, - (true, true) => unreachable!(), - } - }) - .unwrap_or(PathType::Common); - let (final_old_hash, final_new_hash) = match proof.address_hash_traces.first() { - None => (proof.old.hash(), proof.new.hash()), - Some((_, _, old_hash, new_hash, _, _, _)) => (*old_hash, *new_hash), - }; - - if proof.old_account.is_none() && proof.new_account.is_none() { - offset -= 1; - self.is_zero_gadgets[2].assign_value_and_inverse(region, offset, key - other_key); - self.is_zero_gadgets[3].assign_value_and_inverse(region, offset, final_old_hash); - - self.intermediate_values[3].assign(region, offset, other_leaf_data_hash); - - n_rows += proof.n_rows(); - offset = 1 + n_rows; - continue; // we don't need to assign any leaf rows for empty accounts - } - - let segment_types = vec![ - SegmentType::AccountLeaf0, - SegmentType::AccountLeaf1, - SegmentType::AccountLeaf2, - SegmentType::AccountLeaf3, - ]; - - let leaf_path_type = match final_path_type { - PathType::Common => { - // need to check if the old or new account is type 2 empty - match ( - final_old_hash.is_zero_vartime(), - final_new_hash.is_zero_vartime(), - ) { - (true, true) => unreachable!("proof type must be AccountDoesNotExist"), - (true, false) => PathType::ExtensionNew, - (false, true) => PathType::ExtensionOld, - (false, false) => PathType::Common, - } - } - _ => final_path_type, - }; - - let directions = match proof_type { - MPTProofType::NonceChanged | MPTProofType::CodeSizeExists => { - vec![true, false, false, false] - } - MPTProofType::BalanceChanged => vec![true, false, false, true], - MPTProofType::PoseidonCodeHashExists => vec![true, true], - MPTProofType::CodeHashExists => vec![true, false, true, true], - MPTProofType::StorageChanged | MPTProofType::StorageDoesNotExist => { - vec![true, false, true, false] - } - MPTProofType::AccountDoesNotExist => unreachable!(), - MPTProofType::AccountDestructed => unimplemented!(), - }; - let next_offset = offset + directions.len(); - - let old_hashes = proof - .old_account_leaf_hashes() - .unwrap_or_else(|| vec![final_old_hash; 4]); - let new_hashes = proof - .new_account_leaf_hashes() - .unwrap_or_else(|| vec![final_new_hash; 4]); - let siblings = proof.account_leaf_siblings(); - - for (i, (segment_type, sibling, old_hash, new_hash, direction)) in - izip!(segment_types, siblings, old_hashes, new_hashes, directions).enumerate() - { - if i == 0 { - self.is_zero_gadgets[3].assign_value_and_inverse(region, offset, old_hash); - self.domain.assign(region, offset + i, HashDomain::Leaf); - } else { - self.domain - .assign(region, offset + i, HashDomain::AccountFields); - } - self.segment_type.assign(region, offset + i, segment_type); - self.path_type.assign(region, offset + i, leaf_path_type); - self.sibling.assign(region, offset + i, sibling); - self.old_hash.assign(region, offset + i, old_hash); - self.new_hash.assign(region, offset + i, new_hash); - self.direction.assign(region, offset + i, direction); - self.key.assign(region, offset + i, key); - self.other_key.assign(region, offset + i, other_key); - - match segment_type { - SegmentType::AccountLeaf0 => { - let [.., other_key_column, other_leaf_data_hash_column] = - self.intermediate_values; - other_key_column.assign(region, offset, other_key); - other_leaf_data_hash_column.assign(region, offset, other_leaf_data_hash); - } - SegmentType::AccountLeaf3 => { - if let ClaimKind::Storage { key, .. } | ClaimKind::IsEmpty(Some(key)) = - proof.claim.kind - { - self.key.assign(region, offset + 3, proof.storage.key()); - let [storage_key_high, storage_key_low, new_domain, ..] = - self.intermediate_values; - let [rlc_storage_key_high, rlc_storage_key_low, ..] = - self.second_phase_intermediate_values; - assign_word_rlc( - region, - offset + 3, - key, - [storage_key_high, storage_key_low], - [rlc_storage_key_high, rlc_storage_key_low], - randomness, - ); - self.other_key - .assign(region, offset + 3, proof.storage.other_key()); - new_domain.assign(region, offset + 3, HashDomain::AccountFields); - } - } - _ => {} - }; - } - self.key.assign(region, offset, key); - self.other_key.assign(region, offset, other_key); - self.is_zero_gadgets[2].assign_value_and_inverse(region, offset, key - other_key); - if let ClaimKind::CodeHash { old, new } = proof.claim.kind { - let [old_high, old_low, new_high, new_low, ..] = self.intermediate_values; - let [old_rlc_high, old_rlc_low, new_rlc_high, new_rlc_low, ..] = - self.second_phase_intermediate_values; - if let Some(value) = old { - assign_word_rlc( - region, - offset + 3, - value, - [old_high, old_low], - [old_rlc_high, old_rlc_low], - randomness, - ); - } - if let Some(value) = new { - assign_word_rlc( - region, - offset + 3, - value, - [new_high, new_low], - [new_rlc_high, new_rlc_low], - randomness, - ); - } - }; - self.assign_storage(region, next_offset, &proof.storage, randomness); - n_rows += proof.n_rows(); - offset = 1 + n_rows; + n_rows_used += self.assign_proof(region, n_rows_used, proof, randomness); } - n_rows - } - fn assign_account_trie_rows( - &self, - region: &mut Region<'_, Fr>, - starting_offset: usize, - rows: &TrieRows, - ) -> usize { - let n_rows = self.assign_trie_rows(region, starting_offset, rows); - for i in 0..n_rows { - self.segment_type - .assign(region, starting_offset + i, SegmentType::AccountTrie); - } - n_rows - } + let expected_offset = Self::n_rows_required(proofs); + debug_assert!( + n_rows_used == expected_offset, + "assign used {n_rows_used} rows but {expected_offset} rows expected from `n_rows_required`", + ); - fn assign_storage_trie_rows( - &self, - region: &mut Region<'_, Fr>, - starting_offset: usize, - rows: &TrieRows, - ) -> usize { - let n_rows = self.assign_trie_rows(region, starting_offset, rows); - for i in 0..n_rows { - self.segment_type - .assign(region, starting_offset + i, SegmentType::StorageTrie); + for offset in n_rows_used..n_rows { + self.assign_padding_row(region, offset); } - n_rows } - fn assign_trie_rows( + pub fn assignments( &self, - region: &mut Region<'_, Fr>, - starting_offset: usize, - rows: &TrieRows, - ) -> usize { - for (i, row) in rows.0.iter().enumerate() { - let offset = starting_offset + i; - self.depth - .assign(region, offset, u64::try_from(i + 1).unwrap()); - self.path_type.assign(region, offset, row.path_type); - - if let Some(next_row) = rows.0.get(i + 1) { - if !matches!(next_row.path_type, PathType::Start | PathType::Common) - && row.path_type == PathType::Common - { - self.intermediate_values[2].assign( - region, - offset, - next_domain(row.domain, row.direction), - ); - } - } - for (value, column) in [ - (row.sibling, self.sibling), - (row.old, self.old_hash), - (row.new, self.new_hash), - (row.direction.into(), self.direction), - (row.domain.into(), self.domain), - ] { - column.assign(region, offset, value); - } - } - rows.len() - } - - fn assign_storage( - &self, - region: &mut Region<'_, Fr>, - offset: usize, - storage: &StorageProof, + proofs: Vec, + n_rows: usize, randomness: Value, - ) -> usize { - match storage { - StorageProof::Root(_) => 0, - StorageProof::Update { - key, - trie_rows, - old_leaf, - new_leaf, - .. - } => { - let other_key = storage.other_key(); - let n_trie_rows = self.assign_storage_trie_rows(region, offset, trie_rows); - let n_leaf_rows = self.assign_storage_leaf_row( - region, - offset + n_trie_rows, - *key, - other_key, - old_leaf, - new_leaf, - randomness, - ); - let n_rows = n_trie_rows + n_leaf_rows; - - for i in 0..n_rows { - self.key.assign(region, offset + i, *key); - self.other_key.assign(region, offset + i, other_key); + ) -> Vec) -> Result<(), Error> + '_> { + let n_padding_rows = n_rows - Self::n_rows_required(&proofs); + let n_closures = 1 + proofs.len() + n_padding_rows; + dbg!(n_closures); + once(None) + .chain(proofs.into_iter().map(Some).chain(repeat(None))) + .take(n_closures) + .enumerate() + .map(move |(i, maybe_proof)| { + move |mut region: Region<'_, Fr>| { + if let Some(proof) = maybe_proof.clone() { + self.assign_proof(&mut region, 0, &proof, randomness); + } else if i == 0 { + // Need make one assignment so region size is calculated correctly. + self.new_hash.assign(&mut region, 0, 0); + } else { + self.assign_padding_row(&mut region, 0); + } + Ok(()) } - - n_rows - } - } + }) + .collect() } - fn assign_empty_storage_proof( - &self, - region: &mut Region<'_, Fr>, - offset: usize, - key: Fr, - other_key: Fr, - old: &StorageLeaf, - new: &StorageLeaf, - ) -> usize { - let [_, _, _, other_leaf_data_hash, ..] = self.intermediate_values; - let [.., key_equals_other_key, hash_is_zero] = self.is_zero_gadgets; - match (old, new) { - ( - StorageLeaf::Leaf { - mpt_key: old_key, - value_hash: old_value_hash, - }, - StorageLeaf::Leaf { - mpt_key: new_key, - value_hash: new_value_hash, - }, - ) => { - assert!(key != other_key); - - key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); - - assert_eq!(new_key, old_key); - assert_eq!(old_value_hash, new_value_hash); - - hash_is_zero.assign_value_and_inverse(region, offset, old.hash()); - - other_leaf_data_hash.assign(region, offset, *old_value_hash); - } - (StorageLeaf::Empty { .. }, StorageLeaf::Empty { .. }) => { - assert!(key == other_key); - - assert_eq!(old.hash(), Fr::zero()); - assert_eq!(new.hash(), Fr::zero()); - - key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); - } - (StorageLeaf::Entry { .. }, _) | (_, StorageLeaf::Entry { .. }) => return 0, - (StorageLeaf::Leaf { .. }, StorageLeaf::Empty { .. }) - | (StorageLeaf::Empty { .. }, StorageLeaf::Leaf { .. }) => unreachable!(), - } - - 0 - } - - fn assign_storage_leaf_row( - &self, - region: &mut Region<'_, Fr>, - offset: usize, - key: Fr, - other_key: Fr, - old: &StorageLeaf, - new: &StorageLeaf, - randomness: Value, - ) -> usize { - let path_type = match (old, new) { - (StorageLeaf::Entry { .. }, StorageLeaf::Entry { .. }) => PathType::Common, - (StorageLeaf::Entry { .. }, _) => PathType::ExtensionOld, - (_, StorageLeaf::Entry { .. }) => PathType::ExtensionNew, - _ => { - return self.assign_empty_storage_proof( - region, - offset - 1, - key, - other_key, - old, - new, - ) - } - }; - self.path_type.assign(region, offset, path_type); - self.segment_type - .assign(region, offset, SegmentType::StorageLeaf0); - self.direction.assign(region, offset, true); - self.domain.assign(region, offset, HashDomain::Leaf); - - let sibling = match path_type { - PathType::Start => unreachable!(), - PathType::Common | PathType::ExtensionOld => old.key(), - PathType::ExtensionNew => new.key(), - }; - self.sibling.assign(region, offset, sibling); - - let (old_hash, new_hash) = match path_type { - PathType::Start => unreachable!(), - PathType::Common => (old.value_hash(), new.value_hash()), - PathType::ExtensionOld => (old.value_hash(), new.hash()), - PathType::ExtensionNew => (old.hash(), new.value_hash()), - }; - self.old_hash.assign(region, offset, old_hash); - self.new_hash.assign(region, offset, new_hash); - - let [old_high, old_low, new_high, new_low, ..] = self.intermediate_values; - let [old_rlc_high, old_rlc_low, new_rlc_high, new_rlc_low, ..] = - self.second_phase_intermediate_values; - - if let StorageLeaf::Entry { .. } = old { - assign_word_rlc( - region, - offset, - old.value(), - [old_high, old_low], - [old_rlc_high, old_rlc_low], - randomness, - ); - } - - if let StorageLeaf::Entry { .. } = new { - assign_word_rlc( - region, - offset, - new.value(), - [new_high, new_low], - [new_rlc_high, new_rlc_low], - randomness, - ); - } - - let [old_hash_is_zero_storage_hash, new_hash_is_zero_storage_hash, ..] = - self.is_zero_gadgets; - old_hash_is_zero_storage_hash.assign_value_and_inverse( - region, - offset, - old_hash - *ZERO_PAIR_HASH, - ); - new_hash_is_zero_storage_hash.assign_value_and_inverse( - region, - offset, - new_hash - *ZERO_PAIR_HASH, - ); - - match path_type { - PathType::Start => unreachable!(), - PathType::Common => {} - PathType::ExtensionOld => { - let new_key = new.key(); - let other_key = if key != new_key { new_key } else { old.key() }; - - let [.., key_equals_other_key, new_hash_is_zero] = self.is_zero_gadgets; - key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); - new_hash_is_zero.assign_value_and_inverse(region, offset, new_hash); - - if key != other_key { - let [.., other_leaf_data_hash] = self.intermediate_values; - other_leaf_data_hash.assign(region, offset, new.value_hash()); - } - } - PathType::ExtensionNew => { - let old_key = old.key(); - let other_key = if key != old_key { old_key } else { new.key() }; - - let [.., key_equals_other_key, old_hash_is_zero] = self.is_zero_gadgets; - key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); - old_hash_is_zero.assign_value_and_inverse(region, offset, old_hash); - - if key != other_key { - let [.., other_leaf_data_hash] = self.intermediate_values; - other_leaf_data_hash.assign(region, offset, old.value_hash()); - } - } - } - - 1 + pub fn n_rows_required(proofs: &[Proof]) -> usize { + // +1 because assigment starts on offset = 1 instead of offset = 0. + proofs.iter().map(Proof::n_rows).sum::() + 1 } } @@ -2086,6 +1613,8 @@ pub fn hash_traces(proofs: &[Proof]) -> Vec<([Fr; 2], Fr, Fr)> { } } } + hash_traces.sort(); + hash_traces.dedup(); hash_traces } @@ -2118,6 +1647,9 @@ pub fn key_bit_lookups(proofs: &[Proof]) -> Vec<(Fr, usize, bool)> { } lookups.extend(proof.storage.key_bit_lookups()); } + + lookups.sort(); + lookups.dedup(); lookups } @@ -2199,6 +1731,19 @@ pub fn byte_representations(proofs: &[Proof]) -> (Vec, Vec, Vec, _ => {} } } + + u32s.sort(); + u32s.dedup(); + + u64s.sort(); + u64s.dedup(); + + u128s.sort(); + u128s.dedup(); + + frs.sort(); + frs.dedup(); + (u32s, u64s, u128s, frs) } @@ -2213,5 +1758,7 @@ pub fn mpt_update_keys(proofs: &[Proof]) -> Vec { keys.push(proof.claim.old_root); keys.push(proof.claim.new_root); } + keys.sort(); + keys.dedup(); keys } diff --git a/src/gadgets/mpt_update/assign.rs b/src/gadgets/mpt_update/assign.rs new file mode 100644 index 00000000..f21f8987 --- /dev/null +++ b/src/gadgets/mpt_update/assign.rs @@ -0,0 +1,590 @@ +use super::MptUpdateConfig; +use crate::{ + gadgets::mpt_update::{assign_word_rlc, path::PathType, segment::SegmentType, ZERO_PAIR_HASH}, + types::{ + storage::{StorageLeaf, StorageProof}, + trie::{next_domain, TrieRows}, + ClaimKind, HashDomain, Proof, + }, + util::{account_key, rlc, u256_to_big_endian}, + MPTProofType, +}; +use ethers_core::types::Address; +use halo2_proofs::{ + arithmetic::{Field, FieldExt}, + circuit::{Region, Value}, + halo2curves::bn256::Fr, +}; +use itertools::izip; + +impl MptUpdateConfig { + pub fn assign_proof( + &self, + region: &mut Region<'_, Fr>, + offset: usize, + proof: &Proof, + randomness: Value, + ) -> usize { + let mut offset = offset; + let proof_type = MPTProofType::from(proof.claim); + let storage_key = + randomness.map(|r| rlc(&u256_to_big_endian(&proof.claim.storage_key()), r)); + let old_value = randomness.map(|r| proof.claim.old_value_assignment(r)); + let new_value = randomness.map(|r| proof.claim.new_value_assignment(r)); + + for i in 0..proof.n_rows() { + self.proof_type.assign(region, offset + i, proof_type); + self.storage_key_rlc.assign(region, offset + i, storage_key); + self.old_value.assign(region, offset + i, old_value); + self.new_value.assign(region, offset + i, new_value); + } + + let key = account_key(proof.claim.address); + // checking if type 1 or type 2 + let (other_key, other_leaf_data_hash) = if proof.old.key != key { + assert!(proof.new.key == key || proof.new.key == proof.old.key); + (proof.old.key, proof.old.leaf_data_hash.unwrap()) + } else if proof.new.key != key { + assert!(proof.old.key == key); + (proof.new.key, proof.new.leaf_data_hash.unwrap()) + } else { + // neither is a type 1 path + // handle type 0 and type 2 paths here: + (proof.old.key, proof.new.leaf_data_hash.unwrap_or_default()) + }; + // Assign start row + self.segment_type.assign(region, offset, SegmentType::Start); + self.path_type.assign(region, offset, PathType::Start); + self.old_hash.assign(region, offset, proof.claim.old_root); + self.new_hash.assign(region, offset, proof.claim.new_root); + + self.key.assign(region, offset, key); + self.other_key.assign(region, offset, other_key); + self.domain.assign(region, offset, HashDomain::Pair); + + self.intermediate_values[0].assign( + region, + offset, + Fr::from_u128(address_high(proof.claim.address)), + ); + self.intermediate_values[1].assign( + region, + offset, + u64::from(address_low(proof.claim.address)), + ); + + let rlc_fr = |x: Fr| { + let mut bytes = x.to_bytes(); + bytes.reverse(); + randomness.map(|r| rlc(&bytes, r)) + }; + + self.second_phase_intermediate_values[0].assign( + region, + offset, + rlc_fr(proof.claim.old_root), + ); + self.second_phase_intermediate_values[1].assign( + region, + offset, + rlc_fr(proof.claim.new_root), + ); + + offset += 1; + + let n_account_trie_rows = + self.assign_account_trie_rows(region, offset, &proof.account_trie_rows); + for i in 0..n_account_trie_rows { + self.key.assign(region, offset + i, key); + self.other_key.assign(region, offset + i, other_key); + } + offset += n_account_trie_rows; + + let final_path_type = proof + .address_hash_traces + .first() + .map(|(_, _, _, _, _, is_padding_open, is_padding_close)| { + match (*is_padding_open, *is_padding_close) { + (false, false) => PathType::Common, + (false, true) => PathType::ExtensionOld, + (true, false) => PathType::ExtensionNew, + (true, true) => unreachable!(), + } + }) + .unwrap_or(PathType::Common); + let (final_old_hash, final_new_hash) = match proof.address_hash_traces.first() { + None => (proof.old.hash(), proof.new.hash()), + Some((_, _, old_hash, new_hash, _, _, _)) => (*old_hash, *new_hash), + }; + + if proof.old_account.is_none() && proof.new_account.is_none() { + offset -= 1; + self.is_zero_gadgets[2].assign_value_and_inverse(region, offset, key - other_key); + self.is_zero_gadgets[3].assign_value_and_inverse(region, offset, final_old_hash); + + self.intermediate_values[3].assign(region, offset, other_leaf_data_hash); + + // we don't need to assign any leaf rows for empty accounts + return proof.n_rows(); + } + + let segment_types = vec![ + SegmentType::AccountLeaf0, + SegmentType::AccountLeaf1, + SegmentType::AccountLeaf2, + SegmentType::AccountLeaf3, + ]; + + let leaf_path_type = match final_path_type { + PathType::Common => { + // need to check if the old or new account is type 2 empty + match ( + final_old_hash.is_zero_vartime(), + final_new_hash.is_zero_vartime(), + ) { + (true, true) => unreachable!("proof type must be AccountDoesNotExist"), + (true, false) => PathType::ExtensionNew, + (false, true) => PathType::ExtensionOld, + (false, false) => PathType::Common, + } + } + _ => final_path_type, + }; + + let directions = match proof_type { + MPTProofType::NonceChanged | MPTProofType::CodeSizeExists => { + vec![true, false, false, false] + } + MPTProofType::BalanceChanged => vec![true, false, false, true], + MPTProofType::PoseidonCodeHashExists => vec![true, true], + MPTProofType::CodeHashExists => vec![true, false, true, true], + MPTProofType::StorageChanged | MPTProofType::StorageDoesNotExist => { + vec![true, false, true, false] + } + MPTProofType::AccountDoesNotExist => unreachable!(), + MPTProofType::AccountDestructed => unimplemented!(), + }; + let next_offset = offset + directions.len(); + + let old_hashes = proof + .old_account_leaf_hashes() + .unwrap_or_else(|| vec![final_old_hash; 4]); + let new_hashes = proof + .new_account_leaf_hashes() + .unwrap_or_else(|| vec![final_new_hash; 4]); + let siblings = proof.account_leaf_siblings(); + + for (i, (segment_type, sibling, old_hash, new_hash, direction)) in + izip!(segment_types, siblings, old_hashes, new_hashes, directions).enumerate() + { + if i == 0 { + self.is_zero_gadgets[3].assign_value_and_inverse(region, offset, old_hash); + self.domain.assign(region, offset + i, HashDomain::Leaf); + } else { + self.domain + .assign(region, offset + i, HashDomain::AccountFields); + } + self.segment_type.assign(region, offset + i, segment_type); + self.path_type.assign(region, offset + i, leaf_path_type); + self.sibling.assign(region, offset + i, sibling); + self.old_hash.assign(region, offset + i, old_hash); + self.new_hash.assign(region, offset + i, new_hash); + self.direction.assign(region, offset + i, direction); + self.key.assign(region, offset + i, key); + self.other_key.assign(region, offset + i, other_key); + + match segment_type { + SegmentType::AccountLeaf0 => { + let [.., other_key_column, other_leaf_data_hash_column] = + self.intermediate_values; + other_key_column.assign(region, offset, other_key); + other_leaf_data_hash_column.assign(region, offset, other_leaf_data_hash); + } + SegmentType::AccountLeaf3 => { + if let ClaimKind::Storage { key, .. } | ClaimKind::IsEmpty(Some(key)) = + proof.claim.kind + { + self.key.assign(region, offset + 3, proof.storage.key()); + let [storage_key_high, storage_key_low, new_domain, ..] = + self.intermediate_values; + let [rlc_storage_key_high, rlc_storage_key_low, ..] = + self.second_phase_intermediate_values; + assign_word_rlc( + region, + offset + 3, + key, + [storage_key_high, storage_key_low], + [rlc_storage_key_high, rlc_storage_key_low], + randomness, + ); + self.other_key + .assign(region, offset + 3, proof.storage.other_key()); + new_domain.assign(region, offset + 3, HashDomain::AccountFields); + } + } + _ => {} + }; + } + self.key.assign(region, offset, key); + self.other_key.assign(region, offset, other_key); + self.is_zero_gadgets[2].assign_value_and_inverse(region, offset, key - other_key); + if let ClaimKind::CodeHash { old, new } = proof.claim.kind { + let [old_high, old_low, new_high, new_low, ..] = self.intermediate_values; + let [old_rlc_high, old_rlc_low, new_rlc_high, new_rlc_low, ..] = + self.second_phase_intermediate_values; + if let Some(value) = old { + assign_word_rlc( + region, + offset + 3, + value, + [old_high, old_low], + [old_rlc_high, old_rlc_low], + randomness, + ); + } + if let Some(value) = new { + assign_word_rlc( + region, + offset + 3, + value, + [new_high, new_low], + [new_rlc_high, new_rlc_low], + randomness, + ); + } + }; + self.assign_storage(region, next_offset, &proof.storage, randomness); + return proof.n_rows(); + } + + // Valid assignment proving that the address 0 doesn't exist in an empty MPT. + pub fn assign_padding_row(&self, region: &mut Region<'_, Fr>, offset: usize) { + self.proof_type + .assign(region, offset, MPTProofType::AccountDoesNotExist); + self.key.assign(region, offset, *ZERO_PAIR_HASH); + self.other_key.assign(region, offset, *ZERO_PAIR_HASH); + self.domain.assign(region, offset, HashDomain::Pair); + self.new_hash.assign(region, offset, 0); + } + + fn assign_storage_trie_rows( + &self, + region: &mut Region<'_, Fr>, + starting_offset: usize, + rows: &TrieRows, + ) -> usize { + let n_rows = self.assign_trie_rows(region, starting_offset, rows); + for i in 0..n_rows { + self.segment_type + .assign(region, starting_offset + i, SegmentType::StorageTrie); + } + n_rows + } + + fn asssign_storage( + &self, + region: &mut Region<'_, Fr>, + offset: usize, + storage: &StorageProof, + randomness: Value, + ) -> usize { + match storage { + StorageProof::Root(_) => 0, + StorageProof::Update { + key, + trie_rows, + old_leaf, + new_leaf, + .. + } => { + let other_key = storage.other_key(); + let n_trie_rows = self.assign_storage_trie_rows(region, offset, trie_rows); + let n_leaf_rows = self.assign_storage_leaf_row( + region, + offset + n_trie_rows, + *key, + other_key, + old_leaf, + new_leaf, + randomness, + ); + let n_rows = n_trie_rows + n_leaf_rows; + + for i in 0..n_rows { + self.key.assign(region, offset + i, *key); + self.other_key.assign(region, offset + i, other_key); + } + + n_rows + } + } + } + + fn assign_account_trie_rows( + &self, + region: &mut Region<'_, Fr>, + starting_offset: usize, + rows: &TrieRows, + ) -> usize { + let n_rows = self.assign_trie_rows(region, starting_offset, rows); + for i in 0..n_rows { + self.segment_type + .assign(region, starting_offset + i, SegmentType::AccountTrie); + } + n_rows + } + + fn assign_empty_storage_proof( + &self, + region: &mut Region<'_, Fr>, + offset: usize, + key: Fr, + other_key: Fr, + old: &StorageLeaf, + new: &StorageLeaf, + ) -> usize { + let [_, _, _, other_leaf_data_hash, ..] = self.intermediate_values; + let [.., key_equals_other_key, hash_is_zero] = self.is_zero_gadgets; + match (old, new) { + ( + StorageLeaf::Leaf { + mpt_key: old_key, + value_hash: old_value_hash, + }, + StorageLeaf::Leaf { + mpt_key: new_key, + value_hash: new_value_hash, + }, + ) => { + assert!(key != other_key); + + key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); + + assert_eq!(new_key, old_key); + assert_eq!(old_value_hash, new_value_hash); + + hash_is_zero.assign_value_and_inverse(region, offset, old.hash()); + + other_leaf_data_hash.assign(region, offset, *old_value_hash); + } + (StorageLeaf::Empty { .. }, StorageLeaf::Empty { .. }) => { + assert!(key == other_key); + + assert_eq!(old.hash(), Fr::zero()); + assert_eq!(new.hash(), Fr::zero()); + + key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); + } + (StorageLeaf::Entry { .. }, _) | (_, StorageLeaf::Entry { .. }) => return 0, + (StorageLeaf::Leaf { .. }, StorageLeaf::Empty { .. }) + | (StorageLeaf::Empty { .. }, StorageLeaf::Leaf { .. }) => unreachable!(), + } + + 0 + } + + fn assign_trie_rows( + &self, + region: &mut Region<'_, Fr>, + starting_offset: usize, + rows: &TrieRows, + ) -> usize { + for (i, row) in rows.0.iter().enumerate() { + let offset = starting_offset + i; + self.depth + .assign(region, offset, u64::try_from(i + 1).unwrap()); + self.path_type.assign(region, offset, row.path_type); + + if let Some(next_row) = rows.0.get(i + 1) { + if !matches!(next_row.path_type, PathType::Start | PathType::Common) + && row.path_type == PathType::Common + { + self.intermediate_values[2].assign( + region, + offset, + next_domain(row.domain, row.direction), + ); + } + } + for (value, column) in [ + (row.sibling, self.sibling), + (row.old, self.old_hash), + (row.new, self.new_hash), + (row.direction.into(), self.direction), + (row.domain.into(), self.domain), + ] { + column.assign(region, offset, value); + } + } + rows.len() + } + + fn assign_storage( + &self, + region: &mut Region<'_, Fr>, + offset: usize, + storage: &StorageProof, + randomness: Value, + ) -> usize { + match storage { + StorageProof::Root(_) => 0, + StorageProof::Update { + key, + trie_rows, + old_leaf, + new_leaf, + .. + } => { + let other_key = storage.other_key(); + let n_trie_rows = self.assign_storage_trie_rows(region, offset, trie_rows); + let n_leaf_rows = self.assign_storage_leaf_row( + region, + offset + n_trie_rows, + *key, + other_key, + old_leaf, + new_leaf, + randomness, + ); + let n_rows = n_trie_rows + n_leaf_rows; + + for i in 0..n_rows { + self.key.assign(region, offset + i, *key); + self.other_key.assign(region, offset + i, other_key); + } + + n_rows + } + } + } + + fn assign_storage_leaf_row( + &self, + region: &mut Region<'_, Fr>, + offset: usize, + key: Fr, + other_key: Fr, + old: &StorageLeaf, + new: &StorageLeaf, + randomness: Value, + ) -> usize { + let path_type = match (old, new) { + (StorageLeaf::Entry { .. }, StorageLeaf::Entry { .. }) => PathType::Common, + (StorageLeaf::Entry { .. }, _) => PathType::ExtensionOld, + (_, StorageLeaf::Entry { .. }) => PathType::ExtensionNew, + _ => { + return self.assign_empty_storage_proof( + region, + offset - 1, + key, + other_key, + old, + new, + ) + } + }; + self.path_type.assign(region, offset, path_type); + self.segment_type + .assign(region, offset, SegmentType::StorageLeaf0); + self.direction.assign(region, offset, true); + self.domain.assign(region, offset, HashDomain::Leaf); + + let sibling = match path_type { + PathType::Start => unreachable!(), + PathType::Common | PathType::ExtensionOld => old.key(), + PathType::ExtensionNew => new.key(), + }; + self.sibling.assign(region, offset, sibling); + + let (old_hash, new_hash) = match path_type { + PathType::Start => unreachable!(), + PathType::Common => (old.value_hash(), new.value_hash()), + PathType::ExtensionOld => (old.value_hash(), new.hash()), + PathType::ExtensionNew => (old.hash(), new.value_hash()), + }; + self.old_hash.assign(region, offset, old_hash); + self.new_hash.assign(region, offset, new_hash); + + let [old_high, old_low, new_high, new_low, ..] = self.intermediate_values; + let [old_rlc_high, old_rlc_low, new_rlc_high, new_rlc_low, ..] = + self.second_phase_intermediate_values; + + if let StorageLeaf::Entry { .. } = old { + assign_word_rlc( + region, + offset, + old.value(), + [old_high, old_low], + [old_rlc_high, old_rlc_low], + randomness, + ); + } + + if let StorageLeaf::Entry { .. } = new { + assign_word_rlc( + region, + offset, + new.value(), + [new_high, new_low], + [new_rlc_high, new_rlc_low], + randomness, + ); + } + + let [old_hash_is_zero_storage_hash, new_hash_is_zero_storage_hash, ..] = + self.is_zero_gadgets; + old_hash_is_zero_storage_hash.assign_value_and_inverse( + region, + offset, + old_hash - *ZERO_PAIR_HASH, + ); + new_hash_is_zero_storage_hash.assign_value_and_inverse( + region, + offset, + new_hash - *ZERO_PAIR_HASH, + ); + + match path_type { + PathType::Start => unreachable!(), + PathType::Common => {} + PathType::ExtensionOld => { + let new_key = new.key(); + let other_key = if key != new_key { new_key } else { old.key() }; + + let [.., key_equals_other_key, new_hash_is_zero] = self.is_zero_gadgets; + key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); + new_hash_is_zero.assign_value_and_inverse(region, offset, new_hash); + + if key != other_key { + let [.., other_leaf_data_hash] = self.intermediate_values; + other_leaf_data_hash.assign(region, offset, new.value_hash()); + } + } + PathType::ExtensionNew => { + let old_key = old.key(); + let other_key = if key != old_key { old_key } else { new.key() }; + + let [.., key_equals_other_key, old_hash_is_zero] = self.is_zero_gadgets; + key_equals_other_key.assign_value_and_inverse(region, offset, key - other_key); + old_hash_is_zero.assign_value_and_inverse(region, offset, old_hash); + + if key != other_key { + let [.., other_leaf_data_hash] = self.intermediate_values; + other_leaf_data_hash.assign(region, offset, old.value_hash()); + } + } + } + + 1 + } +} + +fn address_high(a: Address) -> u128 { + let high_bytes: [u8; 16] = a.0[..16].try_into().unwrap(); + u128::from_be_bytes(high_bytes) +} + +fn address_low(a: Address) -> u32 { + let low_bytes: [u8; 4] = a.0[16..].try_into().unwrap(); + u32::from_be_bytes(low_bytes) +} diff --git a/src/gadgets/poseidon.rs b/src/gadgets/poseidon.rs index 2586490e..c216643e 100644 --- a/src/gadgets/poseidon.rs +++ b/src/gadgets/poseidon.rs @@ -7,6 +7,9 @@ use halo2_proofs::{ #[cfg(test)] use hash_circuit::hash::Hashable; +#[cfg(test)] +const MAX_POSEIDON_ROWS: usize = 200; + /// Lookup represent the poseidon table in zkevm circuit pub trait PoseidonLookup { fn lookup_columns(&self) -> (FixedColumn, [AdviceColumn; 6]) { @@ -48,6 +51,9 @@ impl PoseidonTable { } pub fn load(&self, region: &mut Region<'_, Fr>, hash_traces: &[([Fr; 2], Fr, Fr)]) { + // The test poseidon table starts assigning from the first row, which has a disabled + // selector, but this is fine because the poseidon_lookup in the ConstraintBuilder + // doesn't include the mpt circuit's selector column. for (offset, hash_trace) in hash_traces.iter().enumerate() { assert!( Hashable::hash_with_domain([hash_trace.0[0], hash_trace.0[1]], hash_trace.1) @@ -67,6 +73,12 @@ impl PoseidonTable { } self.q_enable.assign(region, offset, Fr::one()); } + + // We need to do this so that the fixed columns in the tests will not depend on the + // number of poseidon hashes that are looked up. + for offset in hash_traces.len()..MAX_POSEIDON_ROWS { + self.q_enable.assign(region, offset, Fr::one()); + } } } diff --git a/src/lib.rs b/src/lib.rs index aa76d409..9f529387 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ #![allow(clippy::too_many_arguments)] #![deny(unsafe_code)] +mod assignment_map; pub mod constraint_builder; pub mod gadgets; mod mpt_table; diff --git a/src/mpt.rs b/src/mpt.rs index 08c8c50c..3e1c2982 100644 --- a/src/mpt.rs +++ b/src/mpt.rs @@ -1,4 +1,5 @@ use crate::{ + assignment_map::{AssignmentMap, Column}, constraint_builder::{ConstraintBuilder, SelectorColumn}, gadgets::{ byte_bit::ByteBitGadget, @@ -17,11 +18,12 @@ use crate::{ }; use halo2_proofs::{ arithmetic::FieldExt, - circuit::Layouter, + circuit::{Layouter, Value}, halo2curves::bn256::Fr, plonk::{Challenge, ConstraintSystem, Error, Expression, VirtualCells}, }; use itertools::Itertools; +use rayon::prelude::*; /// Config for MptCircuit #[derive(Clone)] @@ -119,65 +121,70 @@ impl MptCircuitConfig { n_rows: usize, ) -> Result<(), Error> { let randomness = self.rlc_randomness.value(layouter); - let (u32s, u64s, u128s, frs) = byte_representations(proofs); - layouter.assign_region( - || "mpt circuit", - |mut region| { - for offset in 1..n_rows { - self.selector.enable(&mut region, offset); - } - - // pad canonical_representation to fixed count - // notice each input cost 32 rows in canonical_representation, and inside - // assign one extra input is added - let mut keys = mpt_update_keys(proofs); - keys.sort(); - keys.dedup(); - let total_rep_size = n_rows / 32 - 1; - assert!( - total_rep_size >= keys.len(), - "no enough space for canonical representation of all keys (need {})", - keys.len() - ); - - self.canonical_representation.assign( - &mut region, - randomness, - keys.iter() - .chain(std::iter::repeat(&Fr::zero())) - .take(total_rep_size), - ); - self.key_bit.assign(&mut region, &key_bit_lookups(proofs)); - self.byte_bit.assign(&mut region); - self.byte_representation.assign( - &mut region, - &u32s, - &u64s, - &u128s, - &frs, - randomness, - ); - - let n_assigned_rows = self.mpt_update.assign(&mut region, proofs, randomness); - - assert!( - 2 + n_assigned_rows <= n_rows, - "mpt circuit requires {n_assigned_rows} rows for mpt updates + 1 initial \ - all-zero row + at least 1 final padding row. Only {n_rows} rows available." - ); - - for offset in 1 + n_assigned_rows..n_rows { - self.mpt_update.assign_padding_row(&mut region, offset); - } - self.is_final_row.enable(&mut region, n_rows - 1); - - Ok(()) - }, - ) + let proofs_vec: Vec<_> = proofs.iter().cloned().collect(); + layouter.assign_regions( + || "mpt circuit parallel assignment 1", + self.mpt_update.assignments(proofs_vec, n_rows, randomness), + )?; + + let (u32s, u64s, u128s, frs) = byte_representations(proofs); + let byte_representation_assignments = self + .byte_representation + .assignments(u32s, u64s, u128s, frs, randomness); + let selector_assignments = self.selector_assignments(n_rows); + let byte_bit_assignments = self.byte_bit.assignments(); + let canonical_representation_assignments = + self.canonical_representation + .assignments(mpt_update_keys(proofs), n_rows, randomness); + let key_bit_assignments = self.key_bit.assignments(key_bit_lookups(proofs)); + + layouter.assign_regions( + || "mpt circuit parallel assignment 2", + AssignmentMap::new( + selector_assignments + .chain(byte_bit_assignments) + .chain(byte_representation_assignments) + .chain(canonical_representation_assignments) + .chain(key_bit_assignments), + ) + .into_vec(), + )?; + + Ok(()) } pub fn lookup_exprs(&self, meta: &mut VirtualCells<'_, F>) -> [Expression; 8] { self.mpt_update.lookup().map(|q| q.run(meta)) } + + /// The number of minimum number of rows required for the mpt circuit. + pub fn n_rows_required(proofs: &[Proof]) -> usize { + let (u32s, u64s, u128s, frs) = byte_representations(proofs); + + // +1 for the final padding row to satisfy the "final mpt update is padding" constraint. + 1 + *[ + MptUpdateConfig::n_rows_required(proofs), + CanonicalRepresentationConfig::n_rows_required(&mpt_update_keys(proofs)), + KeyBitConfig::n_rows_required(&key_bit_lookups(proofs)), + // TODO: move rlc lookup for frs into CanonicalRepresentationConfig. + ByteRepresentationConfig::n_rows_required(&u32s, &u64s, &u128s, &frs), + ByteBitGadget::n_rows_required(), + ] + .iter() + .max() + .unwrap() + } + + fn selector_assignments( + &self, + n_rows: usize, + ) -> impl ParallelIterator)> + '_ { + (0..n_rows).into_par_iter().flat_map_iter(move |offset| { + [ + self.selector.assignment(offset, offset != 0), + self.is_final_row.assignment(offset, offset == n_rows - 1), + ] + }) + } } diff --git a/src/tests.rs b/src/tests.rs index 91855616..0ea9df52 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -6,10 +6,12 @@ use ethers_core::types::{Address, U256}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner}, dev::MockProver, - halo2curves::bn256::Fr, - plonk::{Circuit, ConstraintSystem, Error, FirstPhase}, + halo2curves::bn256::{Bn256, Fr}, + plonk::{keygen_vk, Circuit, ConstraintSystem, Error, FirstPhase}, + poly::kzg::commitment::ParamsKZG, }; use mpt_zktrie::state::{builder::HASH_SCHEME_DONE, witness::WitnessGenerator, ZktrieState}; +use rand_chacha::rand_core::SeedableRng; const N_ROWS: usize = 8 * 256 + 1; const STORAGE_ADDRESS: Address = Address::repeat_byte(1); @@ -115,6 +117,34 @@ fn degree() { assert_eq!(meta.degree(), 9); } +#[test] +fn verifying_key_constant() { + let params = ParamsKZG::::setup(17, rand_chacha::ChaCha20Rng::seed_from_u64(2)); + + let no_updates = TestCircuit::new(N_ROWS, vec![]); + let one_update = TestCircuit::new( + N_ROWS, + vec![( + MPTProofType::BalanceChanged, + serde_json::from_str(&include_str!( + "traces/empty_account_type_1_balance_update.json" + )) + .unwrap(), + )], + ); + let vk_no_updates = keygen_vk(¶ms, &no_updates).unwrap(); + let vk_one_update = keygen_vk(¶ms, &one_update).unwrap(); + + assert_eq!( + vk_no_updates.fixed_commitments(), + vk_one_update.fixed_commitments() + ); + assert_eq!( + vk_no_updates.permutation().commitments(), + vk_one_update.permutation().commitments() + ); +} + #[test] fn all_padding() { mock_prove(vec![]); @@ -1046,3 +1076,35 @@ fn create_name_registrator_per_txs_not_enough_gas_d0_g0_v0() { .unwrap(), ); } + +#[test] +fn test_n_rows_required() { + assert!(*HASH_SCHEME_DONE); + let mut generator = WitnessGenerator::from(&ZktrieState::default()); + generator.handle_new_state( + mpt_zktrie::mpt_circuits::MPTProofType::BalanceChanged, + Address::repeat_byte(1), + U256::from(23), + U256::zero(), + None, + ); + + let trace = generator.handle_new_state( + mpt_zktrie::mpt_circuits::MPTProofType::AccountDoesNotExist, + Address::repeat_byte(2), + U256::zero(), + U256::zero(), + None, + ); + let json = serde_json::to_string_pretty(&trace).unwrap(); + let trace: SMTTrace = serde_json::from_str(&json).unwrap(); + + let witness = vec![(MPTProofType::AccountDoesNotExist, trace); 3000]; + let proofs: Vec<_> = witness.clone().into_iter().map(Proof::from).collect(); + + let n_rows_required = MptCircuitConfig::n_rows_required(&proofs); + + let circuit = TestCircuit::new(n_rows_required, witness); + let prover = MockProver::::run(14, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); +}