diff --git a/Cargo.lock b/Cargo.lock index 6cb77dbf66..97c56ec1ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if 1.0.0", "cipher", @@ -43,14 +43,21 @@ name = "aggregator" version = "0.1.0" dependencies = [ "ark-std 0.3.0", + "c-kzg", "env_logger", "eth-types", "ethers-core", + "halo2-base", + "halo2-ecc", "halo2_proofs", "hex", "itertools 0.11.0", "log", + "num-bigint", + "once_cell", "rand", + "revm-precompile", + "revm-primitives 3.1.0", "serde", "serde_json", "snark-verifier", @@ -60,9 +67,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", "once_cell", @@ -72,22 +79,49 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "alloy-primitives" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if 1.0.0", + "const-hex", + "derive_more", + "hex-literal 0.4.1", + "itoa", + "k256 0.13.3", + "keccak-asm", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] + [[package]] name = "alloy-rlp" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ "arrayvec", "bytes", - "smol_str", ] [[package]] @@ -161,15 +195,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "ark-ff" @@ -325,13 +359,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -347,14 +381,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] @@ -365,9 +398,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -398,9 +431,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -446,9 +479,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" @@ -507,11 +540,36 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "git+https://github.com/scroll-tech/bls12_381?branch=feat/impl_scalar_field#2c515f73a2462fef8681c8e884edf1710f52b22a" +dependencies = [ + "ff 0.13.0", + "group 0.13.0", + "pairing", + "pasta_curves", + "rand_core", + "subtle", +] + +[[package]] +name = "blst" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "sha2", "tinyvec", @@ -519,9 +577,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bus-mapping" @@ -562,9 +620,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -574,9 +632,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] @@ -602,6 +660,20 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c-kzg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3130f3d8717cc02e668a896af24984d5d5d4e8bf12e278e982e0f1bd88a0f9af" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "serde", +] + [[package]] name = "camino" version = "1.1.6" @@ -613,9 +685,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -628,7 +700,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.20", + "semver 1.0.22", "serde", "serde_json", "thiserror", @@ -664,16 +736,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] @@ -738,9 +810,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.2" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" dependencies = [ "clap_builder", "clap_derive", @@ -760,14 +832,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -799,15 +871,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - [[package]] name = "coins-bip32" version = "0.8.7" @@ -818,7 +881,7 @@ dependencies = [ "coins-core", "digest 0.10.7", "hmac", - "k256 0.13.1", + "k256 0.13.3", "serde", "sha2", "thiserror", @@ -846,7 +909,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bech32", "bs58", "digest 0.10.7", @@ -874,11 +937,10 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "is-terminal", "lazy_static", "windows-sys 0.48.0", ] @@ -889,11 +951,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" +[[package]] +name = "const-hex" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "hex", + "proptest", + "serde", +] + [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" @@ -915,9 +990,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -925,9 +1000,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-graphics" @@ -944,9 +1019,9 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -967,18 +1042,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if 1.0.0", ] @@ -1021,11 +1096,10 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ - "cfg-if 1.0.0", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -1035,56 +1109,46 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if 1.0.0", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if 1.0.0", "crossbeam-utils", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if 1.0.0", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -1106,9 +1170,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f85c3514d2a6e64160359b45a3918c3b4178bcbf4ae5d03ab2d02e521c479a" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core", @@ -1238,9 +1302,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der" @@ -1263,9 +1327,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] @@ -1400,6 +1464,12 @@ dependencies = [ "wio", ] +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "ecdsa" version = "0.14.8" @@ -1414,23 +1484,23 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der 0.7.8", "digest 0.10.7", - "elliptic-curve 0.13.6", + "elliptic-curve 0.13.8", "rfc6979 0.4.0", - "signature 2.1.0", + "signature 2.2.0", "spki", ] [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elliptic-curve" @@ -1453,12 +1523,12 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.6" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct 0.2.0", - "crypto-bigint 0.5.4", + "crypto-bigint 0.5.5", "digest 0.10.7", "ff 0.13.0", "generic-array", @@ -1503,7 +1573,7 @@ dependencies = [ "base64 0.13.1", "bytes", "hex", - "k256 0.13.1", + "k256 0.13.3", "log", "rand", "rlp", @@ -1514,20 +1584,20 @@ dependencies = [ [[package]] name = "enumn" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -1544,12 +1614,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1578,6 +1648,7 @@ dependencies = [ name = "eth-types" version = "0.1.0" dependencies = [ + "base64 0.13.1", "ethers-core", "ethers-signers", "halo2-base", @@ -1649,7 +1720,7 @@ dependencies = [ [[package]] name = "ethers" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -1664,7 +1735,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-core", "once_cell", @@ -1675,7 +1746,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1693,7 +1764,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "Inflector", "dunce", @@ -1708,7 +1779,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.39", + "syn 2.0.55", "toml 0.7.8", "walkdir", ] @@ -1716,7 +1787,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "Inflector", "ethers-contract-abigen", @@ -1725,23 +1796,23 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] name = "ethers-core" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "arrayvec", "bytes", "cargo_metadata", "chrono", - "elliptic-curve 0.13.6", + "elliptic-curve 0.13.8", "ethabi", "generic-array", "hex", - "k256 0.13.1", + "k256 0.13.3", "num_enum 0.6.1", "once_cell", "open-fastrlp", @@ -1750,7 +1821,7 @@ dependencies = [ "serde", "serde_json", "strum 0.24.1", - "syn 2.0.39", + "syn 2.0.55", "tempfile", "thiserror", "tiny-keccak", @@ -1760,12 +1831,12 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-core", "ethers-solc", "reqwest", - "semver 1.0.20", + "semver 1.0.22", "serde", "serde_json", "thiserror", @@ -1775,7 +1846,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "async-trait", "auto_impl", @@ -1801,11 +1872,11 @@ dependencies = [ [[package]] name = "ethers-providers" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.5", + "base64 0.21.7", "bytes", "enr", "ethers-core", @@ -1837,12 +1908,12 @@ dependencies = [ [[package]] name = "ethers-signers" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve 0.13.6", + "elliptic-curve 0.13.8", "eth-keystore", "ethers-core", "hex", @@ -1855,7 +1926,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "2.0.7" -source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7-patch-tungstenite#13d46e5b9e3f657110f030ef39d5f0f5189e803b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "cfg-if 1.0.0", "dunce", @@ -1869,7 +1940,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.20", + "semver 1.0.22", "serde", "serde_json", "solang-parser", @@ -1896,9 +1967,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -1906,9 +1977,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fastrlp" @@ -1923,9 +1994,9 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -2033,18 +2104,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "freetype" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +checksum = "efc8599a3078adf8edeb86c71e9f8fa7d88af5ca31e806a867756081f90f5d83" dependencies = [ "freetype-sys", "libc", @@ -2052,11 +2123,11 @@ dependencies = [ [[package]] name = "freetype-sys" -version = "0.13.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +checksum = "66ee28c39a43d89fbed8b4798fb4ba56722cfd2b5af81f9326c27614ba88ecd5" dependencies = [ - "cmake", + "cc", "libc", "pkg-config", ] @@ -2079,9 +2150,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -2094,9 +2165,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -2104,15 +2175,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -2121,9 +2192,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-locks" @@ -2137,32 +2208,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" dependencies = [ "gloo-timers", "send_wrapper 0.4.0", @@ -2170,9 +2241,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -2229,9 +2300,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2250,30 +2321,28 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git-version" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899" +checksum = "1ad568aa3db0fcbc81f2f116137f263d7304f512a1209b35b85150d3ef88ad19" dependencies = [ "git-version-macro", - "proc-macro-hack", ] [[package]] name = "git-version-macro" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" +checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ - "proc-macro-hack", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] @@ -2326,9 +2395,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -2356,7 +2425,7 @@ dependencies = [ [[package]] name = "halo2-base" version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#40ba7e3bbf013b55c59283534c9489701f9212d0" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#817cace374a9f4b2eca682b1cc36f143255ea25f" dependencies = [ "ff 0.13.0", "halo2_proofs", @@ -2371,7 +2440,7 @@ dependencies = [ [[package]] name = "halo2-ecc" version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#40ba7e3bbf013b55c59283534c9489701f9212d0" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#817cace374a9f4b2eca682b1cc36f143255ea25f" dependencies = [ "ff 0.13.0", "group 0.13.0", @@ -2430,7 +2499,7 @@ dependencies = [ [[package]] name = "halo2_gadgets" version = "1.1.0" -source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.1#909d6477f7c9aa009433e226e88d7a4be501582a" +source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.1#7179a60e4b4b1dafff084deac7b4bea235eecf5f" dependencies = [ "arrayvec", "bitvec", @@ -2447,7 +2516,7 @@ dependencies = [ [[package]] name = "halo2_proofs" version = "1.1.0" -source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.1#c90df713350ce1c9a032a1499ee56787ec25f700" +source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.1#7179a60e4b4b1dafff084deac7b4bea235eecf5f" dependencies = [ "ark-std 0.3.0", "blake2b_simd", @@ -2474,13 +2543,14 @@ dependencies = [ [[package]] name = "halo2curves" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b1142bd1059aacde1b477e0c80c142910f1ceae67fc619311d6a17428007ab" +source = "git+https://github.com/scroll-tech/halo2curves?branch=v0.1.0#112f5b9bf27f6b1708ba7d1c2fc14cb3c6e55604" dependencies = [ "blake2b_simd", + "bls12_381", "ff 0.13.0", "group 0.13.0", "lazy_static", + "maybe-rayon", "num-bigint", "num-traits", "pasta_curves", @@ -2518,9 +2588,13 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hashers" @@ -2537,11 +2611,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -2555,6 +2635,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hmac" version = "0.12.1" @@ -2566,18 +2652,18 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -2586,9 +2672,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -2615,9 +2701,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -2630,7 +2716,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -2653,9 +2739,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2682,9 +2768,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -2692,15 +2778,14 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", "jpeg-decoder", - "num-rational", "num-traits", "png", ] @@ -2751,12 +2836,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -2810,13 +2895,13 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", - "rustix", - "windows-sys 0.48.0", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -2848,9 +2933,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" @@ -2863,15 +2948,15 @@ dependencies = [ [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -2891,27 +2976,37 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if 1.0.0", - "ecdsa 0.16.8", - "elliptic-curve 0.13.6", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2", - "signature 2.1.0", + "signature 2.2.0", ] [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "keccak256" version = "0.1.0" @@ -2967,18 +3062,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if 1.0.0", - "windows-sys 0.48.0", + "windows-targets 0.52.4", ] [[package]] @@ -2993,7 +3088,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "libc", "redox_syscall", ] @@ -3006,9 +3101,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -3022,9 +3117,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "log-mdc" @@ -3034,9 +3129,9 @@ checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" [[package]] name = "log4rs" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36ca1786d9e79b8193a68d480a0907b612f109537115c6ff655a3a1967533fd" +checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" dependencies = [ "anyhow", "arc-swap", @@ -3046,6 +3141,7 @@ dependencies = [ "libc", "log", "log-mdc", + "once_cell", "parking_lot", "thiserror", "thread-id", @@ -3074,18 +3170,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -3095,9 +3182,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -3163,9 +3250,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "num" @@ -3195,28 +3282,33 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -3237,9 +3329,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -3279,7 +3371,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -3291,26 +3383,26 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -3320,9 +3412,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open-fastrlp" @@ -3349,11 +3441,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group 0.13.0", +] + [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", "bitvec", @@ -3365,11 +3466,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 1.0.109", @@ -3479,15 +3580,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ "memchr", "thiserror", @@ -3496,9 +3597,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" dependencies = [ "pest", "pest_generator", @@ -3506,22 +3607,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" dependencies = [ "once_cell", "pest", @@ -3578,7 +3679,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -3601,22 +3702,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -3643,9 +3744,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" @@ -3695,9 +3796,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -3760,12 +3861,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -3803,44 +3904,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-crate" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", + "toml_edit 0.20.7", ] -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -3851,13 +3931,17 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ - "bitflags 2.4.1", + "bit-set", + "bit-vec", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax 0.8.2", + "rusty-fork", + "tempfile", "unarray", ] @@ -3903,11 +3987,17 @@ dependencies = [ "cc", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -3959,9 +4049,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -3969,9 +4059,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -3999,9 +4089,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -4011,9 +4101,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -4034,11 +4124,11 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -4060,6 +4150,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls", @@ -4068,7 +4159,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] @@ -4097,7 +4188,7 @@ dependencies = [ "k256 0.11.6", "num", "once_cell", - "revm-primitives", + "revm-primitives 1.0.0", "ripemd", "secp256k1 0.26.0", "sha2", @@ -4117,12 +4208,33 @@ dependencies = [ "fixed-hash", "hashbrown 0.13.2", "hex", - "hex-literal", + "hex-literal 0.3.4", "rlp", "ruint", "sha3 0.10.8", ] +[[package]] +name = "revm-primitives" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323ad597cf75ac9cb1d161be29fcc3562426f0278a1d04741697fca556e1ceea" +dependencies = [ + "alloy-primitives", + "auto_impl", + "bitflags 2.5.0", + "bitvec", + "c-kzg", + "cfg-if 1.0.0", + "derive_more", + "dyn-clone", + "enumn", + "hashbrown 0.14.3", + "hex", + "once_cell", + "serde", +] + [[package]] name = "revm_precompiles" version = "1.1.2" @@ -4164,16 +4276,32 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if 1.0.0", "getrandom", "libc", "spin 0.9.8", - "untrusted", - "windows-sys 0.48.0", + "untrusted 0.9.0", + "windows-sys 0.52.0", ] [[package]] @@ -4209,9 +4337,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "724fd11728a3804e9944b14cab63825024c40bf42f8af87c8b5d97c4bbacf426" +checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -4233,9 +4361,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" [[package]] name = "rustc-demangle" @@ -4270,31 +4398,31 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.20", + "semver 1.0.22", ] [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.8" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring", - "rustls-webpki", + "ring 0.17.8", + "rustls-webpki 0.101.7", "sct", ] @@ -4304,7 +4432,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -4313,8 +4451,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -4323,11 +4461,23 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "salsa20" @@ -4349,9 +4499,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "2ef2175c2907e7c8bc0a9c3f86aeb5ec1f3b275300ad58a44d0c3ae379a5e52e" dependencies = [ "cfg-if 1.0.0", "derive_more", @@ -4361,11 +4511,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "634d9b8eb8fd61c5cdd3390d9b2132300a7e7618955b98b8416f118c1b4e144f" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -4395,8 +4545,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -4473,9 +4623,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] @@ -4503,9 +4653,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -4521,20 +4671,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -4543,18 +4693,18 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] [[package]] name = "serde_stacker" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f5321e680f77e7b5cfccc78708ff86a814d39aba030610aee67bd5eaf8a1c30" +checksum = "babfccff5773ff80657f0ecf553c7c516bdc2eb16389c0918b36b73e7015276e" dependencies = [ "serde", "stacker", @@ -4638,6 +4788,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +dependencies = [ + "cc", + "cfg-if 1.0.0", +] + [[package]] name = "signature" version = "1.6.4" @@ -4650,9 +4810,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core", @@ -4681,18 +4841,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snark-verifier" @@ -4741,22 +4892,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4787,9 +4928,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der 0.7.8", @@ -4866,7 +5007,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -4879,11 +5020,11 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -4916,7 +5057,7 @@ dependencies = [ "home", "once_cell", "reqwest", - "semver 1.0.20", + "semver 1.0.22", "serde", "serde_json", "sha2", @@ -4938,15 +5079,21 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -4987,15 +5134,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5011,9 +5157,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -5058,22 +5204,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -5086,13 +5232,23 @@ dependencies = [ "winapi", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "time" -version = "0.3.30" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", + "num-conv", "powerfmt", "serde", "time-core", @@ -5140,9 +5296,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -5150,7 +5306,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] @@ -5163,7 +5319,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -5178,9 +5334,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" dependencies = [ "futures-util", "log", @@ -5188,7 +5344,7 @@ dependencies = [ "tokio", "tokio-rustls", "tungstenite", - "webpki-roots", + "webpki-roots 0.23.1", ] [[package]] @@ -5223,7 +5379,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] @@ -5248,6 +5404,17 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -5273,7 +5440,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] @@ -5297,9 +5464,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ttf-parser" @@ -5309,9 +5476,9 @@ checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" dependencies = [ "byteorder", "bytes", @@ -5325,6 +5492,7 @@ dependencies = [ "thiserror", "url", "utf-8", + "webpki", ] [[package]] @@ -5359,9 +5527,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -5371,9 +5539,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -5390,6 +5558,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -5398,9 +5572,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -5447,11 +5621,20 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -5493,15 +5676,15 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5527,7 +5710,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5540,25 +5723,44 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "webpki-roots" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" +dependencies = [ + "rustls-webpki 0.100.3", +] + [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "winapi" @@ -5593,11 +5795,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] @@ -5734,9 +5936,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -5832,14 +6034,14 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -5852,7 +6054,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.55", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 54fb63b8ad..1401f77212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,10 +25,11 @@ license = "MIT OR Apache-2.0" [workspace.dependencies] anyhow = "1.0" ark-std = "0.3" +base64 = "0.13.0" ctor = "0.1" env_logger = "0.10" ethers = { version = "=2.0.7", features = ["ethers-solc"] } -ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7-patch-tungstenite", features = ["scroll"] } +ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7", features = ["scroll"] } ethers-providers = "=2.0.7" ethers-signers = "=2.0.7" ff = "0.13" @@ -44,6 +45,7 @@ num = "0.4" num-bigint = "0.4" num-traits = "0.2" pretty_assertions = "1.0" +once_cell = "1.17" rand = "0.8" rand_chacha = "0.3" rand_xorshift = "0.3" @@ -60,19 +62,27 @@ strum_macros = "0.25" subtle = "2.4" tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] } url = "2.2" +revm-precompile = { git = "https://github.com/scroll-tech/revm", branch = "scroll-fix" } +c-kzg = "1.0.0" [patch.crates-io] -ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7-patch-tungstenite" } -ethers-providers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7-patch-tungstenite" } -ethers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7-patch-tungstenite" } -ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7-patch-tungstenite" } -ethers-signers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7-patch-tungstenite" } +ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } +ethers-providers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } +ethers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } +ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } +ethers-signers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } gobuild = { git = "https://github.com/scroll-tech/gobuild.git" } +halo2curves = { git = "https://github.com/scroll-tech/halo2curves", branch = "v0.1.0" } + [patch."https://github.com/privacy-scaling-explorations/halo2.git"] halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.1" } [patch."https://github.com/privacy-scaling-explorations/poseidon.git"] poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "main" } +[patch."https://github.com/privacy-scaling-explorations/bls12_381"] +bls12_381 = { git = "https://github.com/scroll-tech/bls12_381", branch = "feat/impl_scalar_field" } + + # Definition of benchmarks profile to use. [profile.bench] diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index 50d158d135..e82a9d331c 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -10,21 +10,27 @@ license.workspace = true eth-types = { path = "../eth-types" } zkevm-circuits = { path = "../zkevm-circuits" } - ark-std.workspace = true env_logger.workspace = true ethers-core.workspace = true hex.workspace = true log.workspace = true itertools.workspace = true +once_cell.workspace = true serde.workspace = true serde_json.workspace = true rand.workspace = true - +halo2-base.workspace = true +halo2-ecc.workspace = true halo2_proofs.workspace = true snark-verifier.workspace = true snark-verifier-sdk.workspace = true +revm-precompile.workspace = true +c-kzg.workspace = true +num-bigint.workspace = true + +revm-primitives = "3.1.0" [features] default = [ ] diff --git a/aggregator/src/aggregation.rs b/aggregator/src/aggregation.rs index 4af10408c1..04051b8f7a 100644 --- a/aggregator/src/aggregation.rs +++ b/aggregator/src/aggregation.rs @@ -1,3 +1,7 @@ +/// Config to evaluate blob polynomial at a random challenge. +mod barycentric; +/// Config to constrain blob data +mod blob_data; /// Circuit implementation of aggregation circuit. mod circuit; /// Config for aggregation circuit @@ -5,6 +9,10 @@ mod config; /// config for RLC circuit mod rlc; +pub(crate) use barycentric::{ + interpolate, AssignedBarycentricEvaluationConfig, BarycentricEvaluationConfig, BLS_MODULUS, +}; +pub(crate) use blob_data::BlobDataConfig; pub use circuit::AggregationCircuit; pub use config::AggregationConfig; pub(crate) use rlc::RlcConfig; diff --git a/aggregator/src/aggregation/barycentric.rs b/aggregator/src/aggregation/barycentric.rs new file mode 100644 index 0000000000..902dabda0a --- /dev/null +++ b/aggregator/src/aggregation/barycentric.rs @@ -0,0 +1,460 @@ +use eth_types::{ToLittleEndian, U256}; +use halo2_base::{ + gates::{range::RangeConfig, GateInstructions}, + utils::{fe_to_biguint, modulus}, + AssignedValue, QuantumCell, +}; +use halo2_ecc::{ + bigint::{CRTInteger, OverflowInteger}, + fields::{fp::FpConfig, FieldChip}, + halo2_base::{utils::decompose_bigint_option, Context}, +}; +use halo2_proofs::{ + circuit::Value, + halo2curves::{bls12_381::Scalar, bn256::Fr, ff::PrimeField}, +}; +use itertools::Itertools; +use num_bigint::{BigInt, Sign}; +use std::{iter::successors, sync::LazyLock}; + +use crate::{ + blob::BLOB_WIDTH, + constants::{BITS, LIMBS}, +}; + +/// Base 2 logarithm of BLOB_WIDTH. +const LOG_BLOB_WIDTH: usize = 12; + +pub static BLS_MODULUS: LazyLock = LazyLock::new(|| { + U256::from_str_radix(Scalar::MODULUS, 16).expect("BLS_MODULUS from bls crate") +}); + +pub static ROOTS_OF_UNITY: LazyLock> = LazyLock::new(|| { + // https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/polynomial-commitments.md#constants + let primitive_root_of_unity = Scalar::from(7); + let modulus = *BLS_MODULUS; + + let exponent = (modulus - U256::one()) / U256::from(4096); + let root_of_unity = primitive_root_of_unity.pow(&exponent.0); + + let ascending_order: Vec<_> = successors(Some(Scalar::one()), |x| Some(*x * root_of_unity)) + .take(BLOB_WIDTH) + .collect(); + (0..BLOB_WIDTH) + .map(|i| { + let j = u16::try_from(i).unwrap().reverse_bits() >> (16 - LOG_BLOB_WIDTH); + ascending_order[usize::from(j)] + }) + .collect() +}); + +#[derive(Clone, Debug)] +pub struct BarycentricEvaluationConfig { + pub scalar: FpConfig, +} + +#[derive(Default)] +pub struct AssignedBarycentricEvaluationConfig { + /// CRTIntegers for the BLOB_WIDTH number of blob polynomial coefficients, followed by a + /// CRTInteger for the challenge digest. + pub(crate) barycentric_assignments: Vec>, + /// 32 Assigned cells representing the LE-bytes of challenge z. + pub(crate) z_le: Vec>, + /// 32 Assigned cells representing the LE-bytes of evaluation y. + pub(crate) y_le: Vec>, +} + +impl BarycentricEvaluationConfig { + pub fn construct(range: RangeConfig) -> Self { + Self { + scalar: FpConfig::construct(range, BITS, LIMBS, modulus::()), + } + } + + fn load_u256(&self, ctx: &mut Context, a: U256) -> CRTInteger { + // borrowed from halo2-ecc/src/fields/fp.rs + // similar to FpChip.load_private without range check. + + let a_val = Value::known(BigInt::from_bytes_le(Sign::Plus, &a.to_le_bytes())); + let a_vec = decompose_bigint_option::( + a_val.as_ref(), + self.scalar.num_limbs, + self.scalar.limb_bits, + ); + let limbs = self.scalar.range().gate.assign_witnesses(ctx, a_vec); + + let a_native = OverflowInteger::::evaluate( + &self.scalar.range().gate, + //&self.bigint_chip, + ctx, + &limbs, + self.scalar.limb_bases.iter().cloned(), + ); + + CRTInteger::construct( + OverflowInteger::construct(limbs, self.scalar.limb_bits), + a_native, + a_val, + ) + } + + pub fn assign( + &self, + ctx: &mut Context, + blob: &[U256; BLOB_WIDTH], + challenge_digest: U256, + evaluation: U256, + ) -> AssignedBarycentricEvaluationConfig { + // some constants for later use. + let one = self.scalar.load_constant(ctx, fe_to_biguint(&Fr::one())); + let blob_width = self + .scalar + .load_constant(ctx, fe_to_biguint(&Fr::from(BLOB_WIDTH as u64))); + + let powers_of_256 = + std::iter::successors(Some(Fr::one()), |coeff| Some(Fr::from(256) * coeff)) + .take(11) + .map(QuantumCell::Constant) + .collect::>(); + + let roots_of_unity = ROOTS_OF_UNITY + .iter() + .map(|x| self.scalar.load_constant(ctx, fe_to_biguint(x))) + .collect::>(); + + //////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////// PRECHECKS z ///////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + + let (_, challenge) = challenge_digest.div_mod(*BLS_MODULUS); + let challenge_scalar = Scalar::from_raw(challenge.0); + + let challenge_digest_crt = self.load_u256(ctx, challenge_digest); + let challenge_le = self.scalar.range().gate.assign_witnesses( + ctx, + challenge + .to_le_bytes() + .iter() + .map(|&x| Value::known(Fr::from(x as u64))), + ); + let challenge_digest_mod = self.scalar.carry_mod(ctx, &challenge_digest_crt); + let challenge_crt = self + .scalar + .load_private(ctx, Value::known(fe_to_biguint(&challenge_scalar).into())); + self.scalar + .assert_equal(ctx, &challenge_digest_mod, &challenge_crt); + let challenge_limb1 = self.scalar.range().gate.inner_product( + ctx, + challenge_le[0..11] + .iter() + .map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + let challenge_limb2 = self.scalar.range().gate.inner_product( + ctx, + challenge_le[11..22] + .iter() + .map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + let challenge_limb3 = self.scalar.range().gate.inner_product( + ctx, + challenge_le[22..32] + .iter() + .map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(challenge_limb1), + QuantumCell::Existing(challenge_crt.truncation.limbs[0]), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(challenge_limb2), + QuantumCell::Existing(challenge_crt.truncation.limbs[1]), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(challenge_limb3), + QuantumCell::Existing(challenge_crt.truncation.limbs[2]), + ); + + //////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////// PRECHECKS y ///////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + let evaluation_le = self.scalar.range().gate.assign_witnesses( + ctx, + evaluation + .to_le_bytes() + .iter() + .map(|&x| Value::known(Fr::from(x as u64))), + ); + let evaluation_scalar = Scalar::from_raw(evaluation.0); + let evaluation_crt = self + .scalar + .load_private(ctx, Value::known(fe_to_biguint(&evaluation_scalar).into())); + let evaluation_limb1 = self.scalar.range().gate.inner_product( + ctx, + evaluation_le[0..11] + .iter() + .map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + let evaluation_limb2 = self.scalar.range().gate.inner_product( + ctx, + evaluation_le[11..22] + .iter() + .map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + let evaluation_limb3 = self.scalar.range().gate.inner_product( + ctx, + evaluation_le[22..32] + .iter() + .map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(evaluation_limb1), + QuantumCell::Existing(evaluation_crt.truncation.limbs[0]), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(evaluation_limb2), + QuantumCell::Existing(evaluation_crt.truncation.limbs[1]), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(evaluation_limb3), + QuantumCell::Existing(evaluation_crt.truncation.limbs[2]), + ); + + //////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////// BARYCENTRIC EVALUATION ////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + let mut blob_crts = Vec::with_capacity(BLOB_WIDTH); + let mut evaluation_computed = self.scalar.load_constant(ctx, fe_to_biguint(&Fr::zero())); + blob.iter() + .zip_eq(roots_of_unity.iter()) + .for_each(|(blob_i, root_i_crt)| { + // assign LE-bytes of blob scalar field element. + let blob_i_le = self.scalar.range().gate.assign_witnesses( + ctx, + blob_i + .to_le_bytes() + .iter() + .map(|&x| Value::known(Fr::from(x as u64))), + ); + let blob_i_scalar = Scalar::from_raw(blob_i.0); + let blob_i_crt = self + .scalar + .load_private(ctx, Value::known(fe_to_biguint(&blob_i_scalar).into())); + + // compute the limbs for blob scalar field element. + let limb1 = self.scalar.range().gate.inner_product( + ctx, + blob_i_le[0..11].iter().map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + let limb2 = self.scalar.range().gate.inner_product( + ctx, + blob_i_le[11..22].iter().map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + let limb3 = self.scalar.range().gate.inner_product( + ctx, + blob_i_le[22..32].iter().map(|&x| QuantumCell::Existing(x)), + powers_of_256[0..11].to_vec(), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(limb1), + QuantumCell::Existing(blob_i_crt.truncation.limbs[0]), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(limb2), + QuantumCell::Existing(blob_i_crt.truncation.limbs[1]), + ); + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(limb3), + QuantumCell::Existing(blob_i_crt.truncation.limbs[2]), + ); + + // the most-significant byte of blob scalar field element is 0 as we expect this + // representation to be in its canonical form. + self.scalar.range().gate.assert_equal( + ctx, + QuantumCell::Existing(blob_i_le[31]), + QuantumCell::Constant(Fr::zero()), + ); + + // a = int(polynomial[i]) * int(roots_of_unity_brp[i]) % BLS_MODULUS + let a = self.scalar.mul(ctx, &blob_i_crt, root_i_crt); + + // b = (int(BLS_MODULUS) + int(z) - int(roots_of_unity_brp[i])) % BLS_MODULUS + let b = self.scalar.sub_no_carry(ctx, &challenge_crt, root_i_crt); + let b = self.scalar.carry_mod(ctx, &b); + + // y += int(div(a, b) % BLS_MODULUS) + let a_by_b = self.scalar.divide(ctx, &a, &b); + evaluation_computed = self.scalar.add_no_carry(ctx, &evaluation_computed, &a_by_b); + evaluation_computed = self.scalar.carry_mod(ctx, &evaluation_computed); + blob_crts.push(blob_i_crt); + }); + + let z_to_blob_width = (0..LOG_BLOB_WIDTH).fold(challenge_crt.clone(), |acc, _| { + self.scalar.mul(ctx, &acc, &acc) + }); + let z_to_blob_width_minus_one = self.scalar.sub_no_carry(ctx, &z_to_blob_width, &one); + let z_to_blob_width_minus_one = self.scalar.carry_mod(ctx, &z_to_blob_width_minus_one); + let factor = self + .scalar + .divide(ctx, &z_to_blob_width_minus_one, &blob_width); + evaluation_computed = self.scalar.mul(ctx, &evaluation_computed, &factor); + evaluation_computed = self.scalar.carry_mod(ctx, &evaluation_computed); + + // computed evaluation matches the expected evaluation. + self.scalar + .assert_equal(ctx, &evaluation_computed, &evaluation_crt); + + //////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////// EXPORT ////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + AssignedBarycentricEvaluationConfig { + barycentric_assignments: blob_crts + .into_iter() + .chain(std::iter::once(challenge_digest_crt)) + .collect(), + z_le: challenge_le, + y_le: evaluation_le, + } + } +} + +pub fn interpolate(z: Scalar, coefficients: &[Scalar; BLOB_WIDTH]) -> Scalar { + let blob_width = u64::try_from(BLOB_WIDTH).unwrap(); + (z.pow(&[blob_width, 0, 0, 0]) - Scalar::one()) + * ROOTS_OF_UNITY + .iter() + .zip_eq(coefficients) + .map(|(root, f)| f * root * (z - root).invert().unwrap()) + .sum::() + * Scalar::from(blob_width).invert().unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::blob::BlobData; + use c_kzg::{Blob as RethBlob, KzgProof, KzgSettings}; + use once_cell::sync::Lazy; + use std::{collections::BTreeSet, sync::Arc}; + + /// KZG trusted setup + pub static MAINNET_KZG_TRUSTED_SETUP: Lazy> = Lazy::new(|| { + Arc::new( + c_kzg::KzgSettings::load_trusted_setup( + &revm_primitives::kzg::G1_POINTS.0, + &revm_primitives::kzg::G2_POINTS.0, + ) + .expect("failed to load trusted setup"), + ) + }); + + #[test] + fn log_blob_width() { + assert_eq!(2_usize.pow(LOG_BLOB_WIDTH.try_into().unwrap()), BLOB_WIDTH); + } + + #[test] + fn scalar_field_modulus() { + let bls_modulus = *BLS_MODULUS; + // BLS_MODULUS as decimal string from https://eips.ethereum.org/EIPS/eip-4844. + let expected_bls_modulus = U256::from_str_radix( + "52435875175126190479447740508185965837690552500527637822603658699938581184513", + 10, + ) + .unwrap(); + assert_eq!(bls_modulus, expected_bls_modulus); + } + + #[test] + fn roots_of_unity() { + for root_of_unity in ROOTS_OF_UNITY.iter() { + assert_eq!( + root_of_unity.pow(&[BLOB_WIDTH.try_into().unwrap(), 0, 0, 0]), + Scalar::one() + ); + } + assert_eq!( + ROOTS_OF_UNITY.iter().collect::>().len(), + BLOB_WIDTH + ); + } + + #[test] + fn interpolate_matches_reth_implementation() { + let blob = BlobData::from(&vec![ + vec![30; 56], + vec![200; 100], + vec![0; 340], + vec![10; 23], + ]); + + for z in 0..10 { + let z = Scalar::from(u64::try_from(13241234 + z).unwrap()); + assert_eq!( + reth_point_evaluation(z, &blob.get_coefficients().map(|c| Scalar::from_raw(c.0))), + interpolate(z, &blob.get_coefficients().map(|c| Scalar::from_raw(c.0))) + ); + } + } + + fn reth_point_evaluation(z: Scalar, coefficients: &[Scalar]) -> Scalar { + assert_eq!(coefficients.len(), BLOB_WIDTH); + let blob = RethBlob::from_bytes( + &coefficients + .iter() + .cloned() + .flat_map(to_be_bytes) + .collect::>(), + ) + .unwrap(); + let (_proof, y) = + KzgProof::compute_kzg_proof(&blob, &to_be_bytes(z).into(), &MAINNET_KZG_TRUSTED_SETUP) + .unwrap(); + from_canonical_be_bytes(*y) + } + + #[test] + fn reth_kzg_implementation() { + // check that we are calling the reth implementation correctly + for z in 0..10 { + let z = Scalar::from(u64::try_from(z).unwrap()); + assert_eq!(reth_point_evaluation(z, &ROOTS_OF_UNITY), z) + } + } + + fn to_be_bytes(x: Scalar) -> [u8; 32] { + let mut bytes = x.to_bytes(); + bytes.reverse(); + bytes + } + + fn from_canonical_be_bytes(bytes: [u8; 32]) -> Scalar { + let mut bytes = bytes; + bytes.reverse(); + Scalar::from_bytes(&bytes).expect("non-canonical bytes") + } + + #[test] + fn test_be_bytes() { + let mut be_bytes_one = [0; 32]; + be_bytes_one[31] = 1; + assert_eq!(to_be_bytes(Scalar::one()), be_bytes_one); + } +} diff --git a/aggregator/src/aggregation/blob_data.rs b/aggregator/src/aggregation/blob_data.rs new file mode 100644 index 0000000000..81d0358cbe --- /dev/null +++ b/aggregator/src/aggregation/blob_data.rs @@ -0,0 +1,965 @@ +use ethers_core::utils::keccak256; +use halo2_ecc::bigint::CRTInteger; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Value}, + halo2curves::bn256::Fr, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, SecondPhase, Selector}, + poly::Rotation, +}; +use itertools::Itertools; +use zkevm_circuits::{ + table::{KeccakTable, LookupTable, RangeTable, U8Table}, + util::{Challenges, Expr}, +}; + +use crate::{ + aggregation::rlc::POWS_OF_256, + blob::{ + BlobData, BLOB_WIDTH, N_BYTES_U256, N_DATA_BYTES_PER_COEFFICIENT, N_ROWS_BLOB_DATA_CONFIG, + N_ROWS_DATA, N_ROWS_DIGEST_BYTES, N_ROWS_DIGEST_RLC, N_ROWS_METADATA, + }, + RlcConfig, MAX_AGG_SNARKS, +}; + +#[derive(Clone, Debug)] +pub struct BlobDataConfig { + /// The byte value at this row. + byte: Column, + /// The accumulator serves several purposes. + /// 1. For the metadata section, the accumulator holds the running linear combination of the + /// chunk size. + /// 2. For the chunk data section, the accumulator holds the incremental chunk size, which + /// resets to 1 if we encounter a chunk boundary. The accumulator here is referenced while + /// doing a lookup to the Keccak table that requires the input length. + accumulator: Column, + /// An increasing counter that denotes the chunk ID. The chunk ID is from [1, MAX_AGG_SNARKS]. + chunk_idx: Column, + /// A boolean witness that is set only when we encounter the end of a chunk. We enable a lookup + /// to the Keccak table when the boundary is met. + is_boundary: Column, + /// A running accumulator of the boundary counts. + boundary_count: Column, + /// A boolean witness to indicate padded rows at the end of the data section. + is_padding: Column, + /// Represents the running random linear combination of bytes seen so far, that are a part of + /// the preimage to the Keccak hash. It resets whenever we encounter a chunk boundary. + preimage_rlc: Column, + /// Represents the random linear combination of the Keccak digest. This has meaningful values + /// only at the rows where we actually do the Keccak lookup. + digest_rlc: Column, + /// Boolean to let us know we are in the data section. + pub data_selector: Selector, + /// Boolean to let us know we are in the hash section. + pub hash_selector: Selector, + /// Fixed table that consists of [0, 256). + u8_table: U8Table, + /// Fixed table that consists of [0, MAX_AGG_SNARKS). + chunk_idx_range_table: RangeTable, +} + +pub struct AssignedBlobDataExport { + pub num_valid_chunks: AssignedCell, + pub challenge_digest: Vec>, + pub chunk_data_digests: Vec>>, +} + +struct AssignedBlobDataConfig { + pub byte: AssignedCell, + pub accumulator: AssignedCell, + pub chunk_idx: AssignedCell, + pub is_boundary: AssignedCell, + pub boundary_count: AssignedCell, + pub is_padding: AssignedCell, + pub preimage_rlc: AssignedCell, + pub digest_rlc: AssignedCell, +} + +impl BlobDataConfig { + pub fn configure( + meta: &mut ConstraintSystem, + challenge: Challenges>, + u8_table: U8Table, + range_table: RangeTable, + keccak_table: &KeccakTable, + ) -> Self { + let config = Self { + u8_table, + chunk_idx_range_table: range_table, + byte: meta.advice_column(), + accumulator: meta.advice_column(), + is_boundary: meta.advice_column(), + boundary_count: meta.advice_column(), + chunk_idx: meta.advice_column(), + is_padding: meta.advice_column(), + preimage_rlc: meta.advice_column_in(SecondPhase), + digest_rlc: meta.advice_column_in(SecondPhase), + data_selector: meta.complex_selector(), + hash_selector: meta.complex_selector(), + }; + + // TODO: reduce the number of permutation columns + meta.enable_equality(config.byte); + meta.enable_equality(config.accumulator); + meta.enable_equality(config.is_boundary); + meta.enable_equality(config.boundary_count); + meta.enable_equality(config.is_padding); + meta.enable_equality(config.chunk_idx); + meta.enable_equality(config.preimage_rlc); + meta.enable_equality(config.digest_rlc); + + let r = challenge.keccak_input(); + + meta.lookup("BlobDataConfig (0 < byte < 256)", |meta| { + let byte_value = meta.query_advice(config.byte, Rotation::cur()); + vec![(byte_value, u8_table.into())] + }); + + meta.lookup( + "BlobDataConfig (chunk idx transition on boundary)", + |meta| { + let is_hash = meta.query_selector(config.hash_selector); + let is_not_hash = 1.expr() - is_hash; + let is_padding_next = meta.query_advice(config.is_padding, Rotation::next()); + let is_boundary = meta.query_advice(config.is_boundary, Rotation::cur()); + // if we are in the data section, encounter a boundary and the next row is not a + // padding row. + let cond = is_not_hash * is_boundary * (1.expr() - is_padding_next); + let chunk_idx_curr = meta.query_advice(config.chunk_idx, Rotation::cur()); + let chunk_idx_next = meta.query_advice(config.chunk_idx, Rotation::next()); + // chunk_idx increases by at least 1 and at most MAX_AGG_SNARKS when condition is + // met. + vec![( + cond * (chunk_idx_next - chunk_idx_curr - 1.expr()), + config.chunk_idx_range_table.into(), + )] + }, + ); + + meta.lookup( + "BlobDataConfig (chunk_idx for non-padding, data rows in [1..MAX_AGG_SNARKS])", + |meta| { + let is_data = meta.query_selector(config.data_selector); + let is_padding = meta.query_advice(config.is_padding, Rotation::cur()); + let chunk_idx = meta.query_advice(config.chunk_idx, Rotation::cur()); + vec![( + is_data * (1.expr() - is_padding) * (chunk_idx - 1.expr()), + config.chunk_idx_range_table.into(), + )] + }, + ); + + meta.create_gate("BlobDataConfig (transition when boundary)", |meta| { + let is_data = meta.query_selector(config.data_selector); + let is_boundary = meta.query_advice(config.is_boundary, Rotation::cur()); + let is_padding_next = meta.query_advice(config.is_padding, Rotation::next()); + + let cond = is_data * is_boundary; + + let len_next = meta.query_advice(config.accumulator, Rotation::next()); + let preimage_rlc_next = meta.query_advice(config.preimage_rlc, Rotation::next()); + let byte_next = meta.query_advice(config.byte, Rotation::next()); + + let boundary_count_curr = meta.query_advice(config.boundary_count, Rotation::cur()); + let boundary_count_prev = meta.query_advice(config.boundary_count, Rotation::prev()); + + vec![ + // if boundary followed by padding, length and preimage_rlc is 0. + cond.expr() * is_padding_next.expr() * len_next.expr(), + cond.expr() * is_padding_next.expr() * preimage_rlc_next.expr(), + // if boundary not followed by padding, length resets to 1, preimage_rlc resets to + // the byte value. + cond.expr() * (1.expr() - is_padding_next.expr()) * (len_next.expr() - 1.expr()), + cond.expr() + * (1.expr() - is_padding_next.expr()) + * (preimage_rlc_next - byte_next.expr()), + // the boundary count increments, i.e. + // boundary_count_curr == boundary_count_prev + 1 + cond.expr() * (boundary_count_curr - boundary_count_prev - 1.expr()), + ] + }); + + meta.create_gate("BlobDataConfig (transition when no boundary)", |meta| { + let is_data = meta.query_selector(config.data_selector); + let is_boundary = meta.query_advice(config.is_boundary, Rotation::cur()); + let is_padding = meta.query_advice(config.is_padding, Rotation::cur()); + + // in the data section (not padding) when we traverse the same chunk. + let cond = is_data * (1.expr() - is_padding) * (1.expr() - is_boundary); + + let chunk_idx_curr = meta.query_advice(config.chunk_idx, Rotation::cur()); + let chunk_idx_next = meta.query_advice(config.chunk_idx, Rotation::next()); + let len_curr = meta.query_advice(config.accumulator, Rotation::cur()); + let len_next = meta.query_advice(config.accumulator, Rotation::next()); + let preimage_rlc_curr = meta.query_advice(config.preimage_rlc, Rotation::cur()); + let preimage_rlc_next = meta.query_advice(config.preimage_rlc, Rotation::next()); + let byte_next = meta.query_advice(config.byte, Rotation::next()); + let boundary_count_curr = meta.query_advice(config.boundary_count, Rotation::cur()); + let boundary_count_prev = meta.query_advice(config.boundary_count, Rotation::prev()); + + vec![ + // chunk idx unchanged. + cond.expr() * (chunk_idx_next - chunk_idx_curr), + // length is accumulated. + cond.expr() * (len_next - len_curr - 1.expr()), + // preimage rlc is updated. + cond.expr() * (preimage_rlc_curr * r + byte_next - preimage_rlc_next), + // boundary count continues. + cond.expr() * (boundary_count_curr - boundary_count_prev), + ] + }); + + meta.create_gate("BlobDataConfig (\"chunk data\" section)", |meta| { + let is_data = meta.query_selector(config.data_selector); + let is_boundary = meta.query_advice(config.is_boundary, Rotation::cur()); + let is_padding_curr = meta.query_advice(config.is_padding, Rotation::cur()); + let is_padding_next = meta.query_advice(config.is_padding, Rotation::next()); + let diff = is_padding_next - is_padding_curr.expr(); + let byte = meta.query_advice(config.byte, Rotation::cur()); + let boundary_count_curr = meta.query_advice(config.boundary_count, Rotation::cur()); + let boundary_count_prev = meta.query_advice(config.boundary_count, Rotation::prev()); + + vec![ + // byte is 0 when padding in the "chunk data" section. + is_data.expr() * is_padding_curr.expr() * byte, + // is_boundary is boolean. + is_data.expr() * is_boundary.expr() * (1.expr() - is_boundary.expr()), + // is_padding is boolean. + is_data.expr() * is_padding_curr.expr() * (1.expr() - is_padding_curr.expr()), + // is_padding transitions from 0 -> 1 only once. + is_data.expr() * diff.expr() * (1.expr() - diff.expr()), + // boundary count continues if padding + is_data.expr() * is_padding_curr * (boundary_count_curr - boundary_count_prev), + ] + }); + + // lookup metadata and chunk data digests in keccak table. + meta.lookup_any( + "BlobDataConfig (metadata/chunk_data/challenge digests in keccak table)", + |meta| { + let is_data = meta.query_selector(config.data_selector); + let is_hash = meta.query_selector(config.hash_selector); + let is_not_hash = 1.expr() - is_hash; + let is_boundary = meta.query_advice(config.is_boundary, Rotation::cur()); + + // in the "metadata" or "chunk data" section, wherever is_boundary is set. + let cond = is_not_hash * is_boundary; + + let accumulator = meta.query_advice(config.accumulator, Rotation::cur()); + let preimage_len = + is_data.expr() * accumulator + (1.expr() - is_data) * N_ROWS_METADATA.expr(); + + [ + 1.expr(), // q_enable + 1.expr(), // is final + meta.query_advice(config.preimage_rlc, Rotation::cur()), // input RLC + preimage_len, // input len + meta.query_advice(config.digest_rlc, Rotation::cur()), // output RLC + ] + .into_iter() + .zip_eq(keccak_table.table_exprs(meta)) + .map(|(value, table)| (cond.expr() * value, table)) + .collect() + }, + ); + + // lookup chunk data digests in the "digest rlc section" of BlobDataConfig. + meta.lookup_any( + "BlobDataConfig (chunk data digests in BlobDataConfig \"hash section\")", + |meta| { + let is_data = meta.query_selector(config.data_selector); + let is_boundary = meta.query_advice(config.is_boundary, Rotation::cur()); + + // in the "chunk data" section when we encounter a chunk boundary + let cond = is_data * is_boundary; + + let hash_section_table = vec![ + meta.query_selector(config.hash_selector), + meta.query_advice(config.chunk_idx, Rotation::cur()), + meta.query_advice(config.accumulator, Rotation::cur()), + meta.query_advice(config.digest_rlc, Rotation::cur()), + ]; + [ + 1.expr(), // hash section + meta.query_advice(config.chunk_idx, Rotation::cur()), // chunk idx + meta.query_advice(config.accumulator, Rotation::cur()), // chunk len + meta.query_advice(config.digest_rlc, Rotation::cur()), // digest rlc + ] + .into_iter() + .zip(hash_section_table) + .map(|(value, table)| (cond.expr() * value, table)) + .collect() + }, + ); + + // lookup challenge digest in keccak table. + meta.lookup_any( + "BlobDataConfig (metadata/chunk_data/challenge digests in keccak table)", + |meta| { + let is_hash = meta.query_selector(config.hash_selector); + let is_boundary = meta.query_advice(config.is_boundary, Rotation::cur()); + + // when is_boundary is set in the "digest RLC" section. + // this is also the last row of the "digest RLC" section. + let cond = is_hash * is_boundary; + + // - metadata_digest: 32 bytes + // - chunk[i].chunk_data_digest: 32 bytes each + let preimage_len = 32.expr() * (MAX_AGG_SNARKS + 1).expr(); + + [ + 1.expr(), // q_enable + 1.expr(), // is final + meta.query_advice(config.preimage_rlc, Rotation::cur()), // input rlc + preimage_len, // input len + meta.query_advice(config.digest_rlc, Rotation::cur()), // output rlc + ] + .into_iter() + .zip_eq(keccak_table.table_exprs(meta)) + .map(|(value, table)| (cond.expr() * value, table)) + .collect() + }, + ); + + assert!(meta.degree() <= 5); + + config + } + + pub fn assign( + &self, + layouter: &mut impl Layouter, + challenge_value: Challenges>, + rlc_config: &RlcConfig, + // The chunks_are_padding assigned cells are exports from the conditional constraints in + // `core.rs`. Since these are already constrained, we can just use them as is. + chunks_are_padding: &[AssignedCell], + blob: &BlobData, + barycentric_assignments: &[CRTInteger], + ) -> Result { + // load tables + self.u8_table.load(layouter)?; + self.chunk_idx_range_table.load(layouter)?; + + let assigned_rows = layouter.assign_region( + || "BlobData rows", + |mut region| { + let rows = blob.to_rows(challenge_value); + assert_eq!(rows.len(), N_ROWS_BLOB_DATA_CONFIG); + + // enable data selector + for offset in N_ROWS_METADATA..N_ROWS_METADATA + N_ROWS_DATA { + self.data_selector.enable(&mut region, offset)?; + } + + // enable hash selector + for offset in + N_ROWS_METADATA + N_ROWS_DATA..N_ROWS_METADATA + N_ROWS_DATA + N_ROWS_DIGEST_RLC + { + self.hash_selector.enable(&mut region, offset)?; + } + + let mut assigned_rows = Vec::with_capacity(N_ROWS_BLOB_DATA_CONFIG); + let mut count = 0u64; + for (i, row) in rows.iter().enumerate() { + let byte = region.assign_advice( + || "byte", + self.byte, + i, + || Value::known(Fr::from(row.byte as u64)), + )?; + let accumulator = region.assign_advice( + || "accumulator", + self.accumulator, + i, + || Value::known(Fr::from(row.accumulator)), + )?; + let chunk_idx = region.assign_advice( + || "chunk_idx", + self.chunk_idx, + i, + || Value::known(Fr::from(row.chunk_idx)), + )?; + let is_boundary = region.assign_advice( + || "is_boundary", + self.is_boundary, + i, + || Value::known(Fr::from(row.is_boundary as u64)), + )?; + let bcount = if (N_ROWS_METADATA..N_ROWS_METADATA + N_ROWS_DATA).contains(&i) { + count += row.is_boundary as u64; + count + } else { + 0 + }; + let boundary_count = region.assign_advice( + || "boundary_count", + self.boundary_count, + i, + || Value::known(Fr::from(bcount)), + )?; + let is_padding = region.assign_advice( + || "is_padding", + self.is_padding, + i, + || Value::known(Fr::from(row.is_padding as u64)), + )?; + let preimage_rlc = region.assign_advice( + || "preimage_rlc", + self.preimage_rlc, + i, + || row.preimage_rlc, + )?; + let digest_rlc = region.assign_advice( + || "digest_rlc", + self.digest_rlc, + i, + || row.digest_rlc, + )?; + assigned_rows.push(AssignedBlobDataConfig { + byte, + accumulator, + chunk_idx, + is_boundary, + boundary_count, + is_padding, + preimage_rlc, + digest_rlc, + }); + } + + Ok(assigned_rows) + }, + )?; + + layouter.assign_region( + || "BlobData internal checks", + |mut region| { + rlc_config.init(&mut region)?; + let mut rlc_config_offset = 0; + + // load some constants that we will use later. + let zero = { + let zero = rlc_config.load_private( + &mut region, + &Fr::zero(), + &mut rlc_config_offset, + )?; + let zero_cell = rlc_config.zero_cell(zero.cell().region_index); + region.constrain_equal(zero.cell(), zero_cell)?; + zero + }; + let one = { + let one = + rlc_config.load_private(&mut region, &Fr::one(), &mut rlc_config_offset)?; + let one_cell = rlc_config.one_cell(one.cell().region_index); + region.constrain_equal(one.cell(), one_cell)?; + one + }; + let fixed_chunk_indices = { + let mut fixed_chunk_indices = vec![one.clone()]; + for i in 2..=MAX_AGG_SNARKS { + let i_cell = rlc_config.load_private( + &mut region, + &Fr::from(i as u64), + &mut rlc_config_offset, + )?; + let i_fixed_cell = rlc_config + .fixed_up_to_max_agg_snarks_cell(i_cell.cell().region_index, i); + region.constrain_equal(i_cell.cell(), i_fixed_cell)?; + fixed_chunk_indices.push(i_cell); + } + fixed_chunk_indices + }; + let pows_of_256 = { + let mut pows_of_256 = vec![one.clone()]; + for (exponent, pow_of_256) in (1..=POWS_OF_256).zip_eq( + std::iter::successors(Some(Fr::from(256)), |n| Some(n * Fr::from(256))) + .take(POWS_OF_256), + ) { + let pow_cell = rlc_config.load_private( + &mut region, + &pow_of_256, + &mut rlc_config_offset, + )?; + let fixed_pow_cell = rlc_config.pow_of_two_hundred_and_fifty_six_cell( + pow_cell.cell().region_index, + exponent, + ); + region.constrain_equal(pow_cell.cell(), fixed_pow_cell)?; + pows_of_256.push(pow_cell); + } + pows_of_256 + }; + let two_fifty_six = pows_of_256[1].clone(); + + // read randomness challenges for RLC computations. + let r_keccak = rlc_config.read_challenge1( + &mut region, + challenge_value, + &mut rlc_config_offset, + )?; + let r_evm = rlc_config.read_challenge2( + &mut region, + challenge_value, + &mut rlc_config_offset, + )?; + let r32 = { + let r2 = rlc_config.mul( + &mut region, + &r_keccak, + &r_keccak, + &mut rlc_config_offset, + )?; + let r4 = rlc_config.mul(&mut region, &r2, &r2, &mut rlc_config_offset)?; + let r8 = rlc_config.mul(&mut region, &r4, &r4, &mut rlc_config_offset)?; + let r16 = rlc_config.mul(&mut region, &r8, &r8, &mut rlc_config_offset)?; + rlc_config.mul(&mut region, &r16, &r16, &mut rlc_config_offset)? + }; + + // load cells representing the keccak digest of empty bytes. + let mut empty_digest_cells = Vec::with_capacity(N_BYTES_U256); + for (i, &byte) in keccak256([]).iter().enumerate() { + let cell = rlc_config.load_private( + &mut region, + &Fr::from(byte as u64), + &mut rlc_config_offset, + )?; + let fixed_cell = rlc_config.empty_keccak_cell_i(cell.cell().region_index, i); + region.constrain_equal(cell.cell(), fixed_cell)?; + empty_digest_cells.push(cell); + } + let empty_digest_evm_rlc = rlc_config.rlc( + &mut region, + &empty_digest_cells, + &r_evm, + &mut rlc_config_offset, + )?; + + //////////////////////////////////////////////////////////////////////////////// + /////////////////////////////// NUM_VALID_CHUNKS /////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + let rows = assigned_rows.iter().take(2).collect::>(); + let (byte_hi, byte_lo, lc1, lc2) = ( + &rows[0].byte, + &rows[1].byte, + &rows[0].accumulator, + &rows[1].accumulator, + ); + + // the linear combination starts with the most-significant byte. + region.constrain_equal(byte_hi.cell(), lc1.cell())?; + + // do the linear combination. + let num_valid_chunks = rlc_config.mul_add( + &mut region, + lc1, + &two_fifty_six, + byte_lo, + &mut rlc_config_offset, + )?; + region.constrain_equal(num_valid_chunks.cell(), lc2.cell())?; + + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////// CHUNK_SIZE ////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + let mut num_nonempty_chunks = zero.clone(); + let mut is_empty_chunks = Vec::with_capacity(MAX_AGG_SNARKS); + let mut chunk_sizes = Vec::with_capacity(MAX_AGG_SNARKS); + for (i, is_padded_chunk) in chunks_are_padding.iter().enumerate() { + let rows = assigned_rows + .iter() + .skip(2 + 4 * i) + .take(4) + .collect::>(); + let (byte0, byte1, byte2, byte3) = + (&rows[0].byte, &rows[1].byte, &rows[2].byte, &rows[3].byte); + let (acc0, acc1, acc2, acc3) = ( + &rows[0].accumulator, + &rows[1].accumulator, + &rows[2].accumulator, + &rows[3].accumulator, + ); + + // the linear combination starts with the most-significant byte. + region.constrain_equal(byte0.cell(), acc0.cell())?; + + // do the linear combination. + let lc = rlc_config.mul_add( + &mut region, + acc0, + &two_fifty_six, + byte1, + &mut rlc_config_offset, + )?; + region.constrain_equal(lc.cell(), acc1.cell())?; + let lc = rlc_config.mul_add( + &mut region, + acc1, + &two_fifty_six, + byte2, + &mut rlc_config_offset, + )?; + region.constrain_equal(lc.cell(), acc2.cell())?; + let chunk_size = rlc_config.mul_add( + &mut region, + acc2, + &two_fifty_six, + byte3, + &mut rlc_config_offset, + )?; + region.constrain_equal(chunk_size.cell(), acc3.cell())?; + + // if the chunk is a padded chunk, its size must be set to 0. + rlc_config.conditional_enforce_equal( + &mut region, + &chunk_size, + &zero, + is_padded_chunk, + &mut rlc_config_offset, + )?; + + let is_empty_chunk = + rlc_config.is_zero(&mut region, &chunk_size, &mut rlc_config_offset)?; + let is_nonempty_chunk = + rlc_config.not(&mut region, &is_empty_chunk, &mut rlc_config_offset)?; + num_nonempty_chunks = rlc_config.add( + &mut region, + &is_nonempty_chunk, + &num_nonempty_chunks, + &mut rlc_config_offset, + )?; + + is_empty_chunks.push(is_empty_chunk); + chunk_sizes.push(chunk_size); + } + let all_chunks_empty = rlc_config.is_zero( + &mut region, + &num_nonempty_chunks, + &mut rlc_config_offset, + )?; + let not_all_chunks_empty = + rlc_config.not(&mut region, &all_chunks_empty, &mut rlc_config_offset)?; + + // on the last row of the "metadata" section we want to ensure the keccak table + // lookup would be enabled for the metadata digest + // + // and boundary_count must be 0 + region.constrain_equal( + assigned_rows + .get(N_ROWS_METADATA - 1) + .unwrap() + .is_boundary + .cell(), + one.cell(), + )?; + region.constrain_equal( + assigned_rows + .get(N_ROWS_METADATA - 1) + .unwrap() + .boundary_count + .cell(), + zero.cell(), + )?; + + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////// CHUNK_DATA ////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + // the first data row has a length (accumulator) of 1. But in the special case that + // there are no non-empty chunks, this will be 0 and must also be a padding row. + let rows = assigned_rows + .iter() + .skip(N_ROWS_METADATA) + .take(N_ROWS_DATA) + .collect::>(); + rlc_config.conditional_enforce_equal( + &mut region, + &rows[0].accumulator, + &one, + ¬_all_chunks_empty, + &mut rlc_config_offset, + )?; + rlc_config.conditional_enforce_equal( + &mut region, + &rows[0].is_padding, + &zero, + ¬_all_chunks_empty, + &mut rlc_config_offset, + )?; + rlc_config.conditional_enforce_equal( + &mut region, + &rows[0].accumulator, + &zero, + &all_chunks_empty, + &mut rlc_config_offset, + )?; + rlc_config.conditional_enforce_equal( + &mut region, + &rows[0].is_padding, + &one, + &all_chunks_empty, + &mut rlc_config_offset, + )?; + + // get the boundary count at the end of the "chunk data" section, and equate it to + // the number of non-empty chunks in the batch. + region.constrain_equal( + rows.last().unwrap().boundary_count.cell(), + num_nonempty_chunks.cell(), + )?; + + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////// DIGEST RLC ////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + let rows = assigned_rows + .iter() + .skip(N_ROWS_METADATA + N_ROWS_DATA) + .take(N_ROWS_DIGEST_RLC) + .collect::>(); + + // rows have chunk_idx set from 0 (metadata) -> MAX_AGG_SNARKS. + region.constrain_equal(rows[0].chunk_idx.cell(), zero.cell())?; + for (row, fixed_chunk_idx) in rows + .iter() + .skip(1) + .take(MAX_AGG_SNARKS) + .zip_eq(fixed_chunk_indices.iter()) + { + region.constrain_equal(row.chunk_idx.cell(), fixed_chunk_idx.cell())?; + } + + let challenge_digest_preimage_rlc_specified = &rows.last().unwrap().preimage_rlc; + let challenge_digest_rlc_specified = &rows.last().unwrap().digest_rlc; + + // ensure that on the last row of this section the is_boundary is turned on + // which would enable the keccak table lookup for challenge_digest + region.constrain_equal(rows.last().unwrap().is_boundary.cell(), one.cell())?; + + let metadata_digest_rlc_computed = + &assigned_rows.get(N_ROWS_METADATA - 1).unwrap().digest_rlc; + let metadata_digest_rlc_specified = &rows.first().unwrap().digest_rlc; + region.constrain_equal( + metadata_digest_rlc_computed.cell(), + metadata_digest_rlc_specified.cell(), + )?; + + // if the chunk is a padded chunk, then its chunk data digest should be the + // same as the previous chunk's data digest. + // + // Also, we know that the first chunk is valid. So we can just start the check from + // the second chunk's data digest. + region.constrain_equal(chunks_are_padding[0].cell(), zero.cell())?; + for i in 1..MAX_AGG_SNARKS { + // Note that in `rows`, the first row is the metadata row (hence anyway skip + // it). That's why we have a +1. + rlc_config.conditional_enforce_equal( + &mut region, + &rows[i + 1].digest_rlc, + &rows[i].digest_rlc, + &chunks_are_padding[i], + &mut rlc_config_offset, + )?; + } + + let mut chunk_digest_evm_rlcs = Vec::with_capacity(MAX_AGG_SNARKS); + for (((row, chunk_size_decoded), is_empty), is_padded_chunk) in rows + .iter() + .skip(1) + .take(MAX_AGG_SNARKS) + .zip_eq(chunk_sizes) + .zip_eq(is_empty_chunks) + .zip_eq(chunks_are_padding) + { + // if the chunk is a valid chunk (i.e. not padded chunk), but is empty (i.e. no + // L2 transactions), then the chunk's data digest should be the empty keccak + // digest. + let is_valid = + rlc_config.not(&mut region, is_padded_chunk, &mut rlc_config_offset)?; + let is_valid_empty = rlc_config.mul( + &mut region, + &is_valid, + &is_empty, + &mut rlc_config_offset, + )?; + rlc_config.conditional_enforce_equal( + &mut region, + &row.digest_rlc, + &empty_digest_evm_rlc, + &is_valid_empty, + &mut rlc_config_offset, + )?; + + // constrain chunk size specified here against decoded in metadata. + region.constrain_equal(row.accumulator.cell(), chunk_size_decoded.cell())?; + + chunk_digest_evm_rlcs.push(&row.digest_rlc); + } + + //////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////// DIGEST BYTES ///////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + let mut challenge_digest_preimage_keccak_rlc = zero.clone(); + let rows = assigned_rows + .iter() + .skip(N_ROWS_METADATA + N_ROWS_DATA + N_ROWS_DIGEST_RLC) + .take(N_ROWS_DIGEST_BYTES) + .collect::>(); + for (i, digest_rlc_specified) in std::iter::once(metadata_digest_rlc_specified) + .chain(chunk_digest_evm_rlcs) + .chain(std::iter::once(challenge_digest_rlc_specified)) + .enumerate() + { + let digest_rows = rows + .iter() + .skip(N_BYTES_U256 * i) + .take(N_BYTES_U256) + .collect::>(); + let digest_bytes = digest_rows + .iter() + .map(|row| row.byte.clone()) + .collect::>(); + let digest_rlc_computed = rlc_config.rlc( + &mut region, + &digest_bytes, + &r_evm, + &mut rlc_config_offset, + )?; + region + .constrain_equal(digest_rlc_computed.cell(), digest_rlc_specified.cell())?; + + // compute the keccak input RLC: + // we do this only for the metadata and chunks, not for the blob row itself. + if i < MAX_AGG_SNARKS + 1 { + let digest_keccak_rlc = rlc_config.rlc( + &mut region, + &digest_bytes, + &r_keccak, + &mut rlc_config_offset, + )?; + challenge_digest_preimage_keccak_rlc = rlc_config.mul_add( + &mut region, + &challenge_digest_preimage_keccak_rlc, + &r32, + &digest_keccak_rlc, + &mut rlc_config_offset, + )?; + } + } + region.constrain_equal( + challenge_digest_preimage_keccak_rlc.cell(), + challenge_digest_preimage_rlc_specified.cell(), + )?; + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////// EXPORT //////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + let mut blob_fields: Vec>> = + Vec::with_capacity(BLOB_WIDTH); + let blob_bytes = assigned_rows + .iter() + .take(N_ROWS_METADATA + N_ROWS_DATA) + .map(|row| row.byte.clone()) + .collect::>(); + for chunk in blob_bytes.chunks_exact(N_DATA_BYTES_PER_COEFFICIENT) { + // blob bytes are supposed to be deserialised in big-endianness. However, we + // have the export from BarycentricConfig in little-endian bytes. + blob_fields.push(chunk.iter().rev().cloned().collect()); + } + let mut chunk_data_digests = Vec::with_capacity(MAX_AGG_SNARKS); + let chunk_data_digests_bytes = assigned_rows + .iter() + .skip(N_ROWS_METADATA + N_ROWS_DATA + N_ROWS_DIGEST_RLC + N_BYTES_U256) + .take(MAX_AGG_SNARKS * N_BYTES_U256) + .map(|row| row.byte.clone()) + .collect::>(); + for chunk in chunk_data_digests_bytes.chunks_exact(N_BYTES_U256) { + chunk_data_digests.push(chunk.to_vec()); + } + let export = AssignedBlobDataExport { + num_valid_chunks, + challenge_digest: assigned_rows + .iter() + .rev() + .take(N_BYTES_U256) + .map(|row| row.byte.clone()) + .collect(), + chunk_data_digests, + }; + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////// LINKING /////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + assert_eq!(barycentric_assignments.len(), BLOB_WIDTH + 1); + let blob_crts = barycentric_assignments + .iter() + .take(BLOB_WIDTH) + .collect::>(); + let challenge_digest_crt = barycentric_assignments + .get(BLOB_WIDTH) + .expect("challenge digest CRT"); + + let challenge_digest_limb1 = rlc_config.inner_product( + &mut region, + &export.challenge_digest[0..11], + &pows_of_256, + &mut rlc_config_offset, + )?; + let challenge_digest_limb2 = rlc_config.inner_product( + &mut region, + &export.challenge_digest[11..22], + &pows_of_256, + &mut rlc_config_offset, + )?; + let challenge_digest_limb3 = rlc_config.inner_product( + &mut region, + &export.challenge_digest[22..32], + &pows_of_256[0..10], + &mut rlc_config_offset, + )?; + region.constrain_equal( + challenge_digest_limb1.cell(), + challenge_digest_crt.truncation.limbs[0].cell(), + )?; + region.constrain_equal( + challenge_digest_limb2.cell(), + challenge_digest_crt.truncation.limbs[1].cell(), + )?; + region.constrain_equal( + challenge_digest_limb3.cell(), + challenge_digest_crt.truncation.limbs[2].cell(), + )?; + for (blob_crt, blob_field) in blob_crts.iter().zip_eq(blob_fields.iter()) { + let limb1 = rlc_config.inner_product( + &mut region, + &blob_field[0..11], + &pows_of_256, + &mut rlc_config_offset, + )?; + let limb2 = rlc_config.inner_product( + &mut region, + &blob_field[11..22], + &pows_of_256, + &mut rlc_config_offset, + )?; + let limb3 = rlc_config.inner_product( + &mut region, + &blob_field[22..31], + &pows_of_256[0..9], + &mut rlc_config_offset, + )?; + region.constrain_equal(limb1.cell(), blob_crt.truncation.limbs[0].cell())?; + region.constrain_equal(limb2.cell(), blob_crt.truncation.limbs[1].cell())?; + region.constrain_equal(limb3.cell(), blob_crt.truncation.limbs[2].cell())?; + } + + Ok(export) + }, + ) + } +} diff --git a/aggregator/src/aggregation/circuit.rs b/aggregator/src/aggregation/circuit.rs index 74f9fd6180..92931ce2cb 100644 --- a/aggregator/src/aggregation/circuit.rs +++ b/aggregator/src/aggregation/circuit.rs @@ -1,4 +1,6 @@ +use crate::blob::BlobData; use ark_std::{end_timer, start_timer}; +use halo2_base::{Context, ContextParams}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::bn256::{Bn256, Fr, G1Affine}, @@ -7,6 +9,8 @@ use halo2_proofs::{ }; use itertools::Itertools; use rand::Rng; +#[cfg(not(feature = "disable_proof_aggregation"))] +use std::rc::Rc; use std::{env, fs::File}; #[cfg(not(feature = "disable_proof_aggregation"))] @@ -14,10 +18,7 @@ use snark_verifier::loader::halo2::halo2_ecc::halo2_base; use snark_verifier::pcs::kzg::KzgSuccinctVerifyingKey; #[cfg(not(feature = "disable_proof_aggregation"))] use snark_verifier::{ - loader::halo2::{ - halo2_ecc::halo2_base::{AssignedValue, Context, ContextParams}, - Halo2Loader, - }, + loader::halo2::{halo2_ecc::halo2_base::AssignedValue, Halo2Loader}, pcs::kzg::{Bdfg21, Kzg}, }; #[cfg(not(feature = "disable_proof_aggregation"))] @@ -30,7 +31,7 @@ use crate::{ constants::{ACC_LEN, DIGEST_LEN, MAX_AGG_SNARKS}, core::{assign_batch_hashes, extract_proof_and_instances_with_pairing_check}, util::parse_hash_digest_cells, - ConfigParams, + AssignedBarycentricEvaluationConfig, ConfigParams, }; use super::AggregationConfig; @@ -157,24 +158,63 @@ impl Circuit for AggregationCircuit { let timer = start_timer!(|| "aggregation"); + // load lookup table in range config + config + .range() + .load_lookup_table(&mut layouter) + .expect("load range lookup table"); // ============================================== // Step 1: snark aggregation circuit // ============================================== - #[cfg(not(feature = "disable_proof_aggregation"))] - let (accumulator_instances, snark_inputs) = { - config - .range() - .load_lookup_table(&mut layouter) - .expect("load range lookup table"); + #[cfg(feature = "disable_proof_aggregation")] + let barycentric = { + let mut first_pass = halo2_base::SKIP_FIRST_PASS; + layouter.assign_region( + || "barycentric evaluation", + |region| { + if first_pass { + first_pass = false; + return Ok(AssignedBarycentricEvaluationConfig::default()); + } + let mut ctx = Context::new( + region, + ContextParams { + max_rows: config.flex_gate().max_rows, + num_context_ids: 1, + fixed_columns: config.flex_gate().constants.clone(), + }, + ); + + let barycentric = config.barycentric.assign( + &mut ctx, + &self.batch_hash.blob.coefficients, + self.batch_hash.blob.challenge_digest, + self.batch_hash.blob.evaluation, + ); + + config.barycentric.scalar.range.finalize(&mut ctx); + ctx.print_stats(&["barycentric evaluation"]); + + Ok(barycentric) + }, + )? + }; + + #[cfg(not(feature = "disable_proof_aggregation"))] + let (accumulator_instances, snark_inputs, barycentric) = { let mut first_pass = halo2_base::SKIP_FIRST_PASS; - let (accumulator_instances, snark_inputs) = layouter.assign_region( + let (accumulator_instances, snark_inputs, barycentric) = layouter.assign_region( || "aggregation", - |region| -> Result<(Vec>, Vec>), Error> { + |region| { if first_pass { first_pass = false; - return Ok((vec![], vec![])); + return Ok(( + vec![], + vec![], + AssignedBarycentricEvaluationConfig::default(), + )); } // stores accumulators for all snarks, including the padded ones @@ -215,23 +255,34 @@ impl Circuit for AggregationCircuit { // - the public inputs from each snark accumulator_instances.extend(flatten_accumulator(acc).iter().copied()); // the snark is not a fresh one, assigned_instances already contains an - // accumulator so we want to skip the first 12 elements from the public input + // accumulator so we want to skip the first 12 elements from the public + // input snark_inputs.extend( assigned_aggregation_instances .iter() .flat_map(|instance_column| instance_column.iter().skip(ACC_LEN)), ); - config.range().finalize(&mut loader.ctx_mut()); + loader.ctx_mut().print_stats(&["snark aggregation"]); + + let mut ctx = Rc::into_inner(loader).unwrap().into_ctx(); + let barycentric = config.barycentric.assign( + &mut ctx, + &self.batch_hash.blob.coefficients, + self.batch_hash.blob.challenge_digest, + self.batch_hash.blob.evaluation, + ); + + ctx.print_stats(&["barycentric"]); - loader.ctx_mut().print_stats(&["Range"]); + config.range().finalize(&mut ctx); - Ok((accumulator_instances, snark_inputs)) + Ok((accumulator_instances, snark_inputs, barycentric)) }, )?; assert_eq!(snark_inputs.len(), MAX_AGG_SNARKS * DIGEST_LEN); - (accumulator_instances, snark_inputs) + (accumulator_instances, snark_inputs, barycentric) }; end_timer!(timer); // ============================================== @@ -242,7 +293,7 @@ impl Circuit for AggregationCircuit { let timer = start_timer!(|| "load aux table"); - let hash_digest_cells = { + let assigned_batch_hash = { config .keccak_circuit_config .load_aux_tables(&mut layouter)?; @@ -253,10 +304,13 @@ impl Circuit for AggregationCircuit { // - batch_public_input_hash // - chunk\[i\].piHash for i in \[0, MAX_AGG_SNARKS) // - batch_data_hash_preimage + // - preimage for blob metadata + // - preimage of chunk data digest (only for valid chunks) + // - preimage of challenge digest let preimages = self.batch_hash.extract_hash_preimages(); assert_eq!( preimages.len(), - MAX_AGG_SNARKS + 2, + 4 + MAX_AGG_SNARKS + self.batch_hash.number_of_valid_chunks, "error extracting preimages" ); end_timer!(timer); @@ -268,7 +322,7 @@ impl Circuit for AggregationCircuit { .iter() .map(|chunk| !chunk.is_padding) .collect::>(); - let hash_digest_cells = assign_batch_hashes( + let assigned_batch_hash = assign_batch_hashes( &config, &mut layouter, challenges, @@ -276,12 +330,14 @@ impl Circuit for AggregationCircuit { &preimages, ) .map_err(|_e| Error::ConstraintSystemFailure)?; + end_timer!(timer); - hash_digest_cells + + assigned_batch_hash }; // digests let (batch_pi_hash_digest, chunk_pi_hash_digests, _potential_batch_data_hash_digest) = - parse_hash_digest_cells(&hash_digest_cells); + parse_hash_digest_cells(&assigned_batch_hash.hash_output); // ============================================== // step 3: assert public inputs to the snarks are correct @@ -372,7 +428,64 @@ impl Circuit for AggregationCircuit { } } + // blob data config + { + let barycentric_assignments = &barycentric.barycentric_assignments; + let challenge_le = &barycentric.z_le; + let evaluation_le = &barycentric.y_le; + + let blob_data = BlobData::from(&self.batch_hash); + let blob_data_exports = config.blob_data_config.assign( + &mut layouter, + challenges, + &config.rlc_config, + &assigned_batch_hash.chunks_are_padding, + &blob_data, + barycentric_assignments, + )?; + + layouter.assign_region( + || "blob checks", + |mut region| -> Result<(), Error> { + region.constrain_equal( + assigned_batch_hash.num_valid_snarks.cell(), + blob_data_exports.num_valid_chunks.cell(), + )?; + + for (chunk_data_digest, expected_chunk_data_digest) in blob_data_exports + .chunk_data_digests + .iter() + .zip_eq(assigned_batch_hash.blob.chunk_tx_data_digests.iter()) + { + for (c, ec) in chunk_data_digest + .iter() + .zip_eq(expected_chunk_data_digest.iter()) + { + region.constrain_equal(c.cell(), ec.cell())?; + } + } + + for (c, ec) in evaluation_le + .iter() + .zip_eq(assigned_batch_hash.blob.y.iter().rev()) + { + region.constrain_equal(c.cell(), ec.cell())?; + } + + for (c, ec) in challenge_le + .iter() + .zip_eq(assigned_batch_hash.blob.z.iter().rev()) + { + region.constrain_equal(c.cell(), ec.cell())?; + } + + Ok(()) + }, + )?; + } + end_timer!(witness_time); + Ok(()) } } @@ -397,14 +510,17 @@ impl CircuitExt for AggregationCircuit { fn selectors(config: &Self::Config) -> Vec { // - advice columns from flex gate - // - selector from RLC gate + // - selectors from RLC gate config.0.flex_gate().basic_gates[0] .iter() .map(|gate| gate.q_enable) .chain( [ config.0.rlc_config.selector, - config.0.rlc_config.enable_challenge, + config.0.rlc_config.enable_challenge1, + config.0.rlc_config.enable_challenge2, + config.0.blob_data_config.data_selector, + config.0.blob_data_config.hash_selector, ] .iter() .cloned(), diff --git a/aggregator/src/aggregation/config.rs b/aggregator/src/aggregation/config.rs index 5ad297e3ee..a30d90f524 100644 --- a/aggregator/src/aggregation/config.rs +++ b/aggregator/src/aggregation/config.rs @@ -12,14 +12,14 @@ use snark_verifier::{ }; use zkevm_circuits::{ keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, - table::KeccakTable, + table::{KeccakTable, RangeTable, U8Table}, util::{Challenges, SubCircuitConfig}, }; use crate::{ constants::{BITS, LIMBS}, param::ConfigParams, - RlcConfig, + BarycentricEvaluationConfig, BlobDataConfig, RlcConfig, }; #[derive(Debug, Clone)] @@ -33,6 +33,10 @@ pub struct AggregationConfig { pub keccak_circuit_config: KeccakCircuitConfig, /// RLC config pub rlc_config: RlcConfig, + /// The blob data's config. + pub blob_data_config: BlobDataConfig, + /// Config to do the barycentric evaluation on blob polynomial. + pub barycentric: BarycentricEvaluationConfig, /// Instance for public input; stores /// - accumulator from aggregation (12 elements) /// - batch_public_input_hash (32 elements) @@ -56,16 +60,19 @@ impl AggregationConfig { let rlc_config = RlcConfig::configure(meta, challenges); // hash configuration for aggregation circuit - let keccak_circuit_config = { + let (keccak_table, keccak_circuit_config) = { let keccak_table = KeccakTable::construct(meta); let challenges_exprs = challenges.exprs(meta); let keccak_circuit_config_args = KeccakCircuitConfigArgs { - keccak_table, + keccak_table: keccak_table.clone(), challenges: challenges_exprs, }; - KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + ( + keccak_table, + KeccakCircuitConfig::new(meta, keccak_circuit_config_args), + ) }; // base field configuration for aggregation circuit @@ -83,6 +90,8 @@ impl AggregationConfig { params.degree as usize, ); + let barycentric = BarycentricEvaluationConfig::construct(base_field_config.range.clone()); + let columns = keccak_circuit_config.cell_manager.columns(); log::info!("keccak uses {} columns", columns.len(),); @@ -97,6 +106,13 @@ impl AggregationConfig { // enable equality for the is_final column meta.enable_equality(keccak_circuit_config.keccak_table.is_final); + // Blob data. + let u8_table = U8Table::construct(meta); + let range_table = RangeTable::construct(meta); + let challenges_expr = challenges.exprs(meta); + let blob_data_config = + BlobDataConfig::configure(meta, challenges_expr, u8_table, range_table, &keccak_table); + // Instance column stores public input column // - the accumulator // - the batch public input hash @@ -107,8 +123,10 @@ impl AggregationConfig { Self { base_field_config, rlc_config, + blob_data_config, keccak_circuit_config, instance, + barycentric, } } diff --git a/aggregator/src/aggregation/rlc.rs b/aggregator/src/aggregation/rlc.rs index e89a4d31c9..3c7f67e67c 100644 --- a/aggregator/src/aggregation/rlc.rs +++ b/aggregator/src/aggregation/rlc.rs @@ -2,3 +2,4 @@ mod config; mod gates; pub(crate) use config::RlcConfig; +pub(crate) use gates::POWS_OF_256; diff --git a/aggregator/src/aggregation/rlc/config.rs b/aggregator/src/aggregation/rlc/config.rs index a5fd32f073..f8b7846d30 100644 --- a/aggregator/src/aggregation/rlc/config.rs +++ b/aggregator/src/aggregation/rlc/config.rs @@ -6,7 +6,7 @@ use halo2_proofs::{ #[cfg(test)] use halo2_proofs::plonk::FirstPhase; -use zkevm_circuits::util::Challenges; +use zkevm_circuits::util::{Challenges, Expr}; /// This config is used to compute RLCs for bytes. /// It requires a phase 2 column @@ -18,13 +18,15 @@ pub struct RlcConfig { pub(crate) phase_2_column: Column, pub(crate) selector: Selector, pub(crate) fixed: Column, - pub(crate) enable_challenge: Selector, + pub(crate) enable_challenge1: Selector, + pub(crate) enable_challenge2: Selector, } impl RlcConfig { pub(crate) fn configure(meta: &mut ConstraintSystem, challenge: Challenges) -> Self { let selector = meta.complex_selector(); - let enable_challenge = meta.complex_selector(); + let enable_challenge1 = meta.complex_selector(); + let enable_challenge2 = meta.complex_selector(); let challenge_expr = challenge.exprs(meta); #[cfg(test)] @@ -61,10 +63,13 @@ impl RlcConfig { // constraint: q2*(a-challenge) = 0 // FIXME later: Pretty wasteful to have a dedicated custom gate and selector column just // to extract the keccak challenge cell... - let q2 = meta.query_selector(enable_challenge); - let cs2 = q2 * (a - challenge_expr.keccak_input()); + let q2 = meta.query_selector(enable_challenge1); + let cs2 = q2 * (a.expr() - challenge_expr.keccak_input()); - vec![cs1, cs2] + let q3 = meta.query_selector(enable_challenge2); + let cs3 = q3 * (a - challenge_expr.evm_word()); + + vec![cs1, cs2, cs3] }); Self { #[cfg(test)] @@ -72,7 +77,8 @@ impl RlcConfig { phase_2_column, selector, fixed, - enable_challenge, + enable_challenge1, + enable_challenge2, } } } diff --git a/aggregator/src/aggregation/rlc/gates.rs b/aggregator/src/aggregation/rlc/gates.rs index 29e9eb74eb..f32473b1a6 100644 --- a/aggregator/src/aggregation/rlc/gates.rs +++ b/aggregator/src/aggregation/rlc/gates.rs @@ -1,3 +1,4 @@ +use ethers_core::utils::keccak256; use halo2_proofs::{ arithmetic::Field, circuit::{AssignedCell, Cell, Region, RegionIndex, Value}, @@ -6,32 +7,98 @@ use halo2_proofs::{ }; use zkevm_circuits::util::Challenges; -use crate::{constants::LOG_DEGREE, util::assert_equal}; +use crate::{constants::LOG_DEGREE, util::assert_equal, MAX_AGG_SNARKS}; use super::RlcConfig; +const FIXED_OFFSET_32: usize = MAX_AGG_SNARKS + 1; +const FIXED_OFFSET_168: usize = FIXED_OFFSET_32 + 1; +const FIXED_OFFSET_200: usize = FIXED_OFFSET_168 + 1; +const FIXED_OFFSET_2_POW_32: usize = FIXED_OFFSET_200 + 1; +const FIXED_OFFSET_256: usize = FIXED_OFFSET_2_POW_32 + 1; +const FIXED_OFFSET_EMPTY_KECCAK: usize = FIXED_OFFSET_256 + POWS_OF_256; + +pub(crate) const POWS_OF_256: usize = 10; + impl RlcConfig { /// initialize the chip with fixed cells + /// + /// The layout for fixed cells is: + /// + /// | Offset | Fixed value | + /// |------------------------|----------------------| + /// | 0 | 0 | + /// | 1 | 1 | + /// | i ... | i ... | + /// | MAX_AGG_SNARKS | MAX_AGG_SNARKS | + /// | MAX_AGG_SNARKS + 1 | 32 | + /// | MAX_AGG_SNARKS + 2 | 168 | + /// | MAX_AGG_SNARKS + 3 | 200 | + /// | MAX_AGG_SNARKS + 4 | 2 ^ 32 | + /// | MAX_AGG_SNARKS + 5 | 256 | + /// | MAX_AGG_SNARKS + 6 | 256 ^ 2 | + /// | MAX_AGG_SNARKS + 7 | 256 ^ 3 | + /// | MAX_AGG_SNARKS + j ... | 256 ^ (j - 4) | + /// | MAX_AGG_SNARKS + 14 | 256 ^ 10 | + /// | MAX_AGG_SNARKS + 15 | EMPTY_KECCAK[0] | + /// | MAX_AGG_SNARKS + 16 | EMPTY_KECCAK[1] | + /// | MAX_AGG_SNARKS + k ... | EMPTY_KECCAK[k - 15] | + /// | MAX_AGG_SNARKS + 46 | EMPTY_KECCAK[31] | + /// |------------------------|----------------------| pub(crate) fn init(&self, region: &mut Region) -> Result<(), Error> { - region.assign_fixed(|| "const zero", self.fixed, 0, || Value::known(Fr::zero()))?; - region.assign_fixed(|| "const one", self.fixed, 1, || Value::known(Fr::one()))?; - region.assign_fixed(|| "const two", self.fixed, 2, || Value::known(Fr::from(2)))?; - region.assign_fixed(|| "const five", self.fixed, 3, || Value::known(Fr::from(5)))?; - region.assign_fixed(|| "const nine", self.fixed, 4, || Value::known(Fr::from(9)))?; - region.assign_fixed(|| "const 13", self.fixed, 5, || Value::known(Fr::from(13)))?; - region.assign_fixed(|| "const 32", self.fixed, 6, || Value::known(Fr::from(32)))?; - region.assign_fixed( - || "const 136", - self.fixed, - 7, - || Value::known(Fr::from(136)), - )?; - region.assign_fixed( - || "const 2^32", - self.fixed, - 8, - || Value::known(Fr::from(1 << 32)), - )?; + let mut offset = 0; + + // [0, ..., MAX_AGG_SNARKS] + for const_val in 0..=MAX_AGG_SNARKS { + region.assign_fixed( + || format!("const at offset={offset}"), + self.fixed, + offset, + || Value::known(Fr::from(const_val as u64)), + )?; + offset += 1; + } + assert_eq!(offset, FIXED_OFFSET_32); + + // [32, 168, 200, 1 << 32] + for const_val in [32, 168, 200, 1 << 32] { + region.assign_fixed( + || format!("const at offset={offset}"), + self.fixed, + offset, + || Value::known(Fr::from(const_val)), + )?; + offset += 1; + } + assert_eq!(offset, FIXED_OFFSET_256); + + // [256, ..., 256 ^ i, ..., 256 ^ 10] + for const_val in std::iter::successors(Some(Fr::from(256)), |n| Some(n * Fr::from(256))) + .take(POWS_OF_256) + { + region.assign_fixed( + || format!("const at offset={offset}"), + self.fixed, + offset, + || Value::known(const_val), + )?; + offset += 1; + } + assert_eq!(offset, FIXED_OFFSET_EMPTY_KECCAK); + + // [EMPTY_KECCAK[0], ..., EMPTY_KECCAK[31]] + let empty_keccak = keccak256([]); + for &byte in empty_keccak.iter() { + region.assign_fixed( + || format!("const at offset={offset}"), + self.fixed, + offset, + || Value::known(Fr::from(byte as u64)), + )?; + offset += 1; + } + assert_eq!(offset, FIXED_OFFSET_EMPTY_KECCAK + 32); + Ok(()) } @@ -66,7 +133,7 @@ impl RlcConfig { pub(crate) fn five_cell(&self, region_index: RegionIndex) -> Cell { Cell { region_index, - row_offset: 3, + row_offset: 5, column: self.fixed.into(), } } @@ -75,7 +142,7 @@ impl RlcConfig { pub(crate) fn nine_cell(&self, region_index: RegionIndex) -> Cell { Cell { region_index, - row_offset: 4, + row_offset: 9, column: self.fixed.into(), } } @@ -84,7 +151,21 @@ impl RlcConfig { pub(crate) fn thirteen_cell(&self, region_index: RegionIndex) -> Cell { Cell { region_index, - row_offset: 5, + row_offset: 13, + column: self.fixed.into(), + } + } + + #[inline] + pub(crate) fn fixed_up_to_max_agg_snarks_cell( + &self, + region_index: RegionIndex, + index: usize, + ) -> Cell { + assert!(index <= MAX_AGG_SNARKS, "only up to MAX_AGG_SNARKS"); + Cell { + region_index, + row_offset: index, column: self.fixed.into(), } } @@ -93,15 +174,25 @@ impl RlcConfig { pub(crate) fn thirty_two_cell(&self, region_index: RegionIndex) -> Cell { Cell { region_index, - row_offset: 6, + row_offset: FIXED_OFFSET_32, column: self.fixed.into(), } } + #[inline] - pub(crate) fn one_hundred_and_thirty_six_cell(&self, region_index: RegionIndex) -> Cell { + pub(crate) fn one_hundred_and_sixty_eight_cell(&self, region_index: RegionIndex) -> Cell { Cell { region_index, - row_offset: 7, + row_offset: FIXED_OFFSET_168, + column: self.fixed.into(), + } + } + + #[inline] + pub(crate) fn two_hundred_cell(&self, region_index: RegionIndex) -> Cell { + Cell { + region_index, + row_offset: FIXED_OFFSET_200, column: self.fixed.into(), } } @@ -110,7 +201,35 @@ impl RlcConfig { pub(crate) fn two_to_thirty_two_cell(&self, region_index: RegionIndex) -> Cell { Cell { region_index, - row_offset: 8, + row_offset: FIXED_OFFSET_2_POW_32, + column: self.fixed.into(), + } + } + + #[inline] + pub(crate) fn pow_of_two_hundred_and_fifty_six_cell( + &self, + region_index: RegionIndex, + exponent: usize, + ) -> Cell { + assert!(exponent > 0, "for exponent == 0, fetch the one cell"); + assert!( + exponent <= POWS_OF_256, + "only up to 256 ^ 10 in fixed column" + ); + Cell { + region_index, + row_offset: FIXED_OFFSET_256 + exponent - 1, + column: self.fixed.into(), + } + } + + #[inline] + pub(crate) fn empty_keccak_cell_i(&self, region_index: RegionIndex, index: usize) -> Cell { + assert!(index <= 31, "keccak digest only has 32 bytes"); + Cell { + region_index, + row_offset: FIXED_OFFSET_EMPTY_KECCAK + index, column: self.fixed.into(), } } @@ -131,7 +250,7 @@ impl RlcConfig { res } - pub(crate) fn read_challenge( + pub(crate) fn read_challenge1( &self, region: &mut Region, challenge_value: Challenges>, @@ -139,12 +258,30 @@ impl RlcConfig { ) -> Result, Error> { let challenge_value = challenge_value.keccak_input(); let challenge_cell = region.assign_advice( - || "assign challenge", + || "assign challenge1", self.phase_2_column, *offset, || challenge_value, )?; - self.enable_challenge.enable(region, *offset)?; + self.enable_challenge1.enable(region, *offset)?; + *offset += 1; + Ok(challenge_cell) + } + + pub(crate) fn read_challenge2( + &self, + region: &mut Region, + challenge_value: Challenges>, + offset: &mut usize, + ) -> Result, Error> { + let challenge_value = challenge_value.evm_word(); + let challenge_cell = region.assign_advice( + || "assign challenge2", + self.phase_2_column, + *offset, + || challenge_value, + )?; + self.enable_challenge2.enable(region, *offset)?; *offset += 1; Ok(challenge_cell) } @@ -184,7 +321,7 @@ impl RlcConfig { a.copy_advice(|| "a", region, self.phase_2_column, *offset)?; let one = region.assign_advice( - || "c", + || "b", self.phase_2_column, *offset + 1, || Value::known(Fr::one()), @@ -479,6 +616,24 @@ impl RlcConfig { Ok(res) } + pub(crate) fn inner_product( + &self, + region: &mut Region, + a: &[AssignedCell], + b: &[AssignedCell], + offset: &mut usize, + ) -> Result, Error> { + assert_eq!(a.len(), b.len()); + assert!(!a.is_empty()); + + let mut acc = self.mul(region, &a[0], &b[0], offset)?; + for (a_next, b_next) in a.iter().zip(b.iter()).skip(1) { + acc = self.mul_add(region, a_next, b_next, &acc, offset)?; + } + + Ok(acc) + } + // return a boolean if a ?= 0 #[allow(dead_code)] pub(crate) fn is_zero( @@ -551,6 +706,7 @@ impl RlcConfig { self.is_zero(region, &diff, offset) } } + #[inline] fn byte_to_bits_le(byte: &u8) -> Vec { let mut res = vec![]; diff --git a/aggregator/src/batch.rs b/aggregator/src/batch.rs index 74c8af0e6b..d5550987f8 100644 --- a/aggregator/src/batch.rs +++ b/aggregator/src/batch.rs @@ -1,12 +1,14 @@ //! This module implements related functions that aggregates public inputs of many chunks into a //! single one. -use eth_types::{Field, H256}; +use eth_types::{Field, ToBigEndian, H256, U256}; use ethers_core::utils::keccak256; -use crate::constants::MAX_AGG_SNARKS; - -use super::chunk::ChunkHash; +use crate::{ + blob::{BlobAssignments, BlobData}, + chunk::ChunkHash, + constants::MAX_AGG_SNARKS, +}; #[derive(Default, Debug, Clone)] /// A batch is a set of MAX_AGG_SNARKS num of continuous chunks @@ -17,14 +19,22 @@ use super::chunk::ChunkHash; /// chunk_k-1.withdraw_root || batch_data_hash) /// - batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) pub struct BatchHash { + /// Chain ID of the network. pub(crate) chain_id: u64, - // chunks with padding. - // - the first [0..number_of_valid_chunks) are real ones - // - the last [number_of_valid_chunks, MAX_AGG_SNARKS) are padding - pub(crate) chunks_with_padding: [ChunkHash; MAX_AGG_SNARKS], + /// chunks with padding. + /// - the first [0..number_of_valid_chunks) are real ones + /// - the last [number_of_valid_chunks, MAX_AGG_SNARKS) are padding + pub(crate) chunks_with_padding: Vec, + /// The batch data hash: + /// - keccak256([chunk.hash for chunk in batch]) pub(crate) data_hash: H256, + /// The public input hash, as calculated on-chain: + /// - keccak256( chain_id || prev_state_root || next_state_root || withdraw_trie_root || + /// batch_data_hash || z || y ) pub(crate) public_input_hash: H256, + /// The number of chunks that contain meaningful data, i.e. not padded chunks. pub(crate) number_of_valid_chunks: usize, + pub(crate) blob: BlobAssignments, } impl BatchHash { @@ -67,10 +77,6 @@ impl BatchHash { chunks_with_padding[i + 1].chain_id, ); if chunks_with_padding[i + 1].is_padding { - assert_eq!( - chunks_with_padding[i + 1].data_hash, - chunks_with_padding[i].data_hash - ); assert_eq!( chunks_with_padding[i + 1].prev_state_root, chunks_with_padding[i].prev_state_root @@ -83,6 +89,14 @@ impl BatchHash { chunks_with_padding[i + 1].withdraw_root, chunks_with_padding[i].withdraw_root ); + assert_eq!( + chunks_with_padding[i + 1].data_hash, + chunks_with_padding[i].data_hash + ); + assert_eq!( + chunks_with_padding[i + 1].tx_bytes_hash(), + chunks_with_padding[i].tx_bytes_hash(), + ); } else { assert_eq!( chunks_with_padding[i].post_state_root, @@ -92,22 +106,28 @@ impl BatchHash { } // batch's data hash is build as - // keccak( chunk[0].data_hash || ... || chunk[k-1].data_hash) + // keccak( chunk[0].data_hash || ... || chunk[k-1].data_hash ) let preimage = chunks_with_padding .iter() .take(number_of_valid_chunks) .flat_map(|chunk_hash| chunk_hash.data_hash.0.iter()) .cloned() .collect::>(); - let data_hash = keccak256(preimage); + let batch_data_hash = keccak256(preimage); + + let blob_data = BlobData::new(number_of_valid_chunks, chunks_with_padding); + let blob_assignments = BlobAssignments::from(&blob_data); // public input hash is build as - // keccak( - // chain_id || - // chunk[0].prev_state_root || - // chunk[k-1].post_state_root || - // chunk[k-1].withdraw_root || - // batch_data_hash ) + // keccak( + // chain_id || + // chunk[0].prev_state_root || + // chunk[k-1].post_state_root || + // chunk[k-1].withdraw_root || + // batch_data_hash || + // z || + // y + // ) let preimage = [ chunks_with_padding[0].chain_id.to_be_bytes().as_ref(), chunks_with_padding[0].prev_state_root.as_bytes(), @@ -117,20 +137,35 @@ impl BatchHash { chunks_with_padding[MAX_AGG_SNARKS - 1] .withdraw_root .as_bytes(), - data_hash.as_slice(), + batch_data_hash.as_slice(), + blob_assignments.challenge.to_be_bytes().as_ref(), + blob_assignments.evaluation.to_be_bytes().as_ref(), ] .concat(); - let public_input_hash = keccak256(preimage); + let public_input_hash: H256 = keccak256(preimage).into(); + log::info!( + "batch pihash {:?}, datahash {}, z {} y {}", + public_input_hash, + hex::encode(batch_data_hash), + hex::encode(blob_assignments.challenge.to_be_bytes()), + hex::encode(blob_assignments.evaluation.to_be_bytes()) + ); Self { chain_id: chunks_with_padding[0].chain_id, - chunks_with_padding: chunks_with_padding.try_into().unwrap(), // safe unwrap - data_hash: data_hash.into(), - public_input_hash: public_input_hash.into(), + chunks_with_padding: chunks_with_padding.to_vec(), + data_hash: batch_data_hash.into(), + blob: blob_assignments, + public_input_hash, number_of_valid_chunks, } } + /// Return the blob polynomial and its evaluation at challenge + pub fn blob_assignments(&self) -> BlobAssignments { + self.blob.clone() + } + /// Extract all the hash inputs that will ever be used. /// There are MAX_AGG_SNARKS + 2 hashes. /// @@ -138,6 +173,9 @@ impl BatchHash { /// - batch_public_input_hash /// - chunk\[i\].piHash for i in \[0, MAX_AGG_SNARKS) /// - batch_data_hash_preimage + /// - preimage for blob metadata + /// - chunk\[i\].flattened_l2_signed_data for i in \[0, MAX_AGG_SNARKS) + /// - preimage for challenge digest pub(crate) fn extract_hash_preimages(&self) -> Vec> { let mut res = vec![]; @@ -147,7 +185,17 @@ impl BatchHash { // chunk[0].prev_state_root || // chunk[k-1].post_state_root || // chunk[k-1].withdraw_root || - // batch_data_hash ) + // batch_data_hash || + // z || + // y ) + // TODO: make BLS_MODULUS into a static variable using lazy_static!() + let (_, z) = self.blob.challenge_digest.div_mod( + U256::from_str_radix( + "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", + 16, + ) + .unwrap(), + ); let batch_public_input_hash_preimage = [ self.chain_id.to_be_bytes().as_ref(), self.chunks_with_padding[0].prev_state_root.as_bytes(), @@ -158,6 +206,8 @@ impl BatchHash { .withdraw_root .as_bytes(), self.data_hash.as_bytes(), + &z.to_be_bytes(), + &self.blob.evaluation.to_be_bytes(), ] .concat(); res.push(batch_public_input_hash_preimage); @@ -165,19 +215,15 @@ impl BatchHash { // compute piHash for each chunk for i in [0..MAX_AGG_SNARKS) // chunk[i].piHash = // keccak( - // chain id || - // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot || - // chunk[i].datahash) + // chain id || + // chunk[i].prevStateRoot || + // chunk[i].postStateRoot || + // chunk[i].withdrawRoot || + // chunk[i].datahash || + // chunk[i].tx_data_hash + // ) for chunk in self.chunks_with_padding.iter() { - let chunk_public_input_hash_preimage = [ - self.chain_id.to_be_bytes().as_ref(), - chunk.prev_state_root.as_bytes(), - chunk.post_state_root.as_bytes(), - chunk.withdraw_root.as_bytes(), - chunk.data_hash.as_bytes(), - ] - .concat(); - res.push(chunk_public_input_hash_preimage) + res.push(chunk.extract_hash_preimage()); } // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) @@ -190,6 +236,19 @@ impl BatchHash { .collect(); res.push(batch_data_hash_preimage); + // This is the end of part where preimages to the keccak hashing function are of known + // size. We now move to the part where the preimage is dynamic. + // + // These include: + // - preimage for blob metadata + // - preimage for each chunk's flattened L2 signed tx data + // - preimage for the challenge digest + let blob_data = BlobData::from(self); + let dynamic_preimages = blob_data.preimages(); + for dynamic_preimage in dynamic_preimages { + res.push(dynamic_preimage); + } + res } diff --git a/aggregator/src/blob.rs b/aggregator/src/blob.rs new file mode 100644 index 0000000000..7954338f97 --- /dev/null +++ b/aggregator/src/blob.rs @@ -0,0 +1,658 @@ +use crate::{ + aggregation::{interpolate, BLS_MODULUS}, + BatchHash, ChunkHash, MAX_AGG_SNARKS, +}; + +use eth_types::U256; +use ethers_core::utils::keccak256; +use halo2_proofs::{ + circuit::Value, + halo2curves::{bls12_381::Scalar, bn256::Fr}, +}; +use itertools::Itertools; +use std::iter::{once, repeat}; +use zkevm_circuits::util::Challenges; + +/// The number of coefficients (BLS12-381 scalars) to represent the blob polynomial in evaluation +/// form. +pub const BLOB_WIDTH: usize = 4096; + +/// The number of bytes to represent an unsigned 256 bit number. +pub const N_BYTES_U256: usize = 32; + +/// The number data bytes we pack each BLS12-381 scalar into. The most-significant byte is 0. +pub const N_DATA_BYTES_PER_COEFFICIENT: usize = 31; + +/// The number of rows to encode number of valid chunks (num_valid_snarks) in a batch, in the Blob +/// Data config. Since num_valid_chunks is u16, we use 2 bytes/rows. +pub const N_ROWS_NUM_CHUNKS: usize = 2; + +/// The number of rows to encode the size of each chunk in a batch, in the Blob Data config. +/// chunk_size is u32, we use 4 bytes/rows. +pub const N_ROWS_CHUNK_SIZES: usize = MAX_AGG_SNARKS * 4; + +/// The number of bytes that we can fit in a blob. Note that each coefficient is represented in 32 +/// bytes, however, since those 32 bytes must represent a BLS12-381 scalar in its canonical form, +/// we explicitly set the most-significant byte to 0, effectively utilising only 31 bytes. +pub const N_BLOB_BYTES: usize = BLOB_WIDTH * N_DATA_BYTES_PER_COEFFICIENT; + +/// The number of rows in Blob Data config's layout to represent the "blob metadata" section. +pub const N_ROWS_METADATA: usize = N_ROWS_NUM_CHUNKS + N_ROWS_CHUNK_SIZES; + +/// The number of rows in Blob Data config's layout to represent the "chunk data" section. +pub const N_ROWS_DATA: usize = N_BLOB_BYTES - N_ROWS_METADATA; + +/// The number of rows in Blob Data config's layout to represent the "digest rlc" section. +pub const N_ROWS_DIGEST_RLC: usize = 1 + 1 + MAX_AGG_SNARKS; + +/// The number of rows in Blob Data config's layout to represent the "digest bytes" section. +pub const N_ROWS_DIGEST_BYTES: usize = N_ROWS_DIGEST_RLC * N_BYTES_U256; + +/// The total number of rows in "digest rlc" and "digest bytes" sections. +pub const N_ROWS_DIGEST: usize = N_ROWS_DIGEST_RLC + N_ROWS_DIGEST_BYTES; + +/// The total number of rows used in Blob Data config's layout. +pub const N_ROWS_BLOB_DATA_CONFIG: usize = N_ROWS_METADATA + N_ROWS_DATA + N_ROWS_DIGEST; + +/// Helper struct to generate witness for the Blob Data Config. +#[derive(Clone, Debug)] +pub struct BlobData { + /// The number of valid chunks in the batch. This could be any number between: + /// [1, MAX_AGG_SNARKS] + pub num_valid_chunks: u16, + /// The size of each chunk. The chunk size can be zero if: + /// - The chunk is a padded chunk (not a valid chunk). + /// - The chunk has no L2 transactions, but only L1 msg txs. + pub chunk_sizes: [u32; MAX_AGG_SNARKS], + /// Flattened L2 signed transaction data, for each chunk. + /// + /// Note that in BlobData struct, only `num_valid_chunks` number of chunks' bytes are supposed + /// to be read (for witness generation). For simplicity, the last valid chunk's bytes are + /// copied over for the padded chunks. The `chunk_data_digest` for padded chunks is the + /// `chunk_data_digest` of the last valid chunk (from Aggregation Circuit's perspective). + pub chunk_data: [Vec; MAX_AGG_SNARKS], +} + +impl From<&BatchHash> for BlobData { + fn from(batch_hash: &BatchHash) -> Self { + Self::new( + batch_hash.number_of_valid_chunks, + &batch_hash.chunks_with_padding, + ) + } +} + +// If the chunk data is represented as a vector of u8's this implementation converts data from +// dynamic number of chunks into BlobData. +impl From<&Vec>> for BlobData { + fn from(chunks: &Vec>) -> Self { + let num_valid_chunks = chunks.len(); + assert!(num_valid_chunks > 0); + assert!(num_valid_chunks <= MAX_AGG_SNARKS); + + let chunk_sizes: [u32; MAX_AGG_SNARKS] = chunks + .iter() + .map(|chunk| chunk.len() as u32) + .chain(repeat(0)) + .take(MAX_AGG_SNARKS) + .collect::>() + .try_into() + .expect("we have MAX_AGG_SNARKS chunks"); + assert!(chunk_sizes.iter().sum::() <= N_ROWS_DATA as u32); + + let last_chunk_data = chunks.last().expect("last chunk exists"); + let chunk_data = chunks + .iter() + .chain(repeat(last_chunk_data)) + .take(MAX_AGG_SNARKS) + .cloned() + .collect::>() + .try_into() + .expect("we have MAX_AGG_SNARKS chunks"); + + Self { + num_valid_chunks: num_valid_chunks as u16, + chunk_sizes, + chunk_data, + } + } +} + +impl Default for BlobData { + fn default() -> Self { + // default value corresponds to a batch with 1 chunk with no transactions + Self::from(&vec![vec![]]) + } +} + +impl BlobData { + pub(crate) fn new(num_valid_chunks: usize, chunks_with_padding: &[ChunkHash]) -> Self { + assert!(num_valid_chunks > 0); + assert!(num_valid_chunks <= MAX_AGG_SNARKS); + + // padded chunk has 0 size, valid chunk's size is the number of bytes consumed by the + // flattened data from signed L2 transactions. + let chunk_sizes: [u32; MAX_AGG_SNARKS] = chunks_with_padding + .iter() + .map(|chunk| { + if chunk.is_padding { + 0 + } else { + chunk.tx_bytes.len() as u32 + } + }) + .collect::>() + .try_into() + .unwrap(); + assert!(chunk_sizes.iter().sum::() <= N_ROWS_DATA as u32); + + // chunk data of the "last valid chunk" is repeated over the padded chunks for simplicity + // in calculating chunk_data_digest for those padded chunks. However, for the "chunk data" + // section rows (self.to_data_rows()) we only consider `num_valid_chunks` chunks. + let chunk_data = chunks_with_padding + .iter() + .map(|chunk| chunk.tx_bytes.to_vec()) + .collect::>>() + .try_into() + .unwrap(); + + Self { + num_valid_chunks: num_valid_chunks as u16, + chunk_sizes, + chunk_data, + } + } +} + +impl BlobData { + /// Get the preimage of the challenge digest. + pub(crate) fn get_challenge_digest_preimage(&self) -> Vec { + let metadata_digest = keccak256(self.to_metadata_bytes()); + let chunk_digests = self.chunk_data.iter().map(keccak256); + + // preimage = + // metadata_digest || + // chunk[0].chunk_data_digest || ... + // chunk[MAX_AGG_SNARKS-1].chunk_data_digest + // + // where chunk_data_digest for a padded chunk is set equal to the "last valid chunk"'s + // chunk_data_digest. + metadata_digest + .into_iter() + .chain(chunk_digests.flatten()) + .collect::>() + } + + /// Compute the challenge digest from blob bytes. + pub(crate) fn get_challenge_digest(&self) -> U256 { + let challenge_digest = keccak256(self.get_challenge_digest_preimage()); + U256::from_big_endian(&challenge_digest) + } + + /// Get the BLOB_WIDTH number of scalar field elements, as 32-bytes unsigned integers. + pub(crate) fn get_coefficients(&self) -> [U256; BLOB_WIDTH] { + let mut coefficients = [[0u8; N_BYTES_U256]; BLOB_WIDTH]; + + // We only consider the data from `valid` chunks and ignore the padded chunks. + let metadata_bytes = self.to_metadata_bytes(); + let blob_bytes = metadata_bytes.iter().chain( + self.chunk_data + .iter() + .take(self.num_valid_chunks as usize) + .flatten(), + ); + + for (i, &byte) in blob_bytes.enumerate() { + coefficients[i / 31][1 + (i % 31)] = byte; + } + + coefficients.map(|coeff| U256::from_big_endian(&coeff)) + } + + /// Get the list of preimages that need to go through the keccak hashing function, and + /// eventually required to be checked for the consistency of blob's metadata, its chunks' bytes + /// and the final blob preimage. + pub fn preimages(&self) -> Vec> { + let mut preimages = Vec::with_capacity(2 + MAX_AGG_SNARKS); + + // metadata + preimages.push(self.to_metadata_bytes()); + + // each valid chunk's data + for chunk in self.chunk_data.iter().take(self.num_valid_chunks as usize) { + preimages.push(chunk.to_vec()); + } + + // preimage for challenge digest + preimages.push(self.get_challenge_digest_preimage()); + + preimages + } +} + +impl BlobData { + /// Get the witness rows for assignment to the BlobDataConfig. + pub(crate) fn to_rows(&self, challenge: Challenges>) -> Vec> { + let metadata_rows = self.to_metadata_rows(challenge); + assert_eq!(metadata_rows.len(), N_ROWS_METADATA); + + let data_rows = self.to_data_rows(challenge); + assert_eq!(data_rows.len(), N_ROWS_DATA); + + let digest_rows = self.to_digest_rows(challenge); + assert_eq!(digest_rows.len(), N_ROWS_DIGEST); + + metadata_rows + .into_iter() + .chain(data_rows) + .chain(digest_rows) + .collect::>>() + } + + /// Get the blob bytes that encode the batch's metadata. + /// + /// metadata_bytes = + /// be_bytes(num_valid_chunks) || + /// be_bytes(chunks[0].chunk_size) || ... + /// be_bytes(chunks[MAX_AGG_SNARKS-1].chunk_size) + /// + /// where: + /// - chunk_size of a padded chunk is 0 + /// - num_valid_chunks is u16 + /// - each chunk_size is u32 + fn to_metadata_bytes(&self) -> Vec { + self.num_valid_chunks + .to_be_bytes() + .into_iter() + .chain( + self.chunk_sizes + .iter() + .flat_map(|chunk_size| chunk_size.to_be_bytes()), + ) + .collect() + } + + /// Get the witness rows for the "metadata" section of Blob data config. + fn to_metadata_rows(&self, challenge: Challenges>) -> Vec> { + // metadata bytes. + let bytes = self.to_metadata_bytes(); + + // accumulators represent the runnin linear combination of bytes. + let accumulators_iter = self + .num_valid_chunks + .to_be_bytes() + .into_iter() + .scan(0u64, |acc, x| { + *acc = *acc * 256 + (x as u64); + Some(*acc) + }) + .chain(self.chunk_sizes.into_iter().flat_map(|chunk_size| { + chunk_size.to_be_bytes().into_iter().scan(0u64, |acc, x| { + *acc = *acc * 256 + (x as u64); + Some(*acc) + }) + })); + + // digest_rlc is set only for the last row in the "metadata" section, and it denotes the + // RLC of the metadata_digest bytes. + let digest_rlc_iter = { + let digest = keccak256(&bytes); + let digest_rlc = digest.iter().fold(Value::known(Fr::zero()), |acc, &x| { + acc * challenge.evm_word() + Value::known(Fr::from(x as u64)) + }); + repeat(Value::known(Fr::zero())) + .take(N_ROWS_METADATA - 1) + .chain(once(digest_rlc)) + }; + + // preimage_rlc is the running RLC over bytes in the "metadata" section. + let preimage_rlc_iter = bytes.iter().scan(Value::known(Fr::zero()), |acc, &x| { + *acc = *acc * challenge.keccak_input() + Value::known(Fr::from(x as u64)); + Some(*acc) + }); + + bytes + .iter() + .zip_eq(accumulators_iter) + .zip_eq(preimage_rlc_iter) + .zip_eq(digest_rlc_iter) + .enumerate() + .map( + |(i, (((&byte, accumulator), preimage_rlc), digest_rlc))| BlobDataRow { + byte, + accumulator, + preimage_rlc, + digest_rlc, + // we set boundary on the last row of the "metadata" section to enable a lookup + // to the keccak table. + is_boundary: i == bytes.len() - 1, + ..Default::default() + }, + ) + .collect() + } + + /// Get the witness rows for the "chunk data" section of Blob data config. + fn to_data_rows(&self, challenge: Challenges>) -> Vec> { + // consider only the `valid` chunks while constructing rows for the "chunk data" section. + self.chunk_data + .iter() + .take(self.num_valid_chunks as usize) + .enumerate() + .flat_map(|(i, bytes)| { + let chunk_idx = (i + 1) as u64; + let chunk_size = bytes.len(); + let chunk_digest = keccak256(bytes); + let digest_rlc = chunk_digest + .iter() + .fold(Value::known(Fr::zero()), |acc, &byte| { + acc * challenge.evm_word() + Value::known(Fr::from(byte as u64)) + }); + bytes.iter().enumerate().scan( + (0u64, Value::known(Fr::zero())), + move |acc, (j, &byte)| { + acc.0 += 1; + acc.1 = + acc.1 * challenge.keccak_input() + Value::known(Fr::from(byte as u64)); + Some(BlobDataRow { + byte, + accumulator: acc.0, + chunk_idx, + is_boundary: j == chunk_size - 1, + is_padding: false, + preimage_rlc: acc.1, + digest_rlc: if j == chunk_size - 1 { + digest_rlc + } else { + Value::known(Fr::zero()) + }, + }) + }, + ) + }) + .chain(repeat(BlobDataRow::padding_row())) + .take(N_ROWS_DATA) + .collect() + } + + /// Get the witness rows for both "digest rlc" and "digest bytes" sections of Blob data config. + fn to_digest_rows(&self, challenge: Challenges>) -> Vec> { + let zero = Value::known(Fr::zero()); + + // metadata + let metadata_bytes = self.to_metadata_bytes(); + let metadata_digest = keccak256(metadata_bytes); + let metadata_digest_rlc = metadata_digest.iter().fold(zero, |acc, &byte| { + acc * challenge.evm_word() + Value::known(Fr::from(byte as u64)) + }); + + // chunk data + // Note: here we don't restrict to considering only `valid` chunks, as the + // `chunk_data_digest` gets repeated for the padded chunks, copying the last valid chunk's + // `chunk_data_digest`. + let (chunk_digests, chunk_digest_rlcs): (Vec<[u8; 32]>, Vec>) = self + .chunk_data + .iter() + .map(|chunk| { + let digest = keccak256(chunk); + let digest_rlc = digest.iter().fold(zero, |acc, &byte| { + acc * challenge.evm_word() + Value::known(Fr::from(byte as u64)) + }); + (digest, digest_rlc) + }) + .unzip(); + + // challenge digest + let challenge_digest_preimage = self.get_challenge_digest_preimage(); + let challenge_digest_preimage_rlc = + challenge_digest_preimage.iter().fold(zero, |acc, &byte| { + acc * challenge.keccak_input() + Value::known(Fr::from(byte as u64)) + }); + let challenge_digest = keccak256(&challenge_digest_preimage); + let challenge_digest_rlc = challenge_digest.iter().fold(zero, |acc, &byte| { + acc * challenge.evm_word() + Value::known(Fr::from(byte as u64)) + }); + + // - metadata digest rlc + // - chunks[i].chunk_data_digest rlc for each chunk + // - challenge digest rlc + // - metadata digest bytes + // - chunks[i].chunk_data_digest bytes for each chunk + // - challenge digest bytes + once(BlobDataRow { + digest_rlc: metadata_digest_rlc, + preimage_rlc: Value::known(Fr::zero()), + // this is_padding assignment does not matter as we have already crossed the "chunk + // data" section. This assignment to 1 is simply to allow the custom gate to check: + // - padding transitions from 0 -> 1 only once. + is_padding: true, + ..Default::default() + }) + .chain( + chunk_digest_rlcs + .iter() + .zip_eq(self.chunk_sizes.iter()) + .enumerate() + .map(|(i, (&digest_rlc, &chunk_size))| BlobDataRow { + digest_rlc, + chunk_idx: (i + 1) as u64, + accumulator: chunk_size as u64, + preimage_rlc: Value::known(Fr::zero()), + ..Default::default() + }), + ) + .chain(once(BlobDataRow { + preimage_rlc: challenge_digest_preimage_rlc, + digest_rlc: challenge_digest_rlc, + accumulator: 32 * (MAX_AGG_SNARKS + 1) as u64, + is_boundary: true, + ..Default::default() + })) + .chain(metadata_digest.iter().map(|&byte| BlobDataRow { + byte, + preimage_rlc: Value::known(Fr::zero()), + digest_rlc: Value::known(Fr::zero()), + ..Default::default() + })) + .chain(chunk_digests.iter().flat_map(|digest| { + digest.iter().map(|&byte| BlobDataRow { + byte, + preimage_rlc: Value::known(Fr::zero()), + digest_rlc: Value::known(Fr::zero()), + ..Default::default() + }) + })) + .chain(challenge_digest.iter().map(|&byte| BlobDataRow { + byte, + preimage_rlc: Value::known(Fr::zero()), + digest_rlc: Value::known(Fr::zero()), + ..Default::default() + })) + .collect() + } +} + +#[derive(Clone, Debug)] +pub struct BlobAssignments { + /// The random challenge scalar z. + pub challenge: U256, + /// The 32-bytes keccak digest for the challenge. We have the relation: + /// - challenge := challenge_digest % BLS_MODULUS. + pub challenge_digest: U256, + /// The evaluation of the blob polynomial at challenge. + pub evaluation: U256, + /// The blob polynomial represented in evaluation form. + pub coefficients: [U256; BLOB_WIDTH], +} + +impl Default for BlobAssignments { + fn default() -> Self { + Self { + challenge: U256::default(), + challenge_digest: U256::default(), + evaluation: U256::default(), + coefficients: [U256::default(); BLOB_WIDTH], + } + } +} + +impl From<&BlobData> for BlobAssignments { + fn from(blob: &BlobData) -> Self { + // blob polynomial in evaluation form. + // + // also termed P(x) + let coefficients = blob.get_coefficients(); + let coefficients_as_scalars = coefficients.map(|coeff| Scalar::from_raw(coeff.0)); + + // challenge := challenge_digest % BLS_MODULUS + // + // also termed z + let challenge_digest = blob.get_challenge_digest(); + let (_, challenge) = challenge_digest.div_mod(*BLS_MODULUS); + + // y = P(z) + let evaluation = U256::from_little_endian( + &interpolate( + Scalar::from_raw(challenge_digest.0), + &coefficients_as_scalars, + ) + .to_bytes(), + ); + + Self { + challenge, + challenge_digest, + evaluation, + coefficients, + } + } +} + +/// Witness row to the Blob Data Config. +#[derive(Clone, Copy, Default, Debug)] +pub struct BlobDataRow { + /// Byte value at this row. + pub byte: u8, + /// Multi-purpose accumulator value. + pub accumulator: u64, + /// The chunk index we are currently traversing. + pub chunk_idx: u64, + /// Whether this marks the end of a chunk. + pub is_boundary: bool, + /// Whether the row represents right-padded 0s to the blob data. + pub is_padding: bool, + /// A running accumulator of RLC of preimages. + pub preimage_rlc: Value, + /// RLC of the digest. + pub digest_rlc: Value, +} + +impl BlobDataRow { + fn padding_row() -> Self { + Self { + is_padding: true, + preimage_rlc: Value::known(Fr::zero()), + digest_rlc: Value::known(Fr::zero()), + ..Default::default() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "only required for logging challenge digest"] + fn log_challenge() { + for (annotation, tcase) in [ + ("single empty chunk", vec![vec![]]), + ("single non-empty chunk", vec![vec![1, 2, 3]]), + ("multiple empty chunks", vec![vec![], vec![]]), + ( + "multiple non-empty chunks", + vec![vec![1, 2, 3], vec![7, 8, 9]], + ), + ( + "empty chunk followed by non-empty chunk", + vec![vec![], vec![1, 2, 3]], + ), + ( + "non-empty chunk followed by empty chunk", + vec![vec![7, 8, 9], vec![]], + ), + ( + "max number of chunks all empty", + vec![vec![]; MAX_AGG_SNARKS], + ), + ( + "max number of chunkks all non-empty", + (0..MAX_AGG_SNARKS) + .map(|i| (10u8..11 + u8::try_from(i).unwrap()).collect()) + .collect(), + ), + ("single chunk blob full", vec![vec![123; N_ROWS_DATA]]), + ( + "multiple chunks blob full", + vec![vec![123; 1111], vec![231; N_ROWS_DATA - 1111]], + ), + ( + "max number of chunks only last one non-empty not full blob", + repeat(vec![]) + .take(MAX_AGG_SNARKS - 1) + .chain(once(vec![132; N_ROWS_DATA - 1111])) + .collect(), + ), + ( + "max number of chunks only last one non-empty full blob", + repeat(vec![]) + .take(MAX_AGG_SNARKS - 1) + .chain(once(vec![132; N_ROWS_DATA])) + .collect(), + ), + ( + "max number of chunks but last is empty", + repeat(vec![111; 100]) + .take(MAX_AGG_SNARKS - 1) + .chain(once(vec![])) + .collect(), + ), + ] + .iter() + { + let blob: BlobData = tcase.into(); + let blob_assignments = BlobAssignments::from(&blob); + println!( + "{:60}: challenge (z) = {:0>64x}, evaluation (y) = {:0>64x}", + annotation, blob_assignments.challenge, blob_assignments.evaluation + ); + } + } + + #[test] + fn default_blob_data() { + let mut default_metadata = [0u8; 62]; + default_metadata[1] = 1; + let default_metadata_digest = keccak256(default_metadata); + let default_chunk_digests = [keccak256([]); MAX_AGG_SNARKS]; + + assert_eq!( + BlobData::default().get_challenge_digest(), + U256::from(keccak256( + default_metadata_digest + .into_iter() + .chain(default_chunk_digests.into_iter().flatten()) + .collect::>() + )), + ) + } + + #[test] + fn coefficients_endianness() { + // Check that the blob bytes are being packed into coefficients in big endian order. + let coefficients = BlobData::default().get_coefficients(); + + assert_eq!(coefficients[0], U256::one() << 232); + assert_eq!(coefficients[1..], vec![U256::zero(); BLOB_WIDTH - 1]); + } +} diff --git a/aggregator/src/chunk.rs b/aggregator/src/chunk.rs index c22cd97db1..4be8a1e179 100644 --- a/aggregator/src/chunk.rs +++ b/aggregator/src/chunk.rs @@ -1,19 +1,20 @@ //! This module implements `Chunk` related data types. //! A chunk is a list of blocks. -use eth_types::{ToBigEndian, H256}; +use eth_types::{base64, ToBigEndian, H256}; use ethers_core::utils::keccak256; use halo2_proofs::halo2curves::bn256::Fr; use serde::{Deserialize, Serialize}; use std::iter; use zkevm_circuits::witness::Block; -#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize)] +#[derive(Default, Debug, Clone, Deserialize, Serialize)] /// A chunk is a set of continuous blocks. -/// A ChunkHash consists of 4 hashes, representing the changes incurred by this chunk of blocks: +/// A ChunkHash consists of 5 hashes, representing the changes incurred by this chunk of blocks: /// - state root before this chunk /// - state root after this chunk /// - the withdraw root after this chunk /// - the data hash of this chunk +/// - the tx data hash of this chunk /// - if the chunk is padded (en empty but valid chunk that is padded for aggregation) pub struct ChunkHash { /// Chain identifier @@ -26,6 +27,9 @@ pub struct ChunkHash { pub withdraw_root: H256, /// the data hash of this chunk pub data_hash: H256, + /// Flattened L2 tx bytes (RLP-signed) in this chunk. + #[serde(with = "base64")] + pub tx_bytes: Vec, /// if the chunk is a padded chunk pub is_padding: bool, } @@ -73,8 +77,12 @@ impl ChunkHash { .chain(b_ctx.gas_limit.to_be_bytes()) .chain(num_txs.to_be_bytes()) })) - // Tx Hashes - .chain(block.txs.iter().flat_map(|tx| tx.hash.to_fixed_bytes())) + // Tx Hashes (excluding L2 txs) + .chain(block.txs + .iter() + .filter(|tx| tx.tx_type.is_l1_msg()) + .flat_map(|tx| tx.hash.to_fixed_bytes()) + ) .collect::>(); let data_hash = H256(keccak256(data_bytes)); @@ -83,6 +91,13 @@ impl ChunkHash { hex::encode(data_hash.to_fixed_bytes()) ); + let tx_bytes = block + .txs + .iter() + .filter(|tx| !tx.tx_type.is_l1_msg()) + .flat_map(|tx| tx.rlp_signed.to_vec()) + .collect::>(); + let post_state_root = block .context .ctxs @@ -96,10 +111,17 @@ impl ChunkHash { post_state_root, withdraw_root: H256(block.withdraw_root.to_be_bytes()), data_hash, + tx_bytes: tx_bytes.to_vec(), is_padding, } } + /// The keccak256 hash of the flattened RLP-encoded signed tx bytes over all L2 txs in this + /// chunk. + pub(crate) fn tx_bytes_hash(&self) -> H256 { + H256(keccak256(&self.tx_bytes)) + } + /// Sample a chunk hash from random (for testing) #[cfg(test)] pub(crate) fn mock_random_chunk_hash_for_testing(r: &mut R) -> Self { @@ -111,12 +133,15 @@ impl ChunkHash { r.fill_bytes(&mut withdraw_root); let mut data_hash = [0u8; 32]; r.fill_bytes(&mut data_hash); + let mut tx_bytes = [0u8; 1024]; + r.fill_bytes(&mut tx_bytes); Self { chain_id: 0, prev_state_root: prev_state_root.into(), post_state_root: post_state_root.into(), withdraw_root: withdraw_root.into(), data_hash: data_hash.into(), + tx_bytes: tx_bytes.to_vec(), is_padding: false, } } @@ -134,19 +159,35 @@ impl ChunkHash { post_state_root: previous_chunk.post_state_root, withdraw_root: previous_chunk.withdraw_root, data_hash: previous_chunk.data_hash, + tx_bytes: previous_chunk.tx_bytes.clone(), is_padding: true, } } /// Public input hash for a given chunk is defined as - /// keccak( chain id || prev state root || post state root || withdraw root || data hash ) + /// keccak( + /// chain id || + /// prev state root || + /// post state root || + /// withdraw root || + /// chunk data hash || + /// chunk txdata hash + /// ) pub fn public_input_hash(&self) -> H256 { let preimage = self.extract_hash_preimage(); keccak256::<&[u8]>(preimage.as_ref()).into() } /// Extract the preimage for the hash - /// chain id || prev state root || post state root || withdraw root || data hash + /// + /// [ + /// chain id || + /// prev state root || + /// post state root || + /// withdraw root || + /// chunk data hash || + /// chunk txdata hash + /// ] pub fn extract_hash_preimage(&self) -> Vec { [ self.chain_id.to_be_bytes().as_ref(), @@ -154,6 +195,7 @@ impl ChunkHash { self.post_state_root.as_bytes(), self.withdraw_root.as_bytes(), self.data_hash.as_bytes(), + self.tx_bytes_hash().as_bytes(), ] .concat() } diff --git a/aggregator/src/constants.rs b/aggregator/src/constants.rs index 15e7ab72f0..aea4172918 100644 --- a/aggregator/src/constants.rs +++ b/aggregator/src/constants.rs @@ -12,11 +12,10 @@ pub(crate) const DIGEST_LEN: usize = 32; pub(crate) const INPUT_LEN_PER_ROUND: usize = 136; // TODO(ZZ): update to the right degree -#[allow(dead_code)] pub(crate) const LOG_DEGREE: u32 = 19; // ================================ -// indices for hash table +// indices for chunk pi hash table // ================================ // // the preimages are arranged as @@ -25,12 +24,29 @@ pub(crate) const LOG_DEGREE: u32 = 19; // - post_state_root 32 bytes // - withdraw_root 32 bytes // - chunk_data_hash 32 bytes -// +// - chunk_tx_data_hash 32 bytes pub(crate) const PREV_STATE_ROOT_INDEX: usize = 8; pub(crate) const POST_STATE_ROOT_INDEX: usize = 40; pub(crate) const WITHDRAW_ROOT_INDEX: usize = 72; pub(crate) const CHUNK_DATA_HASH_INDEX: usize = 104; +pub(crate) const CHUNK_TX_DATA_HASH_INDEX: usize = 136; + +// ================================ +// indices for batch pi hash table +// ================================ +// +// the preimages are arranged as +// - chain_id: 8 bytes +// - prev_state_root 32 bytes +// - post_state_root 32 bytes +// - withdraw_root 32 bytes +// - chunk_data_hash 32 bytes +// - z 32 bytes +// - y 32 bytes + +pub(crate) const BATCH_Z_OFFSET: usize = 136; +pub(crate) const BATCH_Y_OFFSET: usize = 168; // ================================ // aggregator parameters @@ -47,5 +63,4 @@ pub(crate) const BITS: usize = 88; /// Max number of snarks to be aggregated in a chunk. /// If the input size is less than this, dummy snarks /// will be padded. -// TODO: update me(?) pub const MAX_AGG_SNARKS: usize = 15; diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index f0bb11a84b..8dda2fc077 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -10,7 +10,7 @@ use halo2_proofs::{ use itertools::Itertools; use rand::Rng; use snark_verifier::{ - loader::{halo2::halo2_ecc::halo2_base, native::NativeLoader}, + loader::native::NativeLoader, pcs::{ kzg::{Bdfg21, Kzg, KzgAccumulator, KzgAs}, AccumulationSchemeProver, @@ -33,13 +33,16 @@ use zkevm_circuits::{ }; use crate::{ - constants::{CHAIN_ID_LEN, DIGEST_LEN, INPUT_LEN_PER_ROUND, LOG_DEGREE, MAX_AGG_SNARKS}, + constants::{ + BATCH_Y_OFFSET, BATCH_Z_OFFSET, CHAIN_ID_LEN, DIGEST_LEN, INPUT_LEN_PER_ROUND, LOG_DEGREE, + MAX_AGG_SNARKS, + }, util::{ assert_conditional_equal, assert_equal, assert_exist, get_indices, get_max_keccak_updates, parse_hash_digest_cells, parse_hash_preimage_cells, parse_pi_hash_rlc_cells, }, - AggregationConfig, RlcConfig, BITS, CHUNK_DATA_HASH_INDEX, LIMBS, POST_STATE_ROOT_INDEX, - PREV_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX, + AggregationConfig, RlcConfig, BITS, CHUNK_DATA_HASH_INDEX, CHUNK_TX_DATA_HASH_INDEX, LIMBS, + POST_STATE_ROOT_INDEX, PREV_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX, }; /// Subroutine for the witness generations. @@ -155,6 +158,20 @@ pub(crate) struct ExtractedHashCells { is_final_cells: Vec>, } +#[derive(Default)] +pub(crate) struct ExpectedBlobCells { + pub(crate) z: Vec>, + pub(crate) y: Vec>, + pub(crate) chunk_tx_data_digests: Vec>>, +} + +pub(crate) struct AssignedBatchHash { + pub(crate) hash_output: Vec>, + pub(crate) blob: ExpectedBlobCells, + pub(crate) num_valid_snarks: AssignedCell, + pub(crate) chunks_are_padding: Vec>, +} + /// Input the hash input bytes, /// assign the circuit for the hash function, /// return @@ -173,7 +190,8 @@ pub(crate) struct ExtractedHashCells { // 6. chunk[i]'s chunk_pi_hash_rlc_cells == chunk[i-1].chunk_pi_hash_rlc_cells when chunk[i] is // padded // 7. the hash input length are correct -// - first MAX_AGG_SNARKS + 1 hashes all have 136 bytes input +// - hashes[0] has 200 bytes +// - hashes[1..MAX_AGG_SNARKS+1] has 168 bytes input // - batch's data_hash length is 32 * number_of_valid_snarks // 8. batch data hash is correct w.r.t. its RLCs // 9. is_final_cells are set correctly @@ -183,7 +201,7 @@ pub(crate) fn assign_batch_hashes( challenges: Challenges>, chunks_are_valid: &[bool], preimages: &[Vec], -) -> Result>, Error> { +) -> Result { let extracted_hash_cells = extract_hash_cells( &config.keccak_circuit_config, layouter, @@ -208,7 +226,7 @@ pub(crate) fn assign_batch_hashes( // - batch's data_hash length is 32 * number_of_valid_snarks // 8. batch data hash is correct w.r.t. its RLCs // 9. is_final_cells are set correctly - conditional_constraints( + let (num_valid_snarks, chunks_are_padding) = conditional_constraints( &config.rlc_config, layouter, challenges, @@ -216,7 +234,25 @@ pub(crate) fn assign_batch_hashes( &extracted_hash_cells, )?; - Ok(extracted_hash_cells.hash_output_cells) + let batch_pi_input = &extracted_hash_cells.hash_input_cells[0..INPUT_LEN_PER_ROUND * 2]; + let expected_blob_cells = ExpectedBlobCells { + z: batch_pi_input[BATCH_Z_OFFSET..BATCH_Z_OFFSET + 32].to_vec(), + y: batch_pi_input[BATCH_Y_OFFSET..BATCH_Y_OFFSET + 32].to_vec(), + chunk_tx_data_digests: (0..MAX_AGG_SNARKS) + .map(|i| { + let chunk_pi_input = &extracted_hash_cells.hash_input_cells + [INPUT_LEN_PER_ROUND * (2 + 2 * i)..INPUT_LEN_PER_ROUND * (2 + 2 * (i + 1))]; + chunk_pi_input[CHUNK_TX_DATA_HASH_INDEX..CHUNK_TX_DATA_HASH_INDEX + 32].to_vec() + }) + .collect(), + }; + + Ok(AssignedBatchHash { + hash_output: extracted_hash_cells.hash_output_cells, + blob: expected_blob_cells, + num_valid_snarks, + chunks_are_padding, + }) } pub(crate) fn extract_hash_cells( @@ -237,11 +273,14 @@ pub(crate) fn extract_hash_cells( // chunk[0].prev_state_root || // chunk[k-1].post_state_root || // chunk[k-1].withdraw_root || - // batch_data_hash) + // batch_data_hash || + // z || + // y) // (2) chunk[i].piHash preimage = // (chain id || // chunk[i].prevStateRoot || chunk[i].postStateRoot || - // chunk[i].withdrawRoot || chunk[i].datahash) + // chunk[i].withdrawRoot || chunk[i].datahash || + // chunk[i].tx_data_hash) // (3) batchDataHash preimage = // (chunk[0].dataHash || ... || chunk[k-1].dataHash) // each part of the preimage is mapped to image by Keccak256 @@ -316,7 +355,12 @@ pub(crate) fn extract_hash_cells( hash_input_cells.len(), max_keccak_updates * INPUT_LEN_PER_ROUND ); - assert_eq!(hash_output_cells.len(), (MAX_AGG_SNARKS + 5) * DIGEST_LEN); + let num_digests = + (MAX_AGG_SNARKS * DIGEST_LEN + INPUT_LEN_PER_ROUND - 1) / INPUT_LEN_PER_ROUND; + assert_eq!( + hash_output_cells.len(), + (MAX_AGG_SNARKS + 1 + num_digests) * DIGEST_LEN + ); keccak_config .keccak_table @@ -385,7 +429,10 @@ fn copy_constraints( // chunk[0].prev_state_root || // chunk[k-1].post_state_root || // chunk[k-1].withdraw_root || - // batchData_hash ) + // batch_data_hash || + // z || + // y + // ) // // chunk[i].piHash = // keccak( @@ -393,7 +440,9 @@ fn copy_constraints( // chunk[i].prevStateRoot || // chunk[i].postStateRoot || // chunk[i].withdrawRoot || - // chunk[i].datahash) + // chunk[i].datahash || + // chunk[i].tx_data_hash + // ) // // PREV_STATE_ROOT_INDEX, POST_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX // used below are byte positions for @@ -487,18 +536,19 @@ fn copy_constraints( // 6. chunk[i]'s chunk_pi_hash_rlc_cells == chunk[i-1].chunk_pi_hash_rlc_cells when chunk[i] is // padded // 7. the hash input length are correct -// - first MAX_AGG_SNARKS + 1 hashes all have 136 bytes input +// - hashes[0] has 200 bytes +// - hashes[1..MAX_AGG_SNARKS+1] has 168 bytes input // - batch's data_hash length is 32 * number_of_valid_snarks // 8. batch data hash is correct w.r.t. its RLCs // 9. is_final_cells are set correctly +#[allow(clippy::type_complexity)] pub(crate) fn conditional_constraints( rlc_config: &RlcConfig, layouter: &mut impl Layouter, challenges: Challenges>, chunks_are_valid: &[bool], extracted_hash_cells: &ExtractedHashCells, -) -> Result<(), Error> { - let mut first_pass = halo2_base::SKIP_FIRST_PASS; +) -> Result<(AssignedCell, Vec>), Error> { let ExtractedHashCells { hash_input_cells, hash_output_cells, @@ -510,12 +560,10 @@ pub(crate) fn conditional_constraints( layouter .assign_region( || "rlc conditional constraints", - |mut region| -> Result<(), halo2_proofs::plonk::Error> { - if first_pass { - first_pass = false; - return Ok(()); - } - + |mut region| -> Result< + (AssignedCell, Vec>), + halo2_proofs::plonk::Error, + > { rlc_config.init(&mut region)?; let mut offset = 0; // ==================================================== @@ -650,7 +698,10 @@ pub(crate) fn conditional_constraints( // chunk[0].prev_state_root || // chunk[k-1].post_state_root || // chunk[k-1].withdraw_root || - // batch_data_hash ) + // batch_data_hash || + // z || + // y + // ) // // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) // @@ -725,7 +776,9 @@ pub(crate) fn conditional_constraints( // chunk[i].prevStateRoot || // chunk[i].postStateRoot || // chunk[i].withdrawRoot || - // chunk[i].datahash) + // chunk[i].datahash || + // chunk[i].tx_data_hash + // ) for i in 0..MAX_AGG_SNARKS { for j in 0..DIGEST_LEN { assert_conditional_equal( @@ -805,14 +858,23 @@ pub(crate) fn conditional_constraints( } // 7. the hash input length are correct - // - first MAX_AGG_SNARKS + 1 hashes all have 136 bytes input + // - hashes[0] has 200 bytes + // - hashes[1..MAX_AGG_SNARKS+1] has 168 bytes input // - batch's data_hash length is 32 * number_of_valid_snarks - // - first MAX_AGG_SNARKS + 1 hashes all have 136 bytes input + // - hashes[0] has 200 bytes + // note: hash_input_len_cells[0] is from dummy rows of keccak circuit. + let batch_pi_hash_input_cell = hash_input_len_cells[2].cell(); + region.constrain_equal( + batch_pi_hash_input_cell, + rlc_config.two_hundred_cell(batch_pi_hash_input_cell.region_index), + )?; + + // - hashes[1..MAX_AGG_SNARKS+1] has 168 bytes input hash_input_len_cells .iter() - .skip(1) - .take((MAX_AGG_SNARKS + 1) * 2) + .skip(3) // dummy (1) and batch pi hash (2) + .take(MAX_AGG_SNARKS * 2) .chunks(2) .into_iter() .try_for_each(|chunk| { @@ -820,7 +882,7 @@ pub(crate) fn conditional_constraints( region.constrain_equal( cur_hash_len.cell(), rlc_config - .one_hundred_and_thirty_six_cell(cur_hash_len.cell().region_index), + .one_hundred_and_sixty_eight_cell(cur_hash_len.cell().region_index), ) })?; @@ -907,7 +969,7 @@ pub(crate) fn conditional_constraints( // 8. batch data hash is correct w.r.t. its RLCs // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) let challenge_cell = - rlc_config.read_challenge(&mut region, challenges, &mut offset)?; + rlc_config.read_challenge1(&mut region, challenges, &mut offset)?; let flags = chunk_is_valid_cells .iter() @@ -1037,11 +1099,10 @@ pub(crate) fn conditional_constraints( .constrain_equal(left.cell(), rlc_config.one_cell(left.cell().region_index))?; log::trace!("rlc chip uses {} rows", offset); - Ok(()) + Ok((num_valid_snarks, chunks_are_padding)) }, ) - .map_err(|e| Error::AssertionFailure(format!("aggregation: {e}")))?; - Ok(()) + .map_err(|e| Error::AssertionFailure(format!("aggregation: {e}"))) } /// Input a list of flags whether the snark is valid diff --git a/aggregator/src/lib.rs b/aggregator/src/lib.rs index f90bbcb560..59afcad26e 100644 --- a/aggregator/src/lib.rs +++ b/aggregator/src/lib.rs @@ -1,8 +1,12 @@ +#![feature(lazy_cell)] + /// proof aggregation mod aggregation; /// This module implements `Batch` related data types. /// A batch is a list of chunk. mod batch; +/// blob struct and constants +mod blob; // This module implements `Chunk` related data types. // A chunk is a list of blocks. mod chunk; diff --git a/aggregator/src/param.rs b/aggregator/src/param.rs index eb3f482096..23102d9db5 100644 --- a/aggregator/src/param.rs +++ b/aggregator/src/param.rs @@ -20,8 +20,8 @@ impl ConfigParams { Self { strategy: FpStrategy::Simple, degree: 19, - num_advice: vec![64], - num_lookup_advice: vec![8], + num_advice: vec![75], + num_lookup_advice: vec![10], num_fixed: 2, lookup_bits: 18, limb_bits: BITS, diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index 36e1ac5ab8..3ed7a3a6ec 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -1,4 +1,5 @@ mod aggregation; +mod blob; mod compression; mod mock_chunk; mod rlc; diff --git a/aggregator/src/tests/aggregation.rs b/aggregator/src/tests/aggregation.rs index 6df4c0f0ad..77e6fc59a6 100644 --- a/aggregator/src/tests/aggregation.rs +++ b/aggregator/src/tests/aggregation.rs @@ -118,11 +118,14 @@ fn build_new_aggregation_circuit(num_real_chunks: usize) -> AggregationCircuit { let circuits = chunks_with_padding .iter() .take(num_real_chunks) - .map(|&chunk| MockChunkCircuit::new(true, chunk)) + .map(|chunk| MockChunkCircuit::new(true, chunk.clone())) .collect_vec(); circuits .iter() - .map(|&circuit| layer_0!(circuit, MockChunkCircuit, params, k0, path)) + .map(|circuit| { + let circuit = circuit.clone(); + layer_0!(circuit, MockChunkCircuit, params, k0, path) + }) .collect_vec() }; diff --git a/aggregator/src/tests/blob.rs b/aggregator/src/tests/blob.rs new file mode 100644 index 0000000000..463c4db392 --- /dev/null +++ b/aggregator/src/tests/blob.rs @@ -0,0 +1,245 @@ +use crate::{ + aggregation::{ + AssignedBarycentricEvaluationConfig, BarycentricEvaluationConfig, BlobDataConfig, RlcConfig, + }, + blob::{BlobAssignments, BlobData, N_ROWS_DATA}, + param::ConfigParams, + MAX_AGG_SNARKS, +}; +use halo2_base::{ + gates::range::{RangeConfig, RangeStrategy}, + Context, ContextParams, +}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, SimpleFloorPlanner}, + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, + plonk::{Circuit, ConstraintSystem, Error}, +}; +use zkevm_circuits::{ + table::{KeccakTable, RangeTable, U8Table}, + util::Challenges, +}; + +struct BlobCircuit { + data: BlobData, +} + +#[derive(Clone, Debug)] +struct BlobConfig { + challenges: Challenges, + + keccak_table: KeccakTable, + + rlc: RlcConfig, + blob_data: BlobDataConfig, + barycentric: BarycentricEvaluationConfig, +} + +impl Circuit for BlobCircuit { + type Config = BlobConfig; + type FloorPlanner = SimpleFloorPlanner; + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let u8_table = U8Table::construct(meta); + let range_table = RangeTable::construct(meta); + let challenges = Challenges::construct(meta); + let keccak_table = KeccakTable::construct(meta); + + let rlc = RlcConfig::configure(meta, challenges); + + let parameters = ConfigParams::aggregation_param(); + let range = RangeConfig::::configure( + meta, + RangeStrategy::Vertical, + ¶meters.num_advice, + ¶meters.num_lookup_advice, + parameters.num_fixed, + parameters.lookup_bits, + 0, + parameters.degree.try_into().unwrap(), + ); + let barycentric = BarycentricEvaluationConfig::construct(range); + + let challenge_expressions = challenges.exprs(meta); + let blob_data = BlobDataConfig::configure( + meta, + challenge_expressions, + u8_table, + range_table, + &keccak_table, + ); + + BlobConfig { + challenges, + + keccak_table, + + rlc, + blob_data, + barycentric, + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenge_values = config.challenges.values(&layouter); + + config + .keccak_table + .dev_load(&mut layouter, &self.data.preimages(), &challenge_values)?; + + let mut first_pass = halo2_base::SKIP_FIRST_PASS; + let barycentric_assignments = layouter.assign_region( + || "barycentric config", + |region| -> Result { + if first_pass { + first_pass = false; + return Ok(AssignedBarycentricEvaluationConfig::default()); + } + + let gate = &config.barycentric.scalar.range.gate; + let mut ctx = Context::new( + region, + ContextParams { + max_rows: gate.max_rows, + num_context_ids: 1, + fixed_columns: gate.constants.clone(), + }, + ); + + let blob = BlobAssignments::from(&self.data); + Ok(config.barycentric.assign( + &mut ctx, + &blob.coefficients, + blob.challenge_digest, + blob.evaluation, + )) + }, + )?; + + let chunks_are_padding = layouter.assign_region( + || "dev: chunks are padding or not", + |mut region| -> Result>, Error> { + let rlc_config = &config.rlc; + rlc_config.init(&mut region)?; + let mut rlc_config_offset = 0; + + let mut chunks_are_padding = Vec::with_capacity(MAX_AGG_SNARKS); + for i in 0..MAX_AGG_SNARKS { + let is_padding = (i as u16) >= self.data.num_valid_chunks; + chunks_are_padding.push(rlc_config.load_private( + &mut region, + &Fr::from(is_padding as u64), + &mut rlc_config_offset, + )?); + } + + Ok(chunks_are_padding) + }, + )?; + + config.blob_data.assign( + &mut layouter, + challenge_values, + &config.rlc, + &chunks_are_padding, + &self.data, + &barycentric_assignments.barycentric_assignments, + )?; + + Ok(()) + } +} + +fn check_circuit(data: BlobData) -> Result<(), Vec> { + let k = 20; + let mock_prover = + MockProver::::run(k, &BlobCircuit { data }, vec![]).expect("failed to run mock prover"); + mock_prover.verify_par() +} + +#[test] +fn blob_circuit_completeness() { + // single chunk in batch, but the chunk has a size of N_ROWS_DATA + let full_blob = vec![vec![123; N_ROWS_DATA]]; + let all_empty_chunks: Vec> = vec![vec![]; MAX_AGG_SNARKS]; + let one_chunk = vec![vec![2, 3, 4, 100, 1]]; + let two_chunks = vec![vec![100; 1000], vec![2, 3, 4, 100, 1]]; + let max_chunks: Vec> = (0..MAX_AGG_SNARKS) + .map(|i| (10u8..10 + u8::try_from(i).unwrap()).collect()) + .collect(); + let empty_chunk_followed_by_nonempty_chunk = vec![vec![], vec![3, 100, 24, 30]]; + let nonempty_chunk_followed_by_empty_chunk = vec![vec![3, 100, 24, 30], vec![]]; + let empty_and_nonempty_chunks = vec![ + vec![3, 100, 24, 30], + vec![], + vec![], + vec![100, 23, 34, 24, 10], + vec![], + ]; + let all_empty_except_last = std::iter::repeat(vec![]) + .take(MAX_AGG_SNARKS - 1) + .chain(std::iter::once(vec![3, 100, 24, 30])) + .collect::>(); + + for blob in [ + full_blob, + one_chunk, + two_chunks, + max_chunks, + all_empty_chunks, + empty_chunk_followed_by_nonempty_chunk, + nonempty_chunk_followed_by_empty_chunk, + empty_and_nonempty_chunks, + all_empty_except_last, + ] { + assert_eq!(check_circuit(BlobData::from(&blob)), Ok(()), "{:?}", blob); + } +} + +fn generic_blob_data() -> BlobData { + BlobData::from(&vec![ + vec![3, 100, 24, 30], + vec![], + vec![100; 300], + vec![100, 23, 34, 24, 10], + vec![200; 20], + vec![], + vec![200; 20], + ]) +} + +#[test] +fn inconsistent_chunk_size() { + let mut blob_data = generic_blob_data(); + blob_data.chunk_sizes[4] += 1; + assert!(check_circuit(blob_data).is_err()); +} + +#[test] +fn too_many_empty_chunks() { + let mut blob_data = generic_blob_data(); + blob_data.num_valid_chunks += 1; + assert!(check_circuit(blob_data).is_err()); +} + +#[test] +fn too_few_empty_chunks() { + let mut blob_data = generic_blob_data(); + blob_data.num_valid_chunks -= 1; + assert!(check_circuit(blob_data).is_err()); +} + +#[test] +fn inconsistent_chunk_bytes() { + let mut blob_data = generic_blob_data(); + blob_data.chunk_data[0].push(128); + assert!(check_circuit(blob_data).is_err()); +} diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index 49beba4d88..338a374adb 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -27,7 +27,7 @@ pub struct MockConfig { pub(crate) instance: Column, } -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Default, Clone)] /// A mock chunk circuit /// /// This mock chunk circuit simulates a zkEVM circuit. diff --git a/aggregator/src/util.rs b/aggregator/src/util.rs index 7281bf256b..f50c000120 100644 --- a/aggregator/src/util.rs +++ b/aggregator/src/util.rs @@ -10,13 +10,31 @@ use zkevm_circuits::keccak_circuit::keccak_packed_multi::{ // needed for the number of snarks pub(crate) fn get_max_keccak_updates(max_snarks: usize) -> usize { // The public input hash for the batch is derived from hashing - // chain_id || chunk_0's prev_state || chunk_k-1's post_state || - // chunk_k-1's withdraw_root || batch_data_hash. - // In total there're 168 bytes. Therefore 2 pi rounds are required. + // [ + // chain_id || + // chunk_0's prev_state || + // chunk_k-1's post_state || + // chunk_k-1's withdraw_root || + // batch_data_hash || + // z || + // y + // ] + // + // In total there are 200 bytes. Therefore 2 keccak-f rounds are required, + // as in a single round we can absorb 136 bytes (INPUT_LEN_PER_ROUND). let pi_rounds = 2; // Hash for each chunk is derived from hashing the chunk's - // chain_id || prev_state || post_state || withdraw_root || data_hash - // Each chunk hash therefore also requires 2 keccak rounds for 168 bytes. + // [ + // chain_id || + // prev_state || + // post_state || + // withdraw_root || + // chunk_data_hash || + // tx_data_hash + // ] + // + // In total there are 168 bytes, therefore each chunk hash + // also requires 2 keccak rounds for 168 bytes. let chunk_hash_rounds = 2 * max_snarks; let data_hash_rounds = get_data_hash_keccak_updates(max_snarks); @@ -177,12 +195,14 @@ pub(crate) fn parse_hash_preimage_cells( Vec<&[AssignedCell]>, &[AssignedCell], ) { - // each pi hash has INPUT_LEN_PER_ROUND bytes as input - // keccak will pad the input with another INPUT_LEN_PER_ROUND bytes // we extract all those bytes + // batch_pi_hash's input has 200 bytes which means + // its keccak takes two rounds of keccak-f permutation let batch_pi_hash_preimage = &hash_input_cells[0..INPUT_LEN_PER_ROUND * 2]; let mut chunk_pi_hash_preimages = vec![]; for i in 0..MAX_AGG_SNARKS { + // each chunk_pi_hash's input has 168 bytes which means + // each chunk takes two rounds of keccak-f permutation chunk_pi_hash_preimages.push( &hash_input_cells[INPUT_LEN_PER_ROUND * 2 * (i + 1)..INPUT_LEN_PER_ROUND * 2 * (i + 2)], ); diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index 7fc367a2bd..099115f877 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -26,7 +26,7 @@ hex.workspace = true strum_macros.workspace = true # precompile related crates -revm-precompile = { git = "https://github.com/scroll-tech/revm", branch = "scroll-fix" } +revm-precompile.workspace = true [dev-dependencies] hex.workspace = true @@ -45,7 +45,8 @@ test = ["mock", "rand"] scroll = ["eth-types/scroll", "mock?/scroll"] # Enable shanghai feature of mock only if mock is enabled (by test). shanghai = ["eth-types/shanghai", "mock?/shanghai"] +strict-ccc = [] tracer-tests = ["enable-memory"] enable-stack = ["eth-types/enable-stack", "mock?/enable-stack"] enable-memory = ["eth-types/enable-memory", "mock?/enable-memory"] -enable-storage = ["eth-types/enable-storage", "mock?/enable-storage"] \ No newline at end of file +enable-storage = ["eth-types/enable-storage", "mock?/enable-storage"] diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index b3bdf8d572..2bce0f9c74 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -29,6 +29,7 @@ use eth_types::{ self, evm_types::GasCost, geth_types, + geth_types::TxType, sign_types::{pk_bytes_le, pk_bytes_swap_endianness, SignData}, Address, GethExecTrace, ToBigEndian, ToWord, Word, H256, }; @@ -818,6 +819,10 @@ fn keccak_inputs_pi_circuit( "start_l1_queue_index in keccak_inputs: {}", start_l1_queue_index ); + let l1transactions = transactions + .iter() + .filter(|&tx| tx.tx_type == TxType::L1Msg) + .collect::>(); let data_bytes = iter::empty() .chain(block_headers.iter().flat_map(|(&block_num, block)| { let num_l2_txs = transactions @@ -852,13 +857,25 @@ fn keccak_inputs_pi_circuit( .chain(num_txs.to_be_bytes()) })) // Tx Hashes - .chain(transactions.iter().flat_map(|tx| tx.hash.to_fixed_bytes())) + .chain( + l1transactions + .iter() + .flat_map(|&tx| tx.hash.to_fixed_bytes()), + ) .collect::>(); let data_hash = H256(keccak256(&data_bytes)); log::debug!( "chunk data hash: {}", hex::encode(data_hash.to_fixed_bytes()) ); + + let chunk_txbytes = transactions + .iter() + .filter(|&tx| tx.tx_type != TxType::L1Msg) + .flat_map(|tx| tx.rlp_bytes.clone()) + .collect::>(); + let chunk_txbytes_hash = H256(keccak256(chunk_txbytes)); + let after_state_root = block_headers .last_key_value() .map(|(_, blk)| blk.eth_block.state_root) @@ -869,6 +886,7 @@ fn keccak_inputs_pi_circuit( .chain(after_state_root.to_fixed_bytes()) .chain(withdraw_trie_root.to_be_bytes()) .chain(data_hash.to_fixed_bytes()) + .chain(chunk_txbytes_hash.to_fixed_bytes()) .collect::>(); vec![data_bytes, pi_bytes] @@ -880,6 +898,7 @@ pub fn keccak_inputs_tx_circuit(txs: &[geth_types::Transaction]) -> Result>>(); let dummy_hash_data = { @@ -890,6 +909,13 @@ pub fn keccak_inputs_tx_circuit(txs: &[geth_types::Transaction]) -> Result>(); + inputs.push(chunk_txbytes); + let sign_datas: Vec = txs .iter() .enumerate() diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index 5bf77d0b63..726c834f85 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -348,6 +348,7 @@ impl Block { self.copy_events.push(event); // Each byte needs 2 rows // TODO: magic num + if self.copy_counter > 500_000 && cfg!(feature = "strict-ccc") { log::error!("copy event len overflow {}", self.copy_counter); panic!("copy event len overflow"); diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index eab7c65315..3fb03a12e4 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -175,6 +175,7 @@ impl<'a> CircuitInputStateRef<'a> { max_rws }; let rwc = self.block_ctx.rwc.0; + if rwc > effective_limit && cfg!(feature = "strict-ccc") { log::error!("rwc > max_rws, rwc={}, max_rws={}", rwc, max_rws); return Err(Error::InternalError("rws not enough")); diff --git a/bus-mapping/src/evm/opcodes/sload.rs b/bus-mapping/src/evm/opcodes/sload.rs index b8f39a4feb..753f7d8c70 100644 --- a/bus-mapping/src/evm/opcodes/sload.rs +++ b/bus-mapping/src/evm/opcodes/sload.rs @@ -58,16 +58,14 @@ impl Opcode for Sload { // Storage read let value_from_statedb = *state.sdb.get_storage(&contract_addr, &key).1; - { - let value_from_step = geth_step.storage.get_or_err(&key)?; - // 1. value_from_step == value_from_statedb - assert_eq!(value_from_step, value_from_statedb, "inconsistent sload: step proof {value_from_step:?}, local statedb {value_from_statedb:?} in contract {contract_addr:?}, key {key:?}",); + #[cfg(feature = "enable-stack")] + assert_eq!( + value_from_statedb, + geth_steps[1].stack.last()?, + "inconsistent sload: step proof {value_from_statedb:?}, result {:?} in contract {contract_addr:?}, key {key:?}", geth_steps[1].stack.last()?, + ); - // 2. value_from_step == value_from_stack - #[cfg(feature = "enable-stack")] - assert_eq!(value_from_step, geth_steps[1].stack.last()?, "inconsistent sload: step proof {value_from_step:?}, result {:?} in contract {contract_addr:?}, key {key:?}", geth_steps[1].stack.last()?); - } let value = value_from_statedb; let is_warm = state diff --git a/eth-types/Cargo.toml b/eth-types/Cargo.toml index c3fa380526..1ba0b09f3e 100644 --- a/eth-types/Cargo.toml +++ b/eth-types/Cargo.toml @@ -23,6 +23,7 @@ num-bigint.workspace = true strum_macros.workspace = true strum.workspace = true hash-circuit.workspace = true +base64.workspace = true [features] default = ["warn-unimplemented"] diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index 1119679a88..6fc26588c0 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -61,6 +61,29 @@ use crate::evm_types::Stack; #[cfg(feature = "enable-storage")] use crate::evm_types::Storage; +/// Used for FFI with Golang. Bytes in golang will be serialized as base64 by default. +pub mod base64 { + use base64::{decode, encode}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// serialize bytes as base64 + pub fn serialize(data: &[u8], s: S) -> Result + where + S: Serializer, + { + String::serialize(&encode(data), s) + } + + /// deserialize base64 to bytes + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(d)?; + decode(s.as_bytes()).map_err(serde::de::Error::custom) + } +} + /// Trait used to reduce verbosity with the declaration of the [`Field`] /// trait and its repr. pub trait Field: diff --git a/eth-types/src/sign_types.rs b/eth-types/src/sign_types.rs index 196adbab2e..25d0e5a2af 100644 --- a/eth-types/src/sign_types.rs +++ b/eth-types/src/sign_types.rs @@ -162,8 +162,7 @@ pub fn recover_pk2( msg_hash: &[u8; 32], ) -> Result { debug_assert!(v == 0 || v == 1, "recovery ID (v) is boolean"); - let recovery_id = RecoveryId::from_byte(v).expect("normalized recovery id always valid"); - let recoverable_sig = { + let mut recoverable_sig = { let mut r_bytes = [0u8; 32]; let mut s_bytes = [0u8; 32]; r.to_big_endian(&mut r_bytes); @@ -172,6 +171,12 @@ pub fn recover_pk2( let gas: &GenericArray = GenericArray::from_slice(&s_bytes); K256Signature::from_scalars(*gar, *gas).map_err(|_| Error::Signature)? }; + let mut v = v; + if let Some(sig_normalized) = recoverable_sig.normalize_s() { + recoverable_sig = sig_normalized; + v ^= 1; + }; + let recovery_id = RecoveryId::from_byte(v).expect("normalized recovery id always valid"); let verify_key = VerifyingKey::recover_from_prehash(msg_hash.as_ref(), &recoverable_sig, recovery_id) .map_err(|_| Error::Signature)?; diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 845cba878e..2c1f620f39 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -17,7 +17,7 @@ snark-verifier.workspace = true snark-verifier-sdk.workspace = true anyhow.workspace = true -base64 = "0.13.0" +base64.workspace = true blake2 = "0.10.3" chrono = "0.4.19" dotenvy = "0.15.7" @@ -41,4 +41,5 @@ default = [] parallel_syn = ["halo2_proofs/parallel_syn", "zkevm-circuits/parallel_syn"] scroll = ["bus-mapping/scroll", "eth-types/scroll", "zkevm-circuits/scroll"] shanghai = ["bus-mapping/shanghai", "eth-types/shanghai", "zkevm-circuits/shanghai"] +strict-ccc = ["bus-mapping/strict-ccc", "zkevm-circuits/strict-ccc"] test = [] diff --git a/prover/src/aggregator.rs b/prover/src/aggregator.rs index 7b8f21b530..4385fbbd15 100644 --- a/prover/src/aggregator.rs +++ b/prover/src/aggregator.rs @@ -2,4 +2,5 @@ mod prover; mod verifier; pub use self::prover::Prover; +pub use aggregator::{BatchHash, MAX_AGG_SNARKS}; pub use verifier::Verifier; diff --git a/prover/src/aggregator/prover.rs b/prover/src/aggregator/prover.rs index 7477e5aa26..8cd7e01c47 100644 --- a/prover/src/aggregator/prover.rs +++ b/prover/src/aggregator/prover.rs @@ -132,7 +132,7 @@ impl Prover { if real_chunk_count < MAX_AGG_SNARKS { let padding_snark = layer2_snarks.last().unwrap().clone(); - let mut padding_chunk_hash = *chunk_hashes.last().unwrap(); + let mut padding_chunk_hash = chunk_hashes.last().unwrap().clone(); padding_chunk_hash.is_padding = true; // Extend to MAX_AGG_SNARKS for both chunk hashes and layer-2 snarks. @@ -188,7 +188,7 @@ macro_rules! compare_field { fn check_chunk_hashes(name: &str, chunk_hashes_proofs: &[(ChunkHash, ChunkProof)]) -> Result<()> { for (idx, (in_arg, chunk_proof)) in chunk_hashes_proofs.iter().enumerate() { - if let Some(in_proof) = chunk_proof.chunk_hash { + if let Some(in_proof) = &chunk_proof.chunk_hash { compare_field!(name, idx, chain_id, in_arg, in_proof); compare_field!(name, idx, prev_state_root, in_arg, in_proof); compare_field!(name, idx, post_state_root, in_arg, in_proof); diff --git a/prover/src/common/prover/aggregation.rs b/prover/src/common/prover/aggregation.rs index 5db14ef359..678204103e 100644 --- a/prover/src/common/prover/aggregation.rs +++ b/prover/src/common/prover/aggregation.rs @@ -27,7 +27,7 @@ impl Prover { AggregationCircuit::new(self.params(degree), previous_snarks, &mut rng, batch_hash) .map_err(|err| anyhow!("Failed to construct aggregation circuit: {err:?}"))?; - self.gen_snark(id, degree, &mut rng, circuit) + self.gen_snark(id, degree, &mut rng, circuit, "gen_agg_snark") } pub fn load_or_gen_agg_snark( diff --git a/prover/src/common/prover/compression.rs b/prover/src/common/prover/compression.rs index 94ccaa1eeb..4fd3de12b5 100644 --- a/prover/src/common/prover/compression.rs +++ b/prover/src/common/prover/compression.rs @@ -24,8 +24,7 @@ impl Prover { let circuit = CompressionCircuit::new(self.params(degree), prev_snark, has_accumulator, &mut rng) .map_err(|err| anyhow!("Failed to construct compression circuit: {err:?}"))?; - - self.gen_snark(id, degree, &mut rng, circuit) + self.gen_snark(id, degree, &mut rng, circuit, "gen_comp_snark") } pub fn load_or_gen_comp_snark( diff --git a/prover/src/common/prover/evm.rs b/prover/src/common/prover/evm.rs index 0314fe20a3..ad43729700 100644 --- a/prover/src/common/prover/evm.rs +++ b/prover/src/common/prover/evm.rs @@ -53,7 +53,10 @@ impl Prover { Self::assert_if_mock_prover(id, degree, &circuit); let (params, pk) = self.params_and_pk(id, degree, &circuit)?; - + log::info!( + "super_circuit vk transcript_repr {:?}", + pk.get_vk().transcript_repr() + ); let instances = circuit.instances(); let num_instance = circuit.num_instance(); let proof = gen_evm_proof_shplonk(params, pk, circuit, instances.clone(), rng); diff --git a/prover/src/common/prover/inner.rs b/prover/src/common/prover/inner.rs index 011b7c6076..a647cc1a99 100644 --- a/prover/src/common/prover/inner.rs +++ b/prover/src/common/prover/inner.rs @@ -30,6 +30,10 @@ impl Prover { Self::assert_if_mock_prover(id, degree, &circuit); let (params, pk) = self.params_and_pk(id, degree, &C::dummy_inner_circuit())?; + log::info!( + "gen_inner_snark vk transcript_repr {:?}", + pk.get_vk().transcript_repr() + ); let snark = gen_snark_shplonk(params, pk, circuit, &mut rng, None::); Ok(snark) diff --git a/prover/src/common/prover/utils.rs b/prover/src/common/prover/utils.rs index d071654b8b..c78e46d603 100644 --- a/prover/src/common/prover/utils.rs +++ b/prover/src/common/prover/utils.rs @@ -16,11 +16,18 @@ impl Prover { degree: u32, rng: &mut (impl Rng + Send), circuit: C, + desc: &str, ) -> Result { Self::assert_if_mock_prover(id, degree, &circuit); let (params, pk) = self.params_and_pk(id, degree, &circuit)?; + log::info!( + "gen_snark id {} desc {} vk transcript_repr {:?}", + id, + desc, + pk.get_vk().transcript_repr() + ); Ok(gen_snark_shplonk(params, pk, circuit, rng, None::)) } diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 8ee5a90d82..50f57c8580 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -13,6 +13,7 @@ pub mod types; pub mod utils; pub mod zkevm; +pub use aggregator::{BatchHash, MAX_AGG_SNARKS}; pub use common::{ChunkHash, CompressionCircuit}; pub use eth_types::l2_types::BlockTrace; pub use proof::{BatchProof, ChunkProof, EvmProof, Proof}; diff --git a/prover/src/types.rs b/prover/src/types.rs index fb145fa941..3058a518d5 100644 --- a/prover/src/types.rs +++ b/prover/src/types.rs @@ -9,23 +9,4 @@ pub type WitnessBlock = Block; pub struct BlockTraceJsonRpcResult { pub result: BlockTrace, } - -pub mod base64 { - use base64::{decode, encode}; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - pub fn serialize(data: &[u8], s: S) -> Result - where - S: Serializer, - { - String::serialize(&encode(data), s) - } - - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let s = String::deserialize(d)?; - decode(s.as_bytes()).map_err(serde::de::Error::custom) - } -} +pub use eth_types::base64; diff --git a/prover/src/zkevm/capacity_checker.rs b/prover/src/zkevm/capacity_checker.rs index 73eba4fe55..a890cdde78 100644 --- a/prover/src/zkevm/capacity_checker.rs +++ b/prover/src/zkevm/capacity_checker.rs @@ -53,6 +53,7 @@ impl RowUsage { (MAX_BYTECODE, 0.95), // bytecode (MAX_RWS, 0.95), // copy (MAX_KECCAK_ROWS, 0.95), // keccak + (MAX_KECCAK_ROWS, 0.95), // sha256 (MAX_VERTICAL_ROWS, 0.95), // tx (MAX_CALLDATA, 0.95), // rlp (7 * MAX_EXP_STEPS, 0.95), // exp @@ -216,8 +217,8 @@ impl CircuitCapacityChecker { if code_db.0.insert(hash, bytes).is_some() { assert_eq!(rows[2].name, "bytecode"); rows[2].row_num_real -= bytes_len + 1; - assert_eq!(rows[10].name, "poseidon"); - rows[10].row_num_real -= bytes_len / (31 * 2) * 9; + assert_eq!(rows[11].name, "poseidon"); + rows[11].row_num_real -= bytes_len / (31 * 2) * 9; } } diff --git a/prover/src/zkevm/circuit/l2_builder.rs b/prover/src/zkevm/circuit/l2_builder.rs index d1c23b00b3..fcd0320efb 100644 --- a/prover/src/zkevm/circuit/l2_builder.rs +++ b/prover/src/zkevm/circuit/l2_builder.rs @@ -73,15 +73,25 @@ pub fn calculate_row_usage_of_witness_block( let mut rows = ::Inner::min_num_rows_block_subcircuits( witness_block, ); - assert_eq!(rows[10].name, "poseidon"); - assert_eq!(rows[13].name, "mpt"); - // empirical estimation is each row in mpt cost 1.5 hash (aka 12 rows) - let mpt_poseidon_rows = rows[13].row_num_real * 12; + assert_eq!(rows[11].name, "poseidon"); + assert_eq!(rows[14].name, "mpt"); + // We collected real metrics from Scroll mainnet, and here is the graph + // https://ibb.co/gVfvW7h + // 6 is already very very conservative. Besides, considering a chunk consists of many txs, + // using this number is safe. + let poseidon_estimate_ratio = if witness_block.txs.len() > 1 { + // follower ccc + 6 + } else { + // singer ccc or single tx block follower ccc, + // even i think 6 is safe, here we still keep the old value + 12 + }; + let mpt_poseidon_rows = rows[14].row_num_real * poseidon_estimate_ratio; if witness_block.mpt_updates.smt_traces.is_empty() { - rows[10].row_num_real += mpt_poseidon_rows; + rows[11].row_num_real += mpt_poseidon_rows; log::debug!("calculate_row_usage_of_witness_block light mode, adding {mpt_poseidon_rows} poseidon rows"); } else { - //rows[10].row_num_real += mpt_poseidon_rows; log::debug!("calculate_row_usage_of_witness_block normal mode, skip adding {mpt_poseidon_rows} poseidon rows"); } diff --git a/prover/src/zkevm/prover.rs b/prover/src/zkevm/prover.rs index 6d634d91ec..ced25bf30e 100644 --- a/prover/src/zkevm/prover.rs +++ b/prover/src/zkevm/prover.rs @@ -14,6 +14,7 @@ use eth_types::l2_types::BlockTrace; pub struct Prover { // Make it public for testing with inner functions (unnecessary for FFI). pub inner: common::Prover, + verifier: Option, raw_vk: Option>, } @@ -22,15 +23,22 @@ impl Prover { let inner = common::Prover::from_params_dir(params_dir, &ZKEVM_DEGREES); let raw_vk = try_to_read(assets_dir, &CHUNK_VK_FILENAME); - if raw_vk.is_none() { + let verifier = if raw_vk.is_none() { log::warn!( "zkevm-prover: {} doesn't exist in {}", *CHUNK_VK_FILENAME, assets_dir ); + None + } else { + Some(super::verifier::Verifier::from_dirs(params_dir, assets_dir)) + }; + + Self { + inner, + raw_vk, + verifier, } - - Self { inner, raw_vk } } pub fn get_vk(&self) -> Option> { @@ -73,7 +81,9 @@ impl Prover { self.check_and_clear_raw_vk(); - match output_dir.and_then(|output_dir| ChunkProof::from_json_file(output_dir, &name).ok()) { + let chunk_proof = match output_dir + .and_then(|output_dir| ChunkProof::from_json_file(output_dir, &name).ok()) + { Some(proof) => Ok(proof), None => { let chunk_hash = ChunkHash::from_witness_block(&witness_block, false); @@ -87,7 +97,15 @@ impl Prover { result } + }?; + + if let Some(verifier) = &self.verifier { + if !verifier.verify_chunk_proof(chunk_proof.clone()) { + anyhow::bail!("chunk prover cannot generate valid proof"); + } } + + Ok(chunk_proof) } fn check_and_clear_raw_vk(&mut self) { diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 27d10c1c33..233369dc4c 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -62,6 +62,7 @@ scroll = ["bus-mapping/scroll", "eth-types/scroll", "mock?/scroll", "zktrie", "p # Enable shanghai feature of mock only if mock is enabled (by test). shanghai = ["bus-mapping/shanghai", "eth-types/shanghai", "mock?/shanghai"] +strict-ccc = ["bus-mapping/strict-ccc"] test-circuits = [] warn-unimplemented = ["eth-types/warn-unimplemented"] onephase = [] # debug only diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 1442c9a508..8e4ac09a0d 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -53,7 +53,7 @@ pub struct EvmCircuitConfig { sig_table: SigTable, modexp_table: ModExpTable, ecc_table: EccTable, - pow_of_rand_table: PowOfRandTable, + pub(crate) pow_of_rand_table: PowOfRandTable, } /// Circuit configuration arguments @@ -334,7 +334,6 @@ impl SubCircuit for EvmCircuit { config.load_fixed_table(layouter, self.fixed_table_tags.clone())?; config.load_byte_table(layouter)?; - config.pow_of_rand_table.assign(layouter, challenges)?; let export = config.execution.assign_block(layouter, block, challenges)?; self.exports.borrow_mut().replace(export); Ok(()) @@ -551,7 +550,9 @@ impl Circuit for EvmCircuit { &block.get_ec_pairing_ops(), &challenges, )?; - + config + .pow_of_rand_table + .assign(&mut layouter, &challenges, 2048)?; self.synthesize_sub(&config, &challenges, &mut layouter) } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index dae0031d99..5d268cf917 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -451,20 +451,14 @@ impl ExecutionConfig { .chain(first_step_check) .chain(last_step_check) }); - - meta.create_gate("q_step", |meta| { + meta.create_gate("q_step_first", |meta| { let q_usable = meta.query_fixed(q_usable, Rotation::cur()); let q_step_first = meta.query_selector(q_step_first); - let q_step_last = meta.query_selector(q_step_last); let q_step = meta.query_advice(q_step, Rotation::cur()); - let num_rows_left_cur = meta.query_advice(num_rows_until_next_step, Rotation::cur()); - let num_rows_left_next = meta.query_advice(num_rows_until_next_step, Rotation::next()); - let num_rows_left_inverse = meta.query_advice(num_rows_inv, Rotation::cur()); - let mut cb = BaseConstraintBuilder::default(); // q_step needs to be enabled on the first row // rw_counter starts at 1 - cb.condition(q_step_first, |cb| { + cb.condition(q_usable, |cb| { cb.require_equal("q_step == 1", q_step.clone(), 1.expr()); cb.require_equal( "rw_counter is initialized to be 1", @@ -472,6 +466,27 @@ impl ExecutionConfig { 1.expr(), ) }); + cb.gate(q_step_first) + }); + meta.create_gate("q_step_last", |meta| { + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); + let q_step_last = meta.query_selector(q_step_last); + let q_step = meta.query_advice(q_step, Rotation::cur()); + let mut cb = BaseConstraintBuilder::default(); + // q_step needs to be enabled on the last row + cb.condition(q_usable, |cb| { + cb.require_equal("q_step == 1", q_step.clone(), 1.expr()); + }); + cb.gate(q_step_last) + }); + meta.create_gate("q_step", |meta| { + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); + let q_step = meta.query_advice(q_step, Rotation::cur()); + let num_rows_left_cur = meta.query_advice(num_rows_until_next_step, Rotation::cur()); + let num_rows_left_next = meta.query_advice(num_rows_until_next_step, Rotation::next()); + let num_rows_left_inverse = meta.query_advice(num_rows_inv, Rotation::cur()); + + let mut cb = BaseConstraintBuilder::default(); // For every step, is_create and is_root are boolean. cb.condition(q_step.clone(), |cb| { cb.require_boolean( @@ -480,12 +495,8 @@ impl ExecutionConfig { ); cb.require_boolean("step.is_root is boolean", step_curr.state.is_root.expr()); }); - // q_step needs to be enabled on the last row - cb.condition(q_step_last, |cb| { - cb.require_equal("q_step == 1", q_step.clone(), 1.expr()); - }); // Except when step is enabled, the step counter needs to decrease by 1 - cb.condition(1.expr() - q_step.clone(), |cb| { + cb.condition(not::expr(q_step.clone()), |cb| { cb.require_equal( "num_rows_left_cur := num_rows_left_next + 1", num_rows_left_cur.clone(), @@ -1274,11 +1285,11 @@ impl ExecutionConfig { debug_assert_eq!(region1_height, region1_height_sum); // part2: assign non-last EndBlock steps when padding needed - + assert!(region2_height >= 1); let (region2_chunk_size, region2_chunk_num) = chunking_fn("region2", region2_height, 300); - let idxs: Vec = (0..region2_height).collect(); - let mut region2_is_first_time = vec![true; region2_chunk_num]; - + let mut region2_is_first_time: Vec<(usize, bool)> = (0..region2_chunk_num) + .map(|chunk_idx| (chunk_idx, true)) + .collect(); log::trace!( "assign non-last EndBlock in range [{},{})", region1_height, @@ -1286,18 +1297,25 @@ impl ExecutionConfig { ); layouter.assign_regions( || "Execution step region2", - idxs.chunks(region2_chunk_size) - .zip_eq(region2_is_first_time.iter_mut()) - .map(|(rows, is_first_time)| { + region2_is_first_time + .iter_mut() + .map(|(chunk_idx, is_first_time)| { |mut region: Region<'_, F>| { + let chunk_idx = *chunk_idx; + let begin = chunk_idx * region2_chunk_size; + let end = ((chunk_idx + 1) * region2_chunk_size).min(region2_height); + let region_height = end - begin; if *is_first_time { *is_first_time = false; - return assign_shape_fn(&mut region, rows.len()); + return assign_shape_fn(&mut region, region_height); + } + if chunk_idx == 0 && region1_height == 0 { + self.q_step_first.enable(&mut region, 0)?; } self.assign_same_exec_step_in_range( &mut region, 0, - rows.len(), + region_height, block, &dummy_tx, &last_call, @@ -1305,10 +1323,10 @@ impl ExecutionConfig { 1, challenges, )?; - for row_idx in 0..rows.len() { + for row_idx in 0..region_height { self.assign_q_step(&mut region, &inverter, row_idx, 1)?; } - Ok(rows.len()) + Ok(region_height) } }) .collect_vec(), diff --git a/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs index cd34c14e9f..b073d84b0e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs @@ -98,7 +98,7 @@ impl ExecutionGadget for EndInnerBlockGadget { rw_counter: Transition::Same, // We propagate call_id so that EndBlock can get the last tx_id // in order to count processed txs. - call_id: Transition::Same, + // call_id: Transition::Same, ..StepStateTransition::any() }); diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index 7c15785a68..ddf921cf13 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -894,6 +894,7 @@ pub fn multi_keccak( capacity: Option, ) -> Result>, Error> { log::info!("multi_keccak assign with capacity: {:?}", capacity); + let mut rows: Vec> = Vec::new(); if let Some(capacity) = capacity { rows.reserve((1 + capacity * (NUM_ROUNDS + 1)) * get_num_rows_per_round()); diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index 40c95973bb..99930d4aad 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -11,7 +11,7 @@ use std::{cell::RefCell, collections::BTreeMap, iter, marker::PhantomData, str:: use crate::{evm_circuit::util::constraint_builder::ConstrainBuilderCommon, table::KeccakTable}; use bus_mapping::circuit_input_builder::get_dummy_tx_hash; -use eth_types::{Address, Field, Hash, ToBigEndian, ToWord, Word, H256}; +use eth_types::{geth_types::TxType, Address, Field, Hash, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; use halo2_proofs::plonk::{Assigned, Expression, Fixed, Instance}; @@ -32,7 +32,7 @@ use crate::{ RPI_LENGTH_ACC_CELL_IDX, RPI_RLC_ACC_CELL_IDX, TIMESTAMP_OFFSET, }, state_circuit::StateCircuitExports, - tx_circuit::{CHAIN_ID_OFFSET as CHAIN_ID_OFFSET_IN_TX, TX_HASH_OFFSET, TX_LEN}, + tx_circuit::{CHAIN_ID_OFFSET as CHAIN_ID_OFFSET_IN_TX, TX_LEN}, witness::{self, Block, BlockContext, BlockContexts, Transaction}, }; use bus_mapping::util::read_env_var; @@ -143,6 +143,11 @@ impl PublicData { self.block_ctxs.ctxs.len() ); let num_all_txs_in_blocks = self.get_num_all_txs(); + let l1transactions = self + .transactions + .iter() + .filter(|&tx| tx.tx_type == TxType::L1Msg) + .collect::>(); let result = iter::empty() .chain(self.block_ctxs.ctxs.iter().flat_map(|(block_num, block)| { // sanity check on coinbase & difficulty @@ -176,16 +181,16 @@ impl PublicData { })) // Tx Hashes .chain( - self.transactions + l1transactions .iter() - .flat_map(|tx| tx.hash.to_fixed_bytes()), + .flat_map(|&tx| tx.hash.to_fixed_bytes()), ) .collect::>(); assert_eq!( result.len(), BLOCK_HEADER_BYTES_NUM * self.block_ctxs.ctxs.len() - + KECCAK_DIGEST_SIZE * self.transactions.len() + + KECCAK_DIGEST_SIZE * l1transactions.len() ); result } @@ -194,7 +199,24 @@ impl PublicData { H256(keccak256(self.data_bytes())) } - fn pi_bytes(&self, data_hash: H256) -> Vec { + /// Obtain the l2 tx (not padding; right now padding txs are l2 txs by default) bytes in the + /// chunk + fn chunk_txbytes(&self) -> Vec { + let mut result: Vec = vec![]; + let chunk_txs_iter = self.transactions.iter().filter(|&tx| tx.is_chunk_l2_tx()); + + for tx in chunk_txs_iter { + result.extend_from_slice(&tx.rlp_signed); + } + + result + } + + fn get_chunk_txbytes_hash(&self) -> H256 { + H256(keccak256(self.chunk_txbytes())) + } + + fn pi_bytes(&self, data_hash: H256, chunk_txbytes_hash: H256) -> Vec { iter::empty() .chain(self.chain_id.to_be_bytes()) // state roots @@ -203,6 +225,7 @@ impl PublicData { .chain(self.withdraw_trie_root.to_fixed_bytes()) // data hash .chain(data_hash.to_fixed_bytes()) + .chain(chunk_txbytes_hash.to_fixed_bytes()) .collect::>() } @@ -212,8 +235,13 @@ impl PublicData { "[pi] chunk data hash: {}", hex::encode(data_hash.to_fixed_bytes()) ); + let chunk_txbytes_hash = H256(keccak256(self.chunk_txbytes())); + log::debug!( + "[pi] chunk txbytes hash: {}", + hex::encode(chunk_txbytes_hash.to_fixed_bytes()) + ); - let pi_bytes = self.pi_bytes(data_hash); + let pi_bytes = self.pi_bytes(data_hash, chunk_txbytes_hash); let pi_hash = keccak256(pi_bytes); H256(pi_hash) @@ -267,14 +295,22 @@ impl PublicData { + self.max_txs * KECCAK_DIGEST_SIZE } - fn pi_bytes_start_offset(&self) -> usize { + fn q_chunk_txbytes_start_offset(&self) -> usize { self.data_bytes_end_offset() + 1 // a row is reserved for the keccak256(rlc(data_bytes)) == data_hash lookup. - + 1 // new row. + + 1 // new row + } + + fn q_chunk_txbytes_end_offset(&self) -> usize { + self.q_chunk_txbytes_start_offset() // chunk_txbytes section is a singular row + } + + fn pi_bytes_start_offset(&self) -> usize { + self.q_chunk_txbytes_end_offset() + 1 // new row. } fn pi_bytes_end_offset(&self) -> usize { - self.pi_bytes_start_offset() + N_BYTES_U64 + N_BYTES_WORD * 4 + self.pi_bytes_start_offset() + N_BYTES_U64 + N_BYTES_WORD * 5 } fn pi_hash_start_offset(&self) -> usize { @@ -349,6 +385,7 @@ pub struct PiCircuitConfig { real_rpi: Column, q_tx_hashes: Column, q_block_context: Column, + q_chunk_txbytes: Column, // indicates a single row // columns for assertion about cum_num_txs in block table cum_num_txs: Column, @@ -438,6 +475,7 @@ impl SubCircuitConfig for PiCircuitConfig { let q_block_context = meta.fixed_column(); let q_tx_hashes = meta.fixed_column(); + let q_chunk_txbytes = meta.fixed_column(); let q_not_end = meta.complex_selector(); // We are accumulating bytes for three different purposes @@ -461,6 +499,7 @@ impl SubCircuitConfig for PiCircuitConfig { meta.enable_equality(block_table.value); // copy block to rpi meta.enable_equality(block_table.index); meta.enable_equality(tx_table.value); // copy tx hashes to rpi + meta.enable_equality(tx_table.chunk_txbytes_hash_rlc); // copy chunk_txbytes_hash meta.enable_equality(cum_num_txs); meta.enable_equality(pi); @@ -624,7 +663,7 @@ impl SubCircuitConfig for PiCircuitConfig { // The layout for entire pi circuit looks like // data bytes: | rpi | rpi_bytes | rpi_bytes_acc | rpi_rlc_acc | rpi_length_acc | // | .. | .. | ... | dbs_rlc | input_len | - // q_keccak = 1 | dbs_rlc | .. | ... | dh_rlc | input_len | + // chunk_txbytes: | .. | .. | ... | ... | ... | // chain_id | chain_id| .. | ... | ... | ... | // prev_state_root | .. | .. | ... | ... | ... | // after_state_root | .. | .. | ... | ... | ... | @@ -657,6 +696,30 @@ impl SubCircuitConfig for PiCircuitConfig { .collect() }); + // Look up L1Msg TxHash in data bytes section from TxTable + meta.lookup_any("tx.hash (L1Msg)", |meta| { + let enable = and::expr([ + meta.query_fixed(q_tx_hashes, Rotation::cur()), + not::expr(meta.query_advice(is_rpi_padding, Rotation::cur())), + ]); + + let l1_tx_hash = meta.query_advice(rpi, Rotation::cur()); + + let input_exprs = vec![ + 1.expr(), // q_enable = true + l1_tx_hash, + (TxType::L1Msg as u64).expr(), + ]; + let tx_table_l1_pi_exprs = tx_table.l1_pi_exprs(meta); + assert_eq!(input_exprs.len(), tx_table_l1_pi_exprs.len()); + + input_exprs + .into_iter() + .zip(tx_table_l1_pi_exprs) + .map(|(input, table)| (enable.clone() * input, table)) + .collect() + }); + // 3. constrain block_table meta.create_gate( "cum_num_txs::next == cum_num_txs::cur + (block_table.tag == NumTxs) ? block_table.value : 0", @@ -703,6 +766,7 @@ impl SubCircuitConfig for PiCircuitConfig { real_rpi, q_tx_hashes, q_field_step, + q_chunk_txbytes, is_field_rlc, q_not_end, is_rlc_keccak, @@ -756,26 +820,34 @@ impl PiCircuitConfig { /// | |------------------------|--------------------------| /// | | rlc(data_bytes) | <- q_keccak == 1 | /// |----------|------------------------|--------------------------| + /// | *PART 2* | | | + /// | ASSIGN | | | + /// | CHUNK | chunk_txbytes_hash_rlc | <- q_chunk_txbytes == 1 | + /// | TXBYTES | | | + /// | (L2TX) | | | + /// |----------|------------------------|--------------------------| /// | | rpi initialise | | /// | | chain_id | | - /// | *PART 2* | prev_state_root | | + /// | *PART 3* | prev_state_root | | /// | | next_state_root | | /// | ASSIGN | withdraw_trie_root | | /// | PI | data_hash | | - /// | BYTES |------------------------|--------------------------| + /// | BYTES | chunk_txbytes_hash | | + /// | |------------------------|--------------------------| /// | | rlc(pi_bytes) | <- q_keccak == 1 | /// |----------|------------------------|--------------------------| - /// | *PART 3* | rpi initialise | | + /// | *PART 4* | rpi initialise | | /// | ASSIGN | pi_hash_hi | | /// | PI HASH | pi_hash_lo | | /// |----------|------------------------|--------------------------| - /// | *PART 4* | rpi initialise | | + /// | *PART 5* | rpi initialise | | /// | ASSIGN | coinbase | | /// | CONSTS | difficulty | | /// |----------|------------------------|--------------------------| /// - /// Where each one of the rows above, i.e. block\[0\].number, block\[0\].timestamp, ..., - /// pi_hash_lo, coinbase, difficulty are assigned using the assign_field method. + /// Where each one of the rows above, i.e. block\[0\].number, block\[0\].timestamp, + /// ..., pi_hash_lo, coinbase, difficulty are assigned using the + /// assign_field method. /// /// Each `field` takes multiple rows in the actual circuit layout depending on how many bytes /// it takes to represent the said field. For instance, pi_hash_lo represent the lower 16 bytes @@ -799,12 +871,16 @@ impl PiCircuitConfig { 0, /* offset == 0 */ public_data, block_value_cells, - tx_value_cells, challenges, )?; + debug_assert_eq!(offset, public_data.q_chunk_txbytes_start_offset()); + + // 2. Assign chunk tx bytes. + let (offset, chunk_txbytes_hash_rlc_cell) = + self.assign_chunk_txbytes(region, offset, public_data, tx_value_cells, challenges)?; debug_assert_eq!(offset, public_data.pi_bytes_start_offset()); - // 2. Assign public input bytes. + // 3. Assign public input bytes. let (offset, pi_hash_rlc_cell, connections) = self.assign_pi_bytes( region, offset, @@ -812,16 +888,17 @@ impl PiCircuitConfig { block_value_cells, tx_value_cells, &data_hash_rlc_cell, + &chunk_txbytes_hash_rlc_cell, challenges, )?; debug_assert_eq!(offset, public_data.pi_hash_start_offset()); - // 3. Assign public input hash (hi, lo) decomposition. + // 4. Assign public input hash (hi, lo) decomposition. let (offset, pi_hash_cells) = self.assign_pi_hash(region, offset, public_data, &pi_hash_rlc_cell, challenges)?; debug_assert_eq!(offset, public_data.constants_start_offset()); - // 4. Assign block coinbase and difficulty. + // 5. Assign block coinbase and difficulty. let offset = self.assign_constants(region, offset, public_data, block_value_cells, challenges)?; debug_assert_eq!(offset, public_data.constants_end_offset() + 1); @@ -837,7 +914,6 @@ impl PiCircuitConfig { offset: usize, public_data: &PublicData, block_value_cells: &[AssignedCell], - tx_value_cells: &[AssignedCell], challenges: &Challenges>, ) -> Result<(usize, AssignedCell), Error> { // Initialise the RLC accumulator and length values. @@ -943,26 +1019,28 @@ impl PiCircuitConfig { } // Assign tx hash values. - let n_txs = public_data.transactions.len(); + // First, Assign actual L1Msg hashes + let transactions = public_data + .transactions + .iter() + .filter(|&tx| tx.tx_type == TxType::L1Msg) + .collect::>(); + let n_txs = transactions.len(); let mut tx_copy_cells = vec![]; let mut data_bytes_rlc = None; let mut data_bytes_length = None; - for (i, tx_hash) in public_data - .transactions + for (i, tx_hash) in transactions .iter() .map(|tx| tx.hash) - .chain(std::iter::repeat(get_dummy_tx_hash())) .take(public_data.max_txs) .enumerate() { - let is_rpi_padding = i >= n_txs; - let (tmp_offset, tmp_rpi_rlc_acc, tmp_rpi_length, cells) = self.assign_field( region, offset, &tx_hash.to_fixed_bytes(), RpiFieldType::DefaultType, - is_rpi_padding, + false, rpi_rlc_acc, rpi_length, challenges, @@ -971,20 +1049,37 @@ impl PiCircuitConfig { rpi_rlc_acc = tmp_rpi_rlc_acc; rpi_length = tmp_rpi_length; tx_copy_cells.push(cells[RPI_CELL_IDX].clone()); + if i == public_data.max_txs - 1 { data_bytes_rlc = Some(cells[RPI_RLC_ACC_CELL_IDX].clone()); data_bytes_length = Some(cells[RPI_LENGTH_ACC_CELL_IDX].clone()); } } - // Copy tx_hashes to tx table - log::trace!("tx_copy_cells: {:?}", tx_copy_cells); - log::trace!("tx_value_cells: {:?}", tx_value_cells); - for (i, tx_hash_cell) in tx_copy_cells.into_iter().enumerate() { - region.constrain_equal( - tx_hash_cell.cell(), - tx_value_cells[i * TX_LEN + TX_HASH_OFFSET - 1].cell(), + // Assign padding hashes + for (i, tx_hash) in iter::empty() + .chain(std::iter::repeat(get_dummy_tx_hash())) + .take(public_data.max_txs - n_txs) + .enumerate() + { + let (tmp_offset, tmp_rpi_rlc_acc, tmp_rpi_length, cells) = self.assign_field( + region, + offset, + &tx_hash.to_fixed_bytes(), + RpiFieldType::DefaultType, + true, + rpi_rlc_acc, + rpi_length, + challenges, )?; + offset = tmp_offset; + rpi_rlc_acc = tmp_rpi_rlc_acc; + rpi_length = tmp_rpi_length; + + if i == (public_data.max_txs - n_txs) - 1 { + data_bytes_rlc = Some(cells[RPI_RLC_ACC_CELL_IDX].clone()); + data_bytes_length = Some(cells[RPI_LENGTH_ACC_CELL_IDX].clone()); + } } // Assign row for validating lookup to check: @@ -1015,9 +1110,52 @@ impl PiCircuitConfig { }; self.q_keccak.enable(region, offset)?; + // After the data bytes, an empty header row is provided for the chunk_txbytes section Ok((offset + 1, data_hash_rlc_cell)) } + /// Assign chunk txbytes hash + /// i.e. keccak256(rlc(chunk_txbytes)) == chunk_txbytes_hash. + /// + /// The chunk_txbytes_hash section consists of only one row containing the final hash + /// Its correctness is established from a copy constraint into the tx circuit, + /// where the actual accumulation for chunk_txbytes is done. + fn assign_chunk_txbytes( + &self, + region: &mut Region<'_, F>, + offset: usize, + public_data: &PublicData, + tx_value_cells: &[AssignedCell], + challenges: &Challenges>, + ) -> Result<(usize, AssignedCell), Error> { + region.assign_fixed( + || "q_chunk_txbytes", + self.q_chunk_txbytes, + offset, + || Value::known(F::one()), + )?; + + let chunk_txbytes_hash_rlc_cell = { + let chunk_txbytes_hash_rlc = rlc_be_bytes( + &public_data.get_chunk_txbytes_hash().to_fixed_bytes(), + challenges.evm_word(), + ); + region.assign_advice( + || "chunk_txbytes_hash_rlc", + self.rpi_rlc_acc, + offset, + || chunk_txbytes_hash_rlc, + )? + }; + + region.constrain_equal( + tx_value_cells[public_data.max_txs * TX_LEN].cell(), + chunk_txbytes_hash_rlc_cell.cell(), + )?; + + Ok((offset + 1, chunk_txbytes_hash_rlc_cell)) + } + /// Assign public input bytes, that represent the pre-image to pi_hash. /// i.e. keccak256(rlc(pi_bytes)) == pi_hash. #[allow(clippy::too_many_arguments)] @@ -1030,6 +1168,7 @@ impl PiCircuitConfig { block_value_cells: &[AssignedCell], tx_value_cells: &[AssignedCell], data_hash_rlc_cell: &AssignedCell, + chunk_txbytes_hash_rlc_cell: &AssignedCell, challenges: &Challenges>, ) -> Result<(usize, AssignedCell, Connections), Error> { let (mut offset, mut rpi_rlc_acc, mut rpi_length) = self.assign_rlc_init(region, offset)?; @@ -1085,7 +1224,7 @@ impl PiCircuitConfig { }; // Assign data_hash - (offset, _, _, cells) = self.assign_field( + (offset, rpi_rlc_acc, rpi_length, cells) = self.assign_field( region, offset, &public_data.get_data_hash().to_fixed_bytes(), @@ -1096,12 +1235,31 @@ impl PiCircuitConfig { challenges, )?; let data_hash_cell = cells[RPI_CELL_IDX].clone(); - let pi_bytes_rlc = cells[RPI_RLC_ACC_CELL_IDX].clone(); - let pi_bytes_length = cells[RPI_LENGTH_ACC_CELL_IDX].clone(); // Copy data_hash value we collected from assigning data bytes. region.constrain_equal(data_hash_rlc_cell.cell(), data_hash_cell.cell())?; + // Assign chunk txbytes hash + (offset, _, _, cells) = self.assign_field( + region, + offset, + &public_data.get_chunk_txbytes_hash().to_fixed_bytes(), + RpiFieldType::DefaultType, + false, + rpi_rlc_acc, + rpi_length, + challenges, + )?; + let chunk_txbytes_hash_cell = cells[RPI_CELL_IDX].clone(); + let pi_bytes_rlc = cells[RPI_RLC_ACC_CELL_IDX].clone(); + let pi_bytes_length = cells[RPI_LENGTH_ACC_CELL_IDX].clone(); + + // Copy chunk_txbytes_hash value from the previous section. + region.constrain_equal( + chunk_txbytes_hash_cell.cell(), + chunk_txbytes_hash_rlc_cell.cell(), + )?; + // Assign row for validating lookup to check: // pi_hash == keccak256(rlc(pi_bytes)) pi_bytes_rlc.copy_advice( @@ -1748,6 +1906,7 @@ impl SubCircuit for PiCircuit { let num_rows = 1 + max_inner_blocks * BLOCK_HEADER_BYTES_NUM + max_txs * KECCAK_DIGEST_SIZE + 1 // for data hash row + + 1 // for chunk txbytes hash row + 1 // for pi bytes start row + N_BYTES_U64 // chain_id + 4 * KECCAK_DIGEST_SIZE // state_roots & data hash diff --git a/zkevm-circuits/src/pi_circuit/dev.rs b/zkevm-circuits/src/pi_circuit/dev.rs index a6c991e688..4bb1225664 100644 --- a/zkevm-circuits/src/pi_circuit/dev.rs +++ b/zkevm-circuits/src/pi_circuit/dev.rs @@ -123,13 +123,16 @@ impl for SuperCircuitConfig { sig_table, u8_table, u16_table, + pow_of_rand_table, challenges: challenges_expr.clone(), }, ); @@ -660,6 +661,10 @@ impl< layouter: &mut impl Layouter, ) -> Result<(), Error> { log::debug!("assigning evm_circuit"); + config + .evm_circuit + .pow_of_rand_table + .assign(layouter, challenges, 4094 * 31)?; self.evm_circuit .synthesize_sub(&config.evm_circuit, challenges, layouter)?; diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 11150e8049..5d641d5795 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -8,7 +8,7 @@ use crate::{ }, exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, - util::{build_tx_log_address, Challenges}, + util::{build_tx_log_address, rlc_be_bytes, Challenges}, witness::{ Block, BlockContexts, Bytecode, MptUpdateRow, MptUpdates, RlpFsmWitnessGen, Rw, RwMap, RwRow, Transaction, @@ -17,12 +17,13 @@ use crate::{ use bus_mapping::{ circuit_input_builder::{ BigModExp, CopyDataType, CopyEvent, CopyStep, EcAddOp, EcMulOp, EcPairingOp, ExpEvent, - PrecompileEcParams, N_BYTES_PER_PAIR, N_PAIRING_PER_OP, + PrecompileEcParams, }, precompile::PrecompileCalls, }; use core::iter::once; -use eth_types::{sign_types::SignData, Field, ToLittleEndian, ToScalar, ToWord, Word, U256}; +use eth_types::{sign_types::SignData, Field, ToLittleEndian, ToScalar, ToWord, Word, H256, U256}; +use ethers_core::utils::keccak256; use gadgets::{ binary_number::{BinaryNumberChip, BinaryNumberConfig}, util::{and, not, split_u256, split_u256_limb64, Expr}, @@ -164,11 +165,9 @@ pub enum TxFieldTag { /// TxSignHash: Hash of the transaction without the signature, used for /// signing. TxSignHash, - /// TxHashLength: Length of the RLP-encoded transaction without the - /// signature, used for signing + /// TxHashLength: Length of the RLP-encoded signed transaction TxHashLength, - /// TxHashRLC: RLC of the RLP-encoded transaction without the signature, - /// used for signing + /// TxHashRLC: RLC of the RLP-encoded signed transaction TxHashRLC, /// TxHash: Hash of the transaction with the signature TxHash, @@ -217,6 +216,8 @@ pub struct TxTable { pub value: Column, /// Access list address pub access_list_address: Column, + /// Chunk Txbytes Hash RLC + pub chunk_txbytes_hash_rlc: Column, } impl TxTable { @@ -231,6 +232,7 @@ impl TxTable { index: meta.advice_column(), value: meta.advice_column_in(SecondPhase), access_list_address: meta.advice_column(), + chunk_txbytes_hash_rlc: meta.advice_column_in(SecondPhase), } } @@ -348,6 +350,27 @@ impl TxTable { } } + // Assign chunk txbytes hash for the last row in the fixed section + let chunk_txbytes = txs + .iter() + .flat_map(|tx| { + if tx.is_chunk_l2_tx() { + tx.rlp_signed.clone() + } else { + vec![] + } + }) + .collect::>(); + let chunk_txbytes_hash = H256(keccak256(chunk_txbytes)); + let chunk_txbytes_hash_rlc = + rlc_be_bytes(&chunk_txbytes_hash.to_fixed_bytes(), challenges.evm_word()); + tx_value_cells.push(region.assign_advice( + || "tx table chunk txbytes hash rlc", + self.chunk_txbytes_hash_rlc, + offset - 1, + || chunk_txbytes_hash_rlc, + )?); + // Assign dynamic calldata and access list section for tx in txs.iter().chain(padding_txs.iter()) { for row in tx.table_assignments_dyn(*challenges).into_iter() { @@ -383,6 +406,15 @@ impl TxTable { }, ) } + + /// Return l1 PICircuit exprs + pub fn l1_pi_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + meta.query_fixed(self.q_enable, Rotation::cur()), + meta.query_advice(self.value, Rotation::cur()), // offset 21: TxHash + meta.query_advice(self.value, Rotation::next()), // offset 22: TxType + ] + } } impl LookupTable for TxTable { @@ -3039,14 +3071,17 @@ impl PowOfRandTable { &self, layouter: &mut impl Layouter, challenges: &Challenges>, + max_rows: usize, ) -> Result<(), Error> { let r = challenges.keccak_input(); + log::info!("assign pow of rand with rows {}", max_rows); + layouter.assign_region( || "power of randomness table", |mut region| { let pows_of_rand = std::iter::successors(Some(Value::known(F::one())), |&v| Some(v * r)) - .take(N_PAIRING_PER_OP * N_BYTES_PER_PAIR); + .take(max_rows); for (idx, pow_of_rand) in pows_of_rand.enumerate() { region.assign_fixed( diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 6a8318de57..a32c8667e0 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -17,7 +17,8 @@ use crate::{ // sig_circuit::SigCircuit, table::{ BlockContextFieldTag::{CumNumTxs, NumAllTxs, NumTxs}, - BlockTable, KeccakTable, LookupTable, RlpFsmRlpTable as RlpTable, SigTable, TxFieldTag, + BlockTable, KeccakTable, LookupTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, SigTable, + TxFieldTag, TxFieldTag::{ AccessListAddressesLen, AccessListRLC, AccessListStorageKeysLen, BlockNumber, CallData, CallDataGasCost, CallDataLength, CallDataRLC, CalleeAddress, CallerAddress, ChainID, @@ -72,6 +73,7 @@ use std::{ collections::{BTreeMap, BTreeSet, HashMap}, iter, marker::PhantomData, + ops::{Add, Mul}, }; use crate::{util::Challenges, witness::rlp_fsm::get_rlp_len_tag_length}; @@ -86,8 +88,23 @@ use itertools::Itertools; pub const TX_LEN: usize = 28; /// Offset of TxHash tag in the tx table pub const TX_HASH_OFFSET: usize = 21; +/// Offset of CallerAddress in the tx table +pub const CALLER_ADDRESS_OFFSET: usize = 4; +/// Offset of TxHashRLC tag in the tx table. TxHashRLC = RLC(tx.rlp_signed) +pub const TX_HASH_RLC_OFFSET: usize = 20; /// Offset of ChainID tag in the tx table pub const CHAIN_ID_OFFSET: usize = 12; +/// Offset of HashLength in the tx table +pub const HASH_LENGTH_OFFSET: usize = 19; +/// Offset of HashRLC in the tx table +pub const HASH_RLC_OFFSET: usize = 20; + +// TODO: Constants from aggregator shouldn't be manually copied, +// but importing aggregator causes cyclic dependency +// CHUNK_TXBYTES_BLOB_LIMIT = +// (BLOB_WIDTH * N_BYTES_31) - (N_ROWS_NUM_CHUNKS + N_ROWS_CHUNK_SIZES) +// N_ROWS_CHUNK_SIZES = MAX_AGG_SNARKS * 4 +const CHUNK_TXBYTES_BLOB_LIMIT: usize = (4096 * 31) - (2 + 15 * 4); #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum LookupCondition { @@ -197,6 +214,7 @@ pub struct TxCircuitConfig { block_table: BlockTable, rlp_table: RlpTable, keccak_table: KeccakTable, + pow_of_rand_table: PowOfRandTable, // Access list columns al_idx: Column, @@ -211,6 +229,16 @@ pub struct TxCircuitConfig { // works together with section_rlc to ensure // no ommittance in access list dynamic section field_rlc: Column, + // column for reducing degree. Excludes L1Msg and padding tx + is_chunk_bytes: Column, + // A tx's len for the chunk's hash is different from HashLen + // A padding tx, for example, has a non-zero HashLen but isn't included in chunk hash. + chunk_bytes_len: Column, + // chunk_txbytes_rlc is the rlc of all signed rlp bytes in the chunk + // used for calculating hash of all chunk bytes + chunk_txbytes_rlc: Column, + chunk_txbytes_len_acc: Column, + pow_of_rand: Column, _marker: PhantomData, } @@ -231,6 +259,8 @@ pub struct TxCircuitConfigArgs { pub u8_table: U8Table, /// Reusable u16 lookup table, pub u16_table: U16Table, + /// Reusable power of rand table, + pub pow_of_rand_table: PowOfRandTable, /// Challenges pub challenges: crate::util::Challenges>, } @@ -249,6 +279,7 @@ impl SubCircuitConfig for TxCircuitConfig { sig_table, u8_table, u16_table, + pow_of_rand_table, challenges, }: Self::ConfigArgs, ) -> Self { @@ -330,6 +361,19 @@ impl SubCircuitConfig for TxCircuitConfig { let is_access_list_storage_key = meta.advice_column(); let field_rlc = meta.advice_column(); + // Chunk bytes accumulator + let is_chunk_bytes = meta.advice_column(); + let chunk_bytes_len = meta.advice_column(); + let chunk_txbytes_rlc = meta.advice_column_in(SecondPhase); + let chunk_txbytes_len_acc = meta.advice_column(); + let pow_of_rand = meta.advice_column_in(SecondPhase); + + meta.enable_equality(chunk_bytes_len); + meta.enable_equality(chunk_txbytes_rlc); + meta.enable_equality(chunk_txbytes_len_acc); + meta.enable_equality(pow_of_rand); + meta.enable_equality(tx_table.chunk_txbytes_hash_rlc); + // TODO: add lookup to SignVerify table for sv_address let sv_address = meta.advice_column(); meta.enable_equality(tx_table.value); @@ -904,19 +948,34 @@ impl SubCircuitConfig for TxCircuitConfig { meta.create_gate("lookup into Keccak table condition", |meta| { let mut cb = BaseConstraintBuilder::default(); - let is_tag_sign_or_hash = sum::expr([ + let is_tag_sign_or_l1_hash = sum::expr([ and::expr([ is_sign_length(meta), not::expr(meta.query_advice(is_l1_msg, Rotation::cur())), ]), - is_hash_length(meta), + and::expr([ + is_hash_length(meta), + meta.query_advice(is_l1_msg, Rotation::cur()), + ]), ]); cb.require_equal( "condition", - is_tag_sign_or_hash, + is_tag_sign_or_l1_hash, meta.query_advice(lookup_conditions[&LookupCondition::Keccak], Rotation::cur()), ); + // For L2 tx hash, it should be assigned 0 (not included in Keccak lookup in this case) + let is_l2_hash = and::expr([ + is_hash(meta), + not::expr(meta.query_advice(is_l1_msg, Rotation::cur())), + ]); + cb.condition(is_l2_hash, |cb| { + cb.require_zero( + "L2 tx hash value is 0", + meta.query_advice(tx_table.value, Rotation::cur()), + ) + }); + cb.gate(meta.query_fixed(q_enable, Rotation::cur())) }); @@ -924,6 +983,7 @@ impl SubCircuitConfig for TxCircuitConfig { Self::configure_lookups( meta, q_enable, + q_calldata_first, rlp_tag, tx_value_rlc, tx_value_length, @@ -951,6 +1011,8 @@ impl SubCircuitConfig for TxCircuitConfig { al_idx, sk_idx, sks_acc, + chunk_txbytes_rlc, + chunk_txbytes_len_acc, ); meta.create_gate("tx_gas_cost == 0 for L1 msg", |meta| { @@ -1690,6 +1752,161 @@ impl SubCircuitConfig for TxCircuitConfig { }, ); + ////////////////////////////////////////////////////////// + //// EIP4844: Accumulation and Hashing of Chunk Bytes /// + ////////////////////////////////////////////////////////// + meta.create_gate("Degree reduction column: is_chunk_bytes", |meta| { + let mut cb = BaseConstraintBuilder::default(); + + cb.require_equal( + "is_chunk_bytes = (tx_type != L1Msg && !padding)", + meta.query_advice(is_chunk_bytes, Rotation::cur()), + and::expr([ + not::expr(meta.query_advice(is_l1_msg, Rotation::cur())), + not::expr(meta.query_advice(is_padding_tx, Rotation::cur())), + ]), + ); + + cb.gate(and::expr([ + meta.query_fixed(q_enable, Rotation::cur()), + not::expr(meta.query_fixed(q_first, Rotation::cur())), + not::expr(meta.query_advice(is_calldata, Rotation::cur())), + not::expr(meta.query_advice(is_access_list, Rotation::cur())), + ])) + }); + + meta.create_gate("Chunk len acc and hash RLC acc starts at 0", |meta| { + let mut cb = BaseConstraintBuilder::default(); + + cb.require_zero( + "chunk_txbytes_len_acc = 0", + meta.query_advice(chunk_txbytes_len_acc, Rotation::cur()), + ); + cb.require_zero( + "chunk_txbytes_rlc = 0", + meta.query_advice(chunk_txbytes_rlc, Rotation::cur()), + ); + + cb.gate(and::expr([ + meta.query_fixed(q_enable, Rotation::cur()), + meta.query_fixed(q_first, Rotation::cur()), + ])) + }); + + meta.create_gate("Chunk Bytes RLC", |meta| { + let mut cb = BaseConstraintBuilder::default(); + + // Accumulate hash length + cb.require_equal( + "chunk_txbytes_len_acc::cur == chunk_txbytes_len_acc::prev + HashLength", + meta.query_advice(chunk_txbytes_len_acc, Rotation::cur()), + meta.query_advice(chunk_txbytes_len_acc, Rotation(-(HASH_RLC_OFFSET as i32))) + // the previous row in fixed tx_table is the signed RLP length of current tx + + meta.query_advice(tx_table.value, Rotation::prev()), + ); + + // Accumulate chunk bytes RLC + cb.require_equal( + "chunk_txbytes_rlc::cur == chunk_txbytes_rlc::prev * pow_of_rand(HashLength) + HashRLC", + meta.query_advice(chunk_txbytes_rlc, Rotation::cur()), + meta.query_advice(chunk_txbytes_rlc, Rotation(-(HASH_RLC_OFFSET as i32))) + * meta.query_advice(pow_of_rand, Rotation::cur()) + + meta.query_advice(tx_table.value, Rotation::cur()), + ); + + // The chunk bytes len is the same as the HashLen field in tx_table (in the prev row) + cb.require_equal( + "chunk_bytes_len = HashLen", + meta.query_advice(chunk_bytes_len, Rotation::cur()), + meta.query_advice(tx_table.value, Rotation::prev()), + ); + + cb.gate(and::expr([ + meta.query_fixed(q_enable, Rotation::cur()), + // Only l2 signed bytes are accumulated + meta.query_advice(is_chunk_bytes, Rotation::cur()), + is_hash_rlc(meta), + ])) + }); + + meta.create_gate( + "Chunk Bytes RLC stays same for l1 msg and padding txs", + |meta| { + let mut cb = BaseConstraintBuilder::default(); + + // Check hash length is unchanged + cb.require_equal( + "chunk_txbytes_len_acc::cur == chunk_txbytes_len_acc::prev", + meta.query_advice(chunk_txbytes_len_acc, Rotation::cur()), + meta.query_advice(chunk_txbytes_len_acc, Rotation(-(HASH_RLC_OFFSET as i32))), + ); + + // Check chunk RLC is unchanged + cb.require_equal( + "chunk_txbytes_rlc::cur == chunk_txbytes_rlc::prev", + meta.query_advice(chunk_txbytes_rlc, Rotation::cur()), + meta.query_advice(chunk_txbytes_rlc, Rotation(-(HASH_RLC_OFFSET as i32))), + ); + + cb.gate(and::expr([ + meta.query_fixed(q_enable, Rotation::cur()), + not::expr(meta.query_advice(is_chunk_bytes, Rotation::cur())), + is_hash_rlc(meta), + ])) + }, + ); + + meta.lookup_any("Correct pow_of_rand for HashLen", |meta| { + let enable = and::expr(vec![ + meta.query_fixed(q_enable, Rotation::cur()), + // A valid chunk txbytes tx is determined by: (tx.tx_type != TxType::L1Msg) && + // !tx.caller_address.is_zero() + not::expr(meta.query_advice(is_l1_msg, Rotation::cur())), + not::expr(value_is_zero.expr(Rotation( + -((HASH_RLC_OFFSET - CALLER_ADDRESS_OFFSET) as i32), + ))(meta)), + is_hash_rlc(meta), + ]); + + vec![ + 1.expr(), // q_enable + meta.query_advice(chunk_bytes_len, Rotation::cur()), // exponent + meta.query_advice(pow_of_rand, Rotation::cur()), // pow_of_rand + ] + .into_iter() + .zip(pow_of_rand_table.table_exprs(meta)) + .map(|(arg, table)| (enable.clone() * arg, table)) + .collect() + }); + + meta.create_gate("One chunk_txbytes_len_acc, chunk_txbytes_rlc value and pow_of_rand for each tx (in fixed section)", |meta| { + let mut cb = BaseConstraintBuilder::default(); + + // chunk_txbytes_len_acc, chunk_txbytes_rlc and pow_of_rand stay the same for the same tx + cb.require_equal( + "chunk_txbytes_len_acc' == chunk_txbytes_len_acc", + meta.query_advice(chunk_txbytes_len_acc, Rotation::cur()), + meta.query_advice(chunk_txbytes_len_acc, Rotation::prev()), + ); + cb.require_equal( + "chunk_txbytes_rlc' == chunk_txbytes_rlc", + meta.query_advice(chunk_txbytes_rlc, Rotation::cur()), + meta.query_advice(chunk_txbytes_rlc, Rotation::prev()), + ); + cb.require_equal( + "pow_of_rand' == pow_of_rand", + meta.query_advice(pow_of_rand, Rotation::cur()), + meta.query_advice(pow_of_rand, Rotation::prev()), + ); + + cb.gate(and::expr([ + meta.query_fixed(q_enable, Rotation::cur()), + not::expr(meta.query_fixed(q_first, Rotation::cur())), + not::expr(is_nonce(meta)), + not::expr(meta.query_advice(is_calldata, Rotation::cur())) + ])) + }); + log_deg("tx_circuit", meta); Self { @@ -1736,6 +1953,7 @@ impl SubCircuitConfig for TxCircuitConfig { tx_table, keccak_table, rlp_table, + pow_of_rand_table, is_tag_block_num, al_idx, sk_idx, @@ -1744,17 +1962,24 @@ impl SubCircuitConfig for TxCircuitConfig { is_access_list_address, is_access_list_storage_key, field_rlc, + is_chunk_bytes, + chunk_bytes_len, + chunk_txbytes_rlc, + chunk_txbytes_len_acc, + pow_of_rand, _marker: PhantomData, num_txs, } } } +type FixedRowsAssignmentResult = (Vec>, Vec>); impl TxCircuitConfig { #[allow(clippy::too_many_arguments)] fn configure_lookups( meta: &mut ConstraintSystem, q_enable: Column, + q_calldata_first: Column, rlp_tag: Column, tx_value_rlc: Column, tx_value_length: Column, @@ -1782,6 +2007,8 @@ impl TxCircuitConfig { al_idx: Column, sk_idx: Column, sks_acc: Column, + chunk_txbytes_rlc: Column, + chunk_txbytes_len_acc: Column, ) { macro_rules! is_tx_type { ($var:ident, $type_variant:ident) => { @@ -2296,7 +2523,7 @@ impl TxCircuitConfig { // lookup Keccak table for tx sign data hash, i.e. the sighash that has to be // signed. // lookup Keccak table for tx hash too. - meta.lookup_any("Keccak table lookup for TxSign and TxHash", |meta| { + meta.lookup_any("Keccak table lookup for TxSign and L1 TxHash", |meta| { let enable = and::expr(vec![ meta.query_fixed(q_enable, Rotation::cur()), meta.query_advice(lookup_conditions[&LookupCondition::Keccak], Rotation::cur()), @@ -2314,6 +2541,29 @@ impl TxCircuitConfig { .map(|(arg, table)| (enable.clone() * arg, table)) .collect() }); + + //////////////////////////////////////////////////////////////////// + ///////////// 4844: Chunk bytes RLC lookups ///////////////// + ///////////////// ////////////////////////////////////////////////// + meta.lookup_any("Keccak table lookup for ChunkHash", |meta| { + // Isolate the last row in the fixed section, which belongs to the last tx in the chunk + let enable = and::expr(vec![ + meta.query_fixed(q_enable, Rotation::cur()), + meta.query_fixed(q_calldata_first, Rotation::cur()), + ]); + + vec![ + 1.expr(), // q_enable + 1.expr(), // is_final + meta.query_advice(chunk_txbytes_rlc, Rotation::prev()), // input_rlc + meta.query_advice(chunk_txbytes_len_acc, Rotation::prev()), // input_len + meta.query_advice(tx_table.chunk_txbytes_hash_rlc, Rotation::prev()), // output_rlc + ] + .into_iter() + .zip(keccak_table.table_exprs(meta)) + .map(|(arg, table)| (enable.clone() * arg, table)) + .collect() + }); } /// Assign 1st empty row with tag = Null @@ -2348,15 +2598,26 @@ impl TxCircuitConfig { num_all_txs_acc: u64, num_txs: u64, cum_num_txs: u64, + chunk_txbytes_rlc_acc: Value, + chunk_txbytes_len_acc: Value, + chunk_txbytes_hash: Value, + pows_of_rand: &mut Vec>, + is_last_tx: bool, challenges: &Challenges>, - ) -> Result>, Error> { + ) -> Result, Error> { let keccak_input = challenges.keccak_input(); let evm_word = challenges.evm_word(); let zero_rlc = keccak_input.map(|_| F::zero()); let sign_hash = keccak256(tx.rlp_unsigned.as_slice()); let hash = keccak256(tx.rlp_signed.as_slice()); let sign_hash_rlc = rlc_be_bytes(&sign_hash, evm_word); - let hash_rlc = rlc_be_bytes(&hash, evm_word); + let hash_rlc = if tx.tx_type != L1Msg { + Value::known(F::zero()) + } else { + rlc_be_bytes(&hash, evm_word) + }; + let mut supplemental_data: Vec> = vec![]; + let mut txbytes_hash_assignment: Option> = None; let mut tx_value_cells = vec![]; let rlp_sign_tag_length = if tx.tx_type.is_l1_msg() { // l1 msg does not have sign data @@ -2367,7 +2628,30 @@ impl TxCircuitConfig { let (access_list_address_size, access_list_storage_key_size) = access_list_size(&tx.access_list); - // fixed_rows of a tx + // Only bytes from L2 txs are accumulated for chunk bytes hash + let is_chunk_bytes = tx.is_chunk_l2_tx(); + + let hash_len = if is_chunk_bytes { + tx.rlp_signed.len() + } else { + 0 + }; + let tx_hash_rlc = rlc_be_bytes(&tx.rlp_signed, keccak_input); + if hash_len >= pows_of_rand.len() { + for _ in 0..(tx.rlp_signed.len() - pows_of_rand.len() + 1) { + pows_of_rand.push(pows_of_rand.last().unwrap().mul(keccak_input)); + } + } + let pow_of_rand = pows_of_rand[hash_len]; + let chunk_txbytes_rlc = if is_chunk_bytes { + chunk_txbytes_rlc_acc.mul(pow_of_rand).add(tx_hash_rlc) + } else { + chunk_txbytes_rlc_acc + }; + let chunk_txbytes_len = chunk_txbytes_len_acc.add(Value::known(F::from(hash_len as u64))); + supplemental_data.push(chunk_txbytes_rlc); + supplemental_data.push(chunk_txbytes_len); + let fixed_rows = vec![ // need to be in same order as that tx table load function uses ( @@ -2550,7 +2834,7 @@ impl TxCircuitConfig { be_bytes_len: 0, be_bytes_rlc: zero_rlc, }), - rlc_be_bytes(&tx.rlp_signed, keccak_input), + tx_hash_rlc, ), (TxFieldTag::TxHash, None, hash_rlc), ( @@ -2690,14 +2974,42 @@ impl TxCircuitConfig { self.is_caller_address, F::from((tx_tag == CallerAddress) as u64), ), + ( + "is_chunk_bytes", + self.is_chunk_bytes, + F::from(is_chunk_bytes as u64), + ), + ( + "chunk_bytes_len", + self.chunk_bytes_len, + F::from(hash_len as u64), + ), ] { region.assign_advice(|| col_anno, col, *offset, || Value::known(col_val))?; } + region.assign_advice( + || "chunk_txbytes_len_acc", + self.chunk_txbytes_len_acc, + *offset, + || chunk_txbytes_len, + )?; + txbytes_hash_assignment = Some(region.assign_advice( + || "tx_table.chunk_txbytes_hash_rlc", + self.tx_table.chunk_txbytes_hash_rlc, + *offset, + || chunk_txbytes_hash, + )?); // 2nd phase columns - { - let (col_anno, col, col_val) = - ("tx_value_rlc", self.tx_value_rlc, rlp_be_bytes_rlc); + for (col_anno, col, col_val) in [ + ("tx_value_rlc", self.tx_value_rlc, rlp_be_bytes_rlc), + ("pow_of_rand", self.pow_of_rand, pow_of_rand), + ( + "chunk_txbytes_rlc", + self.chunk_txbytes_rlc, + chunk_txbytes_rlc, + ), + ] { region.assign_advice(|| col_anno, col, *offset, || col_val)?; } @@ -2781,10 +3093,10 @@ impl TxCircuitConfig { let is_tag_in_set = hash_set.into_iter().filter(|tag| tx_tag == *tag).count() == 1; F::from((is_l1_msg && is_tag_in_set) as u64) }); - // 6. lookup to Keccak table for tx_sign_hash and tx_hash + // 6. lookup to Keccak table for tx_sign_hash and l1 tx_hash conditions.insert(LookupCondition::Keccak, { let case1 = (tx_tag == TxSignLength) && !is_l1_msg; - let case2 = tx_tag == TxHashLength; + let case2 = (tx_tag == TxHashLength) && is_l1_msg; F::from((case1 || case2) as u64) }); @@ -2824,7 +3136,10 @@ impl TxCircuitConfig { *offset += 1; } - Ok(tx_value_cells) + if is_last_tx { + tx_value_cells.push(txbytes_hash_assignment.unwrap()); + } + Ok((tx_value_cells, supplemental_data)) } /// Assign calldata byte rows of each tx @@ -3309,6 +3624,15 @@ impl TxCircuit { let sign_verify_inputs = keccak_inputs_sign_verify(&sign_datas); inputs.extend_from_slice(&sign_verify_inputs); + // Keccak input for chunk bytes (only L2 txs are included) + let chunk_hash_bytes = self + .txs + .iter() + .filter(|&tx| tx.is_chunk_l2_tx()) + .flat_map(|tx| tx.rlp_signed.clone()) + .collect::>(); + inputs.extend_from_slice(&[chunk_hash_bytes]); + Ok(inputs) } @@ -3444,7 +3768,22 @@ impl TxCircuit { } }; + let mut chunk_bytes: Vec = vec![]; + for i in 0..sigs.len() { + let tx = get_tx(i); + if tx.is_chunk_l2_tx() { + chunk_bytes.extend_from_slice(&tx.rlp_signed); + } + } + + let chunk_txbytes_hash = keccak256(chunk_bytes.as_slice()); + let evm_word = challenges.evm_word(); + let chunk_txbytes_hash = rlc_be_bytes(&chunk_txbytes_hash, evm_word); + let mut tx_value_cells = vec![]; + let mut chunk_txbytes_rlc_acc = Value::known(F::zero()); + let mut chunk_txbytes_len_acc = Value::known(F::zero()); + let mut pows_of_rand: Vec> = vec![Value::known(F::one())]; for (i, sign_data) in sigs.iter().enumerate() { let tx = get_tx(i); let block_num = tx.block_number; @@ -3505,20 +3844,31 @@ impl TxCircuit { i, num_all_txs_acc, ); + let (assigned_cells, supplemental_data) = config.assign_fixed_rows( + &mut region, + &mut offset, + tx, + sign_data, + next_tx, + total_l1_popped_before, + num_all_txs_acc, + num_txs, + cum_num_txs, + chunk_txbytes_rlc_acc, + chunk_txbytes_len_acc, + chunk_txbytes_hash, + &mut pows_of_rand, + is_last_tx, + challenges, + )?; + tx_value_cells.extend_from_slice( - config.assign_fixed_rows( - &mut region, - &mut offset, - tx, - sign_data, - next_tx, - total_l1_popped_before, - num_all_txs_acc, - num_txs, - cum_num_txs, - challenges, - )?.as_slice() + assigned_cells.as_slice() ); + + chunk_txbytes_rlc_acc = supplemental_data[0]; + chunk_txbytes_len_acc = supplemental_data[1]; + // set next tx's total_l1_popped_before total_l1_popped_before = total_l1_popped_after; } @@ -3574,7 +3924,7 @@ impl SubCircuit for TxCircuit { type Config = TxCircuitConfig; fn unusable_rows() -> usize { - 8 + 10 } fn new_from_block(block: &witness::Block) -> Self { @@ -3600,17 +3950,62 @@ impl SubCircuit for TxCircuit { // Since each call data byte at least takes one row in RLP circuit. // For L2 tx, each call data byte takes two row in RLP circuit. assert!(block.circuits_params.max_calldata < block.circuits_params.max_rlp_rows); + + // Calculate blob capacity usage + let chunk_txbytes_len = block + .txs + .iter() + .map(|tx| { + if tx.is_chunk_l2_tx() { + tx.rlp_signed.len() + } else { + 0 + } + }) + .sum::(); + let blob_usage: f32 = chunk_txbytes_len as f32 / CHUNK_TXBYTES_BLOB_LIMIT as f32; + + // Calculate tx circuit dynamic section usage let sum_calldata_len = block.txs.iter().map(|tx| tx.call_data.len()).sum::(); - let max_calldata = if block.circuits_params.max_calldata == 0 { - // dynamic max_calldata - sum_calldata_len + let sum_access_list_len = block + .txs + .iter() + .map(|tx| { + if tx.access_list.is_some() { + let access_list = tx.access_list.clone().unwrap().0; + access_list.len() + + access_list + .iter() + .map(|al| al.storage_keys.len()) + .sum::() + } else { + 0usize + } + }) + .sum::(); + + // With the introduction of access list, the max_calldata circuit parameter now has to share + // capacity between calldata and access list rows TODO: The max_calldata parameter + // should be renamed later to max_dynamic + let max_dynamic_data = if block.circuits_params.max_calldata == 0 { + // input-specific max_dynamic + sum_calldata_len + sum_access_list_len } else { block.circuits_params.max_calldata }; - let tx_usage = sum_calldata_len as f32 / max_calldata as f32; + let dynamic_usage = + (sum_calldata_len + sum_access_list_len) as f32 / max_dynamic_data as f32; + + // Get the highest usage fraction out of all capacities + let highest_usage = ([blob_usage, dynamic_usage]) + .iter() + .cloned() + .fold(0_f32, f32::max); + // Return the highest usage percentage ( - (tx_usage * block.circuits_params.max_vertical_circuit_rows as f32).ceil() as usize, + (highest_usage * block.circuits_params.max_vertical_circuit_rows as f32).ceil() + as usize, Self::min_num_rows( block.circuits_params.max_txs, block.circuits_params.max_calldata, diff --git a/zkevm-circuits/src/tx_circuit/dev.rs b/zkevm-circuits/src/tx_circuit/dev.rs index 58604fde71..c304cf6dab 100644 --- a/zkevm-circuits/src/tx_circuit/dev.rs +++ b/zkevm-circuits/src/tx_circuit/dev.rs @@ -7,7 +7,8 @@ pub use super::TxCircuit; use crate::{ sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, table::{ - BlockTable, KeccakTable, RlpFsmRlpTable as RlpTable, SigTable, TxTable, U16Table, U8Table, + BlockTable, KeccakTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, SigTable, TxTable, + U16Table, U8Table, }, tx_circuit::{TxCircuitConfig, TxCircuitConfigArgs}, util::{Challenges, SubCircuit, SubCircuitConfig}, @@ -35,6 +36,8 @@ pub struct TxCircuitTesterConfigArgs { pub u8_table: U8Table, /// u16 lookup table, pub u16_table: U16Table, + /// power of rand lookup table, + pub pow_of_rand_table: PowOfRandTable, /// Challenges pub challenges: Challenges>, } @@ -64,6 +67,7 @@ impl SubCircuitConfig for TxCircuitTesterConfig { sig_table, u8_table, u16_table, + pow_of_rand_table, challenges, }: Self::ConfigArgs, ) -> Self { @@ -85,6 +89,7 @@ impl SubCircuitConfig for TxCircuitTesterConfig { sig_table, u8_table, u16_table, + pow_of_rand_table, challenges, }, ); @@ -172,6 +177,9 @@ impl Circuit for TxCircuitTester { let u16_table = U16Table::construct(meta); let challenges = Challenges::construct(meta); + let challenges_expr = challenges.exprs(meta); + let pow_of_rand_table = PowOfRandTable::construct(meta, &challenges_expr); + let config = { let challenges = challenges.exprs(meta); let sig_config = SigCircuitConfig::new( @@ -192,6 +200,7 @@ impl Circuit for TxCircuitTester { sig_table, u8_table, u16_table, + pow_of_rand_table, challenges, }, ); @@ -228,6 +237,11 @@ impl Circuit for TxCircuitTester { &self.tx_circuit.keccak_inputs()?, &challenges, )?; + + config + .tx_config + .pow_of_rand_table + .assign(&mut layouter, &challenges, 4094 * 31)?; config.tx_config.rlp_table.dev_load( &mut layouter, self.tx_circuit diff --git a/zkevm-circuits/src/tx_circuit/test.rs b/zkevm-circuits/src/tx_circuit/test.rs index 1e25dcbee8..cbabcf3834 100644 --- a/zkevm-circuits/src/tx_circuit/test.rs +++ b/zkevm-circuits/src/tx_circuit/test.rs @@ -21,7 +21,7 @@ use crate::{ use eth_types::{ address, evm_types::gas_utils::{tx_access_list_gas_cost, tx_data_gas_cost}, - word, H256, U256, U64, + word, H160, H256, U256, U64, }; use halo2_proofs::{ dev::{MockProver, VerifyFailure}, @@ -146,7 +146,12 @@ fn build_eip1559_tx(id: usize) -> Transaction { tx.max_priority_fee_per_gas = eth_tx.max_priority_fee_per_gas.unwrap_or(U256::zero()); tx.call_data = eth_tx.input.to_vec(); tx.callee_address = eth_tx.to; - tx.caller_address = eth_tx.from; + + tx.caller_address = H160::from_slice(&[ + 0x20, 0x2b, 0xB2, 0xfa, 0xB1, 0xe3, 0x5D, 0x94, 0x0F, 0xde, 0x99, 0xb2, 0x14, 0xba, 0x49, + 0xDA, 0xfb, 0xce, 0xf6, 0x2A, + ]); + tx.is_create = eth_tx.to.is_none(); tx.call_data_length = tx.call_data.len(); tx.call_data_gas_cost = tx_data_gas_cost(&tx.call_data); diff --git a/zkevm-circuits/src/witness/tx.rs b/zkevm-circuits/src/witness/tx.rs index fc8640d359..a6671c6535 100644 --- a/zkevm-circuits/src/witness/tx.rs +++ b/zkevm-circuits/src/witness/tx.rs @@ -131,6 +131,11 @@ impl Transaction { } } + /// Return whether the transaction is included in the chunk txbytes (not l1Msg and not padding) + pub fn is_chunk_l2_tx(&self) -> bool { + self.tx_type != TxType::L1Msg && !self.caller_address.is_zero() + } + /// Sign data pub fn sign_data(&self) -> Result { if self.r.is_zero() && self.s.is_zero() && self.v == 0 {