diff --git a/Cargo.lock b/Cargo.lock index 6666e8caeb..8db30a4681 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ version = "0.1.0" dependencies = [ "ark-std", "env_logger 0.10.0", - "eth-types 0.1.0", + "eth-types", "ethers-core", "halo2_proofs", "itertools", @@ -2216,7 +2216,7 @@ dependencies = [ [[package]] name = "halo2_proofs" version = "0.2.0" -source = "git+https://github.com/scroll-tech/halo2.git?branch=v0.4#3d40ae4968759ac4516c5f9c45ad20140e2d35d5" +source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#aab39d5c107fdeec62ce7d2aeec6b72de681ba23" dependencies = [ "ark-std", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index af51f7fcbf..2693bd9cd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v0.17.0" } ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v0.17.0" } [patch."https://github.com/privacy-scaling-explorations/halo2.git"] -halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v0.4" } +halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" } [patch."https://github.com/privacy-scaling-explorations/poseidon.git"] poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "scroll-dev-0220" } [patch."https://github.com/privacy-scaling-explorations/halo2curves.git"] diff --git a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs index 653de6a5d0..83a912104c 100644 --- a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs +++ b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs @@ -57,14 +57,72 @@ pub fn unroll_with_codehash(code_hash: U256, bytes: Vec) -> Unroll UnrolledBytecode { bytes, rows } } -/// re-export bytes wrapped in hash field -pub use super::circuit::to_poseidon_hash::HASHBLOCK_BYTES_IN_FIELD; use crate::table::PoseidonTable; +/// re-export bytes wrapped in hash field +/// specify byte in field for encoding bytecode +pub use bus_mapping::util::POSEIDON_HASH_BYTES_IN_FIELD as HASHBLOCK_BYTES_IN_FIELD; + +/// Get unrolled hash inputs as inputs to hash circuit +pub fn unroll_to_hash_input( + code: impl ExactSizeIterator, +) -> Vec<[F; INPUT_LEN]> { + let fl_cnt = code.len() / BYTES_IN_FIELD; + let fl_cnt = if code.len() % BYTES_IN_FIELD != 0 { + fl_cnt + 1 + } else { + fl_cnt + }; + + let (msgs, _) = code + .chain(std::iter::repeat(0)) + .take(fl_cnt * BYTES_IN_FIELD) + .fold((Vec::new(), Vec::new()), |(mut msgs, mut cache), bt| { + cache.push(bt); + if cache.len() == BYTES_IN_FIELD { + let mut buf: [u8; 64] = [0; 64]; + U256::from_big_endian(&cache).to_little_endian(&mut buf[0..32]); + msgs.push(F::from_bytes_wide(&buf)); + cache.clear(); + } + (msgs, cache) + }); + + let input_cnt = msgs.len() / INPUT_LEN; + let input_cnt = if msgs.len() % INPUT_LEN != 0 { + input_cnt + 1 + } else { + input_cnt + }; + if input_cnt == 0 { + return Vec::new(); + } + + let (mut inputs, last) = msgs + .into_iter() + .chain(std::iter::repeat(F::zero())) + .take(input_cnt * INPUT_LEN) + .fold( + (Vec::new(), [None; INPUT_LEN]), + |(mut msgs, mut v_arr), f| { + if let Some(v) = v_arr.iter_mut().find(|v| v.is_none()) { + v.replace(f); + (msgs, v_arr) + } else { + msgs.push(v_arr.map(|v| v.unwrap())); + let mut v_arr = [None; INPUT_LEN]; + v_arr[0].replace(f); + (msgs, v_arr) + } + }, + ); + + inputs.push(last.map(|v| v.unwrap())); + inputs +} /// Apply default constants in mod pub fn unroll_to_hash_input_default( code: impl ExactSizeIterator, ) -> Vec<[F; PoseidonTable::INPUT_WIDTH]> { - use super::circuit::to_poseidon_hash::unroll_to_hash_input; unroll_to_hash_input::(code) } diff --git a/zkevm-circuits/src/bytecode_circuit/circuit.rs b/zkevm-circuits/src/bytecode_circuit/circuit.rs index 6a5f422ec8..6781f41ecd 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit.rs @@ -24,6 +24,7 @@ use super::{ }; /// An extended circuit for binding with poseidon +#[cfg(feature = "scroll")] pub mod to_poseidon_hash; #[cfg(feature = "onephase")] diff --git a/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs b/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs index 231b8bf102..6cf9a69666 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs @@ -15,20 +15,15 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Error, Expression, VirtualCells}, poly::Rotation, }; -use hash_circuit::hash::HASHABLE_DOMAIN_SPEC; -#[cfg(feature = "scroll-trace")] -use itertools::Itertools; use log::trace; use std::vec; +pub use super::super::bytecode_unroller::HASHBLOCK_BYTES_IN_FIELD; use super::{ super::bytecode_unroller::{BytecodeRow, UnrolledBytecode}, BytecodeCircuitConfig, BytecodeCircuitConfigArgs, }; -/// specify byte in field for encoding bytecode -pub const HASHBLOCK_BYTES_IN_FIELD: usize = bus_mapping::util::POSEIDON_HASH_BYTES_IN_FIELD; - #[derive(Clone, Debug)] /// Bytecode circuit (for hash block) configuration /// basically the BytecodeCircuit include two parts: @@ -59,7 +54,6 @@ impl ToHashBlockCircuitConfig Self { let base_conf_cl = base_conf.clone(); let bytecode_table = base_conf.bytecode_table; - let code_hash = bytecode_table.code_hash; let q_enable = base_conf.q_enable; // from 0 to last avaliable row @@ -293,57 +287,89 @@ impl ToHashBlockCircuitConfig, inp_i: usize| { - debug_assert_eq!(PoseidonTable::INPUT_WIDTH, 2); - [ - meta.query_fixed(poseidon_table.q_enable, Rotation::cur()), - meta.query_advice(poseidon_table.hash_id, Rotation::cur()), - meta.query_advice( - match inp_i { - 0 => poseidon_table.input0, - 1 => poseidon_table.input1, - _ => unreachable!("valid poseidon input index"), - }, - Rotation::cur(), - ), - meta.query_advice(poseidon_table.control, Rotation::cur()), - ] - }; - - // we use a special selection exp for only 2 indexs - let field_selector = |meta: &mut VirtualCells| { - let field_index = meta.query_advice(field_index, Rotation::cur()) - 1.expr(); - [1.expr() - field_index.clone(), field_index] - }; - - let domain_spec_factor = Expression::Constant(F::from_u128(HASHABLE_DOMAIN_SPEC)); - - // poseidon lookup: - // * PoseidonTable::INPUT_WIDTH lookups for each input field - // * PoseidonTable::INPUT_WIDTH -1 lookups for the padded zero input - // so we have 2*PoseidonTable::INPUT_WIDTH -1 lookups #[cfg(feature = "scroll-trace")] - for i in 0..PoseidonTable::INPUT_WIDTH { - meta.lookup_any("poseidon input", |meta| { + { + use hash_circuit::hash::HASHABLE_DOMAIN_SPEC; + use itertools::Itertools; + let code_hash = bytecode_table.code_hash; + let pick_hash_tbl_cols = |meta: &mut VirtualCells, inp_i: usize| { + debug_assert_eq!(PoseidonTable::INPUT_WIDTH, 2); + [ + meta.query_fixed(poseidon_table.q_enable, Rotation::cur()), + meta.query_advice(poseidon_table.hash_id, Rotation::cur()), + meta.query_advice( + match inp_i { + 0 => poseidon_table.input0, + 1 => poseidon_table.input1, + _ => unreachable!("valid poseidon input index"), + }, + Rotation::cur(), + ), + meta.query_advice(poseidon_table.control, Rotation::cur()), + ] + }; + + // we use a special selection exp for only 2 indexs + let field_selector = |meta: &mut VirtualCells| { + let field_index = meta.query_advice(field_index, Rotation::cur()) - 1.expr(); + [1.expr() - field_index.clone(), field_index] + }; + + let domain_spec_factor = Expression::Constant(F::from_u128(HASHABLE_DOMAIN_SPEC)); + + // poseidon lookup: + // * PoseidonTable::INPUT_WIDTH lookups for each input field + // * PoseidonTable::INPUT_WIDTH -1 lookups for the padded zero input + // so we have 2*PoseidonTable::INPUT_WIDTH -1 lookups + for i in 0..PoseidonTable::INPUT_WIDTH { + meta.lookup_any("poseidon input", |meta| { + // Conditions: + // - On the row at **field border** (`is_field_border == 1`) + // - the field_index match current i + let enable = and::expr(vec![ + meta.query_advice(is_field_border, Rotation::cur()), + field_selector(meta)[i].clone(), + ]); + let mut constraints = Vec::new(); + + let lookup_inputs = [ + 1.expr(), + meta.query_advice(code_hash, Rotation::cur()), + meta.query_advice(field_input, Rotation::cur()), + meta.query_advice(control_length, Rotation::cur()) + * domain_spec_factor.clone(), + ]; + + for (input_expr, table_expr) in lookup_inputs + .into_iter() + .zip_eq(pick_hash_tbl_cols(meta, i)) + { + constraints.push((enable.clone() * input_expr, table_expr)) + } + constraints + }); + } + + // the canonical form should be `for i in 1..PoseidonTable::INPUT_WIDTH{...}` + meta.lookup_any("poseidon input padding zero for final", |meta| { // Conditions: - // - On the row at **field border** (`is_field_border == 1`) - // - the field_index match current i + // - On the row with the last byte (`is_byte_to_header == 1`) + // - Not padding + // - the (0 begin) field_index is 1 (for we have only 2 input field) let enable = and::expr(vec![ - meta.query_advice(is_field_border, Rotation::cur()), - field_selector(meta)[i].clone(), + is_byte_to_header(meta), + 2.expr() - meta.query_advice(field_index, Rotation::cur()), ]); let mut constraints = Vec::new(); - let lookup_inputs = [ 1.expr(), meta.query_advice(code_hash, Rotation::cur()), - meta.query_advice(field_input, Rotation::cur()), - meta.query_advice(control_length, Rotation::cur()) * domain_spec_factor.clone(), + 0.expr(), + meta.query_advice(control_length, Rotation::cur()) * domain_spec_factor, ]; - for (input_expr, table_expr) in lookup_inputs .into_iter() - .zip_eq(pick_hash_tbl_cols(meta, i)) + .zip_eq(pick_hash_tbl_cols(meta, 1)) { constraints.push((enable.clone() * input_expr, table_expr)) } @@ -351,33 +377,6 @@ impl ToHashBlockCircuitConfig SubCircuitConfig for ToHashBlockCircuitConfig( - code: impl ExactSizeIterator, -) -> Vec<[F; INPUT_LEN]> { - use eth_types::U256; - - let fl_cnt = code.len() / BYTES_IN_FIELD; - let fl_cnt = if code.len() % BYTES_IN_FIELD != 0 { - fl_cnt + 1 - } else { - fl_cnt - }; - - let (msgs, _) = code - .chain(std::iter::repeat(0)) - .take(fl_cnt * BYTES_IN_FIELD) - .fold((Vec::new(), Vec::new()), |(mut msgs, mut cache), bt| { - cache.push(bt); - if cache.len() == BYTES_IN_FIELD { - let mut buf: [u8; 64] = [0; 64]; - U256::from_big_endian(&cache).to_little_endian(&mut buf[0..32]); - msgs.push(F::from_bytes_wide(&buf)); - cache.clear(); - } - (msgs, cache) - }); - - let input_cnt = msgs.len() / INPUT_LEN; - let input_cnt = if msgs.len() % INPUT_LEN != 0 { - input_cnt + 1 - } else { - input_cnt - }; - if input_cnt == 0 { - return Vec::new(); - } - - let (mut inputs, last) = msgs - .into_iter() - .chain(std::iter::repeat(F::zero())) - .take(input_cnt * INPUT_LEN) - .fold( - (Vec::new(), [None; INPUT_LEN]), - |(mut msgs, mut v_arr), f| { - if let Some(v) = v_arr.iter_mut().find(|v| v.is_none()) { - v.replace(f); - (msgs, v_arr) - } else { - msgs.push(v_arr.map(|v| v.unwrap())); - let mut v_arr = [None; INPUT_LEN]; - v_arr[0].replace(f); - (msgs, v_arr) - } - }, - ); - - inputs.push(last.map(|v| v.unwrap())); - inputs -} - /// test module #[cfg(any(feature = "test", test))] #[cfg(test)] @@ -739,6 +678,7 @@ pub mod tests { // use super::super::tests::get_randomness; // use crate::{bytecode_circuit::dev::test_bytecode_circuit_unrolled, // util::DEFAULT_RAND}; use eth_types::Bytecode; + use crate::bytecode_circuit::bytecode_unroller::unroll_to_hash_input; use halo2_proofs::halo2curves::bn256::Fr; #[test] diff --git a/zkevm-circuits/src/poseidon_circuit.rs b/zkevm-circuits/src/poseidon_circuit.rs index 24c49d9d06..c9b5d1eaf1 100644 --- a/zkevm-circuits/src/poseidon_circuit.rs +++ b/zkevm-circuits/src/poseidon_circuit.rs @@ -3,20 +3,15 @@ use crate::{ bytecode_circuit::bytecode_unroller::HASHBLOCK_BYTES_IN_FIELD, table::PoseidonTable, util::{Challenges, SubCircuit, SubCircuitConfig}, - witness, + witness::{self}, }; //use bus_mapping::state_db::CodeDB; use eth_types::Field; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, - halo2curves::bn256::Fr, plonk::{Circuit, ConstraintSystem, Error}, }; use hash_circuit::hash::{Hashable, PoseidonHashChip, PoseidonHashConfig, PoseidonHashTable}; -use itertools::Itertools; -use mpt_zktrie::mpt_circuits::{ - gadgets::mpt_update::hash_traces, serde::SMTTrace, types::Proof, MPTProofType, -}; /// re-wrapping for mpt circuit #[derive(Default, Clone, Debug)] @@ -67,34 +62,14 @@ impl SubCircuit for PoseidonCircuit { // without any feature we just synthesis an empty poseidon circuit #[cfg(feature = "zktrie")] { - let traces: Vec<(MPTProofType, SMTTrace)> = block - .mpt_updates - .proof_types - .iter() - .cloned() - .zip_eq(block.mpt_updates.smt_traces.iter().cloned()) - .collect(); - let proofs: Vec = traces.into_iter().map(Proof::from).collect(); - let triples: Vec<(Fr, Fr, Fr)> = hash_traces(&proofs); - let triples: Vec<(F, F, F)> = triples - .into_iter() - .unique_by(|(a, b, c)| (a.to_bytes(), b.to_bytes(), c.to_bytes())) - .map(|(a, b, c)| (a.into(), b.into(), c.into())) - .collect(); - for elems in &triples { - if elems.2.is_zero_vartime() { - log::info!("zero hash {:?}", elems); - } - } - + let triples = get_storage_poseidon_witness(block); if triples.len() > max_hashes { log::error!( - "poseidon max_hashes: {:?} too low. {:?} needed", + "poseidon max_hashes: {:?} not enough. {:?} needed by zktrie proof", max_hashes, triples.len() ); } - poseidon_table_data.constant_inputs_with_check(&triples); } #[cfg(feature = "poseidon-codehash")] @@ -122,15 +97,7 @@ impl SubCircuit for PoseidonCircuit { #[cfg(feature = "zktrie")] let acc = { let mut cnt = acc; - let traces: Vec<_> = block - .mpt_updates - .proof_types - .iter() - .cloned() - .zip_eq(block.mpt_updates.smt_traces.iter().cloned()) - .collect(); - let proofs: Vec = traces.into_iter().map(Proof::from).collect(); - cnt += hash_traces(&proofs).len(); + cnt += get_storage_poseidon_witness(block).len(); cnt }; #[cfg(feature = "poseidon-codehash")] @@ -197,3 +164,23 @@ impl Circuit for PoseidonCircuit { self.synthesize_sub(&config, &challenges, &mut layouter) } } + +#[cfg(feature = "zktrie")] +fn get_storage_poseidon_witness(block: &crate::witness::Block) -> Vec<(F, F, F)> { + use itertools::Itertools; + use mpt_zktrie::mpt_circuits::{gadgets::mpt_update::hash_traces, types::Proof}; + hash_traces( + &block + .mpt_updates + .proof_types + .iter() + .cloned() + .zip_eq(block.mpt_updates.smt_traces.iter().cloned()) + .map(Proof::from) + .collect_vec(), + ) + .into_iter() + .unique_by(|(a, b, c)| (a.to_bytes(), b.to_bytes(), c.to_bytes())) + .map(|(a, b, c)| (a.into(), b.into(), c.into())) + .collect() +} diff --git a/zkevm-circuits/src/sig_circuit/ecdsa.rs b/zkevm-circuits/src/sig_circuit/ecdsa.rs index 3107e3de9c..5e480704d6 100644 --- a/zkevm-circuits/src/sig_circuit/ecdsa.rs +++ b/zkevm-circuits/src/sig_circuit/ecdsa.rs @@ -1,5 +1,5 @@ //! This module implements the ECDSA circuit. Modified from -//! https://github.com/scroll-tech/halo2-lib/blob/530e744232860641f9533c9b9f8c1fee57f54cab/halo2-ecc/src/ecc/ecdsa.rs#L16 +//! use halo2_base::{ gates::{GateInstructions, RangeInstructions},