From ebfcc841a2e321d86329b02b3566eb031e13f1c7 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Mon, 8 May 2023 13:48:12 -0400 Subject: [PATCH 01/41] impl in the clear --- zkevm-circuits/src/keccak_circuit.rs | 4 +- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/pi_agg_circuit.rs | 5 ++ zkevm-circuits/src/pi_agg_circuit/chunk.rs | 55 +++++++++++++++++++ .../src/pi_agg_circuit/multi_bach.rs | 45 +++++++++++++++ zkevm-circuits/src/pi_circuit.rs | 2 +- 6 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 zkevm-circuits/src/pi_agg_circuit.rs create mode 100644 zkevm-circuits/src/pi_agg_circuit/chunk.rs create mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_bach.rs diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index 3dd2085e3c..5a5525060c 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -982,6 +982,8 @@ impl KeccakCircuitConfig { /// KeccakCircuit #[derive(Default, Clone, Debug)] pub struct KeccakCircuit { + // The input is a two dimensional vector + // Each input inputs: Vec>, num_rows: usize, _marker: PhantomData, @@ -994,7 +996,7 @@ impl SubCircuit for KeccakCircuit { keccak_unusable_rows() } - /// The `block.circuits_params.keccak_padding` parmeter, when enabled, sets + /// The `block.circuits_params.keccak_padding` parameter, when enabled, sets /// up the circuit to support a fixed number of permutations/keccak_f's, /// independently of the permutations required by `inputs`. fn new_from_block(block: &witness::Block) -> Self { diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index a9c6f80ff3..8e8f17ac0b 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -30,6 +30,7 @@ pub mod poseidon_circuit; pub mod rlp_circuit; // we don't use this for aggregation //pub mod root_circuit; +pub mod pi_agg_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; diff --git a/zkevm-circuits/src/pi_agg_circuit.rs b/zkevm-circuits/src/pi_agg_circuit.rs new file mode 100644 index 0000000000..40d895e04c --- /dev/null +++ b/zkevm-circuits/src/pi_agg_circuit.rs @@ -0,0 +1,5 @@ +//! This module implements circuits that aggregates public inputs of many blocks/txs into a single +//! one. + +mod chunk; +mod multi_bach; diff --git a/zkevm-circuits/src/pi_agg_circuit/chunk.rs b/zkevm-circuits/src/pi_agg_circuit/chunk.rs new file mode 100644 index 0000000000..18200ba745 --- /dev/null +++ b/zkevm-circuits/src/pi_agg_circuit/chunk.rs @@ -0,0 +1,55 @@ +//! This module implements `Chunk` related data types. +//! A chunk is a list of blocks. +use crate::pi_circuit::PublicData; +use ethers_core::utils::keccak256; + +#[derive(Default, Debug, Clone)] +/// ChunkPublicData is a list of PublicData, each corresponds to a block +/// with max_txn transactions. +pub struct ChunkPublicData { + pub(crate) public_data_vec: Vec, + pub(crate) max_txs: usize, +} + +impl ChunkPublicData { + /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. + pub(crate) fn raw_data_hash_bytes(&self) -> Vec { + let mut to_be_hashed = vec![]; + + self.public_data_vec.iter().for_each(|public_data| { + to_be_hashed + .extend_from_slice( + // extract all the data from transactions + public_data.get_pi(self.max_txs).as_ref(), + ) + .into() + }); + // data hash is the keccak hash of concatenation of all data fields + keccak256::<&[u8]>(to_be_hashed.as_ref()).into() + } + + /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. + pub(crate) fn raw_public_input_bytes(&self) -> Vec { + let mut to_be_hashed = vec![]; + // extract the prev state root of the first block + // and the next state root of the last block + to_be_hashed.extend_from_slice(self.public_data_vec[0].prev_state_root.as_bytes()); + to_be_hashed.extend_from_slice( + self.public_data_vec + .last() + .unwrap() + .prev_state_root + .as_bytes(), + ); + // withdraw root + // + // FIXME: for each block we hav a withdraw_trie_root + // This is different from the spec. + // Double check this. + self.public_data_vec.iter().for_each(|public_data| { + to_be_hashed.extend_from_slice(public_data.withdraw_trie_root.as_bytes()) + }); + + keccak256::<&[u8]>(to_be_hashed.as_ref()).into() + } +} diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_bach.rs b/zkevm-circuits/src/pi_agg_circuit/multi_bach.rs new file mode 100644 index 0000000000..5370f4d8ef --- /dev/null +++ b/zkevm-circuits/src/pi_agg_circuit/multi_bach.rs @@ -0,0 +1,45 @@ +//! This module implements `MultiBatch` related data types. +//! A multi_batch is a list of chunk. + +use super::chunk::ChunkPublicData; +use ethers_core::utils::keccak256; + +#[derive(Default, Debug, Clone)] +/// MultiBatchPublicData is a list of ChunkPublicData, each corresponds to a chunk +/// with max_txn transactions. +pub struct MultiBatchPublicData { + pub(crate) public_data_vec: Vec, + pub(crate) max_txs: usize, +} + +impl MultiBatchPublicData { + /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. + pub(crate) fn raw_data_hash_bytes(&self) -> Vec { + let mut to_be_hashed = vec![]; + + self.public_data_vec.iter().for_each(|chunk_public_data| { + to_be_hashed + .extend_from_slice( + // extract all the data from each chunk + chunk_public_data.raw_data_hash_bytes().as_ref(), + ) + .into() + }); + // data hash is the keccak hash of concatenation of all data fields + keccak256::<&[u8]>(to_be_hashed.as_ref()).into() + } + + /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. + pub(crate) fn raw_public_input_bytes(&self) -> Vec { + let mut to_be_hashed = vec![]; + self.public_data_vec.iter().for_each(|chunk_public_data| { + to_be_hashed + .extend_from_slice( + // extract all the data from each chunk + chunk_public_data.raw_public_input_bytes().as_ref(), + ) + .into() + }); + keccak256::<&[u8]>(to_be_hashed.as_ref()).into() + } +} diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index 07691111a7..58dff46291 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -176,7 +176,7 @@ impl PublicData { result } - fn get_pi(&self, max_txs: usize) -> H256 { + pub(crate) fn get_pi(&self, max_txs: usize) -> H256 { let rpi_bytes = self.raw_public_input_bytes(max_txs); let rpi_keccak = keccak256(rpi_bytes); H256(rpi_keccak) From f85b0eccbf3b8c14cc855a028a3840e82cc91ae9 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 17 May 2023 15:29:37 -0400 Subject: [PATCH 02/41] implement PI aggregation in circuit --- .gitignore | 3 +- Cargo.lock | 534 +++++++++--------- zkevm-circuits/src/keccak_circuit.rs | 29 +- .../src/keccak_circuit/keccak_packed_multi.rs | 16 +- zkevm-circuits/src/pi_agg_circuit.rs | 14 +- zkevm-circuits/src/pi_agg_circuit/chunk.rs | 44 +- .../src/pi_agg_circuit/multi_bach.rs | 45 -- .../src/pi_agg_circuit/multi_batch.rs | 92 +++ .../src/pi_agg_circuit/multi_batch_circuit.rs | 400 +++++++++++++ zkevm-circuits/src/pi_agg_circuit/tests.rs | 38 ++ 10 files changed, 882 insertions(+), 333 deletions(-) delete mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_bach.rs create mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_batch.rs create mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs create mode 100644 zkevm-circuits/src/pi_agg_circuit/tests.rs diff --git a/.gitignore b/.gitignore index 2f0a46ef53..47b9644c3b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ .idea *.log *.json -*.sh \ No newline at end of file +*.sh +*.txt \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 081f43dc03..ad42776a8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,9 +44,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "ark-std" @@ -111,7 +111,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.15", ] [[package]] @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2", @@ -387,9 +387,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" [[package]] name = "bus-mapping" @@ -567,9 +567,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags", @@ -584,9 +584,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", @@ -636,16 +636,6 @@ dependencies = [ "cc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "coins-bip32" version = "0.7.0" @@ -699,7 +689,7 @@ dependencies = [ "serde", "serde_derive", "sha2 0.10.6", - "sha3 0.10.7", + "sha3 0.10.8", "thiserror", ] @@ -762,9 +752,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" @@ -805,9 +795,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -873,9 +863,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -1002,50 +992,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "cxx" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.11", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.11", -] - [[package]] name = "darling" version = "0.10.2" @@ -1240,9 +1186,9 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dwrote" @@ -1326,7 +1272,7 @@ checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.15", ] [[package]] @@ -1370,13 +1316,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1406,7 +1352,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.6", - "sha3 0.10.7", + "sha3 0.10.8", "thiserror", "uuid", ] @@ -1429,7 +1375,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha3 0.10.7", + "sha3 0.10.8", "strum", "strum_macros", "subtle", @@ -1448,7 +1394,7 @@ dependencies = [ "regex", "serde", "serde_json", - "sha3 0.10.7", + "sha3 0.10.8", "thiserror", "uint", ] @@ -1748,6 +1694,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + [[package]] name = "ff" version = "0.12.1" @@ -1791,9 +1746,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -1889,9 +1844,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1904,9 +1859,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1914,15 +1869,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1931,9 +1886,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-locks" @@ -1948,26 +1903,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" @@ -1977,9 +1932,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -2054,9 +2009,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2103,9 +2058,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -2235,9 +2190,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" dependencies = [ "log", "pest", @@ -2348,11 +2303,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -2397,9 +2352,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -2434,9 +2389,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2448,12 +2403,11 @@ dependencies = [ [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -2575,13 +2529,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2592,14 +2546,14 @@ checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2625,9 +2579,9 @@ checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" dependencies = [ "wasm-bindgen", ] @@ -2642,14 +2596,14 @@ dependencies = [ "ecdsa", "elliptic-curve", "sha2 0.10.6", - "sha3 0.10.7", + "sha3 0.10.8", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -2673,9 +2627,9 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.9" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87" +checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" dependencies = [ "ascii-canvas", "bit-set", @@ -2685,9 +2639,8 @@ dependencies = [ "itertools", "lalrpop-util", "petgraph", - "pico-args", "regex", - "regex-syntax", + "regex-syntax 0.6.29", "string_cache", "term", "tiny-keccak", @@ -2696,9 +2649,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.9" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c1f7869c94d214466c5fd432dfed12c379fd87786768d36455892d46b18edd" +checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" dependencies = [ "regex", ] @@ -2714,9 +2667,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libloading" @@ -2776,15 +2729,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2793,9 +2737,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "lock_api" @@ -2862,11 +2806,12 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -2878,7 +2823,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -3068,9 +3013,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec", "bitvec 1.0.1", @@ -3137,7 +3082,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -3237,9 +3182,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" dependencies = [ "thiserror", "ucd-trie", @@ -3247,9 +3192,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" +checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" dependencies = [ "pest", "pest_generator", @@ -3257,22 +3202,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" +checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "pest_meta" -version = "2.5.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" +checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" dependencies = [ "once_cell", "pest", @@ -3343,12 +3288,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pico-args" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" - [[package]] name = "pin-project" version = "1.0.12" @@ -3393,9 +3332,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plotters" @@ -3445,12 +3384,13 @@ dependencies = [ [[package]] name = "png" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" dependencies = [ "bitflags", "crc32fast", + "fdeflate", "flate2", "miniz_oxide", ] @@ -3590,9 +3530,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.54" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -3619,9 +3559,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] @@ -3721,13 +3661,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -3736,6 +3676,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "rend" version = "0.4.0" @@ -3747,9 +3693,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" dependencies = [ "base64 0.21.0", "bytes", @@ -3798,7 +3744,7 @@ dependencies = [ "primitive-types 0.12.1", "revm_precompiles", "rlp", - "sha3 0.10.7", + "sha3 0.10.8", ] [[package]] @@ -3814,15 +3760,15 @@ dependencies = [ "ripemd", "secp256k1 0.26.0", "sha2 0.10.6", - "sha3 0.10.7", + "sha3 0.10.8", "substrate-bn", ] [[package]] name = "revm-primitives" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180427e1169b860ab63eaa5bcff158010073646abf3312aed11a1d7aa1aa8291" +checksum = "304d998f466ffef72d76c7f20b05bf08a96801736a6fb1fdef47d49a292618df" dependencies = [ "auto_impl", "bitvec 1.0.1", @@ -3836,7 +3782,7 @@ dependencies = [ "primitive-types 0.12.1", "rlp", "ruint", - "sha3 0.10.7", + "sha3 0.10.8", ] [[package]] @@ -3853,7 +3799,7 @@ dependencies = [ "ripemd", "secp256k1 0.24.3", "sha2 0.10.6", - "sha3 0.10.7", + "sha3 0.10.8", "substrate-bn", ] @@ -3894,9 +3840,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.40" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c30f1d45d9aa61cbc8cd1eb87705470892289bb2d01943e7803b873a57404dc3" +checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" dependencies = [ "bytecheck", "hashbrown 0.12.3", @@ -3908,9 +3854,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.40" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff26ed6c7c4dfc2aa9480b86a60e3c7233543a270a680e10758a507c5a4ce476" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" dependencies = [ "proc-macro2", "quote", @@ -4008,16 +3954,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4077,12 +4023,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" - [[package]] name = "scrypt" version = "0.8.1" @@ -4197,9 +4137,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.159" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] @@ -4226,20 +4166,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -4353,9 +4293,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest 0.10.6", "keccak", @@ -4371,6 +4311,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "simdutf8" version = "0.1.4" @@ -4419,7 +4365,7 @@ dependencies = [ "rlp", "rustc-hash", "serde", - "sha3 0.10.7", + "sha3 0.10.8", ] [[package]] @@ -4569,9 +4515,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.11" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -4621,7 +4567,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bus-mapping", - "clap 3.2.23", + "clap 3.2.25", "ctor", "env_logger 0.9.3", "eth-types", @@ -4684,7 +4630,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.15", ] [[package]] @@ -4734,9 +4680,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "bytes", @@ -4746,18 +4692,18 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.15", ] [[package]] @@ -4789,9 +4735,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -4847,20 +4793,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -5044,9 +4990,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -5054,24 +5000,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5081,9 +5027,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5091,22 +5037,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" [[package]] name = "wasm-timer" @@ -5125,9 +5071,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" dependencies = [ "js-sys", "wasm-bindgen", @@ -5191,11 +5137,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", ] [[package]] @@ -5204,7 +5150,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -5213,13 +5168,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -5228,47 +5198,89 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] @@ -5388,7 +5400,7 @@ dependencies = [ "rayon", "serde", "serde_json", - "sha3 0.10.7", + "sha3 0.10.8", "snark-verifier", "snark-verifier-sdk", "strum", diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index 5a5525060c..9c79e99620 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -2,7 +2,7 @@ mod cell_manager; /// Keccak packed multi pub mod keccak_packed_multi; -mod param; +pub(crate) mod param; mod table; /// Util mod util; @@ -38,7 +38,7 @@ use crate::{ use eth_types::Field; use gadgets::util::{and, not, select, sum, Expr}; use halo2_proofs::{ - circuit::{Layouter, Region, Value}, + circuit::{AssignedCell, Layouter, Region, Value}, plonk::{Column, ConstraintSystem, Error, Expression, Fixed, TableColumn, VirtualCells}, poly::Rotation, }; @@ -55,7 +55,7 @@ pub struct KeccakCircuitConfig { q_padding_last: Column, /// The columns for other circuits to lookup Keccak hash results pub keccak_table: KeccakTable, - cell_manager: CellManager, + pub(crate) cell_manager: CellManager, round_cst: Column, normalize_3: [TableColumn; 2], normalize_4: [TableColumn; 2], @@ -66,6 +66,7 @@ pub struct KeccakCircuitConfig { } /// Circuit configuration arguments +#[derive(Debug, Clone)] pub struct KeccakCircuitConfigArgs { /// KeccakTable pub keccak_table: KeccakTable, @@ -300,7 +301,7 @@ impl SubCircuitConfig for KeccakCircuitConfig { // multiple rows with lookups in a way that doesn't require any // extra additional cells or selectors we have to put all `s[i]`'s on the same // row. This isn't that strong of a requirement actually because we the - // words are split into multipe parts, and so only the parts at the same + // words are split into multiple parts, and so only the parts at the same // position of those words need to be on the same row. let target_word_sizes = target_part_sizes(part_size); let num_word_parts = target_word_sizes.len(); @@ -865,6 +866,7 @@ impl SubCircuitConfig for KeccakCircuitConfig { } impl KeccakCircuitConfig { + /// Assign the circuit for hash function pub(crate) fn assign( &self, layouter: &mut impl Layouter, @@ -890,12 +892,13 @@ impl KeccakCircuitConfig { ) } - fn set_row( + /// Set a keccak row; return the cells allocated for the row. + pub(crate) fn set_row( &self, region: &mut Region<'_, F>, offset: usize, row: &KeccakRow, - ) -> Result<(), Error> { + ) -> Result>, Error> { // Fixed selectors for (name, column, value) in &[ ("q_enable", self.q_enable, F::from(row.q_enable)), @@ -930,18 +933,19 @@ impl KeccakCircuitConfig { )?; // Cell values + let mut res = vec![]; for (idx, (bit, column)) in row .cell_values .iter() .zip(self.cell_manager.columns()) .enumerate() { - region.assign_advice( + res.push(region.assign_advice( || format!("assign lookup value {} {}", idx, offset), column.advice, offset, || Value::known(*bit), - )?; + )?); } // Round constant @@ -952,7 +956,7 @@ impl KeccakCircuitConfig { || Value::known(row.round_cst), )?; - Ok(()) + Ok(res) } pub(crate) fn load_aux_tables(&self, layouter: &mut impl Layouter) -> Result<(), Error> { @@ -969,7 +973,7 @@ impl KeccakCircuitConfig { load_pack_table(layouter, &self.pack_table) } - fn annotate_circuit(&self, region: &mut Region) { + pub(crate) fn annotate_circuit(&self, region: &mut Region) { //region.name_column(|| "KECCAK_q_enable", self.q_enable); region.name_column(|| "KECCAK_q_first", self.q_first); region.name_column(|| "KECCAK_q_round", self.q_round); @@ -983,8 +987,11 @@ impl KeccakCircuitConfig { #[derive(Default, Clone, Debug)] pub struct KeccakCircuit { // The input is a two dimensional vector - // Each input + // Each input row is a pre-image of the hash + // The output row of the hash, i.e., the digest is NOT part of the circuit input inputs: Vec>, + // The maximum number of rows, for example, 2^20 + // This needs to be large enough for the circuit. num_rows: usize, _marker: PhantomData, } diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index 2db12e4fd7..3991fd296d 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -420,7 +420,7 @@ pub(crate) mod transform { } } -// Transfroms values to cells +// Transforms values to cells pub(crate) mod transform_to { use super::{Cell, KeccakRegion, Part, PartValue}; use crate::{ @@ -489,7 +489,10 @@ pub(crate) mod transform_to { } } -fn keccak_rows(bytes: &[u8], challenges: Challenges>) -> Vec> { +pub(crate) fn keccak_rows( + bytes: &[u8], + challenges: Challenges>, +) -> Vec> { let mut rows = Vec::new(); keccak(&mut rows, bytes, challenges); rows @@ -865,17 +868,18 @@ pub fn multi_keccak( }); } - // Dedup actual keccaks + // // Dedup actual keccaks // let inputs_len: usize = bytes.iter().map(|k| k.len()).sum(); // let inputs_num = bytes.len(); // for (idx, bytes) in bytes.iter().enumerate() { - // debug!("{}th keccak is of len {}", idx, bytes.len()); + // println!("{}th keccak is of len {}", idx, bytes.len()); // } // let bytes: Vec<_> = bytes.iter().unique().collect(); // let inputs_len2: usize = bytes.iter().map(|k| k.len()).sum(); // let inputs_num2 = bytes.len(); - // debug!("after dedup inputs, input num {inputs_num}->{inputs_num2}, input total len - // {inputs_len}->{inputs_len2}"); + // println!( + // "after dedup inputs, input num {inputs_num}->{inputs_num2}, input total len + // {inputs_len}->{inputs_len2}" ); // TODO: optimize the `extend` using Iter? let real_rows: Vec<_> = bytes diff --git a/zkevm-circuits/src/pi_agg_circuit.rs b/zkevm-circuits/src/pi_agg_circuit.rs index 40d895e04c..531858b63e 100644 --- a/zkevm-circuits/src/pi_agg_circuit.rs +++ b/zkevm-circuits/src/pi_agg_circuit.rs @@ -1,5 +1,17 @@ //! This module implements circuits that aggregates public inputs of many blocks/txs into a single //! one. +// This module implements `Chunk` related data types. +// A chunk is a list of blocks. mod chunk; -mod multi_bach; +// This module implements `MultiBatch` related data types. +// A multi_batch is a list of chunk. +mod multi_batch; +// Circuit implementation of `MultiBatch` public input hashes. +mod multi_batch_circuit; + +// TODO(ZZ): update to the right degree +pub(crate) const LOG_DEGREE: u32 = 19; + +#[cfg(test)] +mod tests; diff --git a/zkevm-circuits/src/pi_agg_circuit/chunk.rs b/zkevm-circuits/src/pi_agg_circuit/chunk.rs index 18200ba745..3bb2a7e1ec 100644 --- a/zkevm-circuits/src/pi_agg_circuit/chunk.rs +++ b/zkevm-circuits/src/pi_agg_circuit/chunk.rs @@ -1,35 +1,48 @@ //! This module implements `Chunk` related data types. //! A chunk is a list of blocks. use crate::pi_circuit::PublicData; +use eth_types::H256; use ethers_core::utils::keccak256; #[derive(Default, Debug, Clone)] /// ChunkPublicData is a list of PublicData, each corresponds to a block /// with max_txn transactions. -pub struct ChunkPublicData { +pub struct ChunkPublicData { pub(crate) public_data_vec: Vec, - pub(crate) max_txs: usize, } -impl ChunkPublicData { +impl ChunkPublicData { /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. pub(crate) fn raw_data_hash_bytes(&self) -> Vec { + let (_, digest) = self.raw_data_hash(); + digest.as_bytes().to_vec() + } + + /// Compute the raw_data_hash, return both the preimage and the hash + pub(crate) fn raw_data_hash(&self) -> (Vec, H256) { let mut to_be_hashed = vec![]; self.public_data_vec.iter().for_each(|public_data| { to_be_hashed .extend_from_slice( // extract all the data from transactions - public_data.get_pi(self.max_txs).as_ref(), + public_data.get_pi(MAX_TXS).as_bytes(), ) .into() }); // data hash is the keccak hash of concatenation of all data fields - keccak256::<&[u8]>(to_be_hashed.as_ref()).into() + let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); + (to_be_hashed, digest) } /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. - pub(crate) fn raw_public_input_bytes(&self) -> Vec { + pub(crate) fn raw_public_input_hash_bytes(&self) -> Vec { + let (_, digest) = self.raw_public_input_hash(); + digest.as_bytes().to_vec() + } + + /// Compute the raw_public_input_hash, return both the preimage and the hash + pub(crate) fn raw_public_input_hash(&self) -> (Vec, H256) { let mut to_be_hashed = vec![]; // extract the prev state root of the first block // and the next state root of the last block @@ -43,13 +56,28 @@ impl ChunkPublicData { ); // withdraw root // - // FIXME: for each block we hav a withdraw_trie_root + // FIXME: for each block we have a withdraw_trie_root // This is different from the spec. // Double check this. self.public_data_vec.iter().for_each(|public_data| { to_be_hashed.extend_from_slice(public_data.withdraw_trie_root.as_bytes()) }); - keccak256::<&[u8]>(to_be_hashed.as_ref()).into() + let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); + (to_be_hashed, digest) + } + + /// Extract all the hash inputs and outputs that will ever be computed + /// for this given multi_batch + /// Ordered as: + /// - the hash preimage/digest pair for the data_hash + /// - the hash preimage/digest pair for the public_input_hash + pub(crate) fn extract_hashes(&self) -> (Vec>, Vec) { + let data_hash_pair = self.raw_data_hash(); + let pi_hash_pair = self.raw_public_input_hash(); + ( + vec![data_hash_pair.0, pi_hash_pair.0], + vec![data_hash_pair.1, pi_hash_pair.1], + ) } } diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_bach.rs b/zkevm-circuits/src/pi_agg_circuit/multi_bach.rs deleted file mode 100644 index 5370f4d8ef..0000000000 --- a/zkevm-circuits/src/pi_agg_circuit/multi_bach.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! This module implements `MultiBatch` related data types. -//! A multi_batch is a list of chunk. - -use super::chunk::ChunkPublicData; -use ethers_core::utils::keccak256; - -#[derive(Default, Debug, Clone)] -/// MultiBatchPublicData is a list of ChunkPublicData, each corresponds to a chunk -/// with max_txn transactions. -pub struct MultiBatchPublicData { - pub(crate) public_data_vec: Vec, - pub(crate) max_txs: usize, -} - -impl MultiBatchPublicData { - /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. - pub(crate) fn raw_data_hash_bytes(&self) -> Vec { - let mut to_be_hashed = vec![]; - - self.public_data_vec.iter().for_each(|chunk_public_data| { - to_be_hashed - .extend_from_slice( - // extract all the data from each chunk - chunk_public_data.raw_data_hash_bytes().as_ref(), - ) - .into() - }); - // data hash is the keccak hash of concatenation of all data fields - keccak256::<&[u8]>(to_be_hashed.as_ref()).into() - } - - /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. - pub(crate) fn raw_public_input_bytes(&self) -> Vec { - let mut to_be_hashed = vec![]; - self.public_data_vec.iter().for_each(|chunk_public_data| { - to_be_hashed - .extend_from_slice( - // extract all the data from each chunk - chunk_public_data.raw_public_input_bytes().as_ref(), - ) - .into() - }); - keccak256::<&[u8]>(to_be_hashed.as_ref()).into() - } -} diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs new file mode 100644 index 0000000000..907bd9064b --- /dev/null +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs @@ -0,0 +1,92 @@ +//! This module implements `MultiBatch` related data types. +//! A multi_batch is a list of chunk. + +use super::chunk::ChunkPublicData; + +use eth_types::H256; +use ethers_core::utils::keccak256; + +#[derive(Default, Debug, Clone)] +/// MultiBatchPublicData is a list of ChunkPublicData, each corresponds to a chunk +/// with max_txn transactions. +pub struct MultiBatchPublicData { + pub(crate) public_data_chunks: Vec>, +} + +impl MultiBatchPublicData { + /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. + pub(crate) fn raw_data_hash_bytes(&self) -> Vec { + let (_, digest) = self.raw_data_hash(); + digest.as_bytes().to_vec() + } + + /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. + pub(crate) fn raw_data_hash(&self) -> (Vec, H256) { + let mut to_be_hashed = vec![]; + + self.public_data_chunks + .iter() + .for_each(|public_data_chunk| { + to_be_hashed + .extend_from_slice( + // extract all the data from each chunk + public_data_chunk.raw_data_hash_bytes().as_ref(), + ) + .into() + }); + // data hash is the keccak hash of concatenation of all data fields + let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); + (to_be_hashed, digest) + } + + /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. + pub(crate) fn raw_public_input_hash_bytes(&self) -> Vec { + let (_, digest) = self.raw_public_input_hash(); + digest.as_bytes().to_vec() + } + + /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. + pub(crate) fn raw_public_input_hash(&self) -> (Vec, H256) { + let mut to_be_hashed = vec![]; + self.public_data_chunks + .iter() + .for_each(|public_data_chunk| { + to_be_hashed + .extend_from_slice( + // extract all the data from each chunk + public_data_chunk.raw_public_input_hash_bytes().as_ref(), + ) + .into() + }); + let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); + (to_be_hashed, digest) + } + + /// Extract all the hash inputs and outputs that will ever be computed + /// for this given multi_batch + /// Ordered as: + /// - the hash preimage/digest pairs for all blocks + /// - the hash preimage/digest pair for the data_hash + /// - the hash preimage/digest pair for the public_input_hash + /// Returns the + /// - hash input + /// - hash output + pub(crate) fn extract_hashes(&self) -> (Vec>, Vec) { + let mut preimages = vec![]; + let mut digests = vec![]; + self.public_data_chunks + .iter() + .for_each(|public_data_chunk| { + let (preimage, digest) = public_data_chunk.extract_hashes(); + preimages.extend_from_slice(preimage.as_ref()); + digests.extend_from_slice(digest.as_ref()) + }); + let data_hash_pair = self.raw_data_hash(); + let pi_hash_pair = self.raw_public_input_hash(); + + preimages.extend_from_slice(&[data_hash_pair.0, pi_hash_pair.0]); + digests.extend_from_slice(&[data_hash_pair.1, pi_hash_pair.1]); + + (preimages, digests) + } +} diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs new file mode 100644 index 0000000000..c661b6b6ce --- /dev/null +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs @@ -0,0 +1,400 @@ +//! Circuit implementation of `MultiBatch` public input hashes. +//! +//! +//! IMPORTANT: +//! +//! The current implementation is KeccakCircuit parameter dependent. +//! The current code base is hardcoded for KeccakCircuit configured +//! with 300 rows and 83 columns per hash call. +//! +//! This is because we need to extract the preimage and digest cells, +//! and argue some relationship between those cells. If the cell manager +//! configuration is changed, the cells indices will be different, +//! and the relationship becomes unsatisfied. +//! +//! For now we stick to this hard coded design for simplicity. +//! A flexible design is a future work. +//! + + +use super::{multi_batch::MultiBatchPublicData, LOG_DEGREE}; +use crate::{ + keccak_circuit::{ + keccak_packed_multi::{get_num_rows_per_round, multi_keccak}, + param::NUM_ROUNDS, + KeccakCircuitConfig, KeccakCircuitConfigArgs, + }, + table::{KeccakTable, LookupTable}, + util::{Challenges, SubCircuitConfig}, +}; + +use eth_types::{Field, H256}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value}, + plonk::{Circuit, Column, ConstraintSystem, Error, Instance}, +}; +use itertools::Itertools; +use std::marker::PhantomData; + +/// Circuit inputs for MultiBatch +#[derive(Clone, Debug)] +pub struct MultiBatchCircuit { + pub(crate) multi_batch_public_data: MultiBatchPublicData, + /// public input: the hash digest obtained via + /// `MultiBatchPublicData::raw_public_input_bytes` + pub(crate) hash_digest: H256, + pub(crate) _marker: PhantomData, +} + +impl MultiBatchCircuit { + fn raw_public_input_bytes(&self) -> Vec { + self.multi_batch_public_data.raw_public_input_hash_bytes() + } +} + +/// Config for MultiBatchCircuit +#[derive(Clone, Debug)] +pub struct MultiBatchCircuitConfig { + /// Log of the degree of the circuit + log_degree: usize, + + /// Max number of supported transactions + max_txs: usize, + + /// Instance column stores the output of the hash + /// i.e., hi(keccak(hash_preimage)), lo(keccak(hash_preimage)) + hash_digest_column: Column, + + /// Challenges + challenges: Challenges, + + /// Keccak circuit config + keccak_circuit_config: KeccakCircuitConfig, + + _marker: PhantomData, +} + +impl MultiBatchCircuitConfig { + /// Input the hash input bytes, + /// assign the circuit for hash function, + /// return cells for the hash inputs and digests. + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + challenges: Challenges>, + preimages: &[Vec], + num_chunks: usize, + ) -> Result< + ( + Vec>, // input cells + Vec>, // digest cells + ), + Error, + > { + let mut is_first_time = true; + let num_rows = 1 << 18; + + let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; + + let preimage_indices = get_preimage_indices(preimages); + let digest_indices = get_digest_indices(preimages.len()); + + let mut inputs = vec![]; + let mut digests = vec![]; + + let _ = layouter.assign_region( + || "assign keccak rows", + |mut region| { + if is_first_time { + is_first_time = false; + let offset = witness.len() - 1; + self.keccak_circuit_config + .set_row(&mut region, offset, &witness[offset])?; + return Ok(()); + } + // assign the hash cells + let mut final_rpi_hash_inputs = vec![]; + let mut final_data_hash_inputs = vec![]; + let mut final_rpi_hash_inputs_reused = vec![]; + let mut final_data_hash_inputs_reused = vec![]; + let total_hash_ctr = 2 * num_chunks + 2; + + for (offset, keccak_row) in witness.iter().enumerate() { + let row = + self.keccak_circuit_config + .set_row(&mut region, offset, keccak_row)?; + + // we have k chunks, each chunk requires 2 hashes + // so the total input hashes prior to the final two is 2k + // extract the cells that are going to be reused as preimages + if offset <= 2 * num_chunks * 300 && digest_indices.contains(&offset) { + let current_hash_ctr = offset / 300; + // this hash occurred in the batch, + // we need to extract the digests + if current_hash_ctr % 2 == 0 { + // data hash + final_data_hash_inputs.push(row.last().unwrap().clone()) + } else { + // rpi hash + final_rpi_hash_inputs.push(row.last().unwrap().clone()) + } + } + // extract the cells that are the preimages + if offset <= (2 * num_chunks + 1) * 300 + && offset > 2 * num_chunks * 300 + && preimage_indices.contains(&offset) + { + // data hash + final_data_hash_inputs_reused.push(row[6].clone()); + } + if offset <= total_hash_ctr * 300 + && offset > (2 * num_chunks + 1) * 300 + && preimage_indices.contains(&offset) + { + // rpi hash + final_rpi_hash_inputs_reused.push(row[6].clone()); + } + + // extract the returning cells + if preimage_indices.contains(&offset) { + inputs.push(row[6].clone()); + } + if digest_indices.contains(&offset) { + digests.push(row.last().unwrap().clone()); + } + } + // now we need to constrain that the hash digests are used as preimages + { + assert_eq!( + final_data_hash_inputs.len(), + final_data_hash_inputs_reused.len(), + "final data hash's input length does not match" + ); + let chunks = final_data_hash_inputs.len() / 8; + for i in 0..chunks { + for j in 0..8 { + { + let mut t1 = F::default(); + let mut t2 = F::default(); + final_data_hash_inputs[i * 8 + j].value().map(|f| t1 = *f); + final_data_hash_inputs_reused[(chunks - i - 1) * 8 + j] + .value() + .map(|f| t2 = *f); + assert_eq!(t1, t2) + } + // preimage and digest has different endianness + // (great design decision btw /s) + region.constrain_equal( + final_data_hash_inputs[i * 8 + j].cell(), + final_data_hash_inputs_reused[(chunks - i - 1) * 8 + j].cell(), + )?; + } + } + } + { + assert_eq!( + final_rpi_hash_inputs.len(), + final_rpi_hash_inputs_reused.len(), + "final data hash's input length does not match" + ); + let chunks = final_rpi_hash_inputs.len() / 8; + for i in 0..chunks { + for j in 0..8 { + { + let mut t1 = F::default(); + let mut t2 = F::default(); + final_rpi_hash_inputs[i * 8 + j].value().map(|f| t1 = *f); + final_rpi_hash_inputs_reused[(chunks - i - 1) * 8 + j] + .value() + .map(|f| t2 = *f); + assert_eq!(t1, t2) + } + // preimage and digest has different endianness + // (great design decision btw /s) + region.constrain_equal( + final_rpi_hash_inputs[i * 8 + j].cell(), + final_rpi_hash_inputs_reused[(chunks - i - 1) * 8 + j].cell(), + )?; + } + } + } + self.keccak_circuit_config + .keccak_table + .annotate_columns_in_region(&mut region); + self.keccak_circuit_config.annotate_circuit(&mut region); + Ok(()) + }, + )?; + Ok((inputs, digests)) + } +} + +impl Circuit for MultiBatchCircuit { + type FloorPlanner = SimpleFloorPlanner; + + type Config = MultiBatchCircuitConfig; + + fn without_witnesses(&self) -> Self { + Self { + multi_batch_public_data: MultiBatchPublicData::default(), + hash_digest: H256([0u8; 32]), //(F::default(), F::default()), + _marker: PhantomData::default(), + } + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + // Instance column stores the output of the hash + // i.e., hi(keccak(hash_preimage)), lo(keccak(hash_preimage)) + let hash_digest_column = meta.instance_column(); + + let challenges = Challenges::construct(meta); + let challenges_exprs = challenges.exprs(meta); + + // hash configuration + let keccak_circuit_config = { + let keccak_table = KeccakTable::construct(meta); + + let keccak_circuit_config_args = KeccakCircuitConfigArgs { + keccak_table, + challenges: challenges_exprs, + }; + + KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + }; + + let columns = keccak_circuit_config.cell_manager.columns(); + + // enabling equality for preimage and digest columns + meta.enable_equality(columns[6].advice); + // digest column + meta.enable_equality(columns.last().unwrap().advice); + + MultiBatchCircuitConfig { + log_degree: LOG_DEGREE as usize, + max_txs: MAX_TXS, + hash_digest_column, + challenges, + keccak_circuit_config, + _marker: PhantomData::default(), + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = config.challenges.values(&layouter); + + //============================================================== + // extract all the hashes and load them to the hash table + //============================================================== + let (preimages, _digests) = self.multi_batch_public_data.extract_hashes(); + + // for i in 0..4 { + // println!("{}-th hash", i); + // println!("preimage: {:02x?}", preimages[i]); + // println!( + // "preimage rlc: {:02x?}", + // data_to_rlc(preimages[i].as_ref(), &challenges) + // ); + // println!("digest: {:02x?}", digests[i]); + // println!( + // "digest rlc: {:02x?}\n\n", + // data_to_rlc(digests[i].as_ref(), &challenges) + // ); + // } + + config + .keccak_circuit_config + .load_aux_tables(&mut layouter)?; + + let (_preimages, _digests) = config.assign( + &mut layouter, + challenges, + &preimages, + self.multi_batch_public_data.public_data_chunks.len(), + )?; + + // Following code are used for debugging + // + // // assert the inputs are correct + // { + // for (i, preimage) in preimages.iter().enumerate() { + // for (j, chunk) in preimage.iter().chunks(8).into_iter().enumerate() { + // for (k, &byte) in chunk.enumerate() { + // let index = i * 300 + j * 12 + k + 12; + // println!("index: {}", index); + // println!("input byte: {:?}", F::from(byte as u64)); + // println!("keccak byte: {:?}\n", witness[index].cell_values[6]); + // assert_eq!(F::from(byte as u64), witness[index].cell_values[6]); + // } + // } + // } + // } + + // // assert the outputs are correct + // { + // for (i, &digest) in digests.iter().enumerate() { + // let preimage_bytes: [u8; 32] = digest.try_into().unwrap(); + // for (j, chunk) in preimage_bytes.iter().chunks(8).into_iter().enumerate() { + // for (k, &byte) in chunk.enumerate() { + // let index = i * 300 + (3 - j) * 12 + k + 252; + // println!("index: {}", index); + // println!("digest byte: {:?}", F::from(byte as u64)); + // println!("keccak byte: {:?}\n", witness[index].cell_values.last()); + // assert_eq!( + // F::from(byte as u64), + // *witness[index].cell_values.last().unwrap() + // ); + // } + // } + // } + // } + + Ok(()) + } +} + +// compute an RLC of the hash digest in the clear +fn data_to_rlc(digest: &[u8], challenges: &Challenges>) -> Value { + digest.iter().fold(Value::known(F::zero()), |acc, byte| { + acc.zip(challenges.evm_word()) + .and_then(|(acc, rand)| Value::known(acc * rand + F::from(*byte as u64))) + }) +} + +fn capacity(num_rows: usize) -> Option { + if num_rows > 0 { + // Subtract two for unusable rows + Some(num_rows / ((NUM_ROUNDS + 1) * get_num_rows_per_round()) - 2) + } else { + None + } +} + +/// Return the indices of the rows that contain the input preimages +fn get_preimage_indices(preimages: &[Vec]) -> Vec { + let mut res = vec![]; + for (i, preimage) in preimages.iter().enumerate() { + for (j, chunk) in preimage.iter().chunks(8).into_iter().enumerate() { + for (k, _) in chunk.enumerate() { + res.push(i * 300 + j * 12 + k + 12) + } + } + } + res +} + +/// Return the indices of the rows that contain the output digest +fn get_digest_indices(digest_len: usize) -> Vec { + let mut res = vec![]; + for i in 0..digest_len { + for j in 0..4 { + for k in 0..8 { + res.push(i * 300 + (3 - j) * 12 + k + 252) + } + } + } + res +} diff --git a/zkevm-circuits/src/pi_agg_circuit/tests.rs b/zkevm-circuits/src/pi_agg_circuit/tests.rs new file mode 100644 index 0000000000..f64b6e2d3c --- /dev/null +++ b/zkevm-circuits/src/pi_agg_circuit/tests.rs @@ -0,0 +1,38 @@ +//! Test modules for aggregating public inputs + +use std::marker::PhantomData; + +use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; + +use crate::pi_circuit::PublicData; + +use super::{ + chunk::ChunkPublicData, multi_batch::MultiBatchPublicData, + multi_batch_circuit::MultiBatchCircuit, LOG_DEGREE, +}; + +const TEST_MAX_TXS: usize = 4; + +#[test] +fn test_pi_agg_circuit() { + let public_data = PublicData::default(); + let chunk = ChunkPublicData:: { + public_data_vec: vec![public_data.clone(), public_data], + }; + + let multi_batch = MultiBatchPublicData { + public_data_chunks: vec![chunk.clone(), chunk], + }; + let (_, hash_digest) = multi_batch.raw_public_input_hash(); + + let multi_batch_circuit = MultiBatchCircuit:: { + multi_batch_public_data: multi_batch, + hash_digest, + _marker: PhantomData::default(), + }; + + let mock_prover = + MockProver::::run(LOG_DEGREE, &multi_batch_circuit, vec![vec![]]).unwrap(); + + mock_prover.assert_satisfied_par() +} From c5a2e88c1c052435bb0523de008fd5b97de5d929 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 17 May 2023 15:37:58 -0400 Subject: [PATCH 03/41] fix clippy --- .../src/pi_agg_circuit/multi_batch_circuit.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs index c661b6b6ce..92e75aa1e3 100644 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs @@ -1,21 +1,19 @@ //! Circuit implementation of `MultiBatch` public input hashes. -//! -//! -//! IMPORTANT: -//! +//! +//! +//! IMPORTANT: +//! //! The current implementation is KeccakCircuit parameter dependent. //! The current code base is hardcoded for KeccakCircuit configured -//! with 300 rows and 83 columns per hash call. -//! +//! with 300 rows and 83 columns per hash call. +//! //! This is because we need to extract the preimage and digest cells, //! and argue some relationship between those cells. If the cell manager -//! configuration is changed, the cells indices will be different, +//! configuration is changed, the cells indices will be different, //! and the relationship becomes unsatisfied. -//! +//! //! For now we stick to this hard coded design for simplicity. //! A flexible design is a future work. -//! - use super::{multi_batch::MultiBatchPublicData, LOG_DEGREE}; use crate::{ @@ -78,6 +76,7 @@ impl MultiBatchCircuitConfig { /// Input the hash input bytes, /// assign the circuit for hash function, /// return cells for the hash inputs and digests. + #[allow(clippy::type_complexity)] pub(crate) fn assign( &self, layouter: &mut impl Layouter, @@ -102,7 +101,7 @@ impl MultiBatchCircuitConfig { let mut inputs = vec![]; let mut digests = vec![]; - let _ = layouter.assign_region( + layouter.assign_region( || "assign keccak rows", |mut region| { if is_first_time { From 82717c58de9ee22fca246f75a59998d6b7bf3af7 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 17 May 2023 15:43:07 -0400 Subject: [PATCH 04/41] clean up --- .../src/pi_agg_circuit/multi_batch_circuit.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs index 92e75aa1e3..d69eb8a603 100644 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs @@ -5,7 +5,8 @@ //! //! The current implementation is KeccakCircuit parameter dependent. //! The current code base is hardcoded for KeccakCircuit configured -//! with 300 rows and 83 columns per hash call. +//! with 300 rows and 87 columns per hash call. The 7-th column is used +//! for hash preimages and the 87-th column is for hash digest. //! //! This is because we need to extract the preimage and digest cells, //! and argue some relationship between those cells. If the cell manager @@ -262,6 +263,13 @@ impl Circuit for MultiBatchCircuit Date: Mon, 22 May 2023 11:58:24 -0400 Subject: [PATCH 05/41] [fix] supports multiple rounds per hash for PI aggregation circuit --- .../src/pi_agg_circuit/multi_batch_circuit.rs | 307 +++++++++--------- zkevm-circuits/src/pi_agg_circuit/tests.rs | 49 ++- 2 files changed, 206 insertions(+), 150 deletions(-) diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs index d69eb8a603..9949443c40 100644 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs @@ -32,8 +32,7 @@ use halo2_proofs::{ circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value}, plonk::{Circuit, Column, ConstraintSystem, Error, Instance}, }; -use itertools::Itertools; -use std::marker::PhantomData; +use std::{marker::PhantomData, vec}; /// Circuit inputs for MultiBatch #[derive(Clone, Debug)] @@ -60,8 +59,7 @@ pub struct MultiBatchCircuitConfig { /// Max number of supported transactions max_txs: usize, - /// Instance column stores the output of the hash - /// i.e., hi(keccak(hash_preimage)), lo(keccak(hash_preimage)) + /// Instance column stores the aggregated rpi hash digest hash_digest_column: Column, /// Challenges @@ -83,11 +81,10 @@ impl MultiBatchCircuitConfig { layouter: &mut impl Layouter, challenges: Challenges>, preimages: &[Vec], - num_chunks: usize, ) -> Result< ( - Vec>, // input cells - Vec>, // digest cells + Vec>>, // input cells + Vec>>, // digest cells ), Error, > { @@ -96,11 +93,14 @@ impl MultiBatchCircuitConfig { let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; - let preimage_indices = get_preimage_indices(preimages); - let digest_indices = get_digest_indices(preimages.len()); + // extract the indices of the rows for which the preimage and the digest cells lie in + let (preimage_indices, digest_indices) = get_indices(preimages); - let mut inputs = vec![]; - let mut digests = vec![]; + log::info!("preimage indices: {:?}", preimage_indices); + log::info!("digest indices: {:?}", digest_indices); + + let mut hash_input_cells = vec![]; + let mut hash_output_cells = vec![]; layouter.assign_region( || "assign keccak rows", @@ -112,112 +112,103 @@ impl MultiBatchCircuitConfig { .set_row(&mut region, offset, &witness[offset])?; return Ok(()); } - // assign the hash cells - let mut final_rpi_hash_inputs = vec![]; - let mut final_data_hash_inputs = vec![]; - let mut final_rpi_hash_inputs_reused = vec![]; - let mut final_data_hash_inputs_reused = vec![]; - let total_hash_ctr = 2 * num_chunks + 2; + // ==================================================== + // Step 1. Extract the hash cells + // ==================================================== + let mut current_hash_input_cells = vec![]; + let mut current_hash_output_cells = vec![]; for (offset, keccak_row) in witness.iter().enumerate() { let row = self.keccak_circuit_config .set_row(&mut region, offset, keccak_row)?; - // we have k chunks, each chunk requires 2 hashes - // so the total input hashes prior to the final two is 2k - // extract the cells that are going to be reused as preimages - if offset <= 2 * num_chunks * 300 && digest_indices.contains(&offset) { - let current_hash_ctr = offset / 300; - // this hash occurred in the batch, - // we need to extract the digests - if current_hash_ctr % 2 == 0 { - // data hash - final_data_hash_inputs.push(row.last().unwrap().clone()) - } else { - // rpi hash - final_rpi_hash_inputs.push(row.last().unwrap().clone()) - } - } - // extract the cells that are the preimages - if offset <= (2 * num_chunks + 1) * 300 - && offset > 2 * num_chunks * 300 - && preimage_indices.contains(&offset) - { - // data hash - final_data_hash_inputs_reused.push(row[6].clone()); - } - if offset <= total_hash_ctr * 300 - && offset > (2 * num_chunks + 1) * 300 - && preimage_indices.contains(&offset) - { - // rpi hash - final_rpi_hash_inputs_reused.push(row[6].clone()); - } - - // extract the returning cells if preimage_indices.contains(&offset) { - inputs.push(row[6].clone()); + current_hash_input_cells.push(row[6].clone()); } if digest_indices.contains(&offset) { - digests.push(row.last().unwrap().clone()); + current_hash_output_cells.push(row.last().unwrap().clone()); + } + + // we reset the current hash when it is finalized + // note that length == 0 indicate that the hash is a padding + // so we simply skip it + if keccak_row.is_final && keccak_row.length != 0 { + hash_input_cells.push(current_hash_input_cells); + hash_output_cells.push(current_hash_output_cells); + current_hash_input_cells = vec![]; + current_hash_output_cells = vec![]; } } - // now we need to constrain that the hash digests are used as preimages + + // sanity: we have same number of hash input and output + let hash_num = hash_input_cells.len(); + assert!(hash_num % 2 == 0); + assert_eq!(hash_num, hash_output_cells.len()); + + // ==================================================== + // Step 2. Constraint the hash digest is reused later for hash preimages + // ==================================================== { - assert_eq!( - final_data_hash_inputs.len(), - final_data_hash_inputs_reused.len(), - "final data hash's input length does not match" - ); - let chunks = final_data_hash_inputs.len() / 8; - for i in 0..chunks { - for j in 0..8 { - { - let mut t1 = F::default(); - let mut t2 = F::default(); - final_data_hash_inputs[i * 8 + j].value().map(|f| t1 = *f); - final_data_hash_inputs_reused[(chunks - i - 1) * 8 + j] - .value() - .map(|f| t2 = *f); - assert_eq!(t1, t2) + // 2.1 assert the data hash's input is well-formed + { + let mut final_data_hash_inputs = vec![]; + for i in 0..hash_num / 2 - 1 { + final_data_hash_inputs + .extend_from_slice(hash_output_cells[i * 2].as_ref()); + } + assert_eq!( + final_data_hash_inputs.len(), + hash_input_cells[hash_num - 2].len() + ); + let chunks = final_data_hash_inputs.len() / 8; + for i in 0..chunks { + for j in 0..8 { + // sanity: the values in cells match + assert_equal( + &final_data_hash_inputs[i * 8 + j], + &hash_input_cells[hash_num - 2][(chunks - i - 1) * 8 + j], + ); + // preimage and digest has different endianness + // (great design decision btw /s) + region.constrain_equal( + final_data_hash_inputs[i * 8 + j].cell(), + hash_input_cells[hash_num - 2][(chunks - i - 1) * 8 + j].cell(), + )?; } - // preimage and digest has different endianness - // (great design decision btw /s) - region.constrain_equal( - final_data_hash_inputs[i * 8 + j].cell(), - final_data_hash_inputs_reused[(chunks - i - 1) * 8 + j].cell(), - )?; } } - } - { - assert_eq!( - final_rpi_hash_inputs.len(), - final_rpi_hash_inputs_reused.len(), - "final data hash's input length does not match" - ); - let chunks = final_rpi_hash_inputs.len() / 8; - for i in 0..chunks { - for j in 0..8 { - { - let mut t1 = F::default(); - let mut t2 = F::default(); - final_rpi_hash_inputs[i * 8 + j].value().map(|f| t1 = *f); - final_rpi_hash_inputs_reused[(chunks - i - 1) * 8 + j] - .value() - .map(|f| t2 = *f); - assert_eq!(t1, t2) + + // 2.2 assert the rpi hash's input is well-formed + { + let mut final_rpi_hash_inputs = vec![]; + for i in 0..hash_num / 2 - 1 { + final_rpi_hash_inputs + .extend_from_slice(hash_output_cells[i * 2 + 1].as_ref()); + } + assert_eq!( + final_rpi_hash_inputs.len(), + hash_input_cells[hash_num - 1].len() + ); + let chunks = final_rpi_hash_inputs.len() / 8; + for i in 0..chunks { + for j in 0..8 { + // sanity: the values in cells match + assert_equal( + &final_rpi_hash_inputs[i * 8 + j], + &hash_input_cells[hash_num - 1][(chunks - i - 1) * 8 + j], + ); + // preimage and digest has different endianness + // (great design decision btw /s) + region.constrain_equal( + final_rpi_hash_inputs[i * 8 + j].cell(), + hash_input_cells[hash_num - 1][(chunks - i - 1) * 8 + j].cell(), + )?; } - // preimage and digest has different endianness - // (great design decision btw /s) - region.constrain_equal( - final_rpi_hash_inputs[i * 8 + j].cell(), - final_rpi_hash_inputs_reused[(chunks - i - 1) * 8 + j].cell(), - )?; } } } + self.keccak_circuit_config .keccak_table .annotate_columns_in_region(&mut region); @@ -225,7 +216,39 @@ impl MultiBatchCircuitConfig { Ok(()) }, )?; - Ok((inputs, digests)) + + // ==================================================== + // Step 3. Constraint the final hash output matches the raw public input + // ==================================================== + { + let final_digest_cells = hash_output_cells.last().unwrap(); + for i in 0..4 { + for j in 0..8 { + // digest in circuit has a different endianness + layouter.constrain_instance( + final_digest_cells[(3 - i) * 8 + j].cell(), + self.hash_digest_column, + i * 8 + j, + )?; + } + } + } + + // debugging info + // + // print!("input: "); + // for e in hash_input_cells.iter() { + // print!("{} ", e.len()); + // } + // println!(); + // + // print!("digests: "); + // for e in hash_output_cells.iter() { + // print!("{} ", e.len()); + // } + // println!(); + + Ok((hash_input_cells, hash_output_cells)) } } @@ -275,6 +298,8 @@ impl Circuit for MultiBatchCircuit Circuit for MultiBatchCircuit Circuit for MultiBatchCircuit(digest: &[u8], challenges: &Challenges>) -> Value { - digest.iter().fold(Value::known(F::zero()), |acc, byte| { - acc.zip(challenges.evm_word()) - .and_then(|(acc, rand)| Value::known(acc * rand + F::from(*byte as u64))) - }) -} - fn capacity(num_rows: usize) -> Option { if num_rows > 0 { // Subtract two for unusable rows @@ -380,28 +378,45 @@ fn capacity(num_rows: usize) -> Option { } } -/// Return the indices of the rows that contain the input preimages -fn get_preimage_indices(preimages: &[Vec]) -> Vec { - let mut res = vec![]; - for (i, preimage) in preimages.iter().enumerate() { - for (j, chunk) in preimage.iter().chunks(8).into_iter().enumerate() { - for (k, _) in chunk.enumerate() { - res.push(i * 300 + j * 12 + k + 12) +/// Return +/// - the indices of the rows that contain the input preimages +/// - the indices of the rows that contain the output digest +/// - number of rounds that used for all but last two hashes +fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { + let mut preimage_indices = vec![]; + let mut digest_indices = vec![]; + let mut round_ctr = 0; + + for preimage in preimages.iter() { + let num_rounds = 1 + preimage.len() / 136; + for (i, round) in preimage.chunks(136).enumerate() { + // indices for preimegas + for (j, _chunk) in round.chunks(8).into_iter().enumerate() { + for k in 0..8 { + preimage_indices.push(round_ctr * 300 + j * 12 + k + 12) + } + } + // indices for digests + if i == num_rounds - 1 { + for j in 0..4 { + for k in 0..8 { + digest_indices.push(round_ctr * 300 + (3 - j) * 12 + k + 252) + } + } } + round_ctr += 1; } } - res + (preimage_indices, digest_indices) } -/// Return the indices of the rows that contain the output digest -fn get_digest_indices(digest_len: usize) -> Vec { - let mut res = vec![]; - for i in 0..digest_len { - for j in 0..4 { - for k in 0..8 { - res.push(i * 300 + (3 - j) * 12 + k + 252) - } - } - } - res +#[inline] +// assert two cells have same value +// (NOT constraining equality in circuit) +fn assert_equal(a: &AssignedCell, b: &AssignedCell) { + let mut t1 = F::default(); + let mut t2 = F::default(); + a.value().map(|f| t1 = *f); + b.value().map(|f| t2 = *f); + assert_eq!(t1, t2) } diff --git a/zkevm-circuits/src/pi_agg_circuit/tests.rs b/zkevm-circuits/src/pi_agg_circuit/tests.rs index f64b6e2d3c..5261bd1276 100644 --- a/zkevm-circuits/src/pi_agg_circuit/tests.rs +++ b/zkevm-circuits/src/pi_agg_circuit/tests.rs @@ -16,14 +16,55 @@ const TEST_MAX_TXS: usize = 4; #[test] fn test_pi_agg_circuit() { let public_data = PublicData::default(); - let chunk = ChunkPublicData:: { - public_data_vec: vec![public_data.clone(), public_data], + // this chunk spans 1 keccak round + let chunk_1 = ChunkPublicData:: { + public_data_vec: vec![ + public_data.clone(), + public_data.clone(), + public_data.clone(), + public_data.clone(), + ], + }; + + // this chunk spans 2 keccak rounds + let chunk_2 = ChunkPublicData:: { + public_data_vec: vec![ + public_data.clone(), + public_data.clone(), + public_data.clone(), + public_data.clone(), + public_data.clone(), + public_data, + ], }; let multi_batch = MultiBatchPublicData { - public_data_chunks: vec![chunk.clone(), chunk], + public_data_chunks: vec![ + chunk_1.clone(), + chunk_2.clone(), + chunk_1.clone(), + chunk_2.clone(), + chunk_1.clone(), + chunk_2, + chunk_1, + ], }; let (_, hash_digest) = multi_batch.raw_public_input_hash(); + println!("hash digest: {:?}", hash_digest); + let raw_public_input: Vec = hash_digest + .0 + .iter() + .map(|byte| Fr::from(*byte as u64)) + .collect(); + // .chunks(8) + // .rev() // reverse the order so it matches the layout in the circuit + // .map(|chunk| chunk.iter().map(|byte| Fr::from(*byte as u64))) + // .flatten() + // .collect(); + println!("rpi"); + for e in raw_public_input.iter() { + println!("{:?}", e) + } let multi_batch_circuit = MultiBatchCircuit:: { multi_batch_public_data: multi_batch, @@ -32,7 +73,7 @@ fn test_pi_agg_circuit() { }; let mock_prover = - MockProver::::run(LOG_DEGREE, &multi_batch_circuit, vec![vec![]]).unwrap(); + MockProver::::run(LOG_DEGREE, &multi_batch_circuit, vec![raw_public_input]).unwrap(); mock_prover.assert_satisfied_par() } From fd595efe1bbdd09797ae87b06748aec6c7467585 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Mon, 22 May 2023 12:05:04 -0400 Subject: [PATCH 06/41] [chore] fix clippy --- zkevm-circuits/src/pi_agg_circuit/chunk.rs | 10 ++++------ .../src/pi_agg_circuit/multi_batch.rs | 20 ++++++++----------- .../src/pi_agg_circuit/multi_batch_circuit.rs | 1 - zkevm-circuits/src/pi_agg_circuit/tests.rs | 5 ----- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/zkevm-circuits/src/pi_agg_circuit/chunk.rs b/zkevm-circuits/src/pi_agg_circuit/chunk.rs index 3bb2a7e1ec..eb28de803d 100644 --- a/zkevm-circuits/src/pi_agg_circuit/chunk.rs +++ b/zkevm-circuits/src/pi_agg_circuit/chunk.rs @@ -23,12 +23,10 @@ impl ChunkPublicData { let mut to_be_hashed = vec![]; self.public_data_vec.iter().for_each(|public_data| { - to_be_hashed - .extend_from_slice( - // extract all the data from transactions - public_data.get_pi(MAX_TXS).as_bytes(), - ) - .into() + to_be_hashed.extend_from_slice( + // extract all the data from transactions + public_data.get_pi(MAX_TXS).as_bytes(), + ) }); // data hash is the keccak hash of concatenation of all data fields let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs index 907bd9064b..62784725ed 100644 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs @@ -27,12 +27,10 @@ impl MultiBatchPublicData { self.public_data_chunks .iter() .for_each(|public_data_chunk| { - to_be_hashed - .extend_from_slice( - // extract all the data from each chunk - public_data_chunk.raw_data_hash_bytes().as_ref(), - ) - .into() + to_be_hashed.extend_from_slice( + // extract all the data from each chunk + public_data_chunk.raw_data_hash_bytes().as_ref(), + ) }); // data hash is the keccak hash of concatenation of all data fields let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); @@ -51,12 +49,10 @@ impl MultiBatchPublicData { self.public_data_chunks .iter() .for_each(|public_data_chunk| { - to_be_hashed - .extend_from_slice( - // extract all the data from each chunk - public_data_chunk.raw_public_input_hash_bytes().as_ref(), - ) - .into() + to_be_hashed.extend_from_slice( + // extract all the data from each chunk + public_data_chunk.raw_public_input_hash_bytes().as_ref(), + ) }); let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); (to_be_hashed, digest) diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs index 9949443c40..872d04702e 100644 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs @@ -381,7 +381,6 @@ fn capacity(num_rows: usize) -> Option { /// Return /// - the indices of the rows that contain the input preimages /// - the indices of the rows that contain the output digest -/// - number of rounds that used for all but last two hashes fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { let mut preimage_indices = vec![]; let mut digest_indices = vec![]; diff --git a/zkevm-circuits/src/pi_agg_circuit/tests.rs b/zkevm-circuits/src/pi_agg_circuit/tests.rs index 5261bd1276..45ff1a483e 100644 --- a/zkevm-circuits/src/pi_agg_circuit/tests.rs +++ b/zkevm-circuits/src/pi_agg_circuit/tests.rs @@ -56,11 +56,6 @@ fn test_pi_agg_circuit() { .iter() .map(|byte| Fr::from(*byte as u64)) .collect(); - // .chunks(8) - // .rev() // reverse the order so it matches the layout in the circuit - // .map(|chunk| chunk.iter().map(|byte| Fr::from(*byte as u64))) - // .flatten() - // .collect(); println!("rpi"); for e in raw_public_input.iter() { println!("{:?}", e) From c4cda3f4198c57ba4ede3bfb4b26f6b2b3f08708 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Mon, 22 May 2023 17:18:38 -0400 Subject: [PATCH 07/41] impl SubCircuit for MultiBatchCircuit --- zkevm-circuits/src/pi_agg_circuit.rs | 5 + .../src/pi_agg_circuit/multi_batch_circuit.rs | 71 +++-------- .../pi_agg_circuit/multi_batch_sub_circuit.rs | 111 ++++++++++++++++++ zkevm-circuits/src/pi_agg_circuit/tests.rs | 17 +-- 4 files changed, 137 insertions(+), 67 deletions(-) create mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs diff --git a/zkevm-circuits/src/pi_agg_circuit.rs b/zkevm-circuits/src/pi_agg_circuit.rs index 531858b63e..8efc91dec1 100644 --- a/zkevm-circuits/src/pi_agg_circuit.rs +++ b/zkevm-circuits/src/pi_agg_circuit.rs @@ -9,9 +9,14 @@ mod chunk; mod multi_batch; // Circuit implementation of `MultiBatch` public input hashes. mod multi_batch_circuit; +// Subcircuit implementation of `MultiBatch` public input hashes. +mod multi_batch_sub_circuit; // TODO(ZZ): update to the right degree pub(crate) const LOG_DEGREE: u32 = 19; +// TODO(ZZ): update to the right size +pub(crate) const MAX_TXS: usize = 20; + #[cfg(test)] mod tests; diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs index 872d04702e..67d8f39636 100644 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs @@ -16,14 +16,16 @@ //! For now we stick to this hard coded design for simplicity. //! A flexible design is a future work. -use super::{multi_batch::MultiBatchPublicData, LOG_DEGREE}; +use super::{ + multi_batch::MultiBatchPublicData, multi_batch_sub_circuit::MultiBatchCircuitConfigArgs, +}; use crate::{ keccak_circuit::{ keccak_packed_multi::{get_num_rows_per_round, multi_keccak}, param::NUM_ROUNDS, - KeccakCircuitConfig, KeccakCircuitConfigArgs, + KeccakCircuitConfig, }, - table::{KeccakTable, LookupTable}, + table::LookupTable, util::{Challenges, SubCircuitConfig}, }; @@ -54,21 +56,18 @@ impl MultiBatchCircuit { #[derive(Clone, Debug)] pub struct MultiBatchCircuitConfig { /// Log of the degree of the circuit - log_degree: usize, + pub(crate) log_degree: usize, /// Max number of supported transactions - max_txs: usize, + pub(crate) max_txs: usize, /// Instance column stores the aggregated rpi hash digest - hash_digest_column: Column, - - /// Challenges - challenges: Challenges, + pub(crate) hash_digest_column: Column, /// Keccak circuit config - keccak_circuit_config: KeccakCircuitConfig, + pub(crate) keccak_circuit_config: KeccakCircuitConfig, - _marker: PhantomData, + pub(crate) _marker: PhantomData, } impl MultiBatchCircuitConfig { @@ -255,7 +254,7 @@ impl MultiBatchCircuitConfig { impl Circuit for MultiBatchCircuit { type FloorPlanner = SimpleFloorPlanner; - type Config = MultiBatchCircuitConfig; + type Config = (MultiBatchCircuitConfig, Challenges); fn without_witnesses(&self) -> Self { Self { @@ -266,49 +265,13 @@ impl Circuit for MultiBatchCircuit) -> Self::Config { - // Instance column stores the output of the hash - // i.e., hi(keccak(hash_preimage)), lo(keccak(hash_preimage)) - let hash_digest_column = meta.instance_column(); - let challenges = Challenges::construct(meta); let challenges_exprs = challenges.exprs(meta); - - // hash configuration - let keccak_circuit_config = { - let keccak_table = KeccakTable::construct(meta); - - let keccak_circuit_config_args = KeccakCircuitConfigArgs { - keccak_table, - challenges: challenges_exprs, - }; - - KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + let args = MultiBatchCircuitConfigArgs { + challenges: challenges_exprs, }; - - let columns = keccak_circuit_config.cell_manager.columns(); - // The current code base is hardcoded for KeccakCircuit configured - // with 300 rows and 87 columns per hash call. - assert_eq!( - columns.len(), - 87, - "cell manager configuration does not match the hard coded setup" - ); - - // enabling equality for preimage and digest columns - meta.enable_equality(columns[6].advice); - // digest column - meta.enable_equality(columns.last().unwrap().advice); - // public input column - meta.enable_equality(hash_digest_column); - - MultiBatchCircuitConfig { - log_degree: LOG_DEGREE as usize, - max_txs: MAX_TXS, - hash_digest_column, - challenges, - keccak_circuit_config, - _marker: PhantomData::default(), - } + let config = MultiBatchCircuitConfig::new(meta, args); + (config, challenges) } fn synthesize( @@ -316,7 +279,9 @@ impl Circuit for MultiBatchCircuit, ) -> Result<(), Error> { - let challenges = config.challenges.values(&layouter); + let (config, challenge) = config; + + let challenges = challenge.values(&layouter); //============================================================== // extract all the hashes and load them to the hash table diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs new file mode 100644 index 0000000000..e0e19069cb --- /dev/null +++ b/zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs @@ -0,0 +1,111 @@ +//! Subcircuit implementation of `MultiBatch` public input hashes. + +use std::marker::PhantomData; + +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, Value}, + plonk::{ConstraintSystem, Error, Expression}, +}; + +use crate::{ + keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, + pi_agg_circuit::{LOG_DEGREE, MAX_TXS}, + table::KeccakTable, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; + +use super::multi_batch_circuit::{MultiBatchCircuit, MultiBatchCircuitConfig}; + +#[derive(Clone, Debug)] +pub struct MultiBatchCircuitConfigArgs { + pub challenges: Challenges>, +} + +impl SubCircuitConfig for MultiBatchCircuitConfig { + type ConfigArgs = MultiBatchCircuitConfigArgs; + + /// Return a new MultiBatchCircuitConfig + fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { + // Instance column stores the output of the hash + let hash_digest_column = meta.instance_column(); + + // hash configuration + let keccak_circuit_config = { + let keccak_table = KeccakTable::construct(meta); + + let keccak_circuit_config_args = KeccakCircuitConfigArgs { + keccak_table, + challenges: config_args.challenges, + }; + + KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + }; + + let columns = keccak_circuit_config.cell_manager.columns(); + // The current code base is hardcoded for KeccakCircuit configured + // with 300 rows and 87 columns per hash call. + assert_eq!( + columns.len(), + 87, + "cell manager configuration does not match the hard coded setup" + ); + + // enabling equality for preimage and digest columns + meta.enable_equality(columns[6].advice); + // digest column + meta.enable_equality(columns.last().unwrap().advice); + // public input column + meta.enable_equality(hash_digest_column); + + MultiBatchCircuitConfig { + log_degree: LOG_DEGREE as usize, + max_txs: MAX_TXS, + hash_digest_column, + keccak_circuit_config, + _marker: PhantomData::default(), + } + } +} + +impl SubCircuit for MultiBatchCircuit { + type Config = MultiBatchCircuitConfig; + + fn new_from_block(_block: &crate::witness::Block) -> Self { + // we cannot instantiate a new Self from a single block + unimplemented!() + } + + /// Return the minimum number of rows required to prove the block + /// Row numbers without/with padding are both returned. + fn min_num_rows_block(_block: &crate::witness::Block) -> (usize, usize) { + (1 << LOG_DEGREE, 1 << LOG_DEGREE) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + vec![self + .hash_digest + .0 + .iter() + .map(|&x| F::from(x as u64)) + .collect()] + } + + /// Make the assignments to the PiCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + // extract all the hashes and load them to the hash table + let (preimages, _digests) = self.multi_batch_public_data.extract_hashes(); + + config.keccak_circuit_config.load_aux_tables(layouter)?; + + let (_preimages, _digests) = config.assign(layouter, *challenges, &preimages)?; + + Ok(()) + } +} diff --git a/zkevm-circuits/src/pi_agg_circuit/tests.rs b/zkevm-circuits/src/pi_agg_circuit/tests.rs index 45ff1a483e..e529cc799e 100644 --- a/zkevm-circuits/src/pi_agg_circuit/tests.rs +++ b/zkevm-circuits/src/pi_agg_circuit/tests.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; -use crate::pi_circuit::PublicData; +use crate::{pi_circuit::PublicData, util::SubCircuit}; use super::{ chunk::ChunkPublicData, multi_batch::MultiBatchPublicData, @@ -50,25 +50,14 @@ fn test_pi_agg_circuit() { ], }; let (_, hash_digest) = multi_batch.raw_public_input_hash(); - println!("hash digest: {:?}", hash_digest); - let raw_public_input: Vec = hash_digest - .0 - .iter() - .map(|byte| Fr::from(*byte as u64)) - .collect(); - println!("rpi"); - for e in raw_public_input.iter() { - println!("{:?}", e) - } - let multi_batch_circuit = MultiBatchCircuit:: { multi_batch_public_data: multi_batch, hash_digest, _marker: PhantomData::default(), }; + let instance = multi_batch_circuit.instance(); - let mock_prover = - MockProver::::run(LOG_DEGREE, &multi_batch_circuit, vec![raw_public_input]).unwrap(); + let mock_prover = MockProver::::run(LOG_DEGREE, &multi_batch_circuit, instance).unwrap(); mock_prover.assert_satisfied_par() } From e76e42415e2a403f14390511ca13bd57b88d7eb9 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 31 May 2023 10:11:23 -0400 Subject: [PATCH 08/41] [refactor] pi aggregation circuit --- .gitignore | 3 +- Cargo.lock | 52 ++- Cargo.toml | 3 +- aggregator/Cargo.toml | 27 ++ aggregator/src/lib.rs | 6 + aggregator/src/public_input_aggregation.rs | 86 ++++ .../src/public_input_aggregation/batch.rs | 57 +++ .../src/public_input_aggregation/chunk.rs | 60 +++ .../src/public_input_aggregation/circuit.rs | 176 ++++++++ .../public_input_aggregation/circuit_ext.rs | 11 + .../src/public_input_aggregation/config.rs | 342 ++++++++++++++++ .../public_input_aggregation/sub_circuit.rs | 81 ++++ .../src/public_input_aggregation/tests.rs | 51 +++ aggregator/src/util.rs | 74 ++++ zkevm-circuits/src/keccak_circuit.rs | 11 +- .../src/keccak_circuit/cell_manager.rs | 9 +- .../src/keccak_circuit/keccak_packed_multi.rs | 6 +- zkevm-circuits/src/lib.rs | 1 - zkevm-circuits/src/pi_agg_circuit.rs | 22 - zkevm-circuits/src/pi_agg_circuit/chunk.rs | 81 ---- .../src/pi_agg_circuit/multi_batch.rs | 88 ---- .../src/pi_agg_circuit/multi_batch_circuit.rs | 386 ------------------ .../pi_agg_circuit/multi_batch_sub_circuit.rs | 111 ----- zkevm-circuits/src/pi_agg_circuit/tests.rs | 63 --- 24 files changed, 1041 insertions(+), 766 deletions(-) create mode 100644 aggregator/Cargo.toml create mode 100644 aggregator/src/lib.rs create mode 100644 aggregator/src/public_input_aggregation.rs create mode 100644 aggregator/src/public_input_aggregation/batch.rs create mode 100644 aggregator/src/public_input_aggregation/chunk.rs create mode 100644 aggregator/src/public_input_aggregation/circuit.rs create mode 100644 aggregator/src/public_input_aggregation/circuit_ext.rs create mode 100644 aggregator/src/public_input_aggregation/config.rs create mode 100644 aggregator/src/public_input_aggregation/sub_circuit.rs create mode 100644 aggregator/src/public_input_aggregation/tests.rs create mode 100644 aggregator/src/util.rs delete mode 100644 zkevm-circuits/src/pi_agg_circuit.rs delete mode 100644 zkevm-circuits/src/pi_agg_circuit/chunk.rs delete mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_batch.rs delete mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs delete mode 100644 zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs delete mode 100644 zkevm-circuits/src/pi_agg_circuit/tests.rs diff --git a/.gitignore b/.gitignore index 47b9644c3b..8de98ea8ed 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ *.log *.json *.sh -*.txt \ No newline at end of file +*.txt +*.srs \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ad42776a8b..b5b5c711b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,23 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "aggregator" +version = "0.1.0" +dependencies = [ + "ark-std 0.4.0", + "env_logger 0.10.0", + "eth-types", + "ethers-core", + "halo2-base", + "halo2_proofs", + "log", + "rand", + "snark-verifier", + "snark-verifier-sdk", + "zkevm-circuits", +] + [[package]] name = "ahash" version = "0.7.6" @@ -76,6 +93,16 @@ dependencies = [ "rand", ] +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + [[package]] name = "array-init" version = "2.1.0" @@ -535,7 +562,7 @@ dependencies = [ name = "circuit-benchmarks" version = "0.1.0" dependencies = [ - "ark-std", + "ark-std 0.3.0", "bus-mapping", "env_logger 0.9.3", "eth-types", @@ -2089,6 +2116,7 @@ dependencies = [ "ff", "halo2_proofs", "itertools", + "jemallocator", "num-bigint", "num-integer", "num-traits", @@ -2136,7 +2164,7 @@ name = "halo2_proofs" version = "0.2.0" source = "git+https://github.com/scroll-tech/halo2.git?branch=v0.4#3d40ae4968759ac4516c5f9c45ad20140e2d35d5" dependencies = [ - "ark-std", + "ark-std 0.3.0", "blake2b_simd", "cfg-if 0.1.10", "crossbeam", @@ -2571,6 +2599,26 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "jemalloc-sys" +version = "0.5.3+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9bd5d616ea7ed58b571b2e209a65759664d7fb021a0819d7a790afc67e47ca1" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c2514137880c52b0b4822b563fadd38257c1f380858addb74a400889696ea6" +dependencies = [ + "jemalloc-sys", + "libc", +] + [[package]] name = "jpeg-decoder" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 1919a64045..aa02eb67b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,8 @@ members = [ "eth-types", "external-tracer", "mock", - "testool" + "testool", + "aggregator" ] [patch."https://github.com/privacy-scaling-explorations/halo2.git"] diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml new file mode 100644 index 0000000000..ccacc1f530 --- /dev/null +++ b/aggregator/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "aggregator" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +eth-types = { path = "../eth-types" } +zkevm-circuits = { path = "../zkevm-circuits" } +ethers-core = "0.17.0" +rand = "0.8" +ark-std = "0.4.0" +log = "0.4" +env_logger = "0.10.0" + +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } +snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } + +[dev-dependencies] +halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "halo2-ecc-snark-verifier-0323", default-features=false, features=["halo2-pse","display"] } +snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } + + +[features] +default = [] +# default = [ "ark-std/print-trace" ] \ No newline at end of file diff --git a/aggregator/src/lib.rs b/aggregator/src/lib.rs new file mode 100644 index 0000000000..9c7d71d64a --- /dev/null +++ b/aggregator/src/lib.rs @@ -0,0 +1,6 @@ +/// public input aggregation +mod public_input_aggregation; +/// utilities +mod util; + +pub use public_input_aggregation::*; diff --git a/aggregator/src/public_input_aggregation.rs b/aggregator/src/public_input_aggregation.rs new file mode 100644 index 0000000000..1075eda5c2 --- /dev/null +++ b/aggregator/src/public_input_aggregation.rs @@ -0,0 +1,86 @@ +//! This module implements circuits that aggregates public inputs of many chunks into a single +//! one. +//! +//! # Spec +//! +//! A chunk is a list of continuous blocks. It consists of 4 hashes: +//! - state root before this chunk +//! - state root after this chunk +//! - the withdraw root of this chunk +//! - the data hash of this chunk +//! Those 4 hashes are obtained from the caller. +//! +//! A chunk's public input hash is then derived from the above 4 attributes via +//! - chunk_pi_hash := keccak(chain_id || prev_state_root || post_state_root || withdraw_root +//! || chunk_data_hash) +//! +//! A batch is a list of continuous chunks. It consists of 2 hashes +//! - batch_data_hash := keccak(chunk_0.data_hash || ... || +//! chunk_k-1.data_hash) +//! +//! - batch_pi_hash := keccak(chain_id || chunk_0.prev_state_root || +//! chunk_k-1.post_state_root || chunk_k-1.withdraw_root || batch_data_hash) +//! +//! Note that chain_id is used for all public input hashes. But not for any data hashes. +//! +//! # Circuit +//! +//! A BatchHashCircuit asserts that the batch is well-formed. +//! +//! ## Public Input +//! The public inputs of the circuit (132 Field elements) is constructed as +//! - first_chunk_prev_state_root: 32 Field elements +//! - last_chunk_post_state_root: 32 Field elements +//! - last_chunk_withdraw_root: 32 Field elements +//! - batch_public_input_hash: 32 Field elements +//! - chain_id: 4 Field elements +//! +//! ## Constraints +//! The circuit attests the following statements: +//! +//! 1. all hashes are computed correctly +//! 2. the relations between hash preimages and digests are satisfied +//! - batch_data_hash is part of the input to compute batch_pi_hash +//! - batch_pi_hash used same roots as chunk_pi_hash +//! - same data_hash is used to compute batch_data_hash and chunk_pi_hash for all chunks +//! - chunks are continuous: they are linked via the state roots +//! - all hashes uses a same chain_id +//! 3. the hash data matches the circuit's public input (132 field elements) above +//! +//! # Example +//! +//! See tests::test_pi_aggregation_circuit + +// This module implements `Chunk` related data types. +// A chunk is a list of blocks. +mod chunk; +// This module implements `Batch` related data types. +// A batch is a list of chunk. +mod batch; +// Circuit implementation of `BatchHashCircuit`. +mod circuit; +// SubCircuit implementation of `BatchHashCircuit`. +mod sub_circuit; +// CircuitExt implementation of `BatchHashCircuit`. +mod circuit_ext; +// Circuit and SubCircuit configurations +mod config; + +pub use batch::BatchHash; +pub use chunk::ChunkHash; +pub use circuit::{BatchHashCircuit, BatchHashCircuitPublicInput}; +pub use config::{BatchCircuitConfig, BatchCircuitConfigArgs}; + +// TODO(ZZ): update to the right degree +pub(crate) const LOG_DEGREE: u32 = 19; + +// Each round requires (NUM_ROUNDS+1) * DEFAULT_KECCAK_ROWS = 300 rows. +// This library is hard coded for this parameter. +// Modifying the following parameters may result into bugs. +// Adopted from keccak circuit +pub(crate) const DEFAULT_KECCAK_ROWS: usize = 12; +// Adopted from keccak circuit +pub(crate) const NUM_ROUNDS: usize = 24; + +#[cfg(test)] +mod tests; diff --git a/aggregator/src/public_input_aggregation/batch.rs b/aggregator/src/public_input_aggregation/batch.rs new file mode 100644 index 0000000000..85d7f4a293 --- /dev/null +++ b/aggregator/src/public_input_aggregation/batch.rs @@ -0,0 +1,57 @@ +use eth_types::H256; +use ethers_core::utils::keccak256; + +use super::chunk::ChunkHash; + +#[derive(Default, Debug, Clone)] +/// A batch is a set of continuous chunks. +/// A BatchHash consists of 2 hashes. +pub struct BatchHash { + pub(crate) data_hash: H256, + pub(crate) public_input_hash: H256, +} + +impl BatchHash { + /// Build Batch hash from a list of chunks + pub(crate) fn construct(chunk_hashes: &[ChunkHash]) -> Self { + // sanity: the chunks are continuous + for i in 0..chunk_hashes.len() - 1 { + assert_eq!( + chunk_hashes[i].post_state_root, + chunk_hashes[i + 1].prev_state_root, + ); + assert_eq!(chunk_hashes[i].chain_id, chunk_hashes[i + 1].chain_id,) + } + + // batch's data hash is build as + // keccak( chunk[0].data_hash || ... || chunk[k-1].data_hash) + let preimage = chunk_hashes + .iter() + .flat_map(|chunk_hash| chunk_hash.data_hash.0.iter()) + .cloned() + .collect::>(); + let data_hash = keccak256(preimage); + + // 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 ) + let preimage = [ + chunk_hashes[0].chain_id.to_le_bytes().as_ref(), + chunk_hashes[0].prev_state_root.as_bytes(), + chunk_hashes.last().unwrap().post_state_root.as_bytes(), + chunk_hashes.last().unwrap().withdraw_root.as_bytes(), + data_hash.as_slice(), + ] + .concat(); + let public_input_hash = keccak256(preimage); + + Self { + data_hash: data_hash.into(), + public_input_hash: public_input_hash.into(), + } + } +} diff --git a/aggregator/src/public_input_aggregation/chunk.rs b/aggregator/src/public_input_aggregation/chunk.rs new file mode 100644 index 0000000000..6d95b21295 --- /dev/null +++ b/aggregator/src/public_input_aggregation/chunk.rs @@ -0,0 +1,60 @@ +//! This module implements `Chunk` related data types. +//! A chunk is a list of blocks. +use eth_types::H256; +use ethers_core::utils::keccak256; + +#[derive(Default, Debug, Clone)] +/// A chunk is a set of continuous blocks. +/// A ChunkHash consists of 4 hashes, representing the changes incurred by this chunk of blocks: +/// - state root before this chunk +/// - state root after this chunk +/// - the withdraw root of this chunk +/// - the data hash of this chunk +pub struct ChunkHash { + /// Chain identifier + pub(crate) chain_id: u32, + /// state root before this chunk + pub(crate) prev_state_root: H256, + /// state root after this chunk + pub(crate) post_state_root: H256, + /// the withdraw root of this chunk + pub(crate) withdraw_root: H256, + /// the data hash of this chunk + pub(crate) data_hash: H256, +} + +impl ChunkHash { + /// Sample a chunk hash from random (for testing) + #[cfg(test)] + pub(crate) fn mock_chunk_hash(r: &mut R) -> Self { + let mut prev_state_root = [0u8; 32]; + r.fill_bytes(&mut prev_state_root); + let mut post_state_root = [0u8; 32]; + r.fill_bytes(&mut post_state_root); + let mut withdraw_root = [0u8; 32]; + r.fill_bytes(&mut withdraw_root); + let mut data_hash = [0u8; 32]; + r.fill_bytes(&mut data_hash); + 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(), + } + } + + /// Public input hash for a given chunk is defined as + /// keccak( chain id || prev state root || post state root || withdraw root || data hash ) + pub fn public_input_hash(&self) -> H256 { + let preimage = [ + self.chain_id.to_le_bytes().as_ref(), + self.prev_state_root.as_bytes(), + self.post_state_root.as_bytes(), + self.withdraw_root.as_bytes(), + self.data_hash.as_bytes(), + ] + .concat(); + keccak256::<&[u8]>(preimage.as_ref()).into() + } +} diff --git a/aggregator/src/public_input_aggregation/circuit.rs b/aggregator/src/public_input_aggregation/circuit.rs new file mode 100644 index 0000000000..9c8ebd5755 --- /dev/null +++ b/aggregator/src/public_input_aggregation/circuit.rs @@ -0,0 +1,176 @@ +use std::marker::PhantomData; + +use ark_std::{end_timer, start_timer}; +use eth_types::{Field, H256}; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Circuit, ConstraintSystem, Error}, +}; + +use zkevm_circuits::util::{Challenges, SubCircuitConfig}; + +use super::{ + batch::BatchHash, + chunk::ChunkHash, + config::{BatchCircuitConfig, BatchCircuitConfigArgs}, +}; + +/// BatchCircuit struct. +/// +/// Contains public inputs and witnesses that are needed to +/// generate the circuit. +#[derive(Clone, Debug, Default)] +pub struct BatchHashCircuit { + pub(crate) chain_id: u32, + pub(crate) chunks: Vec, + pub(crate) batch: BatchHash, + _phantom: PhantomData, +} + +/// Public input to a batch circuit. +/// In raw format. I.e., before converting to field elements. +pub struct BatchHashCircuitPublicInput { + pub(crate) chain_id: u32, + pub(crate) first_chunk_prev_state_root: H256, + pub(crate) last_chunk_post_state_root: H256, + pub(crate) last_chunk_withdraw_root: H256, + pub(crate) batch_public_input_hash: H256, +} + +impl BatchHashCircuit { + /// Sample a batch hash circuit from random (for testing) + #[cfg(test)] + pub(crate) fn mock_batch_hash_circuit(r: &mut R, size: usize) -> Self { + let mut chunks = (0..size) + .map(|_| ChunkHash::mock_chunk_hash(r)) + .collect::>(); + for i in 0..size - 1 { + chunks[i + 1].prev_state_root = chunks[i].post_state_root; + } + + Self::construct(&chunks) + } + + /// Build Batch hash circuit from a list of chunks + pub fn construct(chunk_hashes: &[ChunkHash]) -> Self { + let chain_id = chunk_hashes[0].chain_id; + // BatchHash::construct will check chunks are well-formed + let batch = BatchHash::construct(chunk_hashes); + Self { + chain_id, + chunks: chunk_hashes.to_vec(), + batch, + _phantom: PhantomData::default(), + } + } + + /// The public input to the BatchHashCircuit + pub fn public_input(&self) -> BatchHashCircuitPublicInput { + BatchHashCircuitPublicInput { + chain_id: self.chain_id, + first_chunk_prev_state_root: self.chunks[0].prev_state_root, + last_chunk_post_state_root: self.chunks.last().unwrap().post_state_root, + last_chunk_withdraw_root: self.chunks.last().unwrap().withdraw_root, + batch_public_input_hash: self.batch.public_input_hash, + } + } + + /// Extract all the hash inputs that will ever be used + /// orders: + /// - batch_public_input_hash + /// - batch_data_hash_preimage + /// - chunk[i].piHash for i in [0, k) + pub(crate) fn extract_hash_preimages(&self) -> Vec> { + let mut res = vec![]; + + // batchPiHash = + // keccak( + // chain_id || + // chunk[0].prev_state_root || + // chunk[k-1].post_state_root || + // chunk[k-1].withdraw_root || + // batch_data_hash ) + let batch_public_input_hash_preimage = [ + self.chain_id.to_le_bytes().as_ref(), + self.chunks[0].prev_state_root.as_bytes(), + self.chunks.last().unwrap().post_state_root.as_bytes(), + self.chunks.last().unwrap().withdraw_root.as_bytes(), + self.batch.data_hash.as_bytes(), + ] + .concat(); + res.push(batch_public_input_hash_preimage); + + // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) + let batch_data_hash_preimage = self + .chunks + .iter() + .flat_map(|x| x.data_hash.as_bytes().iter()) + .cloned() + .collect(); + res.push(batch_data_hash_preimage); + + // compute piHash for each chunk for i in [0..k) + // chunk[i].piHash = + // keccak( + // chain id || + // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot || + // chunk[i].datahash) + for chunk in self.chunks.iter() { + let chunk_pi_hash_preimage = [ + self.chain_id.to_le_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_pi_hash_preimage) + } + + res + } +} + +impl Circuit for BatchHashCircuit { + type FloorPlanner = SimpleFloorPlanner; + + type Config = (BatchCircuitConfig, Challenges); + + fn without_witnesses(&self) -> Self { + Self::default() + } + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let challenges = Challenges::construct(meta); + let challenges_exprs = challenges.exprs(meta); + let args = BatchCircuitConfigArgs { + challenges: challenges_exprs, + }; + let config = BatchCircuitConfig::new(meta, args); + (config, challenges) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let (config, challenge) = config; + let challenges = challenge.values(&layouter); + + // extract all the hashes and load them to the hash table + let timer = start_timer!(|| ("extract hash").to_string()); + let preimages = self.extract_hash_preimages(); + end_timer!(timer); + + let timer = start_timer!(|| ("load aux table").to_string()); + config + .keccak_circuit_config + .load_aux_tables(&mut layouter)?; + end_timer!(timer); + + let timer = start_timer!(|| ("assign cells").to_string()); + config.assign(&mut layouter, challenges, &preimages)?; + end_timer!(timer); + Ok(()) + } +} diff --git a/aggregator/src/public_input_aggregation/circuit_ext.rs b/aggregator/src/public_input_aggregation/circuit_ext.rs new file mode 100644 index 0000000000..a02c740eb7 --- /dev/null +++ b/aggregator/src/public_input_aggregation/circuit_ext.rs @@ -0,0 +1,11 @@ +use eth_types::Field; +use snark_verifier_sdk::CircuitExt; +use zkevm_circuits::util::SubCircuit; + +use crate::BatchHashCircuit; + +impl CircuitExt for BatchHashCircuit { + fn instances(&self) -> Vec> { + self.instance() + } +} diff --git a/aggregator/src/public_input_aggregation/config.rs b/aggregator/src/public_input_aggregation/config.rs new file mode 100644 index 0000000000..15eff4ee59 --- /dev/null +++ b/aggregator/src/public_input_aggregation/config.rs @@ -0,0 +1,342 @@ +use ark_std::{end_timer, start_timer}; +use eth_types::Field; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Value}, + plonk::{Column, ConstraintSystem, Error, Expression, Instance}, +}; +use zkevm_circuits::{ + keccak_circuit::{ + keccak_packed_multi::multi_keccak, KeccakCircuitConfig, KeccakCircuitConfigArgs, + }, + table::{KeccakTable, LookupTable}, + util::{Challenges, SubCircuitConfig}, +}; + +use crate::{ + util::{assert_equal, capacity, get_indices}, + LOG_DEGREE, +}; + +/// Config for BatchCircuit +#[derive(Clone, Debug)] +pub struct BatchCircuitConfig { + /// Instance column stores the aggregated rpi hash digest + pub(crate) hash_digest_column: Column, + + /// Keccak circuit config + pub(crate) keccak_circuit_config: KeccakCircuitConfig, +} + +/// Auxiliary arguments for BatchCircuit's Config +#[derive(Clone, Debug)] +pub struct BatchCircuitConfigArgs { + pub challenges: Challenges>, +} + +impl SubCircuitConfig for BatchCircuitConfig { + type ConfigArgs = BatchCircuitConfigArgs; + + /// Return a new BatchCircuitConfig + fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { + // hash configuration + let keccak_circuit_config = { + let keccak_table = KeccakTable::construct(meta); + + let keccak_circuit_config_args = KeccakCircuitConfigArgs { + keccak_table, + challenges: config_args.challenges, + }; + + KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + }; + + // The current code base is hardcoded for KeccakCircuit configured + // with 300 rows and 87 columns per hash call. + let columns = keccak_circuit_config.cell_manager.columns(); + + assert_eq!( + columns.len(), + 87, + "cell manager configuration does not match the hard coded setup" + ); + + // enabling equality for preimage and digest columns + meta.enable_equality(columns[6].advice); + // digest column + meta.enable_equality(columns.last().unwrap().advice); + + // Instance column stores the output of the hash + let hash_digest_column = meta.instance_column(); + // public input column + meta.enable_equality(hash_digest_column); + + BatchCircuitConfig { + hash_digest_column, + keccak_circuit_config, + } + } +} + +impl BatchCircuitConfig { + /// Input the hash input bytes, + /// assign the circuit for hash function, + /// return cells for the hash inputs and digests. + #[allow(clippy::type_complexity)] + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + challenges: Challenges>, + preimages: &[Vec], + ) -> Result< + ( + Vec>>, // input cells + Vec>>, // digest cells + ), + Error, + > { + let mut is_first_time = true; + let num_rows = 1 << LOG_DEGREE; + + let timer = start_timer!(|| ("multi keccak").to_string()); + let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; + end_timer!(timer); + + // extract the indices of the rows for which the preimage and the digest cells lie in + let (preimage_indices, digest_indices) = get_indices(preimages); + let mut preimage_indices_iter = preimage_indices.iter(); + let mut digest_indices_iter = digest_indices.iter(); + + let mut hash_input_cells = vec![]; + let mut hash_output_cells = vec![]; + + let mut cur_preimage_index = preimage_indices_iter.next(); + let mut cur_digest_index = digest_indices_iter.next(); + + layouter.assign_region( + || "assign keccak rows", + |mut region| { + if is_first_time { + is_first_time = false; + let offset = witness.len() - 1; + self.keccak_circuit_config + .set_row(&mut region, offset, &witness[offset])?; + return Ok(()); + } + // ==================================================== + // Step 1. Extract the hash cells + // ==================================================== + let mut current_hash_input_cells = vec![]; + let mut current_hash_output_cells = vec![]; + + let timer = start_timer!(|| "assign row"); + for (offset, keccak_row) in witness.iter().enumerate() { + let row = + self.keccak_circuit_config + .set_row(&mut region, offset, keccak_row)?; + + if cur_preimage_index.is_some() && *cur_preimage_index.unwrap() == offset { + current_hash_input_cells.push(row[6].clone()); + cur_preimage_index = preimage_indices_iter.next(); + } + if cur_digest_index.is_some() && *cur_digest_index.unwrap() == offset { + current_hash_output_cells.push(row.last().unwrap().clone()); + cur_digest_index = digest_indices_iter.next(); + } + + // we reset the current hash when it is finalized + // note that length == 0 indicate that the hash is a padding + // so we simply skip it + if keccak_row.is_final && keccak_row.length != 0 { + hash_input_cells.push(current_hash_input_cells); + hash_output_cells.push(current_hash_output_cells); + current_hash_input_cells = vec![]; + current_hash_output_cells = vec![]; + } + } + end_timer!(timer); + + // sanity: we have same number of hash input and output + let hash_num = hash_input_cells.len(); + let num_chunks = hash_num - 2; + assert!(hash_num == preimages.len()); + assert_eq!(hash_num, hash_output_cells.len()); + + // ==================================================== + // Step 2. Constraint the relations between hash preimages and digests + // ==================================================== + // + // 2.1 batch_data_hash digest is reused for public input hash + // + // 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 ) + for i in 0..4 { + for j in 0..8 { + // sanity check + assert_equal( + &hash_input_cells[0][i * 8 + j + 100], + &hash_output_cells[1][(3 - i) * 8 + j], + ); + region.constrain_equal( + // preimage and digest has different endianness + hash_input_cells[0][i * 8 + j + 100].cell(), + hash_output_cells[1][(3 - i) * 8 + j].cell(), + )?; + } + } + + // 2.2 batch_pi_hash used same roots as chunk_pi_hash + // + // batch_pi_hash = + // keccak( + // chain_id || + // chunk[0].prev_state_root || + // chunk[k-1].post_state_root || + // chunk[k-1].withdraw_root || + // batchData_hash ) + // + // chunk[i].piHash = + // keccak( + // chain id || + // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot + // || chunk[i].datahash) + for i in 0..32 { + // 2.2.1 chunk[0].prev_state_root + // sanity check + assert_equal(&hash_input_cells[0][i + 4], &hash_input_cells[2][i + 4]); + region.constrain_equal( + hash_input_cells[0][i + 4].cell(), + hash_input_cells[2][i + 4].cell(), + )?; + // 2.2.2 chunk[k-1].post_state_root + // sanity check + assert_equal( + &hash_input_cells[0][i + 36], + &hash_input_cells[hash_num - 1][i + 36], + ); + region.constrain_equal( + hash_input_cells[0][i + 36].cell(), + hash_input_cells[hash_num - 1][i + 36].cell(), + )?; + // 2.2.3 chunk[k-1].withdraw_root + assert_equal( + &hash_input_cells[0][i + 68], + &hash_input_cells[hash_num - 1][i + 68], + ); + region.constrain_equal( + hash_input_cells[0][i + 68].cell(), + hash_input_cells[hash_num - 1][i + 68].cell(), + )?; + } + + // 2.3 same dataHash is used for batchDataHash and chunk[i].piHash + // + // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) + // + // chunk[i].piHash = + // keccak( + // &chain id || + // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot + // || chunk[i].datahash) + for (i, chunk) in hash_input_cells[1].chunks(32).enumerate().take(num_chunks) { + for (j, cell) in chunk.iter().enumerate() { + // sanity check + assert_equal(cell, &hash_input_cells[2 + i][j + 100]); + region.constrain_equal( + cell.cell(), + hash_input_cells[2 + i][j + 100].cell(), + )?; + } + } + + // 2.4 chunks are continuous: they are linked via the state roots + for i in 0..num_chunks - 1 { + for j in 0..32 { + // sanity check + assert_equal( + &hash_input_cells[i + 3][4 + j], + &hash_input_cells[i + 2][36 + j], + ); + region.constrain_equal( + // chunk[i+1].prevStateRoot + hash_input_cells[i + 3][4 + j].cell(), + // chunk[i].postStateRoot + hash_input_cells[i + 2][36 + j].cell(), + )?; + } + } + + // 2.5 assert hashes uses a same chain id + for i in 0..num_chunks { + for j in 0..4 { + // sanity check + assert_equal(&hash_input_cells[0][j], &hash_input_cells[i + 2][j]); + region.constrain_equal( + // chunk[i+1].prevStateRoot + hash_input_cells[0][j].cell(), + // chunk[i].postStateRoot + hash_input_cells[i + 2][j].cell(), + )?; + } + } + + self.keccak_circuit_config + .keccak_table + .annotate_columns_in_region(&mut region); + self.keccak_circuit_config.annotate_circuit(&mut region); + Ok(()) + }, + )?; + + // ==================================================== + // Step 3. Constraint the hash data matches the raw public input + // ==================================================== + { + for i in 0..32 { + // first_chunk_prev_state_root + layouter.constrain_instance( + hash_input_cells[2][4 + i].cell(), + self.hash_digest_column, + i, + )?; + // last_chunk_post_state_root + layouter.constrain_instance( + hash_input_cells.last().unwrap()[36 + i].cell(), + self.hash_digest_column, + i + 32, + )?; + // last_chunk_withdraw_root + layouter.constrain_instance( + hash_input_cells.last().unwrap()[68 + i].cell(), + self.hash_digest_column, + i + 64, + )?; + } + // batch_public_input_hash + for i in 0..4 { + for j in 0..8 { + // digest in circuit has a different endianness + layouter.constrain_instance( + hash_output_cells[0][(3 - i) * 8 + j].cell(), + self.hash_digest_column, + i * 8 + j + 96, + )?; + } + } + // last 4 inputs are the chain id + for i in 0..4 { + layouter.constrain_instance( + hash_input_cells[0][i].cell(), + self.hash_digest_column, + 128 + i, + )?; + } + } + + Ok((hash_input_cells, hash_output_cells)) + } +} diff --git a/aggregator/src/public_input_aggregation/sub_circuit.rs b/aggregator/src/public_input_aggregation/sub_circuit.rs new file mode 100644 index 0000000000..e4a1475bcd --- /dev/null +++ b/aggregator/src/public_input_aggregation/sub_circuit.rs @@ -0,0 +1,81 @@ +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, Value}, + plonk::Error, +}; + +use super::{circuit::BatchHashCircuit, config::BatchCircuitConfig, LOG_DEGREE}; +use zkevm_circuits::{ + util::{Challenges, SubCircuit}, + witness::Block, +}; + +impl SubCircuit for BatchHashCircuit { + type Config = BatchCircuitConfig; + + fn new_from_block(_block: &Block) -> Self { + // we cannot instantiate a new Self from a single block + unimplemented!() + } + + /// Return the minimum number of rows required to prove the block + /// Row numbers without/with padding are both returned. + fn min_num_rows_block(_block: &Block) -> (usize, usize) { + (1 << LOG_DEGREE, 1 << LOG_DEGREE) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + let public_input = self.public_input(); + + let first_chunk_prev_state_root = public_input + .first_chunk_prev_state_root + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let last_chunk_post_state_root = public_input + .last_chunk_post_state_root + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let last_chunk_withdraw_root = public_input + .last_chunk_withdraw_root + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let batch_public_input_hash = public_input + .batch_public_input_hash + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let chain_id_bytes = public_input.chain_id.to_le_bytes(); + let chain_id = chain_id_bytes.iter().map(|x| F::from(*x as u64)); + + vec![first_chunk_prev_state_root + .chain(last_chunk_post_state_root) + .chain(last_chunk_withdraw_root) + .chain(batch_public_input_hash) + .chain(chain_id) + .collect()] + } + + /// Make the assignments to the BatchHashCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + // extract all the hashes and load them to the hash table + let preimages = self.extract_hash_preimages(); + + config.keccak_circuit_config.load_aux_tables(layouter)?; + config.assign(layouter, *challenges, &preimages)?; + + Ok(()) + } +} diff --git a/aggregator/src/public_input_aggregation/tests.rs b/aggregator/src/public_input_aggregation/tests.rs new file mode 100644 index 0000000000..6b7946885c --- /dev/null +++ b/aggregator/src/public_input_aggregation/tests.rs @@ -0,0 +1,51 @@ +use ark_std::{end_timer, start_timer, test_rng}; +use halo2_base::utils::fs::gen_srs; +use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use snark_verifier_sdk::{ + gen_pk, + halo2::{gen_snark_shplonk, verify_snark_shplonk}, +}; +use zkevm_circuits::util::SubCircuit; + +use crate::{BatchHashCircuit, LOG_DEGREE}; + +#[test] +fn test_pi_aggregation_mock_prover() { + env_logger::init(); + + let mut rng = test_rng(); + let chunks_per_batch = 8; + + let circuit = BatchHashCircuit::::mock_batch_hash_circuit(&mut rng, chunks_per_batch); + let instance = circuit.instance(); + + let mock_prover = MockProver::::run(LOG_DEGREE, &circuit, instance).unwrap(); + + mock_prover.assert_satisfied_par() +} + +#[test] +fn test_pi_aggregation_real_prover() { + let mut rng = test_rng(); + let param = gen_srs(LOG_DEGREE); + + let chunks_per_batch = 16; + + let circuit = BatchHashCircuit::::mock_batch_hash_circuit(&mut rng, chunks_per_batch); + + let timer = start_timer!(|| format!("key generation for k = {}", LOG_DEGREE)); + let pk = gen_pk(¶m, &circuit, None); + end_timer!(timer); + + let timer = start_timer!(|| "proving"); + let snark = gen_snark_shplonk(¶m, &pk, circuit, &mut rng, None::); + end_timer!(timer); + + let timer = start_timer!(|| "verifying"); + assert!(verify_snark_shplonk::>( + ¶m, + snark, + pk.get_vk() + )); + end_timer!(timer); +} diff --git a/aggregator/src/util.rs b/aggregator/src/util.rs new file mode 100644 index 0000000000..940fe17c64 --- /dev/null +++ b/aggregator/src/util.rs @@ -0,0 +1,74 @@ +use eth_types::Field; +use halo2_proofs::circuit::AssignedCell; + +use crate::{DEFAULT_KECCAK_ROWS, NUM_ROUNDS}; + +use std::env::var; + +pub(crate) fn capacity(num_rows: usize) -> Option { + if num_rows > 0 { + // Subtract two for unusable rows + Some(num_rows / ((NUM_ROUNDS + 1) * get_num_rows_per_round()) - 2) + } else { + None + } +} + +pub(crate) fn get_num_rows_per_round() -> usize { + var("KECCAK_ROWS") + .unwrap_or_else(|_| format!("{DEFAULT_KECCAK_ROWS}")) + .parse() + .expect("Cannot parse KECCAK_ROWS env var as usize") +} + +/// Return +/// - the indices of the rows that contain the input preimages +/// - the indices of the rows that contain the output digest +pub(crate) fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { + let mut preimage_indices = vec![]; + let mut digest_indices = vec![]; + let mut round_ctr = 0; + + for preimage in preimages.iter() { + let num_rounds = 1 + preimage.len() / 136; + for (i, round) in preimage.chunks(136).enumerate() { + // indices for preimages + for (j, _chunk) in round.chunks(8).into_iter().enumerate() { + for k in 0..8 { + preimage_indices.push(round_ctr * 300 + j * 12 + k + 12) + } + } + // indices for digests + if i == num_rounds - 1 { + for j in 0..4 { + for k in 0..8 { + digest_indices.push(round_ctr * 300 + j * 12 + k + 252) + } + } + } + round_ctr += 1; + } + } + + debug_assert!(is_ascending(&preimage_indices)); + debug_assert!(is_ascending(&digest_indices)); + + (preimage_indices, digest_indices) +} + +#[inline] +// assert two cells have same value +// (NOT constraining equality in circuit) +pub(crate) fn assert_equal(a: &AssignedCell, b: &AssignedCell) { + let mut t1 = F::default(); + let mut t2 = F::default(); + a.value().map(|f| t1 = *f); + b.value().map(|f| t2 = *f); + assert_eq!(t1, t2) +} + +#[inline] +// assert that the slice is ascending +fn is_ascending(a: &[usize]) -> bool { + a.windows(2).all(|w| w[0] <= w[1]) +} diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index 9c79e99620..0dea057d41 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -55,7 +55,8 @@ pub struct KeccakCircuitConfig { q_padding_last: Column, /// The columns for other circuits to lookup Keccak hash results pub keccak_table: KeccakTable, - pub(crate) cell_manager: CellManager, + /// The cell manager that stores/allocates the advice columns + pub cell_manager: CellManager, round_cst: Column, normalize_3: [TableColumn; 2], normalize_4: [TableColumn; 2], @@ -893,7 +894,7 @@ impl KeccakCircuitConfig { } /// Set a keccak row; return the cells allocated for the row. - pub(crate) fn set_row( + pub fn set_row( &self, region: &mut Region<'_, F>, offset: usize, @@ -959,7 +960,8 @@ impl KeccakCircuitConfig { Ok(res) } - pub(crate) fn load_aux_tables(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + /// Load the auxiliary tables for keccak circuit + pub fn load_aux_tables(&self, layouter: &mut impl Layouter) -> Result<(), Error> { load_normalize_table(layouter, "normalize_6", &self.normalize_6, 6u64)?; load_normalize_table(layouter, "normalize_4", &self.normalize_4, 4u64)?; load_normalize_table(layouter, "normalize_3", &self.normalize_3, 3u64)?; @@ -973,7 +975,8 @@ impl KeccakCircuitConfig { load_pack_table(layouter, &self.pack_table) } - pub(crate) fn annotate_circuit(&self, region: &mut Region) { + /// Annotate the circuit + pub fn annotate_circuit(&self, region: &mut Region) { //region.name_column(|| "KECCAK_q_enable", self.q_enable); region.name_column(|| "KECCAK_q_first", self.q_first); region.name_column(|| "KECCAK_q_round", self.q_round); diff --git a/zkevm-circuits/src/keccak_circuit/cell_manager.rs b/zkevm-circuits/src/keccak_circuit/cell_manager.rs index c0c671bddb..a2fdb30e57 100644 --- a/zkevm-circuits/src/keccak_circuit/cell_manager.rs +++ b/zkevm-circuits/src/keccak_circuit/cell_manager.rs @@ -89,14 +89,14 @@ impl Expr for &Cell { /// CellColumn #[derive(Clone, Debug)] -pub(crate) struct CellColumn { - pub(crate) advice: Column, +pub struct CellColumn { + pub advice: Column, pub(crate) expr: Expression, } /// CellManager #[derive(Clone, Debug)] -pub(crate) struct CellManager { +pub struct CellManager { height: usize, columns: Vec>, rows: Vec, @@ -153,7 +153,8 @@ impl CellManager { self.rows.iter().cloned().max().unwrap() } - pub(crate) fn columns(&self) -> &[CellColumn] { + /// Expose the columns used by the cell manager by reference. + pub fn columns(&self) -> &[CellColumn] { &self.columns } diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index 3991fd296d..4b7ccb2164 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -67,9 +67,11 @@ pub struct KeccakRow { pub(crate) q_padding: bool, pub(crate) q_padding_last: bool, pub(crate) round_cst: F, - pub(crate) is_final: bool, + /// If this row is the final row for the current keccak round + pub is_final: bool, pub(crate) cell_values: Vec, - pub(crate) length: usize, + /// The input length of the hash function + pub length: usize, pub(crate) data_rlc: Value, pub(crate) hash_rlc: Value, } diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 8e8f17ac0b..a9c6f80ff3 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -30,7 +30,6 @@ pub mod poseidon_circuit; pub mod rlp_circuit; // we don't use this for aggregation //pub mod root_circuit; -pub mod pi_agg_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; diff --git a/zkevm-circuits/src/pi_agg_circuit.rs b/zkevm-circuits/src/pi_agg_circuit.rs deleted file mode 100644 index 8efc91dec1..0000000000 --- a/zkevm-circuits/src/pi_agg_circuit.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! This module implements circuits that aggregates public inputs of many blocks/txs into a single -//! one. - -// This module implements `Chunk` related data types. -// A chunk is a list of blocks. -mod chunk; -// This module implements `MultiBatch` related data types. -// A multi_batch is a list of chunk. -mod multi_batch; -// Circuit implementation of `MultiBatch` public input hashes. -mod multi_batch_circuit; -// Subcircuit implementation of `MultiBatch` public input hashes. -mod multi_batch_sub_circuit; - -// TODO(ZZ): update to the right degree -pub(crate) const LOG_DEGREE: u32 = 19; - -// TODO(ZZ): update to the right size -pub(crate) const MAX_TXS: usize = 20; - -#[cfg(test)] -mod tests; diff --git a/zkevm-circuits/src/pi_agg_circuit/chunk.rs b/zkevm-circuits/src/pi_agg_circuit/chunk.rs deleted file mode 100644 index eb28de803d..0000000000 --- a/zkevm-circuits/src/pi_agg_circuit/chunk.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! This module implements `Chunk` related data types. -//! A chunk is a list of blocks. -use crate::pi_circuit::PublicData; -use eth_types::H256; -use ethers_core::utils::keccak256; - -#[derive(Default, Debug, Clone)] -/// ChunkPublicData is a list of PublicData, each corresponds to a block -/// with max_txn transactions. -pub struct ChunkPublicData { - pub(crate) public_data_vec: Vec, -} - -impl ChunkPublicData { - /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. - pub(crate) fn raw_data_hash_bytes(&self) -> Vec { - let (_, digest) = self.raw_data_hash(); - digest.as_bytes().to_vec() - } - - /// Compute the raw_data_hash, return both the preimage and the hash - pub(crate) fn raw_data_hash(&self) -> (Vec, H256) { - let mut to_be_hashed = vec![]; - - self.public_data_vec.iter().for_each(|public_data| { - to_be_hashed.extend_from_slice( - // extract all the data from transactions - public_data.get_pi(MAX_TXS).as_bytes(), - ) - }); - // data hash is the keccak hash of concatenation of all data fields - let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); - (to_be_hashed, digest) - } - - /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. - pub(crate) fn raw_public_input_hash_bytes(&self) -> Vec { - let (_, digest) = self.raw_public_input_hash(); - digest.as_bytes().to_vec() - } - - /// Compute the raw_public_input_hash, return both the preimage and the hash - pub(crate) fn raw_public_input_hash(&self) -> (Vec, H256) { - let mut to_be_hashed = vec![]; - // extract the prev state root of the first block - // and the next state root of the last block - to_be_hashed.extend_from_slice(self.public_data_vec[0].prev_state_root.as_bytes()); - to_be_hashed.extend_from_slice( - self.public_data_vec - .last() - .unwrap() - .prev_state_root - .as_bytes(), - ); - // withdraw root - // - // FIXME: for each block we have a withdraw_trie_root - // This is different from the spec. - // Double check this. - self.public_data_vec.iter().for_each(|public_data| { - to_be_hashed.extend_from_slice(public_data.withdraw_trie_root.as_bytes()) - }); - - let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); - (to_be_hashed, digest) - } - - /// Extract all the hash inputs and outputs that will ever be computed - /// for this given multi_batch - /// Ordered as: - /// - the hash preimage/digest pair for the data_hash - /// - the hash preimage/digest pair for the public_input_hash - pub(crate) fn extract_hashes(&self) -> (Vec>, Vec) { - let data_hash_pair = self.raw_data_hash(); - let pi_hash_pair = self.raw_public_input_hash(); - ( - vec![data_hash_pair.0, pi_hash_pair.0], - vec![data_hash_pair.1, pi_hash_pair.1], - ) - } -} diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs deleted file mode 100644 index 62784725ed..0000000000 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! This module implements `MultiBatch` related data types. -//! A multi_batch is a list of chunk. - -use super::chunk::ChunkPublicData; - -use eth_types::H256; -use ethers_core::utils::keccak256; - -#[derive(Default, Debug, Clone)] -/// MultiBatchPublicData is a list of ChunkPublicData, each corresponds to a chunk -/// with max_txn transactions. -pub struct MultiBatchPublicData { - pub(crate) public_data_chunks: Vec>, -} - -impl MultiBatchPublicData { - /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. - pub(crate) fn raw_data_hash_bytes(&self) -> Vec { - let (_, digest) = self.raw_data_hash(); - digest.as_bytes().to_vec() - } - - /// Compute the raw_data_hash_bytes bytes from the verifier's perspective. - pub(crate) fn raw_data_hash(&self) -> (Vec, H256) { - let mut to_be_hashed = vec![]; - - self.public_data_chunks - .iter() - .for_each(|public_data_chunk| { - to_be_hashed.extend_from_slice( - // extract all the data from each chunk - public_data_chunk.raw_data_hash_bytes().as_ref(), - ) - }); - // data hash is the keccak hash of concatenation of all data fields - let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); - (to_be_hashed, digest) - } - - /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. - pub(crate) fn raw_public_input_hash_bytes(&self) -> Vec { - let (_, digest) = self.raw_public_input_hash(); - digest.as_bytes().to_vec() - } - - /// Compute the raw_public_inputs_bytes bytes from the verifier's perspective. - pub(crate) fn raw_public_input_hash(&self) -> (Vec, H256) { - let mut to_be_hashed = vec![]; - self.public_data_chunks - .iter() - .for_each(|public_data_chunk| { - to_be_hashed.extend_from_slice( - // extract all the data from each chunk - public_data_chunk.raw_public_input_hash_bytes().as_ref(), - ) - }); - let digest = keccak256::<&[u8]>(to_be_hashed.as_ref()).into(); - (to_be_hashed, digest) - } - - /// Extract all the hash inputs and outputs that will ever be computed - /// for this given multi_batch - /// Ordered as: - /// - the hash preimage/digest pairs for all blocks - /// - the hash preimage/digest pair for the data_hash - /// - the hash preimage/digest pair for the public_input_hash - /// Returns the - /// - hash input - /// - hash output - pub(crate) fn extract_hashes(&self) -> (Vec>, Vec) { - let mut preimages = vec![]; - let mut digests = vec![]; - self.public_data_chunks - .iter() - .for_each(|public_data_chunk| { - let (preimage, digest) = public_data_chunk.extract_hashes(); - preimages.extend_from_slice(preimage.as_ref()); - digests.extend_from_slice(digest.as_ref()) - }); - let data_hash_pair = self.raw_data_hash(); - let pi_hash_pair = self.raw_public_input_hash(); - - preimages.extend_from_slice(&[data_hash_pair.0, pi_hash_pair.0]); - digests.extend_from_slice(&[data_hash_pair.1, pi_hash_pair.1]); - - (preimages, digests) - } -} diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs deleted file mode 100644 index 67d8f39636..0000000000 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch_circuit.rs +++ /dev/null @@ -1,386 +0,0 @@ -//! Circuit implementation of `MultiBatch` public input hashes. -//! -//! -//! IMPORTANT: -//! -//! The current implementation is KeccakCircuit parameter dependent. -//! The current code base is hardcoded for KeccakCircuit configured -//! with 300 rows and 87 columns per hash call. The 7-th column is used -//! for hash preimages and the 87-th column is for hash digest. -//! -//! This is because we need to extract the preimage and digest cells, -//! and argue some relationship between those cells. If the cell manager -//! configuration is changed, the cells indices will be different, -//! and the relationship becomes unsatisfied. -//! -//! For now we stick to this hard coded design for simplicity. -//! A flexible design is a future work. - -use super::{ - multi_batch::MultiBatchPublicData, multi_batch_sub_circuit::MultiBatchCircuitConfigArgs, -}; -use crate::{ - keccak_circuit::{ - keccak_packed_multi::{get_num_rows_per_round, multi_keccak}, - param::NUM_ROUNDS, - KeccakCircuitConfig, - }, - table::LookupTable, - util::{Challenges, SubCircuitConfig}, -}; - -use eth_types::{Field, H256}; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value}, - plonk::{Circuit, Column, ConstraintSystem, Error, Instance}, -}; -use std::{marker::PhantomData, vec}; - -/// Circuit inputs for MultiBatch -#[derive(Clone, Debug)] -pub struct MultiBatchCircuit { - pub(crate) multi_batch_public_data: MultiBatchPublicData, - /// public input: the hash digest obtained via - /// `MultiBatchPublicData::raw_public_input_bytes` - pub(crate) hash_digest: H256, - pub(crate) _marker: PhantomData, -} - -impl MultiBatchCircuit { - fn raw_public_input_bytes(&self) -> Vec { - self.multi_batch_public_data.raw_public_input_hash_bytes() - } -} - -/// Config for MultiBatchCircuit -#[derive(Clone, Debug)] -pub struct MultiBatchCircuitConfig { - /// Log of the degree of the circuit - pub(crate) log_degree: usize, - - /// Max number of supported transactions - pub(crate) max_txs: usize, - - /// Instance column stores the aggregated rpi hash digest - pub(crate) hash_digest_column: Column, - - /// Keccak circuit config - pub(crate) keccak_circuit_config: KeccakCircuitConfig, - - pub(crate) _marker: PhantomData, -} - -impl MultiBatchCircuitConfig { - /// Input the hash input bytes, - /// assign the circuit for hash function, - /// return cells for the hash inputs and digests. - #[allow(clippy::type_complexity)] - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - challenges: Challenges>, - preimages: &[Vec], - ) -> Result< - ( - Vec>>, // input cells - Vec>>, // digest cells - ), - Error, - > { - let mut is_first_time = true; - let num_rows = 1 << 18; - - let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; - - // extract the indices of the rows for which the preimage and the digest cells lie in - let (preimage_indices, digest_indices) = get_indices(preimages); - - log::info!("preimage indices: {:?}", preimage_indices); - log::info!("digest indices: {:?}", digest_indices); - - let mut hash_input_cells = vec![]; - let mut hash_output_cells = vec![]; - - layouter.assign_region( - || "assign keccak rows", - |mut region| { - if is_first_time { - is_first_time = false; - let offset = witness.len() - 1; - self.keccak_circuit_config - .set_row(&mut region, offset, &witness[offset])?; - return Ok(()); - } - // ==================================================== - // Step 1. Extract the hash cells - // ==================================================== - let mut current_hash_input_cells = vec![]; - let mut current_hash_output_cells = vec![]; - - for (offset, keccak_row) in witness.iter().enumerate() { - let row = - self.keccak_circuit_config - .set_row(&mut region, offset, keccak_row)?; - - if preimage_indices.contains(&offset) { - current_hash_input_cells.push(row[6].clone()); - } - if digest_indices.contains(&offset) { - current_hash_output_cells.push(row.last().unwrap().clone()); - } - - // we reset the current hash when it is finalized - // note that length == 0 indicate that the hash is a padding - // so we simply skip it - if keccak_row.is_final && keccak_row.length != 0 { - hash_input_cells.push(current_hash_input_cells); - hash_output_cells.push(current_hash_output_cells); - current_hash_input_cells = vec![]; - current_hash_output_cells = vec![]; - } - } - - // sanity: we have same number of hash input and output - let hash_num = hash_input_cells.len(); - assert!(hash_num % 2 == 0); - assert_eq!(hash_num, hash_output_cells.len()); - - // ==================================================== - // Step 2. Constraint the hash digest is reused later for hash preimages - // ==================================================== - { - // 2.1 assert the data hash's input is well-formed - { - let mut final_data_hash_inputs = vec![]; - for i in 0..hash_num / 2 - 1 { - final_data_hash_inputs - .extend_from_slice(hash_output_cells[i * 2].as_ref()); - } - assert_eq!( - final_data_hash_inputs.len(), - hash_input_cells[hash_num - 2].len() - ); - let chunks = final_data_hash_inputs.len() / 8; - for i in 0..chunks { - for j in 0..8 { - // sanity: the values in cells match - assert_equal( - &final_data_hash_inputs[i * 8 + j], - &hash_input_cells[hash_num - 2][(chunks - i - 1) * 8 + j], - ); - // preimage and digest has different endianness - // (great design decision btw /s) - region.constrain_equal( - final_data_hash_inputs[i * 8 + j].cell(), - hash_input_cells[hash_num - 2][(chunks - i - 1) * 8 + j].cell(), - )?; - } - } - } - - // 2.2 assert the rpi hash's input is well-formed - { - let mut final_rpi_hash_inputs = vec![]; - for i in 0..hash_num / 2 - 1 { - final_rpi_hash_inputs - .extend_from_slice(hash_output_cells[i * 2 + 1].as_ref()); - } - assert_eq!( - final_rpi_hash_inputs.len(), - hash_input_cells[hash_num - 1].len() - ); - let chunks = final_rpi_hash_inputs.len() / 8; - for i in 0..chunks { - for j in 0..8 { - // sanity: the values in cells match - assert_equal( - &final_rpi_hash_inputs[i * 8 + j], - &hash_input_cells[hash_num - 1][(chunks - i - 1) * 8 + j], - ); - // preimage and digest has different endianness - // (great design decision btw /s) - region.constrain_equal( - final_rpi_hash_inputs[i * 8 + j].cell(), - hash_input_cells[hash_num - 1][(chunks - i - 1) * 8 + j].cell(), - )?; - } - } - } - } - - self.keccak_circuit_config - .keccak_table - .annotate_columns_in_region(&mut region); - self.keccak_circuit_config.annotate_circuit(&mut region); - Ok(()) - }, - )?; - - // ==================================================== - // Step 3. Constraint the final hash output matches the raw public input - // ==================================================== - { - let final_digest_cells = hash_output_cells.last().unwrap(); - for i in 0..4 { - for j in 0..8 { - // digest in circuit has a different endianness - layouter.constrain_instance( - final_digest_cells[(3 - i) * 8 + j].cell(), - self.hash_digest_column, - i * 8 + j, - )?; - } - } - } - - // debugging info - // - // print!("input: "); - // for e in hash_input_cells.iter() { - // print!("{} ", e.len()); - // } - // println!(); - // - // print!("digests: "); - // for e in hash_output_cells.iter() { - // print!("{} ", e.len()); - // } - // println!(); - - Ok((hash_input_cells, hash_output_cells)) - } -} - -impl Circuit for MultiBatchCircuit { - type FloorPlanner = SimpleFloorPlanner; - - type Config = (MultiBatchCircuitConfig, Challenges); - - fn without_witnesses(&self) -> Self { - Self { - multi_batch_public_data: MultiBatchPublicData::default(), - hash_digest: H256([0u8; 32]), //(F::default(), F::default()), - _marker: PhantomData::default(), - } - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let challenges = Challenges::construct(meta); - let challenges_exprs = challenges.exprs(meta); - let args = MultiBatchCircuitConfigArgs { - challenges: challenges_exprs, - }; - let config = MultiBatchCircuitConfig::new(meta, args); - (config, challenges) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let (config, challenge) = config; - - let challenges = challenge.values(&layouter); - - //============================================================== - // extract all the hashes and load them to the hash table - //============================================================== - let (preimages, _digests) = self.multi_batch_public_data.extract_hashes(); - - config - .keccak_circuit_config - .load_aux_tables(&mut layouter)?; - - let (_preimages, _digests) = config.assign(&mut layouter, challenges, &preimages)?; - - // Following code are used for debugging - // - // // assert the inputs are correct - // { - // for (i, preimage) in preimages.iter().enumerate() { - // for (j, chunk) in preimage.iter().chunks(8).into_iter().enumerate() { - // for (k, &byte) in chunk.enumerate() { - // let index = i * 300 + j * 12 + k + 12; - // println!("index: {}", index); - // println!("input byte: {:?}", F::from(byte as u64)); - // println!("keccak byte: {:?}\n", witness[index].cell_values[6]); - // assert_eq!(F::from(byte as u64), witness[index].cell_values[6]); - // } - // } - // } - // } - - // // assert the outputs are correct - // { - // for (i, &digest) in digests.iter().enumerate() { - // let preimage_bytes: [u8; 32] = digest.try_into().unwrap(); - // for (j, chunk) in preimage_bytes.iter().chunks(8).into_iter().enumerate() { - // for (k, &byte) in chunk.enumerate() { - // let index = i * 300 + (3 - j) * 12 + k + 252; - // println!("index: {}", index); - // println!("digest byte: {:?}", F::from(byte as u64)); - // println!("keccak byte: {:?}\n", witness[index].cell_values.last()); - // assert_eq!( - // F::from(byte as u64), - // *witness[index].cell_values.last().unwrap() - // ); - // } - // } - // } - // } - - Ok(()) - } -} - -fn capacity(num_rows: usize) -> Option { - if num_rows > 0 { - // Subtract two for unusable rows - Some(num_rows / ((NUM_ROUNDS + 1) * get_num_rows_per_round()) - 2) - } else { - None - } -} - -/// Return -/// - the indices of the rows that contain the input preimages -/// - the indices of the rows that contain the output digest -fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { - let mut preimage_indices = vec![]; - let mut digest_indices = vec![]; - let mut round_ctr = 0; - - for preimage in preimages.iter() { - let num_rounds = 1 + preimage.len() / 136; - for (i, round) in preimage.chunks(136).enumerate() { - // indices for preimegas - for (j, _chunk) in round.chunks(8).into_iter().enumerate() { - for k in 0..8 { - preimage_indices.push(round_ctr * 300 + j * 12 + k + 12) - } - } - // indices for digests - if i == num_rounds - 1 { - for j in 0..4 { - for k in 0..8 { - digest_indices.push(round_ctr * 300 + (3 - j) * 12 + k + 252) - } - } - } - round_ctr += 1; - } - } - (preimage_indices, digest_indices) -} - -#[inline] -// assert two cells have same value -// (NOT constraining equality in circuit) -fn assert_equal(a: &AssignedCell, b: &AssignedCell) { - let mut t1 = F::default(); - let mut t2 = F::default(); - a.value().map(|f| t1 = *f); - b.value().map(|f| t2 = *f); - assert_eq!(t1, t2) -} diff --git a/zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs b/zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs deleted file mode 100644 index e0e19069cb..0000000000 --- a/zkevm-circuits/src/pi_agg_circuit/multi_batch_sub_circuit.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Subcircuit implementation of `MultiBatch` public input hashes. - -use std::marker::PhantomData; - -use eth_types::Field; -use halo2_proofs::{ - circuit::{Layouter, Value}, - plonk::{ConstraintSystem, Error, Expression}, -}; - -use crate::{ - keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, - pi_agg_circuit::{LOG_DEGREE, MAX_TXS}, - table::KeccakTable, - util::{Challenges, SubCircuit, SubCircuitConfig}, -}; - -use super::multi_batch_circuit::{MultiBatchCircuit, MultiBatchCircuitConfig}; - -#[derive(Clone, Debug)] -pub struct MultiBatchCircuitConfigArgs { - pub challenges: Challenges>, -} - -impl SubCircuitConfig for MultiBatchCircuitConfig { - type ConfigArgs = MultiBatchCircuitConfigArgs; - - /// Return a new MultiBatchCircuitConfig - fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { - // Instance column stores the output of the hash - let hash_digest_column = meta.instance_column(); - - // hash configuration - let keccak_circuit_config = { - let keccak_table = KeccakTable::construct(meta); - - let keccak_circuit_config_args = KeccakCircuitConfigArgs { - keccak_table, - challenges: config_args.challenges, - }; - - KeccakCircuitConfig::new(meta, keccak_circuit_config_args) - }; - - let columns = keccak_circuit_config.cell_manager.columns(); - // The current code base is hardcoded for KeccakCircuit configured - // with 300 rows and 87 columns per hash call. - assert_eq!( - columns.len(), - 87, - "cell manager configuration does not match the hard coded setup" - ); - - // enabling equality for preimage and digest columns - meta.enable_equality(columns[6].advice); - // digest column - meta.enable_equality(columns.last().unwrap().advice); - // public input column - meta.enable_equality(hash_digest_column); - - MultiBatchCircuitConfig { - log_degree: LOG_DEGREE as usize, - max_txs: MAX_TXS, - hash_digest_column, - keccak_circuit_config, - _marker: PhantomData::default(), - } - } -} - -impl SubCircuit for MultiBatchCircuit { - type Config = MultiBatchCircuitConfig; - - fn new_from_block(_block: &crate::witness::Block) -> Self { - // we cannot instantiate a new Self from a single block - unimplemented!() - } - - /// Return the minimum number of rows required to prove the block - /// Row numbers without/with padding are both returned. - fn min_num_rows_block(_block: &crate::witness::Block) -> (usize, usize) { - (1 << LOG_DEGREE, 1 << LOG_DEGREE) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - vec![self - .hash_digest - .0 - .iter() - .map(|&x| F::from(x as u64)) - .collect()] - } - - /// Make the assignments to the PiCircuit - fn synthesize_sub( - &self, - config: &Self::Config, - challenges: &Challenges>, - layouter: &mut impl Layouter, - ) -> Result<(), Error> { - // extract all the hashes and load them to the hash table - let (preimages, _digests) = self.multi_batch_public_data.extract_hashes(); - - config.keccak_circuit_config.load_aux_tables(layouter)?; - - let (_preimages, _digests) = config.assign(layouter, *challenges, &preimages)?; - - Ok(()) - } -} diff --git a/zkevm-circuits/src/pi_agg_circuit/tests.rs b/zkevm-circuits/src/pi_agg_circuit/tests.rs deleted file mode 100644 index e529cc799e..0000000000 --- a/zkevm-circuits/src/pi_agg_circuit/tests.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Test modules for aggregating public inputs - -use std::marker::PhantomData; - -use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; - -use crate::{pi_circuit::PublicData, util::SubCircuit}; - -use super::{ - chunk::ChunkPublicData, multi_batch::MultiBatchPublicData, - multi_batch_circuit::MultiBatchCircuit, LOG_DEGREE, -}; - -const TEST_MAX_TXS: usize = 4; - -#[test] -fn test_pi_agg_circuit() { - let public_data = PublicData::default(); - // this chunk spans 1 keccak round - let chunk_1 = ChunkPublicData:: { - public_data_vec: vec![ - public_data.clone(), - public_data.clone(), - public_data.clone(), - public_data.clone(), - ], - }; - - // this chunk spans 2 keccak rounds - let chunk_2 = ChunkPublicData:: { - public_data_vec: vec![ - public_data.clone(), - public_data.clone(), - public_data.clone(), - public_data.clone(), - public_data.clone(), - public_data, - ], - }; - - let multi_batch = MultiBatchPublicData { - public_data_chunks: vec![ - chunk_1.clone(), - chunk_2.clone(), - chunk_1.clone(), - chunk_2.clone(), - chunk_1.clone(), - chunk_2, - chunk_1, - ], - }; - let (_, hash_digest) = multi_batch.raw_public_input_hash(); - let multi_batch_circuit = MultiBatchCircuit:: { - multi_batch_public_data: multi_batch, - hash_digest, - _marker: PhantomData::default(), - }; - let instance = multi_batch_circuit.instance(); - - let mock_prover = MockProver::::run(LOG_DEGREE, &multi_batch_circuit, instance).unwrap(); - - mock_prover.assert_satisfied_par() -} From a595c55f473548a1d07e4b59dfd5f8aa00f897a7 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 31 May 2023 10:26:51 -0400 Subject: [PATCH 09/41] [chore] cargo fmt --- Cargo.lock | 1 - zkevm-circuits/src/keccak_circuit.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e52640b946..b5b5c711b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2151,7 +2151,6 @@ dependencies = [ "halo2_proofs", "hex", "lazy_static", - "log", "num-bigint", "poseidon-circuit", "rand", diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index dfa55fa110..4907803a5c 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -893,7 +893,6 @@ impl KeccakCircuitConfig { ) } - /// Set the cells for a keccak row; return the cells that are assigned. pub fn set_row( &self, From 19a71107c32c8fed66c9eb99634302d5e4123e1e Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 6 Jun 2023 12:50:11 -0400 Subject: [PATCH 10/41] [feat] implement Aggregator --- aggregator/Cargo.toml | 23 +- aggregator/README.md | 26 ++ aggregator/configs/aggregation.config | 1 + aggregator/configs/compression_thin.config | 1 + aggregator/configs/compression_wide.config | 1 + .../{public_input_aggregation => }/batch.rs | 0 .../{public_input_aggregation => }/chunk.rs | 14 +- aggregator/src/core.rs | 285 +++++++++++++ aggregator/src/lib.rs | 24 +- aggregator/src/param.rs | 58 +++ aggregator/src/proof_aggregation.rs | 13 + aggregator/src/proof_aggregation/circuit.rs | 377 ++++++++++++++++++ .../src/proof_aggregation/circuit_ext.rs | 31 ++ aggregator/src/proof_aggregation/config.rs | 129 ++++++ .../public_input_aggregation.rs | 33 +- .../public_input_aggregation/circuit.rs | 64 ++- .../public_input_aggregation/circuit_ext.rs | 49 +++ .../public_input_aggregation/config.rs | 67 ++++ aggregator/src/proof_compression.rs | 14 + aggregator/src/proof_compression/circuit.rs | 221 ++++++++++ .../src/proof_compression/circuit_ext.rs | 36 ++ aggregator/src/proof_compression/config.rs | 69 ++++ .../public_input_aggregation/circuit_ext.rs | 11 - .../src/public_input_aggregation/config.rs | 342 ---------------- .../public_input_aggregation/sub_circuit.rs | 81 ---- aggregator/src/tests.rs | 4 + aggregator/src/tests/mock_chunk.rs | 57 +++ aggregator/src/tests/mock_chunk/circuit.rs | 72 ++++ .../src/tests/mock_chunk/circuit_ext.rs | 23 ++ aggregator/src/tests/mock_chunk/config.rs | 173 ++++++++ aggregator/src/tests/proof_aggregation.rs | 162 ++++++++ aggregator/src/tests/proof_compression.rs | 317 +++++++++++++++ .../public_input_aggregation.rs} | 6 +- 33 files changed, 2303 insertions(+), 481 deletions(-) create mode 100644 aggregator/README.md create mode 100644 aggregator/configs/aggregation.config create mode 100644 aggregator/configs/compression_thin.config create mode 100644 aggregator/configs/compression_wide.config rename aggregator/src/{public_input_aggregation => }/batch.rs (100%) rename aggregator/src/{public_input_aggregation => }/chunk.rs (86%) create mode 100644 aggregator/src/core.rs create mode 100644 aggregator/src/param.rs create mode 100644 aggregator/src/proof_aggregation.rs create mode 100644 aggregator/src/proof_aggregation/circuit.rs create mode 100644 aggregator/src/proof_aggregation/circuit_ext.rs create mode 100644 aggregator/src/proof_aggregation/config.rs rename aggregator/src/{ => proof_aggregation}/public_input_aggregation.rs (76%) rename aggregator/src/{ => proof_aggregation}/public_input_aggregation/circuit.rs (71%) create mode 100644 aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs create mode 100644 aggregator/src/proof_aggregation/public_input_aggregation/config.rs create mode 100644 aggregator/src/proof_compression.rs create mode 100644 aggregator/src/proof_compression/circuit.rs create mode 100644 aggregator/src/proof_compression/circuit_ext.rs create mode 100644 aggregator/src/proof_compression/config.rs delete mode 100644 aggregator/src/public_input_aggregation/circuit_ext.rs delete mode 100644 aggregator/src/public_input_aggregation/config.rs delete mode 100644 aggregator/src/public_input_aggregation/sub_circuit.rs create mode 100644 aggregator/src/tests.rs create mode 100644 aggregator/src/tests/mock_chunk.rs create mode 100644 aggregator/src/tests/mock_chunk/circuit.rs create mode 100644 aggregator/src/tests/mock_chunk/circuit_ext.rs create mode 100644 aggregator/src/tests/mock_chunk/config.rs create mode 100644 aggregator/src/tests/proof_aggregation.rs create mode 100644 aggregator/src/tests/proof_compression.rs rename aggregator/src/{public_input_aggregation/tests.rs => tests/public_input_aggregation.rs} (90%) diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index ccacc1f530..60f35beac1 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -6,22 +6,23 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -eth-types = { path = "../eth-types" } -zkevm-circuits = { path = "../zkevm-circuits" } -ethers-core = "0.17.0" -rand = "0.8" +eth-types = { git = "https://github.com/scroll-tech/zkevm-circuits.git", branch = "develop" } +zkevm-circuits = { git = "https://github.com/scroll-tech/zkevm-circuits.git", branch = "develop" } + + ark-std = "0.4.0" -log = "0.4" env_logger = "0.10.0" +ethers-core = "0.17.0" +log = "0.4" +itertools = "0.10.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +rand = "0.8" halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } -snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } - -[dev-dependencies] -halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "halo2-ecc-snark-verifier-0323", default-features=false, features=["halo2-pse","display"] } snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } - +snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } [features] default = [] -# default = [ "ark-std/print-trace" ] \ No newline at end of file +print-trace = [ "ark-std/print-trace" ] \ No newline at end of file diff --git a/aggregator/README.md b/aggregator/README.md new file mode 100644 index 0000000000..4070c94161 --- /dev/null +++ b/aggregator/README.md @@ -0,0 +1,26 @@ +Proof Aggregation +----- + +![Architecture](./figures/architecture.png) + +This repo does proof aggregations for zkEVM proofs. + +## zkEVM circuit +A zkEVM circuits generates a ZK proof for a chunk of blocks. It takes 64 field elements as its public input, consist of +- chunk's data hash digest +- chunk's public input hash digest +Each hash digest is decomposed into 32 bytes, and then casted as 32 field elements. + +For the ease of testing, this repo implements a `MockCircuit` which hash same public input APIs as a zkEVM circuit. + +## Compression circuit +A compression circuit takes in a snark proof and generates a new (potentially small) snark proof. +It re-expose the same public inputs as the original circuit. + +## Aggregation circuit +An aggregation circuit takes in a batch of proofs, each for a chunk of blocks. +It generates a single proof asserting the validity of all the proofs. +It also performs public input aggregation, i.e., reducing the 64 public elements per proof into a fixed number of elements: +- 12 elements from accumulators +- 132 elements from the hashes +See [public input aggregation](./src/proof_aggregation/public_input_aggregation.rs) for the details of public input aggregation. diff --git a/aggregator/configs/aggregation.config b/aggregator/configs/aggregation.config new file mode 100644 index 0000000000..87f31b575d --- /dev/null +++ b/aggregator/configs/aggregation.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":23,"num_advice":[30],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} diff --git a/aggregator/configs/compression_thin.config b/aggregator/configs/compression_thin.config new file mode 100644 index 0000000000..4fc86986d9 --- /dev/null +++ b/aggregator/configs/compression_thin.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":23,"num_advice":[3],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} \ No newline at end of file diff --git a/aggregator/configs/compression_wide.config b/aggregator/configs/compression_wide.config new file mode 100644 index 0000000000..2114929ce8 --- /dev/null +++ b/aggregator/configs/compression_wide.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":23,"num_advice":[18],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} diff --git a/aggregator/src/public_input_aggregation/batch.rs b/aggregator/src/batch.rs similarity index 100% rename from aggregator/src/public_input_aggregation/batch.rs rename to aggregator/src/batch.rs diff --git a/aggregator/src/public_input_aggregation/chunk.rs b/aggregator/src/chunk.rs similarity index 86% rename from aggregator/src/public_input_aggregation/chunk.rs rename to aggregator/src/chunk.rs index 6d95b21295..ff3e6f6ecb 100644 --- a/aggregator/src/public_input_aggregation/chunk.rs +++ b/aggregator/src/chunk.rs @@ -3,7 +3,7 @@ use eth_types::H256; use ethers_core::utils::keccak256; -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, Copy)] /// A chunk is a set of continuous blocks. /// A ChunkHash consists of 4 hashes, representing the changes incurred by this chunk of blocks: /// - state root before this chunk @@ -47,14 +47,20 @@ impl ChunkHash { /// Public input hash for a given chunk is defined as /// keccak( chain id || prev state root || post state root || withdraw root || data hash ) pub fn public_input_hash(&self) -> H256 { - let preimage = [ + 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 + pub fn extract_hash_preimage(&self) -> Vec { + [ self.chain_id.to_le_bytes().as_ref(), self.prev_state_root.as_bytes(), self.post_state_root.as_bytes(), self.withdraw_root.as_bytes(), self.data_hash.as_bytes(), ] - .concat(); - keccak256::<&[u8]>(preimage.as_ref()).into() + .concat() } } diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs new file mode 100644 index 0000000000..bc91eda195 --- /dev/null +++ b/aggregator/src/core.rs @@ -0,0 +1,285 @@ +use ark_std::{end_timer, start_timer}; +use eth_types::Field; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Value}, + plonk::Error, +}; +use halo2_proofs::{ + halo2curves::bn256::{Bn256, G1Affine}, + poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, +}; +use rand::Rng; +use snark_verifier::{ + pcs::{ + kzg::{Bdfg21, Kzg, KzgAccumulator, KzgAs}, + AccumulationSchemeProver, + }, + verifier::PlonkVerifier, +}; +use snark_verifier_sdk::{ + halo2::{aggregation::Shplonk, PoseidonTranscript, POSEIDON_SPEC}, + NativeLoader, Snark, +}; +use zkevm_circuits::{ + keccak_circuit::{keccak_packed_multi::multi_keccak, KeccakCircuitConfig}, + table::LookupTable, + util::Challenges, +}; + +use crate::{ + util::{assert_equal, capacity, get_indices}, + LOG_DEGREE, +}; + +/// Input the hash input bytes, +/// assign the circuit for the hash function, +/// return cells of the hash inputs and digests. +#[allow(clippy::type_complexity)] +pub(crate) fn assign_batch_hashes( + config: &KeccakCircuitConfig, + layouter: &mut impl Layouter, + challenges: Challenges>, + preimages: &[Vec], +) -> Result< + ( + Vec>>, // input cells + Vec>>, // digest cells + ), + Error, +> { + let mut is_first_time = true; + let num_rows = 1 << LOG_DEGREE; + + let timer = start_timer!(|| ("multi keccak").to_string()); + let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; + end_timer!(timer); + + // extract the indices of the rows for which the preimage and the digest cells lie in + let (preimage_indices, digest_indices) = get_indices(preimages); + let mut preimage_indices_iter = preimage_indices.iter(); + let mut digest_indices_iter = digest_indices.iter(); + + let mut hash_input_cells = vec![]; + let mut hash_output_cells = vec![]; + + let mut cur_preimage_index = preimage_indices_iter.next(); + let mut cur_digest_index = digest_indices_iter.next(); + + layouter.assign_region( + || "assign keccak rows", + |mut region| { + if is_first_time { + is_first_time = false; + let offset = witness.len() - 1; + config.set_row(&mut region, offset, &witness[offset])?; + return Ok(()); + } + // ==================================================== + // Step 1. Extract the hash cells + // ==================================================== + let mut current_hash_input_cells = vec![]; + let mut current_hash_output_cells = vec![]; + + let timer = start_timer!(|| "assign row"); + for (offset, keccak_row) in witness.iter().enumerate() { + let row = config.set_row(&mut region, offset, keccak_row)?; + + if cur_preimage_index.is_some() && *cur_preimage_index.unwrap() == offset { + current_hash_input_cells.push(row[6].clone()); + cur_preimage_index = preimage_indices_iter.next(); + } + if cur_digest_index.is_some() && *cur_digest_index.unwrap() == offset { + current_hash_output_cells.push(row.last().unwrap().clone()); + cur_digest_index = digest_indices_iter.next(); + } + + // we reset the current hash when it is finalized + // note that length == 0 indicate that the hash is a padding + // so we simply skip it + if keccak_row.is_final && keccak_row.length != 0 { + hash_input_cells.push(current_hash_input_cells); + hash_output_cells.push(current_hash_output_cells); + current_hash_input_cells = vec![]; + current_hash_output_cells = vec![]; + } + } + end_timer!(timer); + + // sanity: we have same number of hash input and output + let hash_num = hash_input_cells.len(); + let num_chunks = hash_num - 2; + assert_eq!(hash_num, preimages.len()); + assert_eq!(hash_num, hash_output_cells.len()); + + // ==================================================== + // Step 2. Constraint the relations between hash preimages and digests + // ==================================================== + // + // 2.1 batch_data_hash digest is reused for public input hash + // + // 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 ) + for i in 0..4 { + for j in 0..8 { + // sanity check + assert_equal( + &hash_input_cells[0][i * 8 + j + 100], + &hash_output_cells[1][(3 - i) * 8 + j], + ); + region.constrain_equal( + // preimage and digest has different endianness + hash_input_cells[0][i * 8 + j + 100].cell(), + hash_output_cells[1][(3 - i) * 8 + j].cell(), + )?; + } + } + + // 2.2 batch_pi_hash used same roots as chunk_pi_hash + // + // batch_pi_hash = + // keccak( + // chain_id || + // chunk[0].prev_state_root || + // chunk[k-1].post_state_root || + // chunk[k-1].withdraw_root || + // batchData_hash ) + // + // chunk[i].piHash = + // keccak( + // chain id || + // chunk[i].prevStateRoot || + // chunk[i].postStateRoot || + // chunk[i].withdrawRoot || + // chunk[i].datahash) + for i in 0..32 { + // 2.2.1 chunk[0].prev_state_root + // sanity check + assert_equal(&hash_input_cells[0][i + 4], &hash_input_cells[2][i + 4]); + region.constrain_equal( + hash_input_cells[0][i + 4].cell(), + hash_input_cells[2][i + 4].cell(), + )?; + // 2.2.2 chunk[k-1].post_state_root + // sanity check + assert_equal( + &hash_input_cells[0][i + 36], + &hash_input_cells[hash_num - 1][i + 36], + ); + region.constrain_equal( + hash_input_cells[0][i + 36].cell(), + hash_input_cells[hash_num - 1][i + 36].cell(), + )?; + // 2.2.3 chunk[k-1].withdraw_root + assert_equal( + &hash_input_cells[0][i + 68], + &hash_input_cells[hash_num - 1][i + 68], + ); + region.constrain_equal( + hash_input_cells[0][i + 68].cell(), + hash_input_cells[hash_num - 1][i + 68].cell(), + )?; + } + + // 2.3 same dataHash is used for batchDataHash and chunk[i].piHash + // + // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) + // + // chunk[i].piHash = + // keccak( + // &chain id || + // chunk[i].prevStateRoot || + // chunk[i].postStateRoot || + // chunk[i].withdrawRoot || + // chunk[i].datahash) + for (i, chunk) in hash_input_cells[1].chunks(32).enumerate().take(num_chunks) { + for (j, cell) in chunk.iter().enumerate() { + // sanity check + assert_equal(cell, &hash_input_cells[2 + i][j + 100]); + region.constrain_equal(cell.cell(), hash_input_cells[2 + i][j + 100].cell())?; + } + } + + // 2.4 chunks are continuous: they are linked via the state roots + for i in 0..num_chunks - 1 { + for j in 0..32 { + // sanity check + assert_equal( + &hash_input_cells[i + 3][4 + j], + &hash_input_cells[i + 2][36 + j], + ); + region.constrain_equal( + // chunk[i+1].prevStateRoot + hash_input_cells[i + 3][4 + j].cell(), + // chunk[i].postStateRoot + hash_input_cells[i + 2][36 + j].cell(), + )?; + } + } + + // 2.5 assert hashes use a same chain id + for i in 0..num_chunks { + for j in 0..4 { + // sanity check + assert_equal(&hash_input_cells[0][j], &hash_input_cells[i + 2][j]); + region.constrain_equal( + // chunk[i+1].prevStateRoot + hash_input_cells[0][j].cell(), + // chunk[i].postStateRoot + hash_input_cells[i + 2][j].cell(), + )?; + } + } + + config.keccak_table.annotate_columns_in_region(&mut region); + config.annotate_circuit(&mut region); + Ok(()) + }, + )?; + + Ok((hash_input_cells, hash_output_cells)) +} + +/// Subroutine for the witness generations. +/// Extract the accumulator and proof that from previous snarks. +/// Uses SHPlonk for accumulation. +pub(crate) fn extract_accumulators_and_proof( + params: &ParamsKZG, + snarks: &[Snark], + rng: impl Rng + Send, +) -> (KzgAccumulator, Vec) { + let svk = params.get_g()[0].into(); + + let mut transcript_read = + PoseidonTranscript::::from_spec(&[], POSEIDON_SPEC.clone()); + let accumulators = snarks + .iter() + .flat_map(|snark| { + transcript_read.new_stream(snark.proof.as_slice()); + let proof = Shplonk::read_proof( + &svk, + &snark.protocol, + &snark.instances, + &mut transcript_read, + ); + Shplonk::succinct_verify(&svk, &snark.protocol, &snark.instances, &proof) + }) + .collect::>(); + + let mut transcript_write = + PoseidonTranscript::>::from_spec(vec![], POSEIDON_SPEC.clone()); + // We always use SHPLONK for accumulation scheme when aggregating proofs + let accumulator = + KzgAs::>::create_proof::>, _>( + &Default::default(), + &accumulators, + &mut transcript_write, + rng, + ) + .unwrap(); + (accumulator, transcript_write.finalize()) +} diff --git a/aggregator/src/lib.rs b/aggregator/src/lib.rs index 9c7d71d64a..a6306ec227 100644 --- a/aggregator/src/lib.rs +++ b/aggregator/src/lib.rs @@ -1,6 +1,24 @@ -/// public input aggregation -mod public_input_aggregation; +// This module implements `Chunk` related data types. +// A chunk is a list of blocks. +mod chunk; +// This module implements `Batch` related data types. +// A batch is a list of chunk. +mod batch; +/// Core module for circuit assignment +mod core; +/// Parameters for compression circuit +pub mod param; +/// proof aggregation +mod proof_aggregation; +/// proof compression +mod proof_compression; /// utilities mod util; -pub use public_input_aggregation::*; +#[cfg(test)] +mod tests; + +pub use batch::BatchHash; +pub use chunk::ChunkHash; +pub use proof_aggregation::*; +pub use proof_compression::*; diff --git a/aggregator/src/param.rs b/aggregator/src/param.rs new file mode 100644 index 0000000000..34516f7e44 --- /dev/null +++ b/aggregator/src/param.rs @@ -0,0 +1,58 @@ +use snark_verifier::loader::halo2::halo2_ecc::fields::fp::FpStrategy; + +pub(crate) const LIMBS: usize = 3; +pub(crate) const BITS: usize = 88; + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +/// Parameters for aggregation circuit and compression circuit configs. +pub struct ConfigParams { + pub strategy: FpStrategy, + pub degree: u32, + pub num_advice: Vec, + pub num_lookup_advice: Vec, + pub num_fixed: usize, + pub lookup_bits: usize, + pub limb_bits: usize, + pub num_limbs: usize, +} + +impl ConfigParams { + pub(crate) fn aggregation_param() -> Self { + Self { + strategy: FpStrategy::Simple, + degree: 25, + num_advice: vec![2], + num_lookup_advice: vec![1], + num_fixed: 1, + lookup_bits: 20, + limb_bits: 88, + num_limbs: 3, + } + } + + pub(crate) fn _compress_wide_param() -> Self { + Self { + strategy: FpStrategy::Simple, + degree: 22, + num_advice: vec![35], + num_lookup_advice: vec![1], + num_fixed: 1, + lookup_bits: 20, + limb_bits: 88, + num_limbs: 3, + } + } + + pub(crate) fn _compress_thin_param() -> Self { + Self { + strategy: FpStrategy::Simple, + degree: 25, + num_advice: vec![1], + num_lookup_advice: vec![1], + num_fixed: 1, + lookup_bits: 20, + limb_bits: 88, + num_limbs: 3, + } + } +} diff --git a/aggregator/src/proof_aggregation.rs b/aggregator/src/proof_aggregation.rs new file mode 100644 index 0000000000..04632d8009 --- /dev/null +++ b/aggregator/src/proof_aggregation.rs @@ -0,0 +1,13 @@ +/// Circuit implementation of aggregation circuit. +mod circuit; +/// CircuitExt implementation of compression circuit. +mod circuit_ext; +/// Config for aggregation circuit +mod config; +/// public input aggregation +mod public_input_aggregation; + +pub use circuit::AggregationCircuit; +pub use config::AggregationConfig; + +pub(crate) use public_input_aggregation::*; diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs new file mode 100644 index 0000000000..c5d56df08e --- /dev/null +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -0,0 +1,377 @@ +use ark_std::{end_timer, start_timer}; +use halo2_proofs::plonk::Error; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + halo2curves::bn256::{Bn256, Fq, Fr, G1Affine}, + plonk::{Circuit, ConstraintSystem}, + poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, +}; +use itertools::Itertools; +use rand::Rng; +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{ + self, AssignedValue, Context, ContextParams, +}; +use snark_verifier::loader::halo2::Halo2Loader; +use snark_verifier::{ + pcs::kzg::{Bdfg21, Kzg, KzgAccumulator, KzgSuccinctVerifyingKey}, + util::arithmetic::fe_to_limbs, +}; +use snark_verifier_sdk::halo2::aggregation::{aggregate, flatten_accumulator}; +use snark_verifier_sdk::CircuitExt; +use snark_verifier_sdk::{halo2::aggregation::Svk, NativeLoader, Snark, SnarkWitness}; +use zkevm_circuits::util::Challenges; + +use crate::core::{assign_batch_hashes, extract_accumulators_and_proof}; +use crate::proof_aggregation::config::AggregationConfig; +use crate::{ + param::{ConfigParams, BITS, LIMBS}, + BatchHashCircuit, ChunkHash, +}; + +/// Aggregation circuit that does not re-expose any public inputs from aggregated snarks +#[derive(Clone)] +pub struct AggregationCircuit { + pub(crate) svk: KzgSuccinctVerifyingKey, + pub(crate) snarks: Vec, + // the public instance for this circuit consists of + // - an accumulator (12 elements) + // - batch hash circuit's public inputs, 132 elements + pub(crate) flattened_instances: Vec, + // accumulation scheme proof, private input + pub(crate) as_proof: Value>, + // batch hash circuit for which the snarks are generated + pub(crate) batch_hash_circuit: BatchHashCircuit, +} + +impl AggregationCircuit { + /// Build a new aggregation circuit for a list of __compressed__ snarks. + /// Requires the chunk hashes that are used for the __fresh__ snark + pub fn new( + params: &ParamsKZG, + snarks: &[Snark], + rng: impl Rng + Send, + chunk_hashes: &[ChunkHash], + ) -> Self { + // sanity: for each chunk we have a snark + assert_eq!( + snarks.len(), + chunk_hashes.len(), + "num of snarks ({}) does not match number of chunks ({})", + snarks.len(), + chunk_hashes.len(), + ); + // sanity check: snarks's public input matches chunk_hashes + for (chunk, snark) in chunk_hashes.iter().zip(snarks.iter()) { + let chunk_hash_bytes = chunk.public_input_hash(); + let snark_hash_bytes = &snark.instances[0]; + + for i in 0..32 { + assert_eq!( + Fr::from(chunk.data_hash.as_bytes()[i] as u64), + snark_hash_bytes[i + 12] + ); + + assert_eq!( + Fr::from(chunk_hash_bytes[i] as u64), + snark_hash_bytes[i + 44] + ); + } + } + + // extract the accumulators and proofs + let svk = params.get_g()[0].into(); + + let (accumulator, as_proof) = extract_accumulators_and_proof(params, snarks, rng); + let KzgAccumulator:: { lhs, rhs } = accumulator; + let acc_instances = [lhs.x, lhs.y, rhs.x, rhs.y] + .map(fe_to_limbs::) + .concat(); + + // extract the pi aggregation circuit's instances + let batch_hash_circuit = BatchHashCircuit::construct(chunk_hashes); + let pi_aggregation_instances = &batch_hash_circuit.instances()[0]; + + let flattened_instances: Vec = [ + acc_instances.as_slice(), + pi_aggregation_instances.as_slice(), + ] + .concat(); + + log::trace!("flattened instances during construction"); + for (i, e) in flattened_instances.iter().enumerate() { + log::trace!("{}-th: {:?}", i, e); + } + + Self { + svk, + snarks: snarks.into_iter().cloned().map_into().collect(), + flattened_instances, + as_proof: Value::known(as_proof), + batch_hash_circuit, + } + } + + pub fn succinct_verifying_key(&self) -> &Svk { + &self.svk + } + + pub fn snarks(&self) -> &[SnarkWitness] { + &self.snarks + } + + pub fn as_proof(&self) -> Value<&[u8]> { + self.as_proof.as_ref().map(Vec::as_slice) + } +} + +impl Circuit for AggregationCircuit { + type Config = (AggregationConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let params = ConfigParams::aggregation_param(); + let challenges = Challenges::construct(meta); + let config = AggregationConfig::configure(meta, ¶ms, challenges); + log::info!( + "aggregation circuit configured with k = {} and {:?} advice columns", + params.degree, + params.num_advice + ); + (config, challenges) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let (config, challenge) = config; + + let witness_time = start_timer!(|| "synthesize | Aggregation Circuit"); + config + .range() + .load_lookup_table(&mut layouter) + .expect("load range lookup table"); + let mut first_pass = halo2_base::SKIP_FIRST_PASS; + + // This circuit takes 2 steps + // - 1. use aggregation circuit to aggregate the multiple snarks into a single one; + // re-export all the public input of the snarks, denoted by [snarks_instances], and + // the accumulator [acc_instances] + // - 2. use public input aggregation circuit to aggregate the chunks; + // expose the instance dentoed by [pi_agg_instances] + // - 3. assert [snarks_instances] are private inputs used for public input aggregation circuit + + // ============================================== + // Step 1: aggregation circuit + // ============================================== + // let mut aggregation_instances = vec![]; + let mut accumulator_instances: Vec> = vec![]; + let mut snark_inputs: Vec> = vec![]; + layouter.assign_region( + || "aggregation", + |region| { + if first_pass { + first_pass = false; + return Ok(()); + } + let ctx = Context::new( + region, + ContextParams { + max_rows: config.gate().max_rows, + num_context_ids: 1, + fixed_columns: config.gate().constants.clone(), + }, + ); + + let ecc_chip = config.ecc_chip(); + let loader = Halo2Loader::new(ecc_chip, ctx); + + // + // extract the assigned values for + // - instances which are the public inputs of each chunk (prefixed with 12 instances from previous accumualtors) + // - new accumulator to be verified on chain + // + let (assigned_aggreation_instances, acc) = aggregate::>( + &self.svk, + &loader, + &self.snarks, + self.as_proof(), + ); + log::trace!("aggregation circuit during assigning"); + for (i, e) in assigned_aggreation_instances[0].iter().enumerate() { + log::trace!("{}-th instance: {:?}", i, e.value) + } + + // extract the following cells for later constraints + // - the accumulators + // - the public input from snark + accumulator_instances.extend( + flatten_accumulator(acc) + .iter() + .map(|assigned| assigned.clone()), + ); + // - 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 + snark_inputs.extend( + assigned_aggreation_instances + .iter() + .flat_map(|instance_column| instance_column.iter().skip(12).map(|x| x)), + ); + + config.range().finalize(&mut loader.ctx_mut()); + + loader.ctx_mut().print_stats(&["Range"]); + + Ok(()) + }, + )?; + + log::trace!("instance outside aggregation function"); + for (i, e) in snark_inputs.iter().enumerate() { + log::trace!("{}-th instance: {:?}", i, e.value) + } + // assert the accumulator in aggregation instance matchs public input + for (i, v) in accumulator_instances.iter().enumerate() { + layouter.constrain_instance(v.cell(), config.instance, i)?; + } + + // ============================================== + // step 2: public input aggregation circuit + // ============================================== + // extract all the hashes and load them to the hash table + // assert the public input matches that of the pi_aggregation_circuit + let challenges = challenge.values(&layouter); + + let timer = start_timer!(|| ("extract hash").to_string()); + let preimages = self.batch_hash_circuit.extract_hash_preimages(); + end_timer!(timer); + + let timer = start_timer!(|| ("load aux table").to_string()); + config + .keccak_circuit_config + .load_aux_tables(&mut layouter)?; + end_timer!(timer); + + let timer = start_timer!(|| ("assign cells").to_string()); + let (hash_input_cells, hash_output_cells) = assign_batch_hashes( + &config.keccak_circuit_config, + &mut layouter, + challenges, + &preimages, + )?; + end_timer!(timer); + + log::trace!("hash input"); + for v in hash_input_cells.iter() { + for (i, c) in v.iter().enumerate() { + log::trace!("{}-th {:?}", i, c.value()) + } + } + log::trace!("hash output"); + for v in hash_output_cells.iter() { + for (i, c) in v.iter().enumerate() { + log::trace!("{}-th {:?}", i, c.value()) + } + } + + // ============================================== + // step 3: aggregation circuit and public input aggregation circuit + // share common inputs + // ============================================== + // aggregation circuit's public input: + // - for each chunk: + // - data hash + // - public input hash + // Those are used as private inputs to the public input aggregation circuit + layouter.assign_region( + || "glue circuits", + |mut region| { + if first_pass { + first_pass = false; + return Ok(()); + } + + for chunk_idx in 0..self.snarks.len() { + // step 3.1, data hash + // - batch_data_hash := keccak(chunk_0.data_hash + // || ... + // || chunk_k-1.data_hash) + // where batch_data_hash is the second hash for pi aggregation + for i in 0..32 { + region.constrain_equal( + // the first 32 inputs for the snark + snark_inputs[64 * chunk_idx + i].cell(), + hash_input_cells[1][chunk_idx * 32 + i].cell(), + )?; + } + // step 3.2, public input hash + // the public input hash for the i-th snark is the (i+2)-th hash + for i in 0..4 { + for j in 0..8 { + region.constrain_equal( + // the second 32 inputs for the snark + snark_inputs[64 * chunk_idx + i * 8 + j + 32].cell(), + hash_output_cells[chunk_idx + 2][(3 - i) * 8 + j].cell(), + )?; + } + } + } + + Ok(()) + }, + )?; + + // ==================================================== + // Last step: Constraint the hash data matches the raw public input + // ==================================================== + let acc_len = 12; + { + for i in 0..32 { + // first_chunk_prev_state_root + layouter.constrain_instance( + hash_input_cells[2][4 + i].cell(), + config.instance, + i + acc_len, + )?; + // last_chunk_post_state_root + layouter.constrain_instance( + hash_input_cells.last().unwrap()[36 + i].cell(), + config.instance, + i + 32 + acc_len, + )?; + // last_chunk_withdraw_root + layouter.constrain_instance( + hash_input_cells.last().unwrap()[68 + i].cell(), + config.instance, + i + 64 + acc_len, + )?; + } + // batch_public_input_hash + for i in 0..4 { + for j in 0..8 { + // digest in circuit has a different endianness + layouter.constrain_instance( + hash_output_cells[0][(3 - i) * 8 + j].cell(), + config.instance, + i * 8 + j + 96 + acc_len, + )?; + } + } + // last 4 inputs are the chain id + for i in 0..4 { + layouter.constrain_instance( + hash_input_cells[0][i].cell(), + config.instance, + 128 + acc_len + i, + )?; + } + } + + end_timer!(witness_time); + Ok(()) + } +} diff --git a/aggregator/src/proof_aggregation/circuit_ext.rs b/aggregator/src/proof_aggregation/circuit_ext.rs new file mode 100644 index 0000000000..33cd15a983 --- /dev/null +++ b/aggregator/src/proof_aggregation/circuit_ext.rs @@ -0,0 +1,31 @@ +use halo2_proofs::{halo2curves::bn256::Fr, plonk::Selector}; +use snark_verifier_sdk::CircuitExt; + +use crate::{param::LIMBS, AggregationCircuit}; + +impl CircuitExt for AggregationCircuit { + fn num_instance(&self) -> Vec { + // accumulator [..lhs, ..rhs] + let acc_len = 4 * LIMBS; + // public input + let public_input_agg_instance_len = self.batch_hash_circuit.num_instance()[0]; + + vec![public_input_agg_instance_len + acc_len] + } + + fn instances(&self) -> Vec> { + vec![self.flattened_instances.clone()] + } + + fn accumulator_indices() -> Option> { + // the accumulator are the first 12 cells in the instance + Some((0..4 * LIMBS).map(|idx| (0, idx)).collect()) + } + + fn selectors(config: &Self::Config) -> Vec { + config.0.gate().basic_gates[0] + .iter() + .map(|gate| gate.q_enable) + .collect() + } +} diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/proof_aggregation/config.rs new file mode 100644 index 0000000000..448e346e84 --- /dev/null +++ b/aggregator/src/proof_aggregation/config.rs @@ -0,0 +1,129 @@ +use halo2_proofs::{ + halo2curves::bn256::{Fq, Fr, G1Affine}, + plonk::{Column, ConstraintSystem, Instance}, +}; +use snark_verifier::{ + loader::halo2::halo2_ecc::{ + ecc::{BaseFieldEccChip, EccChip}, + fields::fp::FpConfig, + halo2_base::gates::{flex_gate::FlexGateConfig, range::RangeConfig}, + }, + util::arithmetic::modulus, +}; +use zkevm_circuits::{ + keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, + table::KeccakTable, + util::{Challenges, SubCircuitConfig}, +}; + +use crate::param::{ConfigParams, BITS, LIMBS}; + +#[derive(Debug, Clone)] +/// Configurations for aggregation circuit +/// This config is hard coded for BN256 curve +pub struct AggregationConfig { + /// Non-native field chip configurations + pub base_field_config: FpConfig, + /// Keccak circuit configurations + pub keccak_circuit_config: KeccakCircuitConfig, + /// Instance for public input; stores + /// - accumulator from aggregation (12 elements) + /// - aggregated public inputs (132 elements): + /// chain_id || + /// chunk[0].prev_state_root || + /// chunk[k-1].post_state_root || + /// chunk[k-1].withdraw_root || + /// batch_data_hash + pub instance: Column, +} + +impl AggregationConfig { + /// Build a configuration from parameters. + pub fn configure( + meta: &mut ConstraintSystem, + params: &ConfigParams, + challenges: Challenges, + ) -> Self { + assert!( + params.limb_bits == BITS && params.num_limbs == LIMBS, + "For now we fix limb_bits = {}, otherwise change code", + BITS + ); + + // base field configuration for aggregation circuit + let base_field_config = FpConfig::configure( + meta, + params.strategy.clone(), + ¶ms.num_advice, + ¶ms.num_lookup_advice, + params.num_fixed, + params.lookup_bits, + BITS, + LIMBS, + modulus::(), + 0, + params.degree as usize, + ); + + // hash configuration for aggregation circuit + let keccak_circuit_config = { + let keccak_table = KeccakTable::construct(meta); + let challenges_exprs = challenges.exprs(meta); + + let keccak_circuit_config_args = KeccakCircuitConfigArgs { + keccak_table, + challenges: challenges_exprs, + }; + + KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + }; + + // The current code base is hardcoded for KeccakCircuit configured + // with 300 rows and 87 columns per hash call. + let columns = keccak_circuit_config.cell_manager.columns(); + + assert_eq!( + columns.len(), + 87, + "cell manager configuration does not match the hard coded setup" + ); + + // enabling equality for preimage and digest columns + meta.enable_equality(columns[6].advice); + // digest column + meta.enable_equality(columns.last().unwrap().advice); + + // Instance column stores + // - the accumulator + // - the output of the hash + let instance = meta.instance_column(); + // public input column + meta.enable_equality(instance); + + Self { + base_field_config, + keccak_circuit_config, + instance, + } + } + + /// Expose the instance column + pub fn instance_column(&self) -> Column { + self.instance + } + + /// Range gate configuration + pub fn range(&self) -> &RangeConfig { + &self.base_field_config.range + } + + /// Flex gate configuration + pub fn gate(&self) -> &FlexGateConfig { + &self.base_field_config.range.gate + } + + /// Ecc gate configuration + pub fn ecc_chip(&self) -> BaseFieldEccChip { + EccChip::construct(self.base_field_config.clone()) + } +} diff --git a/aggregator/src/public_input_aggregation.rs b/aggregator/src/proof_aggregation/public_input_aggregation.rs similarity index 76% rename from aggregator/src/public_input_aggregation.rs rename to aggregator/src/proof_aggregation/public_input_aggregation.rs index 1075eda5c2..1221761f92 100644 --- a/aggregator/src/public_input_aggregation.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation.rs @@ -11,15 +11,22 @@ //! Those 4 hashes are obtained from the caller. //! //! A chunk's public input hash is then derived from the above 4 attributes via -//! - chunk_pi_hash := keccak(chain_id || prev_state_root || post_state_root || withdraw_root -//! || chunk_data_hash) +//! - chunk_pi_hash := keccak(chain_id +//! || prev_state_root +//! || post_state_root +//! || withdraw_root +//! || chunk_data_hash) //! //! A batch is a list of continuous chunks. It consists of 2 hashes -//! - batch_data_hash := keccak(chunk_0.data_hash || ... || -//! chunk_k-1.data_hash) +//! - batch_data_hash := keccak(chunk_0.data_hash +//! || ... +//! || chunk_k-1.data_hash) //! -//! - batch_pi_hash := keccak(chain_id || chunk_0.prev_state_root || -//! chunk_k-1.post_state_root || chunk_k-1.withdraw_root || batch_data_hash) +//! - batch_pi_hash := keccak(chain_id +//! || chunk_0.prev_state_root +//! || chunk_k-1.post_state_root +//! || chunk_k-1.withdraw_root +//! || batch_data_hash) //! //! Note that chain_id is used for all public input hashes. But not for any data hashes. //! @@ -51,23 +58,14 @@ //! //! See tests::test_pi_aggregation_circuit -// This module implements `Chunk` related data types. -// A chunk is a list of blocks. -mod chunk; -// This module implements `Batch` related data types. -// A batch is a list of chunk. -mod batch; // Circuit implementation of `BatchHashCircuit`. mod circuit; -// SubCircuit implementation of `BatchHashCircuit`. -mod sub_circuit; // CircuitExt implementation of `BatchHashCircuit`. mod circuit_ext; // Circuit and SubCircuit configurations mod config; -pub use batch::BatchHash; -pub use chunk::ChunkHash; +pub use crate::{BatchHash, ChunkHash}; pub use circuit::{BatchHashCircuit, BatchHashCircuitPublicInput}; pub use config::{BatchCircuitConfig, BatchCircuitConfigArgs}; @@ -81,6 +79,3 @@ pub(crate) const LOG_DEGREE: u32 = 19; pub(crate) const DEFAULT_KECCAK_ROWS: usize = 12; // Adopted from keccak circuit pub(crate) const NUM_ROUNDS: usize = 24; - -#[cfg(test)] -mod tests; diff --git a/aggregator/src/public_input_aggregation/circuit.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs similarity index 71% rename from aggregator/src/public_input_aggregation/circuit.rs rename to aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs index 9c8ebd5755..23cf27a384 100644 --- a/aggregator/src/public_input_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs @@ -9,11 +9,9 @@ use halo2_proofs::{ use zkevm_circuits::util::{Challenges, SubCircuitConfig}; -use super::{ - batch::BatchHash, - chunk::ChunkHash, - config::{BatchCircuitConfig, BatchCircuitConfigArgs}, -}; +use crate::{core::assign_batch_hashes, BatchHash, ChunkHash}; + +use super::config::{BatchCircuitConfig, BatchCircuitConfigArgs}; /// BatchCircuit struct. /// @@ -168,8 +166,60 @@ impl Circuit for BatchHashCircuit { .load_aux_tables(&mut layouter)?; end_timer!(timer); - let timer = start_timer!(|| ("assign cells").to_string()); - config.assign(&mut layouter, challenges, &preimages)?; + let timer = start_timer!(|| ("assign cells").to_string()); + let (hash_input_cells, hash_output_cells) = assign_batch_hashes( + &config.keccak_circuit_config, + &mut layouter, + challenges, + &preimages, + )?; + end_timer!(timer); + + let timer = start_timer!(|| ("constraint public inputs").to_string()); + // ==================================================== + // Constraint the hash data matches the raw public input + // ==================================================== + { + for i in 0..32 { + // first_chunk_prev_state_root + layouter.constrain_instance( + hash_input_cells[2][4 + i].cell(), + config.hash_digest_column, + i, + )?; + // last_chunk_post_state_root + layouter.constrain_instance( + hash_input_cells.last().unwrap()[36 + i].cell(), + config.hash_digest_column, + i + 32, + )?; + // last_chunk_withdraw_root + layouter.constrain_instance( + hash_input_cells.last().unwrap()[68 + i].cell(), + config.hash_digest_column, + i + 64, + )?; + } + // batch_public_input_hash + for i in 0..4 { + for j in 0..8 { + // digest in circuit has a different endianness + layouter.constrain_instance( + hash_output_cells[0][(3 - i) * 8 + j].cell(), + config.hash_digest_column, + i * 8 + j + 96, + )?; + } + } + // last 4 inputs are the chain id + for i in 0..4 { + layouter.constrain_instance( + hash_input_cells[0][i].cell(), + config.hash_digest_column, + 128 + i, + )?; + } + } end_timer!(timer); Ok(()) } diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs new file mode 100644 index 0000000000..b31be5b180 --- /dev/null +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs @@ -0,0 +1,49 @@ +use eth_types::Field; +use snark_verifier_sdk::CircuitExt; + +use crate::BatchHashCircuit; + +impl CircuitExt for BatchHashCircuit { + fn num_instance(&self) -> Vec { + vec![self.instances()[0].len()] + } + + /// Compute the public inputs for this circuit. + fn instances(&self) -> Vec> { + let public_input = self.public_input(); + + let first_chunk_prev_state_root = public_input + .first_chunk_prev_state_root + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let last_chunk_post_state_root = public_input + .last_chunk_post_state_root + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let last_chunk_withdraw_root = public_input + .last_chunk_withdraw_root + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let batch_public_input_hash = public_input + .batch_public_input_hash + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)); + + let chain_id_bytes = public_input.chain_id.to_le_bytes(); + let chain_id = chain_id_bytes.iter().map(|x| F::from(*x as u64)); + + vec![first_chunk_prev_state_root + .chain(last_chunk_post_state_root) + .chain(last_chunk_withdraw_root) + .chain(batch_public_input_hash) + .chain(chain_id) + .collect()] + } +} diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/config.rs b/aggregator/src/proof_aggregation/public_input_aggregation/config.rs new file mode 100644 index 0000000000..94ccf30ec7 --- /dev/null +++ b/aggregator/src/proof_aggregation/public_input_aggregation/config.rs @@ -0,0 +1,67 @@ +use eth_types::Field; +use halo2_proofs::plonk::{Column, ConstraintSystem, Expression, Instance}; +use zkevm_circuits::{ + keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, + table::KeccakTable, + util::{Challenges, SubCircuitConfig}, +}; + +/// Config for BatchCircuit +#[derive(Clone, Debug)] +pub struct BatchCircuitConfig { + /// Instance column stores the aggregated rpi hash digest + pub(crate) hash_digest_column: Column, + + /// Keccak circuit config + pub(crate) keccak_circuit_config: KeccakCircuitConfig, +} + +/// Auxiliary arguments for BatchCircuit's Config +#[derive(Clone, Debug)] +pub struct BatchCircuitConfigArgs { + pub challenges: Challenges>, +} + +impl SubCircuitConfig for BatchCircuitConfig { + type ConfigArgs = BatchCircuitConfigArgs; + + /// Return a new BatchCircuitConfig + fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { + // hash configuration + let keccak_circuit_config = { + let keccak_table = KeccakTable::construct(meta); + + let keccak_circuit_config_args = KeccakCircuitConfigArgs { + keccak_table, + challenges: config_args.challenges, + }; + + KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + }; + + // The current code base is hardcoded for KeccakCircuit configured + // with 300 rows and 87 columns per hash call. + let columns = keccak_circuit_config.cell_manager.columns(); + + assert_eq!( + columns.len(), + 87, + "cell manager configuration does not match the hard coded setup" + ); + + // enabling equality for preimage and digest columns + meta.enable_equality(columns[6].advice); + // digest column + meta.enable_equality(columns.last().unwrap().advice); + + // Instance column stores the output of the hash + let hash_digest_column = meta.instance_column(); + // public input column + meta.enable_equality(hash_digest_column); + + BatchCircuitConfig { + hash_digest_column, + keccak_circuit_config, + } + } +} diff --git a/aggregator/src/proof_compression.rs b/aggregator/src/proof_compression.rs new file mode 100644 index 0000000000..5ed296ac86 --- /dev/null +++ b/aggregator/src/proof_compression.rs @@ -0,0 +1,14 @@ +//! Input a proof, a compression circuit generates a new proof that may have smaller size. +//! +//! It re-exposes same public inputs from the input snark. +//! All this circuit does is to reduce the proof size. + +/// Circuit implementation of compression circuit. +mod circuit; +/// CircuitExt implementation of compression circuit. +mod circuit_ext; +/// Config for compression circuit +mod config; + +pub use circuit::CompressionCircuit; +pub use config::CompressionConfig; diff --git a/aggregator/src/proof_compression/circuit.rs b/aggregator/src/proof_compression/circuit.rs new file mode 100644 index 0000000000..a98b1b50f6 --- /dev/null +++ b/aggregator/src/proof_compression/circuit.rs @@ -0,0 +1,221 @@ +//! Circuit implementation for compression circuit. + +use std::fs::File; + +use ark_std::{end_timer, start_timer}; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + halo2curves::bn256::{Fq, G1Affine}, + plonk::{Circuit, ConstraintSystem, Error}, +}; +use rand::Rng; +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{ + self, + halo2_proofs::{ + halo2curves::bn256::{Bn256, Fr}, + poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, + }, + Context, ContextParams, +}; +use snark_verifier::{ + loader::halo2::Halo2Loader, + pcs::kzg::{Bdfg21, Kzg, KzgAccumulator, KzgSuccinctVerifyingKey}, + util::arithmetic::fe_to_limbs, +}; +use snark_verifier_sdk::{ + halo2::aggregation::{aggregate, flatten_accumulator, Svk}, + NativeLoader, Snark, SnarkWitness, +}; + +use crate::{ + core::extract_accumulators_and_proof, + param::{ConfigParams, BITS, LIMBS}, +}; + +use super::config::CompressionConfig; + +/// Input a proof, this compression circuit generates a new proof that may have smaller size. +/// +/// It re-exposes same public inputs from the input snark. +/// All this circuit does is to reduce the proof size. +#[derive(Clone)] +pub struct CompressionCircuit { + pub(crate) svk: KzgSuccinctVerifyingKey, + pub(crate) snark: SnarkWitness, + /// whether this circuit compresses a fresh snark + pub(crate) is_fresh: bool, + /// instances, flattened. + /// It re-exposes same public inputs from the input snark. + /// If the previous snark is already a compressed, this flattened_instances will + /// exclude the previous accumulator. + pub(crate) flattened_instances: Vec, + // accumulation scheme proof, private input + pub(crate) as_proof: Value>, +} + +impl Circuit for CompressionCircuit { + type Config = CompressionConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + let flattened_instances = self + .snark + .instances + .iter() + .flat_map(|instance| instance.iter().map(|_| Fr::zero())) + .collect(); + + Self { + svk: self.svk, + snark: SnarkWitness::without_witnesses(&self.snark), + is_fresh: true, + flattened_instances, + as_proof: Value::unknown(), + } + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let path = std::env::var("VERIFY_CONFIG") + .unwrap_or_else(|_| "configs/verify_circuit.config".to_owned()); + let params: ConfigParams = serde_json::from_reader( + File::open(path.as_str()).unwrap_or_else(|_| panic!("{path:?} does not exist")), + ) + .unwrap(); + + Self::Config::configure(meta, params) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let witness_time = start_timer!(|| "synthesize | compression Circuit"); + config + .range() + .load_lookup_table(&mut layouter) + .expect("load range lookup table"); + let mut first_pass = halo2_base::SKIP_FIRST_PASS; + let mut instances = vec![]; + layouter.assign_region( + || "", + |region| { + if first_pass { + first_pass = false; + return Ok(()); + } + let ctx = Context::new( + region, + ContextParams { + max_rows: config.gate().max_rows, + num_context_ids: 1, + fixed_columns: config.gate().constants.clone(), + }, + ); + + let ecc_chip = config.ecc_chip(); + let loader = Halo2Loader::new(ecc_chip, ctx); + let (assigned_instances, acc) = aggregate::>( + &self.svk, + &loader, + &[self.snark.clone()], + self.as_proof(), + ); + + // instance of the compression circuit is defined as + // - accumulators + // - re-export the public input from snark + instances.extend( + flatten_accumulator(acc) + .iter() + .map(|assigned| assigned.cell()), + ); + // - if 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 + let skip = if self.is_fresh { 0 } else { 12 }; + instances.extend(assigned_instances.iter().flat_map(|instance_column| { + instance_column.iter().skip(skip).map(|x| x.cell()) + })); + + config.range().finalize(&mut loader.ctx_mut()); + + loader.ctx_mut().print_stats(&["Range"]); + Ok(()) + }, + )?; + + // Expose instances + for (i, cell) in instances.into_iter().enumerate() { + layouter.constrain_instance(cell, config.instance, i)?; + } + + end_timer!(witness_time); + Ok(()) + } +} + +impl CompressionCircuit { + /// Build a new circuit from a snark, with a flag whether this snark has been compressed before + pub fn new( + params: &ParamsKZG, + snark: Snark, + is_fresh: bool, + rng: impl Rng + Send, + ) -> Self { + let svk = params.get_g()[0].into(); + + let (accumulator, as_proof) = extract_accumulators_and_proof(params, &[snark.clone()], rng); + + // the instance for the outer circuit is + // - new accumulator, consists of 12 elements + // - inner circuit's instance, flattened (old accumulator is stripped out if exists) + // + // it is important that new accumulator is the first 12 elements + // as specified in CircuitExt::accumulator_indices() + let KzgAccumulator:: { lhs, rhs } = accumulator; + let acc_instances = [lhs.x, lhs.y, rhs.x, rhs.y] + .map(fe_to_limbs::) + .concat(); + // skip the old accumulator if exists + let skip = if is_fresh { 0 } else { 12 }; + let snark_instance = snark + .instances + .iter() + .flat_map(|instance| instance.iter().skip(skip)); + + let flattened_instances = acc_instances + .iter() + .chain(snark_instance) + .cloned() + .collect::>(); + + { + log::trace!("acc lhs: {:?}", lhs); + log::trace!("acc rhs: {:?}", rhs); + log::trace!("flattened instances:"); + for i in flattened_instances.iter() { + log::trace!("{:?}", i); + } + } + + Self { + svk, + snark: snark.into(), + is_fresh, + flattened_instances, + as_proof: Value::known(as_proof), + } + } + + pub fn succinct_verifying_key(&self) -> &Svk { + &self.svk + } + + pub fn snark(&self) -> &SnarkWitness { + &self.snark + } + + pub fn as_proof(&self) -> Value<&[u8]> { + self.as_proof.as_ref().map(Vec::as_slice) + } +} diff --git a/aggregator/src/proof_compression/circuit_ext.rs b/aggregator/src/proof_compression/circuit_ext.rs new file mode 100644 index 0000000000..94186a28f2 --- /dev/null +++ b/aggregator/src/proof_compression/circuit_ext.rs @@ -0,0 +1,36 @@ +//! CircuitExt implementation for compression circuit. + +use halo2_proofs::{halo2curves::bn256::Fr, plonk::Selector}; +use snark_verifier_sdk::CircuitExt; + +use crate::param::LIMBS; + +use super::circuit::CompressionCircuit; + +impl CircuitExt for CompressionCircuit { + fn num_instance(&self) -> Vec { + // [..lhs, ..rhs] + let acc_len = 4 * LIMBS; + // re-expose inner public input + let snark_pi_len: usize = self.snark.instances.iter().map(|x| x.len()).sum(); + + // if the snark is not fresh, the snark_pi already contains elements for the accumulator + vec![snark_pi_len + acc_len * self.is_fresh as usize] + } + + fn instances(&self) -> Vec> { + vec![self.flattened_instances.clone()] + } + + fn accumulator_indices() -> Option> { + // the accumulator are the first 12 cells in the instance + Some((0..4 * LIMBS).map(|idx| (0, idx)).collect()) + } + + fn selectors(config: &Self::Config) -> Vec { + config.gate().basic_gates[0] + .iter() + .map(|gate| gate.q_enable) + .collect() + } +} diff --git a/aggregator/src/proof_compression/config.rs b/aggregator/src/proof_compression/config.rs new file mode 100644 index 0000000000..8796e530c6 --- /dev/null +++ b/aggregator/src/proof_compression/config.rs @@ -0,0 +1,69 @@ +use halo2_proofs::{ + halo2curves::bn256::{Fq, Fr, G1Affine}, + plonk::{Column, ConstraintSystem, Instance}, +}; +use snark_verifier::loader::halo2::halo2_ecc::ecc::{BaseFieldEccChip, EccChip}; +use snark_verifier::loader::halo2::halo2_ecc::fields::fp::FpConfig; +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{ + gates::{flex_gate::FlexGateConfig, range::RangeConfig}, + utils::modulus, +}; + +use crate::param::{ConfigParams, BITS, LIMBS}; + +#[derive(Clone, Debug)] +/// Configurations for compression circuit +/// This config is hard coded for BN256 curve +pub struct CompressionConfig { + /// Non-native field chip configurations + pub base_field_config: FpConfig, + /// Instance for public input + pub instance: Column, +} + +impl CompressionConfig { + /// Build a configuration from parameters. + pub fn configure(meta: &mut ConstraintSystem, params: ConfigParams) -> Self { + assert!( + params.limb_bits == BITS && params.num_limbs == LIMBS, + "For now we fix limb_bits = {}, otherwise change code", + BITS + ); + let base_field_config = FpConfig::configure( + meta, + params.strategy, + ¶ms.num_advice, + ¶ms.num_lookup_advice, + params.num_fixed, + params.lookup_bits, + BITS, + LIMBS, + modulus::(), + 0, + params.degree as usize, + ); + + let instance = meta.instance_column(); + meta.enable_equality(instance); + + Self { + base_field_config, + instance, + } + } + + /// Range gate configuration + pub fn range(&self) -> &RangeConfig { + &self.base_field_config.range + } + + /// Flex gate configuration + pub fn gate(&self) -> &FlexGateConfig { + &self.base_field_config.range.gate + } + + /// Ecc gate configuration + pub fn ecc_chip(&self) -> BaseFieldEccChip { + EccChip::construct(self.base_field_config.clone()) + } +} diff --git a/aggregator/src/public_input_aggregation/circuit_ext.rs b/aggregator/src/public_input_aggregation/circuit_ext.rs deleted file mode 100644 index a02c740eb7..0000000000 --- a/aggregator/src/public_input_aggregation/circuit_ext.rs +++ /dev/null @@ -1,11 +0,0 @@ -use eth_types::Field; -use snark_verifier_sdk::CircuitExt; -use zkevm_circuits::util::SubCircuit; - -use crate::BatchHashCircuit; - -impl CircuitExt for BatchHashCircuit { - fn instances(&self) -> Vec> { - self.instance() - } -} diff --git a/aggregator/src/public_input_aggregation/config.rs b/aggregator/src/public_input_aggregation/config.rs deleted file mode 100644 index 15eff4ee59..0000000000 --- a/aggregator/src/public_input_aggregation/config.rs +++ /dev/null @@ -1,342 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use eth_types::Field; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Value}, - plonk::{Column, ConstraintSystem, Error, Expression, Instance}, -}; -use zkevm_circuits::{ - keccak_circuit::{ - keccak_packed_multi::multi_keccak, KeccakCircuitConfig, KeccakCircuitConfigArgs, - }, - table::{KeccakTable, LookupTable}, - util::{Challenges, SubCircuitConfig}, -}; - -use crate::{ - util::{assert_equal, capacity, get_indices}, - LOG_DEGREE, -}; - -/// Config for BatchCircuit -#[derive(Clone, Debug)] -pub struct BatchCircuitConfig { - /// Instance column stores the aggregated rpi hash digest - pub(crate) hash_digest_column: Column, - - /// Keccak circuit config - pub(crate) keccak_circuit_config: KeccakCircuitConfig, -} - -/// Auxiliary arguments for BatchCircuit's Config -#[derive(Clone, Debug)] -pub struct BatchCircuitConfigArgs { - pub challenges: Challenges>, -} - -impl SubCircuitConfig for BatchCircuitConfig { - type ConfigArgs = BatchCircuitConfigArgs; - - /// Return a new BatchCircuitConfig - fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { - // hash configuration - let keccak_circuit_config = { - let keccak_table = KeccakTable::construct(meta); - - let keccak_circuit_config_args = KeccakCircuitConfigArgs { - keccak_table, - challenges: config_args.challenges, - }; - - KeccakCircuitConfig::new(meta, keccak_circuit_config_args) - }; - - // The current code base is hardcoded for KeccakCircuit configured - // with 300 rows and 87 columns per hash call. - let columns = keccak_circuit_config.cell_manager.columns(); - - assert_eq!( - columns.len(), - 87, - "cell manager configuration does not match the hard coded setup" - ); - - // enabling equality for preimage and digest columns - meta.enable_equality(columns[6].advice); - // digest column - meta.enable_equality(columns.last().unwrap().advice); - - // Instance column stores the output of the hash - let hash_digest_column = meta.instance_column(); - // public input column - meta.enable_equality(hash_digest_column); - - BatchCircuitConfig { - hash_digest_column, - keccak_circuit_config, - } - } -} - -impl BatchCircuitConfig { - /// Input the hash input bytes, - /// assign the circuit for hash function, - /// return cells for the hash inputs and digests. - #[allow(clippy::type_complexity)] - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - challenges: Challenges>, - preimages: &[Vec], - ) -> Result< - ( - Vec>>, // input cells - Vec>>, // digest cells - ), - Error, - > { - let mut is_first_time = true; - let num_rows = 1 << LOG_DEGREE; - - let timer = start_timer!(|| ("multi keccak").to_string()); - let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; - end_timer!(timer); - - // extract the indices of the rows for which the preimage and the digest cells lie in - let (preimage_indices, digest_indices) = get_indices(preimages); - let mut preimage_indices_iter = preimage_indices.iter(); - let mut digest_indices_iter = digest_indices.iter(); - - let mut hash_input_cells = vec![]; - let mut hash_output_cells = vec![]; - - let mut cur_preimage_index = preimage_indices_iter.next(); - let mut cur_digest_index = digest_indices_iter.next(); - - layouter.assign_region( - || "assign keccak rows", - |mut region| { - if is_first_time { - is_first_time = false; - let offset = witness.len() - 1; - self.keccak_circuit_config - .set_row(&mut region, offset, &witness[offset])?; - return Ok(()); - } - // ==================================================== - // Step 1. Extract the hash cells - // ==================================================== - let mut current_hash_input_cells = vec![]; - let mut current_hash_output_cells = vec![]; - - let timer = start_timer!(|| "assign row"); - for (offset, keccak_row) in witness.iter().enumerate() { - let row = - self.keccak_circuit_config - .set_row(&mut region, offset, keccak_row)?; - - if cur_preimage_index.is_some() && *cur_preimage_index.unwrap() == offset { - current_hash_input_cells.push(row[6].clone()); - cur_preimage_index = preimage_indices_iter.next(); - } - if cur_digest_index.is_some() && *cur_digest_index.unwrap() == offset { - current_hash_output_cells.push(row.last().unwrap().clone()); - cur_digest_index = digest_indices_iter.next(); - } - - // we reset the current hash when it is finalized - // note that length == 0 indicate that the hash is a padding - // so we simply skip it - if keccak_row.is_final && keccak_row.length != 0 { - hash_input_cells.push(current_hash_input_cells); - hash_output_cells.push(current_hash_output_cells); - current_hash_input_cells = vec![]; - current_hash_output_cells = vec![]; - } - } - end_timer!(timer); - - // sanity: we have same number of hash input and output - let hash_num = hash_input_cells.len(); - let num_chunks = hash_num - 2; - assert!(hash_num == preimages.len()); - assert_eq!(hash_num, hash_output_cells.len()); - - // ==================================================== - // Step 2. Constraint the relations between hash preimages and digests - // ==================================================== - // - // 2.1 batch_data_hash digest is reused for public input hash - // - // 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 ) - for i in 0..4 { - for j in 0..8 { - // sanity check - assert_equal( - &hash_input_cells[0][i * 8 + j + 100], - &hash_output_cells[1][(3 - i) * 8 + j], - ); - region.constrain_equal( - // preimage and digest has different endianness - hash_input_cells[0][i * 8 + j + 100].cell(), - hash_output_cells[1][(3 - i) * 8 + j].cell(), - )?; - } - } - - // 2.2 batch_pi_hash used same roots as chunk_pi_hash - // - // batch_pi_hash = - // keccak( - // chain_id || - // chunk[0].prev_state_root || - // chunk[k-1].post_state_root || - // chunk[k-1].withdraw_root || - // batchData_hash ) - // - // chunk[i].piHash = - // keccak( - // chain id || - // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot - // || chunk[i].datahash) - for i in 0..32 { - // 2.2.1 chunk[0].prev_state_root - // sanity check - assert_equal(&hash_input_cells[0][i + 4], &hash_input_cells[2][i + 4]); - region.constrain_equal( - hash_input_cells[0][i + 4].cell(), - hash_input_cells[2][i + 4].cell(), - )?; - // 2.2.2 chunk[k-1].post_state_root - // sanity check - assert_equal( - &hash_input_cells[0][i + 36], - &hash_input_cells[hash_num - 1][i + 36], - ); - region.constrain_equal( - hash_input_cells[0][i + 36].cell(), - hash_input_cells[hash_num - 1][i + 36].cell(), - )?; - // 2.2.3 chunk[k-1].withdraw_root - assert_equal( - &hash_input_cells[0][i + 68], - &hash_input_cells[hash_num - 1][i + 68], - ); - region.constrain_equal( - hash_input_cells[0][i + 68].cell(), - hash_input_cells[hash_num - 1][i + 68].cell(), - )?; - } - - // 2.3 same dataHash is used for batchDataHash and chunk[i].piHash - // - // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) - // - // chunk[i].piHash = - // keccak( - // &chain id || - // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot - // || chunk[i].datahash) - for (i, chunk) in hash_input_cells[1].chunks(32).enumerate().take(num_chunks) { - for (j, cell) in chunk.iter().enumerate() { - // sanity check - assert_equal(cell, &hash_input_cells[2 + i][j + 100]); - region.constrain_equal( - cell.cell(), - hash_input_cells[2 + i][j + 100].cell(), - )?; - } - } - - // 2.4 chunks are continuous: they are linked via the state roots - for i in 0..num_chunks - 1 { - for j in 0..32 { - // sanity check - assert_equal( - &hash_input_cells[i + 3][4 + j], - &hash_input_cells[i + 2][36 + j], - ); - region.constrain_equal( - // chunk[i+1].prevStateRoot - hash_input_cells[i + 3][4 + j].cell(), - // chunk[i].postStateRoot - hash_input_cells[i + 2][36 + j].cell(), - )?; - } - } - - // 2.5 assert hashes uses a same chain id - for i in 0..num_chunks { - for j in 0..4 { - // sanity check - assert_equal(&hash_input_cells[0][j], &hash_input_cells[i + 2][j]); - region.constrain_equal( - // chunk[i+1].prevStateRoot - hash_input_cells[0][j].cell(), - // chunk[i].postStateRoot - hash_input_cells[i + 2][j].cell(), - )?; - } - } - - self.keccak_circuit_config - .keccak_table - .annotate_columns_in_region(&mut region); - self.keccak_circuit_config.annotate_circuit(&mut region); - Ok(()) - }, - )?; - - // ==================================================== - // Step 3. Constraint the hash data matches the raw public input - // ==================================================== - { - for i in 0..32 { - // first_chunk_prev_state_root - layouter.constrain_instance( - hash_input_cells[2][4 + i].cell(), - self.hash_digest_column, - i, - )?; - // last_chunk_post_state_root - layouter.constrain_instance( - hash_input_cells.last().unwrap()[36 + i].cell(), - self.hash_digest_column, - i + 32, - )?; - // last_chunk_withdraw_root - layouter.constrain_instance( - hash_input_cells.last().unwrap()[68 + i].cell(), - self.hash_digest_column, - i + 64, - )?; - } - // batch_public_input_hash - for i in 0..4 { - for j in 0..8 { - // digest in circuit has a different endianness - layouter.constrain_instance( - hash_output_cells[0][(3 - i) * 8 + j].cell(), - self.hash_digest_column, - i * 8 + j + 96, - )?; - } - } - // last 4 inputs are the chain id - for i in 0..4 { - layouter.constrain_instance( - hash_input_cells[0][i].cell(), - self.hash_digest_column, - 128 + i, - )?; - } - } - - Ok((hash_input_cells, hash_output_cells)) - } -} diff --git a/aggregator/src/public_input_aggregation/sub_circuit.rs b/aggregator/src/public_input_aggregation/sub_circuit.rs deleted file mode 100644 index e4a1475bcd..0000000000 --- a/aggregator/src/public_input_aggregation/sub_circuit.rs +++ /dev/null @@ -1,81 +0,0 @@ -use eth_types::Field; -use halo2_proofs::{ - circuit::{Layouter, Value}, - plonk::Error, -}; - -use super::{circuit::BatchHashCircuit, config::BatchCircuitConfig, LOG_DEGREE}; -use zkevm_circuits::{ - util::{Challenges, SubCircuit}, - witness::Block, -}; - -impl SubCircuit for BatchHashCircuit { - type Config = BatchCircuitConfig; - - fn new_from_block(_block: &Block) -> Self { - // we cannot instantiate a new Self from a single block - unimplemented!() - } - - /// Return the minimum number of rows required to prove the block - /// Row numbers without/with padding are both returned. - fn min_num_rows_block(_block: &Block) -> (usize, usize) { - (1 << LOG_DEGREE, 1 << LOG_DEGREE) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - let public_input = self.public_input(); - - let first_chunk_prev_state_root = public_input - .first_chunk_prev_state_root - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let last_chunk_post_state_root = public_input - .last_chunk_post_state_root - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let last_chunk_withdraw_root = public_input - .last_chunk_withdraw_root - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let batch_public_input_hash = public_input - .batch_public_input_hash - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let chain_id_bytes = public_input.chain_id.to_le_bytes(); - let chain_id = chain_id_bytes.iter().map(|x| F::from(*x as u64)); - - vec![first_chunk_prev_state_root - .chain(last_chunk_post_state_root) - .chain(last_chunk_withdraw_root) - .chain(batch_public_input_hash) - .chain(chain_id) - .collect()] - } - - /// Make the assignments to the BatchHashCircuit - fn synthesize_sub( - &self, - config: &Self::Config, - challenges: &Challenges>, - layouter: &mut impl Layouter, - ) -> Result<(), Error> { - // extract all the hashes and load them to the hash table - let preimages = self.extract_hash_preimages(); - - config.keccak_circuit_config.load_aux_tables(layouter)?; - config.assign(layouter, *challenges, &preimages)?; - - Ok(()) - } -} diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs new file mode 100644 index 0000000000..45e43e6ab5 --- /dev/null +++ b/aggregator/src/tests.rs @@ -0,0 +1,4 @@ +pub(crate) mod mock_chunk; +pub(crate) mod proof_aggregation; +pub(crate) mod proof_compression; +pub(crate) mod public_input_aggregation; diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs new file mode 100644 index 0000000000..f195621174 --- /dev/null +++ b/aggregator/src/tests/mock_chunk.rs @@ -0,0 +1,57 @@ +use ark_std::{end_timer, start_timer, test_rng}; +use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; +use snark_verifier_sdk::CircuitExt; +use snark_verifier_sdk::{ + gen_pk, + halo2::{gen_snark_shplonk, verify_snark_shplonk}, +}; + +use crate::{ChunkHash, LOG_DEGREE}; + +mod circuit; +mod circuit_ext; +mod config; + +#[derive(Debug, Default, Clone, Copy)] +/// A mock chunk circuit +pub struct MockChunkCircuit { + pub(crate) chunk: ChunkHash, +} + +#[test] +fn test_mock_chunk_prover() { + env_logger::init(); + + let mut rng = test_rng(); + + let param = gen_srs(LOG_DEGREE); + let circuit = MockChunkCircuit::random(&mut rng); + let instance = circuit.instances(); + + let mock_prover = MockProver::::run(LOG_DEGREE, &circuit, instance).unwrap(); + + mock_prover.assert_satisfied_par(); + + let timer = start_timer!(|| format!("key generation for k = {}", LOG_DEGREE)); + let pk = gen_pk(¶m, &circuit, None); + end_timer!(timer); + + let timer = start_timer!(|| "proving"); + let snark = gen_snark_shplonk(¶m, &pk, circuit, &mut rng, None::); + end_timer!(timer); + + log::trace!("{:?}", circuit.chunk.data_hash); + log::trace!("{:?}", circuit.chunk.public_input_hash()); + for (i, e) in snark.instances[0].iter().enumerate() { + log::trace!("{}-th: {:?}", i, e); + } + + let timer = start_timer!(|| "verifying"); + assert!(verify_snark_shplonk::( + ¶m, + snark, + pk.get_vk() + )); + end_timer!(timer); +} diff --git a/aggregator/src/tests/mock_chunk/circuit.rs b/aggregator/src/tests/mock_chunk/circuit.rs new file mode 100644 index 0000000000..a398d189c5 --- /dev/null +++ b/aggregator/src/tests/mock_chunk/circuit.rs @@ -0,0 +1,72 @@ +use ark_std::{end_timer, start_timer}; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + halo2curves::bn256::Fr, + plonk::{Circuit, ConstraintSystem, Error}, +}; +use zkevm_circuits::util::{Challenges, SubCircuitConfig}; + +use crate::ChunkHash; + +use super::{ + config::{MockChunkCircuitConfig, MockChunkCircuitConfigArgs}, + MockChunkCircuit, +}; + +impl MockChunkCircuit { + pub(crate) fn random(r: &mut R) -> Self { + Self { + chunk: ChunkHash::mock_chunk_hash(r), + } + } + + /// Public input hash for a given chunk is defined as + /// keccak( chain id || prev state root || post state root || withdraw root || data hash ) + fn extract_hash_preimages(&self) -> Vec { + self.chunk.extract_hash_preimage() + } +} + +impl Circuit for MockChunkCircuit { + type FloorPlanner = SimpleFloorPlanner; + + type Config = (MockChunkCircuitConfig, Challenges); + + fn without_witnesses(&self) -> Self { + Self::default() + } + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let challenges = Challenges::construct(meta); + let challenges_exprs = challenges.exprs(meta); + let args = MockChunkCircuitConfigArgs { + challenges: challenges_exprs, + }; + let config = MockChunkCircuitConfig::new(meta, args); + (config, challenges) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let (config, challenge) = config; + let challenges = challenge.values(&layouter); + + // extract all the hashes and load them to the hash table + let timer = start_timer!(|| ("extract hash").to_string()); + let preimages = self.extract_hash_preimages(); + end_timer!(timer); + + let timer = start_timer!(|| ("load aux table").to_string()); + config + .keccak_circuit_config + .load_aux_tables(&mut layouter)?; + end_timer!(timer); + + let timer = start_timer!(|| ("assign cells").to_string()); + config.assign(&mut layouter, challenges, &preimages)?; + end_timer!(timer); + Ok(()) + } +} diff --git a/aggregator/src/tests/mock_chunk/circuit_ext.rs b/aggregator/src/tests/mock_chunk/circuit_ext.rs new file mode 100644 index 0000000000..a731eeef59 --- /dev/null +++ b/aggregator/src/tests/mock_chunk/circuit_ext.rs @@ -0,0 +1,23 @@ +use halo2_proofs::halo2curves::bn256::Fr; +use snark_verifier_sdk::CircuitExt; + +use super::MockChunkCircuit; + +impl CircuitExt for MockChunkCircuit { + /// 64 elements from digest + fn num_instance(&self) -> Vec { + vec![64] + } + + /// return vec![data hash | public input hash] + fn instances(&self) -> Vec> { + vec![self + .chunk + .data_hash + .as_bytes() + .iter() + .chain(self.chunk.public_input_hash().as_bytes().iter()) + .map(|&x| Fr::from(x as u64)) + .collect()] + } +} diff --git a/aggregator/src/tests/mock_chunk/config.rs b/aggregator/src/tests/mock_chunk/config.rs new file mode 100644 index 0000000000..c1509c817e --- /dev/null +++ b/aggregator/src/tests/mock_chunk/config.rs @@ -0,0 +1,173 @@ +use ark_std::{end_timer, start_timer}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Value}, + halo2curves::bn256::Fr, + plonk::{Column, ConstraintSystem, Error, Expression, Instance}, +}; +use zkevm_circuits::{ + keccak_circuit::{ + keccak_packed_multi::multi_keccak, KeccakCircuitConfig, KeccakCircuitConfigArgs, + }, + table::KeccakTable, + util::{Challenges, SubCircuitConfig}, +}; + +use crate::{ + util::{capacity, get_indices}, + LOG_DEGREE, +}; + +/// Config for MockChunkCircuit +#[derive(Clone, Debug)] +pub struct MockChunkCircuitConfig { + /// Instance column stores the aggregated rpi hash digest + pub(crate) hash_digest_column: Column, + + /// Keccak circuit config + pub(crate) keccak_circuit_config: KeccakCircuitConfig, +} +/// Auxiliary arguments for BatchCircuit's Config +#[derive(Clone, Debug)] +pub struct MockChunkCircuitConfigArgs { + pub challenges: Challenges>, +} + +impl SubCircuitConfig for MockChunkCircuitConfig { + type ConfigArgs = MockChunkCircuitConfigArgs; + + /// Return a new BatchCircuitConfig + fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { + // hash configuration + let keccak_circuit_config = { + let keccak_table = KeccakTable::construct(meta); + + let keccak_circuit_config_args = KeccakCircuitConfigArgs { + keccak_table, + challenges: config_args.challenges, + }; + + KeccakCircuitConfig::new(meta, keccak_circuit_config_args) + }; + + // The current code base is hardcoded for KeccakCircuit configured + // with 300 rows and 87 columns per hash call. + let columns = keccak_circuit_config.cell_manager.columns(); + + assert_eq!( + columns.len(), + 87, + "cell manager configuration does not match the hard coded setup" + ); + + // enabling equality for preimage and digest columns + meta.enable_equality(columns[6].advice); + // digest column + meta.enable_equality(columns.last().unwrap().advice); + + // Instance column stores the output of the hash + let hash_digest_column = meta.instance_column(); + // public input column + meta.enable_equality(hash_digest_column); + + MockChunkCircuitConfig { + hash_digest_column, + keccak_circuit_config, + } + } +} + +impl MockChunkCircuitConfig { + /// Input the hash input bytes, + /// assign the circuit for hash function, + /// return cells for the hash inputs and digests. + #[allow(clippy::type_complexity)] + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + challenges: Challenges>, + preimages: &[u8], + ) -> Result< + Vec>, // digest cells + Error, + > { + let mut is_first_time = true; + let num_rows = 1 << LOG_DEGREE; + + let timer = start_timer!(|| ("multi keccak").to_string()); + let witness = multi_keccak(&[preimages.to_vec()], challenges, capacity(num_rows))?; + end_timer!(timer); + + // extract the indices of the rows for which the preimage and the digest cells lie in + let (preimage_indices, digest_indices) = get_indices(&[preimages.to_vec()]); + let mut preimage_indices_iter = preimage_indices.iter(); + let mut digest_indices_iter = digest_indices.iter(); + + let mut hash_input_cells = vec![]; + let mut hash_output_cells = vec![]; + + let mut cur_preimage_index = preimage_indices_iter.next(); + let mut cur_digest_index = digest_indices_iter.next(); + + layouter.assign_region( + || "assign keccak rows", + |mut region| { + if is_first_time { + is_first_time = false; + let offset = witness.len() - 1; + self.keccak_circuit_config + .set_row(&mut region, offset, &witness[offset])?; + return Ok(()); + } + // ==================================================== + // Step 1. Extract the hash cells + // ==================================================== + let timer = start_timer!(|| "assign row"); + for (offset, keccak_row) in witness.iter().enumerate() { + let row = + self.keccak_circuit_config + .set_row(&mut region, offset, keccak_row)?; + + if cur_preimage_index.is_some() && *cur_preimage_index.unwrap() == offset { + hash_input_cells.push(row[6].clone()); + cur_preimage_index = preimage_indices_iter.next(); + } + + if cur_digest_index.is_some() && *cur_digest_index.unwrap() == offset { + hash_output_cells.push(row.last().unwrap().clone()); + cur_digest_index = digest_indices_iter.next(); + } + } + end_timer!(timer); + + // sanity: hash output is 32 cells + assert_eq!(32, hash_output_cells.len()); + + Ok(()) + }, + )?; + + // ==================================================== + // Step 2. check the cells match the public input + // ==================================================== + // chunk's data hash + for i in 0..32 { + layouter.constrain_instance( + hash_input_cells[i + 100].cell(), + self.hash_digest_column, + i, + )?; + } + // chunk's public_input_hash + for i in 0..4 { + for j in 0..8 { + // digest in circuit has a different endianness + layouter.constrain_instance( + hash_output_cells[(3 - i) * 8 + j].cell(), + self.hash_digest_column, + i * 8 + j + 32, + )?; + } + } + Ok(hash_output_cells) + } +} diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs new file mode 100644 index 0000000000..679593d302 --- /dev/null +++ b/aggregator/src/tests/proof_aggregation.rs @@ -0,0 +1,162 @@ +use std::{fs, path::Path, process}; + +use ark_std::test_rng; +use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr, poly::commitment::Params}; +use itertools::Itertools; +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; +use snark_verifier_sdk::{ + gen_pk, + halo2::{gen_snark_shplonk, verify_snark_shplonk}, + CircuitExt, +}; + +use crate::{AggregationCircuit, ChunkHash, CompressionCircuit}; + +use super::mock_chunk::MockChunkCircuit; + +const CHUNKS_PER_BATCH: usize = 2; + +#[test] +fn test_aggregation_circuit() { + env_logger::init(); + + let process_id = process::id(); + + let dir = format!("data/{}", process_id); + let path = Path::new(dir.as_str()); + fs::create_dir(path).unwrap(); + + let k0 = 19; + let k1 = 25; + let k2 = 25; + let layer_2_params = gen_srs(k2); + + let mut rng = test_rng(); + let mut chunks = (0..CHUNKS_PER_BATCH) + .map(|_| ChunkHash::mock_chunk_hash(&mut rng)) + .collect_vec(); + for i in 0..CHUNKS_PER_BATCH - 1 { + chunks[i + 1].prev_state_root = chunks[i].post_state_root; + } + + // build layer 0 snarks + let layer_0_snarks = { + let layer_0_params = { + let mut params = layer_2_params.clone(); + params.downsize(k0); + params + }; + + let circuits = chunks + .iter() + .map(|&chunk| MockChunkCircuit { chunk }) + .collect_vec(); + log::trace!("finished layer 0 pk generation for circuit"); + let layer_0_pk = gen_pk( + &layer_0_params, + &circuits[0], + Some(&path.join(Path::new("layer_0.pkey"))), + ); + log::trace!("finished layer 0 pk generation for circuit"); + + let layer_0_snarks = circuits + .iter() + .enumerate() + .map(|(i, circuit)| { + let snark = gen_snark_shplonk( + &layer_0_params, + &layer_0_pk, + circuit.clone(), + &mut rng, + Some(&path.join(Path::new(format!("layer_0_{}.snark", i).as_str()))), + ); + log::trace!("finished {}-th snark", i); + snark + }) + .collect_vec(); + log::trace!("finished layer 0 snark generation for circuit"); + + // sanity checks + layer_0_snarks.iter().for_each(|snark| { + assert!(verify_snark_shplonk::( + &layer_0_params, + snark.clone(), + layer_0_pk.get_vk() + )) + }); + log::trace!("finished layer 0 snark verification"); + + layer_0_snarks + }; + + // build layer 1 the compression circuit + let layer_1_snarks = { + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + + let layer_1_params = { + let mut params = layer_2_params.clone(); + params.downsize(k1); + params + }; + + let compression_circuit = + CompressionCircuit::new(&layer_1_params, layer_0_snarks[0].clone(), true, &mut rng); + + let layer_1_pk = gen_pk( + &layer_1_params, + &compression_circuit, + None + ); + + log::trace!("finished layer 1 pk gen"); + let mut layer_1_snarks = vec![]; + + for (i, snark) in layer_0_snarks.iter().enumerate() { + let compression_circuit = + CompressionCircuit::new(&layer_1_params, snark.clone(), true, &mut rng); + + let layer_1_snark = gen_snark_shplonk( + &layer_1_params, + &layer_1_pk.clone(), + compression_circuit.clone(), + &mut rng, + Some(&path.join(Path::new(format!("layer_1_{}.snark", i).as_str()))), + ); + + log::trace!("finished layer 1 {}-th snark gen", i); + + log::trace!("{}-th compression circuit instance:", i); + for (i, e) in compression_circuit.instances()[0].iter().enumerate() { + log::trace!("{}-th {:?}", i, e,) + } + + layer_1_snarks.push(layer_1_snark) + } + layer_1_snarks + }; + + // build layer 2 the aggregation circuit + { + std::env::set_var("VERIFY_CONFIG", "./configs/aggregation.config"); + log::trace!("aggregation circuit"); + + let aggregation_circuit = + AggregationCircuit::new(&layer_2_params, &layer_1_snarks, rng, &chunks); + log::trace!("snark"); + for (i, snark) in aggregation_circuit.snarks.iter().enumerate() { + log::trace!("{:?} {:?}", i, snark.instances); + } + log::trace!("flattened instance"); + for (i, pi) in aggregation_circuit.flattened_instances.iter().enumerate() { + log::trace!("{:?} {:?}", i, pi); + } + + let instances = aggregation_circuit.instances(); + + log::trace!("start mock proving"); + let mock_prover = + MockProver::::run(k1, &aggregation_circuit, instances.clone()).unwrap(); + + mock_prover.assert_satisfied_par(); + } +} diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs new file mode 100644 index 0000000000..0afe50eb73 --- /dev/null +++ b/aggregator/src/tests/proof_compression.rs @@ -0,0 +1,317 @@ +use std::{fs, path::Path, process}; + +use ark_std::test_rng; +use halo2_proofs::{ + dev::MockProver, + halo2curves::bn256::{Bn256, Fr, G1Affine}, + plonk::verify_proof, + poly::{ + commitment::Params, + kzg::{multiopen::VerifierSHPLONK, strategy::AccumulatorStrategy}, + VerificationStrategy, + }, + transcript::TranscriptReadBuffer, +}; +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{halo2_proofs, utils::fs::gen_srs}; +use snark_verifier::{ + pcs::kzg::{Bdfg21, Kzg}, + system::halo2::transcript::evm::EvmTranscript, +}; +use snark_verifier_sdk::{ + evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier}, + gen_pk, + halo2::{gen_snark_shplonk, verify_snark_shplonk}, + CircuitExt, +}; + +use crate::{tests::mock_chunk::MockChunkCircuit, CompressionCircuit}; + +#[test] +fn test_proof_compression() { + env_logger::init(); + + let dir = format!("data/{}", process::id()); + let path = Path::new(dir.as_str()); + fs::create_dir(path).unwrap(); + + let k0 = 19; + let k1 = 23; + + let mut rng = test_rng(); + let layer_1_params = gen_srs(k1); + + // Proof for test circuit + let layer_0_snark = { + let layer_0_params = { + let mut params = layer_1_params.clone(); + params.downsize(k0); + params + }; + + let circuit = MockChunkCircuit::random(&mut rng); + let layer_0_pk = gen_pk( + &layer_0_params, + &circuit, + Some(&path.join(Path::new("layer_0.pkey"))), + ); + log::trace!("finished layer 0 pk generation for circuit"); + + let layer_0_snark = gen_snark_shplonk( + &layer_0_params, + &layer_0_pk, + circuit.clone(), + &mut rng, + Some(&path.join(Path::new("layer_0.snark"))), + ); + log::trace!("finished layer 0 snark generation for circuit"); + + assert!(verify_snark_shplonk::( + &layer_0_params, + layer_0_snark.clone(), + layer_0_pk.get_vk() + )); + + log::trace!("finished layer 0 snark verification"); + log::trace!("proof size: {}", layer_0_snark.proof.len()); + log::trace!( + "pi size: {}", + layer_0_snark + .instances + .iter() + .map(|x| x.len()) + .sum::() + ); + + log::trace!("layer 0 circuit instances"); + for (i, e) in circuit.instances()[0].iter().enumerate() { + log::trace!("{}-th public input: {:?}", i, e); + } + + layer_0_snark + }; + + // Layer 1 proof compression + { + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + + let compression_circuit = + CompressionCircuit::new(&layer_1_params, layer_0_snark, true, &mut rng); + let instances = compression_circuit.instances(); + + let mock_prover = + MockProver::::run(k1, &compression_circuit, instances.clone()).unwrap(); + + mock_prover.assert_satisfied_par(); + } +} + +#[test] +fn test_two_layer_proof_compression() { + env_logger::init(); + + let dir = format!("data/{}", process::id()); + let path = Path::new(dir.as_str()); + fs::create_dir(path).unwrap(); + + let k0 = 19; + let k1 = 23; + let k2 = 23; + + let mut rng = test_rng(); + let layer_2_params = gen_srs(k2); + + // Proof for test circuit + let layer_0_snark = { + let layer_0_params = { + let mut params = layer_2_params.clone(); + params.downsize(k0); + params + }; + + let circuit = MockChunkCircuit::random(&mut rng); + let layer_0_pk = gen_pk( + &layer_0_params, + &circuit, + Some(&path.join(Path::new("layer_0.pkey"))), + ); + log::trace!("finished layer 0 pk generation for circuit"); + + let layer_0_snark = gen_snark_shplonk( + &layer_0_params, + &layer_0_pk, + circuit.clone(), + &mut rng, + Some(&path.join(Path::new("layer_0.snark"))), + ); + log::trace!("finished layer 0 snark generation for circuit"); + + assert!(verify_snark_shplonk::( + &layer_0_params, + layer_0_snark.clone(), + layer_0_pk.get_vk() + )); + + log::trace!("finished layer 0 snark verification"); + log::trace!("proof size: {}", layer_0_snark.proof.len()); + log::trace!( + "pi size: {}", + layer_0_snark + .instances + .iter() + .map(|x| x.len()) + .sum::() + ); + + log::trace!("layer 0 circuit instances"); + for (i, e) in circuit.instances()[0].iter().enumerate() { + log::trace!("{}-th public input: {:?}", i, e); + } + + layer_0_snark + }; + + // Layer 1 proof compression + let layer_1_snark = { + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + + let layer_1_params = { + let mut params = layer_2_params.clone(); + params.downsize(k1); + params + }; + + let compression_circuit = + CompressionCircuit::new(&layer_1_params, layer_0_snark, true, &mut rng); + let instances = compression_circuit.instances(); + + log::trace!("layer 1 circuit instances"); + for (i, e) in compression_circuit.instances()[0].iter().enumerate() { + log::trace!("{}-th public input: {:?}", i, e); + } + + let layer_1_pk = gen_pk( + &layer_1_params, + &compression_circuit, + Some(&path.join(Path::new("layer_1.pkey"))), + ); + log::trace!("finished layer 1 pk generation"); + + let layer_1_proof = gen_evm_proof_shplonk( + &layer_1_params, + &layer_1_pk, + compression_circuit.clone(), + instances.clone(), + &mut rng, + ); + + log::trace!("finished layer 1 aggregation generation"); + log::trace!("proof size: {}", layer_1_proof.len()); + log::trace!("pi size: {:?}", compression_circuit.num_instance()); + + { + let mut transcript = + TranscriptReadBuffer::<_, G1Affine, _>::init(layer_1_proof.as_slice()); + let instances = instances + .iter() + .map(|instances| instances.as_slice()) + .collect::>(); + + let res = VerificationStrategy::<_, VerifierSHPLONK<_>>::finalize( + verify_proof::<_, VerifierSHPLONK<_>, _, EvmTranscript<_, _, _, _>, _>( + &layer_1_params, + layer_1_pk.get_vk(), + AccumulatorStrategy::new(&layer_1_params), + &[instances.as_slice()], + &mut transcript, + ) + .unwrap(), + ); + log::trace!("sanity check layer 1 proof: {}", res); + } + + // verify proof via EVM + let deployment_code = gen_evm_verifier::>( + &layer_1_params, + layer_1_pk.get_vk(), + compression_circuit.num_instance(), + Some(&path.join(Path::new("layer_1.sol"))), + ); + log::trace!("finished layer 1 bytecode generation"); + + evm_verify( + deployment_code, + compression_circuit.instances(), + layer_1_proof.clone(), + ); + log::trace!("layer 1 evm verification finished"); + + // build the snark for next layer + let layer_1_snark = gen_snark_shplonk( + &layer_1_params, + &layer_1_pk, + compression_circuit.clone(), + &mut rng, + Some(&path.join(Path::new("layer_1.snark"))), + ); + log::trace!("finished layer 1 snark generation for circuit"); + + assert!(verify_snark_shplonk::( + &layer_1_params, + layer_1_snark.clone(), + layer_1_pk.get_vk() + )); + layer_1_snark + }; + + // Layer 2 proof compression + { + std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); + + let compression_circuit = + CompressionCircuit::new(&layer_2_params, layer_1_snark, false, &mut rng); + + let instances = compression_circuit.instances(); + let mock_prover = + MockProver::::run(k2, &compression_circuit, instances.clone()).unwrap(); + + mock_prover.assert_satisfied_par(); + + log::trace!("layer 2 circuit instances"); + for (i, e) in compression_circuit.instances()[0].iter().enumerate() { + log::trace!("{}-th public input: {:?}", i, e); + } + let layer_2_pk = gen_pk( + &layer_2_params, + &compression_circuit, + Some(&path.join(Path::new("layer_2.pkey"))), + ); + log::trace!("finished layer 2 pk generation"); + + let layer_2_proof = gen_evm_proof_shplonk( + &layer_2_params, + &layer_2_pk, + compression_circuit.clone(), + instances.clone(), + &mut rng, + ); + + log::trace!("finished layer 2 aggregation generation"); + log::trace!("proof size: {}", layer_2_proof.len()); + + // verify proof via EVM + let deployment_code = gen_evm_verifier::>( + &layer_2_params, + layer_2_pk.get_vk(), + compression_circuit.num_instance(), + Some(&path.join(Path::new("layer_2.sol"))), + ); + log::trace!("finished layer 2 bytecode generation"); + + evm_verify( + deployment_code, + compression_circuit.instances(), + layer_2_proof.clone(), + ); + log::trace!("layer 2 evm verification finished"); + } +} diff --git a/aggregator/src/public_input_aggregation/tests.rs b/aggregator/src/tests/public_input_aggregation.rs similarity index 90% rename from aggregator/src/public_input_aggregation/tests.rs rename to aggregator/src/tests/public_input_aggregation.rs index 6b7946885c..650ef21bf7 100644 --- a/aggregator/src/public_input_aggregation/tests.rs +++ b/aggregator/src/tests/public_input_aggregation.rs @@ -1,11 +1,11 @@ use ark_std::{end_timer, start_timer, test_rng}; -use halo2_base::utils::fs::gen_srs; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; use snark_verifier_sdk::{ gen_pk, halo2::{gen_snark_shplonk, verify_snark_shplonk}, + CircuitExt, }; -use zkevm_circuits::util::SubCircuit; use crate::{BatchHashCircuit, LOG_DEGREE}; @@ -17,7 +17,7 @@ fn test_pi_aggregation_mock_prover() { let chunks_per_batch = 8; let circuit = BatchHashCircuit::::mock_batch_hash_circuit(&mut rng, chunks_per_batch); - let instance = circuit.instance(); + let instance = circuit.instances(); let mock_prover = MockProver::::run(LOG_DEGREE, &circuit, instance).unwrap(); From 2f6bc8e502a644f943a79fde3099e2a75ee8ae74 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 6 Jun 2023 12:51:55 -0400 Subject: [PATCH 11/41] [feat] scripts for tests --- aggregator/tests.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 aggregator/tests.sh diff --git a/aggregator/tests.sh b/aggregator/tests.sh new file mode 100644 index 0000000000..48bb03ad24 --- /dev/null +++ b/aggregator/tests.sh @@ -0,0 +1,6 @@ +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_mock_prover -- --nocapture 2>&1 | tee pi_mock.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_real_prover -- --nocapture 2>&1 | tee pi_real.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_proof_compression -- --nocapture 2>&1 | tee compression.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --nocapture 2>&1 | tee compression2.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_aggregation_circuit -- --nocapture 2>&1 | tee aggregation.log From 4d66cd9262e8e4aa2bc97cded523e2a1e53096c3 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 6 Jun 2023 12:57:57 -0400 Subject: [PATCH 12/41] [fix] figure --- Cargo.lock | 756 +++++++++++++++++----------- aggregator/figures/architecture.png | Bin 0 -> 104224 bytes 2 files changed, 465 insertions(+), 291 deletions(-) create mode 100644 aggregator/figures/architecture.png diff --git a/Cargo.lock b/Cargo.lock index b5b5c711b0..b8654070a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,15 +26,17 @@ version = "0.1.0" dependencies = [ "ark-std 0.4.0", "env_logger 0.10.0", - "eth-types", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "ethers-core", - "halo2-base", "halo2_proofs", + "itertools", "log", "rand", + "serde", + "serde_json", "snark-verifier", "snark-verifier-sdk", - "zkevm-circuits", + "zkevm-circuits 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", ] [[package]] @@ -61,13 +63,19 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -99,6 +107,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ + "colored", "num-traits", "rand", ] @@ -138,7 +147,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -217,9 +226,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -291,7 +300,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -414,9 +423,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bus-mapping" @@ -424,18 +433,18 @@ version = "0.1.0" dependencies = [ "ctor", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-providers", "ethers-signers", - "gadgets", + "gadgets 0.1.0", "halo2_proofs", "hex", "itertools", - "keccak256", + "keccak256 0.1.0", "lazy_static", "log", - "mock", + "mock 0.1.0", "once_cell", "poseidon-circuit", "pretty_assertions", @@ -449,6 +458,33 @@ dependencies = [ "url", ] +[[package]] +name = "bus-mapping" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "ethers-core", + "ethers-providers", + "ethers-signers", + "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2_proofs", + "hex", + "itertools", + "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "lazy_static", + "log", + "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "once_cell", + "poseidon-circuit", + "rand", + "revm-precompile", + "serde", + "serde_json", + "strum", + "strum_macros", +] + [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -469,9 +505,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytecheck" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" dependencies = [ "bytecheck_derive", "ptr_meta", @@ -480,9 +516,9 @@ dependencies = [ [[package]] name = "bytecheck_derive" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ "proc-macro2", "quote", @@ -536,13 +572,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time", "wasm-bindgen", @@ -563,22 +599,22 @@ name = "circuit-benchmarks" version = "0.1.0" dependencies = [ "ark-std 0.3.0", - "bus-mapping", + "bus-mapping 0.1.0", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers", "ethers-signers", "halo2_proofs", "itertools", - "keccak256", + "keccak256 0.1.0", "log", - "mock", + "mock 0.1.0", "rand", "rand_chacha", "rand_xorshift", "tokio", "url", - "zkevm-circuits", + "zkevm-circuits 0.1.0", ] [[package]] @@ -672,7 +708,7 @@ dependencies = [ "bincode", "bs58", "coins-core", - "digest 0.10.6", + "digest 0.10.7", "getrandom", "hmac 0.12.1", "k256", @@ -709,7 +745,7 @@ dependencies = [ "base64 0.12.3", "bech32", "blake2", - "digest 0.10.6", + "digest 0.10.7", "generic-array 0.14.7", "hex", "ripemd", @@ -981,9 +1017,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" dependencies = [ "csv-core", "itoa", @@ -1172,9 +1208,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", @@ -1204,9 +1240,9 @@ dependencies = [ [[package]] name = "dlib" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ "libloading", ] @@ -1256,7 +1292,7 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "digest 0.10.6", + "digest 0.10.7", "ff", "generic-array 0.14.7", "group", @@ -1299,7 +1335,7 @@ checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -1349,7 +1385,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1370,7 +1406,7 @@ checksum = "6f65b750ac950f2f825b36d08bef4cda4112e19a7b1a68f6e2bb499413e12440" dependencies = [ "aes", "ctr", - "digest 0.10.6", + "digest 0.10.7", "hex", "hmac 0.12.1", "pbkdf2 0.11.0", @@ -1381,7 +1417,7 @@ dependencies = [ "sha2 0.10.6", "sha3 0.10.8", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -1409,6 +1445,32 @@ dependencies = [ "uint", ] +[[package]] +name = "eth-types" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "ethers-core", + "ethers-signers", + "halo2_proofs", + "hex", + "itertools", + "lazy_static", + "libsecp256k1", + "num", + "num-bigint", + "poseidon-circuit", + "regex", + "serde", + "serde_json", + "serde_with", + "sha3 0.10.8", + "strum", + "strum_macros", + "subtle", + "uint", +] + [[package]] name = "ethabi" version = "17.2.0" @@ -1683,8 +1745,20 @@ dependencies = [ name = "external-tracer" version = "0.1.0" dependencies = [ - "eth-types", - "geth-utils", + "eth-types 0.1.0", + "geth-utils 0.1.0", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "external-tracer" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "geth-utils 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "log", "serde", "serde_json", @@ -1835,9 +1909,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -1936,7 +2010,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -1989,7 +2063,7 @@ name = "gadgets" version = "0.1.0" dependencies = [ "digest 0.7.6", - "eth-types", + "eth-types 0.1.0", "halo2_proofs", "rand", "rand_xorshift", @@ -1997,6 +2071,18 @@ dependencies = [ "strum", ] +[[package]] +name = "gadgets" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "digest 0.7.6", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2_proofs", + "sha3 0.7.3", + "strum", +] + [[package]] name = "generic-array" version = "0.9.1" @@ -2034,11 +2120,21 @@ dependencies = [ "log", ] +[[package]] +name = "geth-utils" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "env_logger 0.9.3", + "gobuild", + "log", +] + [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2085,9 +2181,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -2116,7 +2212,21 @@ dependencies = [ "ff", "halo2_proofs", "itertools", - "jemallocator", + "num-bigint", + "num-integer", + "num-traits", + "rand_chacha", + "rustc-hash", +] + +[[package]] +name = "halo2-base" +version = "0.2.2" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#a80505265f36f87a2df40edc22ea468ea2068fb5" +dependencies = [ + "ff", + "halo2_proofs", + "itertools", "num-bigint", "num-integer", "num-traits", @@ -2131,7 +2241,26 @@ source = "git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-ve dependencies = [ "ff", "group", - "halo2-base", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "rand", + "rand_chacha", + "rand_core", + "serde", + "serde_json", +] + +[[package]] +name = "halo2-ecc" +version = "0.2.2" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#a80505265f36f87a2df40edc22ea468ea2068fb5" +dependencies = [ + "ff", + "group", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", "itertools", "num-bigint", "num-integer", @@ -2146,7 +2275,7 @@ dependencies = [ [[package]] name = "halo2-mpt-circuits" version = "0.1.0" -source = "git+https://github.com/scroll-tech/mpt-circuit.git?branch=scroll-dev-0411#da63fcc93077a43f44eadb2e1eff20d66b693d8c" +source = "git+https://github.com/scroll-tech/mpt-circuit.git?branch=scroll-dev-0411#ea87f9542097f03773a3a63f7b793141689afe41" dependencies = [ "halo2_proofs", "hex", @@ -2188,7 +2317,7 @@ dependencies = [ [[package]] name = "halo2curves" version = "0.3.1" -source = "git+https://github.com/scroll-tech/halo2curves.git?branch=0.3.1-derive-serde#c0ac1935e5da2a620204b5b011be2c924b1e0155" +source = "git+https://github.com/scroll-tech/halo2curves.git?branch=0.3.1-derive-serde#969f1e44d9713ee4cd552563bd0c762c5d53b56e" dependencies = [ "ff", "group", @@ -2315,7 +2444,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -2335,7 +2464,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2404,15 +2533,15 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" dependencies = [ "http", "hyper", - "rustls", + "rustls 0.21.1", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.0", ] [[package]] @@ -2446,9 +2575,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -2533,15 +2662,15 @@ dependencies = [ name = "integration-tests" version = "0.1.0" dependencies = [ - "bus-mapping", + "bus-mapping 0.1.0", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers", "halo2_proofs", "hex", "lazy_static", "log", - "mock", + "mock 0.1.0", "paste", "pretty_assertions", "rand_chacha", @@ -2552,18 +2681,18 @@ dependencies = [ "strum", "tokio", "url", - "zkevm-circuits", + "zkevm-circuits 0.1.0", ] [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2581,7 +2710,7 @@ dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2599,26 +2728,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" -[[package]] -name = "jemalloc-sys" -version = "0.5.3+5.3.0-patched" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9bd5d616ea7ed58b571b2e209a65759664d7fb021a0819d7a790afc67e47ca1" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "jemallocator" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2514137880c52b0b4822b563fadd38257c1f380858addb74a400889696ea6" -dependencies = [ - "jemalloc-sys", - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -2627,9 +2736,9 @@ checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] @@ -2661,7 +2770,7 @@ name = "keccak256" version = "0.1.0" dependencies = [ "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "halo2_proofs", "itertools", "lazy_static", @@ -2673,6 +2782,21 @@ dependencies = [ "rand", ] +[[package]] +name = "keccak256" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "env_logger 0.9.3", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2_proofs", + "itertools", + "lazy_static", + "log", + "num-bigint", + "num-traits", +] + [[package]] name = "lalrpop" version = "0.19.12" @@ -2715,18 +2839,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" dependencies = [ "cfg-if 1.0.0", - "winapi", + "windows-sys", ] [[package]] @@ -2785,15 +2909,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -2801,12 +2925,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "maingate" @@ -2828,7 +2949,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -2864,24 +2985,38 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "mock" version = "0.1.0" dependencies = [ - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-signers", - "external-tracer", + "external-tracer 0.1.0", + "itertools", + "lazy_static", + "rand", + "rand_chacha", +] + +[[package]] +name = "mock" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "ethers-core", + "ethers-signers", + "external-tracer 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "itertools", "lazy_static", "rand", @@ -2892,9 +3027,9 @@ dependencies = [ name = "mpt-zktrie" version = "0.1.0" dependencies = [ - "bus-mapping", + "bus-mapping 0.1.0", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "halo2-mpt-circuits", "halo2_proofs", "hex", @@ -2906,6 +3041,22 @@ dependencies = [ "zktrie", ] +[[package]] +name = "mpt-zktrie" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2-mpt-circuits", + "halo2_proofs", + "hex", + "lazy_static", + "log", + "num-bigint", + "zktrie", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -3022,9 +3173,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" @@ -3103,7 +3254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -3115,22 +3266,22 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] @@ -3207,7 +3358,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3216,7 +3367,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "hmac 0.12.1", "password-hash 0.4.2", "sha2 0.10.6", @@ -3224,9 +3375,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" @@ -3258,7 +3409,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -3338,22 +3489,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -3578,9 +3729,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -3607,9 +3758,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -3696,6 +3847,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -3703,19 +3863,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.1", + "regex-syntax 0.7.2", ] [[package]] @@ -3726,9 +3886,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "rend" @@ -3741,11 +3901,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -3762,13 +3922,13 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.1", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.0", "tower-service", "url", "wasm-bindgen", @@ -3883,28 +4043,31 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "rkyv" -version = "0.7.41" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ + "bitvec 1.0.1", "bytecheck", "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", + "tinyvec", + "uuid 1.3.3", ] [[package]] name = "rkyv_derive" -version = "0.7.41" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ "proc-macro2", "quote", @@ -4011,7 +4174,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4026,13 +4189,35 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + [[package]] name = "rustls-pemfile" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", ] [[package]] @@ -4220,7 +4405,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -4276,7 +4461,7 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4312,7 +4497,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4345,7 +4530,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -4355,7 +4540,7 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core", ] @@ -4395,12 +4580,12 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snark-verifier" version = "0.1.0" -source = "git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323#a3d0a5ab48522bc533686da3ea8400282c91f536" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323#ae6a9ef1ba0f5296f98cd6ba2a94f791278be851" dependencies = [ "bytes", "ethereum-types 0.14.1", - "halo2-base", - "halo2-ecc", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", + "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", "hex", "itertools", "lazy_static", @@ -4419,12 +4604,12 @@ dependencies = [ [[package]] name = "snark-verifier-sdk" version = "0.0.1" -source = "git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323#a3d0a5ab48522bc533686da3ea8400282c91f536" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323#ae6a9ef1ba0f5296f98cd6ba2a94f791278be851" dependencies = [ "bincode", "env_logger 0.10.0", "ethereum-types 0.14.1", - "halo2-base", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", "hex", "itertools", "lazy_static", @@ -4563,9 +4748,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -4614,21 +4799,21 @@ name = "testool" version = "0.1.0" dependencies = [ "anyhow", - "bus-mapping", + "bus-mapping 0.1.0", "clap 3.2.25", "ctor", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-signers", - "external-tracer", + "external-tracer 0.1.0", "glob", "halo2_proofs", "handlebars", "hex", - "keccak256", + "keccak256 0.1.0", "log", - "mock", + "mock 0.1.0", "once_cell", "prettytable-rs", "rand", @@ -4643,7 +4828,7 @@ dependencies = [ "toml", "urlencoding", "yaml-rust", - "zkevm-circuits", + "zkevm-circuits 0.1.0", ] [[package]] @@ -4678,7 +4863,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -4728,9 +4913,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", @@ -4740,7 +4925,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4751,7 +4936,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -4760,11 +4945,21 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", + "rustls 0.20.8", "tokio", "webpki", ] +[[package]] +name = "tokio-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +dependencies = [ + "rustls 0.21.1", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.17.2" @@ -4773,9 +4968,9 @@ checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" dependencies = [ "futures-util", "log", - "rustls", + "rustls 0.20.8", "tokio", - "tokio-rustls", + "tokio-rustls 0.23.4", "tungstenite", "webpki", "webpki-roots", @@ -4806,15 +5001,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "toml_datetime", @@ -4847,7 +5042,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -4894,7 +5089,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls", + "rustls 0.20.8", "sha-1", "thiserror", "url", @@ -4934,9 +5129,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -4967,9 +5162,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -4998,6 +5193,12 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" + [[package]] name = "version_check" version = "0.9.4" @@ -5038,9 +5239,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -5048,24 +5249,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.35" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5075,9 +5276,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5085,22 +5286,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "wasm-timer" @@ -5119,9 +5320,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.62" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5189,16 +5390,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -5207,22 +5399,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -5231,93 +5408,51 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" @@ -5417,27 +5552,27 @@ name = "zkevm-circuits" version = "0.1.0" dependencies = [ "array-init", - "bus-mapping", + "bus-mapping 0.1.0", "cli-table", "criterion", "ctor", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-signers", - "gadgets", - "halo2-base", - "halo2-ecc", + "gadgets 0.1.0", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", + "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", "halo2_proofs", "hex", "itertools", - "keccak256", + "keccak256 0.1.0", "lazy_static", "libsecp256k1", "log", "maingate", - "mock", - "mpt-zktrie", + "mock 0.1.0", + "mpt-zktrie 0.1.0", "num", "num-bigint", "once_cell", @@ -5456,6 +5591,45 @@ dependencies = [ "subtle", ] +[[package]] +name = "zkevm-circuits" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +dependencies = [ + "array-init", + "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "env_logger 0.9.3", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "ethers-core", + "ethers-signers", + "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", + "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", + "halo2_proofs", + "hex", + "itertools", + "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "lazy_static", + "libsecp256k1", + "log", + "maingate", + "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "mpt-zktrie 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "num", + "num-bigint", + "once_cell", + "rand", + "rand_chacha", + "rand_xorshift", + "rayon", + "sha3 0.10.8", + "snark-verifier", + "snark-verifier-sdk", + "strum", + "strum_macros", + "subtle", +] + [[package]] name = "zktrie" version = "0.1.2" diff --git a/aggregator/figures/architecture.png b/aggregator/figures/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..d5d89ef43291a5b648664cce9806756727cbe736 GIT binary patch literal 104224 zcmeFZbyQSs7%z%}fd~dlhYE@`NJ~jdH%N<0OEVxH3Me5V-9y7rlF}+70@5Wd(%l_% zo~_?GYn^+~{rj$U*L8hseWSBs_I~4ee)Y~fB?YO=cw~4uI5?N3AB(Ht;NYTgaLzot za1Q>3#gst={&&`1R9fu<{BgVR;w}80)Is8jgQ~T$gOj115srzKwWSfW{c}4bBP)AT zYlqb{^+IqFGjfrbospq~nY9&-nwg~$j+!ZohK-#@24zLV!N&1`hK-w_?EyaDw#GI5;#o(&CTQUL`DzI=#AKbbwv&xIcd@LgdP+FP#@DaebbOh>JgJA=K3C zRTp`Ii;Jr_9KiUtIKaq|I65`;6Q)}u^u5Dba-&F^uxl42_@y5|R*2m{{=y?wd=X6- zn_xdyxX4eOkhA-M?!j!5Nm5f(SeW4gM{MT(kfyZI(9qVzKI>Pt`qJSI6Ggn?;gjpH zUQ`SlRSf47WPJOi%i{ay%@5-eElQbpYc91dZEfl=UZnS-BVwNND!fJqu|&(F(P#~S z*{1Ml#;}sZb{&L5u5ttrNJAwAx;_wP3&bg z?>g#?&tv{y>{!mplAcj#I}*GcHMw=BH z`wSzppVHqsqHfB;wpO1q6h2LF_Qj^{f*;4}6EFL#^yEGhx=iml_{Bs;3+S)F9$<_e zF!(=gfb&+26tKJ%vK~Ce^1}PmGcybv9Nh~_GIGfMDM?1R-sl{_Z(w^t;+LS1g)H^x zR=OU}8)RhWD0;f@%Z}%_xbBdwjKk;Q`?>b!uZ2Zbi2r+e zbU|A0zn5!wXi=yBz5W~$`u}wC*~BD4SM$*-6fuRc@lO)@7`BPrff9##mE)7$`uc_G z|9;u*o&Rkw`Gk+QqY3}LhZQ%z^yoepm%7uqdqo_VMdriR8dU?&<89n5L7U%R=Lo6t zZZd1rv$HEteh>aIyu1_K(cIiM*Ok^+ZmCoDa!}>Wc>*Q}K5CCWw9D>F{?gJ*N`8mO zAp#q3wJWUh$*pUY&z!?oGB!5;7=8WUCwe!Tp>Vuz2<`noQ@h=-ldGEbK8jf<^Ip;` zQH(~uN~PWOhP(-hpC18bZoE~MbYbmITz5~;_R(g?;;7SjU$$}@t@LD5$UuQ!_11b5 zHAd7IkKg9Ei1)VpcAsV(`*S~`<1JMQNy&vllVo*KQJ=dC3E41dmSZ)QpR4{2sn=H} zRpLkUNv=@CjN6EA+{h4eKY(8>sa^;wIIg}n0gda@CCG+v)$AFNK0eD_6`x&{) zQLE{xDbxNuEwTs3K??(gg`Yn0mYMgp@YJ^%tJ>KW>wD}Q)$WeD=4zMa(l#${w-}e) z7ZA{e-?A`NW|0#~`ELkLg40=iRPwdUHrxy0@w>ab@#HF;R`vTZnyd!oHJ8aaLz^bM zzPvt<`u+J7U!p=|QxkDj(#VMJWlDavT9<8=*XJ&~H6(i+m@(^BsU06|_Z;nSPOrZT z$a(Sa11>nwqOL~B^)Ri~7Pa@VM7`v~7vqj!_L+2A8Of=0KV0Zh6mE;=gdr@iJKB&H zJeW&^|3Vzb-FNTbyEn@r``U*N+`kNiz>Go&4nf8ezHG1RO?z%1!MBG3Z^65ZGXdMGMLBV zV9P8cBg5=x4Ew@(eZ8otC=n@Xj()9++16Z_jJ$kbj%rq^?d120GP7JY1oerI4tyz0fCHGxBe+);tw3XK?b?7BGbieVPV+9m3eW#e!;u z>-QAY92^|H=bg#9Z|+h&Jw;m7Aqxd!zf8?OY+ zhs*P?UBAA`e29os3WwQS*1k=xX`eA%-_Wmc;-$DLrD7*Q6|&^$ineS%hZo(N0p0%gaP78LXSa2?{2k z^&j|5oG_ELH$MF&)z;FYA})S*KtlK?(y&MBaSu>kZ1_edCdAs6_muu`1r5g0*WaIb z(eBJH-r0XofOG2Y|95(Hg2s8AC)1z3&uIA5Narf2`SsoU_moH_hK2gjsUXSo#2M2N zN-KD{RN^v+_Rk|Y^Y0Q5pBdsSrRD<#mZ%Hi>04V47_onE{9gc#6%I^^KOuGh!S-Tg zLc&WFZxjHtp?c5yC>Fik=S}amC<*@kpwi373+G}Q{0T9N$wFj2RtYVUjGD%AB=qv} zJk}c3j>}sU{?rUYLi#rt)Qb3N|NT(PuTEuc9BFxZ^+NqxPNx-J*cnX=HcTI%emPyc zKNGKA=T^y}mQC|_HC@3pFxXZ*t@fX%c1wZ9tWAkvYWO2VB0Y*TVOQy}JL+W75^?`g ze5at>UQrSho_xJ(?m6?>0)zUoh5wM8l8mXxEfFSzI$fxoD#PWL`2>v|t2LYNkx~wY z5+inB9lPJ*xz+w~)%4_O9|kEg$!$Ygx;uvb`BpOaNb34sIVI=;oQ7Y|N_W>dZ}w&> zQkR)_iRx83bP*f4g;_g4F#g%eTrrXgl_*Clgo+>ThW0))vpmvdHfP%U@^yIHl7+{4 zI?!VNTR-lEWTO`QtJc4hb3OfxV`OX`)qV5dlU-ox?Y(>jN?AYl&=cFSz{d1EE)(i{ z8llkMXTSz}t7EmOjVbBlgT+GJID;5&%ebL(OAKq>p6GN-DUZ%%q^D<__vONDhX|j1a@k(Ucz6Atc}J3< z%T|{ZY7{k)BN2ELxi~U3GzH#bU}ThqiQUQnuR%8k##Nnq2DkHQVCwU}Tu<+OPu6P} za-#1OHgk={25N0_T<*h}mc!-dohjZ>5FL8td2*gL1|Um`c78HMxc_;GVCK&9Fe5j& z#=Cd#iiejEc80C#qzQ$Oic(WkdrQq!QLAHmmxx&+Xklj$zT}3@ja;l(?O2xi`QLaw z@`Ag#&5FLcZw94ZvB2uxbXhrN@!1W@;?yd6)w_` zd*NwVSlE6YD>OIj+U=-Jx!6?LCbHrDPAf~~tJ^M@^xhki$jHg{Zq4^xqBN*>v~=H} zjy`|_zSZG*qDt*~^ik+!?>QCcznhQ1i8H`C?KNsu|5S9xZQSE9zsh0$kL;YWiOJ%x zkI(DYp@nzD2wnO~4AVI^^#Yna5vdp;jBUUW&C8A!P`@>muKoLMy7U(ATsS1~oswsymw9CJBuyl{uD)AHj-XMesNm{XKscRjL_|b7-0&hbc25f3gS6S98s`#z)Ixep42j_Sx9h<0wu9@Ti#hDA83t4E zsUS@co`TDAh_mtAH`DHPX%~P?r~y5vW=^DSN~KAKMExK76!8!@>%ZqNNLq&Bo|$fc z5tmt`Z!O~V1`u<)ePP7mYuA-ZsXJ2d)#AA=S)8c~2N>hqW_XO40t`vayVKwG7wBQ2 zW*z!fk6Q61wx3S)ZHwnoRLxRQH#axGFQwfXdP{8U+jT|Vt{OkXAIpx6OiXz(X$%i# z{_sNQI#{V*b=aCyPUN)}Z3>|tX#Q{y1t?(Dc{UNoN7LIG8rs;a$;->onLmH(>{OCZ zld~h$}{hW2+-_8;~ZXS5^z3;mziC?+( zcmdD2TghkM>d}I+T36yKK^7iKV9(HNWrwJuOA1n-F|8~ss|5jeL4M0u-%FQ*7|oOQ zOkH)&f;8dso0FYrF?T|*jgut(c_GHurXsVr;=z}ir5yo7RCFIZ@P?`24h-DZeYs!$ zN`V2vsAS0G>Q)x#YUJmhyG)jCH{J5c7Hb@rNkQO`&=AjRsoHCNe0+G9FJGSHn9(B! z;*=2Ry`{4t3V48mK+1bu#rrE8KEh~j-~RfS(Ifl4s^g3xfEsK;i~$1%El(n9IHoN91stpOGR+QlX0ZweH)Aq$ zU?WIKNZjY=*H%_mj&@6p&%9qfs-M}SsuF;KpI(?v_Utb*Z1^WLxbyHSWT`@Z@hxa& z!v~nP>eC!~snLW)6219r*)fvUKxo{}8N2IJ%kOdY3cU$>#N5x=2aPw=3`zLyXMW;d zcfY5Y#N<%d8qB`RTbr!})M3s>zzLc)KU_*xPc4q#vYAfu3LrGw7AVcODhhwP3`4NrU886S?k?w z&D*P06hDlnPMB@Z+L2mv>!9m1PQUA!$DebLxfj}skhiD2AX-$#>|QDQ)M^UY>{Du7 z6;;T+l#)5;M1}p<>-p8IbvkL1!AO^5b*2hwnv^x)f7rm*`^olszCpc^fxO;7oOW8V zbf!HKb6+(x;$2)7P@_&Lk&%&+bRkW5qyqv2Re`&yXDLuZ|})-N$Y>*548}f(q)OwB<-|5 zr#rVy;0fEa{N-Tk&!2LzH&kN!-8LUqC;BxLQMKDQEkdGB4Oe7yPI+C+GJ zt<|fUpQH%720+riGFn~cvTYtr`A~!Mp`)5V0cCHcy-DDm9YQF08h{RZOH4XWPXexn zb^qd#8jcb_umd2B%+d?JIUi_mXjeBZJ1%A=d#J!fo;lc7g%U*52{gDRbZyNB`V$5= zOC9xOcjLC4~TIJe0t`4RTTv@ zz6XG%$86Fm&be1bLE?aZxw{I^X;4~mOSUp+DJ&)ib@-_|*ofljn0zEqwxnS+< zCD=HCaz;JHBl}>EG2dl$05#PG!0u?vM$&efl0mnr7g!-?(5CFbWV7 zot5Mw6B`?=;eQ(E(HBm6;5dxT%>VuD5p{T#2+qQ2bp@b}EcWN$=X!HgHT+dyay)pz z<+$|3a;Q{V>OI9rehQomc(5%2_?3I?R*$cXAUux#4v3eh0EIm=|5b$6ci&bAz{?Go zKvO~CJ=LqMv~&anKiUAu-5){1eD;RPGy zQGiih=|qS|qM)k=vcUV$x@E7=6PmA#R93;l;{w$K)9{FN4SMI^wJ*NzR33YVNSnNP z@gi~yaomRE|4J1RY3a`(&ujJB>l7lo zsHm#u6HMpI#dFIO_=_DM?rJ(Xl=e-0R{Zw;I|D4;?#geA(DN=OTFrN7)M0m*1~+|L z0YGV?8+f1{1_cFS-fr>0pdvjUhM&uB>apPF&j*dgy1&hNDF`l82|NJ}kA{i}`RtST zFgCvtg{gX_O139Uv4TH>F)A`L0|Dr;d$#*h&$c@)=2tAh;%?rpge^=vt=OHET+=qW z?Na8pZ#TY6CFr7SBk0@4Dji77TG!p_j_5fiNzRf`a#W#qxHk|pJxM9E7|`F|@WvQc z4EI%`EJj~V1|vd+oxOej-{pD1{I7Um2ap6KM$ZMpOgF;$Z!l`i_3K!d@h7z21cd@7 zRmZV9QM=0H1(1w`&19_Gmv!K4olrmnbp3{x`vC)$8aAAh?q04KFQ^!Ek#%%EF$ss3 z06K>!y7dz%WuTlFBqUH+{wjK;rKJ@R5U>%9Jvm%O{rPrLS|#u%vl=v{)2*hUbCp{T zBkTV@9i7DX;=pcjE1)}HeWEXDs zmo~r@ZFS4CngK(qGjt{t1c?)cJ>Fe}$8F|Ol#vO5G2ukWRSdhBt*xz$jEo9Ah{Mt^ zcBIKRH#eJ)eEMje1kbvEGLFcGqE1sk!uG#QcYB{XzqRU#)qz#re=pfB73%*K`Re*B zkRoYvRZSFd@-8y9vqLP_CnW@LwujzaH_D2GVT00yE(DVeh{SdxVhW1yWD z&o4f!|El*^1x*FsMG*v^hbBX6z2j}5CULn+Md8SRALgc zvoeZ-ny;*yDHjI45K%&a{z!KNc6XQ7Yhc?byE6iMrF3@!pS|0(71X}J%{Ny~`qE9N z&UEPyh};-V`XIBjTyxXhI+{^a0m^~Khr99x<~nExz|+?d=SqYWX?eFn2=7IxHq;fO zg8)9Igg<}&xJiS(kOAxT;93>1ub#9JA?-Q0uC6W@sK=Zp?G)0pLve}F^>5$4&GM-L zs)lJ}z>R%B^#a|&=-%aDAJbZ*Sqni$0=Y%Qq$5dPT>MSl;mSiwVGrH#-a2>J#_!*6 zRvkfAKrIicgQnWop7^lL6YHVpG>U3zZ|{d%MJqj8?F2ojoz5!J2g)>;`+-9uKT5`; zKX0AR$dWrA23TWGpf6XwXR7%_Uy-5Mc+tl#~|7j%SGLHbqrtWx02lk&_cyF#cBXR@b=^*z0tT@utcW#V6OU3;% z23T+wNEP$()^*B~l77#gJ;VDZXxd40nObOMb&L!K6UVFA=Y6*qO?S5P9n{K*mi=sd z89&k4SP;9V5iKCyzAgD^yU?@t=>t6IY`s8z?@0q-=qokr!IQ_=e;*op#ejJPM#SLu z&XIPu1S)jKvAQoqmY9!%K)N0kvjb%bG;S$ab|7#>4lLIGAyHLP+4@dyZ8}tHwjU=? z0)Q%_P-T?CUPwGv(ZaWK5 z8Stg0raBB;R_sT4dTb0$+f5&m_)U;&{;C{?eiB%og^$c6JsC(Ak20k{+(nc=c)Bcr z6!Syb#|pm(Mf|isQlu^ltgq_nS~}Iw;ar8C70UIDj3|eavHI1 zQg6H~T`}qyf3j0iuh%c&+vk|CmJ_~;1x18`?F1x^I&AXzxizHkg7%5XZ>`a+nzQYR z?mc6+O_Tv|--epSfyf>)YPCyeJy2*+2sp;T?x(`N3M%i<%Z@;I`Kyr9$s{VTM{{9Y zm}tEFe`W!ehc#gV_YIbq060pO3?e~;!lzAnuAmcI+rnZ>9@rBIVrS*=5ZoO}gX<3Y zr}>>X(g7ppV9bX^_y@Qx_0O9DHSTSq_FN(#0OBO8EeX;TT_UFPaKyd?XN zxS441oY=c@aoZef4qrV(lfLIMN;-VhY5+z% z3$&a?*v{+jx-6RB`*ECRx8vI~qxD^+K~xK;b%&lN(f@3;$nZ6&c;Txb1~|X&w_E$% zvUc;uyMbtxdMdF9wSDvEtn@}1<1py99H<~kaq(>H@w&QIfV=$w!nvM*#~<~?9@n*x z#PL|KEdFNd*l!*N9b6_H2xA(ubK(m;u_tv{EYyZRsAJm=guO zz%-ywcY?jg2yQblqd7lvEBexXKe&y9@K9&)ecZVeH#q^>!w>ba)*qz;NRXb5qx_Vh z@Yni+MGgH+s^?PUGAvOI@Cy-s4#FAH<06fGZFOrHYZyW&8AO24VkV_ZoR#@K58l|;S3siPfRT(Q+TK7 z1e1!TJ!U4JBj-D?tt8M+6ZsAb-Pfr&{D)ZSiL z%^wQO*-mSMFaFOxdDIMn7282Q6v#*+P4-$`@kjFd!@&gp4Syc9w1Kmm5B@7snxX|j zP6->eiXL6TR=WSyR3=YT?)=NJoPDXd_)j5;?){=_Xxx#M4+{Sc?bd=kQvgIT2S|GZ z;>GUm@K+u|#8`+DkRblPq=?h$8N13CamFG}L7-DlJMf>`iBs0ywT1!<rdG)_}O=juP7Ia1BinL{fXt^BM3vU!8hwH4D>UkfA~-bS~+5>{mlj}K1)ds zRQYW{JZe=@qhI*7p&<}e>FtVp+D7?r{W%=RGSJaAE-10kGchrxP}BTVc6AFryX&6$ zoeIsc7b+Oy=mKc3;7<@eUJvn9gSC_o=8{bkG&Ydqa2+ml!EC26O)5}aumg(SQ*6}w zA})ILnrsdUAbPM&t$Jbi3Ajl^^x{Lk!>Hxt(Mf!;lW{=Ot zCG$fTh3fF{fwg*U$c52el7ct^mo0~uKzu`!O z66*q7uhgjZCL|KN7i`?9T$k9ntVTZl?9DrelThse!Wbx8tyWoqNH#2`)Rvgwa~AxV zcCAZUpW#%A^QQ6gSncovJU_N+a(xaqxhja+rGTXVJ)6%pNM;BDZ0d$$^}ozM37VWs zpR<5(BD5kFlm9QU9ohjl)&ER(q~JpUgC-e_V1)YoCqE$1;U$V(stD7F*d_5N#({T^8Vo66ijC6UZk#dM9_$>oo9>Qb2iNooj)&ml9;lOMx&Zz$b$0`PzMd z^#-pLad|x)T(uk(2?VOb``e$-5I&()_}IKHbI6gNHkkQ7p=^GDN!6;*4E2OVKAu*U zJKhj7GPhZ}GiA`+dgOQdg1FV$1zs5pvcddj9=MonIoG9G>Y&zAIqm{GPM8lAUYL z)3gw`6G^0hzf^qSJUms(E8H2@J2;y8Foy@=CmjprSj1$OQ$z|KXdRpkY+nD=f6w0Y z`kb_aYPeW;Wjd;iUX}3PufA>m`=whOucK^@UX?nH8CrTUOC-H4?3>uDjp0`|XD=== zcf{OweT%KP0kB4p&yOxlK9T0jh?#PNA6V>pJ+wXlNLpnx3>CpP<-+&%$TJ>Uk6*kV zI^p;t$|V7nKsHo)9mx2pcQ|>I4`-9?7!M!6?=vK7W@{I`xLKhfeh1^nnD^{$pCR1S zS$ymv?%9;y5xZ7q1Wou|CO6i?hkl0KrGvv3o%||$I+{^_nO9nLz#u&#%B78GYjwp@ zy}oo*H#6~999qK3vY0<8#n!a#qI+1WzF>5P<|`wt88NrJW>k4?S?dBD-X5w!dRb=; z^9MU#CZRC9{%Q)6g=AzPFk<6F`D#wy?6g138&NgFJ_<5Rs;=9gbR@)(q%Iz+sg@CZ z{98qS?;b5)6~2< znSTep7*gZ6_4%5rRLQaHIOXvQ(M7#WQwcU^wyV|=_Nhxp$2BoiIXOn<((f-To~XSj zne;O>?a7osfZAUGGF7zxQ@R(sOJWE6$HyLFo!d^8StH168ND^kU(~fHXufdz<{{*o zkjYHsvzOFgt)$+Aa3<(IDsFByp!H&aoo53(hhP%x_MTPsGB?sQKr^z)DLk|b9(=Lz zaHxG-r&3>amo@C>Rx#tx3C~0J*%;Z@)%~#rHlP03W0SJ@1ZVh1uaU^ zu;P#zhGO#x%T#axp!n(oYgQhwb5{orpz^+8??t9Dqh<3|Ov45K90LlCNOA`HTtP^b{w6XI#576UAZMeMq>6b33e2vrDirMr8VfxCRlz+ef) z>rBPug5F-WKFA||8*s|dSmG%=@WRra4l{_-aapa`zih3w^TBTRL2YKgK(V}^|MO0n z*u!=P=LI1y=Oq{D_TjsE=peyC2(YH-oiSmJ>Ag%V#hnyRnv%#BWt4dj-g*X!Y~kBnF6SVf&`Y?pKtFD zmP$H$fwUfj@YaVo4Z+mH`k?yI6^~hsR+)jmL|44@XKnQA{BKfBnux87Q=li^JkR?P zE7XNO;gtvamROTq*r9D}g_93MNh^(nzffgU7!v$VqpVr9*Ki9Ie(U?G(?7kRB-h#HjM6yN9%QRatusnuIug!lCuH{h09@1 z2?+o~kUQdHuqQABzQ%ay=1danTeX zgvZbCTZ}jd@P7x@%3w8KRz0dc@sDHWY^pF``nZb3ecAf>fOxYwJ6?)Yo(bm~D(P`| z#?|f@bu%UicEh>Tj>!;dI@3i98LW=}U%C z<=49I-W;w%0&5wv(Km-TSo*+lNPqL@&87FkvCqv-23M>vXqq~?3@Xmabff$3UyWMe510-^Ju)A7#>dZM^R4hzWKFChyhdax-LkG;j!Q~k?cm?C` zNPA;@IY{#PO;tZI7|rU|h6>sfXi=WVvvqi0#n5}-No$kDPkD|j?Gqn`5!PBmdEhS4 zxo_XZA@fSl&0Q`x%1G)rF&q;8)vDr9?-~E9ly`mOaXrile#3K}B7Gu7xLl)p#&KJz zt=(3`KW(YO!Ao5Q)K-ME3U2(k!vOBt|3pE+Wj_P6%0!g-dhGEklFdNETQbtpm4BuY z28e`#pa2E1w_r9cZc2}elr1_8j#v0)Ej2wMyPf+c=Qt)aZrnA|xLi9|%s^+HR^p(5 z>0i3b>zWuSfTuy*>;+F-Jx%g5lBkRmaNfAVJd2=fu)w$=dim`AT1%qix(Sog^6qQA zqRgzNdPxJa76a=1yap4Los^$0sg<*R{_524Zz1y<{!kjg{PKHw;R-EaLa>b3jwh+H zop6(!uhyP|)NHT=zQcSPB#st;e|b%JWqR7)HRt}t#fY-B)v;hb^$Nwy+lLD`1xq&- zMt9pRbHlcDR4?$xBDrv=7rhjSHsg?u!Wgb3YKXx+R9`_)miXTwaOQP)?PG_ z7`?y&7H{{Pi`RRhA|R1f5H0HR_80pwSs+hFYg>XjPFFFk$FO+%^q0#WoZYG8p8162 zOud`-h6WcegE%(k+YMQowBN|-dMqz1{?>W7gn=}sODm)}>EGx(^ z@3^-&Bkh)v14}n+P*20$xRtKuU^~wWo1I8IAI)c`&9~*6vC5aWyU5hJAyvn`&4DO2 zB1Lex;A{>k<)EV5B7P2H6aM@u>p4&hAv+}C2bo+Mwy*Yg6_fJzH)o^wT)ad+9v=&~ z2-y{UD5||gIKQVjpF5o8XI(SFUbYjPFEbvWk??^2 zd{_111GPHGh}A=MVaTinPpass9e-E?OV6I=?{~}d#SfPQa&F|nON93h8P&a_Yv_W?2Z%|lyEZ_WN1;|G@5m8&cRfI+DPWuzC$nzDi1b% zhMdc#T{5})udt4rC z2aIv!obr@HZch+r2kf*!K7QnxqkRm_acHHHH2Pinxb$Fh9`oL8WhCGo)}xvCL>W=p zL9=r{zDX~}(BEp{S}xNXAfwn;^?U1Hc#nFewDTYZ_ju8-@9Z44ejGa{jPARnuy)jW ztt)qwsu{R~y5=Kft;=zZ#Ka@nBI|`@HmwZur$TnL|Hdu77?Hs9B7A^`ztRjXEv+e} z9&`0;tB@Q#SY0>x+@UCem6Qc`NVLiu9G1+k*8_`j+#m7YIp#J_Zr`xiOLuImUK5KG zTMW5B8zH-l?N*zJ6jeVtphvl-`FbQ2>-e>XK5R{+OipJo6P0c34N!UYGRBH*ST5FV zkiT^O)%_@@R}s}LDuTvNoy;G!F~k1Ly(n#RzONMF=}Gqd*@I~)RL6nNjd&&fFk8cg z6X{eJPv?@A14Su=joPA#TVV=ip0FYML%I@^N7IWoZb|qe>^ueE9_Zs!UjQ>XAU6F? z4HJQMRX&tnI`|S1A^RY_QhU5J3~97}=!h)5#}I+t-kS_zynkOBBnj5W$HzLpHu3#q z)M-(xS*@}XCPZn>tC`sipNw-yN`1OjOG@{=gYAqyvGUeVux}gHUzUmMR3SY2r8h<) zR{v9>ho{hLSH9$=(o)8)GUvd(PpkcL7C|0gE*+uO&jD`M08QOMyo2m{N#}MKRKtVd z-Z}4~1G6D37^Cn3qq!0SMq25LqvLbSd-Uo#vV@ptuSrlI6_GQeA0?M1d6*scDhlZs zYgA%xj+K1*a}8l;pUD1qH!Btl2p*tFbC5phf|AMg?ArxIC;03jAIGU&?{Um&)Ix+r z5E`kZ;D8h)y#w4UjYe}%*aL!#Ank~7M;{xO@nnlj< zkPWf16Y%eEy5fUZy-c^_pT(t`FGFY&OYP8aTeWPSWqzBya2jo3u`t4I5;@)CzoNdsY$H0Coc#Qb`jy#5;7AmNhKglJw z?=OFtWRF~o)X1pn6BP2)i5Z;~+giRA@;+HGdY>KNtW7AyrMFdB&keVl;mvUAF^RNZ z3KOQgCiUy{j@pbX;}}1We0CRR`sWGWYbG7i~WxUEBJM)S<&Z~Q%9{vZ&EWHI^S5Y8o1c?XSBX7exXzr zyWg0hzb(4iHVorFll6&cfqqci4X2>JD?clO6P>=Ux*Eb%N8e4buXL6&{5n*|yrfKY=drSjZs;IF|8*!gjDLHCL>nmq2-S zaDVtKGx4*Y@oEXPKf9Gf4xJyYM}8_FOruw58#pz6zc zs+qq*z^o`(_<$+CbUsBY?AEOLDeH#|k)wAJ za05s*I!VQFTDaqbhqmbB3oH#Jq_~rNzq1R6cfx_OqASSVB1fknIIdSb?5v=y zEh;9q4YGZ%Y&44txGjHE1Yo3TfZg2-7Ojzykr_nvT#nY8`YNmqFb!qBDK`!IW6VsO zshsL22&x_Cf^yz4aF6QW6PI9YymdELWs*+Vcbik`>|I;@_6_ z^GK-EF}xv)>M@8jn_gRW-*m#J)ZP?6Dx*LZKtg*Gh^=eHrwLCbiDPh zgO#?a@bS;E66lFY+2|oL0|-M6$`J}8?xt(Mztq_RkwdZ+$f5wF2?Nwh8*EhTBT$GS zhpqNB7KwF&iB6knp^XNU9;azRX!MoLkBgT)ad(5Q+s(JCN?g$HZ*s-;50as=iBEpuHHuc*H(HIvt#7D;N~(%f2`h?!{Tp z)y?1s@~Y!CCm;F+E7Wf+M5yjBrm0s*4bu8k_VB7lD?qT2XA0rV=-LqiN`6z=Zgf{{n@gn0MsJC(TDEr;Y7tv)JavzgV%(|PZfU!^ z?-xy2l4JMHI{8QFxPB8i9rNg9{~CEinO(9}Wzwa48Z+2G-Pz?=wJSUF6K-do@sc%+ z%Y^7g86>r)r-)9XM2g;8=u4mHq4+Q%w99jHM7tZds(9dMMA@SJq_I;p+E!?Qw4!@A z$)f$ohsnqZ?kRkyy{MiYMR|v(qsocDlJw_}Rvu6pWf~>M3lC$0jt3bG4s|&O*GPt{ zw?eIlTI3n$epjW<*2Q$6a7hsjr2dg;0t}h*k|u|I3Ni9w>Hj@m2|UlCjcXtqERec& zFwr3IkylW?!VfY~u4bV=q}w-)?#;rX6^M#X%hr@bfDpn=`%%wJ29c~I_|-YEVO;hm z{4eQgKui*0?$BlBK(^=si-U;LeSL_P-WkFVaKNV%c)PV898GmuE*seT6Udqi88;*` zgrsi3ItMnq3kflZs6>f;qr*JP6U&E26O#Y63^2ramfMpryPKK zt`+q8OVpEx_%D;{?|g>dTy?jlK%zRpO2u&9s$z+IKG0x9Lc#yia1{!p%Mw@cNo85z zJhfkutCT-T1qFd;x_jr1sJ7eMQi zJaJzBQuF#ua_`-F&&;yHm&JUZ)ptn>d|F#{JWMOEP@e33G9TBFZ-4vsVRb3r28g4M z!svk-MWRq`^dz^e8UDmJsT}J38-E4)SKICBpYn;Y&eNg1@RshMyiNB zi-Jl?1Q%dUR@*02v$4Wn^~S5ybyt;$#5u3luoAHfbTT!T))_hKxaR|)KZvA^(;70= z*3vK^Z|SYs$c~UvtqH-uPN;P1s?v%@;ovy+HwL>qY}Kuc`>*ywM=SM99yZ{w?*BBQ za+$mJa;%0Fjyg%2n=?ST*9VN;iNn?UCUD?wnkbz_(AmcWaB}#rzUwmAt5>g{!oe%< zO_3T^P5HNbsSEvTI1KoV+%fDfDDUa*PON%eetE&BV*8D&o5y9&)AK*)_Kh0X^@B=A zS(xK=^!2HdoM%YSo;~{%6qC80tdQQYmkJBiv263&{bvp!R1(BzKa&EBF-FLp|MZzN z8Si_a0S6`o9s3icEU%K2U#hHG@G%wBmHr{&KRiq`UD0|a*!Tj+DdUd^4{vYzN@gcS zgx){G-iWC?c&2o=Ox&39W4u_|>5@A^w)Cdv zl6rE95mlR{NCcrmz7XM~E1h>+&TBQd#QC~a0o_}t*U?2Ut++9a9_rhU0Lo;`og4*f)6cf_6-ku@hjIK1E> z1uNDcl6bHhPOY!6zYPhIQc<~9w?9Mf29b~pgoI7N`PiO)duv`%wqbei;X`te${zZ> ze!adwo6Iwtt8{5*VIcr^tRaL?V)*RuLR8)tP8&IZbNp!2;z95EV*^5eDJfzigD%4X zdnsEFSAHTgzH>hVMSkq{hgwWMJ{G+0Dd0pTF>iFtPUuoWzQIa_RySbX+p;+?{$Qs* z5+_YTJUQCRyG7kML3RJv{`hP9DqXzbEHbB)tHHQZFp*zc+yOlYJ_SGejKeXK8ebt`)(d~;iJnIPdHFuL<9PZ-}(ng z;@d~7sMlB#Ym&eq-rBpkt_BZJDH~|)9V;GPpwM0;ynRA``kv=`5?Q%lyWhtj4<$I3 z7?_k?Bp{Fk6Tt>TH*j3}3@Dv%-@P-m)jSRR>njw{q8H8AD0ppf;EiBMcHYg$+#^1T z>^4?TG4i(y?oLfiVZ$+$<1aZTx2gi?cI|j&3g%K+*;44!7K8>cA8G_IQ&oJTnJVw+ zt>!EHS`^MislB}}?*H&K+D_&7-fWg36Suk+uY#`ARXyr?uk6avr`%^WPue9~s7cER z?m(R3_TuMMt4qWb6hRQRL2KAfcymy^SXoWXlICpF+&*((4v`O3|keo|D~ux;STRt+>%8wUq* z2%lYnkAX&M2x)N~96~}ul3>XTJ~+xmwqO?pQSe=Z4?25J_}+S)vMRsJmT6kMB1^Pb zzJ^$aiq;7$Rx4ja^Wmu1sWVJ^UPpkCcs-7Ik>wZGm9|jkmW9LoOk=w#DH{8A-*Vmt zuQPQ`Me9)(mVCm-Vl}9&%a)BOzSDJv`K{I<;iBY`acteLY~sg>e&JA*0P(Wq{w-r8 z;q>K7pR%s{FG?@Jttq;u&2A@nr3A+dWIQ+gSmxi= zl{8!to_%9fQ^)FWsJdiDlupzj+q&}wd>C+ z2qeFwU-BMUpx2cqLrg+wux`_@M4rAWJAM++; zOOTTX0+7K6r}8TVK`zD0`2o?FWzXjZKZl-~xe?Nw-f)CptZ?A@7av^Ii5P|fBDPJ} z6$QNEpaB6n_e(Df#v*t!n%EH{w0`g&&#TH_HyF+kpF^R5-lADE zLCaBD4jp-C>4e5@0IMl`%VAfx$rd~Q{P#ddYxmdfKkfLf%$>8I3jTxmN89tHBmqG< zUiR_boy$3$c&!3QWA|k*{W`O$@aK#D^NFFci_WtxEOqZBA2`;&`q{l|EE2(k^ZQqm z2@X!Il%0*EJA>$oX>hB5foSL@wt4Xwhua$+#9DZ-Iec+qb-h@|agy@SC_@~ald&dpv&@>r&66+bYnklUhJ`45^Zmi`D zEor=LAY4@&e^y+Ff9|!>FgscB4Zle1J7mG%RZ>)Pkgt8pW*dX^qPB35`$bZR51D1I zp2l3dhzL$ULA^#Nb$L4RiIy`J^0=;2#Q4WW=letU&)}qNYdl{Zb-kt8y1uqQTx8s- zFqLp$BssdFljS?1$w1cEoU$`JH5rqfKHzI#EmLD!D7PsnD0oum#&?tHi5D1YA}rBq@ck4hmIXMi{0vUsKPh^8 z3OO@NA1Nc3BtSabu5LxALJc^u@#OfB9lq$q4e(;wN!T?aqBr12L_|etQ+=20pN3o| zukD|+5F%tv;I%#NQcDFqRl>~dKAc{dn3?hW@#8W06Xza1dSv71C@Cs>8c`}t%Z3cRkuFs4g4V#Nw}-f9+hfkXx>;CD$jpCfJLtw zHLPat)BW)ey0(T556AD>9XyqQiJ|r`mEPLY-GGdgOE@c(2@`{ZxFLG)%WCfZ@S4h> z_ac4ky1^#Nvif@KSku<6IktHI6s=ogkk=QXPrHxtPxL(qE*jj8EIX=Qe{jL}d*27= z8wZrNaiQm9MbAZ-6Z{a=iDJb1Y1Dmxb|*psC2aYQ3m(0jP!WNv3`L zx}&VEt!+45S+`dWCtUD)`1$xIAjkFST5f$>V`2ThYxVU{CIrXzsrHt08gRS-iO?rT zhGJT^V~wd;7#-pr?h<82(ag$eB@anN%cQI`F@6eg=_+2<8*;@J6UyFKh#6Q-3~{We z$aOr1?;!{PuVMlOJQ0YFS|kj#F|ck$5xWLU-hn>hJ1{Lwj(@HdH-6)5QI{z!#v>K8 zItYJuGL+H3DUjY&?SlnCe#^;EM-3;e3!xhb0Xl3-gOldg&bIyKxyc3-Y#*bjA9Bfv38QC$Q%m`XN+; z2je;qGMu zDysLNKZ|^-tek*&3PKv-3zyb`O5VuCaHhKk1H!SAWGPe?dX$Tx5OF71~ z6JIy~1kEYVb-zBNj+;>Vo!ZNeZc=<>pQ@)extu1%9fCoNJdEZ&d462jO@LN zkd>9l9%Yn-N@PU#UfCm~?6S9{Y*I?*dv5jn|KInzURPa>=Xt)*{k^~UeeQEU=W{-X zRH3+e+i&kX9t1TGcj-@`egIy&yn@2)g18Mv{KAs%U~3@Pg~Q~az(5Uc?c@2nMmL68 zA@FH^d*_=o#I%5o@aR`LGlJ{i4~0dYQbu;gY7mPIXmBn)F?su4cWG%4l0w_?J5luN zqy*FqJma+p9N?o>u(V{lzrUsTuC-N$0tf^0DW2<7UJ%^(!*j?pt)qn^jtjt1cm#go zNvN{R{ls88T)FpaW1|rkQ$H+(Ok+x>2(UQtPdw>SoGchQF@xz4TTXG={gC$BJ0TlO zOS<#BfolFncs5@S#y@CfK50siC301#p3uJko1{m%!BDsCqj&b~gkeN2?T%LWB@_QC z0jB_2=0cBsVg<#Q`W__TgkOKI4>l_HTf4I0`V-^dm40daPk?W@Vc!XhlekvDyR0ZP z>%93-PA0ElWVjpmoMgsIONqKz$^KABT;%H1r3a@SA}EYM8MC4}Bq!sijdmtp?Bdk> zY;cS{B9iv@F}P?8E5vtNr!3;T+~$y~^ZE?2TxzOmh?5L=$`&cZ>vuQK*_KHa?8S{Y z3SG3%Fg+kuGb#eHBg_rYt*25&J*}+@pS1@xU~}OU5Hw|J-tc|#?b|mbFd*u+aSpL1 zzI^*;wK7s%wOTR$qL%U!SpJx&`U4qMoesh+-TAIA8JA3|oUtxkxbWm=mBF7se@5Qk zVMEX6rI6u4UibGq z@H`tt?a}U}P9Iv|7YZXEM%QNV*G~%?!YDN7i@Sw})uvf0O9g6Q z%sAWnS|yA99I+w#^73?-drwIZu@K|W9%44!y(Ot{Cq!tX;w)+y+yWUFy7OLK<VaR_kiWU0UTO$NZiwi zdvG$mR)tOB@j$`Eu6V#s2zkZXRwH>ppoS-T41h6wBBGX#7-kUZ2~@#BK$ayG%hE{Q zmjKKS4?>~!8?CLaI(La!b%g6Gaa(Z(>P3eHD^K}J4v|HiTy)-c9p&1+=y<1jKQWhw zbnF-#+Z&brG+}L1SF-zey`*;P#^9`22>8rNfB)5enzuHD8{hKg7NpqdPI;1qG|L1) zKvBG3zfHJ5Ih0@^$UM8uGEvV+c4fL%X%ZRI$Fa{I zNhR~xyNusAwkLje2J5Y2Gz#4zJh&Nq+D?x$lyeE|&5+^3W~B1Yg%f^Uxf2c5wt{nS z9^B*gvhY;9#7CQ{MP@UUX?{2n99oz~Nh|7n4wuAXM2=2DZct&I%X2OJ=Tx>6kG9u7 zL`z&!Y(8<;e+&wqSoE&Uo%mq2eED$iPBycK02H2zqlgr;TQuN8HABH>fWJQ`l3tt& zj@vk~v#>uj(=9X;!k6-1zdjBgdLzIg7W~gi>FJ~3tVBG|xHc~E?V%LV`TJ*uTF3#r z-`_qv`Evi3qTmgy_HLUKRnN>S{I4audfggzvhYosj*tE^{_$cR&yvE*bKlyr=J5jR zXdUnMh>Apx+WIPiW7M9e_VgD!a9Et`f=idLFH5Ln*lV=|SFeB0rK^}P_zKaAG`m0yLjsQ@bFm&$}g-fhmM}I zUp|!lUORDp)-@GhJ>UO)I^AqGWs&_uqMwQap?;osd>gc`TL?W0H{=eH4Hy~GhiY`B zr8H7|R{&MCO8`x)3QnVpXeD&QnR70YYnDS137SHO>3Vu7_6m|#U4Vx8ndCiT zI(qu=APF_?777GGsPH%jxtp5ByMu%8u5pxk#9Qo*VJ}>j)?>J&wYfaCS00y6teH>w ztf&CPDzM?eaAJz~b*r4whqKp&(kpRERIn-wxcv>e&6{}fCKw9ZHs@}HSn}liC*awB zKRD2O*-L$q((MNJ#wY@ z@&RF0*72@KJWUz0WYj<8Uv~59JWtQLJ9#Rz%|^+6B;h95)YH2>rx~Z@8}j7`O2X5^ zAv0j{{>k}{?(R4-63x1;waGIOW1k0L_dCdXSU`b;mJ-6iazrWy&ND?o*{V4>B+A#W zDS}Q6)U|7VyZo)|rhuvU;(2xo!mdP-!UwU!`Q>kZE-V)|#@D?3-f~4!_b^>4p#Hv2 z@6TrY))Dgb(+Py#wsV|8vZSe+_#<~8Mck;`aKifI9riJ5u%{+Jm-xY`WfD=bNKbB3 zE-_o7w#jkUT}^MDu#C0k*U>T_$`lH8Vs7{l^S1zXZf0$b+npuNJNCUbLZpG^83GyU zk&kwj0F!GeApkGu6qWk}L(%kp+eMyXYkrHp?eF?!wq)8II(1(KK=KV!^e2!T^OUyR zYbk*#`0o4f8hS#)S6$nfjUt>2UJMo4-JSpa)D;tIUr2d4Ma_8X_=1}BtHMo<>sKq@ z>F;aYm0WkLcQGCOIiPXg-IrQXK8kxO$#L68hreJiY1sD1ErBlT3ofV2)#a_f0X-4i zH+6{KjswYn7gP;G0cyTN9|6WGOLygdKvqaKL=Q-7QtcTp7SwJw0B@R% zpQuY-+Yj&St9OI3f65=}xBuxFbahJ_N+o+jHi}nn7zI^!R8X+n3=%I=wZF#f|1A>A zh-!H*HSlhy{0@E9UW$@|E(5X#h~*JUE0ojpV;3^;yilZA^A@M_Q3G~T-4~7borEQ~ zO|0YBD6W|ZSvaSV(<&GU39#xvAbs=@M@zt;%{!}q%mCLj%6~M*kgl8evt=@VQh($+ zxtkZ4N(3*@CF23?YOhW{(V{hxYb!O@!9Do3eR{{7@UZgfZiH`S-;3Ga_510SS3@gl zbYUiqs&DWOr;=`QxN*|yma(|4_2oQrXS1s6u|Kb=A8xojO^tm+XNQwpBOfL6m?+%) z%^B7;nup?^3N3G=j^Xx1`FrY#h~kNvcZ5lj)|FASww*YJ+5Y)cdgT}6vnnh0on8Vw z%)}k@*EjB)&N|Z6=ZRqSzkaguu-X3Q)}Eh>ch1~=vU-HkjsUnafe^^0!zl~t(%|>+ z3V>=K-r-%mi<3r$0!j4jL6XNAO6%p7l&oNrE=;`rakcjzNbOVse8y|u=&z`3_}nl* zR4Y5r_nqRGH1drl43|%mHTytf@sC=pX;uJHrC689zMgN-ymF2A!HxaFg3H;3ymh0* zd6zQUzgf}E&at=jcPj2pGn|i~WuWJ1{ZuEUPdkUt7U$m?!gN_GGxyHrSbVj^a{N-} z@)3^&B9*9{d7`N;X3yPs=YG5q@ZJ_9)aO+r5{V+F-c*3ZGXukDO9?Z`%>LIHGIC^eS1?^Y2j*P`1+mQC9{qt)T#LAx zZO~S_AJ6hb8BFN?kz+;&rPLwWtefz#v zuT@)9SoX1KLQ8$Nf2BL}XWp9i&d!zDT*JOcha(Tf`jlp$ZX1({j%F6M1a zMSrnhCcD>CbUsYA_>1%W*12U;(OOETvnQL>PaQhM|M9%bPO~G+eC^P=_)7dR`S#Xt z`Om)x{q7t1%^U6vxjSSiEb90g$WTj1(YuTAXJ`>9DJgYW*Dgk^JUWN}OvLFdii^7- z4*|G=ec2s0FnVci&1u{*Nuzc5L?E9K0)|_fLV=j!G9B1WaN?rI{VV}LgSy&4l#AB< zb}v2btak@%1nx?tf1XUwqR&f%gpUqCoKCGq+&S~KO_2rP06&45i|d5?z2Nlfwyk44~5^ih4@)A@Z zzbxN=#LZCq@Yh_yWYW2T4Z-HL&J1VUjDXmNisq^`(_mR6-LBh%y`>-B+O$s5`3Roq zVaLbb+uujl-B$3#tCKNYUZ5KY^@kU0feY5r(-SX7f`U&RBUqWwpo$xS!RAf%H=h`p zn6Lp<3Xh6X@bVJ;6bSdzbRN+TwGMg+FFbY)iU)?uCND{Z}r>({+Cy!@O=cYnswipV?@>PV?h7 z3*o1Snoo4zCQ`;;lmFm!I(+i-@vx~&51vl*Cp!3suQXjOtPO;ezR}Le4I+0~btSm4 z@eBf>L_qeF)-xY7Q~?izxqyw@2=W&p0LtE6{l+CA za0>Lag{G4K2tNFe3vhpT^`=oKSGN7DD)}gd)VTB?Pd)S{&GB4&C@}2yO$JFnw{u|< z-fxQi4}7nTi=nV7d1QUz0WMjCl<({2d*W~COZI%`TDm{Z6`D_mKRl@;UUQie0BYSA zzqfvjZcX&Jg{ux&)`Gl(*LvOW4JbsI1u9X@(E}rcBTZ|(!A}ar2qDyms33LFE>$;o zc3}bE%*?FJZjcaM+n~Zip1r+24z~<|u2VkgIED;9tv}eYC#Dyp0GYL0L-XXCgD;QK zGtzd)2Rh#7UVWdlF_vF#3$ej8YX-=R=bl|Brn$50eJ7nUJF z>?DBmq>($QrIbAgVh+W($A$Ai$7M9$z}T1=>_NWnxI-sP$$9>_n9@<>-S9(xiqjWs z3$lBMa@Yk|qF=3qd7c#c!b+t=pRT}9amJqEJt0({-|<>0ZYQOseF&HQ_l3Ohg?av5 z6mD4NAad}G{{|t5M@>3(z7)Pq3#y`#Kpznq=YQaEw+gdEg2%75@DE}5~gj%ynl64sfXWMgCNu&yv#aFT33!{Lc@ z&G$L>_3SH4qTPy*u}JP4ik4}$^qXCuziE1sato`WDVH(cAVbgxi9#L`%fHy{u4({E`|)2 ze&gfcHFLjDD`~v2^4Fu*dwrl;8D^Gb{5-PeSETftdt*#@jl}3`2YC|%AhB1qsmCqb zR;lQ{M~Ux>6GAM{s!YgC4uDf18gt^M`0`@Cdp^d`JQ^B!)`#||=wmv4HxyT`fx zu{JUu6K8TTB$cSCR;JI;WT%_oSekwKxCXm#`B03>)q*;Bf*$kAbj_JKtNMoH)qyn? zuPtWOSuNp?+B8GEqRdPQSn4R{o<`(kL8K(F5Xs*PR`|opV24!snQJ z&6&hax|!{p%a(ldfD5hsVv6)~%+$u}HigeXMQN2SJjl zeIM*!rI6iqa|u z+BBI;bb*42ut+EnVR3)T@kfl%Iq%@@RqK|tapj~G$3Uz7*U{2CwnB|^OZZ%Y##6O8 z@4lYvz$tsR@F?6yZf3leLc84AUQdGe@#m%<&2k`3rx4-5{<`I(&HWTuIzEY?&)-=mkNkUjZr0? zB@AxvuYNdu?RHt!4LhS?snf)V?CKptLw8ixKaJCqeF`F@SjKx^vSa+KZ(sH;q&cWT zh_DF=7#Y#BFf&}?TQ9N5V+Jod&tQPJ!F=oDBq-rwgJ_^d)ZD~)_VZ?q($i-+r7f71 z*z+IJ*6$mPl(VMB8Xc=}x!QR1?%lhokV66LP~3p#iOaj$aSvsZwUh#sY;4?o z3nabksUGU?OD^p01E)Guzw5hC*dJc4;3+VwB!MF5i)yT~*1PwBZ3)RkKs0jb+s$y~cQ}eg zS}r@zJ1o*s-kdmPjBOrfC9_MU;P~sKIAT_nrgVWio=`LoH-dnT9qV7fPEZeqbY<%f4p4F8fK92%r{q;^# zZ=d`fEN3tD#r4up@4ESk_MBS*kF)b1mncLJaOrUn=Pd3iQ78a2SWu~1Q2=;Y4x=SF zK$4PEQ&WqYt9adz@xs!qj0$p`pfo4^s}K%IY`(TfWdLfJ~y_#18&`EwDOLAZ>+kI$@B-qKRK^}Sk> z0UIx2sm3Au0bETs)pm0($dFq$GhSxEz=^R@V4$0|_s54`ycan}__yzINsQOXNHvA8 zochyoS<`=|;m4V}#$}q65Th*S39(B}LIVUG3HH=nox}a{J=3ymxXt}?5l>eQ*z0oe zFrrmk3AHjB*$qT+H$czlw3I zWYk(bm4rF*br9GS#{oXpKq`*RbWxvYy2n?gGGg4KM6W-elDwA9noY!$PGE|Zw zU5cyR6EDkf&%3I#e0@r5#(U^zXHot1_|^+o@yW7Vr%=KD_V%{mPVKTBMXQ*v;=#4X zyvsLheH-r=_Ej9Otfc1Uqj+r4f7y^ajXkaX&|O+ai-7-E`AfD2qr{Cjfn=f|E1Nzg zn918MSp};x2f+-Ks8a|ox;lnlx6kiRW)N(@l+$(<=a8LeTj_2pdp^Z;JlDs5sWgAS zJv8UE(+|*_PxH6OV}k=I#M86VZufn8>m6;3t-u6z&uw~_&*>~Q5o)~g&+Lrxj3ktz z9z1A*qNnfOBGWSgcxSWReTt7`H2AkN)t)7wmP>RH^Vb&O5ml zQK3#6%zBA?)AR;Y&KDp-kJS6tbW6VfGjtB?7Ea>_KmCz%1xcUd5|bvl&&?C3Um0`B z4YbdWoV|w$xlrLgULb+|(GA%& z+P14MIgfcp%iU;l%*Sg$m?(&AaO50T?=Q@N>-fP`*=4)P(Fy9B0x?&5+6={3IQ8ty?yAdLf@2`JsGD_Q`4LqmOG%C_hd*ZD*2H5 zVP4Q%*dOE&q$|P*5W!LcbWF9{As}KBzbWpwPpVnU&duEhNIf5zo$PddC@Yx-I7|-Y z(DUa$9G~gYz+xboEfg_w@l?X%*&<8P9cf>cw>wFhrne*1WL;uMhibdSGS9g*MeAO5OxGi}OT#u3^O2 zYw$HXSJU-vo!Tm?i)Mwf)ICm_J54`UV?4Jshab70A+?7$?ldc_VsUt)IjKD~aC3*sras<#RX$qf!VGiA<{n811OR}wi$Q9dCaO(L{aY61EdHsY z8@0rDb_np@?VBHKvnA8cEUsY-PjSzLm}1w`=cPXz)SeK(#O$r3{%-AoY{D&>$GNKw zyEdNcIy6iV3#a?=-uI*!n_@NTbWHd~LbGMXKx{ZhDSLvt zDN>Oc7}VjQTLG+MK`U9NacE9K<1k)v;_cgCqus}0=vR7oI8@}j&6|S>{-((DdO*GJ zI0Oq2Kxr?rQ&v?~&46fS87n>^2UKLP{Uz%W{BLy8F** zyLEK|oewy+DPdvhgk0sBi!k0b9(U>A31W@{4Wn<`q@?J^Lsw?kHQ%w5-9cQu5-fBZ!QdcMiIG{dNvfqNWBapN7$$ zyP6a+PoD+@vA_bkKM)7Teqk=52CPLMxEn>wPB^tq*RNo(2kyOoP;xw zj|!r26oNaznXio4#8NZmRD$owC@wy37f}s@%}pp0hX@@47?UMY@RUN|Tg&?RNPyyL zC}J(E?5mnElBrixiZd90o56|+D z2?BT#l=}b-1m>aS0>rq;%w|N(z?M4tY%_=nIwJ-n2zM;83vB|)0hGG>KvD#`NaZvt z8RURaaiSaY^YepHX!I#4PMbs92*^Uj|6Tc~;l}Vmr?ZLPa{`N+ni_pFh7yXU)2k8Z*5)O}p*2cz$)3lZb zN@NLPVoFL&Goryz4?;o;YRZDCO}((7EyObc8xoioVt8|QI@xFj2!EIZ)5-@I1C~dK z@K?6#05)oghRPRaXJ*d1KUEX~(F;(v$2H8a_%I96Jv?e)bi2NN>AOxIV>l+^;}EHGIDUcF*CF5_>^U0Bg^TaXqs518?>p)^5|_G;Xx z#jPEV-CvZ)vFysBlUVIX-qO)Xhy*ykvEj=G@pHs|P-%U+c>`A0Gqp^`_aoY`l&+pW z^W#U=^GJ}zgHaQPq%{i)`JTP{JxS;H-H8=XR{X&8_J8c}O@spR4YFe8)FF0bmuYv! zYeuKV!KkFMGf7D?KwYN{(4*r#Z6MJHX1VOcT{6wNh{(uy5M3ESa*!!4u?_93C(oZh z0O3(uz@lTt9$I^V!UmMj8=4XYVx**`%5INSnE_gz@m)dl9dJ&+!?Mz*1wXzI4$rO^ zLb)G#VK$(O-U{TP;zBP#Qt^cl5eUlV2zHtC&H!wm&tIdGk>jZdr~y>vm_s%%F7$Mi zBBy>42GUJYPl_vg(Py^~B+`HRNCWHsCP>MNz|J~o5CXXj6!*-VP_ zI}6jLWlnLt$+6M`9wZf&?z(SvaYJe1eqE`{oFWAU#SGNZ#aqBsgQlf_db(A{VtCdc zc}2y~Q1|v!3>TfdSa9wt?0}kivG1-&wiN~g#57*(j>k6*hJei%1q|qP5XDag+P^F9R3NczzEaLfofk-8Wyqd82nJNhL!T zn4)o?9;E5d3u1C^Ki9ji2+?0hM~9t2@@3!ZetzNBZ<^QJGPfmYcarF#mBYtfc)lXB z#f)(G_icd510iL+#jn~Exr0k~2|AO%`YEPIyZ5G`?^Vd7M<+qFftZ1T0SFXeT*!eF z%S#(I$Q>L0!W7abGb<~RC`%07oWCd88CKv_B`%A{j5>t&+vffHrE^c?exkKbbDJ6V=axite~JN4|x~_=`?qCc5c?27KTC2s?VDQbARu<5_)nV z2IsVvMfviwFDAv>9dV~^YU>!~vCR42363@S;s;^XM zYGxJiX$a;fdl%*@EA7#F;X3&zP(CU&Ir*Ej~|2z3OXreC2Rm0j?MOUfU`? zI<#n7GBJzF(a~|Rg0)lxvB-gS`3O|LN3>-U0Ke*(BBtTRfZSf(-^C%iTbHLSPX$cO z%P`H6aC9jjS;{PhW#Hm${`dhN1dBTbI({rr(&{bj6(#|J(k2rLKv7_a#dOXix)bQ( z{ykfSBjLam_kd~{A23?b5iHc?cPdJ+SgQih;@?I*%KcRT#X}_#d1GO$Ja@na#uvU| z=8%o(RU#4SI!}S53Ye}HG~)Qn?~sF+@&ct;J<{nH)|&-Ksd4fIYp7}}tu@%-O+=0L3$Iw1ZrNJbw(xv(V! zCJQhV;5cyqdlwc%JSyrE2|@!bf>KV2?U^9Bfs4R5`1hSTC|3f7o^ZG^{vDtUVfHan z%@$bb7BF}O1%K^&9+Z{Ep3Mx?NTx|IDlVQ5i7&*<_VM-2h(wHAd(LMtQ4T}e~I{+3>T2&Pdu=y}E%gYg_ao7Sj_AD31NR9lQ~t4gDx1LyFdhyym~}!~dV}qaY>4Km`jd@xT6G6v`_ceaQ0Q z;GYwc1YhVOLwX$32)mT!8bTC-CxL~9b#z0T|Gf;79?buRjldA10{;M_pKL2wPBG}R zD~LQZ<-N@HpR-9uHmC~7m&@YE^Z(bo+MvKQMT4CegnBPuIx4$yAgnP1_b_co0}5Jj z)qMm$jSz)fN*ZsTxx>^hf{!5EJ^I8Hea zg6|?y&I1Q)%{2AuZSi~+u%h>-uVEFWOWG26-W%EV#4n37nf7&UtdS>@$!{(_q(lwf@OgP?j}zwEGG(W_xGnGjLAZ zK~j|uZu&LsWl*LDWZf#{I|U&Qw{g`u%?}Tu6soSia{0L$>w=q@sHmu*E61|Mqw&Qk z!)CE1m=>pR6_Dx%lrKb<(fujw_50YCz0>Ic?2?@vw6p~l zKtgx>p+AP{1ECLab5xJ4bah* z&|v1w-`FS)458s`c>gRwf%4|&DX%Yao7RToo%O;1CQJSEg$Fp7xi{k_B1kvIR%vCfSrqixu{`!$b>fY!h$XERBJasT*N0Ki5G*)b$CP4+nSov4D=0y z5RR8z1ovAEc6|t(cNS1yk7}TxN4=PCthnbIo$1|q%A=_x(_}VRJ$ia47@jJ6%mKQM zY9K+I7qD<<_m?-fz+qFNN0T2;OR#(N2*-*txJRQv>JgpWK*5O@Qz;!QvN(ncm;eJH zK(2v6uf#Eo7xc(M>`+eQDpF9sYy=@pEDS=;!1RFzNGRNg0|=2f?|}$=0F>Lq^TGxS zvI6+Tm~-dP6P+w0hEL;p`rbf_fWEa0bAPc|dCjB@Yi_ZnOGGNRi|=7oh_co^>k#Wn8U} zaNcyil459TY7&J6QL@)(0-#>O#wURCpccS)`Jgonw3Z=*E}o%~@rO7Q^{9$GBSHe6 zj3Q*(ke>E&T--FHO2=nXewU%(x5(?eC0LnRpsRBgR9%Dd4d{;??N&QTkd5MKQb1wX z%;Mq&cZyu5gwF5K2uzyvc;`JBRQa9+C~j2M)XYI16pmN{kRuBL!Ffh-(lLDvetwun zC_}@-AUp|kPC|kXZnO<>l0MMx0;2?V!;sR5QVfFf3&CLySV&}}^dcf6$mSIeL&Y$H zAs_??U&UI_n*SHOYaJ?IW?#T=1c#`|aZ>Ed*RQgq$Bo~*;@}2hVVn>4?god1=)6#e z8-+HWQP5`KCDo1V&WOhIS~!y70+X4I==zy zc@~;MG=sPF9f$xHpx~`vZl@D*&zv4@0V|DcZGa|ZuU#WKGDX_wh3#2Nj`2Uw8w*_7 zBB2xYhZUzPQwi=6*uVH$RFDU#cg7-=@1VLzsNEZShWr||1px7YuN(9iIx@W&XHQR? z24vr~xxnhK08b19LP!s5YintV3g_pbujFSyJP_aK;?GaQfE}P(DpY_1?!60iB7%4c z)fFO$2oYwW(JLsm&jCjm1;AJsSS$)EDuimRr{IM1hvo$cC;j7n3O%rkfZ4#u{~D}3 z3ON2tP=!1s8pKgJ0nB7hlj^b$-=Vw7;S8)X8h&ehV`F3FGwey-^E^wfQ+I&1CqP6z z(}j;89{7-n`&{7T%y3o1hu*3+W4+a;30?6WXp3-X+BfTEr# zsd7i<{sCa(U@ViwvDNZGPSF+xn+d(3fDsPAB9klufJbb0Ie>VbL4{a`g`9wn3qC+L zc>*$$5D`-SU*FZ$*+~TTS_J}oU;wuvRCIw|lm{7FL|+EY9X`-70)s(Fdg15_T!bNk ze-BSn7^wj}04Cdi{>(PvF&xLBiX@Z-0HR|*prdNf`EkHdVJlrhx9J(El`U^z@Vw6g zIhi5kMD!T4Nz@9VD@mNX;qX8l)I5TqaLp2ty*oNc#ej4KzW+`5KKcy!YR#kh=7;$P z0}Cxd_Auh&;u6qS2Boz6EYRRI1uT`2`|c*d*&uD5At5Qr2aTCv@Rolz=ar$P0+E4J zThMjWZbHLsgTKdiYkal5E~s^-ZbJzo0`Cze(3}^a9zAd}p%|^~VADbyOXR3(Nm4yi7r^FCDgUJqiXb|}$JG|Md)>crezy~aF#M}W; zhVu+JalIQa7ia@H|zt{h|e=V=M%k?3gk_~en zh6aNtpngr%cc-i`62r!k|jJEt)<8 zgi7flY_FeNM!O*`)9Qb(tqh@OJFMdnlDPtOtI(+?5G+_rd%MztGrFAxD%iDXVvF{$ zHHrFD=GQqCD(J|hv^WRAfI-ZZthh#I%$rJ-PLfxmAxuaUOdf>k814WCCSld0Sg{M5~2~nTCNq%76JrChc5rzsKy7P3r5<-g>PbFV$DstJM;X@H~y>B4Wm2m z#TlTW2&1TYdb@1C*OJRfDpdN1Q@S?CZ7+k#vDj?BABI59z{5ONX2Di+y(iB^#zKB2 zDEfhJZU}4!eTRJ=Q9CIbk+oyxh9@4#Tb7C#i6xN$BFO)IrQ+LN0K$r)#T?j@R*oJg zBQf>mPvjMvz$W}Dl>SRq$Y@FtK86vlivq2H%)--Lvj_R-eW>zR&zAG$!&HK;lJO+z$RN>!0W6mWz(g9yPOU{9zXWjp!p%QqIwVcJBY@oAL25kH}U?`L3JpMuRUZEekx?6Y~LX@~)&W?(}TRoq8gGv^f;VwH?QfM)f( zsJYM_CH(95uvi3$(HVDLK0+CF_@#Q8q+B>XU>+z zG=Q;yY7+IoEf0}Lm_Fytzc#t)H8&ZfsOU2=&G18%p}`JkYA^}H%sBDEx&=e-5R5XK zvDCk&uX26l5^&ZY4xNIPN6dQvc*d8{#5`$y;2M31o_gWZ)L}F(4{`|mJIs$iwuXDt~ zX$K!XT3P8(EBO+d%r5Yvr?wyl=RL?q{63W*A3D$Mp28 zfAu~TepZMA|%{lB$xkgCD{ca@_F zk0F4V#p4JswU&WT3eui{&TJt^%Zf7?EfzzuIF%3p&4GrG19>NBcXy!!&S1C^P!%(H z?2f!lv|i|>yM5E)r?DS75a(e*Dw~6)Mzs^b;0EMeLth^sj6tz}BvhrOLDR7|KTkRE zgi&vttU;W?smD#9GYH&jayI*433C z8kRpm$uo{PLWmvZAaURV;+)gPy0icVAVwjyf z5YCRji%k(`(%=EKZ9owvB0WO}XXBH)6pA)cT|PQ!I;=}>8YMI|NI~cWgy_GABh(_x zxVRMm^SVfp*CjLo`b#L7fnvCm@VeKR7^Na8oxN#vA7%^g=Ed#1XzL6Ja&w~wR}_CO z4k@7W)~!g~2b?w}8Ul6Z?SRaGhrp^4N>;9Pp1{YKHZVAizzB$eM6Q<|T}2+Eun_7UxI)Fe03(bBd~@M7GDY9;h$c=BIrK42iYEd=#!P6Ft@(m zd$i)^sRD(Njb8AESshIQ3k)F&1@I7Cpz3%e>jtXRg&F{#?nkhPI%zWL*JHQK<*I&4%$L?8+*V{2!K2l^!-?ub~_q8DJ8 zrGc>ygWk94O+WDP@R%7HXG!Fb6yi=WCUjcZ*tCNIA|u0s<=yf1LYxX38O#!}{!N3v zo&X^-W8(?>hmmM^(>AxZhGZ1O-|#VDXSf3E>LgJLAHm-cdsa%Cfx@{nRr&>%})lENnlN1-8k^i?ko@0PP4a$OyZ-yDK1ySiSGPRJj1z z)T-s>TA&!jKqU+;ZX_o7Sb52b`!r|=LH|QY^@1P$Z4GDi_@LNKi~g|+)3P;db=8e4 zFR}bg1ZUDyEvq_$qci_dBwe@o_AXX~+CG@1_2y!ZyC^>!!J?nFRo{nmBSsbmp{2Pz zj4lc<`YdSTOtRk{u+8x~nBcSg;`(v#aEa*zESX@Exy)epkdJgP$Hb(+TC%y(==OGZ zp8$6Azmu$SC)q#zMH8rK@ImjI)E3eKfO8(dd>IMjMkT&eivXGeZbHVu;6!=FB>$|J zV^g?C?c2d9QFW1+#Bu04 zbp=ZX9RWiIKD{FSXc&>p;v)yQ(>!|#$)4ATQn8KqxQGT7;tv%fn>6nDo_dg1==P|q zl_ZJ5G0bE3Hr%=0T`#Y{#v7v#WwQ#2;-1{m3{ufhLbR3n33OFIFKBkEYw0ZdU&4cd zDT+z*{^2?1dNFTzWImbQQdSS6gJb8w;7P25GxX{E#Hi3BmC=*AjR+gg+?8$m`05L% z{QP`JImoMr3aXWZ8XD>&e@c~v!1n^wnqE{yN_(PL4BQ5ZGRX{GMmZanj1ngTcDAlH}k;^V!Hm%uMd+$HaR=F?^t9*pn$0(G$L;*3x15pFXnPdH96+8@IUBd)aQbaA0;N3;QYu^1QZAX8tC9; zQynT!jddPY5c9&$x`L~lJ zTi$iwlY7VX?I+3|J)*B_n96)-CqSV{FST+WJ9JB|fach~6U= z>VMmhDxc5u6HUc|fx`#)e#7wA&o`&r6w3#MXoz+FnD1WXvN!`5?>@9B`WjX~4(e}f z9eh3@yWqY5L$)ux)iS(w z$Sg#TCFjb4G7EXEFHls@#VAM4-R@7?=KL`43Lp<2;E%E}C1g}oH|azW+Z9S1fckiA z$bIOW?zdZx8MhC=JH6LZjx#<7 zkm;>)?S8BrJS|oEvF{$Cs^)HJ&uSYtRYd;U+0*_!aBr-V9x!jYrVL zIN4w;EuGJ$vw9~7eE0;sZt=QaqiiZq#I8&J+4@{4JN)i4@zW$j3ukTKv9KCBqXLD^ zUOi`P8K$30FJ6}q$OHzg|56-wnR+a$?CkExO(NfxDv?Wme@;B*aL@Gdy-SwfOF>m7 zB6u~)65L-L&oPzUEk92KvXtn=&3OGfrKqTHmPbUCs4vAHe{<|4b z3?wmtqY4FTi+AU4@(KSws5;f2SHIssLSz4131Xa?r6t0XCucj2$zpCGz`)TCN|+D= zUUajY7RifY{6S1&7RZ@MU<@QYaH1cF7kKt|N*3RkkkS64o%-Y1uWBpv>8vwT?IQVTx|$?k^U)m#Z$*A2cR*4KbEftk@XbT6%mN zGaTU)(?nN8NPNF-$-mn<*q#sYt=j0Mgo|5rDEV&4H6+P8ijiqX%+AjP@Y@ZZJq^T` zTDK&`qitgc?P8rTI$V(LoAyw;0VMQNDAP9%yS}F8>G!rA+_Tk zD&pZc!P)k8ih-KP6lnt_Zao{Ul%hS8{&qiAn`0KH5-7d3khWTY{;pROuo;3?yL>gX@;3$ILw1NR)!UDg}E@3?{m9(Tv4v=w@zF{hq#yt0ZJxR{R!G-*t78!P?N86)g9;sg} zyIbBh>5=npb=j6JuZQNP{|R(pBhyqJ0*?_0frRv-I?zI}74k%pQipq|Aeq}*d-n%K z)=O%?Ax$^~S;3n|mM>H{v53pkrPBcDNz4+b9fL|rMErrwM5}= zhtHoNO#)ezo8Lfn88NeNRNSz$vT|74cPXVwyyrLZz)E-ez}6vBWbF}9Rv^%3ynOjA zyQ@QuN#>e0exa-c!)P?j|ZffDdy8`j9z&7Wz>r zGX~A-k7JN{FYxNyAs+<#jmM)Svu;6lWtqMbxNj&zz`&oz#Z7uQ zi)WRTkV89VGQrSLq_JY^HH(9EWeV$eI3SxHW>9()>1hLaL@6f0ggx`wW`{8Q9s~8u zKP3lxZbUut=e#l*0V8?D%*q?-?>LnHFXRjDPo94m ztK;b?w-D{@mXPZHO?R8ivH`5P<|$x*q{0}$3%zMy^CTOz!DK<{7n*Xycjo`z@?|o5 z^6XhSfT)P9kA{dies0S016|KRfKQb*mJt{r;7RrX-u4~Z6j(x7?X|P`237k&j)feA zeq;Jl$;kYZuZzvuxdo}8k@}Ada2D3kxtZ_W8Xj)4ADp%-zkZN-8kWN()I3EfzoO(P zH#9~P6pzd7;?D8YP7;3g_`Zr}Wcbxo|II3u`%~I_Z89uQ>6^r=JQD;Dqp#6yYT|R0 zT(n@T3^L7apZaE*nmaVxWY+9*%SPDy+OMXyH|i<;g@khPA<{!*1(~Ls6J8W%8cpxz zbZxxuDxPniA_-O%Ip5|$7e1*@ujGDZ(mO=n`Va8tz+^)MF=AGCrxsvW@IiMr@OrCr zFYhccBSk5c1O&Q0subZgs))?@c;J9Poz0Sl>*DU-3bRrSF91t|4D?OpAv=JIMpETU zIeJK#UMBa`k{?iJ-lY2N!rwf-8pWo?Am4kqW4`-^CQEf$ePXd3ckj|Huc2p*mFs8r z$Z1L9KW|i>9bBHcypStoJ$I-^z`FAG0T!I?_MUs!j!fMQ5w>jxl+A# zQ5X^X&U*^-eLb*mp_4I+C#f8OTe=MzAKzac?%@HU7=YE|ARQJ$5)uA_00=IcLBO1` z1c7K|QUGe&fJ49nPBz4YZ)j-fn;i}IjeTl6XFFEh^kUm?&m^Tqb)Syc&T4Xa%8%w^ z`w)fD{~_xuqpIAZc2NOQQBXo8MJefSL=i=#B&54TLb^kwOQcJr8w3ey5G16#ySuyY ze9w2k`{$l99FC(%?ES8_=6W(%*Kp?y6)jlbaHs#UqI&I8L&~X&rTacvV&)%(TLX3< ziQW>Vt>_I~Up@adm%P*zN231ygP-XYkIWY9fw927+DP|?0?KHT#7{I>g8O45+nQPG zCw`RO@~bE)3Wtk7hH%s@7R6As?{~UHnZ@UC2;>hRIn%qxO75)V+J0@JIJigsWURiT z!Un|GsK5bz2?z*)C8C508g&H?7d%803Bm|o>*dGD^DjR9CeW9V5}*pm2t!Apisa-c z;<4@nLa4vnUx%y?{HiI;Ct~cD^N5%L7tT8&A0gom1kW}uhH-BW32K4ErUzOo zL3@PAj)i>mq%R%iJeFJE2}{Y(w%}6Erk8Q@seSc9J)?Ryvu4Slq>|40;RHr1LIH)rLdEpHIExn#m7;JE7of9e=TgW$)AP5*A!bMoxkqG z$x&To_h>C>9mh*Qqm5S?CDGRw#hAAhI&OdD3=Z(c`+oERJprJhG}Z+8T5x%@UrMl> zURv^j{1{9);)HwvD70=a1el+r6|(seaVJp4h+p6s<_Qqg?ttbOgCzE~@32Ll_#dJh zBw7ZG8wTcH1Y{FiUlfiM9v^Tjcbka~OmEanDmv!gK6O*x3fe7curugyGD_9f+(Y9~ zCzFd)(YJYv*M7aW(IV*RaN}B`1pSY20koXpjts809vF)-kZ8&y9rzbrjOM;%EB}~LB_Jh6`R(62f$uWJUA=#XlmaF5D2F3l)iOk$OdERR zOWJbex#L*cUc(^j52FRb(6nm>j^i3eWHiA!JNM-!eL7o5!FIiQmZc*1J z;Y3~Vz2B|ch(}im9espLV>Z&B(ls1h>M70}pSJzc^^84R&mC`U8~CH^RF2$0`BR)?%iYHj#A_CEb9?{^`! zz3fyCl-iGfwY#I(-GAb|v8|B3a$I#=fc3*7&QBFt_~+NCtE_%Mcv>;6^|zJwSD&e< zyw1fd%PKNizru<2oywSb7SJB358bSu;b^&m@|8%#*Ge$;21?aUhq@63m`+Lj8N;9K zl%#zpI#t6b_f7bEVRY zo2v`Z9~-F=-tLMb^buSCOHiDOWjPlxy{wJp!^xU@{t1nT`VM*F!>1o#n$I`lCAqk9 zQCwL*;>oz(Z4%3bOQQUe0-u~I31i8v0u_NQ#6Q}`i;z9E$2xR>i^^>mIw#ye!dL6L zk`5w$#C~8+@YydgP#8+u5LN@^zM%C89oX65|MKe>8{#V!A#=e6CnFv&#S>!`fOC@d z7vRtZb>AJNzE4_S-W!p<0Ffi)LmA!Ksm+i7xFPw~qDH-%WA1}H_vB{}m_>f`OAm*dZCEoZFoCK%mK{Ok~PdX8j$PAjy5Ddy#K{lA3vR8ahc4 z5jzWz-c*0?RqIxzOKiYQV6k4!#(iweAMb)^IHBa)w(zVf;p-=whZ)Z=h?*ngJ+4w2 zspisxAB>;9L-|(2Cb2vyWBbvI?#v4NtaXa}@bb}q?CPB?S&8z5K{ed&O!i?xYWx@$ zhvmbq;7rEB>29JO?qf&d`3;hw)|Jzyg?L)w!5-(-t#)mpAtBJsJYisvSY}4$fSsJm z+AmfUs2oGE=pj;bppui(5e3MmFIY)tA$H{@NS=`-3dGC+K5Km5+{$;TnlFDpt>OOQ zwZ3~QMoL_f{C6{euu5RKN7vpY2E#0P=#pnd&52|TMN=de{nr_<<`yMGb_sgFK^q#! z;`o|xbrgGMMJrG={mi5<^IN#%Z&s%-npf3ZY?y!SY!#aOx!X?LqPlKg&TkWgPWiA6 z1!a_$WS>wV_1=Ry%guzLI|^8f$&a%d*Uw{F8RQiHnJgA`C+%CJDWCOIgdHdgFmAT6 zORnKB2L^hUbJP;Jx81ayzQ1nS_DNR>X+{F^zG2CEZ)qWr)Yt?BzN7+DlHxv+YHn*=+MR@$;+u4o&I%xYn}G8p0i-hmq|IdQi%$(U2}D*2V~A&i z&q*3}k8n#Cc#3ac`rC4{46GR7-F z9H)G!Pku$58t>X~E#=U+sG<9#94`yhh@FTQMcj|+O*HoLKL7s8P7)jlU>m6}c54q6-)_y%rY5S5|^wp2hh$utzg z)2M4XkPD`Rp;p; zMoQ>y{JY$f0--2JHnL&|b{_YtYw7EMn2WI-SGZ9QpyMh$>OO0GuzYD$r;@~?+5ZO5 zl~*B*yK3u9X8*FNaegX;tG^>p?Q$k3J9`q5pa|&aFI`vG0$%nL+$yTHN&qXJeHlL*YiDr3?^$ zteey&zNhi+EeWN?9Sd|6?$XdCn}70p0l2)72T`9#K=1@0n#*zELV z%5HI*Phz520RIff-Zd1E_*H%-SBe0i?G49jJ=;G{@QBVKiL|iN{U8DGTINaeM-cxp zY~KB&0^4UJILE$*hI+uriKyo$sBFXEaLxdB2^Q8b)faghrW9I$bs{7-(v1T-_Yfjb zXtg8>KB~FzPMaX$LWM@nj!qMEb1X;%{rB%3qJt(SB?St;L2dH7aTcgn5cA_x&<6vc zhXi&5o#+AR#2bc!1{YF-ZTZKlm2}df8bQk~+^JU)Xjz`9H{dFhKUYQm>Ff0Br7E#+ z;424t34z6WF;8j7EaLA+74=UW(YlE&K1x3*9&m)ta_V)Jv6SU4ne~&YTj2%@^+`dP zS9Y4*!3^(&40W-%e-9@;&cby{6ubp<$sDWI{|^&B&ttY$!Wg}4=vkB#!M#(@LvbY+ z7QIgh-si1-Z!tTxY%%t?W8XZM%XhHH0$7%-A6q+g-?7b0rQy}^hd*rLQSxQHGRBgK z!B-M7c~JRJGLM;?mSCe{B8g$>$;lEOchTefjy4^yW>Q$Nrsr=ib)UJZk(+O!nx@?o znR@P7EXW$`$h{Q{qxlbjc)%C@7m1mN#~bYXqjl!aBExNIZxzEZO}#ol&K{pLMcrp_!1Bbu*`SHC z+l4##&m&$X_-R{TFRvJ%)gmTD-8~L#H51rjP_3}O1?miR6wF7gsPN_^>S-9skkry& zcU3_~@zZug8F6)jL0w;E%?(&j0$ytx0PB!6iuLhw9JnUxPeBX?MjaGve1?iG&6z)5 zU*Dx6F9i5K5qVlXzrzF2$sl8U;2v~c5KlSp-32iKYz4PL4)W#8muWxh?=RlJ|Ju4s z^x($(y|j7Qu@J54`ufN4sHg^@=b(wB8DPZ#Aj42lK)Z&twaq~(COe}6AQnxDU_O5Z zEN9cP6WII)^VI^tsg8)5K{W$7Z#^`+AjKJB_DIq(l3f8t?c>X{twJaxK(Goxh!FS* zQET*%pj#n24)DTWLjfbJQ6K@!buTZk%e;{l&A2bGi&`pg{=0$a!SYySlRLUtH=)KmpJ z|9vHNDx20-kVI<1)Sy`FTHT$*t_;`ll*PWa13_ z1GI<8M)ZI4P(Z|w$Y=k&MUgn-|b&VCcb`R#uB_c8npW0?&Iso*PjoH`srec zsao-jS%2YOU_t$u z*LFy7dHf)NdG+iXLL@kS7r1rPz-hdU75AtH-#yyPuW4n|MF2&G^f|^r#O-1%tA^;H z8=nqdF!rYnO20{kjQ}zI!g(q!C-(*3)E@guOfriri6uNyVGK7e4AWO?k4qowoTU;h zXWI}I(W4d(+IE}V_5Ds1GJA)1|5%C|AZ7GIIaF#XkyP_8;Wb7}?uypOR)x(PVLCtZ z`e$ZLUwySI!pwyKB>g;oP@jt@KCkX{hQPJsgCPwhIiIL(sHdl})4J>xlv2Sr+GNR38v znLsW!nJiqe>o>z*5(phM#pY9_08p||(jZ@St7`8axNS6x+CiL)3~gCg|)$4$~c-W)_q=Gp9$_LhS0(NJh9T^V2CcUYkjX-pxuuy(Y^y z*L}K4Y<=CKEyAcZr1u@dv8K~ z@Db5LSs~i`j$PO6PQP1HNdYls7Ds{ZS_!&vEwOoS~)mKM{@UNTr2(a z=c+?picp^TVN@6kSPwXcr$-Utcxq>1X!~Idq!BI;II51A)O<)B(zJuy`u$(qn3#?? zX!o-3lROG>+amVY2?z@$kxWpELm!BU#s3>4vWt*;Vq9K@ds$G{!UhZDcP3o`tUUrNU&iO{l?)9xiYYAf@=-pY5;l%UIl@P0PNI8phAFaBnENt z@X-EKO(0VTa!=%T*rfnZ!*vw!_9qr5wF0ze3>hjgY>gD@frP+*H7mnnzU4kj9e}!C zunNm0KSG=fm3wTh8fa~rmg12>0~oR)MFWYfdUknFQ)w0}Nz7C> zZTYcOw*a^gH&A!7QBg?qRHN$?b28P&MPbKN&ditlQ6fdBBg3`#wuS||-WgqQ7w;&{ zuB`h6H3(}@&z92Hp{yr73b?Ov@M^H|Iz?`kW^P2>e#dM3yE}DmQm?1m6|&x*^5&&4 zYz3YD$hnSYAg0-^p<)EoPNNJcj+3U&jI51KX$}e zIcBkGkfH+lq15t(d%R+OI(OUOaTjM9O{6KT1udyBhB{-aky^GLUtC<68?Z6>!?9hj znc3IN#=wx*S?uBAVNq-){76wl72^&y{mY@|s=!)k<^$C^U?0f<6yxFHA#!pE*#b`m zQcE3?=zSbjrgJM3doFhKDSGL z`2zL3pwNdoLF;&XCdeQ0pf;Hf8nVMifZ)lH7=wwzOho}bu%JK@SN1wRIT?6%wG9?I zA{|m_;D?9 zxQE^gDG5QM6Kg6fAOdKBU5ti~j_sZTZG=7i+aQf@=+R38gdT!ehrwBykZLA-1af^Y zDjSvGFtXhR={vH51F;H+rf^M4lo3CB6QMi22!DZO{{+JdI;G8B^c%=+ONZnK%p~!^lh-((s_Y26X08a$Cu#vzt zr2bGpW5~E9n%BlSfg%8ym70q~l`6YGDB;mt4)M8DDIw2kt>L_al(wYo)kOIcnP>mC zCqETQt~AFwKOX!(rrsGO&^Pp8N?$~@guzV0d&SK4QcU-O;lKQ9d`F#v!%@q=;6q^5 z^EbQxlFebos7?OOi}_t4_)vh#=Fbg{iGTnVHn@q9j=m!)6>vl)by4j$u(jj2b*3l{ z40={9DCPne_qW;$=!huHF4>3O<}~8)_Vv*vc+P zgG<_bk%8Ppp9C}D5S}dKYMlyyr><$PfkudyWa#?$xA+iJ1B%2ut>_rO;^=? zH?lb>sU-p%IiW;HRP<2o#wVAez0=6$p)2~(Xr^m@X<`rIY#S^mgG9Y9OOakq;+lOFViJ`E*MiBPvg@kD08qUTmZ%hw4S}$nt z0}DCmXr>nyypW(lIL?uqJDlSHc7o&0Tzt}B_{sU%)D=2PJfB@>^szit=-y(vm@TnY z-S|{3EqnOCPdX@Qkok4;rXsjz;027v3~O6xGUqXmQlx(a8;gWD4=-;leRF5$Jp^oo z`G6THFUKgLj(k8Iq61AYP=)>yqK*LU`?#JLN#6%3K_OuF$i4>$J}hw7n(sDr;An43 zOo)%)23ZhNqGv}d_XG#@+}zH`%qWk6X9nO6;_YH%Ge@(%DM`g-+NSa4$`(F}8b1=& zz8`;qOLLhKkv6Q3IYjynmwG7gWDpxp7G1o4es`XV)*sup!mSdRLiHB^EF(0AZ@yn` zicy~5->~?kLZy0++Z}F17=DZ|D!fg)>OtpZ)$JhXG@oe3Q|&rb`eqG+hzc8c(`24m z$n`4%1`W(Rm1qcnlQ0(%X_%;&7~>&)yT^^2ParS>WZIk>k&l^~e~K30xcFuB?GFqzzrkRB{L)MgUBmCt!qSss{%L zLxfPnD=dL8pav`wO?Vrx=5`+kOVFM4YUNA7*stk%MwrM!TQf6$v_i0}MYitr9DkLS z;k0w@7T_ehP5lf4mSdPNY85UD&28z~^^`GB9}_4w-wsTB*j`SU5C9UU-&=j}iB;lJ z0g&B%*_qj1fCyJAE;K|`M%EW3XHtY_+LNom86wEDNI3C5+AMI|yhh^4g-(JDey2iJ1_^0{{#0Wn_S zEhq15Pc(!E zhe&%5Otq8NY6s=dKdH#+Du2!{Ex7(;@eYwytYt1hvl1X>St-pZFjoVtr76*T9KBlI z{9AbSrC0+J*a(j)iZS7D)kJYpLD#CCrL~Y85OS27nZ7VBh-;isgzI|~2-gE3J27EM z;>n%nRI@una6Zg=rL!nQ;s>>dilXB1bY#_wj=Luyh-B7o#6aBc&}Sl*pdi)0b9mSU zg3lf3a2!4F$SqNVHIJ0Hpdb>orl87;dujP)!1&=G+}4wAUtM#*9du2jX)0#Oul7WN z&e^euuyBNf;l-c;O>@5>;iW?v6^IQ-9PyaS6@wF`h+hE@$87C@LA#q>=q69^&y;Dg zJb;R+NZ@)bWnFK8HJ&i4^fjV)xK2!k z_a$WaMzzHohZ-M9XveY?n)Dkwa|r7Av82<+HVUV(95UkLu9M)Ru9&uMVa>j|PDArE ze9wWDOlz?-fvs|Vb3KM{X)tww`mS_L&fCCui)B%VDMPE$V=+ChuClo%%3mW=XL`H1 zc*@VUYc_2N1q88%#VQ>n>{sNlieq>?uPhQg6NMK|R6-}nNRGL!dC;g$-5ku!YxJbo z@)u8BT-r>%6Q3$v%8JPArR#3-QIpbr_m}Lvn@t54Rj5XK1hXpO!g`Rs3j2vSI9dtS z6Z4cy{#l8+yj}zuOiauppqlU;^Zk6_8mA9<5Gp$Q^BoE!EZ3_Q*l}w3F3qNDIv_?C z({zj4W-IFCx62tPq+&(K#6TU-TZq!@;@`uRmzQ_=k5B0BeLzP?m#t6`?B#`Kv)Z2o z0_(q|^A@$WXB?LZq6gUwSER+V{RF@z>`~BDOMMS~m-YvSWo8eBm!!^&w+XdIQ$=wa zNTdMn%7Ag!+Xf&z_H$wag5#gBxUz4WTx?EOkG+3)&GP2Yak59ICMI#zw?=c7*ud!e z+j8MHfOTx+E`~ZfI+;G0gSmJ7v{;u5x%I| z-0{%zcVv8}r(-Z}JUwlv_}aqX2nH6_kq2Ss<%}xLlXjB0vG!vJ`ocAOR|+Pw-=_*p zYdcCdt^$KUWa6q`9^gOZ9H+5NM9C^+J>E$BY(96faB}YV+FdNp+|p>Tn!uePJv|++DBy_IjJ9B4VF?A;G^K9^BDK=t4iX(3yQoet zAwGY4um(9_xO9h*QS=>wVuIc@z-6cjIy?}eD!>wuuxJU$0|ZKeQ^Lfb#1kykNu2a` z@EwW3#?B2FJOFPQ*wbMSgxT&93O<9fFvwy3LPDCmx?2ec5o z`D8pi3~-y9A~<~nmF0z`0Y$jr)`wJJn9jlLztbzm)Kh6^{w*x*E{6ws8U96WSUw!a z29VD@Ugglo%vuhtk^>{n$nbDCbdy8WLC=acG%)(ZL<`YyuOPt*MBR(9#lkI2Jqfn< zG}kH!HHD~dv#CzE<_PR$=g@TpJisxUf$c|jPJqtdq{+ZY8t{bLhJ=J91~k&}PA$Tv z=}&)u_)AZ85Zana2NLsq^Sp`03Mok7Kss%qC=3h3fhj&UHFZ8`fa2dE3@;slvW zz!&jWLE0_`x3v;Wh$|K17qQV#K$K>oQWi@ z!YMbLtkBj@mD$Wxc*)Aj>e?h<`;@e%F3ywWuW04WZ$#p=+bM)*d6{?W?w$0A^E$+Q z`<}97pSzgkQjPmV|GoPoN&I*D8OsD0DujzvKWh>>r1DOe@QX$d-_>mDNVLR`>v>*u z_(}}S7$#|oa?uep;I2EiMt%(X-Z{muw4oraOc6)@1^Wo#sF|guW`Gm89rtNqna+DX zR7ZeEpv>^gC*2%y2?$!?Nu-;n@X<4ZbJDP+9JGtqukTV?lb4~Jz>aXT*EKH60C(XS zke@&*G9qk((H7YRoX5&5Dp<`Y`QbtIPDmgDT5TSxP&HwUb$F8|WF}M&`yVL6Q(97( zs;jCH37P|3*6)^lBFhcA}pcaPriWODgeNB*OP@P=LIx%_`SKNGL0VcI-ruf8598! zBrpe_{CMurqbJVrqusi5r>)Es;@J^t0i=sqaIJxFez(L4zNK>eohR_ssi>&Dp}Jz; zBxNXSW4?Q5N9z4`aSUlHiYz(^gkV6oU5P2shy+)+RN4!!)l!A?@L?(fQ{(67c|ei75@V@hv-$|t&bTY3_Y{Zz>}n~{db z7r@xgM8Pf~1s!13LJJWdkkHCKs#X8tpmV;R#)iZi!`|( zGB1ovzY~+JS1KK^|M5~H4Sn4?QK)VY#{Z+WuB{)C_{ky5Y~_YtA)@+|DYL-i(m0Z&YqoBx?VdjF)$tc>A>V&M zGDbn1Ss3{%Z&(-TNPY&i6CiQIuwvP5?O!i$ZApQEISF9p{F`AFSZvVr%HKK&?AZ>G zgF5>9O_0F^|95wBaS^a-DEC2%L3n`80azB`?QigzzsAIf0tXtyYeNf29}4WgKaKjR zkn1EwI-mj#KMh+C%`VLvB+bB?5a?41xac$6|Ar?j_qNv7`o>07FQIz3t21aBq9=vt z=V=>te~v;3_)73rjvP9WUc|%)J|8O+k)!{*OzuTKbeZ&3ncLQ@;DXHEi*R62S#%_*U>PC}@c#p8; zcv5yoI!pzJG~#|Y%+t%D;C*@2^2JQc#bM+b4-QkxC|zZHrIPdJ_R~>%hkP&WWEzk- z5Wr+Ac(IQIU;({GO*|M`kcUslM_%_xjM?{_om;9N6z(k#=EZ3x)5F7TJd_`ln3$Nf z0Y%Jr!$d=fd3wK!dyU_Z!3q|yo{|!UWtP~ZgN+vk*>Y(zNst;x;*ULhANH*;_D=vv zT>_aEs3GFv$wks2uA78?pok$9xSEJ<`fa+>Zzof@qC~XHjq56t!l)UsQX3g_eqoj) zN7K?Pv=<@Des-qO9VJ?<5*MH0;+?)7n3o;Rv*>p8;cc4t^{jwOHgaJ<6Sf={$!j6s z*^9X^?5%T;!-Q^0E4O!aILvrGM3@|~$>~Ad2PD;wFh7!Wz{kaHgr7Q)mvZ7kgdEzi3xp8gW&FbYTy*s|IqBF%j4JHxo!K995E&4>;7#{(8ikuo zVtxuF!Yn8eF)svj9O5%W3)!9tcEQ3p3rzlpYWtv;xj=I8{@v)msg;|4zTGR1e$(z=R0{ z++)B>yuYC{sFc!2*tfmH6v7~X7lrIaA$stV5(_wngVxj?7QroOyn$k9?e(AUge`1v z2)VPEWf!P)O-5+%F5o6_8XGMc`In2DasH|-d-i-_m+I}o3q@HD4%2Y%XgYi9#?NzQ z9I9fyp=mvP61w)3VMCvG*K8ZYEvs zg1p_k4KGm8Nmq@RyPkD_?`(^^L2!D@HRD?T%X|D6rgX-|`NMYd7}EIk^z=v$4Z_>+ z?Cf9?bAN>d^~qr^ycRGof+-W;5rg&jvOP&%|WbC&tBX4%LqAmZCxA>riNTB091K1u8R!Q!Ix`o+|RWhF=cfGpG7-?tg5J~aE|C^?@Lh7EY=u3Cgh} z62W#4(3a7rn6tDXs|mq2hm+)ZVIg~0_Coj+lJ8eE{Yi$C zld}p~8RVDVBkl5lml)*2E1{|R*vX1O)7L|PJe1wC!8?NZ1^Lg`f3X{O*9|8{L<{E6 zZ#PH8UmwylnW-+ixkhYO`iy$Yl5E&*pXoo9~|%*oXz|7tI@9&S5Wj8W?a9 z)~q&k-8c*vy}H&=-6}a?u~})7R#cl^SDp5>XYG#L`K;t*o5b>Uc{6<7$&OBmWueUg z6%6TB2)RYf%LvOB1Kcv0IiA7;D@F1_2lf&?IKe3S_*AIs9>5}}WqDTnI2*Rnl03nyz@vRgs^;z{<$ zl_HYaiyozDWe&_`CfbZ_3U{QHzq?&JAOjzKYDBjVvgw?{SJ&%cups0xetQd@yio^4 zdvD^x-(yHyf9uElAP=$1FfF{fQjms;0q~$Uz_eF-Qf{KaEhH6YFV@?_7*E*>-}hYh zr3e1*j|=5G>pg28SMgAb<;G?nAM4)qx=^#&FH2cE#eVbmFh%|$7YaY`@9pt?$tp*_ z@GIfN#+*t%lvgQEGrf)ET{Yfsj{ZFBey(vv^88M+yn@|mhxwoFfNCS|e;NHDm8|{( z&QZKBCIqeu{JNZfiigGNC)x}1%|bKs7*%}yUYoHF<$3AUY_P{D)2q>TE`?w(!~p3I zq3Zfz-$J5u0PqU}(36ml{uTVVUug0{2oIUO|A00L`tPt!J^!5f4Jl_ue!*az1WM)* zc!yZP8-W6DDo}^%0-O6LSRCX2?x$D8t0O-PhZtFLihkzleJ~u86pOoGzROxU75Mn+ z^E+#V_|{k1lujA(^Zz0Va!nM!IFy_x@lK=L^iyFK7lF)RCQ@`#o z_=mgcQ!Knu`GUS*dqFzZDihb;qc3*Wovv_2mR|Q*sbOSs^|ku8xheQ_iZveyn&X_R zI0d)!HXI0Ug^%uFNPn>29FKv4ABv0Gfzg6~5lpoTYZhgGI7H?_gU>R0DdzbfST!GN z$Bse%y&Zn?M|`&QuuSzq%g}UB1Y-vq-g+VaUXU>~0^xByb#-Z=h)+r?305(f6j`)& z7o?<3W^YT&h||ll+qVXot6aRCJ;8l!hiagueKy(?(G4Qm=uRs!SY#|J3v$rQ` zOKCtHRg`_X$4AS2HP%s-&H(N+YFha_O@ zz_m8LD#aHHM@xGro$E($*~{+W>ZaH+_&GYdEz2{LKR#}hi0=z69ktrt@*BKpICua> z(QEli4!tLFK%PcAV8X7rK2=-Wenf{1{zc>q55Hi0#X?XKnAJfA)cBL0oZJJbBVeSo z8(RP%pwy6P5YGEEIppp1nmF(5M`}P8HwP_uVMJK^OTB#7^L$(8hW5eY?wbA4S!zEo z=JZ&#cor*W^3}FAL$;Epdl~IC;hHOE9%*reIjf6kHD)CvyfWsxU!7U*N+=IFe0--2 zZ@tm*PjY~l4B%P_ool4#=364>iq~Vg%!A9zc>sb+gFQ^IND5vdp>PspYP($wK6J3U zAWDHfdI-&-h6G3SyMzSKimE=g2a2%Pw3{`|eCC?k7C+x>SqPxSGvD zwf>wZ12G&?4zA`8SMTQjU0`5Qo0&Ypot2tw$k33`K5R~T|IB_h0e$=H%lg)# z-tG1IerD~|K9Mavi5-; zYZ>=L&7ucZZ_DE){|#8nhw;~`U18n>mtRqJd z@z()Labz&3VTEW<$WYD-ul)6eW()bFmL;&8AiiwGKxUq3hdKoyG~gC&GP)-`Dm3nHY9;0^(oIJ0u%euhW8FdN861X-8V?2V`~k zQ|}cH6BCURnocFp7Ut=AT`iBQj#BM0xl+oHU2&yjNIJC(t%rZR@zQ`gG zm*yLu>F>YiUK}mH`CXi9M~?NDfxnv%ihGvcz!~8Ke0*;J6X7YKrls|V*CXQexzqk~ zJy1W0C=j4nrh|)!^s_W?T>ssj5jtT#@YMYS+-iG!o7t8|$X6uO(Lh$n*Qdg6ixhEb z_Ge0?qMe(KYgfB+GwFCCSJ49s?dkARY})XG#79w+J9Mj<@8YIB)@~~ zZ*VR9uwz@CCk^RY#o(~L@`LjI_f#MMUoOCHJ1NRXeDTfN#zsON`JKN@E$aTfRx>^; zwB&03H$mKG#z+w_IWwrS(@0k9Q$yfsN9QCl@dx(WN07(@gu^Si3qa)vB(Y`m9|HI( z0%QdM%3@wptLhcjet`aW7aPg;ulAmu;U{NXSy>^0lTG(tVMwF0epkl`Z3aFH+-6H~ zxo#;lgMSQpTum6#c=?f-O*zHmx&Pc`9@dq-=FstZd|OjZ_FF~rvE1Uj>&=5rzcT3j zZ(B!x)He}ISTgR`mb_7Q`a8l{=x8b%Z5SI{KJKf-wcjP-M-9pF9%zC<214Suf#QO< zEK+7d1HKeMZyL}K!Z2_VUJ6!Fmt!KYjS20YK_<+YLgvztdY>fa2lb0F4@o9o0{1=Sqw6E8O>7{?L6Hj)Vrx@zgHjb=zlg-}x9(}!fnd|>k@|EC@ywi+3} z^xY{KxNIGtYcKTmsh}b8KQbW6_cjD}yW}dD_#>R6mR6{_i>@xkty{Mi7_)NZU#_j< z=?G`g@QUGN8kw%s_&22~q0wBb{FS@&lc6uvKiM*Su+AI3pg(!iLP|XM-%z8|ASr%7 znNmO<%cPw7obSJ?^Z4FMcDo-lisr^X6)Eft{Q23+t}|0`+h{?8&XH*jxE44^(~1(o zXZTh|0j>|3y4mxTyVMDPS)M=t4ms-&{1;m{z`F*@N@8M)!pEdt34C_Za00-6ISt+* zw9<0HcV+yYpHGv?&RqwFrG3Bc2Nqn{Lu(y~8;O~o>r-6Z70-6^YJ8eq4@NPh6i ze8bo3tH8tB>ef89CYJ;S6U|p0o1Mc$WpC1GHT)KDK9jp$&J?O((j^n#!I6q!h~QM`QXK&uz#?=wyv`ZuEz}WFE;g($H7&>K z0w8-2#_B#;a1bvJDo__+zY~Mm7?>KEAvyzQTVe35La|1T7XDB(-ouB@MBpuj?>{k< zOuD244srOYK%=2-7`LnFIP)CJ<@0ol&C;|?Z#SIN4K~Z}Ww|!pdwJfGSQiuJPf-{=b&3@zLbyS>AZ zWcV5oeq?OoO$Y`a57NY%0Ko%96!MagedrBHeqruFXd@^wEjAvw4Z9fV2_;~aj1=nJ zjEuYzZ)62n26`KzD+3QeMWEN_78msuYazQ8%#D%_-gkMR-vEAV06GeU!xh8L(2U97{q2v^Fh6Cc{H>ApIxt;XaV0PHCkXS?bO9MuRi$X91jUiFfr&@y^00azcpG2Wj(7%U>-{ ze0*WNimzYieyu-WEe;$>RuIMt(g#&-JJEynkuYGS?&IOTu68_lm)cd7Dn|?55;VA! zPUIQ|p4Ebrx(yi*-xX`@7?v?*?2Tm3_3xG~2$FHtO_o?jO7^bp>!up|*2ak1K65^# zi@3e6)~~3*_P71Kb!!$hSu+4^rbCLnH3*ndpWq6GG% zhc&O7Un0SyhKn3u!XT4VE!|-I-x<~WRnsX?sWZ;0DsLwbtvMK*2^5JK|F(Lszrcxq^;5Zj zdGmO9%MWY?AD^?~YOd+LoL$-F!>P{tRV4I%X`=Q?Y;vm3kv{ka{gBRKF`*V!AH{qAb1RP>>znZ(9H?OR%d=kOY7_6K%Il|5J{qx z8lP$iZ!#$ars8)+MY4R3SngDK<%l!e0e_SvMZ{aT*&Wp&aed|GYv@vYPk-d;#B3-O z3<)0Ett7X+8``~SzZ1{c@IX!eFdg%>8*7WI97q0nr^ya*C1Cmc206_T=<=TSQ)H_D zX}Kb?o^7zPF5b@guS`&+Ldj}lNKa#9>ajvA)G-6=-#jun48$|wc_4FX%8|pMfQhGFj|9FcY2SM5y)TKq4_s{VlT^;oeBl#C1!ITKA5S9ev@WELFgVH1FeiilZ) zJIQ@3kU8Ge-kv)=Hu?LLC&Z#4&O$9&a5H2Bo(@ zXItGbs1NPyNvlb0)-u-lT#~bpLKOMxhzT`PuUe^6d2o2(G=oA&Ui;p&s6Ns1SV?`M zQY{Bt?R&hYn-8UA>2T=B`xS?Z>v|=EymAO}0+RnaU&Uwe5B_$;ZFaO}cj^iWyTb@^ zZg&I<;IOS5AFt~DUi)&BXSJa-M^#NN2Amb(8rOqcA9CbDZu#>a*DGks*@m2N$!4r<7#;#;AUP*;8t84YMIJFGTT-nU7)imTDvJsny*0PWv= z`B`b|xUb~G-cKC8%cBTeCZ7fK`1Fon9LsEjM7X$TCHA!&4U-vd@3cQs#Ta2p=}$_n z$}e_d=E_5f)Jz@s3XC}tryTkg%ol_@9e>hiqpm`T)po4B((E7pw z(FanJl*>F^y{X%p!tU3_WCaovGZo9W6l~N?>Qy=-GTXMQ1hH(cbbH68CAuUSj`tSk z8cC`R<};))ZWYfxrDwj6OiD* z!PcG=GefMak$skx*%g|W!_zT&^mKE(S|}umpFcz|iKdy$ao-TgmJWEr+kp}!V9{>G z+2{lA@#=g@5Xm7*QPg%IQNT&#IntJB0 zBXLLh(5m_GFK5;Eg7z-*@XkF&hrVenq^`FiQw3>s1S^s^DB?hU3mp?+;~AV@j&@pW z1_h+3loZ!TH#glc@@HK{fuJ;pIT4cF=H}<+^qW}$f=W(KMl3W)@OaEd87s*4y;G|T zRjcenVMmBMab4<&^Ldjp69iG_x?|u4hXHP{--od){5FA{l!qtba~=x63I89SKA-Ip zuYaIC!BpUj?bUN(E3LK=eo(3RV-iE^^W9*R8c>5wr16mygWMt^?{ zEFU|IqqgphgqYC>YWF1va1Ry#c)8|(^gJ{yZ7wb(IgkV$rPk$>SB2DwY>L_6tgwQ< z)su|?y7BUFm&;AFeiPN9^-N-nJr{UP<7;y9eu{quvvp;-C@H;otQHXk^N*pm7!aJm zt^Em{mvAkI83%4&6wJ(DVblS3kR1S5%&|%cj+f6FDK#YmHtRKzFhH9EUhS}!SB6CR z!;A^??0di$tgpX2YU{a4=8TlB!ri7FC&Qk*M7aatBw)N^R3JPOG#Cp21~AB+fxEfy z_oq3T;OzN>+`Q>$c4&g}Lwly|x<-`sDwEbZXs<3J>_-XszJ1G(pW&YH>d7tI% zdf^#PAc;)c99QFwD`|Dbf!yDTPMs1PMpv(OY^!arCo~;zNtZa5_gbun4Q;@{Pz5Uv z@wV%CeV2esC-}qCweo|TS?sqp5j7Cp=h})@MIE-!uGAJbJH&N-$Vs&}`hSUE@Sw`t zoRkYx(c~vekT1%_7B(;+yDmCR9v96zNAXoMy?juiCtlz)F*TuGN1+iOp%*TJCsQ~{ zF4h8&7K|LwGUx+4;R7fW-{C8#;jJ#Aq-#M$3tfjSWe7e15Sn z3t|eSO#uZVYQdb#4_ZsO*?NP#B)Yx5S5WIGoMyef-{4&eKdlk|TME23((H`10>VBV zP>j6Rw7PKj?IC`HMvJkPGCh%j`};$DKz~G}GFkyD?Qu=iw<8s~&M)~M*W*3cSBh#L zRbOG$oBL8{`zvcC$20%pNE$hF-52bSoHjb3vlcpf9%9D0KQ`g5TpeV`^~ID#tl|P! ze5{Ko>1&t+`9B7@n@_r3xj-#%I|Ms+!rV++SpWmn%;sk2Xo-m!^PgR&9b2lgAyqb8 zf@F-M-eTf$Vv}`d|BZf)ou+%_Azz}!UkMx~L;nVdqEh6MFp0pX-0vFz<)r&4bM%~uZFwf`hfmaJ z;DxM%KF@z_VWdv6qw7ocLSvEsOnCR6yuM=QKHeth33WOOpdzSJIU;b;nNKxt6&?7R zko&SAkNemDWa6i?Tx#a`u{6?r(b~qQpZ)X}#QjwUm@_uMdF#5UCOR$2`0-hW1pdyI z8Htk2c$FoWER$nlW-L;IqLP~(w}emmP)H5~ToUzdZ2_=SEf)UaL0{CF;sp{Q1Wa-a z00uC#s{WCV>{c?gfVx4U-aP5PJCkVeBoSvTD0-VeA$QQ9=w_ z0g;q80O7g3nJS z6t?V*eJ3>FKB^Qm6`IsOiXO;UL5EkoLcoNB3!+9lBY~>}F%1QIt8=z8FvAE2PzLQIWG3GPHO$Z09~4D}MvC;yALB005$UO&}ga$$Y>uuH8~zQPv1fXza?4yg_`!A2VmG|cDN8M3#7rBXwgx8oPJBI*!eY-1dg(vJKUbK zD`CjS>TX`-`Q;?q?;t*$MR6eVz!E^CS435cL9R}XHJXvYJqhH!7)lSJn72t-TOg#W z$)-g`MRx$sAmnz92@C80vm>EZeLr>YVYRWKbo_z}0FcL$Bkuz8&^GzGQ6qK>YL z@^XkhUWyX)mOQ~nV9MJS6k+-%$mVF3leuZpUuS+S*K91k$GkprQ_meo|Fn#cO!8vJJ{gyQ)LTi= zOCF3okcUCh5N9JHT7b|zC}5`p>Ow5iN;+(cl7$eRfU9D+xJaxP>=dGO#t z^xW6OzoWi+ik3Ed_eqv(zj@9*rM{TQX(36{Rg(W{wU*~BJ4={uvEo5a-ggxIo{4DG zifT0m$pGd?_t7a2hsS>wQdvA0b5I}<^Pzfz$usCKc8&C4{o&IxGdIT;F+{x06PMM8 z!L|Z5kf7H=g2xA$f`AnzIM+Cx)xaacygR*e+)E-s8R^8mixCdV;~a`ybW{oh-+q~X ztkkmqT-g-RRdFT4KPX>s7#GPh?RrTl$9F_*KXL4W*vL+W_gV-{Kx?psHX#A#fLRpw zt{%PrK*18Qa62wDuHQ>l5NgIm#0{D#L2$v;^ToM|wFXmI!Y7`d-{E5Qpoyn<$f%L{ zG2{mNVK&F)CA+;QHa|P^=B~Z&w!-CcyoBZAk&|x8d7DM#)$BKF6jqQHa}(`(8^v*oi``CSdoqdt_zGRz_W4O;Z(04DS zsAdAr!JgB(xkKwSOVRNen+L?!V15LMBExDx5+e?eqE3vmH3)y8+2cG6qB(saDXp0K zBV$rdbHRkg!Vf``n`zO1+U?bhI7*w-zh>lbS(|HPHw%{f`8A|lqL;5k`At}0$l!g44$_aSgGNSh7O39y!V;5-nbbwN3Rhe@pVVv+vcgTxizOZ-&4s0 zE{%&gZ7m|dB6*Qps+aeo-Om?p;t)UZ6=aIo7db}Y#i#hu=0mXcTUrin+xAFcZR5s` zL_NQ_xLEISJ%%g;1_+dBT0dIMOq7(AgpeOXv8#Y`fhHMXs3vA!4H~I|wJGqV2X3hK z%6>K1wPS+Us(VA;@L&{#WBBkH(cn=bXI}6)%J**hOdT^iG2yGoq&E9^V`YNowGX5* ztnN|tJ;O2!z|nAZlWm1FuEsL&i8+F9gr^6R&`%lHeB(nHMy>Vt^FGc- zv<}_5qk53SgV);5jf%~s;n{;UYFZ{%ubfQ+!@u8>QXUp-%zDAkUz}Id`kD3=4R&2J zj>h)q;@IsFTjI1kvVIC9cP}h0p#zL6$F!>)3;HGB_B0kV21HL12S-PW0|f}Uz;Sts zkml(5k*Z5?qIAD_L~Tjs&*yp=bxn7tX717>ld;u4jffQ?phe@b!|ajE=u!px3+W?yc$$z4Xw#MxCybslceT&ieK5L^Hx>tZS?}8 z`a$I#C!Q$MGC7^_c#rNW)ERmHTS6)eYwP}$+^@voagRrjI1C$(V`7RVpiseNMohE4r7j_{;AlS<2 zb_6luKI&sAcRWlms$`Hnv{N)s?yO*{d2ARrN?^OGmIvt7>VJgP+}4?&aUP#tcJLMy z{+V{)dHB}JOzqA$<_lH!rzBag5H0{izsJVMK_g^pES3sVjbeySW7yM`A8}5>;@6Ot zU24fwdhu=U1-W}pxCmPQ&bpY?xZjpBRI69#{g;oPuDT<-M!Hq;d9{r{hvrLitxYs| z#+5_Lse971xae@wCd&*e>*`pZ_mLUOB^76iQH5!q0rJ=6ay#YVNz+J9m&q`b8{=$TL`$=DZ*fc`FX%h^)umofrpcJwfd_~l>R ze%#(a6@(b{4YGsru%2{LS=-s^L!M(9bH329K;#od{SSDNE`c);G*eL1U^n-`aM8W3 zMh0c^`fz4P#?fxxz^B~e;)>4n?NKi* zlg-o(-MsnVSr4^SYWhl^Jf-q+CZ2Y_;KTik5!csZO>hYPUc}ISufM7(m~xDwH-d$y z!DuG@(I(;E(%Nc@ZECwdE?y(|JD+#@<&T2bILMoGEC-q@FGubze;$z9SDtsvXW8yW zIBVV$V^U%MniT1d%s%F$2^0rza&+Xap2IgsD62k8+8TLT0`#lRBYM2BAZCXnzNSP)Quomr4+VdlC!3$29xzii zYnnFS!G82=XHeuhmB`x0zF(%7d)@tml{KhpZH%t z49n!mj+YK#RPhvG7hq%(s-Co#efcuFK)r)6QKB$}U*c=+ROMP{$(G!-*a3CBK@Exv zT1lJaYis@HDUBXp<|qxJG-CAMITl>IX9bBCvAP5W&=4qB#z!Jh_Si=zpKI0whCd}L zoRBel0}=myM~^-Mx(i*j!B#b$7y#uEbMS(bq~O;)ahdKJTR`Icpm3M5@#0FZ&^0xk zZ%t1wqw>@9a8W3$M7(vpZB;8NH(oh*r`6kI^I8G9Yr)BdE3aP(EbA}2nX`EyioZyb zC((m;9!mb*f6wS$6wkep6>&iG1ue@WXcXK-caUfY0CHu(@MM69)CTpd=`KajpPV?I`_x+sZ?{wDZ9mnl&jk_Ffi|yb| z+;cF7jP{qlMzP=Cx=>ZxuXUWs_Nhhz3}%BAszWx8*v$J?d9)&6RH-eIwh+eOMGA;p}zMG zMT#3p7U>)g1q42MeE_k-otI^rk^>FqJl=;C76SeKKLMycf*=)J?wlQO3?%cR>aV~o zC8VX?kvbIgkTT!a#9xyxH zwpah1!_|m$wSsL*1!hyq^%O0W8KR~}_uv_ft1AlLYo)a}N2YA8H0fa}AvF#bZ@f>p0v94TS)bxlxSj1!s3CajB z&}JY3%`@bnk?8K~=;#1azDtI)HBE;mTSG=w^)P}TT_DHAD@F0><$Q(%kJWLT+kyg= z9TAm{mXm{ToOzbQ6e^0u+XoUtxzcTwbk zptq#3w>f2#Ortuh-@*5@ZEuk^m$$vsin?Jky}snvg;*}cInI+x{L_4byTqtJCB)K= zhfUpdb1*wlshsjXPhyW@TMA#SF`dC&#neni+`Bn@(?;^%9mj#yW>a)16hsIP-B^pI z^kDO0{asNc06DHGly?a>H6p@h6h=X5(M~9qB6=Ya@CXP9NES}`xh~88@)Ug&W$?L$ zK3>{CnEyb1UkRIJcX3eF?{W6sM<#qPCVU&a%Z~@4eo=DpUF8mSs)cd-m5QaP)}e)x zTpx>B5h_)?*lCA6R>bABpY+o$f2(H&eGkwg5h0DU>y7R0SnS}9&%1NtwW$fKpv{Zu zdFc;RyFLzcqlL1iRX-vR2+HH(Nzg0ykmt&-w`7GW_$d^ zQ--ijFiptp?*C_+R4yarO+a}DZFXE|3eXz~cy9oLv5^&YOgRGe?`o}xpZqq4kMZE zadX{$HO0I^c9wBQ2KGX(t~}?}HOg6CJcmBoFW4^JIiB17zD@%-RcIVpjw`SE?CvT!}smOjw z1|USnF7~B%TH8Rj+#7b?hGUoB2t|#)oy@*}=8XmUHF2&9*Y=0j;=inBMVy}q{TdL3 zrG)YnIU$VA4-qtxa_GXXOLElFcfBNGL5~vO3)_%`ycxTV>BAX`U zB!|q$Xs#2QPp^FXT1I8U&w;oKa!CwSN!Q*Y&7oSHp+~3~-`uf5$(oFNR$ue+m2ZK1 zgHk`{F_II>`_(7YD$gqJEbp{h?a->DDe%%xGngn(+S2y1r(}U|iHAIN@{Hk1U#}U( zfsdpb^PQ^#C2 zHO>C&k%F8nWRSqK0$K^pNIsARxLT@PMrWXj=U5bq#uNV8dYmMlIL}*Ex~Vme&J5Y`DKo2NmgA@ zrS^!^YFH6Jsc&N8lJM$lI;Z1zJ?O?HuAaR1;beg%iRA_P{kfiPLZbUw;!{>{Ydz2L zG%4F+njIf+n?UM0ul%yg=fJ)zBDEK{-`K;y@e2pEHi8aGZ>+rK$6@_*C7(4->SxXQ zsl57Nj=@m#UwQSlt)V6vS90sdlGNNX6)JX0-pW`W%wSkFuFK*K;nj@wVz6x@Q_3I7 zJg;utNH0xUvUs6@L(XtH>zAagX%r7lck(y+H>jzk_InJ^dggDx zilKzw=j5K1phB$%chQR4e^e9+VrF-!PMzl9P(nEbI*&~VE?QHy_@VrmfXJt_#H|D% zbOJomm)qQ5#_jM=^)rX5g`BT$+e_5|iGV~?9&T>c&het)9hEGcv$L18e>pfRn|S5i z=q%~Cxy#1;i9WCXRaa9=Vjk=Li?{u*WwQoG-!Je?+?nrps7pxteFV2)?sC&ws_t{i zozyB_HenfpbM-m)smX%}yH04b#$9`4N3J6HGv30;%`Bpm?y@SG?de^`0aa2VJH02( z68aE`&yCDRH;eW_K@#14p=*@}7Q9rBn2%WJSfiggOh|Lj29MPH8j~e{=pW!ymH!jg z9uRr$QQ~CWpTxPvf|xBq?Do%SD~BzNntHe*DQB)f zSG*<0-o{&0E5UL$ERJgLBUEr*+H?b!>Sv-ZM34xJDv)n5LQ_)^*tL!9&KDgW^Amo$ zZzLgccf&~FR}e)=1iuqhcNQV*s+nlr7BNBN9u;|f|$E}uS6ZASKEC3l~i z_L#uvOtPvMbCu>|?SnPGq{Es_g9TCf!=yzm5#rY4)#nqu=xv+YRJM)!&p3}=u%#sF zkDt6=-01k&$2j7vAm1IA0@gq&Y%YvDOKx|ZC|*jHl)@Ifk=tckMy59OwwhH5lN_c$ zZ!<{}EQxIl;x5Q;8|1ChGAL>{s2gZYg|%RwhgL@6DglnzAs%K?YMiuban1-VnwHoPcp?y(CmQro{)wM za;|nyWRIP0j*Z!}_*0mN+PWado~FK`$b5H`UM|DP=i-}1+`780d+Lp?4COM;s3s`Y zGmbCzUCtg5pCvP??BbvP#{bdgyG4TCEvk0$Ot*L?RSBzt?zf%BtKRePwM5@)uo!pI zy)|~+=JZTuJYOvFxs&kupI=0C+SK zFrI7NDruH`4Wc|j90oq6!EY)&s=9DxE z9kYFl&E6(I;w=)gKA*@PEfMXKG8;KO3~`QDaXc40M(YF+E5OPU$` zZl<1bX;i2y>e{iT83)U?ciF92TwDtci`&*qP4CqjIpZ}u;+6O1?Y6MuFRJPp zN;jU{2`dqQXvmM6xNn$-Axe#7^l4M7s$sMY;Yq zG}p3s^ZVJI9nopxwp>FR=798R(h$PO_&YC#uRbWaL$gW=WAl z!cO17>SMQ5!o04H<{+LkGP=;}COpV!Km20QwS}QxrNpbnRF3|%K{NZxK;29tqcsz4 zP3(@j=>`uc$06^fk|ZvZm}#9%*)qO)=zgOsMVyIsYmkV8qD4!8_PF$plpQ!It z9UXn|WW2OV{1TLBjZk~_eEitZe|*#AG0%Jv_p18(cJwIQHj0uTrQQPjME?ik#iQuj zQ?RmnyNcl3NsrkB{IQtWSjQ7u{YLHCug5N^>Lm+Yo#{x>)$7b%iN5$@JpcNY(=jW( zLS)C-0&ViI2Nf*5x_2q%?Nt^7nPz#l6|wBrnkPxh3U-V78hUiH1Ezwhvpqp~;uJ_o z!r+6p%cG5}6&fC`$w#S$a1)%G@I)xJ$D#OVg67-U!VOJ%RA1DRKgVCUrEE~_;-P*~ zc5;TQO>z^fNnGGB&9rp=3kX4yYO8lDtA1C`Jp4YtFG9OBf8?3?7OmlFf)i%`wLn+Hx$eMm~T$+v9T*n(0$;kTl6aSx^S0sj6&K+fCI z?`P9xnmZg=vK*ty8rYZ-r1`sYXVF4~e$VvMS5MD9&#MR8-Y=hAS&3ljd||5y=RX=N zJl#Z7A{~G#IZpd?xZ4TfN4RMpkBp_S6x2fmm@I7hc)uOKigWAyBQbzL&GMS<}6uKa+U6rpJBu-13U z$sG~)G}V(Mqtu_-TBPh_@7TT}Az8c5#Yf^Et9ECu53$gC0)}^xkV`&hC&Yt* zt4MTe@*Vg#h1g=m$6(3HmOq7GG+yP=46tAcOg}az8 zvx3^8Be=6kC2$TJPNx+{f(&_ z1yu~rN|U-bJaTA6ZiDHM2GfJP(q!Y;eZ34C?ZO>O+yyGl>%wVS3PI=a2HO4z*NIjMZ{H8bLcJ)22{o}PzUdJD4J zI3!{~MLEm26W_i3xO$+8W_5BKBHSVf6W4@weLm)F|0;ivVetOS{f+N~if(6J{2*KZ zngX7$D|D=jF^y~}QsY75q(hz$ng12P?q4JIU;fzM^^{Y&;+H>To-lEE!o2D+$?}m? z_(H|=l03Y;9Yc)7YM(;oP5H`|$5=Hz8-Mq7*G6sXug#MS3MPtHi5HWEzr_4ejH69x zf86H%hmTiKan4!NE7%}e-esYF*ZcP;x=VML8WEf5G6tLxa>ioA*xupV+Rnd4iRc_x z=j8Joo~G)Q^j(>{S=-|r6S^>W1QEh12?`gf!cKd0dWrmnzONLHw%cS1*ESaU+2zrH zb-PQ;#M=^EDL37!)21D(#;qB#w90qY&j!AkYbE=FK~WtYk4;+-8>v6-10K>QE)Kt2 zq&dc)KFQ`U@(6PNNZh-KN$&Ym3#&4%)9#baedk28cdB@CNBGeu&2+1LEqofrdHuF# zYe|Z#m+UbXC7aRa(QS`9Ti6rsWY56mA>P%xyIfl!r7FWijIvuDFznD33!sMgp|eNTT}eqsz?F_0XtOYSVP3wJ@{pz=dqX zgNb2#1KQ@htK9onKT)bg;`zkOLYs6Lsl`epyLu1d63N0lt#NhfAFo{d$Vjq zr~Dz_wH;>Z1Ez4y6T*2a_C*f);l(af3MumzYAN+=vtEMv1C2t26U7O2oQkNex@swZ zzaqQc-(ay4KIB);i`KTT`Bou>=qoMOhRHt#gTJQNnB@k`#+^Crb(>pk%ckO|k*pih z4^@$cn!(Ia8lolz}bY{l`=kvkGp zrk`19mRqeD*qgcZ#4BwgIwgsei1g_s7<~O(7d7LWQ+z{Li$~(-k!*RtOMMX<{7!P4 z-C;?^lr1-om@NI&bzFRzY3@8*B}hpt(R-%bx~*UK(X}Z9RkqZU#W&tuW(8LKOF3ap zG-*2H-tVeDe{O*8Qp)~QVnb-=nyKR6z`2)5G10;zDJ8|hC-&kMk9jDuM!0*5bx-Je z9h@~s(8y+R+LM6Eh*f|@mnqE?X8wltdDYr#9G{^5Qmn2)Z3;!Rkf!XTq{Q%DB-;h- z>}7Iw1bnM(1=QPh-u0N_WR{rdx=iKwnBm_tbprHmUqgdB2c=BSK0C<=T3bR_rSq+x z7qSPfFk^d{R|C8$GRK``CGR#0W#%gEd^*>dysMZ} zu5W_d+UhaAJsxxE5oZQp>&!^&(bZ+Hnr5q(+-=L-{9t}=+=in6>P~-?hsq_~p$pxy zUwJ|txto8{$Z-vuEP7Q@sOU1Cwxn=nJ+iVW^S;Y8i=2$C)rrol>|{X?t6bX*d4P2V zZ%EygKr_!fO;-5CmQleSJ9qkh>^ocbq~9u!Rsxxn!T5a$0FaTZm6+UymUAngSWzy8;uf-eH>&=mWN}+vs6w*nCU! z69r_etBlvaM3*E92vt{C?~!eC7_%#Y=Ct$A#fBp_uB@uF3Q|(FO9yY^9s{fOM*3UE z?UJISKMfu6E=MJ}_BDq%*yvH{Mf#yoC-RQf@nb}f=!ne)fAP^{U(_PQ_a$}OJ!!eS zoGCu~l8ir*%u<*Ca!Wo-U^7cnt$kcJ?}}iq<)4tz0)KU0Dj&z}jI1nWv)qEOqjx?& zlVIPe#k}%v)*+-FQM2osIIUBO z$1v{5`$pPc_ApUEs7R|lU0h7e@lbi6#pf5cE24Vr?K<>Zb2U>wxIq&M5KYy&0bU2uIozWMPJ?)kE{(UQdkiLR~LIj$F zlxo`@Vnv2ZR6@PbTPY@P#JSogK66B7tnD*EK`!Mc7c(oW7Y#W93fUp|6_iM&elvqtW@( zo~qIX7SugD$?rbtQ`m^|55&?WSzZ&L3pD&Zzt{8ePDRnzQUfu?TQqZ0YH`Z^&38nFu;93#pt0nxr;3m16SigS;x4xRimc397xH!(Tu zRA-=*q?dMmezqyE!OB9eApGfM7G(Al^I=^9!=6CNI`m3h>r&{QRG)=#sIz-l$e&GO1XDI7d z`*9i;3BFf4$u3_WbcWnfteAD0HHmq4c4{@FqXzLN{WL_t4I!BTDaMzb0C8-nV6x}m z`S~3n0ty0~aQ7tRlBd#`L>OTKs8=qo?;W>z2JY_N_0pQ)yjun{S!pO1-8r z)X?P{E9I-IlXKfO^kr>5|7gYl&6qze@`Cmdm*Ql{!pNGK_ShxW6LZoxY8kI_#(e&K zWzUhbA}B&X{5jMqtdT+eVR|Che)NaJ+Ld-0G?>2yw~ouG zi8d;#1qVqEML0#&D+PmY#@NOM#ht!Cv1>2agz}lGc6c zfr1l9pSGr6i@tYhLEYi_E))IsS6%$0-{0^trY23)E9Ks0u^CR|8|_>wU_Noa zs)2G}x!v|=JtaPqNe|xa+xDw>G+(>E7iL!^Kl=nV;vvwz+M&#CQ!hAognAoZjca&% zx_nyUA5To6i6GoEb)4#+klswCdVOuld$F9iHY!#2Wu>k}wZ_O<$FT7yn6b^cnw$w*bvcrBgpV(AyfHUCngK81^m=QkBK~43m=x&-Fc`8Nu|$2590k4iT|u9cc1V=nT0EP0*x9g}T!x zPhM}%Cvn3MPcPeE`IEB0Y?A7f?FQj& z1o1^vb^Lsm-#wt?5Cv^83hEuidi;O}elu9^7&GR0lzN~rh>Ks)i7d~H_UGSKo+$3Qfe1gXxXwXEi3CojBrU&&lDrx7`;qT^rXeFpSu9hpa>fHDY~iDp|%sAPj;zIc&@6X3d#A#ic?&vP@dVS09uYi(Q#sEp-fY$WBDTNrf<}o&Nw?Y0mKn7} zc=r~lKDi*KBfz4Rni^>>+g6pu3gOiVq?3usPZun(4D!MgC2PfI#S8i{{dGf(woP$z zG+>m=K;8L8&##5{j+`C)^ZQ79W-6P{YfEo4AA#Lc(jGFk+3lP4#8Y7RQ+e>2N z3P>V?rmwHWQK{#eg%mIUk?OH&mhpa;_&a;zSi)@28h=sg?@cj$`6bHYb1#)K0M0C7 zD9$r+(1R;8Nl!CAsAB7BY_p)U&DF<#ltfvMj@-M!(kj4xRj0n!)aH=G<4D$Tx(FI< zQ86PbA`A@Te=8!1`FG1OKs4#|=Vo%IxECd{NIAk?iQELUEd?PYD<+?|pgHxcfc2pE6wl+5i#U!N~>;l%2Kl7*LO(0rF!=WEN0!XPe8)^g1;B;L85 zu9>>%tz27Mt72i14n9%$mC7Jhubc?uMs8)TC2>mXG6v!if0sze|CT_M_ick`zWaMr z0d1;Ta^qoj2nRkzPE>5r&X$kXq}C{L(q>I%-01aWC=7~VdzpyH_1V_CXtgFPy%Ta` zZy#cu_l-WmV%r=g*pjT5j@Xe_pel9g=S+KrTb=69+=`&(8QsoDM&Bp$nT@VgJCfVT zh497PySEY3JJX*xy(bT0b*2p27#beG>&O`Aj=!n*`eJ$(WILHz0RR5iM2OpQhum3VydT z91N5VEaGK8rkSMeswgvBvaiVgb6>Eknzt{&G%Ekw**9V`zv5kWHBRS5EqLawq9jG6 z8OB|0ZaW)AjsvYBuD#(pgo=XB-nd6hTJ-tOjzA3!J*R1DFM~{q;Z}b-bM9YC8R0r9 zRwVZ6^RPuiWKD|Shn)ezHbcU;KbU3XlvI;iO&;j!{IKA?mFpCDYaf-qlA(u8#D2Ri z56;#Grx6heGxL#@z4N;~^NDY~ZUtFtjZA%SqFFC0iu1~yJ9(IHeGGt@D5}IN($dmV zg+8@r0ky1f11*s4-*r>0K9Jn_>!JZcz|_Yx&- zJsa9mw!9v*JWW<$j+6UDwTRz4n*pBpaAH#GIlhN#J+4dRBbXFJNbj2V~P<9Bw?3Q`qeXj(Wu)W4c?4vOzzRs)6>YaicwTj!hSP^pPjMBkk1~B zAgrpXX~Ekgifl)ycep=d9Qkkvhyq%yVqlln0ZI~~8%T$V2Zpw{kkd!(Kc_N#vd5yn+B>M@}MKs zTPB%MA$Z$09>iyxs9zUsgph2{Wd-tBOZWmQKOBF$+%8iK3gQ~PM3oo}1sVP@GgD+& z(_o((LmFBm=k{x01q)^*!F?D~(v0R*Z=VgNyYLj8=t)3}-$@poVokIrw1Ok%?L)kc zBRBCh6lbryLksSyPg68b3MX1c&WFhMV5q}lwa#iJr0_Y6$v4F12g-eV+63Z-e3zyx%BbnO-B5W4sG@_6=1bm|v<|Bo$ zo=C~X#5Yld031X&tA9X1jHl<$sD2W|%XU$lNWSW$(g{O6^zxdSf{{@I`X`bE4NwOr z0Ok$DiMz5I58_)4HKB!%)LhBfII&S-cQZ=tF4K+TivS;HP@pzM>@sQRNnKnc6J z{`G!$w|{nw-tXnhShP(XrT%DuRs0^j)Q|V5VrG_#7zX1aX+_1wt8ZqYdZq}&##MJg zLtZ`{%T_&LrE(2klqdW47e!T*nJ;^D&3jW(vS@#&jqHN#zyZba-`EaJ{QL#wVNDff zWeMn2c-xiamd(b<*nk$)A2xiW0hXxAFrPmCqIp;u88+TG-Ei>I$L6P@W4;_5tKAD< zGdboSb&ad4tG@>Cd^Ahg`1|q{|+Xz2uJJYHL6C7>4nT^Cc z@~@$$C+1)4>FX2yVq)UIDKT3Q(x@RQ?>n~jLq~xCxmppr8IM9iPMR>JSchyqwxVf2 zw2rCtb6TVo%?6>>TS0|Uuo+$VtzecK`%ZLZTtZCW02nZlWodxTErGCZpnP_|h4&mH z_M&}x@9JzWA11odCcqK#IZTE}TThV6aX7veb`AY4JyOk#+lEY@yAj?%ti%O=76Z2l|Caex!H=>M@^~}u9b&Pkmh!!;52R7wyoXB zt>shK#FxC;iY?@t^8G_!0i|d`@Se+z)ICofMpJz$nkCz!pvQ`rifc9c{TXIAzdp7X zLy$|Far}07+X$Cj^2fest;Q+88R+#Dw6#-#k2Tkw!oE;ZRgDD)6;3WlgjaS`gf~jW z*>>8s;eX;xxoop)BdtC?e%)Jv8v(0>u;y#BLX#>i(_w64W1r@3kI~34^`e!_z2v}2@a<$2 z6eF$UL329^BSx|zy0B0aS`Y2dPF#9Ql%yz*{_|9KK@Uj+*KY*VD;rVG6FK$YnM#My zl%q_=R%CnR|9vIFB-_P%+&2H4PF9W*3CEUZ1b|9c55h02AyV+gOoH}(0Al67@6ar7&(!u|J z@Kgao#AcK|iSPbaL0n*;!+)2-3Q2+kf}-c&U;1}+T$yn-jEj}La%CL=GQ3C_(AC>u zXE|bMRX|u+8hYdRQ1dAhh6U#rL*hx?D;r7g(#FKaB|)ury@D(Qy!=}P#<8fv9t3oa zzoaEQbjT2t8GL6u7_@t2k&o9rb=}VFg2pUK85u4L7-E44`Y2I^;$%mF{RqC@SnK7RH(eeQc<}HDnvKKLPU9B5Eto1u zxL0?2F$A2?%5MHPJc;c`=tRURtaB9L z;!=UCm9gK{#{sL#n;EYbHFrn%w~U!`E?H7+H}$`@$PEyuL}+6lFP9VLX&m7?)h z)Eqjc%nMSKr-hS8U$pCVw(Seh*-6oF@EBcijRh0MN%mjwh(no8QCj*bZZZ~3T%O_L z?5k&DUD;~8b0qIJl;gaOE*%{+XBHN(?G}^pwyT}oa<{>4mxpzRU)uB~iu{xtO_a`H*M$Gbf~StX*_5{^yNZ?w|!t|C%)y1><1u&T%`ZA~maZ zOYrBGmYagQVD6XOTNx540kprVRZW}ww2s$S8FNjXp~6{5tl~Y}$8)`u1n^k6LXH+W z;$?hYtPuI8KHfbZ+^V5o`U6&ZKdkbkd&&{`OPsUCL(A26-q`E0dhK|ym%*%p_pe8s zFF`Sbt+Lh<@P+mg@LmNXx(ZahBZtr6u{f4&8wacsl9T^BUn9=D7Oacc#>)@t3zO4L zx#=VW$oIEyEL1yA9GkODW`0{qM6Ulk=C1T$;St2JMt1D33;!LagkU-Bn9cP6j+!_| zC#GT+95WMBC2`J6kN*sLaKvyO971bv%JQ-^Y`Zat#89gbN?_>1VJN=qdgUAOZo#fV zG~&*0&u#vO7jp1AA1V1qcRnT_NaBy7!E z$rCtCJJK&e8ixD$er)AK)0Z}01D4@9J#>SxCfASu|DK)$9mrVM&BQnS^BBry$5?RI z3>onaCh+il5C!0o*hF%?6cPWwgTrnGv&2b{PY|>@m_`*8ObL_XewVmLKGpvrFQ&#XHLF6A6K?l^Ov<5cU3n!NJq?^kT?Y+W~Fr z+i>EJt?&qHcm#yQy6cYs#-zw5#O(x>yKZwfJa)ZuouEew&UOF$NURRAh9ttz!ot|% zniC)~vQGLq0_#Nl_xZbpg#u+26_kWUnJ4+TdfQOL_J8%tkocBwoBk~^4d$XNUA%a2 zecegFi5ZD(g7A9&NNO|GgDCiH;vr%Z3nvs6g>`rlMRULbSF!1_ZC|%m#I^t57IF~z z0-}cF)`Yc}Ak5+I|BE@?A%Mb@=aVN22&dM06V4=}TBVl|>twT5*VdYTpoQ6NXdvAo z0Oglk{&?VcIF|pz814}0rrkqA5)9<>TLi+hw)|vb$mJ_I!Oav{Q9}AeQd*jmu+5YE zk#UhC=5|3q*K1+{RoRiQ+_Vi(2*Vrx_jzBr;^CDOz8>5x&wzNPl|VfqR&WhTEN-+B z1P@#AN{&+FJ6}<)JS%;Rc7TAFFVC&uS&5r7bca$nYFQNo+2S}!Bk2yRfT`im+uZA^ z63zSR^)&q3M`A`!3;z7=%)cz~k%|xDVdIhE5bNsPfe?vUIQPtd4wgnmQ85y?w4SLN z{Bw{(%z(yWAB=nQv)6Yl>3=`)B#x7a%)hnw?F6|2VL7rNTzmIl?>?!3%03AR*MGla zjCV!cjR@Djd)h|LYas&I=l}eVuyl|&mfg8y2Rp%Mm}LL&)9hiy3hnuK@e5cpWoPHY zzuWDfZx(v|9IiZ^``@or_y-1_78VX8_Hz9Gf4|6%O|g@dR1Fq?J#umWmt#*7Klq=u zRQT@_W8HZ#2xljf^53(fLHd6`>!JV8Ro5Rk`@dWHfyE@u0r3I()BjG+B`iz=MhBt6 z2`MO!6yyK=0CNQ(S4(0cON0|*kg!Z^+)pb(&1Z9#4~tLYd_`M36g7*#ulR_l*5+sk z-wgr3=H`1yJB&uQb{vfLpOVl#M-T!==HF9$HdaTZbfhW9i@07)OpKT~F6ZuRk*A5w zLU$2&VIlkPX~lmG3@|Y=UV(5Yl6<`U8^}E+;btIK-v%W&8^DUl8Sy{cx7LHQ|6>?< z3z~I=G*TC8eCe^B4fLzb2kRE9W>6Iv`_2Fqe1CfX+~!{NcKf`b+=91$$YXrHl#jnPlW)z=K`z0u z$op6iW9`k1d1ZxUc4gPIuU4Yj-=H9*h$&6?AXuM_rdwp=pomB5as^e@(UOGsb(`fT zt4^&xWD=p5YJVU#HY)bR?xUtaN z=@}p9Pr4aH8zn{UYZd9%OlP8$I~4KgSQ8qf$POMnH9W23n5=4&P3oXyZ0*vU8vP!=nw;P4_s-lfe z7GWevNPKVU>#uuu7^zZH`IZL>kk>v+jn9)zUa_;|R${{j6XzIbto_+!iKo*~#= zepFM4+5`?8BdR!ndmx1r0@cgwguf)=1b6+wz|Wt5jVrF|{vOx_fsCL+L3a3XVto7+ z^eU-1IJ`q|Y%JtBypOP7M3&FP$BbX6xL{|OgJQ%Xa&iL#YWl_i1F;p=!jNC6*<>CA z-9a8Z#LC)wrr%e1q>>?wAWETwFcs`LMa&lCU}V8%3fBV@5f|89XAFdUh5#f2f=N&t zwsf3)zYWGe7Sen}b^H_XCs8)Y%aywELeAbrs876zaP4;r6yF$toFs2M<{&}@2oS?h zh1}vWD)c;jhlyKdKbJ>n4N1ejrhx;ISdgX{ewNRq!b?_aE$us4Y=7z|uiM;fw4}uJLzI6jC z0C4r}6NDqfD1Nm8J>vpGxo&#GiSg#Ih-k8f67Kvm05pR1VuU&>QMOSSoFQc2TnKRc z72*>TVT^n?gF*9L_X0dv=1O!wYx@c<#(%}MUreM(mK{yjD#&@Rggg5-2u3s!g>4~r zEMcLK;x~2>Xc|my8=fg*4RN&lpyr~JI3r14XbKM2fyrrZDT>B2}Wm{l7 zD+~2*Z5V(DiMAw)u(?6uHFXSGY>bw8!Fl6@N9M~F@Oaz7VTgz1l_uT+OsfJeWmYiG zjlg>ZSaQ-nYdrz={epUm9S)2=Ow|GMNg@EeYia zzi6UztEOceK%6rKn=-G?UcjBT0577GfFFZ;&d$AiRS}7BVHyxy)-f0;d)R;xG$a!1 zupZ>l+>Be11xt$U|l{=74YtjP@cZcZ5hI^s1Qy0s<6_+=qb z1MEUaORL-^i(?Omr^*$n=0|{^)O@sFWsz~Aj*=xS9zwaR>YR;!6zzMMI?Cq3ta==q5PRkWvp0mNjTy&JzX;njbwe zvIz+Z*c*YXi|r+3;^{ja zOv~RwLNq~@20T2B{n7M7PUueCTmE0~E)g{e;wLyJYHL%%YfAmx$DAiGtRFQ9?-0#u z>&MdEb?Qyx01kILam1h}A_1t0Lck%NI4s!7qvmc)Z-~J^D?m^c;4~B!6(LH`DA5GX zkr}8rBw@#a9^(jLS1fszRl!T=I58t+WNp3on7%G`4{| zwB{YLEhV66vfjbtiZpZIb`+Hl~|Lb;MBO{~y$qy)F!6GkHq3?%83o{oq z@@*1OVbJ}1EkN|;M2Zvs9%p6)qmYm`N;~>y@LKNl{-L2D0mi`M{=FM!X8rNjzscfh8(yxV z^S?HS^@~XpzyA$%_aw-=jiQ@={o`k2&{r}9RqO`%|MiUJ@8{5!!wD=%Y!ul6rys;T zMo?Q+#r9vfVG?lk;RMA7ba{3nJ9TFwN>MmEf1l(g4km5R;4|AY6GBqW!JwLZI_I19uH2TAO<&2EXNu+?b>2;x*W>^|%ziZ6gH zHQ9Pr#HImlT5E#QorW}Hb29ee5$X$m@V-J(r>^*+97uG$t^0vj4^fvwW>8D4Xz3_c z^cgL@U_8p6xX6R`uu__+0}`1iJmGrODvff0J2UZ(C@vCCkVN_}Wi-H(a-0B>0K#ws zlqEPd>%YI;iLTHY6p{?loA!4lkPV9jAh5&7yqK`RNhqMoDGIPv1$?l-?;4B>_e?@@ z?eEXS-@UtvQ34O>rC(hq_ThC!L`cDOz%(I{+F9h3`}eDwP+|fxHu}FE_yR%Aw=+PE zDIk!hbQa&_Kz!5sdG$tQ;%@xv9`<@E2cU`~rD?k~jpY22=Q z*rshs(2_62iZ8_5KXF*OSZ?p5s7@iYjJAeO*zh51dPCc_;P9t71f*)G6-_Gkn;zN@ zeOm|!8hRGmJvj_DR>7fC$Ftb?{V(?3!Yj+Q`x<=+X{1A15KtNvL6Aly1r!kJQjnA` z38h6!MWjoV4(aX^5RmSYP9-JJx_#f@_kH7xbH@1x&NJM5jJS}iGs4j5m%uoE|UguQf4^TCOml)KK$!TSTUyV+~brbXIj#bayx zNy2oO;c$79cP?R6{RiW8`6MMRP5O4c3T5bG?y_#)uw}%RR>;*}@@P`}H#ara#KBWo zK>(~%>wye^ZPn)SpCx;pr4KI2-&TyGl(3b!M6|gyg~NEas`Yq)U!8o1tu{~d6FJ$u zhThF;5xJ@%=E0YnPX9Vlmc?R1i9Tk(Fb{b{6rE*Z7ovDMMo7IWFy}PE=DSs3>oaw? z3F3P$IFBvXneMrUX{hbCt$PCN7v7IbqvqUje;&I=;nJrAZi(FVE9|uU`>*3ChE~7d zep4+hXA@YaX3;e(=T-aV;fP~@@?TxXAu_1F;t>+=cZsjT$yWmAtUXC$CkgGwz%0#* z7?2dE>eeV~n2~!y%~t`TgL+bd?Z|YbUsE@>#?y(@ zys-UF-6yht%5M+<>ONdw-ptzMH|&o*OyQ3hKvn>3m~s53fg|PmjD%tj^li(_=-w@v zE6mWpP|D_-r!nk4N_Yap33~0o$%Uxe|)gSu(P3s$$`^Dt*we_?K5R0L>zu)6GlT(5ldb-F?|NYD&*+F8Q zT|x0&Kh0x`f>$@#$A=*{%kB}a&u02?OnqI%|1>~AnQm8ngmL2W<5vzmakXLZE|Nl> zjf=E_(V#LReuFRkQGO^O=LZVYcIiHclW4c%K*AKkDB!q*=%M{K!-xF8O?g*1r!H=+ zG*OY0E2!GMxUv5zM*N3;m+?Z{Pi>AT^@&VC(wNwd$LD>b(@> zzdOY%y^Kn{WX$fC%XHbzOn$yJMdMggC}a3m=j$8k-HQ?;3oba0AYLEf_5O{IoC>}u zqv@g^6b%=wdW2m7Ku=isL*AzkRs&RQ&t@K6@El;ijw!Dwb^EHz8(xwf;OY}%lDQE3 zwPdetTjTt_`*MIlTyb}n9mA2--_Xk|X(R6_CW8kAZhd6DC{vm+=z->g=q52%9rycH zat0^DKlx}(}8%*d`K>=3_PWFF$7P{|+ z7Y2Ht8FCVdZr415>dRv!H@9DFnR~bOWJ_y_AFt}1d6|86jaTn@2QO#*K2746x$rN^ zgY|5=LLzbvqxEZ#@H<9jlf%U|JLqM=WZ!&nc0K9Yu^SdpLDuO6q- zIG#TK_sOg8;V0*eCLDHqYY`Ia1kXu6A6YGs3o*9=$>k8jvsDP35P$;N9VLBt3M77G zuth_u4|Dz}NW_4ppCZsj8hbN9$**WoRQF z^$AL@_B(hvtHdcr9RFBe{(E}#tgiP#XVKRAwwKlTZ@IFCfwS*%mW!bva+#p z@bjyKs**~Q9UPB3g+{MzE9L^(d^V4d7d*;u3%KUba5z>8x)+tKSmsDupC5{UWZ)V3 zMq3YK*uXKVpVm6^`FYcyxiAL|K|9e8E|V)b%Nj2;mFH}fZ?P+KKUk!)>YewKYsV4( zi;oXF5GBm>(qZBA6cc!wMG zh>I8J^B+9t1|cc}x3P&+nab)|0ya8#adbJcWIvPds$6n=b$Qyy7aC;NiIpMm5r)n}2>XbJ5h*g%8@Kpo(ifX0sNj%qC-G#01O~qq0=n zkZFIxD1rf+(3yOcstcB0UQ7PxrJt&1EbG#Bqfd!_Lk-(f2Ocq~34hvtt&ElU_iJ!K z7VC>u78i>Bg1&C5dsf|eq}?ZeVR6TR63i|v_<_078+|-*BN62PC`j=pGh}yfuLV+t z5YWgn2D}3Y2d4pS7Iy#xn1w(}*2&4Kc5)kzef!x4G(ZRkyN-y~3uq;?-9O3^t?a(& z!dq>olnc>EKT~;ZBso})YZ9X_mi|<%p7v&X+bPCDn_~8dH~LL7kPfv&3Q;Lc1J@3D zoyrpx;Na}s3J&YPVcr-+LKWGy`01wkWlA2sV5EGs>IGD}OHh>(oVYY_Tb7^cx%K6* zpHc+1+ZJ>pwcpMYgpEG!)1NQP`2Eg7r%JnAe! z`nZn}F-GgWL}B!Q1K@>7PlJAQPVosfI3mMI^vHZ+C_0yv%%t11_Ns?uQRT;?*KTXc z;wQrW6;`#%f)Tfv}8lH^`ANhy=jDI9AQc6+$Oy5OX zjmd2JIh^j!Yw+?i1pv$>MjD!ERH$Y2)8xQb<}yUK4bbBe4*ju+2%NCn&a6xX*rNDf z06orG&3HPpMD0p1*t2Gu+evXGGgH z+6;}%NX4Vef<%30V`40YFOT|3%%7B=fyNa6V0}^n7ROeeSHU!~@a|ad-lc;a{y)RR z99Si*5)d6t0gyx}5WVmqy=(@(BH%abuiFC^j+lWV92Tla=C=bUtREk){r)sjyq#>E z-gmVF<2_MWL&>N6zkg-*?@)VurD%$b@JQc%Z!>%N!1Zpj0Gju<6E5l0A+H^qjvKvqo$lk4BzBd%k}oJ|2330WUywo^Jepd~pG|I|*O znPcd1M!)DVi|nXwmm%ThHZ!^qp>NXzgR+_B#RriS9Xs(pd`*w_UonyJ9k5H7>3?q~ z5<4_L8D)8~xe$CFTKnC0;n09cxTL&w*q8WprIs;ZwH0Ub7L;C`bp@oC5~OVP(1y!r z!u)!ZZFd~mn6p(OSJ19tZXMQUB*LS}k(C)MOpP$@ySio0*mvOJ_ zDvz4Tb>gx;<~MehkzRs~srbF=?f9Elqm-B)Db4NciPCv5(ji}VO2@oUb*~{mOmGrg zIMg{#R+f-iOpG2huUi2*2?wa^KewlT1-ZRXa2x=u3?sci82Hn30IUgCCC10o0>?50 z5mrg!yp}fpU6qI9(Ah?xhCC|Z&g-&FY`KAaJ+d^Vkta@c+$2AzfOVziB z`Dm0o!Z@!_*bUM|Io}})*SB8ZmJJS;g=E(sWTu-&MyOyXApbX(X%5InVSjLNfEYCL zx&JXk{9u5H?Gy&}G`yo28WJ?r7vb|9=C5DE)tl->e(dot$D!co?*vPfqUp{lCsd-p^s?M1C1BPL-X*6Qs7;Wp$`jGz)GM9pNUik0KUF2Dzu{$6QTNe);2vk>={frZi~^6 z`+#%#@?gw7I6S-!pw8^O-oSJI1cJQCP7l=j+eR55d!$&I_O6J!74!x)UUv~3{N(TB zeaY|fp1&gzHVlr*%pjk|u9GH%-C+FA$*%?K;}D@Qa^V4~Ex|du@9iz!HOBbcr{DU{ z{ABp_(n8owOW-qIXT!O9^Y{IwjH<=)3AA_*ez%b=9 ztfTw!cPc1EU8aa1p5`SScR?(jW}wM{yCI(OVlqauHL6%PaIyz72$ z20U*aJsAw)Xuo9{CR`_s-cEI2qe^0bRONI-_pIAHJ5e{PvX6=uh3dR2)aUhuoyQwn zl6m%L1n$#z!R-kGpC6+5DNgElEA!|cW|Vy2l+~z&Y{m5NpGE3B4%YxwW_P)RJosVj z0lusTE4VkmgpF=xrXI1o^#d?i%VQF18hikR0a29zoXCEfW*(fE))UKkcXOVU!*ix8 zh>Yf7?m2NY{wOJlPKhrj_i2g#(V^Tal=j&1#$+z%d0g_S!Yiw>K&(61%pQECe-nwE zuRTn))+&)Q^;LH@_o$a;oAey8EGJXH2c&}*aP54B7%=y-!psb9REr7TvVyzO2C%El zdh9i1V?@9tpa^#dc-Gk5JP64-{PB85&V9z2owDhrzmCtf6uaOgtGjna800@Z{aZpm zE;@~Cg%-*V$7w=Rrl@n}KI2^TzXqR7#TJFV^dodzGdd&sap)8V7mC6cBX*r4 z8(=!GvB~H&J1+g~BH>_XMKw6S#{PNnJ$tXBML7uP$SSr6O;38i*#L zAdiFvhek7O%dI>?NbKAO8>V{L(ciy_HaW@{WIK$&`h+4Jaa;x98T1*~0(p9^FP+Cl zT$T7`+(G0%N}JQ1Z#9nB?l!8pvntP7<6ePNB=||HT1YFXKM7BMD=t{5td{P?WP48~ zTEUn8ap|W%b@i<>GSM4_>u)IWZkYcji7kG+JBBQ=*9U|FnvSeI7YS;s_Qpyq{WdzH zn47eCu=y26 zkPp$uxnz00S7-ghtlX||D-2-z(N3qs9t{(_Y$e8sX?JYck-zI;zXi5 zS%ML)=)imHO$SAM#B@&vb-z}4h;G+mQ)x85wpbfY&JT+h0;Mn4-w2xK8P`}{&Ax;E zhBzs|y>s28(Q(3J;YSNo@>Uf(`=NT*z3#m|K%9Ge{!UE@UgLN*YM6wU2(EnV*x zPYb%?!LapP?|sqhF1Q-^VQW3*-!u!@JQQzh6%riXm2JJ(n?0VCtkH7sCynm~o?XJp zya3)n=(^e6Ea}(2nsvfE(yhy~GQ|$+jmwKXd=>=qcRvNLv)hH}Kg@$-v?-L9N5NL? z9O`2xu#jF}$io7n3`yk?!~=}7u(ULww6t_I;}$O1L4YS6CWw;wLV(H)VJC36eZ#^C zq31Ug_}a5G7w1BlQnMLvQNKuM>3ukJ2{`4FmL4ct@u6=Mf*qH6Q#dDu`A(hlXX+S% z?e57QE`g6zTShgPO((kSMN}e+HYtSTa%El(Yiu3Jk?oua(z7`|WMWRI_7gLc?+~2# zcts*>HjVkJc(wCxDfP;0+U0LA_I(sgVjf!6gYYE)=Mm5ngCs2g5~Po^A|a3%UtrbX zbz~HQRv#qW6wXX|FI8<8EUP6YB~i$)fIA=-Y$EMo&Cyq2DDe`i=tsZatG)o|J1)Hn zV!VL)bHCdUi#<4_;_O<^h4m{pAL72KZNTKELVLBjILfH&ebK|rlE3_zp=#R2OM*C(}TI-kJw~zR8smVlc6c)eR`4k+?pciD%V6nQ5hw2bc4p8zb z|Gu;18X)7Td!7DQM_pV{hjS4GTYM{1NjUC*mB%*QZK;F322>@pBaJ$O#4GH1@HwzA z4gOHPb`~4{t6Av#RPxUUug>i}u3DPXeKXnGtBB#awsX-ydu6#e)kQ;c@pDV*RA+@f z8~(!pOpKY;5Zbvh3;u>HkIPs=AY0ifd6=OrFZZfE=H%NBxo*14T_(y8WXK9pL}^22{$^({6df9RJmg57QB;6M&) zxcpE={^lhRO8^uCBz=a>Appq!CXjJFJplm%ekcp$nF4))8fbEt)W&s;fyaQ%Mh4Ff zeu!gdp=^Fb=tnJVEPPO+gAjJHZl4kSp@3-73R#dr{JPoRUt0>Bd50daIjN3Js5z z;6?OHas3FBL|mAUb0(~_^55!yE|@sJmG2fbF)_b%T-o)4FZIttaL3ESd#@K?Y|Z&D zc_qHs%i9(Hv+Y=Ms$?=I{=U=y(M4r&x?NHbjOJ*0apksK8H1L**vD6nIXnx4*i`+S z&wCwCo?6~G=KUi*7_o>kHL;VQ)%<+l5s5?F`_D?S6KEN@o$2>p$NEBT_ zys1)@U@3=TnXlcphg27QU9ga@@|u&a>c~#B3Mj6Pd4nVkl>Cff&48&wGc*!|FBvn4 z={8JF)wUfvZjKAmiMkL#n@E6SMiX>r@Pm-GVtb|YIwiDxzXIBb=EQqV2*04<)SxPH z<`2bt>5PX%=d_Z}Y)nikxvb3)UmEl=m+d|7+007#hx98QZ@)t`dQiS(#Obe} zBP3o?XzmL43hk`?60H96t%U_&Ed21iyUcJ|_+l~oC$F;P&nVg*>Ww!a`e=u?Zn}cJ zdJ{l8FTiybxJ@QdiO{Zg;RY%Zlgo1?K5>3?(>cBZtTUSdwE!IUbx;sCDCzR3LT-C=DMk6#y{x!r7TyD4YqWqqnym4lyKS zN8GA^7C~zYvXOxRFj-MikgqCeVf}{-P$UcvJl57*5+?y`$xJxNeDMCeNCkGP|3(J- z;0WPTT>4eyKspfv{HIo@cl-=4h`5ZaYK|oTc<}J3qH`xL0x$K^=J)e4PR{eI=lxlv zC8aU5A^EQ38o3OnVUb_qM?D*w-dAI4i$p4(C>0*+>|brH)26?MUbvf`KfaJ@2{Q9R zT3y2wM3xN`Q~k?BJSH*yDbOkyw?Toov;@`qj_=>IlN$s6NNr8+u?GGL!b5G{(IUpg z`6gO`D`(SMWE)XX7|pV|$6Z3dc{#S~Pb<%*=YL12LS(O>krQWJwX5{{mVH5xX`5UL z^*l^O7)Lo);1Yzk%?PLl%p?=QatNG4AfQp7SDs?yxqX`wP>KQvDasI8bk*KJG3R5T zI1V4JLzXD{K0sR>CI&5?EA4+d+%a%pq#&oDFh+R9;TaknMZg$J5OKzXy{`pW%R7^f zy+hdy*@j??13sz$77BtM0molnLE#nnqyE|5`h0256q1U>;k%Mk$1JUz64WGj zIYil4-R`w1yRJNiAikROC6AU(m4JSu*N2ll!jBYAFk*G^%z3dV-+X^Hu7K;QO8bjV z>pY2@AhTg?Qnn0*_UP*GI;&O3hXedpH6eGLH4fCdr_^cIAgrDr!& zjr$rUBqXH#i4P5`k5fiLOu_kLWwP=n*zIw=HMOw`1Ck$TPa6YeYX_iNn4+}DPMIN~_umdZO|U@TfNBl~ zW7FlZi2DDnvO5W89|< zOy|ohVcZ22i-os{*!C=OZfyaFT19x@n|zOkD}=Cz zW6lnqsIL?k;%kIEn8__(HJiTji=84m%zZ)ccAWFKdxy&t3-jaJ{vA2 zb1ehuUMkUsF98&Ltyl>5HSh9Oe4}~H2wZ)E0>_yk=1B>tsU(C0=olDRw%!;4Ut+Yv zo<*y`APfRA7Ct^=7P+^X?~88!c-q)FKrh;uHLfyk<&Of+M%Za$u-;;%^R!a`nZVboQVO^TIJf@rLE z{)u;eR$BLuc()1P;{w=)-Fe@3(lwbHwx1Uuizs{le$1sxApNiW%)p2C&DVfR4UL&9 z!Xz}iflR`rq$DUowPVaokB?VL`^HB{Yp%!ENUnBIAu|9-C2hcn=z~U1hdd?@*gJLr zC1iqU0xlUOsb+cGF*JyeGGr5c;Oz)L$>26cK*ehSnEoyAi!)cytS7?qLn|vQvl#ov zI$mx^#K_3#v_4D-l!_K$E;Hevzm;P_+?b($VhZBF4SfM7$uxp!eprMYR%n0V;AdiH zMn^E56<_{Rn)WXlG_NF%=8kl!A`5v5Z*vb_uP~rlIjSAOxV&joB*=tW#f))W+fVT- z9?NJVQ=-du#!g*m!)*`8R_<5ICbcW3EsgKsLV2O!tm?{}J?g(I5_|4>_|k@=hAuvN z_XXPv{4O_r7BZgo6eHfzG~968cpyra93y&le?{eViIG4h|1z;}1LNt7r;f}ADujNp zY22-HC;l^!ALY$PW;%S>o*MIm33ezzVhe5bkEPy@IBpt^3}e+F)BD%8po9$Bf3;D~ z8@!fn7)?Hnq;tRsG7!Ot_Cs%ed?1H(ce0^SSNl6En64un=1Dvr$rgnWNqGHpr}ja=-lSaDktb)u4;^3Y)`u$6e~p= zK4nGYu?@~Eto&9Kro+-y|0ZiMA$x+Vn6>EVW~uW{UW_j}_n^B*MoJ0|P8xMJHDBn; zfk#iTsGx>85YATVmoM)E?Kd1|QPac(EfBb_fe12QrJezMDKW&(PXOaq(Nj|+Mp~s_ z0s@MZQJA9SiOI}7d6MvR)oHZY zoTw)8C0b4+p%>nuWZmB-+Lksgg6l1_v$MVl2|v3c1_lO->=)z`#GPrpGyl%}7z~I- z9o-j66Z-h$7w7luXKKn{J~pjax*WA`8$BWRa8k!isy@O2P&%gPg^Gw%`UdHWzr;@? z0W_+*N<95k1{85MCq?bDGly%kxkR1bZH$AxHx|kfUIYi5Hj%Wt$8+)OcG4^Ly)pY! z)~nm-D2V+uQIc(y)_N03uP;Y2VP>KH+Hm~Zf@}Pnz8R@8lQBi%_5E5k0xX*D z;osc4k1~2GPHIfntM@*=Wh0*%WOABwR~F$T`1yb44pjR9Scp z*-C`=6jjV^R;Ia#Tf#17%{#qbg=bMLC{$@Esb}(Cw5*43VsRG=pLSd|Gwcc4)-?d^ zC){e{kfB zr}Nco*DwnXb>Fm=w8-j<`>ao#TerIF1@vl4Kyjd1>lqU>Q%JIAaKL?MV9%MR}PQl7aVq3LJ0gQ zDUR(!8v#WJI^|-SLdL_r^$DWUN3CHTbA8iTSLVhj$|eA~?KhIPolMN31N{3N&vIi? zsLhj1-N=V^RNtgbCFk}M*5 zzOJxyaWnp^)YAz}T;h5mLO!&sj=d&(B8Hz6zst6)+8 zZMZ%9$7$`MXI~>_Y=7nAariU2MQP^uP=BFt$d4Y5cJ1Xd^on9|h##-Dc@AJt-Z(uq zL7>QHd0bqa&YZicsi_}q1I(8-4g2S}mi<2&zW;7xHC=w?Ji(O`6-oad8v$FNKF_;i zuH-il$w~bKs=CGcD_T$O|pkbdJvc7dyP82ECT0mmhxj9!JO>#^$h+}hp-!&1stmWvdmIoHPi=IZ-}K3j39*IQh@vzc`g-@R>ZgN!ngygQp?YVMGn-9kL zgikUkM&c1;qCt8~yfv;)==k?<4qy(+8*tv zd>)&T8a@65SC>f8A!jus&Wn}tYG=`pWcbv3Q4z1;B}qoUeVi2njm?t;k&vmIi3|yuZ+d==iM8Q$(RfgFyI_( ziUhs(Cdiz>d)qyC%-+UNIDgDGu4)ldQuy;p-)jxY@lNFuLZ|zu)9WubEb#ST%GCI_^p)s$d)MKY)=3Ju_K>B+9)d%&cqPwEXWKsI4GbK z?G7}E4Q1!abQdU4|FO4I7Qcl~>4=v}G;^67i&WT--VlPJX?w ze-M90zg&Ct*haO}1UQzds>$M?z~DV^+#ZQ}NMdgSRBU!5TCt_IHAFwK6@-ywfC~f- zL$iyE0Z6b7Ar1w40hE#E!ArUc&?~5`zrohY61jHG-)rB}!drskjT75T*E(+=_VaUo zt6Y)v#xdxyIS>jjZgi~9xr{@L0mjCdApFs(UHLmem~WRHt7eM6`F!0kEuMxh$%hgG zPWz1>oFnedSDitDGS66LzDE&k_}tFbtDhJbxS|q#EPi>x^{BZ`KMbliIPR<6UbdJU zK5PVj5*uMYbd0!C#IR=vu@prAsIZY}#PIug=&!+g?_lvR60E*&#LTEgZx-Kw6&6Gj zrzbY=t=bwONO6(_wf>`tz3te(@OQU7;L0trRVzkKa^~VixQ&8z{9v-Fy_def; zS=J4fj7fUvTr`<&9c2!gPbKxjNxD?YSp=GWqgir^y#{=@S+=|V_3t#{Q3((n{Z7NW z%z;*aY0PNo3gNRuttNvTvx*=?P*_D_HEY1|;&`I3wWMTG0aX!V8zCPT5LD`CEV ziVeMF);r%DnQJ7J!^#dG5?t^TU8?#sAgDX00KY(UHkomqA5|}F^?PWH6tg5cUEW;U zt*UOk=!?ta`PD5v;T%@^A42mPX8~%RzJ~1MDHn&Xlx3F7OpU4wHheE0t(KYBkjwM( zH7Rf#?NeI`XI=7tQ0k{;*VJLikblTIMD@iTj#l`D5ke&~8ze9YTsB5u!4cXFrv_j+ z_1oxAMG`NEQt<|smI@+vHdxsuwklg`zCn4IWaUdTRw$23#i)eGyQ>AZ!=y)^9emGq z#R=GEH4Q1I8PnR{ie-d8;$HdsI$EzGJ$^U!1D-a#vEF?(6Mfm|Y8$zfJHt$Z;-Z?b z{qLFvNIqspB}aT~ioJy~Enz?qORgr*LA1p|YxVXk-GB$j&Ty28UZ+4xdOEofUYLcc z;VvZ=RI#=9%t<~T=lou=BXA7q)_+=@3@Bi5aEAjjRq-Aqi-!R`2vSyLZW~4Ig)W;q zP@|ZuE5At-rd{L450EU-C5BPa!$euKz8WaJ61J7M4Ivs{dF|Sc1{hN_cXrl_Y5c`a zLx&GYC$Ha{`~Ab=?J+B3B@G#h?6&^ktl_k)w0^X5!xwp#=)?61X)MtI-wS_=X18ST zoanIq4>6=OfmSd6Y}7?kF7Fg&b3E=1$5;h z`dG;7AR!sTK*&1@1_iw9F;;xke83+=@~F<-MJw0T=+@!8vvW~A8pSC)9$>Jv7Nk-`}cy(K3YyoMg`MEgC1wD zVLbzAE+Q;Wq$lbW2oY}cA?%j)E4DLr^l;~H;9qeSCyX8QZPn9;CYgJ}#0AYCcrs?* zGbiGH_Y}XpTbOs0QKL{%sQfVJ#euCt zrJR#XvTLi<51nd)tj2N(CC)BT1u-RVjk+P? zwgT|oJqPZ*g=bMDcWB7uN}@4P))eSTo2j`D2_@((dU>oX^Vb3%wmHRQ^58r~J7+v- z`!ze~#(WC}l|()_ch!nL2VWa2PJ)w3Y&66D)?bk%DAb5}4{?bPH5)cj5YS>!zF!|i z@X+>F24TqMuVSN~E*$Y}1ul8f{a#s;vba<6jN1iSn86)2g=gsMWRU=E&(0db;kf`Q zAwr0S@Vy@n;~<6;Ab7SyuWbN=O-N-dH9ojHyD4RPt2`=2(yA#HlioX=L%(IihhN9- zj#81;8$yD%pWdbM>7|Rdb^>U+JXS>qBvutVN=SMB9ZT!elX~GtuVYTKb~IB2t;x}U z;=k4QGa_hh|@3}<)c+qzwlXWRQ%w@b51NYg!^-Fx{HbT@Qp8xcbUW1K-Y`)ZGT|@7z9Nl zc{GW-3=n+=gA;}A(V-DZT3#Lpnq0#h8YEx->0~eb_AUGU?|PV0nM0OH=;thuREhJT z@2arW<5lrIG;LBHz16yjYv?=XX_tLkG{xHPvn%cNcfP|raZprV`8#!X@~mx4yC%vv$4wpZD_d!)OC*iS;S ziy^dnRqi7tRW z_V^EQBpv+1W;jyrn7Do!o;VY{bhxgR&)GNQY`xe%nYj5~gJ`<`KiBA{f@2Kd6{?(i zwH_2yJbIF5X4iln&*!#d)SW0wb$))n?@|vZd_(>DK5q#mveR3B!IpAgKAP;jF|d$G zWxb||`&0Fq$>Bo#?Am7V*3G*lyGqQcS>UR^crpBZ&u~6Kt68teXHDT6gYy0V$CAL8 z*1LhV7k9(o?reNJiKE=5_D+6DP|oc5Jxl_ag(X)uFGr;2Q=dxlCOmP+rK+oC8!m4# z$+(CPIUv(v=H8(6{mD?Rc>@Xq(eZeA=&oa)pDRpwqQ^x)p4<8(ZsQ)YXVvFx~g+figW+cIBK zLxY@|g#~d$mz9%~PVY}aQadp2g#)M$l5+WspWlkso1u*wM^{%@IYGFnu`Xt5?~{5n z|6%)--Wu(Zx2mv9QEPa+G>}MN>C>)rL=YZT@uuBwS%A`l`b#B@_>Y#D=d(czcKVV{ zb+<-KxCn8q)_dK}jc0z)^v9ESe>rRN6Bpk+m_2e0wyX|eMMaib{|5LVl)xV6aZ~JIa+`HRh z!`-^+c#DSHbcpeNC#GMcT26b5nLwk(t?9#us!A_T?6WsKQwCN!64LuEZp9l?0}md^ zG~jwD1)%9I8U%|{)7B2A6|!XlYOIj$q@CT*a00%QwWp^|A@|d_dZHjM|ZSzQ5#6A(73sDE@H|Td_i;dky5{nrLHvt z`P+@0zbHlX7h0^`Tx9CB^WWy zu|HaLTr8vaSXBnRS$~oXCRkY*M}6ja>E;b2qHY>>jUcPys~M-UI!S8Mn>@Q}r}Amw z}mVwAYSIL;~Lq4;QXIpwD87A7ptP-t= z5P*(lnQu_x=wwgSH#`-1Qn~#~Dv%4 z)^wja%N?DRN-LM;sq{X5R#`wlKJme-pzqV*!C#P?VS3-WcDCPWHmz!f zqA*nD4(PY=hd;_%#g|Q&_PkTdxJtJsvOQ-2p8~XN>iTrL*5ZNZU|V*XMCiv%{xfdP z=s*vB^3Z+@u*@K_wzh8J$(X(skbF<6%a%db^x_F7&Bc1!gRg##M!t%g<=lNV+p4#t z0uW~7IVkxGTJW8uKo?*U^va%sx+>D8;PYPI%5^Lyzwq>H^U%I^-dp87DnV%Dr&vXF z0fyWz+%mqjx3;6*;lBjc_iL+fXtu2pdV5-6gIY`nfwnYw(ZKv{0SOOiMZW5CLw2vc zrbW8r!$V=Gl6wyb&DW>7h00JrYUwzpzH$!AWLMo>ms-vAZOUI$?NbRH4{DJ$`9r;k z9*|BDGs51i#(ds=638r%;&c_vo%~Q}o0`41Je(q!21QBtqJTcC+lkwRaF#`77-cHS z3X(!a6{-UWeG~Xy;d#;QfGz>p0Qr=utWIDLqyR#rLDb9V!l-{q^s2YG2SV4KHC( ztCU^-`}pMQnp%7$jo?B<@}ek+N=&|gxbbWxqVpcNub4CK*9YMy zk6-wZ#vY~epy)hz(N85d0y=}_B$;EF_E&mnf~x*s;Q*3@2iF39`muX~Tr6cJyVvGs zmQYZ2VSBX9PhD$_N>XKrc+{0{?>!Vrt86ou!SZfKI-TNuPvMhM3>4mgVx;?3qOmCVjyL&d6zC|X za)E+FG9O>e5k< zM8)v_2)3x|xkJjh{fZgJ#IRQIu?SjhRHaGGuH-K*0k^jurkL7ncT^El`|)sY7#*})2Ro2$(&h@TvyNd zsf<3#LOkWlyMrm>YJ#euFl-t9Y_TlG*9^N8S8Y9M-W|?lYRcbW3rHl$Lyu2yRb%fM zB%JX0{KQrF*19deP5$Mgdj0zz+FE|o5=h|ns1;CxY-jKU0MKX-I1XtDRa>-n+cJ5Hj{&soJwl(I)}CUU zGh*M`UdG8%WVYnS^So0fK7;k3b~Dn23;#*f5X3hp2pF3d)a?35RwEB#ZNU^%<5=Go z_z{%)0o_j*=ZGcB%bAvp^UAC-w3-PnjE|tlUEJpHshSvC&sZM3b!+`u=(8#7xzQf1 zk=tLT4X;l(`5b%Xcdw+-by<_86iBBiY1_ld3fzH8&3!_luTML*I|Fy{3;QDZOaN~v~=Tf6Fl zI~ZI+Dcl z7vUI3Bgg`1PXnDSL|?H~L>P~Hu<7naVa9lI6vjX;*ZN}CO!cW*qn@x!alrBdcMDIt z&g<@PNi&!ogAbjOhH2`$s?rv5Vz6xJnWB_tmqrNcwq5QOjEFR(!imv_l3dJ{EV#7U zZ<|&GP?Fgh3C6U#ys51pO#e8+~%1dr-D8#Z(@}{M-;rQJ@qx`k(1+ z{CkR=L=%z2qZYEIgMLLg8{~8*&kd<;MGx=#z+LUZec)LuaEnobu9PoZsw+%GEoJ}R33-q+QCfXPoN!U9hkXg69L z4=taXV57ydHUwVBs{WX1=!jNuto?v@%Cj@~Gxf2G>IS_l-lfh~lbC^DZVzlcfdzZmIk>4}i zXmQ=k>)jIt4e8Tt8eiy!jNVh_=L8@3gtk$3{TI_ z`hhA6la>Olfq)my^2&}Trk}o&KowzOW-=-uz?|m$GrYUstWf6(6yfpajx9W2J6A+< z2y3XE{biqB4dL-UxUdMxk@(8AtoRCEB;TK};wCXh{U5>|uG4SMZJ6@9+<2z2nJt^l zn)qUu_dNU>T;GE_Y`uzy*~0>QZ^6@%o$W!wb(b4Nvdkz|SCXBN1&jWoDpNaJ=;sCX zsEh#Ys`49&zo_@E3@i5RiK{=brFL}>uM~eS9y{!>jhWPWgT`4MqAzzz%Q4s zp!brF&)UY$zm6I$-QaXa-G;#`JZV>~mB;U2!e;3Uobe~{n9wq65@YNF+Jzw?nWMfT zu;%|B&n-2`_p1m-dp^dD{;jLEY8%;cw=>Ay^5cEgZ7@p;DXZf=*ibxPbbMaJ8D;E!MHpqp;>qna{tWUrIq5^)&r^xtm*@; zos&#ay%PtQJFuAl6Gx~(b?QAINB~3gizvbohscWW<@dl=M@6{BGplNz`v3c;7SND}DlZjM1lq zoYBg@ideTEp)I{LS=slMMVG&}q^|1KXQ`sr(aj=D^`40&rSQEJEL>;&qMR)HJS}__ zU~$Zkf4beEU$mH>A?Z009qu{;sL-P}>2cP-f`=gb$I@&ZlA(Bz%>r(l;4!R}|9m=r z!rEp=H9%pQdWH1gEdeP6X(#jt+8_(ii$EQR{67l}nRiu0$dBMingyx;pu|Mz%>H); z_|1MVIE^jm;Y-Anl!5=6|BycCf1e=~w9&HJLiP63U3YFfyCe$tbT} z2>}5e`;R>T{gtRoP$3)ipGwMDpPy%jp!B~V4H*LGkT|gDxDM_c4*&b>0m#>7!6y?J z6pmgY)Pw(ZXX@O@oxO(o4nk`IDs#xc4($Kj87XpS79eEh27+ZqNJrKGzLXMjsZXGV zOade=96UVI|NSC7P{_|BK?CU}$Z%U%OaAxv%o@#1s3fliLJPGi=>D3-prLRG2wI>u zi{@8d-Nm^l0lf%0ka`IneE!!H9YWFdJ?IMtz%Qi(T_J6lgo+B=)%6ey*I5Q`nvU)* zNFWTIc>VX)kf@*is~-CKVL-~>%9Aej`RmsO5M4qj*s?_5{=wYvdROKOzrw5C0a-9( zP%3i+DZT&Wy)z(BE@ulA)Q*mh258Uc`ahl=8|jub0SKTKl&=v0x?|DdUsE?S37FGF zAalr!0#Vc!Afdelu4y`G$NZ;@`QKAH`y-v-pkMYDYA2>3rQ`-A- zK|u;AAhdxsJRh)R|Ifd_4gP)-kg;orLViDRoV&r!cf;X-rc5RSGN9Mqy?eI<)nf*MD!0 z5PIgLt^k&B{rYuDX=!v2&n01Gi~#glnkR;Zi3v61bx03fK|`0CMkC@Po&R}cQVhsP zqVCDbF|S-qRQpU_|HyT@;j6iawJBFB?JHSyv=CNo99$)Nf8eHJ%GaQ9uc|hPGP$9X zK9pwqg(lC3drdYN)rqBgF@fsIKR|c=og3t$>1Bv#lQ^J1^mWO+tInAdy6jEFlkdy6 zz<@6R(H6j>R%nBl+Su9(*JB0kA{2Xo8kJmGaWNSH9l=Pi&C=2{Am03?uS9-&`c94N zf$!$#W|0!|pG+k0i$K*0KC_2IDg#{3b9@9v8>Kg>1FvRhzp2}9=UFT#XeQPlq5m6O zVneH`nU|R@Vg5T|9DEiSZmI0bMCgyS@)7vt*7H(4MK}NNo@9@JN*V zB|$~Cesh^2$T+ynrk%$$!@=-t=&tS$F}au9H=}Q^2~c|S2i;tgxM7^QG--;vTt2xx zKYwMZ?mn!pqiJal)f@iFTB+KS?==&MHQSxn z-TQs$S*ZQh$LDY2=empJW+U97&fuEiwccOn;$a>2?X9A0%g$Awf+{822cKQ2FV<19 zeE)Ri&SCjZ@viOzhnY~;?t3>`yh0n`R$2Ze(^ei*MfpUqcE4>U`bB?u@@0B-RQS)m z(CtxfM;i`GArFznaJ|m$tZWWIJHq#r$IHU1@=XO4F3MkU-M%jss?QQH zuYc-!#=hhytv{l)osqvjzq7-)UJG3}%53kshF1%Il~DOSEuwN9I`rD}VsuX2F33*q zT_zzZDl5C~z?w5~OJ&g6%Veq9o^$TBY&7&v!9f1HiAW-wqnv)*`MGy)K|y>dE~hci z#}{%oacsjQBgKiKUNh~DjZ!3@{d&r}Tb}&$Cwh8@hK$v3qo2pIRppBw9IDijfkq{a zzKk?TvVmxhKh8ftA`~g}e*|6SgVlt{?pzFMB0rs#@UMy$1})8(pfJ24AXj|E8JR5Z zMN^6RextOxQ*GJ60gr~)poY5oZNG*lg{Y|LZ%_yE0&gd^aa zjAp!D0uI`5Q0B3Sd$#cq`AdwR!X-whr^7*Vtre*VLO5j_bMp-nRq2n|!zR9R;=SSU z@2jl-Rae|kkL(jfU3pwwT^C?S_znLQDIKRjD}Hj<=lm{o8;}XvO}_=Qn?xxxyCq%V zcVc^<{RSG7+un-$T_C>mKo2i;M$uO1N~}RK=uHf-VH-3KEP$?{Jx~vzjrnBt!V9jP z%zYWTZn)GwD8J2RQV}*2+f1}s-`2gS!Y)1HOhUq+_~3rNnKMj+PBYT?3Uy=WFU2Fq ztSL1$&vxvfU6VF7jWJQeNBp*sP)t?XdX@0qyL!jRghRh~KQ zKlB=O7IV45c{3S~{cqmkDg(0mBNB;M?p&Wcg{n2z+}o=9dPa~<5uhUb_ur=!0gBU0_3KrhqP|M$7@|9=5q6s(E=kM|(I|95xUI2Qk;?*Cf+_=aA5;e|h^){63t0y%e|v za8v!y(9o&5{ns+(yj~c7SG47E{$@Mv!@kHl`9*iTRp;D)bJT|CaDtxNI#4?Kxwd8J z4HMsMueShsr((`j>bp%b6c>L!`|*~N714)4<{sU^VN?9hP-&B0&eRR!-Vw`Hzeqf+ zoz*?3`pjSET{9=Lr(dagawnei{Du~};`zXmXLh%wMxq|jx#C6jZL2v-KkrQjr7{Ld zA<&fo3=AqRkb7Sy2mr_CfWX121$@B^1E=7SR`8E?hmW%R?rW2pK)&*H^>bP0l+XkK DOic3` literal 0 HcmV?d00001 From c0f5b9420648e864b7a0ffd2a58f2399784f9025 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 6 Jun 2023 13:53:33 -0400 Subject: [PATCH 13/41] [chore] clean up; fix clippy; fix cargo fmt --- aggregator/src/core.rs | 4 +- aggregator/src/lib.rs | 3 +- aggregator/src/proof_aggregation/circuit.rs | 54 +++++++++---------- aggregator/src/proof_aggregation/config.rs | 11 ++-- .../public_input_aggregation.rs | 20 +++---- .../public_input_aggregation/circuit.rs | 2 +- .../public_input_aggregation/circuit_ext.rs | 2 +- aggregator/src/proof_compression/circuit.rs | 22 ++++---- aggregator/src/proof_compression/config.rs | 12 +++-- aggregator/src/tests/mock_chunk.rs | 7 ++- aggregator/src/tests/mock_chunk/circuit.rs | 1 + aggregator/src/tests/proof_aggregation.rs | 11 ++-- aggregator/src/tests/proof_compression.rs | 17 +++--- 13 files changed, 79 insertions(+), 87 deletions(-) diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index bc91eda195..1a5624bfcd 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -2,10 +2,8 @@ use ark_std::{end_timer, start_timer}; use eth_types::Field; use halo2_proofs::{ circuit::{AssignedCell, Layouter, Value}, - plonk::Error, -}; -use halo2_proofs::{ halo2curves::bn256::{Bn256, G1Affine}, + plonk::Error, poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, }; use rand::Rng; diff --git a/aggregator/src/lib.rs b/aggregator/src/lib.rs index a6306ec227..bf2e75cc29 100644 --- a/aggregator/src/lib.rs +++ b/aggregator/src/lib.rs @@ -7,7 +7,7 @@ mod batch; /// Core module for circuit assignment mod core; /// Parameters for compression circuit -pub mod param; +mod param; /// proof aggregation mod proof_aggregation; /// proof compression @@ -20,5 +20,6 @@ mod tests; pub use batch::BatchHash; pub use chunk::ChunkHash; +pub use param::*; pub use proof_aggregation::*; pub use proof_compression::*; diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index c5d56df08e..2114f42378 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -1,30 +1,30 @@ use ark_std::{end_timer, start_timer}; -use halo2_proofs::plonk::Error; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::bn256::{Bn256, Fq, Fr, G1Affine}, - plonk::{Circuit, ConstraintSystem}, + plonk::{Circuit, ConstraintSystem, Error}, poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, }; use itertools::Itertools; use rand::Rng; -use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{ - self, AssignedValue, Context, ContextParams, -}; -use snark_verifier::loader::halo2::Halo2Loader; use snark_verifier::{ + loader::halo2::{ + halo2_ecc::halo2_base::{self, AssignedValue, Context, ContextParams}, + Halo2Loader, + }, pcs::kzg::{Bdfg21, Kzg, KzgAccumulator, KzgSuccinctVerifyingKey}, util::arithmetic::fe_to_limbs, }; -use snark_verifier_sdk::halo2::aggregation::{aggregate, flatten_accumulator}; -use snark_verifier_sdk::CircuitExt; -use snark_verifier_sdk::{halo2::aggregation::Svk, NativeLoader, Snark, SnarkWitness}; +use snark_verifier_sdk::{ + halo2::aggregation::{aggregate, flatten_accumulator, Svk}, + CircuitExt, NativeLoader, Snark, SnarkWitness, +}; use zkevm_circuits::util::Challenges; -use crate::core::{assign_batch_hashes, extract_accumulators_and_proof}; -use crate::proof_aggregation::config::AggregationConfig; use crate::{ + core::{assign_batch_hashes, extract_accumulators_and_proof}, param::{ConfigParams, BITS, LIMBS}, + proof_aggregation::config::AggregationConfig, BatchHashCircuit, ChunkHash, }; @@ -104,7 +104,7 @@ impl AggregationCircuit { Self { svk, - snarks: snarks.into_iter().cloned().map_into().collect(), + snarks: snarks.iter().cloned().map_into().collect(), flattened_instances, as_proof: Value::known(as_proof), batch_hash_circuit, @@ -157,18 +157,18 @@ impl Circuit for AggregationCircuit { .expect("load range lookup table"); let mut first_pass = halo2_base::SKIP_FIRST_PASS; - // This circuit takes 2 steps + // This circuit takes 3 steps // - 1. use aggregation circuit to aggregate the multiple snarks into a single one; - // re-export all the public input of the snarks, denoted by [snarks_instances], and - // the accumulator [acc_instances] - // - 2. use public input aggregation circuit to aggregate the chunks; - // expose the instance dentoed by [pi_agg_instances] - // - 3. assert [snarks_instances] are private inputs used for public input aggregation circuit + // re-export all the public input of the snarks, denoted by [snarks_instances], and the + // accumulator [acc_instances] + // - 2. use public input aggregation circuit to aggregate the chunks; expose the instance + // dentoed by [pi_agg_instances] + // - 3. assert [snarks_instances] are private inputs used for public input aggregation + // circuit // ============================================== // Step 1: aggregation circuit // ============================================== - // let mut aggregation_instances = vec![]; let mut accumulator_instances: Vec> = vec![]; let mut snark_inputs: Vec> = vec![]; layouter.assign_region( @@ -192,7 +192,8 @@ impl Circuit for AggregationCircuit { // // extract the assigned values for - // - instances which are the public inputs of each chunk (prefixed with 12 instances from previous accumualtors) + // - instances which are the public inputs of each chunk (prefixed with 12 instances + // from previous accumualtors) // - new accumulator to be verified on chain // let (assigned_aggreation_instances, acc) = aggregate::>( @@ -209,17 +210,13 @@ impl Circuit for AggregationCircuit { // extract the following cells for later constraints // - the accumulators // - the public input from snark - accumulator_instances.extend( - flatten_accumulator(acc) - .iter() - .map(|assigned| assigned.clone()), - ); + 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 snark_inputs.extend( assigned_aggreation_instances .iter() - .flat_map(|instance_column| instance_column.iter().skip(12).map(|x| x)), + .flat_map(|instance_column| instance_column.iter().skip(12)), ); config.range().finalize(&mut loader.ctx_mut()); @@ -243,7 +240,6 @@ impl Circuit for AggregationCircuit { // step 2: public input aggregation circuit // ============================================== // extract all the hashes and load them to the hash table - // assert the public input matches that of the pi_aggregation_circuit let challenges = challenge.values(&layouter); let timer = start_timer!(|| ("extract hash").to_string()); @@ -297,9 +293,7 @@ impl Circuit for AggregationCircuit { for chunk_idx in 0..self.snarks.len() { // step 3.1, data hash - // - batch_data_hash := keccak(chunk_0.data_hash - // || ... - // || chunk_k-1.data_hash) + // - batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) // where batch_data_hash is the second hash for pi aggregation for i in 0..32 { region.constrain_equal( diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/proof_aggregation/config.rs index 448e346e84..584a65a53c 100644 --- a/aggregator/src/proof_aggregation/config.rs +++ b/aggregator/src/proof_aggregation/config.rs @@ -19,8 +19,9 @@ use zkevm_circuits::{ use crate::param::{ConfigParams, BITS, LIMBS}; #[derive(Debug, Clone)] -/// Configurations for aggregation circuit -/// This config is hard coded for BN256 curve +#[rustfmt::skip] +/// Configurations for aggregation circuit. +/// This config is hard coded for BN256 curve. pub struct AggregationConfig { /// Non-native field chip configurations pub base_field_config: FpConfig, @@ -30,9 +31,9 @@ pub struct AggregationConfig { /// - accumulator from aggregation (12 elements) /// - aggregated public inputs (132 elements): /// chain_id || - /// chunk[0].prev_state_root || - /// chunk[k-1].post_state_root || - /// chunk[k-1].withdraw_root || + /// chunk\[0\].prev_state_root || + /// chunk\[k-1\].post_state_root || + /// chunk\[k-1\].withdraw_root || /// batch_data_hash pub instance: Column, } diff --git a/aggregator/src/proof_aggregation/public_input_aggregation.rs b/aggregator/src/proof_aggregation/public_input_aggregation.rs index 1221761f92..08fbc1f6ba 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation.rs @@ -11,22 +11,16 @@ //! Those 4 hashes are obtained from the caller. //! //! A chunk's public input hash is then derived from the above 4 attributes via -//! - chunk_pi_hash := keccak(chain_id -//! || prev_state_root -//! || post_state_root -//! || withdraw_root -//! || chunk_data_hash) +//! +//! - chunk_pi_hash := keccak(chain_id || prev_state_root || post_state_root || withdraw_root || +//! chunk_data_hash) //! //! A batch is a list of continuous chunks. It consists of 2 hashes -//! - batch_data_hash := keccak(chunk_0.data_hash -//! || ... -//! || chunk_k-1.data_hash) //! -//! - batch_pi_hash := keccak(chain_id -//! || chunk_0.prev_state_root -//! || chunk_k-1.post_state_root -//! || chunk_k-1.withdraw_root -//! || batch_data_hash) +//! - batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) +//! +//! - batch_pi_hash := keccak(chain_id || chunk_0.prev_state_root || chunk_k-1.post_state_root || +//! chunk_k-1.withdraw_root || batch_data_hash) //! //! Note that chain_id is used for all public input hashes. But not for any data hashes. //! diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs index 23cf27a384..1e3f51b8aa 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs @@ -77,7 +77,7 @@ impl BatchHashCircuit { /// orders: /// - batch_public_input_hash /// - batch_data_hash_preimage - /// - chunk[i].piHash for i in [0, k) + /// - chunk\[i\].piHash for i in \[0, k) pub(crate) fn extract_hash_preimages(&self) -> Vec> { let mut res = vec![]; diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs index b31be5b180..241badf009 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs @@ -5,7 +5,7 @@ use crate::BatchHashCircuit; impl CircuitExt for BatchHashCircuit { fn num_instance(&self) -> Vec { - vec![self.instances()[0].len()] + vec![132] } /// Compute the public inputs for this circuit. diff --git a/aggregator/src/proof_compression/circuit.rs b/aggregator/src/proof_compression/circuit.rs index a98b1b50f6..5b985ed70d 100644 --- a/aggregator/src/proof_compression/circuit.rs +++ b/aggregator/src/proof_compression/circuit.rs @@ -9,16 +9,18 @@ use halo2_proofs::{ plonk::{Circuit, ConstraintSystem, Error}, }; use rand::Rng; -use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{ - self, - halo2_proofs::{ - halo2curves::bn256::{Bn256, Fr}, - poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, - }, - Context, ContextParams, -}; use snark_verifier::{ - loader::halo2::Halo2Loader, + loader::halo2::{ + halo2_ecc::halo2_base::{ + self, + halo2_proofs::{ + halo2curves::bn256::{Bn256, Fr}, + poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, + }, + Context, ContextParams, + }, + Halo2Loader, + }, pcs::kzg::{Bdfg21, Kzg, KzgAccumulator, KzgSuccinctVerifyingKey}, util::arithmetic::fe_to_limbs, }; @@ -98,7 +100,7 @@ impl Circuit for CompressionCircuit { let mut first_pass = halo2_base::SKIP_FIRST_PASS; let mut instances = vec![]; layouter.assign_region( - || "", + || "compression circuit", |region| { if first_pass { first_pass = false; diff --git a/aggregator/src/proof_compression/config.rs b/aggregator/src/proof_compression/config.rs index 8796e530c6..ce66edeb07 100644 --- a/aggregator/src/proof_compression/config.rs +++ b/aggregator/src/proof_compression/config.rs @@ -2,11 +2,13 @@ use halo2_proofs::{ halo2curves::bn256::{Fq, Fr, G1Affine}, plonk::{Column, ConstraintSystem, Instance}, }; -use snark_verifier::loader::halo2::halo2_ecc::ecc::{BaseFieldEccChip, EccChip}; -use snark_verifier::loader::halo2::halo2_ecc::fields::fp::FpConfig; -use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{ - gates::{flex_gate::FlexGateConfig, range::RangeConfig}, - utils::modulus, +use snark_verifier::loader::halo2::halo2_ecc::{ + ecc::{BaseFieldEccChip, EccChip}, + fields::fp::FpConfig, + halo2_base::{ + gates::{flex_gate::FlexGateConfig, range::RangeConfig}, + utils::modulus, + }, }; use crate::param::{ConfigParams, BITS, LIMBS}; diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index f195621174..80950c7921 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -1,10 +1,10 @@ use ark_std::{end_timer, start_timer, test_rng}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; -use snark_verifier_sdk::CircuitExt; use snark_verifier_sdk::{ gen_pk, halo2::{gen_snark_shplonk, verify_snark_shplonk}, + CircuitExt, }; use crate::{ChunkHash, LOG_DEGREE}; @@ -15,6 +15,11 @@ mod config; #[derive(Debug, Default, Clone, Copy)] /// A mock chunk circuit +/// +/// This mock chunk circuit simulates a zkEVM circuit. +/// It's public inputs consists of 64 elements: +/// - data hash +/// - public input hash pub struct MockChunkCircuit { pub(crate) chunk: ChunkHash, } diff --git a/aggregator/src/tests/mock_chunk/circuit.rs b/aggregator/src/tests/mock_chunk/circuit.rs index a398d189c5..2d795ff60f 100644 --- a/aggregator/src/tests/mock_chunk/circuit.rs +++ b/aggregator/src/tests/mock_chunk/circuit.rs @@ -35,6 +35,7 @@ impl Circuit for MockChunkCircuit { fn without_witnesses(&self) -> Self { Self::default() } + fn configure(meta: &mut ConstraintSystem) -> Self::Config { let challenges = Challenges::construct(meta); let challenges_exprs = challenges.exprs(meta); diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index 679593d302..2e8219d631 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -66,7 +66,7 @@ fn test_aggregation_circuit() { let snark = gen_snark_shplonk( &layer_0_params, &layer_0_pk, - circuit.clone(), + *circuit, &mut rng, Some(&path.join(Path::new(format!("layer_0_{}.snark", i).as_str()))), ); @@ -102,11 +102,7 @@ fn test_aggregation_circuit() { let compression_circuit = CompressionCircuit::new(&layer_1_params, layer_0_snarks[0].clone(), true, &mut rng); - let layer_1_pk = gen_pk( - &layer_1_params, - &compression_circuit, - None - ); + let layer_1_pk = gen_pk(&layer_1_params, &compression_circuit, None); log::trace!("finished layer 1 pk gen"); let mut layer_1_snarks = vec![]; @@ -154,8 +150,7 @@ fn test_aggregation_circuit() { let instances = aggregation_circuit.instances(); log::trace!("start mock proving"); - let mock_prover = - MockProver::::run(k1, &aggregation_circuit, instances.clone()).unwrap(); + let mock_prover = MockProver::::run(k1, &aggregation_circuit, instances).unwrap(); mock_prover.assert_satisfied_par(); } diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index 0afe50eb73..b51f39510a 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -12,8 +12,8 @@ use halo2_proofs::{ }, transcript::TranscriptReadBuffer, }; -use snark_verifier::loader::halo2::halo2_ecc::halo2_base::{halo2_proofs, utils::fs::gen_srs}; use snark_verifier::{ + loader::halo2::halo2_ecc::halo2_base::{halo2_proofs, utils::fs::gen_srs}, pcs::kzg::{Bdfg21, Kzg}, system::halo2::transcript::evm::EvmTranscript, }; @@ -59,7 +59,7 @@ fn test_proof_compression() { let layer_0_snark = gen_snark_shplonk( &layer_0_params, &layer_0_pk, - circuit.clone(), + circuit, &mut rng, Some(&path.join(Path::new("layer_0.snark"))), ); @@ -98,8 +98,7 @@ fn test_proof_compression() { CompressionCircuit::new(&layer_1_params, layer_0_snark, true, &mut rng); let instances = compression_circuit.instances(); - let mock_prover = - MockProver::::run(k1, &compression_circuit, instances.clone()).unwrap(); + let mock_prover = MockProver::::run(k1, &compression_circuit, instances).unwrap(); mock_prover.assert_satisfied_par(); } @@ -139,7 +138,7 @@ fn test_two_layer_proof_compression() { let layer_0_snark = gen_snark_shplonk( &layer_0_params, &layer_0_pk, - circuit.clone(), + circuit, &mut rng, Some(&path.join(Path::new("layer_0.snark"))), ); @@ -241,7 +240,7 @@ fn test_two_layer_proof_compression() { evm_verify( deployment_code, compression_circuit.instances(), - layer_1_proof.clone(), + layer_1_proof, ); log::trace!("layer 1 evm verification finished"); @@ -249,7 +248,7 @@ fn test_two_layer_proof_compression() { let layer_1_snark = gen_snark_shplonk( &layer_1_params, &layer_1_pk, - compression_circuit.clone(), + compression_circuit, &mut rng, Some(&path.join(Path::new("layer_1.snark"))), ); @@ -291,7 +290,7 @@ fn test_two_layer_proof_compression() { &layer_2_params, &layer_2_pk, compression_circuit.clone(), - instances.clone(), + instances, &mut rng, ); @@ -310,7 +309,7 @@ fn test_two_layer_proof_compression() { evm_verify( deployment_code, compression_circuit.instances(), - layer_2_proof.clone(), + layer_2_proof, ); log::trace!("layer 2 evm verification finished"); } From 97f396305f2edfc1456be3ddac7a578e7d534113 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 6 Jun 2023 21:46:07 -0400 Subject: [PATCH 14/41] [fix] remove env log for tests --- aggregator/src/tests/mock_chunk.rs | 2 -- aggregator/src/tests/proof_aggregation.rs | 2 -- aggregator/src/tests/proof_compression.rs | 4 ---- aggregator/src/tests/public_input_aggregation.rs | 2 -- 4 files changed, 10 deletions(-) diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index 80950c7921..a178671b9d 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -26,8 +26,6 @@ pub struct MockChunkCircuit { #[test] fn test_mock_chunk_prover() { - env_logger::init(); - let mut rng = test_rng(); let param = gen_srs(LOG_DEGREE); diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index 2e8219d631..0360a143de 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -18,8 +18,6 @@ const CHUNKS_PER_BATCH: usize = 2; #[test] fn test_aggregation_circuit() { - env_logger::init(); - let process_id = process::id(); let dir = format!("data/{}", process_id); diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index b51f39510a..f9d1a463f7 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -28,8 +28,6 @@ use crate::{tests::mock_chunk::MockChunkCircuit, CompressionCircuit}; #[test] fn test_proof_compression() { - env_logger::init(); - let dir = format!("data/{}", process::id()); let path = Path::new(dir.as_str()); fs::create_dir(path).unwrap(); @@ -106,8 +104,6 @@ fn test_proof_compression() { #[test] fn test_two_layer_proof_compression() { - env_logger::init(); - let dir = format!("data/{}", process::id()); let path = Path::new(dir.as_str()); fs::create_dir(path).unwrap(); diff --git a/aggregator/src/tests/public_input_aggregation.rs b/aggregator/src/tests/public_input_aggregation.rs index 650ef21bf7..c4ed87295a 100644 --- a/aggregator/src/tests/public_input_aggregation.rs +++ b/aggregator/src/tests/public_input_aggregation.rs @@ -11,8 +11,6 @@ use crate::{BatchHashCircuit, LOG_DEGREE}; #[test] fn test_pi_aggregation_mock_prover() { - env_logger::init(); - let mut rng = test_rng(); let chunks_per_batch = 8; From fa582d87c9c139045b57d2821d9fcd8d443e33f8 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 7 Jun 2023 08:30:37 -0400 Subject: [PATCH 15/41] [chore] partial address comments --- aggregator/src/tests/mock_chunk.rs | 36 +++++++++++++++++++++-- aggregator/src/tests/proof_aggregation.rs | 3 ++ aggregator/src/tests/proof_compression.rs | 2 ++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index a178671b9d..269318f717 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -1,7 +1,14 @@ use ark_std::{end_timer, start_timer, test_rng}; -use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; -use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; +use halo2_proofs::{ + dev::MockProver, + halo2curves::bn256::{Bn256, Fr}, +}; +use snark_verifier::{ + loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs, + pcs::kzg::{Bdfg21, Kzg}, +}; use snark_verifier_sdk::{ + evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier}, gen_pk, halo2::{gen_snark_shplonk, verify_snark_shplonk}, CircuitExt, @@ -26,6 +33,8 @@ pub struct MockChunkCircuit { #[test] fn test_mock_chunk_prover() { + env_logger::init(); + let mut rng = test_rng(); let param = gen_srs(LOG_DEGREE); @@ -53,8 +62,29 @@ fn test_mock_chunk_prover() { let timer = start_timer!(|| "verifying"); assert!(verify_snark_shplonk::( ¶m, - snark, + snark.clone(), pk.get_vk() )); end_timer!(timer); + + let proof = gen_evm_proof_shplonk( + ¶m, + &pk, + circuit.clone(), + snark.instances.clone(), + &mut rng, + ); + log::trace!("proof size: {}", proof.len()); + + // verify proof via EVM + let deployment_code = gen_evm_verifier::>( + ¶m, + pk.get_vk(), + circuit.num_instance(), + None, + ); + log::trace!("finished layer 2 bytecode generation"); + + evm_verify(deployment_code, circuit.instances(), proof.clone()); + log::trace!("layer 2 evm verification finished"); } diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index 0360a143de..ec5d02b964 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -16,6 +16,9 @@ use super::mock_chunk::MockChunkCircuit; const CHUNKS_PER_BATCH: usize = 2; + +// This test takes about 1 hour on CPU +#[ignore="it takes too much time"] #[test] fn test_aggregation_circuit() { let process_id = process::id(); diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index f9d1a463f7..ae51494d43 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -102,6 +102,8 @@ fn test_proof_compression() { } } +// This test takes about 1 hour on CPU +#[ignore="it takes too much time"] #[test] fn test_two_layer_proof_compression() { let dir = format!("data/{}", process::id()); From 4f23a96b03747f477cf2bcb73b92bbe06d810376 Mon Sep 17 00:00:00 2001 From: Zhang Zhuo Date: Tue, 6 Jun 2023 09:27:26 +0800 Subject: [PATCH 16/41] fix some audit issues (#512) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix some audit issues * fix create rw * fix create rw * TOB-SCROLL-5: additional assertions (#524) Co-authored-by: Aurélien Nicolas --------- Co-authored-by: naure Co-authored-by: Aurélien Nicolas --- bus-mapping/src/evm/opcodes/create.rs | 9 +++- .../src/evm_circuit/execution/bitwise.rs | 3 +- .../src/evm_circuit/execution/blockhash.rs | 11 ++--- .../src/evm_circuit/execution/callop.rs | 1 + .../src/evm_circuit/execution/create.rs | 48 ++++++++----------- .../src/evm_circuit/execution/not.rs | 7 ++- .../src/evm_circuit/execution/sstore.rs | 10 ++-- zkevm-circuits/src/evm_circuit/step.rs | 3 +- .../util/math_gadget/constant_division.rs | 12 +++-- .../src/evm_circuit/util/math_gadget/lt.rs | 10 ++-- .../evm_circuit/util/math_gadget/modulo.rs | 20 ++------ .../util/math_gadget/range_check.rs | 10 ++-- zkevm-circuits/src/witness/rw.rs | 4 +- 13 files changed, 78 insertions(+), 70 deletions(-) diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index f5e3e7497a..bf8b1dec5b 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -36,6 +36,13 @@ impl Opcode for Create { let callee = state.parse_call(geth_step)?; + state.call_context_read( + &mut exec_step, + state.call()?.call_id, + CallContextField::IsStatic, + Word::from(state.call()?.is_static as u8), + ); + let n_pop = if IS_CREATE2 { 4 } else { 3 }; for i in 0..n_pop { state.stack_read( @@ -428,7 +435,7 @@ mod tests { ); let container = builder.block.container.clone(); - let operation = &container.stack[step.bus_mapping_instance[0].as_usize()]; + let operation = &container.stack[step.bus_mapping_instance[1].as_usize()]; assert_eq!(operation.rw(), RW::READ); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs index 90979590fa..9a8addaa1b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs +++ b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs @@ -1,6 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, + param::N_BYTES_WORD, step::ExecutionState, table::{FixedTableTag, Lookup}, util::{ @@ -44,7 +45,7 @@ impl ExecutionGadget for BitwiseGadget { // OpcodeId::AND as the delta to FixedTableTag::BitwiseAnd. let tag = FixedTableTag::BitwiseAnd.expr() + (opcode.expr() - OpcodeId::AND.as_u64().expr()); - for idx in 0..32 { + for idx in 0..N_BYTES_WORD { cb.add_lookup( "Bitwise lookup", Lookup::Fixed { diff --git a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs index 252640a662..2cd5774051 100644 --- a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs @@ -43,12 +43,11 @@ impl ExecutionGadget for BlockHashGadget { let block_number = WordByteCapGadget::construct(cb, current_block_number.expr()); cb.stack_pop(block_number.original_word()); - // FIXME - // cb.block_lookup( - // BlockContextFieldTag::Number.expr(), - // None, - // current_block_number.expr(), - //); + cb.block_lookup( + BlockContextFieldTag::Number.expr(), + cb.curr.state.block_number.expr(), + current_block_number.expr(), + ); let block_hash = cb.query_word_rlc(); diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 655017eeb2..61e452a0a3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -129,6 +129,7 @@ impl ExecutionGadget for CallOpGadget { // Add callee to access list let is_warm = cb.query_bool(); let is_warm_prev = cb.query_bool(); + cb.require_true("callee add should be updated to warm", is_warm.expr()); cb.account_access_list_write( tx_id.expr(), call_gadget.callee_address_expr(), diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index 13d4ef1f8e..e3d3cf8611 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -95,6 +95,10 @@ impl ExecutionGadget< .expr(), ); + // constrain not in static call + let is_static = cb.call_context(None, CallContextFieldTag::IsStatic); + cb.require_zero("is_static is false", is_static.expr()); + let value = cb.query_word_rlc(); let init_code_memory_offset = cb.query_cell_phase2(); @@ -514,20 +518,20 @@ impl ExecutionGadget< self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; + let mut rw_offset = 1; // is_static let [value, init_code_start, init_code_length] = [0, 1, 2] - .map(|i| step.rw_indices[i]) + .map(|i| step.rw_indices[i + rw_offset]) .map(|idx| block.rws[idx].stack_value()); self.value .assign(region, offset, Some(value.to_le_bytes()))?; let salt = if is_create2 { - block.rws[step.rw_indices[3]].stack_value() + block.rws[step.rw_indices[3 + rw_offset]].stack_value() } else { U256::zero() }; - - let values: Vec<_> = (4 + usize::from(is_create2) - ..4 + usize::from(is_create2) + init_code_length.as_usize()) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) + rw_offset += usize::from(is_create2); + let values: Vec<_> = (4..4 + init_code_length.as_usize()) + .map(|i| block.rws[step.rw_indices[i + rw_offset]].memory_value()) .collect(); let copy_rw_increase = init_code_length.as_usize(); let keccak_code_hash = keccak256(&values); @@ -561,9 +565,8 @@ impl ExecutionGadget< call.rw_counter_end_of_reversion, call.is_persistent, )?; - - let tx_access_rw = - block.rws[step.rw_indices[7 + usize::from(is_create2) + copy_rw_increase]]; + rw_offset += copy_rw_increase; + let tx_access_rw = block.rws[step.rw_indices[7 + rw_offset]]; self.was_warm.assign( region, offset, @@ -576,13 +579,11 @@ impl ExecutionGadget< ), )?; - let caller_balance = block.rws - [step.rw_indices[10 + usize::from(is_create2) + copy_rw_increase]] + let caller_balance = block.rws[step.rw_indices[10 + rw_offset]] .account_balance_pair() .1; - let caller_nonce = block.rws - [step.rw_indices[11 + usize::from(is_create2) + copy_rw_increase]] + let caller_nonce = block.rws[step.rw_indices[11 + rw_offset]] .account_nonce_pair() .1 .low_u64(); @@ -593,10 +594,9 @@ impl ExecutionGadget< } else { 0 }; - + rw_offset += is_precheck_ok; let [callee_rw_counter_end_of_reversion, callee_is_persistent] = [12, 13].map(|i| { - let rw = block.rws - [step.rw_indices[i + usize::from(is_create2) + copy_rw_increase + is_precheck_ok]]; + let rw = block.rws[step.rw_indices[i + rw_offset]]; rw.call_context_value() }); @@ -611,9 +611,7 @@ impl ExecutionGadget< )?; // retrieve code_hash for creating address - let code_hash_previous = block.rws - [step.rw_indices[14 + usize::from(is_create2) + copy_rw_increase + is_precheck_ok]] - .account_codehash_pair(); + let code_hash_previous = block.rws[step.rw_indices[14 + rw_offset]].account_codehash_pair(); let code_hash_previous_rlc = region.code_hash(code_hash_previous.0); self.code_hash_previous .assign(region, offset, code_hash_previous_rlc)?; @@ -621,15 +619,12 @@ impl ExecutionGadget< .assign_value(region, offset, code_hash_previous_rlc)?; let is_address_collision = !code_hash_previous.0.is_zero(); - let mut rw_offset = 0; if is_precheck_ok == 1 && !is_address_collision { let [caller_balance_pair, callee_balance_pair] = if !value.is_zero() { + let account_balance_pair = [16, 17] + .map(|i| block.rws[step.rw_indices[i + rw_offset]].account_balance_pair()); rw_offset += 2; - [16, 17].map(|i| { - block.rws[step.rw_indices - [i + usize::from(is_create2) + copy_rw_increase + is_precheck_ok]] - .account_balance_pair() - }) + account_balance_pair } else { [(0.into(), 0.into()), (0.into(), 0.into())] }; @@ -672,8 +667,7 @@ impl ExecutionGadget< Value::known(if is_precheck_ok == 0 || is_address_collision { F::zero() } else { - block.rws[step.rw_indices - [23 + rw_offset + usize::from(is_create2) + copy_rw_increase + is_precheck_ok]] + block.rws[step.rw_indices[23 + rw_offset]] .call_context_value() .to_scalar() .unwrap() diff --git a/zkevm-circuits/src/evm_circuit/execution/not.rs b/zkevm-circuits/src/evm_circuit/execution/not.rs index 7a076021b3..8d72ebbf24 100644 --- a/zkevm-circuits/src/evm_circuit/execution/not.rs +++ b/zkevm-circuits/src/evm_circuit/execution/not.rs @@ -5,7 +5,10 @@ use crate::{ table::{FixedTableTag, Lookup}, util::{ common_gadget::SameContextGadget, - constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, + constraint_builder::{ + EVMConstraintBuilder, StepStateTransition, + Transition::{Delta, Same}, + }, CachedRegion, Word, }, witness::{Block, Call, ExecStep, Transaction}, @@ -50,7 +53,7 @@ impl ExecutionGadget for NotGadget { let step_state_transition = StepStateTransition { rw_counter: Delta(2.expr()), program_counter: Delta(1.expr()), - stack_pointer: Delta(0.expr()), + stack_pointer: Same, gas_left: Delta(-OpcodeId::NOT.constant_gas_cost().expr()), ..Default::default() }; diff --git a/zkevm-circuits/src/evm_circuit/execution/sstore.rs b/zkevm-circuits/src/evm_circuit/execution/sstore.rs index 162238328c..c78f65ea25 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sstore.rs @@ -283,21 +283,21 @@ impl SstoreTxRefundGadget { let prev_eq_value = prev_eq_value_gadget.expr(); let original_eq_prev = original_eq_prev_gadget.expr(); - // (value_prev != value) && (original_value != value) && (value == - // Word::from(0)) + // (value_prev != value) && (original_value != 0) && (value == + // 0) let delete_slot = not::expr(prev_eq_value.clone()) * not::expr(original_is_zero.clone()) * value_is_zero; // (value_prev != value) && (original_value == value) && (original_value != - // Word::from(0)) + // 0) let reset_existing = not::expr(prev_eq_value.clone()) * original_eq_value.clone() * not::expr(original_is_zero.clone()); // (value_prev != value) && (original_value == value) && (original_value == - // Word::from(0)) + // 0) let reset_inexistent = not::expr(prev_eq_value.clone()) * original_eq_value * (original_is_zero); // (value_prev != value) && (original_value != value_prev) && (value_prev == - // Word::from(0)) + // 0) let recreate_slot = not::expr(prev_eq_value) * not::expr(original_eq_prev) * (value_prev_is_zero); diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index d26cdb4e8e..f18f6ffda3 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -302,7 +302,8 @@ impl ExecutionState { OpcodeId::LOG3, OpcodeId::LOG4, ], - Self::CREATE => vec![OpcodeId::CREATE, OpcodeId::CREATE2], + Self::CREATE => vec![OpcodeId::CREATE], + Self::CREATE2 => vec![OpcodeId::CREATE2], Self::CALL_OP => vec![ OpcodeId::CALL, OpcodeId::CALLCODE, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs index 838cc585c2..fddc49ccec 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs @@ -1,8 +1,11 @@ use crate::{ - evm_circuit::util::{ - constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - math_gadget::*, - transpose_val_ret, CachedRegion, Cell, CellType, + evm_circuit::{ + param::MAX_N_BYTES_INTEGER, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + math_gadget::*, + transpose_val_ret, CachedRegion, Cell, CellType, + }, }, util::Expr, }; @@ -31,6 +34,7 @@ impl ConstantDivisionGadget { numerator: Expression, denominator: u64, ) -> Self { + assert!(N_BYTES * 8 + 64 - denominator.leading_zeros() as usize <= MAX_N_BYTES_INTEGER * 8); let quotient = cb.query_cell_with_type(CellType::storage_for_expr(&numerator)); let remainder = cb.query_cell_with_type(CellType::storage_for_expr(&numerator)); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs index ef5ca9afce..d8b4342b45 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs @@ -1,7 +1,10 @@ use crate::{ - evm_circuit::util::{ - constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, pow_of_two, transpose_val_ret, CachedRegion, Cell, + evm_circuit::{ + param::MAX_N_BYTES_INTEGER, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, pow_of_two, transpose_val_ret, CachedRegion, Cell, + }, }, util::Expr, }; @@ -34,6 +37,7 @@ impl LtGadget { lhs: Expression, rhs: Expression, ) -> Self { + assert!(N_BYTES <= MAX_N_BYTES_INTEGER); let lt = cb.query_bool(); let diff = cb.query_bytes(); let range = pow_of_two(N_BYTES * 8); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs index d4b65f20b0..bfff4b7e02 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs @@ -3,7 +3,7 @@ use crate::{ self, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::*, - sum, CachedRegion, + select, sum, CachedRegion, }, util::Expr, }; @@ -26,7 +26,6 @@ pub(crate) struct ModGadget { mul_add_words: MulAddWordsGadget, n_is_zero: IsZeroGadget, a_or_is_zero: IsZeroGadget, - eq: IsEqualGadget, lt: LtWordGadget, } impl ModGadget { @@ -37,13 +36,12 @@ impl ModGadget { let n_is_zero = IsZeroGadget::construct(cb, sum::expr(&n.cells)); let a_or_is_zero = IsZeroGadget::construct(cb, sum::expr(&a_or_zero.cells)); let mul_add_words = MulAddWordsGadget::construct(cb, [&k, n, r, &a_or_zero]); - let eq = IsEqualGadget::construct(cb, a.expr(), a_or_zero.expr()); let lt = LtWordGadget::construct(cb, r, n); // Constrain the aux variable a_or_zero to be =a or =0 if n==0: - // (a == a_or_zero) ^ (n == 0 & a_or_zero == 0) - cb.add_constraint( - " (1 - (a == a_or_zero)) * ( 1 - (n == 0) * (a_or_zero == 0)", - (1.expr() - eq.expr()) * (1.expr() - n_is_zero.expr() * a_or_is_zero.expr()), + cb.require_equal( + "a_or_zero == if n == 0 { 0 } else { a }", + a_or_zero.expr(), + select::expr(n_is_zero.expr(), 0.expr(), a.expr()), ); // Constrain the result r to be valid: (r ModGadget { mul_add_words, n_is_zero, a_or_is_zero, - eq, lt, } } @@ -89,13 +86,6 @@ impl ModGadget { self.mul_add_words .assign(region, offset, [k, n, r, a_or_zero])?; self.lt.assign(region, offset, r, n)?; - self.eq.assign_value( - region, - offset, - region.word_rlc(a), - region.word_rlc(a_or_zero), - )?; - Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs index 989011c68a..e9c796a5af 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs @@ -1,6 +1,9 @@ -use crate::evm_circuit::util::{ - constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, CachedRegion, Cell, +use crate::evm_circuit::{ + param::MAX_N_BYTES_INTEGER, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, CachedRegion, Cell, + }, }; use eth_types::Field; use halo2_proofs::{ @@ -17,6 +20,7 @@ pub struct RangeCheckGadget { impl RangeCheckGadget { pub(crate) fn construct(cb: &mut EVMConstraintBuilder, value: Expression) -> Self { + assert!(N_BYTES <= MAX_N_BYTES_INTEGER); let parts = cb.query_bytes(); // Require that the reconstructed value from the parts equals the diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 37be293a0e..c5b193b908 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -274,8 +274,8 @@ impl RwRow { } pub(crate) fn rlc(&self, randomness: F) -> F { let values = self.values(); - values - .iter() + std::iter::once(&F::one()) + .chain(values.iter()) .rev() .fold(F::zero(), |acc, value| acc * randomness + value) } From afa2c28453942c320db102a22fd4f8254979d36e Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 8 Jun 2023 19:19:48 -0400 Subject: [PATCH 17/41] [chore] update cargo lock --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8654070a0..1dbcf0aeba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -461,7 +461,7 @@ dependencies = [ [[package]] name = "bus-mapping" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "ethers-core", @@ -1448,7 +1448,7 @@ dependencies = [ [[package]] name = "eth-types" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "ethers-core", "ethers-signers", @@ -1755,7 +1755,7 @@ dependencies = [ [[package]] name = "external-tracer" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "geth-utils 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", @@ -2074,7 +2074,7 @@ dependencies = [ [[package]] name = "gadgets" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "digest 0.7.6", "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", @@ -2123,7 +2123,7 @@ dependencies = [ [[package]] name = "geth-utils" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "env_logger 0.9.3", "gobuild", @@ -2785,7 +2785,7 @@ dependencies = [ [[package]] name = "keccak256" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "env_logger 0.9.3", "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", @@ -3011,7 +3011,7 @@ dependencies = [ [[package]] name = "mock" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "ethers-core", @@ -3044,7 +3044,7 @@ dependencies = [ [[package]] name = "mpt-zktrie" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", @@ -5594,7 +5594,7 @@ dependencies = [ [[package]] name = "zkevm-circuits" version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#bc306aca32b7f439126bf8c131ba02dae0f856e2" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" dependencies = [ "array-init", "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", From 6defdc583ed4b1a88bf42a02836218431c58a385 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 8 Jun 2023 19:22:23 -0400 Subject: [PATCH 18/41] [chore] partial address comments --- aggregator/src/proof_aggregation/circuit.rs | 6 +++--- aggregator/src/tests/proof_aggregation.rs | 3 +-- aggregator/src/tests/proof_compression.rs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index 2114f42378..080b8e9dd1 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -196,14 +196,14 @@ impl Circuit for AggregationCircuit { // from previous accumualtors) // - new accumulator to be verified on chain // - let (assigned_aggreation_instances, acc) = aggregate::>( + let (assigned_aggregation_instances, acc) = aggregate::>( &self.svk, &loader, &self.snarks, self.as_proof(), ); log::trace!("aggregation circuit during assigning"); - for (i, e) in assigned_aggreation_instances[0].iter().enumerate() { + for (i, e) in assigned_aggregation_instances[0].iter().enumerate() { log::trace!("{}-th instance: {:?}", i, e.value) } @@ -214,7 +214,7 @@ impl Circuit for AggregationCircuit { // - 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 snark_inputs.extend( - assigned_aggreation_instances + assigned_aggregation_instances .iter() .flat_map(|instance_column| instance_column.iter().skip(12)), ); diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index ec5d02b964..aaec42825f 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -16,9 +16,8 @@ use super::mock_chunk::MockChunkCircuit; const CHUNKS_PER_BATCH: usize = 2; - // This test takes about 1 hour on CPU -#[ignore="it takes too much time"] +#[ignore = "it takes too much time"] #[test] fn test_aggregation_circuit() { let process_id = process::id(); diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index ae51494d43..784ea219af 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -103,7 +103,7 @@ fn test_proof_compression() { } // This test takes about 1 hour on CPU -#[ignore="it takes too much time"] +#[ignore = "it takes too much time"] #[test] fn test_two_layer_proof_compression() { let dir = format!("data/{}", process::id()); From 3ff1f029c5fe2e5420e423545fbb8dd555d18990 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 8 Jun 2023 20:58:46 -0400 Subject: [PATCH 19/41] [doc] update readme for aggregator --- aggregator/README.md | 53 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/aggregator/README.md b/aggregator/README.md index 4070c94161..91d7bca1bc 100644 --- a/aggregator/README.md +++ b/aggregator/README.md @@ -7,20 +7,53 @@ This repo does proof aggregations for zkEVM proofs. ## zkEVM circuit A zkEVM circuits generates a ZK proof for a chunk of blocks. It takes 64 field elements as its public input, consist of -- chunk's data hash digest -- chunk's public input hash digest -Each hash digest is decomposed into 32 bytes, and then casted as 32 field elements. +- chunk's data hash digest: each byte is encoded in an Fr element +- chunk's public input hash digest: each byte is encoded in an Fr element +The total size for a public input is 64 bytes, encoded in 64 Fr element For the ease of testing, this repo implements a `MockCircuit` which hash same public input APIs as a zkEVM circuit. -## Compression circuit -A compression circuit takes in a snark proof and generates a new (potentially small) snark proof. -It re-expose the same public inputs as the original circuit. +## First compression circuit +The first compression circuit takes in a fresh snark proof and generates a new (potentially small) snark proof. +The public inputs to the new snark proof consists of +- 12 elements from the accumulators + - an accumulator consists of 2 G1 elements, which are the left and right inputs to the pairing + - this is treated as 4 Fq elements, each decomposed into 3 limbs and encoded in Fr +- 64 elements from previous snark + - re-expose the same public inputs as the original snark + +The first compression circuit is configured [wide config file](./configs/compression_wide.config). + +## Second compression circuit + +The second compression circuit takes in a compressed snark proof and generates a new (potentially small) snark proof. +The public inputs to the new snark proof consists of +- 12 elements from the accumulators + - an accumulator consists of 2 G1 elements, which are the left and right inputs to the pairing + - this is treated as 4 Fq elements, each decomposed into 3 limbs and encoded in Fr + - accumulator from the previous snark is accumulated into the current accumulator +- 64 elements from previous snark + - skipping the first 12 elements which are previous accumulator, as they are already accumulated + - re-expose the rest 64 field elements as the public inputs + +The second compression circuit is configured [thin config file](./configs/compression_thin.config). ## Aggregation circuit -An aggregation circuit takes in a batch of proofs, each for a chunk of blocks. +An aggregation circuit takes in a batch of `k` proofs, each for a chunk of blocks. It generates a single proof asserting the validity of all the proofs. -It also performs public input aggregation, i.e., reducing the 64 public elements per proof into a fixed number of elements: -- 12 elements from accumulators + +It also performs public input aggregation, i.e., reducing the `64k` public elements into a fixed number of `144` elements: +- 12 elements from accumulators, which accumulates all the previous `k` accumulators from each snark - 132 elements from the hashes -See [public input aggregation](./src/proof_aggregation/public_input_aggregation.rs) for the details of public input aggregation. + - first_chunk_prev_state_root: 32 Field elements + - last_chunk_post_state_root: 32 Field elements + - last_chunk_withdraw_root: 32 Field elements + - batch_public_input_hash: 32 Field elements + - chain_id: 4 Field elements + +In addition, it attests that, for chunks indexed from `0` to `k-1`, +- batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) where chunk_i.data_hash is a public input to the i-th batch snark circuit +- chunk_pi_hash := keccak(chain_id || prev_state_root || post_state_root || withdraw_root || chunk_data_hash) where chunk_data_hash is a public input to the i-th batch snark circuit +- and the related field matches public input + +See [public input aggregation](./src/proof_aggregation/public_input_aggregation.rs) for the details of public input aggregation. \ No newline at end of file From ceb68e6c78c1223d235cc75a15cc4c2d1e37988d Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 15 Jun 2023 19:15:47 -0400 Subject: [PATCH 20/41] [fix] fix test configs and clean up --- aggregator/configs/aggregation.config | 2 +- aggregator/configs/compression_thin.config | 2 +- .../public_input_aggregation/circuit.rs | 1 + aggregator/src/tests.rs | 193 ++++++++++++ aggregator/src/tests/aggregator_e2e.rs | 86 ++++++ aggregator/src/tests/proof_aggregation.rs | 165 +++------- aggregator/src/tests/proof_compression.rs | 292 ++---------------- aggregator/tests.sh | 8 +- 8 files changed, 361 insertions(+), 388 deletions(-) create mode 100644 aggregator/src/tests/aggregator_e2e.rs diff --git a/aggregator/configs/aggregation.config b/aggregator/configs/aggregation.config index 87f31b575d..019ada0f6a 100644 --- a/aggregator/configs/aggregation.config +++ b/aggregator/configs/aggregation.config @@ -1 +1 @@ -{"strategy":"Simple","degree":23,"num_advice":[30],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":26,"num_advice":[4],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} diff --git a/aggregator/configs/compression_thin.config b/aggregator/configs/compression_thin.config index 4fc86986d9..57559be7ef 100644 --- a/aggregator/configs/compression_thin.config +++ b/aggregator/configs/compression_thin.config @@ -1 +1 @@ -{"strategy":"Simple","degree":23,"num_advice":[3],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} \ No newline at end of file +{"strategy":"Simple","degree":26,"num_advice":[1],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3}e \ No newline at end of file diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs index 1e3f51b8aa..dee651b31d 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs @@ -137,6 +137,7 @@ impl Circuit for BatchHashCircuit { fn without_witnesses(&self) -> Self { Self::default() } + fn configure(meta: &mut ConstraintSystem) -> Self::Config { let challenges = Challenges::construct(meta); let challenges_exprs = challenges.exprs(meta); diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index 45e43e6ab5..2221ffd997 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -1,4 +1,197 @@ +pub(crate) mod aggregator_e2e; pub(crate) mod mock_chunk; pub(crate) mod proof_aggregation; pub(crate) mod proof_compression; pub(crate) mod public_input_aggregation; + +#[macro_export] +macro_rules! layer_0 { + // generate a snark for layer 0 + ($circuit: ident, $circuit_type: ident, $param: ident, $degree: ident, $path: ident) => {{ + let timer = start_timer!(|| "gen layer 0 snark"); + + let mut rng = test_rng(); + let param = { + let mut param = $param.clone(); + param.downsize($degree); + param + }; + + let pk = gen_pk( + ¶m, &$circuit, None, + // Some(&$path.join(Path::new("layer_0.pkey"))), + ); + log::trace!("finished layer 0 pk generation for circuit"); + + let snark = gen_snark_shplonk( + ¶m, + &pk, + $circuit.clone(), + &mut rng, + None::, + // Some(&$path.join(Path::new("layer_0.snark"))), + ); + log::trace!("finished layer 0 snark generation for circuit"); + + assert!(verify_snark_shplonk::<$circuit_type>( + ¶m, + snark.clone(), + pk.get_vk() + )); + + log::trace!("finished layer 0 snark verification"); + log::trace!("proof size: {}", snark.proof.len()); + log::trace!( + "pi size: {}", + snark.instances.iter().map(|x| x.len()).sum::() + ); + + log::trace!("layer 0 circuit instances"); + for (i, e) in $circuit.instances()[0].iter().enumerate() { + log::trace!("{}-th public input: {:?}", i, e); + } + end_timer!(timer); + snark + }}; +} + +#[macro_export] +macro_rules! compression_layer_snark { + // generate a snark for compression layer + ($previous_snark: ident, $param: ident, $degree: ident, $path: ident, $layer_index: expr) => {{ + let timer = start_timer!(|| format!("gen layer {} snark", $layer_index)); + + let param = { + let mut param = $param.clone(); + param.downsize($degree); + param + }; + + let mut rng = test_rng(); + + let is_fresh = if $layer_index == 1 { true } else { false }; + let compression_circuit = + CompressionCircuit::new(&$param, $previous_snark.clone(), is_fresh, &mut rng); + + let pk = gen_pk(&$param, &compression_circuit, None); + // build the snark for next layer + let snark = gen_snark_shplonk( + ¶m, + &pk, + compression_circuit.clone(), + &mut rng, + None::, // Some(&$path.join(Path::new("layer_1.snark"))), + ); + log::trace!( + "finished layer {} snark generation for circuit", + $layer_index + ); + + assert!(verify_snark_shplonk::( + ¶m, + snark.clone(), + pk.get_vk() + )); + + end_timer!(timer); + snark + }}; +} + +#[macro_export] +macro_rules! compression_layer_evm { + // generate a evm proof and verify it for compression layer + ($previous_snark: ident, $param: ident, $degree: ident, $path: ident,$layer_index: expr) => {{ + let timer = start_timer!(|| format!("gen layer {} snark", $layer_index)); + + let param = { + let mut param = $param.clone(); + param.downsize($degree); + param + }; + + let mut rng = test_rng(); + + let compression_circuit = + CompressionCircuit::new(&$param, $previous_snark, false, &mut rng); + + let instances = compression_circuit.instances(); + + let pk = gen_pk(&$param, &compression_circuit, None); + // build the snark for next layer + let proof = gen_evm_proof_shplonk( + ¶m, + &pk, + compression_circuit.clone(), + instances.clone(), + &mut rng, + ); + + log::trace!("finished layer 4 aggregation generation"); + log::trace!("proof size: {}", proof.len()); + + // verify proof via EVM + let deployment_code = gen_evm_verifier::>( + ¶m, + pk.get_vk(), + compression_circuit.num_instance(), + Some(&$path.join(Path::new("contract.sol"))), + ); + log::trace!("finished layer 4 bytecode generation"); + + evm_verify( + deployment_code, + compression_circuit.instances(), + proof.clone(), + ); + log::trace!("layer 2 evm verification finished"); + + end_timer!(timer); + }}; +} + +#[macro_export] +macro_rules! aggregation_layer_snark { + // generate a snark for compression layer + ($previous_snarks: ident, $param: ident, $degree: ident, $path: ident, $layer_index: expr, $chunks: ident) => {{ + let timer = start_timer!(|| format!("gen layer {} snark", $layer_index)); + + let param = { + let mut param = $param.clone(); + param.downsize($degree); + param + }; + + let mut rng = test_rng(); + + let aggregation_circuit = AggregationCircuit::new( + &$param, + $previous_snarks.as_ref(), + &mut rng, + $chunks.as_ref(), + ); + + let pk = gen_pk(&$param, &aggregation_circuit, None); + // build the snark for next layer + let snark = gen_snark_shplonk( + ¶m, + &pk, + aggregation_circuit.clone(), + &mut rng, + None::, // Some(&$path.join(Path::new("layer_3.snark"))), + ); + log::trace!( + "finished layer {} snark generation for circuit", + $layer_index + ); + + assert!(verify_snark_shplonk::( + ¶m, + snark.clone(), + pk.get_vk() + )); + + end_timer!(timer); + snark + }}; +} diff --git a/aggregator/src/tests/aggregator_e2e.rs b/aggregator/src/tests/aggregator_e2e.rs new file mode 100644 index 0000000000..a9bf1bd8a7 --- /dev/null +++ b/aggregator/src/tests/aggregator_e2e.rs @@ -0,0 +1,86 @@ +use std::{fs, path::Path, process}; + +use ark_std::{end_timer, start_timer, test_rng}; +use halo2_proofs::{halo2curves::bn256::Bn256, poly::commitment::Params}; +use itertools::Itertools; +use snark_verifier::{ + loader::halo2::halo2_ecc::halo2_base::{halo2_proofs, utils::fs::gen_srs}, + pcs::kzg::{Bdfg21, Kzg}, +}; +use snark_verifier_sdk::{ + evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier}, + gen_pk, + halo2::{gen_snark_shplonk, verify_snark_shplonk}, + CircuitExt, +}; + +use crate::{ + aggregation_layer_snark, compression_layer_evm, compression_layer_snark, layer_0, + tests::mock_chunk::MockChunkCircuit, AggregationCircuit, ChunkHash, CompressionCircuit, +}; + +const CHUNKS_PER_BATCH: usize = 2; + +// This test takes about 1 hour on CPU +#[ignore = "it takes too much time"] +#[test] +fn test_e2e() { + env_logger::init(); + + let dir = format!("data/{}", process::id()); + let path = Path::new(dir.as_str()); + fs::create_dir(path).unwrap(); + + // inner circuit: Mock circuit + let k0 = 19; + // wide compression + let k1 = 21; + // thin compression + let k2 = 26; + // aggregation + let k3 = 26; + // thin compression + let k4 = 26; + + let mut rng = test_rng(); + let params = gen_srs(k4); + + let mut chunks = (0..CHUNKS_PER_BATCH) + .map(|_| ChunkHash::mock_chunk_hash(&mut rng)) + .collect_vec(); + for i in 0..CHUNKS_PER_BATCH - 1 { + chunks[i + 1].prev_state_root = chunks[i].post_state_root; + } + + // Proof for test circuit + let circuits = chunks + .iter() + .map(|&chunk| MockChunkCircuit { chunk }) + .collect_vec(); + let layer_0_snarks = circuits + .iter() + .map(|&circuit| layer_0!(circuit, MockChunkCircuit, params, k0, path)) + .collect_vec(); + + // Layer 1 proof compression + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + let layer_1_snarks = layer_0_snarks + .iter() + .map(|layer_0_snark| compression_layer_snark!(layer_0_snark, params, k1, path, 1)) + .collect_vec(); + + // Layer 2 proof compression + std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); + let layer_2_snarks = layer_1_snarks + .iter() + .map(|layer_1_snark| compression_layer_snark!(layer_1_snark, params, k2, path, 2)) + .collect_vec(); + + // layer 3 proof aggregation + std::env::set_var("VERIFY_CONFIG", "./configs/aggregation.config"); + let layer_3_snark = aggregation_layer_snark!(layer_2_snarks, params, k3, path, 3, chunks); + + // layer 4 proof compression and final evm verification + std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); + compression_layer_evm!(layer_3_snark, params, k4, path, 4); +} diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index aaec42825f..85d77e53cd 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -1,6 +1,6 @@ use std::{fs, path::Path, process}; -use ark_std::test_rng; +use ark_std::{end_timer, start_timer, test_rng}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr, poly::commitment::Params}; use itertools::Itertools; use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; @@ -10,7 +10,7 @@ use snark_verifier_sdk::{ CircuitExt, }; -use crate::{AggregationCircuit, ChunkHash, CompressionCircuit}; +use crate::{AggregationCircuit, ChunkHash, CompressionCircuit, compression_layer_snark, layer_0}; use super::mock_chunk::MockChunkCircuit; @@ -26,132 +26,67 @@ fn test_aggregation_circuit() { let path = Path::new(dir.as_str()); fs::create_dir(path).unwrap(); + // inner circuit: Mock circuit let k0 = 19; - let k1 = 25; - let k2 = 25; - let layer_2_params = gen_srs(k2); + // wide compression + let k1 = 26; + // thin compression + let k2 = 26; + // aggregation + let k3 = 26; + let mut rng = test_rng(); + let params = gen_srs(k2); + let mut chunks = (0..CHUNKS_PER_BATCH) .map(|_| ChunkHash::mock_chunk_hash(&mut rng)) .collect_vec(); for i in 0..CHUNKS_PER_BATCH - 1 { chunks[i + 1].prev_state_root = chunks[i].post_state_root; } + // Proof for test circuit + let circuits = chunks + .iter() + .map(|&chunk| MockChunkCircuit { chunk }) + .collect_vec(); + let layer_0_snarks = circuits + .iter() + .map(|&circuit| layer_0!(circuit, MockChunkCircuit, params, k0, path)) + .collect_vec(); - // build layer 0 snarks - let layer_0_snarks = { - let layer_0_params = { - let mut params = layer_2_params.clone(); - params.downsize(k0); - params - }; - - let circuits = chunks - .iter() - .map(|&chunk| MockChunkCircuit { chunk }) - .collect_vec(); - log::trace!("finished layer 0 pk generation for circuit"); - let layer_0_pk = gen_pk( - &layer_0_params, - &circuits[0], - Some(&path.join(Path::new("layer_0.pkey"))), - ); - log::trace!("finished layer 0 pk generation for circuit"); - - let layer_0_snarks = circuits - .iter() - .enumerate() - .map(|(i, circuit)| { - let snark = gen_snark_shplonk( - &layer_0_params, - &layer_0_pk, - *circuit, - &mut rng, - Some(&path.join(Path::new(format!("layer_0_{}.snark", i).as_str()))), - ); - log::trace!("finished {}-th snark", i); - snark - }) - .collect_vec(); - log::trace!("finished layer 0 snark generation for circuit"); - - // sanity checks - layer_0_snarks.iter().for_each(|snark| { - assert!(verify_snark_shplonk::( - &layer_0_params, - snark.clone(), - layer_0_pk.get_vk() - )) - }); - log::trace!("finished layer 0 snark verification"); - - layer_0_snarks - }; - - // build layer 1 the compression circuit - let layer_1_snarks = { - std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); - - let layer_1_params = { - let mut params = layer_2_params.clone(); - params.downsize(k1); - params - }; - - let compression_circuit = - CompressionCircuit::new(&layer_1_params, layer_0_snarks[0].clone(), true, &mut rng); - - let layer_1_pk = gen_pk(&layer_1_params, &compression_circuit, None); - - log::trace!("finished layer 1 pk gen"); - let mut layer_1_snarks = vec![]; - - for (i, snark) in layer_0_snarks.iter().enumerate() { - let compression_circuit = - CompressionCircuit::new(&layer_1_params, snark.clone(), true, &mut rng); - - let layer_1_snark = gen_snark_shplonk( - &layer_1_params, - &layer_1_pk.clone(), - compression_circuit.clone(), - &mut rng, - Some(&path.join(Path::new(format!("layer_1_{}.snark", i).as_str()))), - ); - - log::trace!("finished layer 1 {}-th snark gen", i); - - log::trace!("{}-th compression circuit instance:", i); - for (i, e) in compression_circuit.instances()[0].iter().enumerate() { - log::trace!("{}-th {:?}", i, e,) - } + // Layer 1 proof compression + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + let layer_1_snarks = layer_0_snarks + .iter() + .map(|layer_0_snark| compression_layer_snark!(layer_0_snark, params, k1, path, 1)) + .collect_vec(); - layer_1_snarks.push(layer_1_snark) - } - layer_1_snarks - }; + // Layer 2 proof compression + std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); + let layer_2_snarks = layer_1_snarks + .iter() + .map(|layer_1_snark| compression_layer_snark!(layer_1_snark, params, k2, path, 2)) + .collect_vec(); - // build layer 2 the aggregation circuit + // layer 3 proof aggregation { - std::env::set_var("VERIFY_CONFIG", "./configs/aggregation.config"); - log::trace!("aggregation circuit"); - - let aggregation_circuit = - AggregationCircuit::new(&layer_2_params, &layer_1_snarks, rng, &chunks); - log::trace!("snark"); - for (i, snark) in aggregation_circuit.snarks.iter().enumerate() { - log::trace!("{:?} {:?}", i, snark.instances); - } - log::trace!("flattened instance"); - for (i, pi) in aggregation_circuit.flattened_instances.iter().enumerate() { - log::trace!("{:?} {:?}", i, pi); - } - - let instances = aggregation_circuit.instances(); - - log::trace!("start mock proving"); - let mock_prover = MockProver::::run(k1, &aggregation_circuit, instances).unwrap(); + let param = { + let mut param = params; + param.downsize(k3); + param + }; + let aggregation_circuit = AggregationCircuit::new( + ¶m, + &layer_2_snarks, + &mut rng, + chunks.as_ref(), + ); + let instance = aggregation_circuit.instances(); - mock_prover.assert_satisfied_par(); + let mock_prover = MockProver::::run(k3, &aggregation_circuit, instance).unwrap(); + + mock_prover.assert_satisfied_par() } + } diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index 784ea219af..5e68daf4d0 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -1,21 +1,10 @@ use std::{fs, path::Path, process}; -use ark_std::test_rng; -use halo2_proofs::{ - dev::MockProver, - halo2curves::bn256::{Bn256, Fr, G1Affine}, - plonk::verify_proof, - poly::{ - commitment::Params, - kzg::{multiopen::VerifierSHPLONK, strategy::AccumulatorStrategy}, - VerificationStrategy, - }, - transcript::TranscriptReadBuffer, -}; +use ark_std::{end_timer, start_timer, test_rng}; +use halo2_proofs::{halo2curves::bn256::Bn256, poly::commitment::Params}; use snark_verifier::{ loader::halo2::halo2_ecc::halo2_base::{halo2_proofs, utils::fs::gen_srs}, pcs::kzg::{Bdfg21, Kzg}, - system::halo2::transcript::evm::EvmTranscript, }; use snark_verifier_sdk::{ evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier}, @@ -24,291 +13,56 @@ use snark_verifier_sdk::{ CircuitExt, }; -use crate::{tests::mock_chunk::MockChunkCircuit, CompressionCircuit}; +use crate::{ + compression_layer_evm, compression_layer_snark, layer_0, tests::mock_chunk::MockChunkCircuit, + CompressionCircuit, +}; #[test] fn test_proof_compression() { + env_logger::init(); + let dir = format!("data/{}", process::id()); let path = Path::new(dir.as_str()); fs::create_dir(path).unwrap(); let k0 = 19; - let k1 = 23; + let k1 = 25; let mut rng = test_rng(); let layer_1_params = gen_srs(k1); // Proof for test circuit - let layer_0_snark = { - let layer_0_params = { - let mut params = layer_1_params.clone(); - params.downsize(k0); - params - }; - - let circuit = MockChunkCircuit::random(&mut rng); - let layer_0_pk = gen_pk( - &layer_0_params, - &circuit, - Some(&path.join(Path::new("layer_0.pkey"))), - ); - log::trace!("finished layer 0 pk generation for circuit"); - - let layer_0_snark = gen_snark_shplonk( - &layer_0_params, - &layer_0_pk, - circuit, - &mut rng, - Some(&path.join(Path::new("layer_0.snark"))), - ); - log::trace!("finished layer 0 snark generation for circuit"); - - assert!(verify_snark_shplonk::( - &layer_0_params, - layer_0_snark.clone(), - layer_0_pk.get_vk() - )); - - log::trace!("finished layer 0 snark verification"); - log::trace!("proof size: {}", layer_0_snark.proof.len()); - log::trace!( - "pi size: {}", - layer_0_snark - .instances - .iter() - .map(|x| x.len()) - .sum::() - ); - - log::trace!("layer 0 circuit instances"); - for (i, e) in circuit.instances()[0].iter().enumerate() { - log::trace!("{}-th public input: {:?}", i, e); - } - - layer_0_snark - }; - - // Layer 1 proof compression - { - std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + let circuit = MockChunkCircuit::random(&mut rng); + let layer_0_snark = layer_0!(circuit, MockChunkCircuit, layer_1_params, k0, path); - let compression_circuit = - CompressionCircuit::new(&layer_1_params, layer_0_snark, true, &mut rng); - let instances = compression_circuit.instances(); - - let mock_prover = MockProver::::run(k1, &compression_circuit, instances).unwrap(); - - mock_prover.assert_satisfied_par(); - } + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + compression_layer_evm!(layer_0_snark, layer_1_params, k1, path, 1) } // This test takes about 1 hour on CPU #[ignore = "it takes too much time"] #[test] fn test_two_layer_proof_compression() { + env_logger::init(); + let dir = format!("data/{}", process::id()); let path = Path::new(dir.as_str()); fs::create_dir(path).unwrap(); let k0 = 19; - let k1 = 23; - let k2 = 23; + let k1 = 25; + let k2 = 25; let mut rng = test_rng(); let layer_2_params = gen_srs(k2); - // Proof for test circuit - let layer_0_snark = { - let layer_0_params = { - let mut params = layer_2_params.clone(); - params.downsize(k0); - params - }; - - let circuit = MockChunkCircuit::random(&mut rng); - let layer_0_pk = gen_pk( - &layer_0_params, - &circuit, - Some(&path.join(Path::new("layer_0.pkey"))), - ); - log::trace!("finished layer 0 pk generation for circuit"); - - let layer_0_snark = gen_snark_shplonk( - &layer_0_params, - &layer_0_pk, - circuit, - &mut rng, - Some(&path.join(Path::new("layer_0.snark"))), - ); - log::trace!("finished layer 0 snark generation for circuit"); - - assert!(verify_snark_shplonk::( - &layer_0_params, - layer_0_snark.clone(), - layer_0_pk.get_vk() - )); - - log::trace!("finished layer 0 snark verification"); - log::trace!("proof size: {}", layer_0_snark.proof.len()); - log::trace!( - "pi size: {}", - layer_0_snark - .instances - .iter() - .map(|x| x.len()) - .sum::() - ); - - log::trace!("layer 0 circuit instances"); - for (i, e) in circuit.instances()[0].iter().enumerate() { - log::trace!("{}-th public input: {:?}", i, e); - } - - layer_0_snark - }; - - // Layer 1 proof compression - let layer_1_snark = { - std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); - - let layer_1_params = { - let mut params = layer_2_params.clone(); - params.downsize(k1); - params - }; - - let compression_circuit = - CompressionCircuit::new(&layer_1_params, layer_0_snark, true, &mut rng); - let instances = compression_circuit.instances(); - - log::trace!("layer 1 circuit instances"); - for (i, e) in compression_circuit.instances()[0].iter().enumerate() { - log::trace!("{}-th public input: {:?}", i, e); - } - - let layer_1_pk = gen_pk( - &layer_1_params, - &compression_circuit, - Some(&path.join(Path::new("layer_1.pkey"))), - ); - log::trace!("finished layer 1 pk generation"); - - let layer_1_proof = gen_evm_proof_shplonk( - &layer_1_params, - &layer_1_pk, - compression_circuit.clone(), - instances.clone(), - &mut rng, - ); - - log::trace!("finished layer 1 aggregation generation"); - log::trace!("proof size: {}", layer_1_proof.len()); - log::trace!("pi size: {:?}", compression_circuit.num_instance()); - - { - let mut transcript = - TranscriptReadBuffer::<_, G1Affine, _>::init(layer_1_proof.as_slice()); - let instances = instances - .iter() - .map(|instances| instances.as_slice()) - .collect::>(); - - let res = VerificationStrategy::<_, VerifierSHPLONK<_>>::finalize( - verify_proof::<_, VerifierSHPLONK<_>, _, EvmTranscript<_, _, _, _>, _>( - &layer_1_params, - layer_1_pk.get_vk(), - AccumulatorStrategy::new(&layer_1_params), - &[instances.as_slice()], - &mut transcript, - ) - .unwrap(), - ); - log::trace!("sanity check layer 1 proof: {}", res); - } - - // verify proof via EVM - let deployment_code = gen_evm_verifier::>( - &layer_1_params, - layer_1_pk.get_vk(), - compression_circuit.num_instance(), - Some(&path.join(Path::new("layer_1.sol"))), - ); - log::trace!("finished layer 1 bytecode generation"); - - evm_verify( - deployment_code, - compression_circuit.instances(), - layer_1_proof, - ); - log::trace!("layer 1 evm verification finished"); - - // build the snark for next layer - let layer_1_snark = gen_snark_shplonk( - &layer_1_params, - &layer_1_pk, - compression_circuit, - &mut rng, - Some(&path.join(Path::new("layer_1.snark"))), - ); - log::trace!("finished layer 1 snark generation for circuit"); - - assert!(verify_snark_shplonk::( - &layer_1_params, - layer_1_snark.clone(), - layer_1_pk.get_vk() - )); - layer_1_snark - }; - - // Layer 2 proof compression - { - std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); - - let compression_circuit = - CompressionCircuit::new(&layer_2_params, layer_1_snark, false, &mut rng); - - let instances = compression_circuit.instances(); - let mock_prover = - MockProver::::run(k2, &compression_circuit, instances.clone()).unwrap(); - - mock_prover.assert_satisfied_par(); - - log::trace!("layer 2 circuit instances"); - for (i, e) in compression_circuit.instances()[0].iter().enumerate() { - log::trace!("{}-th public input: {:?}", i, e); - } - let layer_2_pk = gen_pk( - &layer_2_params, - &compression_circuit, - Some(&path.join(Path::new("layer_2.pkey"))), - ); - log::trace!("finished layer 2 pk generation"); - - let layer_2_proof = gen_evm_proof_shplonk( - &layer_2_params, - &layer_2_pk, - compression_circuit.clone(), - instances, - &mut rng, - ); - - log::trace!("finished layer 2 aggregation generation"); - log::trace!("proof size: {}", layer_2_proof.len()); + let circuit = MockChunkCircuit::random(&mut rng); + let layer_0_snark = layer_0!(circuit, MockChunkCircuit, layer_2_params, k0, path); - // verify proof via EVM - let deployment_code = gen_evm_verifier::>( - &layer_2_params, - layer_2_pk.get_vk(), - compression_circuit.num_instance(), - Some(&path.join(Path::new("layer_2.sol"))), - ); - log::trace!("finished layer 2 bytecode generation"); + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + let layer_1_snark = compression_layer_snark!(layer_0_snark, layer_2_params, k1, path, 1); - evm_verify( - deployment_code, - compression_circuit.instances(), - layer_2_proof, - ); - log::trace!("layer 2 evm verification finished"); - } + std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); + compression_layer_evm!(layer_1_snark, layer_2_params, k2, path, 2); } diff --git a/aggregator/tests.sh b/aggregator/tests.sh index 48bb03ad24..89d1f74a6a 100644 --- a/aggregator/tests.sh +++ b/aggregator/tests.sh @@ -2,5 +2,9 @@ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_real_prover -- --nocapture 2>&1 | tee pi_real.log RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_proof_compression -- --nocapture 2>&1 | tee compression.log -RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --nocapture 2>&1 | tee compression2.log -RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_aggregation_circuit -- --nocapture 2>&1 | tee aggregation.log + + +# the following 3 tests takes super long time +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --ignored --nocapture 2>&1 | tee compression2.log +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_aggregation_circuit -- --ignored --nocapture 2>&1 | tee aggregation.log +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_e2e -- --ignored --nocapture 2>&1 | tee aggregation.log From 87ecfa9167b83e558f3177b6777ab6edf6434746 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 15 Jun 2023 22:15:34 -0400 Subject: [PATCH 21/41] [chore] clean up --- aggregator/configs/compression_thin.config | 2 +- aggregator/src/tests/mock_chunk.rs | 55 +--------------------- aggregator/src/tests/proof_compression.rs | 2 +- 3 files changed, 4 insertions(+), 55 deletions(-) diff --git a/aggregator/configs/compression_thin.config b/aggregator/configs/compression_thin.config index 57559be7ef..6975f69170 100644 --- a/aggregator/configs/compression_thin.config +++ b/aggregator/configs/compression_thin.config @@ -1 +1 @@ -{"strategy":"Simple","degree":26,"num_advice":[1],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3}e \ No newline at end of file +{"strategy":"Simple","degree":26,"num_advice":[1],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} \ No newline at end of file diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index 269318f717..7b57b9ce59 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -1,16 +1,9 @@ -use ark_std::{end_timer, start_timer, test_rng}; +use ark_std::{ test_rng}; use halo2_proofs::{ dev::MockProver, - halo2curves::bn256::{Bn256, Fr}, -}; -use snark_verifier::{ - loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs, - pcs::kzg::{Bdfg21, Kzg}, + halo2curves::bn256::{ Fr}, }; use snark_verifier_sdk::{ - evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier}, - gen_pk, - halo2::{gen_snark_shplonk, verify_snark_shplonk}, CircuitExt, }; @@ -37,54 +30,10 @@ fn test_mock_chunk_prover() { let mut rng = test_rng(); - let param = gen_srs(LOG_DEGREE); let circuit = MockChunkCircuit::random(&mut rng); let instance = circuit.instances(); let mock_prover = MockProver::::run(LOG_DEGREE, &circuit, instance).unwrap(); mock_prover.assert_satisfied_par(); - - let timer = start_timer!(|| format!("key generation for k = {}", LOG_DEGREE)); - let pk = gen_pk(¶m, &circuit, None); - end_timer!(timer); - - let timer = start_timer!(|| "proving"); - let snark = gen_snark_shplonk(¶m, &pk, circuit, &mut rng, None::); - end_timer!(timer); - - log::trace!("{:?}", circuit.chunk.data_hash); - log::trace!("{:?}", circuit.chunk.public_input_hash()); - for (i, e) in snark.instances[0].iter().enumerate() { - log::trace!("{}-th: {:?}", i, e); - } - - let timer = start_timer!(|| "verifying"); - assert!(verify_snark_shplonk::( - ¶m, - snark.clone(), - pk.get_vk() - )); - end_timer!(timer); - - let proof = gen_evm_proof_shplonk( - ¶m, - &pk, - circuit.clone(), - snark.instances.clone(), - &mut rng, - ); - log::trace!("proof size: {}", proof.len()); - - // verify proof via EVM - let deployment_code = gen_evm_verifier::>( - ¶m, - pk.get_vk(), - circuit.num_instance(), - None, - ); - log::trace!("finished layer 2 bytecode generation"); - - evm_verify(deployment_code, circuit.instances(), proof.clone()); - log::trace!("layer 2 evm verification finished"); } diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index 5e68daf4d0..dfd44f36df 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -36,7 +36,7 @@ fn test_proof_compression() { let circuit = MockChunkCircuit::random(&mut rng); let layer_0_snark = layer_0!(circuit, MockChunkCircuit, layer_1_params, k0, path); - std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); compression_layer_evm!(layer_0_snark, layer_1_params, k1, path, 1) } From 2394703dc4aba7bd27523e1b7509df5eb02281d2 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 15 Jun 2023 22:18:13 -0400 Subject: [PATCH 22/41] [chore] update cargo toml --- Cargo.lock | 242 ++++++------------------------------------ aggregator/Cargo.toml | 4 +- 2 files changed, 37 insertions(+), 209 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1dbcf0aeba..faf29c8b6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ version = "0.1.0" dependencies = [ "ark-std 0.4.0", "env_logger 0.10.0", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "eth-types", "ethers-core", "halo2_proofs", "itertools", @@ -36,7 +36,7 @@ dependencies = [ "serde_json", "snark-verifier", "snark-verifier-sdk", - "zkevm-circuits 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "zkevm-circuits", ] [[package]] @@ -433,18 +433,18 @@ version = "0.1.0" dependencies = [ "ctor", "env_logger 0.9.3", - "eth-types 0.1.0", + "eth-types", "ethers-core", "ethers-providers", "ethers-signers", - "gadgets 0.1.0", + "gadgets", "halo2_proofs", "hex", "itertools", - "keccak256 0.1.0", + "keccak256", "lazy_static", "log", - "mock 0.1.0", + "mock", "once_cell", "poseidon-circuit", "pretty_assertions", @@ -458,33 +458,6 @@ dependencies = [ "url", ] -[[package]] -name = "bus-mapping" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "ethers-core", - "ethers-providers", - "ethers-signers", - "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2_proofs", - "hex", - "itertools", - "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "lazy_static", - "log", - "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "once_cell", - "poseidon-circuit", - "rand", - "revm-precompile", - "serde", - "serde_json", - "strum", - "strum_macros", -] - [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -599,22 +572,22 @@ name = "circuit-benchmarks" version = "0.1.0" dependencies = [ "ark-std 0.3.0", - "bus-mapping 0.1.0", + "bus-mapping", "env_logger 0.9.3", - "eth-types 0.1.0", + "eth-types", "ethers", "ethers-signers", "halo2_proofs", "itertools", - "keccak256 0.1.0", + "keccak256", "log", - "mock 0.1.0", + "mock", "rand", "rand_chacha", "rand_xorshift", "tokio", "url", - "zkevm-circuits 0.1.0", + "zkevm-circuits", ] [[package]] @@ -1445,32 +1418,6 @@ dependencies = [ "uint", ] -[[package]] -name = "eth-types" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "ethers-core", - "ethers-signers", - "halo2_proofs", - "hex", - "itertools", - "lazy_static", - "libsecp256k1", - "num", - "num-bigint", - "poseidon-circuit", - "regex", - "serde", - "serde_json", - "serde_with", - "sha3 0.10.8", - "strum", - "strum_macros", - "subtle", - "uint", -] - [[package]] name = "ethabi" version = "17.2.0" @@ -1745,20 +1692,8 @@ dependencies = [ name = "external-tracer" version = "0.1.0" dependencies = [ - "eth-types 0.1.0", - "geth-utils 0.1.0", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "external-tracer" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "geth-utils 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "eth-types", + "geth-utils", "log", "serde", "serde_json", @@ -2063,7 +1998,7 @@ name = "gadgets" version = "0.1.0" dependencies = [ "digest 0.7.6", - "eth-types 0.1.0", + "eth-types", "halo2_proofs", "rand", "rand_xorshift", @@ -2071,18 +2006,6 @@ dependencies = [ "strum", ] -[[package]] -name = "gadgets" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "digest 0.7.6", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2_proofs", - "sha3 0.7.3", - "strum", -] - [[package]] name = "generic-array" version = "0.9.1" @@ -2120,16 +2043,6 @@ dependencies = [ "log", ] -[[package]] -name = "geth-utils" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "env_logger 0.9.3", - "gobuild", - "log", -] - [[package]] name = "getrandom" version = "0.2.10" @@ -2662,15 +2575,15 @@ dependencies = [ name = "integration-tests" version = "0.1.0" dependencies = [ - "bus-mapping 0.1.0", + "bus-mapping", "env_logger 0.9.3", - "eth-types 0.1.0", + "eth-types", "ethers", "halo2_proofs", "hex", "lazy_static", "log", - "mock 0.1.0", + "mock", "paste", "pretty_assertions", "rand_chacha", @@ -2681,7 +2594,7 @@ dependencies = [ "strum", "tokio", "url", - "zkevm-circuits 0.1.0", + "zkevm-circuits", ] [[package]] @@ -2770,7 +2683,7 @@ name = "keccak256" version = "0.1.0" dependencies = [ "env_logger 0.9.3", - "eth-types 0.1.0", + "eth-types", "halo2_proofs", "itertools", "lazy_static", @@ -2782,21 +2695,6 @@ dependencies = [ "rand", ] -[[package]] -name = "keccak256" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "env_logger 0.9.3", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2_proofs", - "itertools", - "lazy_static", - "log", - "num-bigint", - "num-traits", -] - [[package]] name = "lalrpop" version = "0.19.12" @@ -2998,25 +2896,10 @@ dependencies = [ name = "mock" version = "0.1.0" dependencies = [ - "eth-types 0.1.0", - "ethers-core", - "ethers-signers", - "external-tracer 0.1.0", - "itertools", - "lazy_static", - "rand", - "rand_chacha", -] - -[[package]] -name = "mock" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "eth-types", "ethers-core", "ethers-signers", - "external-tracer 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "external-tracer", "itertools", "lazy_static", "rand", @@ -3027,9 +2910,9 @@ dependencies = [ name = "mpt-zktrie" version = "0.1.0" dependencies = [ - "bus-mapping 0.1.0", + "bus-mapping", "env_logger 0.9.3", - "eth-types 0.1.0", + "eth-types", "halo2-mpt-circuits", "halo2_proofs", "hex", @@ -3041,22 +2924,6 @@ dependencies = [ "zktrie", ] -[[package]] -name = "mpt-zktrie" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2-mpt-circuits", - "halo2_proofs", - "hex", - "lazy_static", - "log", - "num-bigint", - "zktrie", -] - [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -4799,21 +4666,21 @@ name = "testool" version = "0.1.0" dependencies = [ "anyhow", - "bus-mapping 0.1.0", + "bus-mapping", "clap 3.2.25", "ctor", "env_logger 0.9.3", - "eth-types 0.1.0", + "eth-types", "ethers-core", "ethers-signers", - "external-tracer 0.1.0", + "external-tracer", "glob", "halo2_proofs", "handlebars", "hex", - "keccak256 0.1.0", + "keccak256", "log", - "mock 0.1.0", + "mock", "once_cell", "prettytable-rs", "rand", @@ -4828,7 +4695,7 @@ dependencies = [ "toml", "urlencoding", "yaml-rust", - "zkevm-circuits 0.1.0", + "zkevm-circuits", ] [[package]] @@ -5552,27 +5419,27 @@ name = "zkevm-circuits" version = "0.1.0" dependencies = [ "array-init", - "bus-mapping 0.1.0", + "bus-mapping", "cli-table", "criterion", "ctor", "env_logger 0.9.3", - "eth-types 0.1.0", + "eth-types", "ethers-core", "ethers-signers", - "gadgets 0.1.0", + "gadgets", "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", "halo2_proofs", "hex", "itertools", - "keccak256 0.1.0", + "keccak256", "lazy_static", "libsecp256k1", "log", "maingate", - "mock 0.1.0", - "mpt-zktrie 0.1.0", + "mock", + "mpt-zktrie", "num", "num-bigint", "once_cell", @@ -5591,45 +5458,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "zkevm-circuits" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#801a048a9475f1bd64b09dba331c9e4ba8e63176" -dependencies = [ - "array-init", - "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "env_logger 0.9.3", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "ethers-core", - "ethers-signers", - "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", - "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", - "halo2_proofs", - "hex", - "itertools", - "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "lazy_static", - "libsecp256k1", - "log", - "maingate", - "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "mpt-zktrie 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "num", - "num-bigint", - "once_cell", - "rand", - "rand_chacha", - "rand_xorshift", - "rayon", - "sha3 0.10.8", - "snark-verifier", - "snark-verifier-sdk", - "strum", - "strum_macros", - "subtle", -] - [[package]] name = "zktrie" version = "0.1.2" diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index 60f35beac1..8a1a20a1b0 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -eth-types = { git = "https://github.com/scroll-tech/zkevm-circuits.git", branch = "develop" } -zkevm-circuits = { git = "https://github.com/scroll-tech/zkevm-circuits.git", branch = "develop" } +eth-types = { path = "../eth-types" } +zkevm-circuits = { path = "../zkevm-circuits" } ark-std = "0.4.0" From d822823a1a72184dcf7e93ac2560e319195bc679 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 15 Jun 2023 23:00:10 -0400 Subject: [PATCH 23/41] cargo fmt --- aggregator/src/tests/mock_chunk.rs | 11 +++-------- aggregator/src/tests/proof_aggregation.rs | 14 ++++---------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index 7b57b9ce59..fdca61007e 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -1,11 +1,6 @@ -use ark_std::{ test_rng}; -use halo2_proofs::{ - dev::MockProver, - halo2curves::bn256::{ Fr}, -}; -use snark_verifier_sdk::{ - CircuitExt, -}; +use ark_std::test_rng; +use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use snark_verifier_sdk::CircuitExt; use crate::{ChunkHash, LOG_DEGREE}; diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index 85d77e53cd..deae2b9653 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -10,7 +10,7 @@ use snark_verifier_sdk::{ CircuitExt, }; -use crate::{AggregationCircuit, ChunkHash, CompressionCircuit, compression_layer_snark, layer_0}; +use crate::{compression_layer_snark, layer_0, AggregationCircuit, ChunkHash, CompressionCircuit}; use super::mock_chunk::MockChunkCircuit; @@ -35,7 +35,6 @@ fn test_aggregation_circuit() { // aggregation let k3 = 26; - let mut rng = test_rng(); let params = gen_srs(k2); @@ -76,17 +75,12 @@ fn test_aggregation_circuit() { param.downsize(k3); param }; - let aggregation_circuit = AggregationCircuit::new( - ¶m, - &layer_2_snarks, - &mut rng, - chunks.as_ref(), - ); + let aggregation_circuit = + AggregationCircuit::new(¶m, &layer_2_snarks, &mut rng, chunks.as_ref()); let instance = aggregation_circuit.instances(); let mock_prover = MockProver::::run(k3, &aggregation_circuit, instance).unwrap(); - + mock_prover.assert_satisfied_par() } - } From 7c49e413e72445ab494119531f8df64de147d66d Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 20 Jun 2023 15:23:42 -0400 Subject: [PATCH 24/41] [chore] sync up with halo2-lib dev branch --- Cargo.lock | 567 +++++++++++++----- aggregator/Cargo.toml | 5 +- aggregator/src/core.rs | 5 +- aggregator/src/proof_aggregation/circuit.rs | 12 +- aggregator/src/proof_compression/circuit.rs | 24 +- aggregator/src/tests/aggregator_e2e.rs | 6 +- aggregator/src/tests/proof_aggregation.rs | 6 +- aggregator/src/tests/proof_compression.rs | 6 +- .../src/tests/public_input_aggregation.rs | 6 +- aggregator/tests.sh | 2 +- zkevm-circuits/Cargo.toml | 8 +- zkevm-circuits/src/tx_circuit.rs | 5 +- zkevm-circuits/src/tx_circuit/sign_verify.rs | 174 ++---- 13 files changed, 506 insertions(+), 320 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index faf29c8b6c..785a3ca22c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ version = "0.1.0" dependencies = [ "ark-std 0.4.0", "env_logger 0.10.0", - "eth-types", + "eth-types 0.1.0", "ethers-core", "halo2_proofs", "itertools", @@ -34,9 +34,9 @@ dependencies = [ "rand", "serde", "serde_json", - "snark-verifier", - "snark-verifier-sdk", - "zkevm-circuits", + "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", + "snark-verifier-sdk 0.0.1 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", + "zkevm-circuits 0.1.0", ] [[package]] @@ -126,9 +126,9 @@ checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "ascii-canvas" @@ -433,18 +433,18 @@ version = "0.1.0" dependencies = [ "ctor", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-providers", "ethers-signers", - "gadgets", + "gadgets 0.1.0", "halo2_proofs", "hex", "itertools", - "keccak256", + "keccak256 0.1.0", "lazy_static", "log", - "mock", + "mock 0.1.0", "once_cell", "poseidon-circuit", "pretty_assertions", @@ -458,6 +458,33 @@ dependencies = [ "url", ] +[[package]] +name = "bus-mapping" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "ethers-core", + "ethers-providers", + "ethers-signers", + "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2_proofs", + "hex", + "itertools", + "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "lazy_static", + "log", + "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "once_cell", + "poseidon-circuit", + "rand", + "revm-precompile", + "serde", + "serde_json", + "strum", + "strum_macros", +] + [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -572,22 +599,22 @@ name = "circuit-benchmarks" version = "0.1.0" dependencies = [ "ark-std 0.3.0", - "bus-mapping", + "bus-mapping 0.1.0", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers", "ethers-signers", "halo2_proofs", "itertools", - "keccak256", + "keccak256 0.1.0", "log", - "mock", + "mock 0.1.0", "rand", "rand_chacha", "rand_xorshift", "tokio", "url", - "zkevm-circuits", + "zkevm-circuits 0.1.0", ] [[package]] @@ -687,7 +714,7 @@ dependencies = [ "k256", "lazy_static", "serde", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", ] @@ -704,7 +731,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "rand", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", ] @@ -724,7 +751,7 @@ dependencies = [ "ripemd", "serde", "serde_derive", - "sha2 0.10.6", + "sha2 0.10.7", "sha3 0.10.8", "thiserror", ] @@ -760,9 +787,9 @@ checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" [[package]] name = "convert_case" @@ -807,13 +834,12 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ "bitflags", "core-foundation", - "foreign-types", "libc", ] @@ -831,9 +857,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -920,9 +946,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if 1.0.0", @@ -943,9 +969,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if 1.0.0", ] @@ -1387,7 +1413,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "sha3 0.10.8", "thiserror", "uuid 0.8.2", @@ -1418,6 +1444,32 @@ dependencies = [ "uint", ] +[[package]] +name = "eth-types" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "ethers-core", + "ethers-signers", + "halo2_proofs", + "hex", + "itertools", + "lazy_static", + "libsecp256k1", + "num", + "num-bigint", + "poseidon-circuit", + "regex", + "serde", + "serde_json", + "serde_with", + "sha3 0.10.8", + "strum", + "strum_macros", + "subtle", + "uint", +] + [[package]] name = "ethabi" version = "17.2.0" @@ -1653,7 +1705,7 @@ dependencies = [ "ethers-core", "hex", "rand", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", ] @@ -1692,8 +1744,20 @@ dependencies = [ name = "external-tracer" version = "0.1.0" dependencies = [ - "eth-types", - "geth-utils", + "eth-types 0.1.0", + "geth-utils 0.1.0", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "external-tracer" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "geth-utils 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "log", "serde", "serde_json", @@ -1998,7 +2062,7 @@ name = "gadgets" version = "0.1.0" dependencies = [ "digest 0.7.6", - "eth-types", + "eth-types 0.1.0", "halo2_proofs", "rand", "rand_xorshift", @@ -2006,6 +2070,18 @@ dependencies = [ "strum", ] +[[package]] +name = "gadgets" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "digest 0.7.6", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2_proofs", + "sha3 0.7.3", + "strum", +] + [[package]] name = "generic-array" version = "0.9.1" @@ -2039,7 +2115,17 @@ name = "geth-utils" version = "0.1.0" dependencies = [ "env_logger 0.9.3", - "gobuild", + "gobuild 0.1.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log", +] + +[[package]] +name = "geth-utils" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "env_logger 0.9.3", + "gobuild 0.1.0-alpha.2 (git+https://github.com/scroll-tech/gobuild.git)", "log", ] @@ -2058,9 +2144,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.11.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" dependencies = [ "color_quant", "weezl", @@ -2081,6 +2167,14 @@ dependencies = [ "cc", ] +[[package]] +name = "gobuild" +version = "0.1.0-alpha.2" +source = "git+https://github.com/scroll-tech/gobuild.git#8b84111fc3b58e2134e4794a06d1f199412cf2b0" +dependencies = [ + "cc", +] + [[package]] name = "group" version = "0.12.1" @@ -2117,6 +2211,21 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "halo2-base" +version = "0.2.2" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#c2ea19569db5fedd85a7465ee3841f23be8e7b22" +dependencies = [ + "ff", + "halo2_proofs", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "rand_chacha", + "rustc-hash", +] + [[package]] name = "halo2-base" version = "0.2.2" @@ -2135,7 +2244,7 @@ dependencies = [ [[package]] name = "halo2-base" version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#a80505265f36f87a2df40edc22ea468ea2068fb5" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#b1d1567063eb13fd1fc868fa9ed6b438961ef9e9" dependencies = [ "ff", "halo2_proofs", @@ -2147,6 +2256,25 @@ dependencies = [ "rustc-hash", ] +[[package]] +name = "halo2-ecc" +version = "0.2.2" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#c2ea19569db5fedd85a7465ee3841f23be8e7b22" +dependencies = [ + "ff", + "group", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "rand", + "rand_chacha", + "rand_core", + "serde", + "serde_json", +] + [[package]] name = "halo2-ecc" version = "0.2.2" @@ -2169,7 +2297,7 @@ dependencies = [ [[package]] name = "halo2-ecc" version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#a80505265f36f87a2df40edc22ea468ea2068fb5" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#b1d1567063eb13fd1fc868fa9ed6b438961ef9e9" dependencies = [ "ff", "group", @@ -2452,16 +2580,16 @@ checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" dependencies = [ "http", "hyper", - "rustls 0.21.1", + "rustls 0.21.2", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.24.1", ] [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2575,15 +2703,15 @@ dependencies = [ name = "integration-tests" version = "0.1.0" dependencies = [ - "bus-mapping", + "bus-mapping 0.1.0", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers", "halo2_proofs", "hex", "lazy_static", "log", - "mock", + "mock 0.1.0", "paste", "pretty_assertions", "rand_chacha", @@ -2594,7 +2722,7 @@ dependencies = [ "strum", "tokio", "url", - "zkevm-circuits", + "zkevm-circuits 0.1.0", ] [[package]] @@ -2649,9 +2777,9 @@ checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -2665,7 +2793,7 @@ dependencies = [ "cfg-if 1.0.0", "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", "sha3 0.10.8", ] @@ -2683,7 +2811,7 @@ name = "keccak256" version = "0.1.0" dependencies = [ "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "halo2_proofs", "itertools", "lazy_static", @@ -2695,6 +2823,21 @@ dependencies = [ "rand", ] +[[package]] +name = "keccak256" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "env_logger 0.9.3", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2_proofs", + "itertools", + "lazy_static", + "log", + "num-bigint", + "num-traits", +] + [[package]] name = "lalrpop" version = "0.19.12" @@ -2823,9 +2966,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "maingate" @@ -2858,9 +3001,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -2896,10 +3039,25 @@ dependencies = [ name = "mock" version = "0.1.0" dependencies = [ - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-signers", - "external-tracer", + "external-tracer 0.1.0", + "itertools", + "lazy_static", + "rand", + "rand_chacha", +] + +[[package]] +name = "mock" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "ethers-core", + "ethers-signers", + "external-tracer 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "itertools", "lazy_static", "rand", @@ -2910,9 +3068,9 @@ dependencies = [ name = "mpt-zktrie" version = "0.1.0" dependencies = [ - "bus-mapping", + "bus-mapping 0.1.0", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "halo2-mpt-circuits", "halo2_proofs", "hex", @@ -2924,6 +3082,22 @@ dependencies = [ "zktrie", ] +[[package]] +name = "mpt-zktrie" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2-mpt-circuits", + "halo2_proofs", + "hex", + "lazy_static", + "log", + "num-bigint", + "zktrie", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -3064,9 +3238,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "output_vt100" @@ -3079,9 +3253,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.5.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" +checksum = "2287753623c76f953acd29d15d8100bcab84d29db78fb6f352adb3c53e83b967" dependencies = [ "arrayvec", "bitvec 1.0.1", @@ -3093,9 +3267,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "2b6937b5e67bfba3351b87b040d48352a2fcb6ad72f81855412ce97b45c8f110" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -3237,7 +3411,7 @@ dependencies = [ "digest 0.10.7", "hmac 0.12.1", "password-hash 0.4.2", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -3248,9 +3422,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "16833386b02953ca926d19f64af613b9bf742c48dcd5e09b32fbfc9740bf84e2" dependencies = [ "thiserror", "ucd-trie", @@ -3258,9 +3432,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +checksum = "7763190f9406839f99e5197afee8c9e759969f7dbfa40ad3b8dbee8757b745b5" dependencies = [ "pest", "pest_generator", @@ -3268,9 +3442,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +checksum = "249061b22e99973da1f5f5f1410284419e283bb60b79255bf5f42a94b66a2e00" dependencies = [ "pest", "pest_meta", @@ -3281,13 +3455,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +checksum = "457c310cfc9cf3f22bc58901cc7f0d3410ac5d6298e432a4f9a6138565cb6df6" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -3404,9 +3578,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plotters" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "chrono", "font-kit", @@ -3424,15 +3598,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-bitmap" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4a1f21490a6cf4a84c272ad20bd7844ed99a3178187a4c5ab7f2051295beef" +checksum = "0cebbe1f70205299abc69e8b295035bb52a6a70ee35474ad10011f0a4efb8543" dependencies = [ "gif", "image", @@ -3441,18 +3615,18 @@ dependencies = [ [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "png" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" dependencies = [ "bitflags", "crc32fast", @@ -3596,9 +3770,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] @@ -3789,13 +3963,13 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.1", + "rustls 0.21.2", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -3834,7 +4008,7 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1 0.26.0", - "sha2 0.10.6", + "sha2 0.10.7", "sha3 0.10.8", "substrate-bn", ] @@ -3873,7 +4047,7 @@ dependencies = [ "primitive-types 0.12.1", "ripemd", "secp256k1 0.24.3", - "sha2 0.10.6", + "sha2 0.10.7", "sha3 0.10.8", "substrate-bn", ] @@ -3927,7 +4101,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.3.3", + "uuid 1.3.4", ] [[package]] @@ -3984,9 +4158,9 @@ checksum = "62cc5760263ea229d367e7dff3c0cbf09e4797a125bd87059a6c095804f3b2d1" [[package]] name = "rust_decimal" -version = "1.29.1" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26bd36b60561ee1fb5ec2817f198b6fd09fa571c897a5e86d1487cfc2b096dfc" +checksum = "d0446843641c69436765a35a5a77088e28c2e6a12da93e84aa3ab1cd4aa5a042" dependencies = [ "arrayvec", "borsh", @@ -4032,9 +4206,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags", "errno", @@ -4058,9 +4232,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" dependencies = [ "log", "ring", @@ -4133,7 +4307,7 @@ dependencies = [ "password-hash 0.3.2", "pbkdf2 0.10.1", "salsa20", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -4237,9 +4411,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] @@ -4266,9 +4440,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", @@ -4277,9 +4451,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" dependencies = [ "itoa", "ryu", @@ -4358,9 +4532,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -4444,6 +4618,30 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "snark-verifier" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#90b60591fd68d3ca5fc5eee400b3dd185e63dc38" +dependencies = [ + "bytes", + "ethereum-types 0.14.1", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", + "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", + "hex", + "itertools", + "lazy_static", + "num-bigint", + "num-integer", + "num-traits", + "poseidon", + "rand", + "revm", + "rlp", + "rustc-hash", + "serde", + "sha3 0.10.8", +] + [[package]] name = "snark-verifier" version = "0.1.0" @@ -4468,6 +4666,31 @@ dependencies = [ "sha3 0.10.8", ] +[[package]] +name = "snark-verifier-sdk" +version = "0.0.1" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#90b60591fd68d3ca5fc5eee400b3dd185e63dc38" +dependencies = [ + "bincode", + "env_logger 0.10.0", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "ethereum-types 0.14.1", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", + "hex", + "itertools", + "lazy_static", + "log", + "num-bigint", + "num-integer", + "num-traits", + "rand", + "rand_chacha", + "serde", + "serde_json", + "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", + "zkevm-circuits 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", +] + [[package]] name = "snark-verifier-sdk" version = "0.0.1" @@ -4488,7 +4711,7 @@ dependencies = [ "rand_chacha", "serde", "serde_json", - "snark-verifier", + "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323)", ] [[package]] @@ -4666,21 +4889,21 @@ name = "testool" version = "0.1.0" dependencies = [ "anyhow", - "bus-mapping", + "bus-mapping 0.1.0", "clap 3.2.25", "ctor", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-signers", - "external-tracer", + "external-tracer 0.1.0", "glob", "halo2_proofs", "handlebars", "hex", - "keccak256", + "keccak256 0.1.0", "log", - "mock", + "mock 0.1.0", "once_cell", "prettytable-rs", "rand", @@ -4695,7 +4918,7 @@ dependencies = [ "toml", "urlencoding", "yaml-rust", - "zkevm-circuits", + "zkevm-circuits 0.1.0", ] [[package]] @@ -4819,11 +5042,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.1", + "rustls 0.21.2", "tokio", ] @@ -4903,9 +5126,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "8803eee176538f94ae9a14b55b2804eb7e1441f8210b1c31290b3bccdccff73b" dependencies = [ "proc-macro2", "quote", @@ -4939,9 +5162,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ttf-parser" -version = "0.15.2" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" +checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" [[package]] name = "tungstenite" @@ -5062,9 +5285,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" [[package]] name = "version_check" @@ -5084,11 +5307,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -5106,9 +5328,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -5116,9 +5338,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", @@ -5131,9 +5353,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5143,9 +5365,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5153,9 +5375,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", @@ -5166,9 +5388,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-timer" @@ -5187,9 +5409,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -5328,9 +5550,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" dependencies = [ "memchr", ] @@ -5419,27 +5641,27 @@ name = "zkevm-circuits" version = "0.1.0" dependencies = [ "array-init", - "bus-mapping", + "bus-mapping 0.1.0", "cli-table", "criterion", "ctor", "env_logger 0.9.3", - "eth-types", + "eth-types 0.1.0", "ethers-core", "ethers-signers", - "gadgets", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", - "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", + "gadgets 0.1.0", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", + "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", "halo2_proofs", "hex", "itertools", - "keccak256", + "keccak256 0.1.0", "lazy_static", "libsecp256k1", "log", "maingate", - "mock", - "mpt-zktrie", + "mock 0.1.0", + "mpt-zktrie 0.1.0", "num", "num-bigint", "once_cell", @@ -5451,8 +5673,47 @@ dependencies = [ "serde", "serde_json", "sha3 0.10.8", - "snark-verifier", - "snark-verifier-sdk", + "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", + "snark-verifier-sdk 0.0.1 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", + "strum", + "strum_macros", + "subtle", +] + +[[package]] +name = "zkevm-circuits" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" +dependencies = [ + "array-init", + "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "env_logger 0.9.3", + "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "ethers-core", + "ethers-signers", + "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", + "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", + "halo2_proofs", + "hex", + "itertools", + "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "lazy_static", + "libsecp256k1", + "log", + "maingate", + "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "mpt-zktrie 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "num", + "num-bigint", + "once_cell", + "rand", + "rand_chacha", + "rand_xorshift", + "rayon", + "sha3 0.10.8", + "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323)", + "snark-verifier-sdk 0.0.1 (git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323)", "strum", "strum_macros", "subtle", @@ -5463,5 +5724,5 @@ name = "zktrie" version = "0.1.2" source = "git+https://github.com/scroll-tech/zktrie.git?branch=scroll-dev-0226#1a5562f663a81ff903383db69dc6c9404b63e69d" dependencies = [ - "gobuild", + "gobuild 0.1.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index 8a1a20a1b0..759727ed5d 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -20,8 +20,9 @@ serde_json = "1.0" rand = "0.8" halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" } -snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } -snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } +snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", branch = "develop" } +snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "develop", default-features=false, features = ["loader_halo2", "loader_evm", "halo2-pse"] } + [features] default = [] diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index 1a5624bfcd..97a8e3084a 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -8,6 +8,7 @@ use halo2_proofs::{ }; use rand::Rng; use snark_verifier::{ + loader::native::NativeLoader, pcs::{ kzg::{Bdfg21, Kzg, KzgAccumulator, KzgAs}, AccumulationSchemeProver, @@ -15,8 +16,8 @@ use snark_verifier::{ verifier::PlonkVerifier, }; use snark_verifier_sdk::{ - halo2::{aggregation::Shplonk, PoseidonTranscript, POSEIDON_SPEC}, - NativeLoader, Snark, + types::{PoseidonTranscript, Shplonk, POSEIDON_SPEC}, + Snark, }; use zkevm_circuits::{ keccak_circuit::{keccak_packed_multi::multi_keccak, KeccakCircuitConfig}, diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index 080b8e9dd1..262fd65d33 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -8,16 +8,18 @@ use halo2_proofs::{ use itertools::Itertools; use rand::Rng; use snark_verifier::{ - loader::halo2::{ - halo2_ecc::halo2_base::{self, AssignedValue, Context, ContextParams}, - Halo2Loader, + loader::{ + halo2::{ + halo2_ecc::halo2_base::{self, AssignedValue, Context, ContextParams}, + Halo2Loader, + }, + native::NativeLoader, }, pcs::kzg::{Bdfg21, Kzg, KzgAccumulator, KzgSuccinctVerifyingKey}, util::arithmetic::fe_to_limbs, }; use snark_verifier_sdk::{ - halo2::aggregation::{aggregate, flatten_accumulator, Svk}, - CircuitExt, NativeLoader, Snark, SnarkWitness, + aggregate, flatten_accumulator, types::Svk, CircuitExt, Snark, SnarkWitness, }; use zkevm_circuits::util::Challenges; diff --git a/aggregator/src/proof_compression/circuit.rs b/aggregator/src/proof_compression/circuit.rs index 5b985ed70d..52b17f325c 100644 --- a/aggregator/src/proof_compression/circuit.rs +++ b/aggregator/src/proof_compression/circuit.rs @@ -10,24 +10,24 @@ use halo2_proofs::{ }; use rand::Rng; use snark_verifier::{ - loader::halo2::{ - halo2_ecc::halo2_base::{ - self, - halo2_proofs::{ - halo2curves::bn256::{Bn256, Fr}, - poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, + loader::{ + halo2::{ + halo2_ecc::halo2_base::{ + self, + halo2_proofs::{ + halo2curves::bn256::{Bn256, Fr}, + poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, + }, + Context, ContextParams, }, - Context, ContextParams, + Halo2Loader, }, - Halo2Loader, + native::NativeLoader, }, pcs::kzg::{Bdfg21, Kzg, KzgAccumulator, KzgSuccinctVerifyingKey}, util::arithmetic::fe_to_limbs, }; -use snark_verifier_sdk::{ - halo2::aggregation::{aggregate, flatten_accumulator, Svk}, - NativeLoader, Snark, SnarkWitness, -}; +use snark_verifier_sdk::{aggregate, flatten_accumulator, types::Svk, Snark, SnarkWitness}; use crate::{ core::extract_accumulators_and_proof, diff --git a/aggregator/src/tests/aggregator_e2e.rs b/aggregator/src/tests/aggregator_e2e.rs index a9bf1bd8a7..8023ea136f 100644 --- a/aggregator/src/tests/aggregator_e2e.rs +++ b/aggregator/src/tests/aggregator_e2e.rs @@ -8,10 +8,8 @@ use snark_verifier::{ pcs::kzg::{Bdfg21, Kzg}, }; use snark_verifier_sdk::{ - evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier}, - gen_pk, - halo2::{gen_snark_shplonk, verify_snark_shplonk}, - CircuitExt, + evm_verify, gen_evm_proof_shplonk, gen_evm_verifier, gen_pk, gen_snark_shplonk, + verify_snark_shplonk, CircuitExt, }; use crate::{ diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index deae2b9653..c6febfbd82 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -4,11 +4,7 @@ use ark_std::{end_timer, start_timer, test_rng}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr, poly::commitment::Params}; use itertools::Itertools; use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; -use snark_verifier_sdk::{ - gen_pk, - halo2::{gen_snark_shplonk, verify_snark_shplonk}, - CircuitExt, -}; +use snark_verifier_sdk::{gen_pk, gen_snark_shplonk, verify_snark_shplonk, CircuitExt}; use crate::{compression_layer_snark, layer_0, AggregationCircuit, ChunkHash, CompressionCircuit}; diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index dfd44f36df..f6b18f43ad 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -7,10 +7,8 @@ use snark_verifier::{ pcs::kzg::{Bdfg21, Kzg}, }; use snark_verifier_sdk::{ - evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier}, - gen_pk, - halo2::{gen_snark_shplonk, verify_snark_shplonk}, - CircuitExt, + evm_verify, gen_evm_proof_shplonk, gen_evm_verifier, gen_pk, gen_snark_shplonk, + verify_snark_shplonk, CircuitExt, }; use crate::{ diff --git a/aggregator/src/tests/public_input_aggregation.rs b/aggregator/src/tests/public_input_aggregation.rs index c4ed87295a..cc6fae725d 100644 --- a/aggregator/src/tests/public_input_aggregation.rs +++ b/aggregator/src/tests/public_input_aggregation.rs @@ -1,11 +1,7 @@ use ark_std::{end_timer, start_timer, test_rng}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; -use snark_verifier_sdk::{ - gen_pk, - halo2::{gen_snark_shplonk, verify_snark_shplonk}, - CircuitExt, -}; +use snark_verifier_sdk::{gen_pk, gen_snark_shplonk, verify_snark_shplonk, CircuitExt}; use crate::{BatchHashCircuit, LOG_DEGREE}; diff --git a/aggregator/tests.sh b/aggregator/tests.sh index 89d1f74a6a..dbed5bfdcc 100644 --- a/aggregator/tests.sh +++ b/aggregator/tests.sh @@ -1,10 +1,10 @@ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_mock_prover -- --nocapture 2>&1 | tee pi_mock.log RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_real_prover -- --nocapture 2>&1 | tee pi_real.log RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log -RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_proof_compression -- --nocapture 2>&1 | tee compression.log # the following 3 tests takes super long time +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --ignored --nocapture 2>&1 | tee compression2.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_aggregation_circuit -- --ignored --nocapture 2>&1 | tee aggregation.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_e2e -- --ignored --nocapture 2>&1 | tee aggregation.log diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 60f7b75684..58abf326df 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -29,8 +29,8 @@ keccak256 = { path = "../keccak256"} log = "0.4" env_logger = "0.9" -halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "halo2-ecc-snark-verifier-0323", default-features=false, features=["halo2-pse","display"] } -halo2-ecc = { git = "https://github.com/scroll-tech/halo2-lib", branch = "halo2-ecc-snark-verifier-0323", default-features=false, features=["halo2-pse","display"] } +halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } +halo2-ecc = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } maingate = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_02_02" } @@ -38,8 +38,8 @@ libsecp256k1 = "0.7" num-bigint = { version = "0.4" } subtle = "2.4" rand_chacha = "0.3" -snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323" } -snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "halo2-ecc-snark-verifier-0323", default-features=false, features = ["loader_halo2", "loader_evm", "halo2-pse"] } +snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", branch = "develop" } +snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "develop", default-features=false, features = ["loader_halo2", "loader_evm", "halo2-pse"] } hex = "0.4.3" rayon = "1.5" once_cell = "1.17.0" diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index a738754c32..ffd2025934 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -1593,8 +1593,7 @@ impl TxCircuit { CallerAddress => { #[cfg(feature = "enable-sign-verify")] { - let address: AssignedValue<_> = - assigned_sig_verif.address.clone().into(); + let address: AssignedValue<_> = assigned_sig_verif.address; address.copy_advice(&mut region, config.sv_address, offset - 1); } #[cfg(not(feature = "enable-sign-verify"))] @@ -1616,7 +1615,7 @@ impl TxCircuit { #[cfg(feature = "enable-sign-verify")] { region.constrain_equal( - assigned_sig_verif.msg_hash_rlc.clone().cell, + assigned_sig_verif.msg_hash_rlc.cell, Cell { // FIXME region_index: RegionIndex(1), diff --git a/zkevm-circuits/src/tx_circuit/sign_verify.rs b/zkevm-circuits/src/tx_circuit/sign_verify.rs index 4d231d5986..7704ee53a2 100644 --- a/zkevm-circuits/src/tx_circuit/sign_verify.rs +++ b/zkevm-circuits/src/tx_circuit/sign_verify.rs @@ -37,7 +37,7 @@ use halo2_proofs::plonk::FirstPhase; #[cfg(not(feature = "onephase"))] use halo2_proofs::plonk::SecondPhase; use halo2_proofs::{ - circuit::{Cell, Layouter, Value}, + circuit::{Layouter, Value}, halo2curves::secp256k1::{Fp, Fq, Secp256k1Affine}, plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Selector}, poly::Rotation, @@ -62,7 +62,7 @@ fn calc_required_advices(num_verif: usize) -> usize { let total_cells = num_verif * CELLS_PER_SIG; while num_adv < 150 { if num_adv << TOTAL_NUM_ROWS > total_cells { - log::debug!( + log::info!( "ecdsa chip uses {} advice columns for {} signatures", num_adv, num_verif @@ -116,7 +116,7 @@ impl SignVerifyChip { CELLS_PER_SIG ) } else { - log::debug!( + log::info!( "ecdsa chip: rows: {}, advice {}, num of sigs {}, cells per sig {}", TOTAL_NUM_ROWS, calc_required_advices(num_verif), @@ -164,9 +164,9 @@ impl SignVerifyConfig { let num_advice = [calc_required_advices(MAX_NUM_SIG), 1]; #[cfg(feature = "onephase")] - log::debug!("configuring ECDSA chip with single phase"); + log::info!("configuring ECDSA chip with single phase"); #[cfg(not(feature = "onephase"))] - log::debug!("configuring ECDSA chip with multiple phases"); + log::info!("configuring ECDSA chip with multiple phases"); // halo2-ecc's ECDSA config // @@ -258,89 +258,27 @@ impl SignVerifyConfig { } } -pub(crate) struct AssignedECDSA<'v, F: Field, FC: FieldChip> { - pk: EcPoint>, - msg_hash: CRTInteger<'v, F>, - sig_is_valid: AssignedValue<'v, F>, -} - -/// Temp struct to hold the intermediate data; removing life timer. -// Issue with life timer: -// -// Suppose we have two piece of codes, that request different regions/contexts from the layouter. -// The first piece of the code will return an `assigned_cell` that is to be used by the second code -// piece. With halo2 we can safely pass this `assigned_cell` around. They are bounded by a life -// timer `'v` which is when the field element is created. -// -// Now in halo2-lib, there is an additional life timer which says an `assigned_cell` cannot outlive -// the `region` for which this cell is created. (is this understanding correct?) -// That means the output cells of the first region cannot be passed to the second region. -// -// To temporary resolve this issue, we create a temp struct without life timer. -// This works with halo2-lib/pse but not halo2-lib/axiom. -// We do not support halo2-lib/axiom. -// -// NOTE: this is a temp issue with halo2-lib v0.2.2. -// with halo2-lib v0.3.0 the timers are already removed. -// So we don't need this temp fix once we sync with halo2-lib audited version. -#[derive(Debug, Clone)] -pub(crate) struct AssignedValueNoTimer { - pub cell: Cell, - pub value: Value, - pub row_offset: usize, - pub context_id: usize, -} - -impl<'v, F: Field> From> for AssignedValueNoTimer { - fn from(input: AssignedValue<'v, F>) -> Self { - Self { - cell: input.cell(), - value: input.value, - row_offset: input.row_offset, - context_id: input.context_id, - } - } -} - -impl<'v, F: Field> From> for AssignedValue<'v, F> { - fn from(input: AssignedValueNoTimer) -> Self { - Self { - cell: input.cell, - value: input.value, - row_offset: input.row_offset, - _marker: PhantomData::default(), - context_id: input.context_id, - } - } -} - -impl<'v, F: Field> From<&AssignedValueNoTimer> for AssignedValue<'v, F> { - fn from(input: &AssignedValueNoTimer) -> Self { - Self { - cell: input.cell, - value: input.value, - row_offset: input.row_offset, - _marker: PhantomData::default(), - context_id: input.context_id, - } - } +pub(crate) struct AssignedECDSA> { + pk: EcPoint, + msg_hash: CRTInteger, + sig_is_valid: AssignedValue, } #[derive(Debug)] pub(crate) struct AssignedSignatureVerify { - pub(crate) address: AssignedValueNoTimer, + pub(crate) address: AssignedValue, pub(crate) msg_len: usize, pub(crate) msg_rlc: Value, - pub(crate) msg_hash_rlc: AssignedValueNoTimer, - pub(crate) sig_is_valid: AssignedValueNoTimer, + pub(crate) msg_hash_rlc: AssignedValue, + pub(crate) sig_is_valid: AssignedValue, } -struct SignDataDecomposed<'a: 'v, 'v, F: Field> { - pk_hash_cells: Vec>, - msg_hash_cells: Vec>, - pk_cells: Vec>, - address: AssignedValue<'v, F>, - is_address_zero: AssignedValue<'v, F>, +struct SignDataDecomposed { + pk_hash_cells: Vec>, + msg_hash_cells: Vec>, + pk_cells: Vec>, + address: AssignedValue, + is_address_zero: AssignedValue, } impl SignVerifyChip { @@ -355,12 +293,12 @@ impl SignVerifyChip { /// /// WARNING: this circuit does not enforce the returned value to be true /// make sure the caller checks this result! - fn assign_ecdsa<'v>( + fn assign_ecdsa( &self, - ctx: &mut Context<'v, F>, + ctx: &mut Context, ecdsa_chip: &FpChip, sign_data: &SignData, - ) -> Result>, Error> { + ) -> Result>, Error> { log::trace!("start ecdsa assignment"); let SignData { signature, @@ -467,12 +405,12 @@ impl SignVerifyChip { /// Input the signature data, /// Output the cells for byte decomposition of the keys and messages - fn sign_data_decomposition<'a: 'v, 'v>( + fn sign_data_decomposition( &self, - ctx: &mut Context<'v, F>, + ctx: &mut Context, ecdsa_chip: &FpChip, sign_data: Option<&SignData>, - ) -> Result, Error> { + ) -> Result, Error> { // build ecc chip from Fp chip let ecc_chip = EccChip::>::construct(ecdsa_chip.clone()); @@ -525,10 +463,10 @@ impl SignVerifyChip { let is_address_zero = ecdsa_chip.range.gate.is_equal( ctx, - QuantumCell::Existing(&address), - QuantumCell::Existing(&zero), + QuantumCell::Existing(address), + QuantumCell::Existing(zero), ); - let is_address_zero_cell = QuantumCell::Existing(&is_address_zero); + let is_address_zero_cell = QuantumCell::Existing(is_address_zero); // ================================================ // message hash cells @@ -609,15 +547,15 @@ impl SignVerifyChip { } #[allow(clippy::too_many_arguments)] - fn assign_sig_verify<'a: 'v, 'v>( + fn assign_sig_verify( &self, - ctx: &mut Context<'v, F>, + ctx: &mut Context, rlc_chip: &RangeConfig, sign_data: Option<&SignData>, - sign_data_decomposed: &SignDataDecomposed<'a, 'v, F>, + sign_data_decomposed: &SignDataDecomposed, challenges: &Challenges>, - sig_is_valid: &AssignedValue<'v, F>, - ) -> Result<([AssignedValue<'v, F>; 3], AssignedSignatureVerify), Error> { + sig_is_valid: &AssignedValue, + ) -> Result<([AssignedValue; 3], AssignedSignatureVerify), Error> { let (_padding, sign_data) = match sign_data { Some(sign_data) => (false, sign_data.clone()), None => (true, SignData::default()), @@ -675,25 +613,21 @@ impl SignVerifyChip { let pk_hash_rlc = rlc_chip.gate.inner_product( ctx, sign_data_decomposed.pk_hash_cells.clone(), - evm_challenge_powers.clone(), + evm_challenge_powers, ); log::trace!("pk hash rlc halo2ecc: {:?}", pk_hash_rlc.value()); log::trace!("finished sign verify"); Ok(( - [ - sign_data_decomposed.is_address_zero.clone(), - pk_rlc, - pk_hash_rlc, - ], + [sign_data_decomposed.is_address_zero, pk_rlc, pk_hash_rlc], AssignedSignatureVerify { - address: sign_data_decomposed.address.clone().into(), + address: sign_data_decomposed.address, msg_len: sign_data.msg.len(), msg_rlc: challenges .keccak_input() .map(|r| rlc::value(sign_data.msg.iter().rev(), r)), - msg_hash_rlc: msg_hash_rlc.into(), - sig_is_valid: sig_is_valid.clone().into(), + msg_hash_rlc, + sig_is_valid: *sig_is_valid, }, )) } @@ -754,7 +688,7 @@ impl SignVerifyChip { } // IMPORTANT: Move to Phase2 before RLC - log::debug!("before proceeding to the next phase"); + log::info!("before proceeding to the next phase"); ctx.print_stats(&["Range"]); #[cfg(not(feature = "onephase"))] @@ -805,7 +739,7 @@ impl SignVerifyChip { // check lookups // This is not optional. let lookup_cells = ecdsa_chip.finalize(&mut ctx); - log::debug!("total number of lookup cells: {}", lookup_cells); + log::info!("total number of lookup cells: {}", lookup_cells); ctx.print_stats(&["Range"]); Ok(assigned_sig_verifs) @@ -837,35 +771,35 @@ impl SignVerifyChip { let flex_gate_chip = &range_chip.gate; let zero = flex_gate_chip.load_zero(ctx); - let zero_cell = QuantumCell::Existing(&zero); + let zero_cell = QuantumCell::Existing(zero); // apply the overriding flag let limb1_value = match overriding { Some(p) => flex_gate_chip.select( ctx, zero_cell.clone(), - QuantumCell::Existing(&crt_int.truncation.limbs[0]), + QuantumCell::Existing(crt_int.truncation.limbs[0]), (*p).clone(), ), - None => crt_int.truncation.limbs[0].clone(), + None => crt_int.truncation.limbs[0], }; let limb2_value = match overriding { Some(p) => flex_gate_chip.select( ctx, zero_cell.clone(), - QuantumCell::Existing(&crt_int.truncation.limbs[1]), + QuantumCell::Existing(crt_int.truncation.limbs[1]), (*p).clone(), ), - None => crt_int.truncation.limbs[1].clone(), + None => crt_int.truncation.limbs[1], }; let limb3_value = match overriding { Some(p) => flex_gate_chip.select( ctx, zero_cell, - QuantumCell::Existing(&crt_int.truncation.limbs[2]), + QuantumCell::Existing(crt_int.truncation.limbs[2]), (*p).clone(), ), - None => crt_int.truncation.limbs[2].clone(), + None => crt_int.truncation.limbs[2], }; // assert the byte_repr is the right decomposition of overflow_int @@ -889,18 +823,18 @@ impl SignVerifyChip { ); flex_gate_chip.assert_equal( ctx, - QuantumCell::Existing(&limb1_value), - QuantumCell::Existing(&limb1_recover), + QuantumCell::Existing(limb1_value), + QuantumCell::Existing(limb1_recover), ); flex_gate_chip.assert_equal( ctx, - QuantumCell::Existing(&limb2_value), - QuantumCell::Existing(&limb2_recover), + QuantumCell::Existing(limb2_value), + QuantumCell::Existing(limb2_recover), ); flex_gate_chip.assert_equal( ctx, - QuantumCell::Existing(&limb3_value), - QuantumCell::Existing(&limb3_recover), + QuantumCell::Existing(limb3_value), + QuantumCell::Existing(limb3_recover), ); log::trace!( "limb 1 \ninput {:?}\nreconstructed {:?}", @@ -1104,7 +1038,7 @@ mod sign_verify_tests { let mut rng = XorShiftRng::seed_from_u64(1); let max_sigs = [4]; for max_sig in max_sigs.iter() { - log::debug!("testing for {} signatures", max_sig); + log::info!("testing for {} signatures", max_sig); let mut signatures = Vec::new(); for _ in 0..*max_sig { let (sk, pk) = gen_key_pair(&mut rng); @@ -1127,7 +1061,7 @@ mod sign_verify_tests { let k = TOTAL_NUM_ROWS as u32; run::(k, *max_sig, signatures); - log::debug!("end of testing for {} signatures", max_sig); + log::info!("end of testing for {} signatures", max_sig); } } } From c3e82074ae5722e9cc20238f9d1852481ddd3df1 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 20 Jun 2023 15:35:44 -0400 Subject: [PATCH 25/41] [chore] update cargo lock --- Cargo.lock | 348 ++++++----------------------------------------------- 1 file changed, 39 insertions(+), 309 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 785a3ca22c..45e40683a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ dependencies = [ "rand", "serde", "serde_json", - "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", - "snark-verifier-sdk 0.0.1 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", - "zkevm-circuits 0.1.0", + "snark-verifier", + "snark-verifier-sdk", + "zkevm-circuits", ] [[package]] @@ -437,14 +437,14 @@ dependencies = [ "ethers-core", "ethers-providers", "ethers-signers", - "gadgets 0.1.0", + "gadgets", "halo2_proofs", "hex", "itertools", - "keccak256 0.1.0", + "keccak256", "lazy_static", "log", - "mock 0.1.0", + "mock", "once_cell", "poseidon-circuit", "pretty_assertions", @@ -458,33 +458,6 @@ dependencies = [ "url", ] -[[package]] -name = "bus-mapping" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "ethers-core", - "ethers-providers", - "ethers-signers", - "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2_proofs", - "hex", - "itertools", - "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "lazy_static", - "log", - "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "once_cell", - "poseidon-circuit", - "rand", - "revm-precompile", - "serde", - "serde_json", - "strum", - "strum_macros", -] - [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -599,22 +572,22 @@ name = "circuit-benchmarks" version = "0.1.0" dependencies = [ "ark-std 0.3.0", - "bus-mapping 0.1.0", + "bus-mapping", "env_logger 0.9.3", "eth-types 0.1.0", "ethers", "ethers-signers", "halo2_proofs", "itertools", - "keccak256 0.1.0", + "keccak256", "log", - "mock 0.1.0", + "mock", "rand", "rand_chacha", "rand_xorshift", "tokio", "url", - "zkevm-circuits 0.1.0", + "zkevm-circuits", ] [[package]] @@ -1745,19 +1718,7 @@ name = "external-tracer" version = "0.1.0" dependencies = [ "eth-types 0.1.0", - "geth-utils 0.1.0", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "external-tracer" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "geth-utils 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "geth-utils", "log", "serde", "serde_json", @@ -2070,18 +2031,6 @@ dependencies = [ "strum", ] -[[package]] -name = "gadgets" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "digest 0.7.6", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2_proofs", - "sha3 0.7.3", - "strum", -] - [[package]] name = "generic-array" version = "0.9.1" @@ -2115,17 +2064,7 @@ name = "geth-utils" version = "0.1.0" dependencies = [ "env_logger 0.9.3", - "gobuild 0.1.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log", -] - -[[package]] -name = "geth-utils" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "env_logger 0.9.3", - "gobuild 0.1.0-alpha.2 (git+https://github.com/scroll-tech/gobuild.git)", + "gobuild", "log", ] @@ -2167,14 +2106,6 @@ dependencies = [ "cc", ] -[[package]] -name = "gobuild" -version = "0.1.0-alpha.2" -source = "git+https://github.com/scroll-tech/gobuild.git#8b84111fc3b58e2134e4794a06d1f199412cf2b0" -dependencies = [ - "cc", -] - [[package]] name = "group" version = "0.12.1" @@ -2226,36 +2157,6 @@ dependencies = [ "rustc-hash", ] -[[package]] -name = "halo2-base" -version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323#d24871338ade7dd56362de517b718ba14f3e7b90" -dependencies = [ - "ff", - "halo2_proofs", - "itertools", - "num-bigint", - "num-integer", - "num-traits", - "rand_chacha", - "rustc-hash", -] - -[[package]] -name = "halo2-base" -version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#b1d1567063eb13fd1fc868fa9ed6b438961ef9e9" -dependencies = [ - "ff", - "halo2_proofs", - "itertools", - "num-bigint", - "num-integer", - "num-traits", - "rand_chacha", - "rustc-hash", -] - [[package]] name = "halo2-ecc" version = "0.2.2" @@ -2263,45 +2164,7 @@ source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#c2ea19569d dependencies = [ "ff", "group", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", - "itertools", - "num-bigint", - "num-integer", - "num-traits", - "rand", - "rand_chacha", - "rand_core", - "serde", - "serde_json", -] - -[[package]] -name = "halo2-ecc" -version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323#d24871338ade7dd56362de517b718ba14f3e7b90" -dependencies = [ - "ff", - "group", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", - "itertools", - "num-bigint", - "num-integer", - "num-traits", - "rand", - "rand_chacha", - "rand_core", - "serde", - "serde_json", -] - -[[package]] -name = "halo2-ecc" -version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff#b1d1567063eb13fd1fc868fa9ed6b438961ef9e9" -dependencies = [ - "ff", - "group", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", + "halo2-base", "itertools", "num-bigint", "num-integer", @@ -2703,7 +2566,7 @@ dependencies = [ name = "integration-tests" version = "0.1.0" dependencies = [ - "bus-mapping 0.1.0", + "bus-mapping", "env_logger 0.9.3", "eth-types 0.1.0", "ethers", @@ -2711,7 +2574,7 @@ dependencies = [ "hex", "lazy_static", "log", - "mock 0.1.0", + "mock", "paste", "pretty_assertions", "rand_chacha", @@ -2722,7 +2585,7 @@ dependencies = [ "strum", "tokio", "url", - "zkevm-circuits 0.1.0", + "zkevm-circuits", ] [[package]] @@ -2823,21 +2686,6 @@ dependencies = [ "rand", ] -[[package]] -name = "keccak256" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "env_logger 0.9.3", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2_proofs", - "itertools", - "lazy_static", - "log", - "num-bigint", - "num-traits", -] - [[package]] name = "lalrpop" version = "0.19.12" @@ -3042,22 +2890,7 @@ dependencies = [ "eth-types 0.1.0", "ethers-core", "ethers-signers", - "external-tracer 0.1.0", - "itertools", - "lazy_static", - "rand", - "rand_chacha", -] - -[[package]] -name = "mock" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "ethers-core", - "ethers-signers", - "external-tracer 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", + "external-tracer", "itertools", "lazy_static", "rand", @@ -3068,7 +2901,7 @@ dependencies = [ name = "mpt-zktrie" version = "0.1.0" dependencies = [ - "bus-mapping 0.1.0", + "bus-mapping", "env_logger 0.9.3", "eth-types 0.1.0", "halo2-mpt-circuits", @@ -3082,22 +2915,6 @@ dependencies = [ "zktrie", ] -[[package]] -name = "mpt-zktrie" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2-mpt-circuits", - "halo2_proofs", - "hex", - "lazy_static", - "log", - "num-bigint", - "zktrie", -] - [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -4621,36 +4438,12 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snark-verifier" version = "0.1.0" -source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#90b60591fd68d3ca5fc5eee400b3dd185e63dc38" -dependencies = [ - "bytes", - "ethereum-types 0.14.1", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", - "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", - "hex", - "itertools", - "lazy_static", - "num-bigint", - "num-integer", - "num-traits", - "poseidon", - "rand", - "revm", - "rlp", - "rustc-hash", - "serde", - "sha3 0.10.8", -] - -[[package]] -name = "snark-verifier" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323#ae6a9ef1ba0f5296f98cd6ba2a94f791278be851" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#4c90dccc4b71a791e5b0c6bccee1cd5e90193629" dependencies = [ "bytes", "ethereum-types 0.14.1", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", - "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", + "halo2-base", + "halo2-ecc", "hex", "itertools", "lazy_static", @@ -4669,37 +4462,13 @@ dependencies = [ [[package]] name = "snark-verifier-sdk" version = "0.0.1" -source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#90b60591fd68d3ca5fc5eee400b3dd185e63dc38" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#4c90dccc4b71a791e5b0c6bccee1cd5e90193629" dependencies = [ "bincode", "env_logger 0.10.0", "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", "ethereum-types 0.14.1", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", - "hex", - "itertools", - "lazy_static", - "log", - "num-bigint", - "num-integer", - "num-traits", - "rand", - "rand_chacha", - "serde", - "serde_json", - "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", - "zkevm-circuits 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", -] - -[[package]] -name = "snark-verifier-sdk" -version = "0.0.1" -source = "git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323#ae6a9ef1ba0f5296f98cd6ba2a94f791278be851" -dependencies = [ - "bincode", - "env_logger 0.10.0", - "ethereum-types 0.14.1", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=minimize-diff)", + "halo2-base", "hex", "itertools", "lazy_static", @@ -4711,7 +4480,7 @@ dependencies = [ "rand_chacha", "serde", "serde_json", - "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323)", + "snark-verifier", ] [[package]] @@ -4889,21 +4658,21 @@ name = "testool" version = "0.1.0" dependencies = [ "anyhow", - "bus-mapping 0.1.0", + "bus-mapping", "clap 3.2.25", "ctor", "env_logger 0.9.3", "eth-types 0.1.0", "ethers-core", "ethers-signers", - "external-tracer 0.1.0", + "external-tracer", "glob", "halo2_proofs", "handlebars", "hex", - "keccak256 0.1.0", + "keccak256", "log", - "mock 0.1.0", + "mock", "once_cell", "prettytable-rs", "rand", @@ -4918,7 +4687,7 @@ dependencies = [ "toml", "urlencoding", "yaml-rust", - "zkevm-circuits 0.1.0", + "zkevm-circuits", ] [[package]] @@ -5641,7 +5410,7 @@ name = "zkevm-circuits" version = "0.1.0" dependencies = [ "array-init", - "bus-mapping 0.1.0", + "bus-mapping", "cli-table", "criterion", "ctor", @@ -5649,19 +5418,19 @@ dependencies = [ "eth-types 0.1.0", "ethers-core", "ethers-signers", - "gadgets 0.1.0", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", - "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)", + "gadgets", + "halo2-base", + "halo2-ecc", "halo2_proofs", "hex", "itertools", - "keccak256 0.1.0", + "keccak256", "lazy_static", "libsecp256k1", "log", "maingate", - "mock 0.1.0", - "mpt-zktrie 0.1.0", + "mock", + "mpt-zktrie", "num", "num-bigint", "once_cell", @@ -5673,47 +5442,8 @@ dependencies = [ "serde", "serde_json", "sha3 0.10.8", - "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", - "snark-verifier-sdk 0.0.1 (git+https://github.com/scroll-tech/snark-verifier?branch=develop)", - "strum", - "strum_macros", - "subtle", -] - -[[package]] -name = "zkevm-circuits" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop#6c6865f64dfbcc69431adf67361a4e49029a5f19" -dependencies = [ - "array-init", - "bus-mapping 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "env_logger 0.9.3", - "eth-types 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "ethers-core", - "ethers-signers", - "gadgets 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", - "halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=halo2-ecc-snark-verifier-0323)", - "halo2_proofs", - "hex", - "itertools", - "keccak256 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "lazy_static", - "libsecp256k1", - "log", - "maingate", - "mock 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "mpt-zktrie 0.1.0 (git+https://github.com/scroll-tech/zkevm-circuits.git?branch=develop)", - "num", - "num-bigint", - "once_cell", - "rand", - "rand_chacha", - "rand_xorshift", - "rayon", - "sha3 0.10.8", - "snark-verifier 0.1.0 (git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323)", - "snark-verifier-sdk 0.0.1 (git+https://github.com/scroll-tech/snark-verifier?branch=halo2-ecc-snark-verifier-0323)", + "snark-verifier", + "snark-verifier-sdk", "strum", "strum_macros", "subtle", @@ -5724,5 +5454,5 @@ name = "zktrie" version = "0.1.2" source = "git+https://github.com/scroll-tech/zktrie.git?branch=scroll-dev-0226#1a5562f663a81ff903383db69dc6c9404b63e69d" dependencies = [ - "gobuild 0.1.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gobuild", ] From 990a2d149d34fef4a204dd77fbd0ae1ed7b03301 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 20 Jun 2023 19:47:30 -0400 Subject: [PATCH 26/41] [fix] chain id u32 -> u64 --- aggregator/README.md | 2 +- aggregator/src/chunk.rs | 2 +- aggregator/src/core.rs | 48 +++++++++++-------- aggregator/src/proof_aggregation/circuit.rs | 12 ++--- aggregator/src/proof_aggregation/config.rs | 2 +- .../public_input_aggregation.rs | 5 +- .../public_input_aggregation/circuit.rs | 16 +++---- .../public_input_aggregation/circuit_ext.rs | 2 +- .../src/tests/mock_chunk/circuit_ext.rs | 11 +++-- aggregator/src/util.rs | 4 +- aggregator/tests.sh | 0 11 files changed, 60 insertions(+), 44 deletions(-) mode change 100644 => 100755 aggregator/tests.sh diff --git a/aggregator/README.md b/aggregator/README.md index 91d7bca1bc..98d25c60c8 100644 --- a/aggregator/README.md +++ b/aggregator/README.md @@ -49,7 +49,7 @@ It also performs public input aggregation, i.e., reducing the `64k` public eleme - last_chunk_post_state_root: 32 Field elements - last_chunk_withdraw_root: 32 Field elements - batch_public_input_hash: 32 Field elements - - chain_id: 4 Field elements + - chain_id: 8 Field elements In addition, it attests that, for chunks indexed from `0` to `k-1`, - batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) where chunk_i.data_hash is a public input to the i-th batch snark circuit diff --git a/aggregator/src/chunk.rs b/aggregator/src/chunk.rs index ff3e6f6ecb..86c424e40d 100644 --- a/aggregator/src/chunk.rs +++ b/aggregator/src/chunk.rs @@ -12,7 +12,7 @@ use ethers_core::utils::keccak256; /// - the data hash of this chunk pub struct ChunkHash { /// Chain identifier - pub(crate) chain_id: u32, + pub(crate) chain_id: u64, /// state root before this chunk pub(crate) prev_state_root: H256, /// state root after this chunk diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index 97a8e3084a..fa0f22bd0f 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -27,7 +27,7 @@ use zkevm_circuits::{ use crate::{ util::{assert_equal, capacity, get_indices}, - LOG_DEGREE, + CHAIN_ID_LEN, LOG_DEGREE, }; /// Input the hash input bytes, @@ -127,12 +127,12 @@ pub(crate) fn assign_batch_hashes( for j in 0..8 { // sanity check assert_equal( - &hash_input_cells[0][i * 8 + j + 100], + &hash_input_cells[0][i * 8 + j + 96 + CHAIN_ID_LEN], &hash_output_cells[1][(3 - i) * 8 + j], ); region.constrain_equal( // preimage and digest has different endianness - hash_input_cells[0][i * 8 + j + 100].cell(), + hash_input_cells[0][i * 8 + j + 96 + CHAIN_ID_LEN].cell(), hash_output_cells[1][(3 - i) * 8 + j].cell(), )?; } @@ -158,29 +158,32 @@ pub(crate) fn assign_batch_hashes( for i in 0..32 { // 2.2.1 chunk[0].prev_state_root // sanity check - assert_equal(&hash_input_cells[0][i + 4], &hash_input_cells[2][i + 4]); + assert_equal( + &hash_input_cells[0][i + CHAIN_ID_LEN], + &hash_input_cells[2][i + CHAIN_ID_LEN], + ); region.constrain_equal( - hash_input_cells[0][i + 4].cell(), - hash_input_cells[2][i + 4].cell(), + hash_input_cells[0][i + CHAIN_ID_LEN].cell(), + hash_input_cells[2][i + CHAIN_ID_LEN].cell(), )?; // 2.2.2 chunk[k-1].post_state_root // sanity check assert_equal( - &hash_input_cells[0][i + 36], - &hash_input_cells[hash_num - 1][i + 36], + &hash_input_cells[0][i + CHAIN_ID_LEN + 32], + &hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 32], ); region.constrain_equal( - hash_input_cells[0][i + 36].cell(), - hash_input_cells[hash_num - 1][i + 36].cell(), + hash_input_cells[0][i + CHAIN_ID_LEN + 32].cell(), + hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 32].cell(), )?; // 2.2.3 chunk[k-1].withdraw_root assert_equal( - &hash_input_cells[0][i + 68], - &hash_input_cells[hash_num - 1][i + 68], + &hash_input_cells[0][i + CHAIN_ID_LEN + 64], + &hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 64], ); region.constrain_equal( - hash_input_cells[0][i + 68].cell(), - hash_input_cells[hash_num - 1][i + 68].cell(), + hash_input_cells[0][i + CHAIN_ID_LEN + 64].cell(), + hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 64].cell(), )?; } @@ -198,8 +201,11 @@ pub(crate) fn assign_batch_hashes( for (i, chunk) in hash_input_cells[1].chunks(32).enumerate().take(num_chunks) { for (j, cell) in chunk.iter().enumerate() { // sanity check - assert_equal(cell, &hash_input_cells[2 + i][j + 100]); - region.constrain_equal(cell.cell(), hash_input_cells[2 + i][j + 100].cell())?; + assert_equal(cell, &hash_input_cells[2 + i][j + CHAIN_ID_LEN + 96]); + region.constrain_equal( + cell.cell(), + hash_input_cells[2 + i][j + CHAIN_ID_LEN + 96].cell(), + )?; } } @@ -208,21 +214,21 @@ pub(crate) fn assign_batch_hashes( for j in 0..32 { // sanity check assert_equal( - &hash_input_cells[i + 3][4 + j], - &hash_input_cells[i + 2][36 + j], + &hash_input_cells[i + 3][CHAIN_ID_LEN + j], + &hash_input_cells[i + 2][CHAIN_ID_LEN + 32 + j], ); region.constrain_equal( // chunk[i+1].prevStateRoot - hash_input_cells[i + 3][4 + j].cell(), + hash_input_cells[i + 3][CHAIN_ID_LEN + j].cell(), // chunk[i].postStateRoot - hash_input_cells[i + 2][36 + j].cell(), + hash_input_cells[i + 2][CHAIN_ID_LEN + 32 + j].cell(), )?; } } // 2.5 assert hashes use a same chain id for i in 0..num_chunks { - for j in 0..4 { + for j in 0..CHAIN_ID_LEN { // sanity check assert_equal(&hash_input_cells[0][j], &hash_input_cells[i + 2][j]); region.constrain_equal( diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index 262fd65d33..b7e751c5a2 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -27,7 +27,7 @@ use crate::{ core::{assign_batch_hashes, extract_accumulators_and_proof}, param::{ConfigParams, BITS, LIMBS}, proof_aggregation::config::AggregationConfig, - BatchHashCircuit, ChunkHash, + BatchHashCircuit, ChunkHash, CHAIN_ID_LEN, }; /// Aggregation circuit that does not re-expose any public inputs from aggregated snarks @@ -329,19 +329,19 @@ impl Circuit for AggregationCircuit { for i in 0..32 { // first_chunk_prev_state_root layouter.constrain_instance( - hash_input_cells[2][4 + i].cell(), + hash_input_cells[2][CHAIN_ID_LEN + i].cell(), config.instance, i + acc_len, )?; // last_chunk_post_state_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[36 + i].cell(), + hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 32 + i].cell(), config.instance, i + 32 + acc_len, )?; // last_chunk_withdraw_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[68 + i].cell(), + hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 64 + i].cell(), config.instance, i + 64 + acc_len, )?; @@ -357,8 +357,8 @@ impl Circuit for AggregationCircuit { )?; } } - // last 4 inputs are the chain id - for i in 0..4 { + // last 8 inputs are the chain id + for i in 0..CHAIN_ID_LEN { layouter.constrain_instance( hash_input_cells[0][i].cell(), config.instance, diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/proof_aggregation/config.rs index 584a65a53c..f20fc3a1bf 100644 --- a/aggregator/src/proof_aggregation/config.rs +++ b/aggregator/src/proof_aggregation/config.rs @@ -29,7 +29,7 @@ pub struct AggregationConfig { pub keccak_circuit_config: KeccakCircuitConfig, /// Instance for public input; stores /// - accumulator from aggregation (12 elements) - /// - aggregated public inputs (132 elements): + /// - aggregated public inputs (136 elements): /// chain_id || /// chunk\[0\].prev_state_root || /// chunk\[k-1\].post_state_root || diff --git a/aggregator/src/proof_aggregation/public_input_aggregation.rs b/aggregator/src/proof_aggregation/public_input_aggregation.rs index 08fbc1f6ba..bf3653a43b 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation.rs @@ -34,7 +34,7 @@ //! - last_chunk_post_state_root: 32 Field elements //! - last_chunk_withdraw_root: 32 Field elements //! - batch_public_input_hash: 32 Field elements -//! - chain_id: 4 Field elements +//! - chain_id: 8 Field elements //! //! ## Constraints //! The circuit attests the following statements: @@ -66,6 +66,9 @@ pub use config::{BatchCircuitConfig, BatchCircuitConfigArgs}; // TODO(ZZ): update to the right degree pub(crate) const LOG_DEGREE: u32 = 19; +// A chain_id is u64 and uses 8 bytes +pub(crate) const CHAIN_ID_LEN: usize = 8; + // Each round requires (NUM_ROUNDS+1) * DEFAULT_KECCAK_ROWS = 300 rows. // This library is hard coded for this parameter. // Modifying the following parameters may result into bugs. diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs index dee651b31d..e4c8949c2d 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs @@ -9,7 +9,7 @@ use halo2_proofs::{ use zkevm_circuits::util::{Challenges, SubCircuitConfig}; -use crate::{core::assign_batch_hashes, BatchHash, ChunkHash}; +use crate::{core::assign_batch_hashes, BatchHash, ChunkHash, CHAIN_ID_LEN}; use super::config::{BatchCircuitConfig, BatchCircuitConfigArgs}; @@ -19,7 +19,7 @@ use super::config::{BatchCircuitConfig, BatchCircuitConfigArgs}; /// generate the circuit. #[derive(Clone, Debug, Default)] pub struct BatchHashCircuit { - pub(crate) chain_id: u32, + pub(crate) chain_id: u64, pub(crate) chunks: Vec, pub(crate) batch: BatchHash, _phantom: PhantomData, @@ -28,7 +28,7 @@ pub struct BatchHashCircuit { /// Public input to a batch circuit. /// In raw format. I.e., before converting to field elements. pub struct BatchHashCircuitPublicInput { - pub(crate) chain_id: u32, + pub(crate) chain_id: u64, pub(crate) first_chunk_prev_state_root: H256, pub(crate) last_chunk_post_state_root: H256, pub(crate) last_chunk_withdraw_root: H256, @@ -184,19 +184,19 @@ impl Circuit for BatchHashCircuit { for i in 0..32 { // first_chunk_prev_state_root layouter.constrain_instance( - hash_input_cells[2][4 + i].cell(), + hash_input_cells[2][CHAIN_ID_LEN + i].cell(), config.hash_digest_column, i, )?; // last_chunk_post_state_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[36 + i].cell(), + hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 32 + i].cell(), config.hash_digest_column, i + 32, )?; // last_chunk_withdraw_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[68 + i].cell(), + hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 64 + i].cell(), config.hash_digest_column, i + 64, )?; @@ -212,8 +212,8 @@ impl Circuit for BatchHashCircuit { )?; } } - // last 4 inputs are the chain id - for i in 0..4 { + // last 8 inputs are the chain id + for i in 0..CHAIN_ID_LEN { layouter.constrain_instance( hash_input_cells[0][i].cell(), config.hash_digest_column, diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs index 241badf009..3cf8c973c9 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs @@ -5,7 +5,7 @@ use crate::BatchHashCircuit; impl CircuitExt for BatchHashCircuit { fn num_instance(&self) -> Vec { - vec![132] + vec![136] } /// Compute the public inputs for this circuit. diff --git a/aggregator/src/tests/mock_chunk/circuit_ext.rs b/aggregator/src/tests/mock_chunk/circuit_ext.rs index a731eeef59..5528b7e654 100644 --- a/aggregator/src/tests/mock_chunk/circuit_ext.rs +++ b/aggregator/src/tests/mock_chunk/circuit_ext.rs @@ -1,22 +1,27 @@ use halo2_proofs::halo2curves::bn256::Fr; use snark_verifier_sdk::CircuitExt; +use crate::CHAIN_ID_LEN; + use super::MockChunkCircuit; impl CircuitExt for MockChunkCircuit { /// 64 elements from digest fn num_instance(&self) -> Vec { - vec![64] + vec![64 + CHAIN_ID_LEN] } /// return vec![data hash | public input hash] fn instances(&self) -> Vec> { - vec![self + vec![ + self.chain_id.to_le_bytes().iter().zip( + + self .chunk .data_hash .as_bytes() .iter() - .chain(self.chunk.public_input_hash().as_bytes().iter()) + .chain(self.chunk.public_input_hash().as_bytes().iter())) .map(|&x| Fr::from(x as u64)) .collect()] } diff --git a/aggregator/src/util.rs b/aggregator/src/util.rs index 940fe17c64..f4e36f0969 100644 --- a/aggregator/src/util.rs +++ b/aggregator/src/util.rs @@ -31,7 +31,9 @@ pub(crate) fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { for preimage in preimages.iter() { let num_rounds = 1 + preimage.len() / 136; - for (i, round) in preimage.chunks(136).enumerate() { + let mut preimage_padded = preimage.clone(); + preimage_padded.resize(136 * num_rounds, 0); + for (i, round) in preimage_padded.chunks(136).enumerate() { // indices for preimages for (j, _chunk) in round.chunks(8).into_iter().enumerate() { for k in 0..8 { diff --git a/aggregator/tests.sh b/aggregator/tests.sh old mode 100644 new mode 100755 From df40b50209cbc437b74dbc1a5a71ffbc87642524 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 20 Jun 2023 19:56:17 -0400 Subject: [PATCH 27/41] [fix] chain id len for tests --- aggregator/src/tests/aggregator_e2e.rs | 2 +- aggregator/src/tests/mock_chunk.rs | 1 + aggregator/src/tests/mock_chunk/circuit.rs | 1 + aggregator/src/tests/mock_chunk/circuit_ext.rs | 18 ++++++++++-------- aggregator/src/tests/mock_chunk/config.rs | 13 +++++++++---- aggregator/src/tests/proof_aggregation.rs | 2 +- aggregator/tests.sh | 2 +- 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/aggregator/src/tests/aggregator_e2e.rs b/aggregator/src/tests/aggregator_e2e.rs index 8023ea136f..5ec26a1f4b 100644 --- a/aggregator/src/tests/aggregator_e2e.rs +++ b/aggregator/src/tests/aggregator_e2e.rs @@ -53,7 +53,7 @@ fn test_e2e() { // Proof for test circuit let circuits = chunks .iter() - .map(|&chunk| MockChunkCircuit { chunk }) + .map(|&chunk| MockChunkCircuit { chain_id: 0, chunk }) .collect_vec(); let layer_0_snarks = circuits .iter() diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index fdca61007e..aa70e46047 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -16,6 +16,7 @@ mod config; /// - data hash /// - public input hash pub struct MockChunkCircuit { + pub(crate) chain_id: u64, pub(crate) chunk: ChunkHash, } diff --git a/aggregator/src/tests/mock_chunk/circuit.rs b/aggregator/src/tests/mock_chunk/circuit.rs index 2d795ff60f..31cb968ee3 100644 --- a/aggregator/src/tests/mock_chunk/circuit.rs +++ b/aggregator/src/tests/mock_chunk/circuit.rs @@ -16,6 +16,7 @@ use super::{ impl MockChunkCircuit { pub(crate) fn random(r: &mut R) -> Self { Self { + chain_id: 0, chunk: ChunkHash::mock_chunk_hash(r), } } diff --git a/aggregator/src/tests/mock_chunk/circuit_ext.rs b/aggregator/src/tests/mock_chunk/circuit_ext.rs index 5528b7e654..3d270c98fe 100644 --- a/aggregator/src/tests/mock_chunk/circuit_ext.rs +++ b/aggregator/src/tests/mock_chunk/circuit_ext.rs @@ -13,15 +13,17 @@ impl CircuitExt for MockChunkCircuit { /// return vec![data hash | public input hash] fn instances(&self) -> Vec> { - vec![ - self.chain_id.to_le_bytes().iter().zip( - - self - .chunk - .data_hash - .as_bytes() + vec![self + .chain_id + .to_le_bytes() .iter() - .chain(self.chunk.public_input_hash().as_bytes().iter())) + .chain( + self.chunk + .data_hash + .as_bytes() + .iter() + .chain(self.chunk.public_input_hash().as_bytes().iter()), + ) .map(|&x| Fr::from(x as u64)) .collect()] } diff --git a/aggregator/src/tests/mock_chunk/config.rs b/aggregator/src/tests/mock_chunk/config.rs index c1509c817e..8bd49cbe02 100644 --- a/aggregator/src/tests/mock_chunk/config.rs +++ b/aggregator/src/tests/mock_chunk/config.rs @@ -14,7 +14,7 @@ use zkevm_circuits::{ use crate::{ util::{capacity, get_indices}, - LOG_DEGREE, + CHAIN_ID_LEN, LOG_DEGREE, }; /// Config for MockChunkCircuit @@ -152,9 +152,9 @@ impl MockChunkCircuitConfig { // chunk's data hash for i in 0..32 { layouter.constrain_instance( - hash_input_cells[i + 100].cell(), + hash_input_cells[i + 96 + CHAIN_ID_LEN].cell(), self.hash_digest_column, - i, + i + CHAIN_ID_LEN, )?; } // chunk's public_input_hash @@ -164,10 +164,15 @@ impl MockChunkCircuitConfig { layouter.constrain_instance( hash_output_cells[(3 - i) * 8 + j].cell(), self.hash_digest_column, - i * 8 + j + 32, + i * 8 + j + 32 + CHAIN_ID_LEN, )?; } } + // chain id + for i in 0..CHAIN_ID_LEN { + layouter.constrain_instance(hash_input_cells[i].cell(), self.hash_digest_column, i)?; + } + Ok(hash_output_cells) } } diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index c6febfbd82..fa9c42d84e 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -43,7 +43,7 @@ fn test_aggregation_circuit() { // Proof for test circuit let circuits = chunks .iter() - .map(|&chunk| MockChunkCircuit { chunk }) + .map(|&chunk| MockChunkCircuit { chain_id: 0, chunk }) .collect_vec(); let layer_0_snarks = circuits .iter() diff --git a/aggregator/tests.sh b/aggregator/tests.sh index dbed5bfdcc..77a9958704 100755 --- a/aggregator/tests.sh +++ b/aggregator/tests.sh @@ -4,7 +4,7 @@ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_moc # the following 3 tests takes super long time -# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_proof_compression -- --nocapture 2>&1 | tee mock_chunk.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --ignored --nocapture 2>&1 | tee compression2.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_aggregation_circuit -- --ignored --nocapture 2>&1 | tee aggregation.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_e2e -- --ignored --nocapture 2>&1 | tee aggregation.log From a3f4aef066c148fe651427e64340801454221c5f Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 20 Jun 2023 20:24:35 -0400 Subject: [PATCH 28/41] [fix] typo in scripts --- aggregator/tests.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aggregator/tests.sh b/aggregator/tests.sh index 77a9958704..2d342d3a79 100755 --- a/aggregator/tests.sh +++ b/aggregator/tests.sh @@ -2,9 +2,8 @@ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_real_prover -- --nocapture 2>&1 | tee pi_real.log RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log - -# the following 3 tests takes super long time -# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_proof_compression -- --nocapture 2>&1 | tee mock_chunk.log +# the following 4 tests takes super long time +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_proof_compression -- --nocapture 2>&1 | tee compression.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --ignored --nocapture 2>&1 | tee compression2.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_aggregation_circuit -- --ignored --nocapture 2>&1 | tee aggregation.log # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_e2e -- --ignored --nocapture 2>&1 | tee aggregation.log From a6d627de6a3c203c4ac350d5eef1a6f85d6c4ce1 Mon Sep 17 00:00:00 2001 From: Zhang Zhuo Date: Wed, 21 Jun 2023 10:10:25 +0800 Subject: [PATCH 29/41] lint --- aggregator/src/tests/mock_chunk/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aggregator/src/tests/mock_chunk/config.rs b/aggregator/src/tests/mock_chunk/config.rs index 8bd49cbe02..fcb061d5b5 100644 --- a/aggregator/src/tests/mock_chunk/config.rs +++ b/aggregator/src/tests/mock_chunk/config.rs @@ -169,8 +169,8 @@ impl MockChunkCircuitConfig { } } // chain id - for i in 0..CHAIN_ID_LEN { - layouter.constrain_instance(hash_input_cells[i].cell(), self.hash_digest_column, i)?; + for (i, cell) in hash_input_cells.iter().enumerate().take(CHAIN_ID_LEN) { + layouter.constrain_instance(cell.cell(), self.hash_digest_column, i)?; } Ok(hash_output_cells) From 3dbdc4cf77c6e573663ff426279fba45005d6d02 Mon Sep 17 00:00:00 2001 From: Wenqing Hu Date: Wed, 21 Jun 2023 11:16:14 -0500 Subject: [PATCH 30/41] a few comments on proof aggregation circuit (#558) * a few comments inside core function for proof compression: extract_accumulators_and_proof * comments on proof compression circuit * comments on pi aggregation circuit, especially calculation of hash indexes when using Keccak circuit, which are hard-coded here so needs be very careful * comments on proof aggregation circuit * a few comments inside core function for proof compression: extract_accumulators_and_proof * comments on proof compression circuit * comments on pi aggregation circuit, especially calculation of hash indexes when using Keccak circuit, which are hard-coded here so needs be very careful * comments on proof aggregation circuit * clean up all commits from my side * clean up of all commits on my side --- aggregator/src/core.rs | 28 +++++++++++++++++++++ aggregator/src/proof_aggregation/circuit.rs | 13 ++++++++-- aggregator/src/proof_aggregation/config.rs | 5 ++-- aggregator/src/proof_compression/circuit.rs | 6 +++++ aggregator/src/util.rs | 6 +++++ 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index fa0f22bd0f..e83af24358 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -50,6 +50,20 @@ pub(crate) fn assign_batch_hashes( let num_rows = 1 << LOG_DEGREE; let timer = start_timer!(|| ("multi keccak").to_string()); + // wenqing: preimages consists of the following parts + // (1) batchPiHash preimage = + // (chain_id || + // chunk[0].prev_state_root || + // chunk[k-1].post_state_root || + // chunk[k-1].withdraw_root || + // batch_data_hash) + // (2) batchDataHash preimage = + // (chunk[0].dataHash || ... || chunk[k-1].dataHash) + // (3) chunk[i].piHash preimage = + // (chain id || + // chunk[i].prevStateRoot || chunk[i].postStateRoot || + // chunk[i].withdrawRoot || chunk[i].datahash) + // each part of the preimage is mapped to image by Keccak256 let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; end_timer!(timer); @@ -84,10 +98,12 @@ pub(crate) fn assign_batch_hashes( let row = config.set_row(&mut region, offset, keccak_row)?; if cur_preimage_index.is_some() && *cur_preimage_index.unwrap() == offset { + // wenqing: 7-th column is Keccak input in Keccak circuit current_hash_input_cells.push(row[6].clone()); cur_preimage_index = preimage_indices_iter.next(); } if cur_digest_index.is_some() && *cur_digest_index.unwrap() == offset { + // wenqing: last column is Keccak output in Keccak circuit current_hash_output_cells.push(row.last().unwrap().clone()); cur_digest_index = digest_indices_iter.next(); } @@ -126,6 +142,7 @@ pub(crate) fn assign_batch_hashes( for i in 0..4 { for j in 0..8 { // sanity check + // wenqing: 96 + CHAIN_ID_LEN is the byte position for batch_data_hash assert_equal( &hash_input_cells[0][i * 8 + j + 96 + CHAIN_ID_LEN], &hash_output_cells[1][(3 - i) * 8 + j], @@ -155,6 +172,10 @@ pub(crate) fn assign_batch_hashes( // chunk[i].postStateRoot || // chunk[i].withdrawRoot || // chunk[i].datahash) + // wenqing: CHAIN_ID_LEN, + // CHAIN_ID_LEN+32, + // CHAIN_ID_LEN+64 used below are byte positions for + // prev_state_root, post_state_root, withdraw_root for i in 0..32 { // 2.2.1 chunk[0].prev_state_root // sanity check @@ -271,6 +292,8 @@ pub(crate) fn extract_accumulators_and_proof( &snark.instances, &mut transcript_read, ); + // wenqing: each accumulator has (lhs, rhs) based on Shplonk + // lhs and rhs are EC points Shplonk::succinct_verify(&svk, &snark.protocol, &snark.instances, &proof) }) .collect::>(); @@ -279,6 +302,11 @@ pub(crate) fn extract_accumulators_and_proof( PoseidonTranscript::>::from_spec(vec![], POSEIDON_SPEC.clone()); // We always use SHPLONK for accumulation scheme when aggregating proofs let accumulator = + // wenqing: core step + // KzgAs does KZG accumulation scheme based on given accumulators and random number (for adding blinding) + // accumulated ec_pt = ec_pt_1 * 1 + ec_pt_2 * r + ... + ec_pt_n * r^{n-1} + // ec_pt can be lhs and rhs + // r is the challenge squeezed from proof KzgAs::>::create_proof::>, _>( &Default::default(), &accumulators, diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index b7e751c5a2..18e2c68b8f 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -68,6 +68,11 @@ impl AggregationCircuit { let snark_hash_bytes = &snark.instances[0]; for i in 0..32 { + // wenqing: for each snark, + // first 12 elements are accumulator + // next 32 elements are data hash (44=12+32) + // next 32 elements are public_input_hash + // data hash + public_input_hash = snark public input assert_eq!( Fr::from(chunk.data_hash.as_bytes()[i] as u64), snark_hash_bytes[i + 12] @@ -83,6 +88,8 @@ impl AggregationCircuit { // extract the accumulators and proofs let svk = params.get_g()[0].into(); + // wenqing: this aggregates MULTIPLE snarks + // (instead of ONE as in proof compression) let (accumulator, as_proof) = extract_accumulators_and_proof(params, snarks, rng); let KzgAccumulator:: { lhs, rhs } = accumulator; let acc_instances = [lhs.x, lhs.y, rhs.x, rhs.y] @@ -164,7 +171,7 @@ impl Circuit for AggregationCircuit { // re-export all the public input of the snarks, denoted by [snarks_instances], and the // accumulator [acc_instances] // - 2. use public input aggregation circuit to aggregate the chunks; expose the instance - // dentoed by [pi_agg_instances] + // denoted by [pi_agg_instances] // - 3. assert [snarks_instances] are private inputs used for public input aggregation // circuit @@ -195,7 +202,7 @@ impl Circuit for AggregationCircuit { // // extract the assigned values for // - instances which are the public inputs of each chunk (prefixed with 12 instances - // from previous accumualtors) + // from previous accumulators) // - new accumulator to be verified on chain // let (assigned_aggregation_instances, acc) = aggregate::>( @@ -350,6 +357,7 @@ impl Circuit for AggregationCircuit { for i in 0..4 { for j in 0..8 { // digest in circuit has a different endianness + // wenqing: 96 is the byte position for batch data hash layouter.constrain_instance( hash_output_cells[0][(3 - i) * 8 + j].cell(), config.instance, @@ -358,6 +366,7 @@ impl Circuit for AggregationCircuit { } } // last 8 inputs are the chain id + // wenqing: chain_id is put at last here for i in 0..CHAIN_ID_LEN { layouter.constrain_instance( hash_input_cells[0][i].cell(), diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/proof_aggregation/config.rs index f20fc3a1bf..480651ec33 100644 --- a/aggregator/src/proof_aggregation/config.rs +++ b/aggregator/src/proof_aggregation/config.rs @@ -30,11 +30,12 @@ pub struct AggregationConfig { /// Instance for public input; stores /// - accumulator from aggregation (12 elements) /// - aggregated public inputs (136 elements): - /// chain_id || /// chunk\[0\].prev_state_root || /// chunk\[k-1\].post_state_root || /// chunk\[k-1\].withdraw_root || - /// batch_data_hash + /// batch_data_hash || + /// chain_id + /// wenqing: chain_id is put at last here for instance pub instance: Column, } diff --git a/aggregator/src/proof_compression/circuit.rs b/aggregator/src/proof_compression/circuit.rs index 52b17f325c..cf8e6b5f2b 100644 --- a/aggregator/src/proof_compression/circuit.rs +++ b/aggregator/src/proof_compression/circuit.rs @@ -84,6 +84,8 @@ impl Circuit for CompressionCircuit { ) .unwrap(); + // wenqing: circuit configuration is built from config with given num columns etc + // can be wide or thin circuit Self::Config::configure(meta, params) } @@ -166,6 +168,10 @@ impl CompressionCircuit { ) -> Self { let svk = params.get_g()[0].into(); + // wenqing: for the proof compression, only ONE snark is under accumulation + // it is turned into an accumulator via KzgAs accumulation scheme + // in case not first time: + // (old_accumulator, public inputs) -> (new_accumulator, public inputs) let (accumulator, as_proof) = extract_accumulators_and_proof(params, &[snark.clone()], rng); // the instance for the outer circuit is diff --git a/aggregator/src/util.rs b/aggregator/src/util.rs index f4e36f0969..8604a293ba 100644 --- a/aggregator/src/util.rs +++ b/aggregator/src/util.rs @@ -30,6 +30,12 @@ pub(crate) fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { let mut round_ctr = 0; for preimage in preimages.iter() { + // wenqing: 136 = 17 * 8 is the size in bits of each + // input chunk that can be processed by Keccak circuit using absorb + // each chunk of size 136 needs 300 Keccak circuit rows to prove + // which consists of 12 Keccak rows for each of 24 + 1 Keccak cicuit rounds + // digest only happens at the end of the last input chunk with + // 4 Keccak circuit rounds, so 48 Keccak rows, and 300 - 48 = 256 let num_rounds = 1 + preimage.len() / 136; let mut preimage_padded = preimage.clone(); preimage_padded.resize(136 * num_rounds, 0); From c01fcfe04fea7989dcc19993a8239fca1d297ec8 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 21 Jun 2023 12:41:49 -0400 Subject: [PATCH 31/41] [feat] parameterize hard coded constants --- aggregator/src/core.rs | 67 ++++++++++--------- aggregator/src/proof_aggregation/circuit.rs | 17 ++--- aggregator/src/proof_aggregation/config.rs | 2 +- .../public_input_aggregation.rs | 14 ++++ .../public_input_aggregation/circuit.rs | 11 +-- aggregator/src/proof_compression/circuit.rs | 6 +- aggregator/src/util.rs | 4 +- 7 files changed, 70 insertions(+), 51 deletions(-) diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index e83af24358..33db582f63 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -27,7 +27,8 @@ use zkevm_circuits::{ use crate::{ util::{assert_equal, capacity, get_indices}, - CHAIN_ID_LEN, LOG_DEGREE, + CHAIN_ID_LEN, CHUNK_DATA_HASH_INDEX, LOG_DEGREE, POST_STATE_ROOT_INDEX, PREV_STATE_ROOT_INDEX, + WITHDRAW_ROOT_INDEX, }; /// Input the hash input bytes, @@ -50,18 +51,18 @@ pub(crate) fn assign_batch_hashes( let num_rows = 1 << LOG_DEGREE; let timer = start_timer!(|| ("multi keccak").to_string()); - // wenqing: preimages consists of the following parts + // preimages consists of the following parts // (1) batchPiHash preimage = // (chain_id || // chunk[0].prev_state_root || // chunk[k-1].post_state_root || // chunk[k-1].withdraw_root || // batch_data_hash) - // (2) batchDataHash preimage = + // (2) batchDataHash preimage = // (chunk[0].dataHash || ... || chunk[k-1].dataHash) // (3) chunk[i].piHash preimage = // (chain id || - // chunk[i].prevStateRoot || chunk[i].postStateRoot || + // chunk[i].prevStateRoot || chunk[i].postStateRoot || // chunk[i].withdrawRoot || chunk[i].datahash) // each part of the preimage is mapped to image by Keccak256 let witness = multi_keccak(preimages, challenges, capacity(num_rows))?; @@ -98,12 +99,12 @@ pub(crate) fn assign_batch_hashes( let row = config.set_row(&mut region, offset, keccak_row)?; if cur_preimage_index.is_some() && *cur_preimage_index.unwrap() == offset { - // wenqing: 7-th column is Keccak input in Keccak circuit + // 7-th column is Keccak input in Keccak circuit current_hash_input_cells.push(row[6].clone()); cur_preimage_index = preimage_indices_iter.next(); } if cur_digest_index.is_some() && *cur_digest_index.unwrap() == offset { - // wenqing: last column is Keccak output in Keccak circuit + // last column is Keccak output in Keccak circuit current_hash_output_cells.push(row.last().unwrap().clone()); cur_digest_index = digest_indices_iter.next(); } @@ -142,14 +143,14 @@ pub(crate) fn assign_batch_hashes( for i in 0..4 { for j in 0..8 { // sanity check - // wenqing: 96 + CHAIN_ID_LEN is the byte position for batch_data_hash + // CHUNK_DATA_HASH_INDEX is the byte position for batch_data_hash assert_equal( - &hash_input_cells[0][i * 8 + j + 96 + CHAIN_ID_LEN], + &hash_input_cells[0][i * 8 + j + CHUNK_DATA_HASH_INDEX], &hash_output_cells[1][(3 - i) * 8 + j], ); region.constrain_equal( // preimage and digest has different endianness - hash_input_cells[0][i * 8 + j + 96 + CHAIN_ID_LEN].cell(), + hash_input_cells[0][i * 8 + j + CHUNK_DATA_HASH_INDEX].cell(), hash_output_cells[1][(3 - i) * 8 + j].cell(), )?; } @@ -172,39 +173,39 @@ pub(crate) fn assign_batch_hashes( // chunk[i].postStateRoot || // chunk[i].withdrawRoot || // chunk[i].datahash) - // wenqing: CHAIN_ID_LEN, - // CHAIN_ID_LEN+32, - // CHAIN_ID_LEN+64 used below are byte positions for - // prev_state_root, post_state_root, withdraw_root + // + // PREV_STATE_ROOT_INDEX, POST_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX + // used below are byte positions for + // prev_state_root, post_state_root, withdraw_root for i in 0..32 { // 2.2.1 chunk[0].prev_state_root // sanity check assert_equal( - &hash_input_cells[0][i + CHAIN_ID_LEN], - &hash_input_cells[2][i + CHAIN_ID_LEN], + &hash_input_cells[0][i + PREV_STATE_ROOT_INDEX], + &hash_input_cells[2][i + PREV_STATE_ROOT_INDEX], ); region.constrain_equal( - hash_input_cells[0][i + CHAIN_ID_LEN].cell(), - hash_input_cells[2][i + CHAIN_ID_LEN].cell(), + hash_input_cells[0][i + PREV_STATE_ROOT_INDEX].cell(), + hash_input_cells[2][i + PREV_STATE_ROOT_INDEX].cell(), )?; // 2.2.2 chunk[k-1].post_state_root // sanity check assert_equal( - &hash_input_cells[0][i + CHAIN_ID_LEN + 32], - &hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 32], + &hash_input_cells[0][i + POST_STATE_ROOT_INDEX], + &hash_input_cells[hash_num - 1][i + POST_STATE_ROOT_INDEX], ); region.constrain_equal( - hash_input_cells[0][i + CHAIN_ID_LEN + 32].cell(), - hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 32].cell(), + hash_input_cells[0][i + POST_STATE_ROOT_INDEX].cell(), + hash_input_cells[hash_num - 1][i + POST_STATE_ROOT_INDEX].cell(), )?; // 2.2.3 chunk[k-1].withdraw_root assert_equal( - &hash_input_cells[0][i + CHAIN_ID_LEN + 64], - &hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 64], + &hash_input_cells[0][i + WITHDRAW_ROOT_INDEX], + &hash_input_cells[hash_num - 1][i + WITHDRAW_ROOT_INDEX], ); region.constrain_equal( - hash_input_cells[0][i + CHAIN_ID_LEN + 64].cell(), - hash_input_cells[hash_num - 1][i + CHAIN_ID_LEN + 64].cell(), + hash_input_cells[0][i + WITHDRAW_ROOT_INDEX].cell(), + hash_input_cells[hash_num - 1][i + WITHDRAW_ROOT_INDEX].cell(), )?; } @@ -222,10 +223,10 @@ pub(crate) fn assign_batch_hashes( for (i, chunk) in hash_input_cells[1].chunks(32).enumerate().take(num_chunks) { for (j, cell) in chunk.iter().enumerate() { // sanity check - assert_equal(cell, &hash_input_cells[2 + i][j + CHAIN_ID_LEN + 96]); + assert_equal(cell, &hash_input_cells[2 + i][j + CHUNK_DATA_HASH_INDEX]); region.constrain_equal( cell.cell(), - hash_input_cells[2 + i][j + CHAIN_ID_LEN + 96].cell(), + hash_input_cells[2 + i][j + CHUNK_DATA_HASH_INDEX].cell(), )?; } } @@ -235,14 +236,14 @@ pub(crate) fn assign_batch_hashes( for j in 0..32 { // sanity check assert_equal( - &hash_input_cells[i + 3][CHAIN_ID_LEN + j], - &hash_input_cells[i + 2][CHAIN_ID_LEN + 32 + j], + &hash_input_cells[i + 3][PREV_STATE_ROOT_INDEX + j], + &hash_input_cells[i + 2][POST_STATE_ROOT_INDEX + j], ); region.constrain_equal( // chunk[i+1].prevStateRoot - hash_input_cells[i + 3][CHAIN_ID_LEN + j].cell(), + hash_input_cells[i + 3][PREV_STATE_ROOT_INDEX + j].cell(), // chunk[i].postStateRoot - hash_input_cells[i + 2][CHAIN_ID_LEN + 32 + j].cell(), + hash_input_cells[i + 2][POST_STATE_ROOT_INDEX + j].cell(), )?; } } @@ -292,7 +293,7 @@ pub(crate) fn extract_accumulators_and_proof( &snark.instances, &mut transcript_read, ); - // wenqing: each accumulator has (lhs, rhs) based on Shplonk + // each accumulator has (lhs, rhs) based on Shplonk // lhs and rhs are EC points Shplonk::succinct_verify(&svk, &snark.protocol, &snark.instances, &proof) }) @@ -302,7 +303,7 @@ pub(crate) fn extract_accumulators_and_proof( PoseidonTranscript::>::from_spec(vec![], POSEIDON_SPEC.clone()); // We always use SHPLONK for accumulation scheme when aggregating proofs let accumulator = - // wenqing: core step + // core step // KzgAs does KZG accumulation scheme based on given accumulators and random number (for adding blinding) // accumulated ec_pt = ec_pt_1 * 1 + ec_pt_2 * r + ... + ec_pt_n * r^{n-1} // ec_pt can be lhs and rhs diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index 18e2c68b8f..bdf9ae2a88 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -27,7 +27,8 @@ use crate::{ core::{assign_batch_hashes, extract_accumulators_and_proof}, param::{ConfigParams, BITS, LIMBS}, proof_aggregation::config::AggregationConfig, - BatchHashCircuit, ChunkHash, CHAIN_ID_LEN, + BatchHashCircuit, ChunkHash, CHAIN_ID_LEN, POST_STATE_ROOT_INDEX, PREV_STATE_ROOT_INDEX, + WITHDRAW_ROOT_INDEX, }; /// Aggregation circuit that does not re-expose any public inputs from aggregated snarks @@ -68,7 +69,7 @@ impl AggregationCircuit { let snark_hash_bytes = &snark.instances[0]; for i in 0..32 { - // wenqing: for each snark, + // for each snark, // first 12 elements are accumulator // next 32 elements are data hash (44=12+32) // next 32 elements are public_input_hash @@ -88,7 +89,7 @@ impl AggregationCircuit { // extract the accumulators and proofs let svk = params.get_g()[0].into(); - // wenqing: this aggregates MULTIPLE snarks + // this aggregates MULTIPLE snarks // (instead of ONE as in proof compression) let (accumulator, as_proof) = extract_accumulators_and_proof(params, snarks, rng); let KzgAccumulator:: { lhs, rhs } = accumulator; @@ -336,19 +337,19 @@ impl Circuit for AggregationCircuit { for i in 0..32 { // first_chunk_prev_state_root layouter.constrain_instance( - hash_input_cells[2][CHAIN_ID_LEN + i].cell(), + hash_input_cells[2][PREV_STATE_ROOT_INDEX + i].cell(), config.instance, i + acc_len, )?; // last_chunk_post_state_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 32 + i].cell(), + hash_input_cells.last().unwrap()[POST_STATE_ROOT_INDEX + i].cell(), config.instance, i + 32 + acc_len, )?; // last_chunk_withdraw_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 64 + i].cell(), + hash_input_cells.last().unwrap()[WITHDRAW_ROOT_INDEX + i].cell(), config.instance, i + 64 + acc_len, )?; @@ -357,7 +358,7 @@ impl Circuit for AggregationCircuit { for i in 0..4 { for j in 0..8 { // digest in circuit has a different endianness - // wenqing: 96 is the byte position for batch data hash + // 96 is the byte position for batch data hash layouter.constrain_instance( hash_output_cells[0][(3 - i) * 8 + j].cell(), config.instance, @@ -366,7 +367,7 @@ impl Circuit for AggregationCircuit { } } // last 8 inputs are the chain id - // wenqing: chain_id is put at last here + // chain_id is put at last here for i in 0..CHAIN_ID_LEN { layouter.constrain_instance( hash_input_cells[0][i].cell(), diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/proof_aggregation/config.rs index 480651ec33..b0e2b827e7 100644 --- a/aggregator/src/proof_aggregation/config.rs +++ b/aggregator/src/proof_aggregation/config.rs @@ -35,7 +35,7 @@ pub struct AggregationConfig { /// chunk\[k-1\].withdraw_root || /// batch_data_hash || /// chain_id - /// wenqing: chain_id is put at last here for instance + /// chain_id is put at last here for instance pub instance: Column, } diff --git a/aggregator/src/proof_aggregation/public_input_aggregation.rs b/aggregator/src/proof_aggregation/public_input_aggregation.rs index bf3653a43b..e0fb7b13fa 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation.rs @@ -66,8 +66,22 @@ pub use config::{BatchCircuitConfig, BatchCircuitConfigArgs}; // TODO(ZZ): update to the right degree pub(crate) const LOG_DEGREE: u32 = 19; +// ================================ +// indices for hash bytes +// +// 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 +// // A chain_id is u64 and uses 8 bytes pub(crate) const CHAIN_ID_LEN: usize = 8; +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; // Each round requires (NUM_ROUNDS+1) * DEFAULT_KECCAK_ROWS = 300 rows. // This library is hard coded for this parameter. diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs index e4c8949c2d..ba0a385e33 100644 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs @@ -9,7 +9,10 @@ use halo2_proofs::{ use zkevm_circuits::util::{Challenges, SubCircuitConfig}; -use crate::{core::assign_batch_hashes, BatchHash, ChunkHash, CHAIN_ID_LEN}; +use crate::{ + core::assign_batch_hashes, BatchHash, ChunkHash, CHAIN_ID_LEN, POST_STATE_ROOT_INDEX, + PREV_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX, +}; use super::config::{BatchCircuitConfig, BatchCircuitConfigArgs}; @@ -184,19 +187,19 @@ impl Circuit for BatchHashCircuit { for i in 0..32 { // first_chunk_prev_state_root layouter.constrain_instance( - hash_input_cells[2][CHAIN_ID_LEN + i].cell(), + hash_input_cells[2][PREV_STATE_ROOT_INDEX + i].cell(), config.hash_digest_column, i, )?; // last_chunk_post_state_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 32 + i].cell(), + hash_input_cells.last().unwrap()[POST_STATE_ROOT_INDEX + i].cell(), config.hash_digest_column, i + 32, )?; // last_chunk_withdraw_root layouter.constrain_instance( - hash_input_cells.last().unwrap()[CHAIN_ID_LEN + 64 + i].cell(), + hash_input_cells.last().unwrap()[WITHDRAW_ROOT_INDEX + i].cell(), config.hash_digest_column, i + 64, )?; diff --git a/aggregator/src/proof_compression/circuit.rs b/aggregator/src/proof_compression/circuit.rs index cf8e6b5f2b..2158cfd9ce 100644 --- a/aggregator/src/proof_compression/circuit.rs +++ b/aggregator/src/proof_compression/circuit.rs @@ -84,7 +84,7 @@ impl Circuit for CompressionCircuit { ) .unwrap(); - // wenqing: circuit configuration is built from config with given num columns etc + // circuit configuration is built from config with given num columns etc // can be wide or thin circuit Self::Config::configure(meta, params) } @@ -168,9 +168,9 @@ impl CompressionCircuit { ) -> Self { let svk = params.get_g()[0].into(); - // wenqing: for the proof compression, only ONE snark is under accumulation + // for the proof compression, only ONE snark is under accumulation // it is turned into an accumulator via KzgAs accumulation scheme - // in case not first time: + // in case not first time: // (old_accumulator, public inputs) -> (new_accumulator, public inputs) let (accumulator, as_proof) = extract_accumulators_and_proof(params, &[snark.clone()], rng); diff --git a/aggregator/src/util.rs b/aggregator/src/util.rs index 8604a293ba..301e30a9b8 100644 --- a/aggregator/src/util.rs +++ b/aggregator/src/util.rs @@ -30,11 +30,11 @@ pub(crate) fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { let mut round_ctr = 0; for preimage in preimages.iter() { - // wenqing: 136 = 17 * 8 is the size in bits of each + // 136 = 17 * 8 is the size in bits of each // input chunk that can be processed by Keccak circuit using absorb // each chunk of size 136 needs 300 Keccak circuit rows to prove // which consists of 12 Keccak rows for each of 24 + 1 Keccak cicuit rounds - // digest only happens at the end of the last input chunk with + // digest only happens at the end of the last input chunk with // 4 Keccak circuit rounds, so 48 Keccak rows, and 300 - 48 = 256 let num_rounds = 1 + preimage.len() / 136; let mut preimage_padded = preimage.clone(); From b68e644b90cebbe3b73ff1e4c59749c674c293cc Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 21 Jun 2023 12:48:57 -0400 Subject: [PATCH 32/41] [fix] compiling after merge --- zkevm-circuits/src/sig_circuit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zkevm-circuits/src/sig_circuit.rs b/zkevm-circuits/src/sig_circuit.rs index 0a044cd549..f976386b33 100644 --- a/zkevm-circuits/src/sig_circuit.rs +++ b/zkevm-circuits/src/sig_circuit.rs @@ -670,7 +670,7 @@ impl SigCircuit { let pk_hash_rlc = rlc_chip.gate.inner_product( ctx, sign_data_decomposed.pk_hash_cells.clone(), - evm_challenge_powers, + evm_challenge_powers.clone(), ); // step 4: r,s rlc From 65912f6fa9d2363f524503fa86f14d88cb0b1c9d Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 22 Jun 2023 09:41:59 -0400 Subject: [PATCH 33/41] [refactor] remove pi agg circuit; integrated into proof agg circuit --- aggregator/src/batch.rs | 142 ++++++++++- aggregator/src/proof_aggregation.rs | 31 ++- aggregator/src/proof_aggregation/circuit.rs | 48 +--- .../src/proof_aggregation/circuit_ext.rs | 6 +- aggregator/src/proof_aggregation/config.rs | 8 +- .../public_input_aggregation.rs | 92 ------- .../public_input_aggregation/circuit.rs | 230 ------------------ .../public_input_aggregation/circuit_ext.rs | 49 ---- .../public_input_aggregation/config.rs | 67 ----- aggregator/src/tests.rs | 1 - .../src/tests/public_input_aggregation.rs | 45 ---- aggregator/tests.sh | 2 - 12 files changed, 181 insertions(+), 540 deletions(-) delete mode 100644 aggregator/src/proof_aggregation/public_input_aggregation.rs delete mode 100644 aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs delete mode 100644 aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs delete mode 100644 aggregator/src/proof_aggregation/public_input_aggregation/config.rs delete mode 100644 aggregator/src/tests/public_input_aggregation.rs diff --git a/aggregator/src/batch.rs b/aggregator/src/batch.rs index 85d7f4a293..226608bc28 100644 --- a/aggregator/src/batch.rs +++ b/aggregator/src/batch.rs @@ -1,4 +1,50 @@ -use eth_types::H256; +//! This module implements related functions that aggregates public inputs of many chunks into a +//! single one. +//! +//! # Spec +//! +//! A chunk is a list of continuous blocks. It consists of 4 hashes: +//! - state root before this chunk +//! - state root after this chunk +//! - the withdraw root of this chunk +//! - the data hash of this chunk +//! Those 4 hashes are obtained from the caller. +//! +//! A chunk's public input hash is then derived from the above 4 attributes via +//! +//! - chunk_pi_hash := keccak(chain_id || prev_state_root || post_state_root || withdraw_root || +//! chunk_data_hash) +//! +//! A batch is a list of continuous chunks. It consists of 2 hashes +//! +//! - batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) +//! +//! - batch_pi_hash := keccak(chain_id || chunk_0.prev_state_root || chunk_k-1.post_state_root || +//! chunk_k-1.withdraw_root || batch_data_hash) +//! +//! Note that chain_id is used for all public input hashes. But not for any data hashes. +//! +//! # Circuit +//! +//! A BatchHashCircuit asserts that the batch is well-formed. +//! +//! ## Public Input +//! The public inputs of the circuit (32 Field elements) is constructed as +//! - batch_pi_hash: 32 Field elements +//! +//! ## Constraints +//! The circuit attests the following statements: +//! +//! 1. all hashes are computed correctly +//! 2. the relations between hash preimages and digests are satisfied +//! - batch_data_hash is part of the input to compute batch_pi_hash +//! - batch_pi_hash used same roots as chunk_pi_hash +//! - same data_hash is used to compute batch_data_hash and chunk_pi_hash for all chunks +//! - chunks are continuous: they are linked via the state roots +//! - all hashes uses a same chain_id +//! 3. the batch_pi_hash matches the circuit's public input (32 field elements) above + +use eth_types::{Field, H256}; use ethers_core::utils::keccak256; use super::chunk::ChunkHash; @@ -6,14 +52,34 @@ use super::chunk::ChunkHash; #[derive(Default, Debug, Clone)] /// A batch is a set of continuous chunks. /// A BatchHash consists of 2 hashes. +/// - batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) +/// - batch_pi_hash := keccak(chain_id || chunk_0.prev_state_root || chunk_k-1.post_state_root || +/// chunk_k-1.withdraw_root || batch_data_hash) pub struct BatchHash { + pub(crate) chain_id: u64, + pub(crate) chunks: Vec, pub(crate) data_hash: H256, pub(crate) public_input_hash: H256, } impl BatchHash { + /// Sample a batch hash circuit from random (for testing) + #[cfg(test)] + pub(crate) fn mock_batch_hash_circuit(r: &mut R, size: usize) -> Self { + let mut chunks = (0..size) + .map(|_| ChunkHash::mock_chunk_hash(r)) + .collect::>(); + for i in 0..size - 1 { + chunks[i + 1].prev_state_root = chunks[i].post_state_root; + } + + Self::construct(&chunks) + } + /// Build Batch hash from a list of chunks pub(crate) fn construct(chunk_hashes: &[ChunkHash]) -> Self { + assert!(!chunk_hashes.is_empty(), "input chunk slice is empty"); + // sanity: the chunks are continuous for i in 0..chunk_hashes.len() - 1 { assert_eq!( @@ -50,8 +116,82 @@ impl BatchHash { let public_input_hash = keccak256(preimage); Self { + chain_id: chunk_hashes[0].chain_id, + chunks: chunk_hashes.to_vec(), data_hash: data_hash.into(), public_input_hash: public_input_hash.into(), } } + + /// Extract all the hash inputs that will ever be used + /// orders: + /// - batch_public_input_hash + /// - batch_data_hash_preimage + /// - chunk\[i\].piHash for i in \[0, k) + pub(crate) fn extract_hash_preimages(&self) -> Vec> { + let mut res = vec![]; + + // batchPiHash = + // keccak( + // chain_id || + // chunk[0].prev_state_root || + // chunk[k-1].post_state_root || + // chunk[k-1].withdraw_root || + // batch_data_hash ) + let batch_public_input_hash_preimage = [ + self.chain_id.to_le_bytes().as_ref(), + self.chunks[0].prev_state_root.as_bytes(), + self.chunks.last().unwrap().post_state_root.as_bytes(), + self.chunks.last().unwrap().withdraw_root.as_bytes(), + self.data_hash.as_bytes(), + ] + .concat(); + res.push(batch_public_input_hash_preimage); + + // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) + let batch_data_hash_preimage = self + .chunks + .iter() + .flat_map(|x| x.data_hash.as_bytes().iter()) + .cloned() + .collect(); + res.push(batch_data_hash_preimage); + + // compute piHash for each chunk for i in [0..k) + // chunk[i].piHash = + // keccak( + // chain id || + // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot || + // chunk[i].datahash) + for chunk in self.chunks.iter() { + let chunk_pi_hash_preimage = [ + self.chain_id.to_le_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_pi_hash_preimage) + } + + res + } + + fn num_instance(&self) -> Vec { + // 12 elements from the accumulators + // 32 elements from batch_data_hash_digest + vec![44] + } + + /// Compute the public inputs for this circuit + /// which is the public_input_hash + pub(crate) fn instances(&self) -> Vec> { + vec![self + .public_input_hash + .as_bytes() + .iter() + .map(|&x| F::from(x as u64)) + .collect()] + } } diff --git a/aggregator/src/proof_aggregation.rs b/aggregator/src/proof_aggregation.rs index 04632d8009..c8ac19823e 100644 --- a/aggregator/src/proof_aggregation.rs +++ b/aggregator/src/proof_aggregation.rs @@ -4,10 +4,35 @@ mod circuit; mod circuit_ext; /// Config for aggregation circuit mod config; -/// public input aggregation -mod public_input_aggregation; pub use circuit::AggregationCircuit; pub use config::AggregationConfig; -pub(crate) use public_input_aggregation::*; +// TODO(ZZ): update to the right degree +pub(crate) const LOG_DEGREE: u32 = 19; + +// ================================ +// indices for hash bytes +// ================================ +// +// 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 +// +// A chain_id is u64 and uses 8 bytes +pub(crate) const CHAIN_ID_LEN: usize = 8; +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; + +// Each round requires (NUM_ROUNDS+1) * DEFAULT_KECCAK_ROWS = 300 rows. +// This library is hard coded for this parameter. +// Modifying the following parameters may result into bugs. +// Adopted from keccak circuit +pub(crate) const DEFAULT_KECCAK_ROWS: usize = 12; +// Adopted from keccak circuit +pub(crate) const NUM_ROUNDS: usize = 24; diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index bdf9ae2a88..b2ad616b7c 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -27,7 +27,7 @@ use crate::{ core::{assign_batch_hashes, extract_accumulators_and_proof}, param::{ConfigParams, BITS, LIMBS}, proof_aggregation::config::AggregationConfig, - BatchHashCircuit, ChunkHash, CHAIN_ID_LEN, POST_STATE_ROOT_INDEX, PREV_STATE_ROOT_INDEX, + BatchHash, ChunkHash, CHAIN_ID_LEN, POST_STATE_ROOT_INDEX, PREV_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX, }; @@ -43,7 +43,7 @@ pub struct AggregationCircuit { // accumulation scheme proof, private input pub(crate) as_proof: Value>, // batch hash circuit for which the snarks are generated - pub(crate) batch_hash_circuit: BatchHashCircuit, + pub(crate) batch_hash: BatchHash, } impl AggregationCircuit { @@ -98,8 +98,8 @@ impl AggregationCircuit { .concat(); // extract the pi aggregation circuit's instances - let batch_hash_circuit = BatchHashCircuit::construct(chunk_hashes); - let pi_aggregation_instances = &batch_hash_circuit.instances()[0]; + let batch_hash = BatchHash::construct(chunk_hashes); + let pi_aggregation_instances = &batch_hash.instances()[0]; let flattened_instances: Vec = [ acc_instances.as_slice(), @@ -117,7 +117,7 @@ impl AggregationCircuit { snarks: snarks.iter().cloned().map_into().collect(), flattened_instances, as_proof: Value::known(as_proof), - batch_hash_circuit, + batch_hash, } } @@ -177,7 +177,7 @@ impl Circuit for AggregationCircuit { // circuit // ============================================== - // Step 1: aggregation circuit + // Step 1: snark aggregation circuit // ============================================== let mut accumulator_instances: Vec> = vec![]; let mut snark_inputs: Vec> = vec![]; @@ -253,7 +253,7 @@ impl Circuit for AggregationCircuit { let challenges = challenge.values(&layouter); let timer = start_timer!(|| ("extract hash").to_string()); - let preimages = self.batch_hash_circuit.extract_hash_preimages(); + let preimages = self.batch_hash.extract_hash_preimages(); end_timer!(timer); let timer = start_timer!(|| ("load aux table").to_string()); @@ -330,51 +330,21 @@ impl Circuit for AggregationCircuit { )?; // ==================================================== - // Last step: Constraint the hash data matches the raw public input + // Last step: Constraint the hash data matches the public input // ==================================================== let acc_len = 12; { - for i in 0..32 { - // first_chunk_prev_state_root - layouter.constrain_instance( - hash_input_cells[2][PREV_STATE_ROOT_INDEX + i].cell(), - config.instance, - i + acc_len, - )?; - // last_chunk_post_state_root - layouter.constrain_instance( - hash_input_cells.last().unwrap()[POST_STATE_ROOT_INDEX + i].cell(), - config.instance, - i + 32 + acc_len, - )?; - // last_chunk_withdraw_root - layouter.constrain_instance( - hash_input_cells.last().unwrap()[WITHDRAW_ROOT_INDEX + i].cell(), - config.instance, - i + 64 + acc_len, - )?; - } // batch_public_input_hash for i in 0..4 { for j in 0..8 { // digest in circuit has a different endianness - // 96 is the byte position for batch data hash layouter.constrain_instance( hash_output_cells[0][(3 - i) * 8 + j].cell(), config.instance, - i * 8 + j + 96 + acc_len, + i * 8 + j + acc_len, )?; } } - // last 8 inputs are the chain id - // chain_id is put at last here - for i in 0..CHAIN_ID_LEN { - layouter.constrain_instance( - hash_input_cells[0][i].cell(), - config.instance, - 128 + acc_len + i, - )?; - } } end_timer!(witness_time); diff --git a/aggregator/src/proof_aggregation/circuit_ext.rs b/aggregator/src/proof_aggregation/circuit_ext.rs index 33cd15a983..ed5ad94460 100644 --- a/aggregator/src/proof_aggregation/circuit_ext.rs +++ b/aggregator/src/proof_aggregation/circuit_ext.rs @@ -7,10 +7,8 @@ impl CircuitExt for AggregationCircuit { fn num_instance(&self) -> Vec { // accumulator [..lhs, ..rhs] let acc_len = 4 * LIMBS; - // public input - let public_input_agg_instance_len = self.batch_hash_circuit.num_instance()[0]; - - vec![public_input_agg_instance_len + acc_len] + // 32 elements for batch's public_input_hash + vec![acc_len + 32] } fn instances(&self) -> Vec> { diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/proof_aggregation/config.rs index b0e2b827e7..963d1c9290 100644 --- a/aggregator/src/proof_aggregation/config.rs +++ b/aggregator/src/proof_aggregation/config.rs @@ -29,13 +29,7 @@ pub struct AggregationConfig { pub keccak_circuit_config: KeccakCircuitConfig, /// Instance for public input; stores /// - accumulator from aggregation (12 elements) - /// - aggregated public inputs (136 elements): - /// chunk\[0\].prev_state_root || - /// chunk\[k-1\].post_state_root || - /// chunk\[k-1\].withdraw_root || - /// batch_data_hash || - /// chain_id - /// chain_id is put at last here for instance + /// - batch_public_input_hash (32 elements) pub instance: Column, } diff --git a/aggregator/src/proof_aggregation/public_input_aggregation.rs b/aggregator/src/proof_aggregation/public_input_aggregation.rs deleted file mode 100644 index e0fb7b13fa..0000000000 --- a/aggregator/src/proof_aggregation/public_input_aggregation.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! This module implements circuits that aggregates public inputs of many chunks into a single -//! one. -//! -//! # Spec -//! -//! A chunk is a list of continuous blocks. It consists of 4 hashes: -//! - state root before this chunk -//! - state root after this chunk -//! - the withdraw root of this chunk -//! - the data hash of this chunk -//! Those 4 hashes are obtained from the caller. -//! -//! A chunk's public input hash is then derived from the above 4 attributes via -//! -//! - chunk_pi_hash := keccak(chain_id || prev_state_root || post_state_root || withdraw_root || -//! chunk_data_hash) -//! -//! A batch is a list of continuous chunks. It consists of 2 hashes -//! -//! - batch_data_hash := keccak(chunk_0.data_hash || ... || chunk_k-1.data_hash) -//! -//! - batch_pi_hash := keccak(chain_id || chunk_0.prev_state_root || chunk_k-1.post_state_root || -//! chunk_k-1.withdraw_root || batch_data_hash) -//! -//! Note that chain_id is used for all public input hashes. But not for any data hashes. -//! -//! # Circuit -//! -//! A BatchHashCircuit asserts that the batch is well-formed. -//! -//! ## Public Input -//! The public inputs of the circuit (132 Field elements) is constructed as -//! - first_chunk_prev_state_root: 32 Field elements -//! - last_chunk_post_state_root: 32 Field elements -//! - last_chunk_withdraw_root: 32 Field elements -//! - batch_public_input_hash: 32 Field elements -//! - chain_id: 8 Field elements -//! -//! ## Constraints -//! The circuit attests the following statements: -//! -//! 1. all hashes are computed correctly -//! 2. the relations between hash preimages and digests are satisfied -//! - batch_data_hash is part of the input to compute batch_pi_hash -//! - batch_pi_hash used same roots as chunk_pi_hash -//! - same data_hash is used to compute batch_data_hash and chunk_pi_hash for all chunks -//! - chunks are continuous: they are linked via the state roots -//! - all hashes uses a same chain_id -//! 3. the hash data matches the circuit's public input (132 field elements) above -//! -//! # Example -//! -//! See tests::test_pi_aggregation_circuit - -// Circuit implementation of `BatchHashCircuit`. -mod circuit; -// CircuitExt implementation of `BatchHashCircuit`. -mod circuit_ext; -// Circuit and SubCircuit configurations -mod config; - -pub use crate::{BatchHash, ChunkHash}; -pub use circuit::{BatchHashCircuit, BatchHashCircuitPublicInput}; -pub use config::{BatchCircuitConfig, BatchCircuitConfigArgs}; - -// TODO(ZZ): update to the right degree -pub(crate) const LOG_DEGREE: u32 = 19; - -// ================================ -// indices for hash bytes -// -// 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 -// -// A chain_id is u64 and uses 8 bytes -pub(crate) const CHAIN_ID_LEN: usize = 8; -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; - -// Each round requires (NUM_ROUNDS+1) * DEFAULT_KECCAK_ROWS = 300 rows. -// This library is hard coded for this parameter. -// Modifying the following parameters may result into bugs. -// Adopted from keccak circuit -pub(crate) const DEFAULT_KECCAK_ROWS: usize = 12; -// Adopted from keccak circuit -pub(crate) const NUM_ROUNDS: usize = 24; diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs deleted file mode 100644 index ba0a385e33..0000000000 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit.rs +++ /dev/null @@ -1,230 +0,0 @@ -use std::marker::PhantomData; - -use ark_std::{end_timer, start_timer}; -use eth_types::{Field, H256}; -use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - plonk::{Circuit, ConstraintSystem, Error}, -}; - -use zkevm_circuits::util::{Challenges, SubCircuitConfig}; - -use crate::{ - core::assign_batch_hashes, BatchHash, ChunkHash, CHAIN_ID_LEN, POST_STATE_ROOT_INDEX, - PREV_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX, -}; - -use super::config::{BatchCircuitConfig, BatchCircuitConfigArgs}; - -/// BatchCircuit struct. -/// -/// Contains public inputs and witnesses that are needed to -/// generate the circuit. -#[derive(Clone, Debug, Default)] -pub struct BatchHashCircuit { - pub(crate) chain_id: u64, - pub(crate) chunks: Vec, - pub(crate) batch: BatchHash, - _phantom: PhantomData, -} - -/// Public input to a batch circuit. -/// In raw format. I.e., before converting to field elements. -pub struct BatchHashCircuitPublicInput { - pub(crate) chain_id: u64, - pub(crate) first_chunk_prev_state_root: H256, - pub(crate) last_chunk_post_state_root: H256, - pub(crate) last_chunk_withdraw_root: H256, - pub(crate) batch_public_input_hash: H256, -} - -impl BatchHashCircuit { - /// Sample a batch hash circuit from random (for testing) - #[cfg(test)] - pub(crate) fn mock_batch_hash_circuit(r: &mut R, size: usize) -> Self { - let mut chunks = (0..size) - .map(|_| ChunkHash::mock_chunk_hash(r)) - .collect::>(); - for i in 0..size - 1 { - chunks[i + 1].prev_state_root = chunks[i].post_state_root; - } - - Self::construct(&chunks) - } - - /// Build Batch hash circuit from a list of chunks - pub fn construct(chunk_hashes: &[ChunkHash]) -> Self { - let chain_id = chunk_hashes[0].chain_id; - // BatchHash::construct will check chunks are well-formed - let batch = BatchHash::construct(chunk_hashes); - Self { - chain_id, - chunks: chunk_hashes.to_vec(), - batch, - _phantom: PhantomData::default(), - } - } - - /// The public input to the BatchHashCircuit - pub fn public_input(&self) -> BatchHashCircuitPublicInput { - BatchHashCircuitPublicInput { - chain_id: self.chain_id, - first_chunk_prev_state_root: self.chunks[0].prev_state_root, - last_chunk_post_state_root: self.chunks.last().unwrap().post_state_root, - last_chunk_withdraw_root: self.chunks.last().unwrap().withdraw_root, - batch_public_input_hash: self.batch.public_input_hash, - } - } - - /// Extract all the hash inputs that will ever be used - /// orders: - /// - batch_public_input_hash - /// - batch_data_hash_preimage - /// - chunk\[i\].piHash for i in \[0, k) - pub(crate) fn extract_hash_preimages(&self) -> Vec> { - let mut res = vec![]; - - // batchPiHash = - // keccak( - // chain_id || - // chunk[0].prev_state_root || - // chunk[k-1].post_state_root || - // chunk[k-1].withdraw_root || - // batch_data_hash ) - let batch_public_input_hash_preimage = [ - self.chain_id.to_le_bytes().as_ref(), - self.chunks[0].prev_state_root.as_bytes(), - self.chunks.last().unwrap().post_state_root.as_bytes(), - self.chunks.last().unwrap().withdraw_root.as_bytes(), - self.batch.data_hash.as_bytes(), - ] - .concat(); - res.push(batch_public_input_hash_preimage); - - // batchDataHash = keccak(chunk[0].dataHash || ... || chunk[k-1].dataHash) - let batch_data_hash_preimage = self - .chunks - .iter() - .flat_map(|x| x.data_hash.as_bytes().iter()) - .cloned() - .collect(); - res.push(batch_data_hash_preimage); - - // compute piHash for each chunk for i in [0..k) - // chunk[i].piHash = - // keccak( - // chain id || - // chunk[i].prevStateRoot || chunk[i].postStateRoot || chunk[i].withdrawRoot || - // chunk[i].datahash) - for chunk in self.chunks.iter() { - let chunk_pi_hash_preimage = [ - self.chain_id.to_le_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_pi_hash_preimage) - } - - res - } -} - -impl Circuit for BatchHashCircuit { - type FloorPlanner = SimpleFloorPlanner; - - type Config = (BatchCircuitConfig, Challenges); - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let challenges = Challenges::construct(meta); - let challenges_exprs = challenges.exprs(meta); - let args = BatchCircuitConfigArgs { - challenges: challenges_exprs, - }; - let config = BatchCircuitConfig::new(meta, args); - (config, challenges) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let (config, challenge) = config; - let challenges = challenge.values(&layouter); - - // extract all the hashes and load them to the hash table - let timer = start_timer!(|| ("extract hash").to_string()); - let preimages = self.extract_hash_preimages(); - end_timer!(timer); - - let timer = start_timer!(|| ("load aux table").to_string()); - config - .keccak_circuit_config - .load_aux_tables(&mut layouter)?; - end_timer!(timer); - - let timer = start_timer!(|| ("assign cells").to_string()); - let (hash_input_cells, hash_output_cells) = assign_batch_hashes( - &config.keccak_circuit_config, - &mut layouter, - challenges, - &preimages, - )?; - end_timer!(timer); - - let timer = start_timer!(|| ("constraint public inputs").to_string()); - // ==================================================== - // Constraint the hash data matches the raw public input - // ==================================================== - { - for i in 0..32 { - // first_chunk_prev_state_root - layouter.constrain_instance( - hash_input_cells[2][PREV_STATE_ROOT_INDEX + i].cell(), - config.hash_digest_column, - i, - )?; - // last_chunk_post_state_root - layouter.constrain_instance( - hash_input_cells.last().unwrap()[POST_STATE_ROOT_INDEX + i].cell(), - config.hash_digest_column, - i + 32, - )?; - // last_chunk_withdraw_root - layouter.constrain_instance( - hash_input_cells.last().unwrap()[WITHDRAW_ROOT_INDEX + i].cell(), - config.hash_digest_column, - i + 64, - )?; - } - // batch_public_input_hash - for i in 0..4 { - for j in 0..8 { - // digest in circuit has a different endianness - layouter.constrain_instance( - hash_output_cells[0][(3 - i) * 8 + j].cell(), - config.hash_digest_column, - i * 8 + j + 96, - )?; - } - } - // last 8 inputs are the chain id - for i in 0..CHAIN_ID_LEN { - layouter.constrain_instance( - hash_input_cells[0][i].cell(), - config.hash_digest_column, - 128 + i, - )?; - } - } - end_timer!(timer); - Ok(()) - } -} diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs b/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs deleted file mode 100644 index 3cf8c973c9..0000000000 --- a/aggregator/src/proof_aggregation/public_input_aggregation/circuit_ext.rs +++ /dev/null @@ -1,49 +0,0 @@ -use eth_types::Field; -use snark_verifier_sdk::CircuitExt; - -use crate::BatchHashCircuit; - -impl CircuitExt for BatchHashCircuit { - fn num_instance(&self) -> Vec { - vec![136] - } - - /// Compute the public inputs for this circuit. - fn instances(&self) -> Vec> { - let public_input = self.public_input(); - - let first_chunk_prev_state_root = public_input - .first_chunk_prev_state_root - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let last_chunk_post_state_root = public_input - .last_chunk_post_state_root - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let last_chunk_withdraw_root = public_input - .last_chunk_withdraw_root - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let batch_public_input_hash = public_input - .batch_public_input_hash - .as_bytes() - .iter() - .map(|&x| F::from(x as u64)); - - let chain_id_bytes = public_input.chain_id.to_le_bytes(); - let chain_id = chain_id_bytes.iter().map(|x| F::from(*x as u64)); - - vec![first_chunk_prev_state_root - .chain(last_chunk_post_state_root) - .chain(last_chunk_withdraw_root) - .chain(batch_public_input_hash) - .chain(chain_id) - .collect()] - } -} diff --git a/aggregator/src/proof_aggregation/public_input_aggregation/config.rs b/aggregator/src/proof_aggregation/public_input_aggregation/config.rs deleted file mode 100644 index 94ccf30ec7..0000000000 --- a/aggregator/src/proof_aggregation/public_input_aggregation/config.rs +++ /dev/null @@ -1,67 +0,0 @@ -use eth_types::Field; -use halo2_proofs::plonk::{Column, ConstraintSystem, Expression, Instance}; -use zkevm_circuits::{ - keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, - table::KeccakTable, - util::{Challenges, SubCircuitConfig}, -}; - -/// Config for BatchCircuit -#[derive(Clone, Debug)] -pub struct BatchCircuitConfig { - /// Instance column stores the aggregated rpi hash digest - pub(crate) hash_digest_column: Column, - - /// Keccak circuit config - pub(crate) keccak_circuit_config: KeccakCircuitConfig, -} - -/// Auxiliary arguments for BatchCircuit's Config -#[derive(Clone, Debug)] -pub struct BatchCircuitConfigArgs { - pub challenges: Challenges>, -} - -impl SubCircuitConfig for BatchCircuitConfig { - type ConfigArgs = BatchCircuitConfigArgs; - - /// Return a new BatchCircuitConfig - fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { - // hash configuration - let keccak_circuit_config = { - let keccak_table = KeccakTable::construct(meta); - - let keccak_circuit_config_args = KeccakCircuitConfigArgs { - keccak_table, - challenges: config_args.challenges, - }; - - KeccakCircuitConfig::new(meta, keccak_circuit_config_args) - }; - - // The current code base is hardcoded for KeccakCircuit configured - // with 300 rows and 87 columns per hash call. - let columns = keccak_circuit_config.cell_manager.columns(); - - assert_eq!( - columns.len(), - 87, - "cell manager configuration does not match the hard coded setup" - ); - - // enabling equality for preimage and digest columns - meta.enable_equality(columns[6].advice); - // digest column - meta.enable_equality(columns.last().unwrap().advice); - - // Instance column stores the output of the hash - let hash_digest_column = meta.instance_column(); - // public input column - meta.enable_equality(hash_digest_column); - - BatchCircuitConfig { - hash_digest_column, - keccak_circuit_config, - } - } -} diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index 2221ffd997..96be003a62 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -2,7 +2,6 @@ pub(crate) mod aggregator_e2e; pub(crate) mod mock_chunk; pub(crate) mod proof_aggregation; pub(crate) mod proof_compression; -pub(crate) mod public_input_aggregation; #[macro_export] macro_rules! layer_0 { diff --git a/aggregator/src/tests/public_input_aggregation.rs b/aggregator/src/tests/public_input_aggregation.rs deleted file mode 100644 index cc6fae725d..0000000000 --- a/aggregator/src/tests/public_input_aggregation.rs +++ /dev/null @@ -1,45 +0,0 @@ -use ark_std::{end_timer, start_timer, test_rng}; -use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; -use snark_verifier::loader::halo2::halo2_ecc::halo2_base::utils::fs::gen_srs; -use snark_verifier_sdk::{gen_pk, gen_snark_shplonk, verify_snark_shplonk, CircuitExt}; - -use crate::{BatchHashCircuit, LOG_DEGREE}; - -#[test] -fn test_pi_aggregation_mock_prover() { - let mut rng = test_rng(); - let chunks_per_batch = 8; - - let circuit = BatchHashCircuit::::mock_batch_hash_circuit(&mut rng, chunks_per_batch); - let instance = circuit.instances(); - - let mock_prover = MockProver::::run(LOG_DEGREE, &circuit, instance).unwrap(); - - mock_prover.assert_satisfied_par() -} - -#[test] -fn test_pi_aggregation_real_prover() { - let mut rng = test_rng(); - let param = gen_srs(LOG_DEGREE); - - let chunks_per_batch = 16; - - let circuit = BatchHashCircuit::::mock_batch_hash_circuit(&mut rng, chunks_per_batch); - - let timer = start_timer!(|| format!("key generation for k = {}", LOG_DEGREE)); - let pk = gen_pk(¶m, &circuit, None); - end_timer!(timer); - - let timer = start_timer!(|| "proving"); - let snark = gen_snark_shplonk(¶m, &pk, circuit, &mut rng, None::); - end_timer!(timer); - - let timer = start_timer!(|| "verifying"); - assert!(verify_snark_shplonk::>( - ¶m, - snark, - pk.get_vk() - )); - end_timer!(timer); -} diff --git a/aggregator/tests.sh b/aggregator/tests.sh index 2d342d3a79..c95568840e 100755 --- a/aggregator/tests.sh +++ b/aggregator/tests.sh @@ -1,5 +1,3 @@ -RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_mock_prover -- --nocapture 2>&1 | tee pi_mock.log -RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_pi_aggregation_real_prover -- --nocapture 2>&1 | tee pi_real.log RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log # the following 4 tests takes super long time From 911423c2d8b0117f254b98e30323d61f89ba5f52 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 22 Jun 2023 09:46:59 -0400 Subject: [PATCH 34/41] [fix] chain id to_be_bytes --- aggregator/src/batch.rs | 6 +++--- aggregator/src/chunk.rs | 2 +- aggregator/src/tests/mock_chunk/circuit_ext.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aggregator/src/batch.rs b/aggregator/src/batch.rs index 226608bc28..0632935c61 100644 --- a/aggregator/src/batch.rs +++ b/aggregator/src/batch.rs @@ -106,7 +106,7 @@ impl BatchHash { // chunk[k-1].withdraw_root || // batch_data_hash ) let preimage = [ - chunk_hashes[0].chain_id.to_le_bytes().as_ref(), + chunk_hashes[0].chain_id.to_be_bytes().as_ref(), chunk_hashes[0].prev_state_root.as_bytes(), chunk_hashes.last().unwrap().post_state_root.as_bytes(), chunk_hashes.last().unwrap().withdraw_root.as_bytes(), @@ -139,7 +139,7 @@ impl BatchHash { // chunk[k-1].withdraw_root || // batch_data_hash ) let batch_public_input_hash_preimage = [ - self.chain_id.to_le_bytes().as_ref(), + self.chain_id.to_be_bytes().as_ref(), self.chunks[0].prev_state_root.as_bytes(), self.chunks.last().unwrap().post_state_root.as_bytes(), self.chunks.last().unwrap().withdraw_root.as_bytes(), @@ -165,7 +165,7 @@ impl BatchHash { // chunk[i].datahash) for chunk in self.chunks.iter() { let chunk_pi_hash_preimage = [ - self.chain_id.to_le_bytes().as_ref(), + 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(), diff --git a/aggregator/src/chunk.rs b/aggregator/src/chunk.rs index 86c424e40d..734a9bd750 100644 --- a/aggregator/src/chunk.rs +++ b/aggregator/src/chunk.rs @@ -55,7 +55,7 @@ impl ChunkHash { /// chain id || prev state root || post state root || withdraw root || data hash pub fn extract_hash_preimage(&self) -> Vec { [ - self.chain_id.to_le_bytes().as_ref(), + self.chain_id.to_be_bytes().as_ref(), self.prev_state_root.as_bytes(), self.post_state_root.as_bytes(), self.withdraw_root.as_bytes(), diff --git a/aggregator/src/tests/mock_chunk/circuit_ext.rs b/aggregator/src/tests/mock_chunk/circuit_ext.rs index 3d270c98fe..a8cc9c21e9 100644 --- a/aggregator/src/tests/mock_chunk/circuit_ext.rs +++ b/aggregator/src/tests/mock_chunk/circuit_ext.rs @@ -15,7 +15,7 @@ impl CircuitExt for MockChunkCircuit { fn instances(&self) -> Vec> { vec![self .chain_id - .to_le_bytes() + .to_be_bytes() .iter() .chain( self.chunk From 02b09566d8d2bab09465b6200765a5862a227f19 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 22 Jun 2023 10:08:14 -0400 Subject: [PATCH 35/41] [feat] mock aggregation --- aggregator/src/tests/mock_chunk.rs | 13 ++++- aggregator/src/tests/mock_chunk/circuit.rs | 1 + .../src/tests/mock_chunk/circuit_ext.rs | 24 +++++---- aggregator/src/tests/mock_chunk/config.rs | 8 +-- aggregator/src/tests/proof_aggregation.rs | 53 ++++++++++++++++++- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index aa70e46047..fa309b72fe 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -15,11 +15,22 @@ mod config; /// It's public inputs consists of 64 elements: /// - data hash /// - public input hash -pub struct MockChunkCircuit { +pub(crate) struct MockChunkCircuit { + pub(crate) is_fresh: bool, pub(crate) chain_id: u64, pub(crate) chunk: ChunkHash, } +impl MockChunkCircuit { + pub(crate) fn new(is_fresh: bool, chain_id: u64, chunk: ChunkHash) -> Self { + MockChunkCircuit { + is_fresh, + chain_id, + chunk, + } + } +} + #[test] fn test_mock_chunk_prover() { env_logger::init(); diff --git a/aggregator/src/tests/mock_chunk/circuit.rs b/aggregator/src/tests/mock_chunk/circuit.rs index 31cb968ee3..eca8f3c4d8 100644 --- a/aggregator/src/tests/mock_chunk/circuit.rs +++ b/aggregator/src/tests/mock_chunk/circuit.rs @@ -16,6 +16,7 @@ use super::{ impl MockChunkCircuit { pub(crate) fn random(r: &mut R) -> Self { Self { + is_fresh: true, chain_id: 0, chunk: ChunkHash::mock_chunk_hash(r), } diff --git a/aggregator/src/tests/mock_chunk/circuit_ext.rs b/aggregator/src/tests/mock_chunk/circuit_ext.rs index a8cc9c21e9..9ce87e7ec5 100644 --- a/aggregator/src/tests/mock_chunk/circuit_ext.rs +++ b/aggregator/src/tests/mock_chunk/circuit_ext.rs @@ -1,3 +1,5 @@ +use std::iter; + use halo2_proofs::halo2curves::bn256::Fr; use snark_verifier_sdk::CircuitExt; @@ -8,21 +10,23 @@ use super::MockChunkCircuit; impl CircuitExt for MockChunkCircuit { /// 64 elements from digest fn num_instance(&self) -> Vec { - vec![64 + CHAIN_ID_LEN] + let acc_len = if self.is_fresh { 0 } else { 12 }; + vec![64 + CHAIN_ID_LEN + acc_len] } /// return vec![data hash | public input hash] fn instances(&self) -> Vec> { - vec![self - .chain_id - .to_be_bytes() - .iter() + let acc_len = if self.is_fresh { 0 } else { 12 }; + vec![iter::repeat(0) + .take(acc_len) .chain( - self.chunk - .data_hash - .as_bytes() - .iter() - .chain(self.chunk.public_input_hash().as_bytes().iter()), + self.chain_id.to_be_bytes().iter().chain( + self.chunk + .data_hash + .as_bytes() + .iter() + .chain(self.chunk.public_input_hash().as_bytes().iter()), + ), ) .map(|&x| Fr::from(x as u64)) .collect()] diff --git a/aggregator/src/tests/mock_chunk/config.rs b/aggregator/src/tests/mock_chunk/config.rs index fcb061d5b5..7998ab556d 100644 --- a/aggregator/src/tests/mock_chunk/config.rs +++ b/aggregator/src/tests/mock_chunk/config.rs @@ -149,12 +149,14 @@ impl MockChunkCircuitConfig { // ==================================================== // Step 2. check the cells match the public input // ==================================================== + let acc_len = if self.is_fresh { 0 } else { 12 }; + // chunk's data hash for i in 0..32 { layouter.constrain_instance( hash_input_cells[i + 96 + CHAIN_ID_LEN].cell(), self.hash_digest_column, - i + CHAIN_ID_LEN, + i + CHAIN_ID_LEN + acc_len, )?; } // chunk's public_input_hash @@ -164,13 +166,13 @@ impl MockChunkCircuitConfig { layouter.constrain_instance( hash_output_cells[(3 - i) * 8 + j].cell(), self.hash_digest_column, - i * 8 + j + 32 + CHAIN_ID_LEN, + i * 8 + j + 32 + CHAIN_ID_LEN + acc_len, )?; } } // chain id for (i, cell) in hash_input_cells.iter().enumerate().take(CHAIN_ID_LEN) { - layouter.constrain_instance(cell.cell(), self.hash_digest_column, i)?; + layouter.constrain_instance(cell.cell(), self.hash_digest_column, i + acc_len)?; } Ok(hash_output_cells) diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index fa9c42d84e..36925887f3 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -12,6 +12,57 @@ use super::mock_chunk::MockChunkCircuit; const CHUNKS_PER_BATCH: usize = 2; +#[test] +fn test_mock_aggregation() { + let process_id = process::id(); + + let dir = format!("data/{}", process_id); + let path = Path::new(dir.as_str()); + fs::create_dir(path).unwrap(); + + // inner circuit: Mock circuit + let k0 = 19; + // aggregation + let k1 = 26; + + let mut rng = test_rng(); + let params = gen_srs(k2); + + let mut chunks = (0..CHUNKS_PER_BATCH) + .map(|_| ChunkHash::mock_chunk_hash(&mut rng)) + .collect_vec(); + for i in 0..CHUNKS_PER_BATCH - 1 { + chunks[i + 1].prev_state_root = chunks[i].post_state_root; + } + // Proof for test circuit + let circuits = chunks + .iter() + .map(|&chunk| MockChunkCircuit::new(false, 0, chunk)) + .collect_vec(); + let layer_0_snarks = circuits + .iter() + .map(|&circuit| layer_0!(circuit, MockChunkCircuit, params, k0, path)) + .collect_vec(); + + // layer 1 proof aggregation + { + let param = { + let mut param = params; + param.downsize(k1); + param + }; + let aggregation_circuit = + AggregationCircuit::new(¶m, &layer_0_snarks, &mut rng, chunks.as_ref()); + let instance = aggregation_circuit.instances(); + + let mock_prover = MockProver::::run(k1, &aggregation_circuit, instance).unwrap(); + + mock_prover.assert_satisfied_par() + } +} + + + // This test takes about 1 hour on CPU #[ignore = "it takes too much time"] #[test] @@ -43,7 +94,7 @@ fn test_aggregation_circuit() { // Proof for test circuit let circuits = chunks .iter() - .map(|&chunk| MockChunkCircuit { chain_id: 0, chunk }) + .map(|&chunk| MockChunkCircuit::new(true, 0, chunk)) .collect_vec(); let layer_0_snarks = circuits .iter() From 2b0d19eebc8722e3f02d057b44d29e5e4c161400 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 22 Jun 2023 10:19:53 -0400 Subject: [PATCH 36/41] wip --- aggregator/src/tests/aggregator_e2e.rs | 6 +++++- aggregator/src/tests/mock_chunk/circuit.rs | 2 +- .../src/tests/mock_chunk/circuit_ext.rs | 20 +++++++++++-------- aggregator/src/tests/mock_chunk/config.rs | 3 ++- aggregator/src/tests/proof_aggregation.rs | 4 +--- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/aggregator/src/tests/aggregator_e2e.rs b/aggregator/src/tests/aggregator_e2e.rs index 5ec26a1f4b..e5f047b0a9 100644 --- a/aggregator/src/tests/aggregator_e2e.rs +++ b/aggregator/src/tests/aggregator_e2e.rs @@ -53,7 +53,11 @@ fn test_e2e() { // Proof for test circuit let circuits = chunks .iter() - .map(|&chunk| MockChunkCircuit { chain_id: 0, chunk }) + .map(|&chunk| MockChunkCircuit { + is_fresh: true, + chain_id: 0, + chunk, + }) .collect_vec(); let layer_0_snarks = circuits .iter() diff --git a/aggregator/src/tests/mock_chunk/circuit.rs b/aggregator/src/tests/mock_chunk/circuit.rs index eca8f3c4d8..ec34d87701 100644 --- a/aggregator/src/tests/mock_chunk/circuit.rs +++ b/aggregator/src/tests/mock_chunk/circuit.rs @@ -68,7 +68,7 @@ impl Circuit for MockChunkCircuit { end_timer!(timer); let timer = start_timer!(|| ("assign cells").to_string()); - config.assign(&mut layouter, challenges, &preimages)?; + config.assign(&mut layouter, challenges, &preimages, self.is_fresh)?; end_timer!(timer); Ok(()) } diff --git a/aggregator/src/tests/mock_chunk/circuit_ext.rs b/aggregator/src/tests/mock_chunk/circuit_ext.rs index 9ce87e7ec5..cfc6b1aec5 100644 --- a/aggregator/src/tests/mock_chunk/circuit_ext.rs +++ b/aggregator/src/tests/mock_chunk/circuit_ext.rs @@ -20,15 +20,19 @@ impl CircuitExt for MockChunkCircuit { vec![iter::repeat(0) .take(acc_len) .chain( - self.chain_id.to_be_bytes().iter().chain( - self.chunk - .data_hash - .as_bytes() - .iter() - .chain(self.chunk.public_input_hash().as_bytes().iter()), - ), + self.chain_id + .to_be_bytes() + .iter() + .chain( + self.chunk + .data_hash + .as_bytes() + .iter() + .chain(self.chunk.public_input_hash().as_bytes().iter()), + ) + .copied(), ) - .map(|&x| Fr::from(x as u64)) + .map(|x| Fr::from(x as u64)) .collect()] } } diff --git a/aggregator/src/tests/mock_chunk/config.rs b/aggregator/src/tests/mock_chunk/config.rs index 7998ab556d..49d54c7985 100644 --- a/aggregator/src/tests/mock_chunk/config.rs +++ b/aggregator/src/tests/mock_chunk/config.rs @@ -86,6 +86,7 @@ impl MockChunkCircuitConfig { layouter: &mut impl Layouter, challenges: Challenges>, preimages: &[u8], + is_fresh: bool, ) -> Result< Vec>, // digest cells Error, @@ -149,7 +150,7 @@ impl MockChunkCircuitConfig { // ==================================================== // Step 2. check the cells match the public input // ==================================================== - let acc_len = if self.is_fresh { 0 } else { 12 }; + let acc_len = if is_fresh { 0 } else { 12 }; // chunk's data hash for i in 0..32 { diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index 36925887f3..a2bbd21407 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -26,7 +26,7 @@ fn test_mock_aggregation() { let k1 = 26; let mut rng = test_rng(); - let params = gen_srs(k2); + let params = gen_srs(k1); let mut chunks = (0..CHUNKS_PER_BATCH) .map(|_| ChunkHash::mock_chunk_hash(&mut rng)) @@ -61,8 +61,6 @@ fn test_mock_aggregation() { } } - - // This test takes about 1 hour on CPU #[ignore = "it takes too much time"] #[test] From 4afe75a7e67c2007a44a014ddef2ca227bb69741 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Fri, 23 Jun 2023 12:21:25 -0400 Subject: [PATCH 37/41] [fix] pi length in agg circuit --- aggregator/src/proof_aggregation/circuit.rs | 20 +++++++++++++------- aggregator/src/proof_aggregation/config.rs | 5 ++--- aggregator/src/tests.rs | 5 +++-- aggregator/src/tests/mock_chunk.rs | 9 ++++++++- aggregator/src/tests/mock_chunk/circuit.rs | 4 ++-- aggregator/src/tests/proof_aggregation.rs | 2 ++ aggregator/src/tests/proof_compression.rs | 4 ++-- 7 files changed, 32 insertions(+), 17 deletions(-) diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/proof_aggregation/circuit.rs index b2ad616b7c..dfc621ef9f 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/proof_aggregation/circuit.rs @@ -38,7 +38,7 @@ pub struct AggregationCircuit { pub(crate) snarks: Vec, // the public instance for this circuit consists of // - an accumulator (12 elements) - // - batch hash circuit's public inputs, 132 elements + // - the batch's public_input_hash (32 elements) pub(crate) flattened_instances: Vec, // accumulation scheme proof, private input pub(crate) as_proof: Value>, @@ -71,17 +71,18 @@ impl AggregationCircuit { for i in 0..32 { // for each snark, // first 12 elements are accumulator - // next 32 elements are data hash (44=12+32) + // next 8 elements are chain id + // next 32 elements are data hash (52=20+32) // next 32 elements are public_input_hash // data hash + public_input_hash = snark public input assert_eq!( Fr::from(chunk.data_hash.as_bytes()[i] as u64), - snark_hash_bytes[i + 12] + snark_hash_bytes[i + 20] ); assert_eq!( Fr::from(chunk_hash_bytes[i] as u64), - snark_hash_bytes[i + 44] + snark_hash_bytes[i + 52] ); } } @@ -99,11 +100,11 @@ impl AggregationCircuit { // extract the pi aggregation circuit's instances let batch_hash = BatchHash::construct(chunk_hashes); - let pi_aggregation_instances = &batch_hash.instances()[0]; + let public_input_hash = &batch_hash.instances()[0]; let flattened_instances: Vec = [ acc_instances.as_slice(), - pi_aggregation_instances.as_slice(), + public_input_hash.as_slice(), ] .concat(); @@ -226,7 +227,7 @@ impl Circuit for AggregationCircuit { snark_inputs.extend( assigned_aggregation_instances .iter() - .flat_map(|instance_column| instance_column.iter().skip(12)), + .flat_map(|instance_column| instance_column.iter().skip(20)), ); config.range().finalize(&mut loader.ctx_mut()); @@ -256,6 +257,11 @@ impl Circuit for AggregationCircuit { let preimages = self.batch_hash.extract_hash_preimages(); end_timer!(timer); + log::trace!("hash preimages"); + for (i, e) in preimages.iter().enumerate() { + log::trace!("{}-th hash preimage {:02x?}", i, e) + } + let timer = start_timer!(|| ("load aux table").to_string()); config .keccak_circuit_config diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/proof_aggregation/config.rs index 963d1c9290..20664b6f04 100644 --- a/aggregator/src/proof_aggregation/config.rs +++ b/aggregator/src/proof_aggregation/config.rs @@ -89,11 +89,10 @@ impl AggregationConfig { // digest column meta.enable_equality(columns.last().unwrap().advice); - // Instance column stores + // Instance column stores public input column // - the accumulator - // - the output of the hash + // - the batch public input hash let instance = meta.instance_column(); - // public input column meta.enable_equality(instance); Self { diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index 96be003a62..ade186115f 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -17,8 +17,9 @@ macro_rules! layer_0 { }; let pk = gen_pk( - ¶m, &$circuit, None, - // Some(&$path.join(Path::new("layer_0.pkey"))), + ¶m, + &$circuit, + Some(&$path.join(Path::new("layer_0.pkey"))), ); log::trace!("finished layer 0 pk generation for circuit"); diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index fa309b72fe..0a8a4c17e7 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -37,7 +37,14 @@ fn test_mock_chunk_prover() { let mut rng = test_rng(); - let circuit = MockChunkCircuit::random(&mut rng); + let circuit = MockChunkCircuit::random(&mut rng, true); + let instance = circuit.instances(); + + let mock_prover = MockProver::::run(LOG_DEGREE, &circuit, instance).unwrap(); + + mock_prover.assert_satisfied_par(); + + let circuit = MockChunkCircuit::random(&mut rng, false); let instance = circuit.instances(); let mock_prover = MockProver::::run(LOG_DEGREE, &circuit, instance).unwrap(); diff --git a/aggregator/src/tests/mock_chunk/circuit.rs b/aggregator/src/tests/mock_chunk/circuit.rs index ec34d87701..ffcba61df6 100644 --- a/aggregator/src/tests/mock_chunk/circuit.rs +++ b/aggregator/src/tests/mock_chunk/circuit.rs @@ -14,9 +14,9 @@ use super::{ }; impl MockChunkCircuit { - pub(crate) fn random(r: &mut R) -> Self { + pub(crate) fn random(r: &mut R, is_fresh: bool) -> Self { Self { - is_fresh: true, + is_fresh, chain_id: 0, chunk: ChunkHash::mock_chunk_hash(r), } diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/proof_aggregation.rs index a2bbd21407..ed69a415f5 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/proof_aggregation.rs @@ -14,6 +14,7 @@ const CHUNKS_PER_BATCH: usize = 2; #[test] fn test_mock_aggregation() { + env_logger::init(); let process_id = process::id(); let dir = format!("data/{}", process_id); @@ -54,6 +55,7 @@ fn test_mock_aggregation() { let aggregation_circuit = AggregationCircuit::new(¶m, &layer_0_snarks, &mut rng, chunks.as_ref()); let instance = aggregation_circuit.instances(); + println!("instance length {:?}", instance.len()); let mock_prover = MockProver::::run(k1, &aggregation_circuit, instance).unwrap(); diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/proof_compression.rs index f6b18f43ad..b07dc1e0c4 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/proof_compression.rs @@ -31,7 +31,7 @@ fn test_proof_compression() { let layer_1_params = gen_srs(k1); // Proof for test circuit - let circuit = MockChunkCircuit::random(&mut rng); + let circuit = MockChunkCircuit::random(&mut rng, true); let layer_0_snark = layer_0!(circuit, MockChunkCircuit, layer_1_params, k0, path); std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); @@ -55,7 +55,7 @@ fn test_two_layer_proof_compression() { let mut rng = test_rng(); let layer_2_params = gen_srs(k2); - let circuit = MockChunkCircuit::random(&mut rng); + let circuit = MockChunkCircuit::random(&mut rng, true); let layer_0_snark = layer_0!(circuit, MockChunkCircuit, layer_2_params, k0, path); std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); From a4d5eb925edb7e61de283ddb96d5fe880a635100 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Fri, 23 Jun 2023 15:58:35 -0400 Subject: [PATCH 38/41] [refactor] optimize tests --- aggregator/configs/aggregation.config | 2 +- aggregator/configs/compression_wide.config | 2 +- .../{proof_aggregation.rs => aggregation.rs} | 0 .../circuit.rs | 14 +- .../circuit_ext.rs | 4 +- .../config.rs | 0 .../{proof_compression.rs => compression.rs} | 0 .../circuit.rs | 8 + .../circuit_ext.rs | 0 .../config.rs | 0 aggregator/src/lib.rs | 12 +- aggregator/src/param.rs | 4 +- aggregator/src/tests.rs | 4 +- .../{proof_aggregation.rs => aggregation.rs} | 6 +- aggregator/src/tests/aggregator_e2e.rs | 2 +- .../{proof_compression.rs => compression.rs} | 28 ++- aggregator/src/tests/mock_chunk/circuit.rs | 86 ++++--- aggregator/src/tests/mock_chunk/config.rs | 221 ++++-------------- aggregator/tests.sh | 9 +- 19 files changed, 161 insertions(+), 241 deletions(-) rename aggregator/src/{proof_aggregation.rs => aggregation.rs} (100%) rename aggregator/src/{proof_aggregation => aggregation}/circuit.rs (97%) rename aggregator/src/{proof_aggregation => aggregation}/circuit_ext.rs (93%) rename aggregator/src/{proof_aggregation => aggregation}/config.rs (100%) rename aggregator/src/{proof_compression.rs => compression.rs} (100%) rename aggregator/src/{proof_compression => compression}/circuit.rs (96%) rename aggregator/src/{proof_compression => compression}/circuit_ext.rs (100%) rename aggregator/src/{proof_compression => compression}/config.rs (100%) rename aggregator/src/tests/{proof_aggregation.rs => aggregation.rs} (98%) rename aggregator/src/tests/{proof_compression.rs => compression.rs} (69%) diff --git a/aggregator/configs/aggregation.config b/aggregator/configs/aggregation.config index 019ada0f6a..2416aff792 100644 --- a/aggregator/configs/aggregation.config +++ b/aggregator/configs/aggregation.config @@ -1 +1 @@ -{"strategy":"Simple","degree":26,"num_advice":[4],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":20,"num_advice":[4],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} diff --git a/aggregator/configs/compression_wide.config b/aggregator/configs/compression_wide.config index 2114929ce8..78bbf04075 100644 --- a/aggregator/configs/compression_wide.config +++ b/aggregator/configs/compression_wide.config @@ -1 +1 @@ -{"strategy":"Simple","degree":23,"num_advice":[18],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":22,"num_advice":[8],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} diff --git a/aggregator/src/proof_aggregation.rs b/aggregator/src/aggregation.rs similarity index 100% rename from aggregator/src/proof_aggregation.rs rename to aggregator/src/aggregation.rs diff --git a/aggregator/src/proof_aggregation/circuit.rs b/aggregator/src/aggregation/circuit.rs similarity index 97% rename from aggregator/src/proof_aggregation/circuit.rs rename to aggregator/src/aggregation/circuit.rs index dfc621ef9f..938be7410f 100644 --- a/aggregator/src/proof_aggregation/circuit.rs +++ b/aggregator/src/aggregation/circuit.rs @@ -24,9 +24,9 @@ use snark_verifier_sdk::{ use zkevm_circuits::util::Challenges; use crate::{ + aggregation::config::AggregationConfig, core::{assign_batch_hashes, extract_accumulators_and_proof}, param::{ConfigParams, BITS, LIMBS}, - proof_aggregation::config::AggregationConfig, BatchHash, ChunkHash, CHAIN_ID_LEN, POST_STATE_ROOT_INDEX, PREV_STATE_ROOT_INDEX, WITHDRAW_ROOT_INDEX, }; @@ -55,6 +55,7 @@ impl AggregationCircuit { rng: impl Rng + Send, chunk_hashes: &[ChunkHash], ) -> Self { + let timer = start_timer!(|| "generate aggregation circuit"); // sanity: for each chunk we have a snark assert_eq!( snarks.len(), @@ -102,17 +103,14 @@ impl AggregationCircuit { let batch_hash = BatchHash::construct(chunk_hashes); let public_input_hash = &batch_hash.instances()[0]; - let flattened_instances: Vec = [ - acc_instances.as_slice(), - public_input_hash.as_slice(), - ] - .concat(); + let flattened_instances: Vec = + [acc_instances.as_slice(), public_input_hash.as_slice()].concat(); log::trace!("flattened instances during construction"); for (i, e) in flattened_instances.iter().enumerate() { log::trace!("{}-th: {:?}", i, e); } - + end_timer!(timer); Self { svk, snarks: snarks.iter().cloned().map_into().collect(), @@ -147,7 +145,7 @@ impl Circuit for AggregationCircuit { let challenges = Challenges::construct(meta); let config = AggregationConfig::configure(meta, ¶ms, challenges); log::info!( - "aggregation circuit configured with k = {} and {:?} advice columns", + "aggregation circuit configured with k = {} and {:?} advice columns", params.degree, params.num_advice ); diff --git a/aggregator/src/proof_aggregation/circuit_ext.rs b/aggregator/src/aggregation/circuit_ext.rs similarity index 93% rename from aggregator/src/proof_aggregation/circuit_ext.rs rename to aggregator/src/aggregation/circuit_ext.rs index ed5ad94460..9f6b09c5b3 100644 --- a/aggregator/src/proof_aggregation/circuit_ext.rs +++ b/aggregator/src/aggregation/circuit_ext.rs @@ -1,7 +1,9 @@ use halo2_proofs::{halo2curves::bn256::Fr, plonk::Selector}; use snark_verifier_sdk::CircuitExt; -use crate::{param::LIMBS, AggregationCircuit}; +use crate::param::LIMBS; + +use super::AggregationCircuit; impl CircuitExt for AggregationCircuit { fn num_instance(&self) -> Vec { diff --git a/aggregator/src/proof_aggregation/config.rs b/aggregator/src/aggregation/config.rs similarity index 100% rename from aggregator/src/proof_aggregation/config.rs rename to aggregator/src/aggregation/config.rs diff --git a/aggregator/src/proof_compression.rs b/aggregator/src/compression.rs similarity index 100% rename from aggregator/src/proof_compression.rs rename to aggregator/src/compression.rs diff --git a/aggregator/src/proof_compression/circuit.rs b/aggregator/src/compression/circuit.rs similarity index 96% rename from aggregator/src/proof_compression/circuit.rs rename to aggregator/src/compression/circuit.rs index 2158cfd9ce..cb55d7f575 100644 --- a/aggregator/src/proof_compression/circuit.rs +++ b/aggregator/src/compression/circuit.rs @@ -77,6 +77,8 @@ impl Circuit for CompressionCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { + // Too bad that configure function doesn't take additional input + // it would be nicer to load parameters from API rather than ENV let path = std::env::var("VERIFY_CONFIG") .unwrap_or_else(|_| "configs/verify_circuit.config".to_owned()); let params: ConfigParams = serde_json::from_reader( @@ -84,6 +86,12 @@ impl Circuit for CompressionCircuit { ) .unwrap(); + log::info!( + "compression circuit configured with k = {} and {:?} advice columns", + params.degree, + params.num_advice + ); + // circuit configuration is built from config with given num columns etc // can be wide or thin circuit Self::Config::configure(meta, params) diff --git a/aggregator/src/proof_compression/circuit_ext.rs b/aggregator/src/compression/circuit_ext.rs similarity index 100% rename from aggregator/src/proof_compression/circuit_ext.rs rename to aggregator/src/compression/circuit_ext.rs diff --git a/aggregator/src/proof_compression/config.rs b/aggregator/src/compression/config.rs similarity index 100% rename from aggregator/src/proof_compression/config.rs rename to aggregator/src/compression/config.rs diff --git a/aggregator/src/lib.rs b/aggregator/src/lib.rs index bf2e75cc29..2c8d22b66d 100644 --- a/aggregator/src/lib.rs +++ b/aggregator/src/lib.rs @@ -3,23 +3,23 @@ mod chunk; // This module implements `Batch` related data types. // A batch is a list of chunk. +/// proof aggregation +mod aggregation; mod batch; +/// proof compression +mod compression; /// Core module for circuit assignment mod core; /// Parameters for compression circuit mod param; -/// proof aggregation -mod proof_aggregation; -/// proof compression -mod proof_compression; /// utilities mod util; #[cfg(test)] mod tests; +pub use aggregation::*; pub use batch::BatchHash; pub use chunk::ChunkHash; +pub use compression::*; pub use param::*; -pub use proof_aggregation::*; -pub use proof_compression::*; diff --git a/aggregator/src/param.rs b/aggregator/src/param.rs index 34516f7e44..8b042211e4 100644 --- a/aggregator/src/param.rs +++ b/aggregator/src/param.rs @@ -20,8 +20,8 @@ impl ConfigParams { pub(crate) fn aggregation_param() -> Self { Self { strategy: FpStrategy::Simple, - degree: 25, - num_advice: vec![2], + degree: 23, + num_advice: vec![8], num_lookup_advice: vec![1], num_fixed: 1, lookup_bits: 20, diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index ade186115f..0348e57ac6 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -1,7 +1,7 @@ +pub(crate) mod aggregation; pub(crate) mod aggregator_e2e; +pub(crate) mod compression; pub(crate) mod mock_chunk; -pub(crate) mod proof_aggregation; -pub(crate) mod proof_compression; #[macro_export] macro_rules! layer_0 { diff --git a/aggregator/src/tests/proof_aggregation.rs b/aggregator/src/tests/aggregation.rs similarity index 98% rename from aggregator/src/tests/proof_aggregation.rs rename to aggregator/src/tests/aggregation.rs index ed69a415f5..92aa455678 100644 --- a/aggregator/src/tests/proof_aggregation.rs +++ b/aggregator/src/tests/aggregation.rs @@ -22,9 +22,9 @@ fn test_mock_aggregation() { fs::create_dir(path).unwrap(); // inner circuit: Mock circuit - let k0 = 19; + let k0 = 8; // aggregation - let k1 = 26; + let k1 = 22; let mut rng = test_rng(); let params = gen_srs(k1); @@ -74,7 +74,7 @@ fn test_aggregation_circuit() { fs::create_dir(path).unwrap(); // inner circuit: Mock circuit - let k0 = 19; + let k0 = 8; // wide compression let k1 = 26; // thin compression diff --git a/aggregator/src/tests/aggregator_e2e.rs b/aggregator/src/tests/aggregator_e2e.rs index e5f047b0a9..e69d76ee9f 100644 --- a/aggregator/src/tests/aggregator_e2e.rs +++ b/aggregator/src/tests/aggregator_e2e.rs @@ -30,7 +30,7 @@ fn test_e2e() { fs::create_dir(path).unwrap(); // inner circuit: Mock circuit - let k0 = 19; + let k0 = 8; // wide compression let k1 = 21; // thin compression diff --git a/aggregator/src/tests/proof_compression.rs b/aggregator/src/tests/compression.rs similarity index 69% rename from aggregator/src/tests/proof_compression.rs rename to aggregator/src/tests/compression.rs index b07dc1e0c4..c412758645 100644 --- a/aggregator/src/tests/proof_compression.rs +++ b/aggregator/src/tests/compression.rs @@ -17,25 +17,39 @@ use crate::{ }; #[test] -fn test_proof_compression() { +fn test_mock_compression() { env_logger::init(); let dir = format!("data/{}", process::id()); let path = Path::new(dir.as_str()); fs::create_dir(path).unwrap(); - let k0 = 19; - let k1 = 25; + let k0 = 8; + let k1 = 22; let mut rng = test_rng(); - let layer_1_params = gen_srs(k1); + let params = gen_srs(k1); // Proof for test circuit let circuit = MockChunkCircuit::random(&mut rng, true); - let layer_0_snark = layer_0!(circuit, MockChunkCircuit, layer_1_params, k0, path); + let layer_0_snark = layer_0!(circuit, MockChunkCircuit, params, k0, path); - std::env::set_var("VERIFY_CONFIG", "./configs/compression_thin.config"); - compression_layer_evm!(layer_0_snark, layer_1_params, k1, path, 1) + std::env::set_var("VERIFY_CONFIG", "./configs/compression_wide.config"); + // layer 1 proof compression + { + let param = { + let mut param = params; + param.downsize(k1); + param + }; + let compression_circuit = CompressionCircuit::new(¶m, &layer_0_snarks, true, &mut rng); + let instance = compression_circuit.instances(); + println!("instance length {:?}", instance.len()); + + let mock_prover = MockProver::::run(k1, &compression_circuit, instance).unwrap(); + + mock_prover.assert_satisfied_par() + } } // This test takes about 1 hour on CPU diff --git a/aggregator/src/tests/mock_chunk/circuit.rs b/aggregator/src/tests/mock_chunk/circuit.rs index ffcba61df6..0867e7217f 100644 --- a/aggregator/src/tests/mock_chunk/circuit.rs +++ b/aggregator/src/tests/mock_chunk/circuit.rs @@ -1,6 +1,8 @@ +use std::iter; + use ark_std::{end_timer, start_timer}; use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, + circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::bn256::Fr, plonk::{Circuit, ConstraintSystem, Error}, }; @@ -8,10 +10,7 @@ use zkevm_circuits::util::{Challenges, SubCircuitConfig}; use crate::ChunkHash; -use super::{ - config::{MockChunkCircuitConfig, MockChunkCircuitConfigArgs}, - MockChunkCircuit, -}; +use super::{config::MockPlonkConfig, MockChunkCircuit}; impl MockChunkCircuit { pub(crate) fn random(r: &mut R, is_fresh: bool) -> Self { @@ -21,31 +20,19 @@ impl MockChunkCircuit { chunk: ChunkHash::mock_chunk_hash(r), } } - - /// Public input hash for a given chunk is defined as - /// keccak( chain id || prev state root || post state root || withdraw root || data hash ) - fn extract_hash_preimages(&self) -> Vec { - self.chunk.extract_hash_preimage() - } } impl Circuit for MockChunkCircuit { + type Config = MockPlonkConfig; type FloorPlanner = SimpleFloorPlanner; - type Config = (MockChunkCircuitConfig, Challenges); - fn without_witnesses(&self) -> Self { Self::default() } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let challenges = Challenges::construct(meta); - let challenges_exprs = challenges.exprs(meta); - let args = MockChunkCircuitConfigArgs { - challenges: challenges_exprs, - }; - let config = MockChunkCircuitConfig::new(meta, args); - (config, challenges) + meta.set_minimum_degree(4); + MockPlonkConfig::configure(meta) } fn synthesize( @@ -53,23 +40,54 @@ impl Circuit for MockChunkCircuit { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - let (config, challenge) = config; - let challenges = challenge.values(&layouter); + layouter.assign_region( + || "mock circuit", + |mut region| { + let acc_len = if self.is_fresh { 0 } else { 12 }; - // extract all the hashes and load them to the hash table - let timer = start_timer!(|| ("extract hash").to_string()); - let preimages = self.extract_hash_preimages(); - end_timer!(timer); + for (i, byte) in iter::repeat(0) + .take(acc_len) + .chain( + self.chunk + .chain_id + .to_be_bytes() + .iter() + .chain( + self.chunk + .data_hash + .as_bytes() + .iter() + .chain(self.chunk.public_input_hash().as_bytes().iter()), + ) + .copied(), + ) + .enumerate() + { + // "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", + region.assign_advice( + || "a", + config.a, + i, + || Value::known(Fr::from(byte as u64)), + )?; + region.assign_advice(|| "b", config.b, i, || Value::known(Fr::zero()))?; + region.assign_advice(|| "c", config.c, i, || Value::known(Fr::zero()))?; - let timer = start_timer!(|| ("load aux table").to_string()); - config - .keccak_circuit_config - .load_aux_tables(&mut layouter)?; - end_timer!(timer); + region.assign_fixed(|| "q_a", config.q_a, i, || Value::known(-Fr::one()))?; + region.assign_fixed(|| "q_b", config.q_b, i, || Value::known(Fr::zero()))?; + region.assign_fixed(|| "q_c", config.q_c, i, || Value::known(Fr::zero()))?; + region.assign_fixed(|| "q_ab", config.q_ab, i, || Value::known(Fr::zero()))?; + region.assign_fixed( + || "constant", + config.constant, + i, + || Value::known(Fr::zero()), + )?; + } + Ok(()) + }, + )?; - let timer = start_timer!(|| ("assign cells").to_string()); - config.assign(&mut layouter, challenges, &preimages, self.is_fresh)?; - end_timer!(timer); Ok(()) } } diff --git a/aggregator/src/tests/mock_chunk/config.rs b/aggregator/src/tests/mock_chunk/config.rs index 49d54c7985..d5e7f4a4f0 100644 --- a/aggregator/src/tests/mock_chunk/config.rs +++ b/aggregator/src/tests/mock_chunk/config.rs @@ -1,181 +1,60 @@ -use ark_std::{end_timer, start_timer}; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Value}, halo2curves::bn256::Fr, - plonk::{Column, ConstraintSystem, Error, Expression, Instance}, + plonk::{Advice, Column, ConstraintSystem, Fixed, Instance}, + poly::Rotation, }; -use zkevm_circuits::{ - keccak_circuit::{ - keccak_packed_multi::multi_keccak, KeccakCircuitConfig, KeccakCircuitConfigArgs, - }, - table::KeccakTable, - util::{Challenges, SubCircuitConfig}, -}; - -use crate::{ - util::{capacity, get_indices}, - CHAIN_ID_LEN, LOG_DEGREE, -}; - -/// Config for MockChunkCircuit -#[derive(Clone, Debug)] -pub struct MockChunkCircuitConfig { - /// Instance column stores the aggregated rpi hash digest - pub(crate) hash_digest_column: Column, - - /// Keccak circuit config - pub(crate) keccak_circuit_config: KeccakCircuitConfig, +use snark_verifier::loader::halo2::halo2_ecc::halo2_base::halo2_proofs; + +#[derive(Clone, Copy)] +pub(crate) struct MockPlonkConfig { + pub(crate) a: Column, + pub(crate) b: Column, + pub(crate) c: Column, + pub(crate) q_a: Column, + pub(crate) q_b: Column, + pub(crate) q_c: Column, + pub(crate) q_ab: Column, + pub(crate) constant: Column, + #[allow(dead_code)] + pub(crate) instance: Column, } -/// Auxiliary arguments for BatchCircuit's Config -#[derive(Clone, Debug)] -pub struct MockChunkCircuitConfigArgs { - pub challenges: Challenges>, -} - -impl SubCircuitConfig for MockChunkCircuitConfig { - type ConfigArgs = MockChunkCircuitConfigArgs; - - /// Return a new BatchCircuitConfig - fn new(meta: &mut ConstraintSystem, config_args: Self::ConfigArgs) -> Self { - // hash configuration - let keccak_circuit_config = { - let keccak_table = KeccakTable::construct(meta); - - let keccak_circuit_config_args = KeccakCircuitConfigArgs { - keccak_table, - challenges: config_args.challenges, - }; - KeccakCircuitConfig::new(meta, keccak_circuit_config_args) - }; - - // The current code base is hardcoded for KeccakCircuit configured - // with 300 rows and 87 columns per hash call. - let columns = keccak_circuit_config.cell_manager.columns(); - - assert_eq!( - columns.len(), - 87, - "cell manager configuration does not match the hard coded setup" - ); - - // enabling equality for preimage and digest columns - meta.enable_equality(columns[6].advice); - // digest column - meta.enable_equality(columns.last().unwrap().advice); - - // Instance column stores the output of the hash - let hash_digest_column = meta.instance_column(); - // public input column - meta.enable_equality(hash_digest_column); - - MockChunkCircuitConfig { - hash_digest_column, - keccak_circuit_config, - } - } -} - -impl MockChunkCircuitConfig { - /// Input the hash input bytes, - /// assign the circuit for hash function, - /// return cells for the hash inputs and digests. - #[allow(clippy::type_complexity)] - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - challenges: Challenges>, - preimages: &[u8], - is_fresh: bool, - ) -> Result< - Vec>, // digest cells - Error, - > { - let mut is_first_time = true; - let num_rows = 1 << LOG_DEGREE; - - let timer = start_timer!(|| ("multi keccak").to_string()); - let witness = multi_keccak(&[preimages.to_vec()], challenges, capacity(num_rows))?; - end_timer!(timer); - - // extract the indices of the rows for which the preimage and the digest cells lie in - let (preimage_indices, digest_indices) = get_indices(&[preimages.to_vec()]); - let mut preimage_indices_iter = preimage_indices.iter(); - let mut digest_indices_iter = digest_indices.iter(); - - let mut hash_input_cells = vec![]; - let mut hash_output_cells = vec![]; - - let mut cur_preimage_index = preimage_indices_iter.next(); - let mut cur_digest_index = digest_indices_iter.next(); - - layouter.assign_region( - || "assign keccak rows", - |mut region| { - if is_first_time { - is_first_time = false; - let offset = witness.len() - 1; - self.keccak_circuit_config - .set_row(&mut region, offset, &witness[offset])?; - return Ok(()); - } - // ==================================================== - // Step 1. Extract the hash cells - // ==================================================== - let timer = start_timer!(|| "assign row"); - for (offset, keccak_row) in witness.iter().enumerate() { - let row = - self.keccak_circuit_config - .set_row(&mut region, offset, keccak_row)?; - - if cur_preimage_index.is_some() && *cur_preimage_index.unwrap() == offset { - hash_input_cells.push(row[6].clone()); - cur_preimage_index = preimage_indices_iter.next(); - } - - if cur_digest_index.is_some() && *cur_digest_index.unwrap() == offset { - hash_output_cells.push(row.last().unwrap().clone()); - cur_digest_index = digest_indices_iter.next(); - } - } - end_timer!(timer); - - // sanity: hash output is 32 cells - assert_eq!(32, hash_output_cells.len()); - - Ok(()) +impl MockPlonkConfig { + pub(crate) fn configure(meta: &mut ConstraintSystem) -> Self { + let [a, b, c] = [(); 3].map(|_| meta.advice_column()); + let [q_a, q_b, q_c, q_ab, constant] = [(); 5].map(|_| meta.fixed_column()); + let instance = meta.instance_column(); + + [a, b, c].map(|column| meta.enable_equality(column)); + + meta.create_gate( + "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", + |meta| { + let [a, b, c] = [a, b, c].map(|column| meta.query_advice(column, Rotation::cur())); + let [q_a, q_b, q_c, q_ab, constant] = [q_a, q_b, q_c, q_ab, constant] + .map(|column| meta.query_fixed(column, Rotation::cur())); + let instance = meta.query_instance(instance, Rotation::cur()); + Some( + q_a * a.clone() + + q_b * b.clone() + + q_c * c + + q_ab * a * b + + constant + + instance, + ) }, - )?; - - // ==================================================== - // Step 2. check the cells match the public input - // ==================================================== - let acc_len = if is_fresh { 0 } else { 12 }; + ); - // chunk's data hash - for i in 0..32 { - layouter.constrain_instance( - hash_input_cells[i + 96 + CHAIN_ID_LEN].cell(), - self.hash_digest_column, - i + CHAIN_ID_LEN + acc_len, - )?; - } - // chunk's public_input_hash - for i in 0..4 { - for j in 0..8 { - // digest in circuit has a different endianness - layouter.constrain_instance( - hash_output_cells[(3 - i) * 8 + j].cell(), - self.hash_digest_column, - i * 8 + j + 32 + CHAIN_ID_LEN + acc_len, - )?; - } + MockPlonkConfig { + a, + b, + c, + q_a, + q_b, + q_c, + q_ab, + constant, + instance, } - // chain id - for (i, cell) in hash_input_cells.iter().enumerate().take(CHAIN_ID_LEN) { - layouter.constrain_instance(cell.cell(), self.hash_digest_column, i + acc_len)?; - } - - Ok(hash_output_cells) } } diff --git a/aggregator/tests.sh b/aggregator/tests.sh index c95568840e..91fca11530 100755 --- a/aggregator/tests.sh +++ b/aggregator/tests.sh @@ -1,7 +1,8 @@ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_aggregation -- --ignored --nocapture 2>&1 | tee mock_aggregation.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_compression -- --nocapture 2>&1 | tee compression.log -# the following 4 tests takes super long time -# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_proof_compression -- --nocapture 2>&1 | tee compression.log -# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --ignored --nocapture 2>&1 | tee compression2.log +# the following 3 tests takes super long time # RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_aggregation_circuit -- --ignored --nocapture 2>&1 | tee aggregation.log -# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_e2e -- --ignored --nocapture 2>&1 | tee aggregation.log +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_two_layer_proof_compression -- --ignored --nocapture 2>&1 | tee compression_2_layer.log +# RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_e2e -- --ignored --nocapture 2>&1 | tee aggregation_e2e.log From 6b124cf23827618c40b3cb5b8d90c540249fc743 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Fri, 23 Jun 2023 17:37:02 -0400 Subject: [PATCH 39/41] [fix] compiling error --- aggregator/configs/aggregation.config | 1 - aggregator/src/tests.rs | 2 +- aggregator/src/tests/compression.rs | 4 ++-- aggregator/src/tests/{aggregator_e2e.rs => end_to_end.rs} | 0 4 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 aggregator/configs/aggregation.config rename aggregator/src/tests/{aggregator_e2e.rs => end_to_end.rs} (100%) diff --git a/aggregator/configs/aggregation.config b/aggregator/configs/aggregation.config deleted file mode 100644 index 2416aff792..0000000000 --- a/aggregator/configs/aggregation.config +++ /dev/null @@ -1 +0,0 @@ -{"strategy":"Simple","degree":20,"num_advice":[4],"num_lookup_advice":[1],"num_fixed":1,"lookup_bits":20,"limb_bits":88,"num_limbs":3} diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index 0348e57ac6..e80b41178d 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -1,5 +1,5 @@ pub(crate) mod aggregation; -pub(crate) mod aggregator_e2e; +pub(crate) mod end_to_end; pub(crate) mod compression; pub(crate) mod mock_chunk; diff --git a/aggregator/src/tests/compression.rs b/aggregator/src/tests/compression.rs index c412758645..f03166a032 100644 --- a/aggregator/src/tests/compression.rs +++ b/aggregator/src/tests/compression.rs @@ -1,7 +1,7 @@ use std::{fs, path::Path, process}; use ark_std::{end_timer, start_timer, test_rng}; -use halo2_proofs::{halo2curves::bn256::Bn256, poly::commitment::Params}; +use halo2_proofs::{halo2curves::bn256::{Bn256, Fr}, poly::commitment::Params, dev::MockProver}; use snark_verifier::{ loader::halo2::halo2_ecc::halo2_base::{halo2_proofs, utils::fs::gen_srs}, pcs::kzg::{Bdfg21, Kzg}, @@ -42,7 +42,7 @@ fn test_mock_compression() { param.downsize(k1); param }; - let compression_circuit = CompressionCircuit::new(¶m, &layer_0_snarks, true, &mut rng); + let compression_circuit = CompressionCircuit::new(¶m, layer_0_snark, true, &mut rng); let instance = compression_circuit.instances(); println!("instance length {:?}", instance.len()); diff --git a/aggregator/src/tests/aggregator_e2e.rs b/aggregator/src/tests/end_to_end.rs similarity index 100% rename from aggregator/src/tests/aggregator_e2e.rs rename to aggregator/src/tests/end_to_end.rs From ec604b63af83156cd439f7e70f164e4ec4c4b432 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Fri, 23 Jun 2023 18:15:45 -0400 Subject: [PATCH 40/41] [fix] parameters for tests --- aggregator/src/tests/aggregation.rs | 2 +- aggregator/tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aggregator/src/tests/aggregation.rs b/aggregator/src/tests/aggregation.rs index 92aa455678..0fdb66c1f6 100644 --- a/aggregator/src/tests/aggregation.rs +++ b/aggregator/src/tests/aggregation.rs @@ -24,7 +24,7 @@ fn test_mock_aggregation() { // inner circuit: Mock circuit let k0 = 8; // aggregation - let k1 = 22; + let k1 = 23; let mut rng = test_rng(); let params = gen_srs(k1); diff --git a/aggregator/tests.sh b/aggregator/tests.sh index 91fca11530..e871301512 100755 --- a/aggregator/tests.sh +++ b/aggregator/tests.sh @@ -1,5 +1,5 @@ RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_chunk_prover -- --nocapture 2>&1 | tee mock_chunk.log -RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_aggregation -- --ignored --nocapture 2>&1 | tee mock_aggregation.log +RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_aggregation -- --nocapture 2>&1 | tee mock_aggregation.log RUST_LOG=trace MODE=greeter cargo test --release --features=print-trace test_mock_compression -- --nocapture 2>&1 | tee compression.log # the following 3 tests takes super long time From a04b1f014b161dbbe4cac443e3783f3773d8aa7d Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 29 Jun 2023 21:00:41 +0800 Subject: [PATCH 41/41] Add a convertion from witness `Block` to `ChunkHash` (#577) * Add a convertion witness Block to `ChunkHash`. * Fix lint. --- aggregator/src/chunk.rs | 48 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/aggregator/src/chunk.rs b/aggregator/src/chunk.rs index 734a9bd750..84712936dc 100644 --- a/aggregator/src/chunk.rs +++ b/aggregator/src/chunk.rs @@ -1,7 +1,10 @@ //! This module implements `Chunk` related data types. //! A chunk is a list of blocks. -use eth_types::H256; +use eth_types::{ToBigEndian, H256}; use ethers_core::utils::keccak256; +use halo2_proofs::halo2curves::bn256::Fr; +use std::iter; +use zkevm_circuits::witness::Block; #[derive(Default, Debug, Clone, Copy)] /// A chunk is a set of continuous blocks. @@ -23,6 +26,49 @@ pub struct ChunkHash { pub(crate) data_hash: H256, } +impl From<&Block> for ChunkHash { + fn from(block: &Block) -> Self { + // + + let data_bytes = iter::empty() + .chain(block.context.ctxs.iter().flat_map(|(b_num, b_ctx)| { + let num_txs = block + .txs + .iter() + .filter(|tx| tx.block_number == *b_num) + .count() as u16; + + iter::empty() + // Block Values + .chain(b_ctx.number.as_u64().to_be_bytes()) + .chain(b_ctx.timestamp.as_u64().to_be_bytes()) + .chain(b_ctx.base_fee.to_be_bytes()) + .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())) + .collect::>(); + + let data_hash = H256(keccak256(data_bytes)); + + let post_state_root = block + .context + .ctxs + .last_key_value() + .map(|(_, b_ctx)| b_ctx.eth_block.state_root) + .unwrap_or(H256(block.prev_state_root.to_be_bytes())); + + Self { + chain_id: block.chain_id, + prev_state_root: H256(block.prev_state_root.to_be_bytes()), + post_state_root, + withdraw_root: H256(block.withdraw_root.to_be_bytes()), + data_hash, + } + } +} + impl ChunkHash { /// Sample a chunk hash from random (for testing) #[cfg(test)]