diff --git a/Cargo.toml b/Cargo.toml index b0caa78e..865d3573 100644 --- a/Cargo.toml +++ b/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] -hash-circuit = { git = "https://github.com/scroll-tech/poseidon-circuit.git", branch = "scroll-dev-1230"} -halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "scroll-dev-1220" } +hash-circuit = { package = "poseidon-circuit", git = "https://github.com/scroll-tech/poseidon-circuit.git", branch = "halo2-ecc-snark-verifier-0220"} +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2022_09_10" } rand = "0.8" lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } @@ -16,6 +16,24 @@ num-bigint = "0.4" hex = "0.4" thiserror = "1.0" +[patch."https://github.com/privacy-scaling-explorations/halo2.git"] +halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "halo2-ecc-snark-verifier-0220" } + +# remove once +# https://github.com/privacy-scaling-explorations/poseidon/pull/7 +# https://github.com/privacy-scaling-explorations/halo2curves/pull/31 +# are merged +[patch."https://github.com/privacy-scaling-explorations/poseidon.git"] +poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "halo2-ecc-snark-verifier-0220" } + + +[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"] +halo2curves = { git = "https://github.com/scroll-tech/halo2curves.git", branch = "halo2-ecc-snark-verifier-0220" } + +[patch.crates-io] +# temporary solution to funty@1.2.0 being yanked, tracking issue: https://github.com/ferrilab/funty/issues/7 +funty = { git = "https://github.com/ferrilab/funty/", rev = "7ef0d890fbcd8b3def1635ac1a877fc298488446" } + [features] # printout the layout of circuits for demo and some unittests print_layout = ["halo2_proofs/dev-graph"] diff --git a/src/eth.rs b/src/eth.rs index 266a86db..5b587ae7 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -36,8 +36,8 @@ use super::mpt; use super::CtrlTransitionKind; use crate::operation::{Account, AccountOp, KeyValue}; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Chip, Region, Value}, + ff::PrimeField, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, poly::Rotation, }; @@ -60,6 +60,7 @@ pub(crate) struct AccountGadget { new_state: AccountChipConfig, s_enable: Column, ctrl_type: Column, + s_ctrl_type: [Column; 4], state_change_key: Column, state_change_aux: [Column; 2], @@ -70,14 +71,19 @@ impl AccountGadget { 6 } + pub fn min_ctrl_types() -> usize { + 4 + } + /// create gadget from assigned cols, we need: /// + circuit selector * 1 /// + exported col * 8 (MUST by following sequence: layout_flag, s_enable, old_val, new_val, key_val and 3 ext field for old/new/key_val) /// + free col * 4 - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, sel: Selector, exported: &[Column], + s_ctrl_type: &[Column], free: &[Column], address_index: Option>, tables: mpt::MPTOpTables, @@ -88,16 +94,17 @@ impl AccountGadget { let ctrl_type = exported[0]; let data_old = exported[2]; let data_new = exported[3]; - let data_key = exported[4]; + let data_key = exported[4]; //the mpt gadget above it use the col as 'data key' + let state_change_key = data_key; //while we use it as 'state_change_key' let data_old_ext = exported[5]; let data_new_ext = exported[6]; - let state_change_key = data_key; + let s_ctrl_type = s_ctrl_type[0..4].try_into().expect("same size"); let old_state = AccountChip::configure( meta, sel, s_enable, - ctrl_type, + s_ctrl_type, data_old, data_old_ext, &free[0..2], @@ -107,7 +114,7 @@ impl AccountGadget { meta, sel, s_enable, - ctrl_type, + s_ctrl_type, data_new, data_new_ext, &free[2..4], @@ -119,10 +126,8 @@ impl AccountGadget { //transition meta.lookup("account row trans", |meta| { let s_enable = meta.query_advice(s_enable, Rotation::cur()) - * (Expression::Constant(Fp::one()) - - AccountChip::<'_, Fp>::lagrange_polynomial_for_row::<0>( - meta.query_advice(ctrl_type, Rotation::cur()), - )); + * (Expression::Constant(Fp::ONE) + - meta.query_advice(s_ctrl_type[0], Rotation::cur())); tables.build_lookup( s_enable, @@ -136,9 +141,7 @@ impl AccountGadget { meta.create_gate("address constraint", |meta| { let s_enable = meta.query_selector(sel) * meta.query_advice(s_enable, Rotation::cur()); - let row0 = AccountChip::<'_, Fp>::lagrange_polynomial_for_row::<0>( - meta.query_advice(ctrl_type, Rotation::cur()), - ); + let row0 = meta.query_advice(s_ctrl_type[0], Rotation::cur()); let address_limb_0 = meta.query_advice(old_state.intermediate_1, Rotation::cur()); let address_limb_1 = meta.query_advice(new_state.intermediate_1, Rotation::cur()); @@ -158,9 +161,7 @@ impl AccountGadget { meta.lookup_any("address hash", |meta| { let s_enable = meta.query_advice(s_enable, Rotation::cur()) - * AccountChip::<'_, Fp>::lagrange_polynomial_for_row::<0>( - meta.query_advice(ctrl_type, Rotation::cur()), - ); + * meta.query_advice(s_ctrl_type[0], Rotation::cur()); let address_limb_0 = meta.query_advice(old_state.intermediate_1, Rotation::cur()); let address_limb_1 = meta.query_advice(new_state.intermediate_1, Rotation::cur()); @@ -171,36 +172,36 @@ impl AccountGadget { } // this gate constraint each gadget handle at most one change in account data - meta.create_gate("single update for account data", |meta| { - let enable = meta.query_selector(sel) * meta.query_advice(s_enable, Rotation::cur()); - let data_diff = meta.query_advice(data_old, Rotation::cur()) - - meta.query_advice(data_new, Rotation::cur()); - let data_ext_diff = meta.query_advice(data_old_ext, Rotation::cur()) - - meta.query_advice(data_new_ext, Rotation::cur()); - - let is_diff_boolean = - data_diff.clone() * meta.query_advice(state_change_aux[0], Rotation::cur()); - let is_diff_ext_boolean = - data_ext_diff.clone() * meta.query_advice(state_change_aux[0], Rotation::cur()); - - let one = Expression::Constant(Fp::one()); - // switch A || B to ! (!A ^ !B) - let has_diff = one.clone() - - (one.clone() - is_diff_boolean.clone()) - * (one.clone() - is_diff_ext_boolean.clone()); - let diff_acc = has_diff - + meta.query_advice(s_enable, Rotation::prev()) - * meta.query_advice(data_key, Rotation::prev()); - let data_key = meta.query_advice(data_key, Rotation::cur()); - - vec![ - enable.clone() * data_diff * (one.clone() - is_diff_boolean), - enable.clone() * data_ext_diff * (one.clone() - is_diff_ext_boolean), - enable.clone() * (data_key.clone() - diff_acc), - enable * data_key.clone() * (one - data_key), - ] - }); - + /* meta.create_gate("single update for account data", |meta| { + let enable = meta.query_selector(sel) * meta.query_advice(s_enable, Rotation::cur()); + let data_diff = meta.query_advice(data_old, Rotation::cur()) + - meta.query_advice(data_new, Rotation::cur()); + let data_ext_diff = meta.query_advice(data_old_ext, Rotation::cur()) + - meta.query_advice(data_new_ext, Rotation::cur()); + + let is_diff_boolean = + data_diff.clone() * meta.query_advice(state_change_aux[0], Rotation::cur()); + let is_diff_ext_boolean = + data_ext_diff.clone() * meta.query_advice(state_change_aux[1], Rotation::cur()); + + let one = Expression::Constant(Fp::ONE); + // switch A || B to ! (!A ^ !B) + let has_diff = one.clone() + - (one.clone() - is_diff_boolean.clone()) + * (one.clone() - is_diff_ext_boolean.clone()); + let diff_acc = has_diff + + meta.query_advice(s_enable, Rotation::prev()) + * meta.query_advice(state_change_key, Rotation::prev()); + let state_change_key = meta.query_advice(state_change_key, Rotation::cur()); + + vec![ + enable.clone() * data_diff * (one.clone() - is_diff_boolean), + enable.clone() * data_ext_diff * (one.clone() - is_diff_ext_boolean), + enable.clone() * (state_change_key.clone() - diff_acc), + enable * state_change_key.clone() * (one - state_change_key), + ] + }); + */ //additional row // TODO: nonce now can increase more than 1, we should constraint it with lookup table (better than a compare circuit) // BUT: this constraint should also exist in state circui so do we really need it? @@ -216,16 +217,14 @@ impl AccountGadget { s_enable * row0 * (new_nonce.clone() - old_nonce.clone()) - * (new_nonce - old_nonce - Expression::Constant(Fp::one())), + * (new_nonce - old_nonce - Expression::Constant(Fp::ONE)), ] });*/ //additional row meta.create_gate("padding row", |meta| { let s_enable = meta.query_selector(sel) * meta.query_advice(s_enable, Rotation::cur()); - let row3 = AccountChip::<'_, Fp>::lagrange_polynomial_for_row::<3>( - meta.query_advice(ctrl_type, Rotation::cur()), - ); + let row3 = meta.query_advice(s_ctrl_type[3], Rotation::cur()); let old_root = meta.query_advice(data_old, Rotation::cur()); let new_root = meta.query_advice(data_new, Rotation::cur()); @@ -235,6 +234,7 @@ impl AccountGadget { Self { s_enable, ctrl_type, + s_ctrl_type, old_state, new_state, state_change_key, @@ -242,15 +242,15 @@ impl AccountGadget { } } - pub fn transition_rules() -> impl Iterator + Clone { + pub fn transition_rules() -> impl Iterator + Clone { TRANSMAP .iter() .copied() - .map(|(a, b)| (a, b, CtrlTransitionKind::Account as u32)) + .map(|(a, b)| ([a, b, 0], CtrlTransitionKind::Account as u32)) } /// assign data and enable flag for account circuit - pub fn assign<'d, Fp: FieldExt>( + pub fn assign<'d, Fp: PrimeField>( &self, region: &mut Region<'_, Fp>, offset: usize, @@ -298,7 +298,7 @@ impl AccountGadget { || "enable account circuit", self.s_enable, offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; region.assign_advice( || "account circuit rows", @@ -306,29 +306,35 @@ impl AccountGadget { offset, || Value::known(Fp::from(index as u64)), )?; + region.assign_advice( + || "enable s_ctrl", + self.s_ctrl_type[index as usize], + offset, + || Value::known(Fp::ONE), + )?; if index == LAST_ROW { region.assign_advice( || "padding last row", self.old_state.intermediate_2, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; region.assign_advice( || "padding last row", self.new_state.intermediate_2, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; } let data_delta = match index { - 0 => [data.0.nonce - data.1.nonce, Fp::zero()], - 1 => [data.0.balance - data.1.balance, Fp::zero()], + 0 => [data.0.nonce - data.1.nonce, Fp::ZERO], + 1 => [data.0.balance - data.1.balance, Fp::ZERO], 2 => [ data.0.codehash.0 - data.1.codehash.0, data.0.codehash.1 - data.1.codehash.1, ], - 3 => [data.0.state_root - data.1.state_root, Fp::zero()], + 3 => [data.0.state_root - data.1.state_root, Fp::ZERO], _ => unreachable!("no such row number"), }; @@ -344,7 +350,7 @@ impl AccountGadget { offset, || { Value::known(if bool::from(val.is_zero()) { - Fp::zero() + Fp::ZERO } else { val.invert().unwrap() }) @@ -356,13 +362,7 @@ impl AccountGadget { || "is data delta", self.state_change_key, offset, - || { - Value::known(if has_data_delta { - Fp::one() - } else { - Fp::zero() - }) - }, + || Value::known(if has_data_delta { Fp::ONE } else { Fp::ZERO }), )?; } @@ -385,7 +385,7 @@ struct AccountChip<'d, F> { data: &'d Account, } -impl Chip for AccountChip<'_, Fp> { +impl Chip for AccountChip<'_, Fp> { type Config = AccountChipConfig; type Loaded = Account; @@ -398,7 +398,7 @@ impl Chip for AccountChip<'_, Fp> { } } -impl<'d, Fp: FieldExt> AccountChip<'d, Fp> { +impl<'d, Fp: PrimeField> AccountChip<'d, Fp> { fn lagrange_polynomial_for_row(ref_n: Expression) -> Expression { super::lagrange_polynomial::(ref_n) } @@ -407,7 +407,7 @@ impl<'d, Fp: FieldExt> AccountChip<'d, Fp> { meta: &mut ConstraintSystem, sel: Selector, s_enable: Column, - ctrl_type: Column, + s_ctrl_type: [Column; 4], acc_data_fields: Column, acc_data_fields_ext: Column, free_cols: &[Column], @@ -420,79 +420,46 @@ impl<'d, Fp: FieldExt> AccountChip<'d, Fp> { meta.lookup_any("account hash1 calc", |meta| { // only enable on row 2 let s_enable = meta.query_advice(s_enable, Rotation::cur()); - let ctrl_type = meta.query_advice(ctrl_type, Rotation::cur()); - let enable_rows = Self::lagrange_polynomial_for_row::<2>(ctrl_type); + let enable_rows = meta.query_advice(s_ctrl_type[2], Rotation::cur()); let enable = enable_rows * s_enable; + let fst = meta.query_advice(acc_data_fields, Rotation::cur()); + let snd = meta.query_advice(acc_data_fields_ext, Rotation::cur()); + let hash = meta.query_advice(intermediate_1, Rotation::cur()); - vec![ - ( - enable.clone() * meta.query_advice(acc_data_fields, Rotation::cur()), - meta.query_advice(hash_table.0, Rotation::cur()), - ), - ( - enable.clone() * meta.query_advice(acc_data_fields_ext, Rotation::cur()), - meta.query_advice(hash_table.1, Rotation::cur()), - ), - ( - enable * meta.query_advice(intermediate_1, Rotation::cur()), - meta.query_advice(hash_table.2, Rotation::cur()), - ), - ] + hash_table.build_lookup(meta, enable, fst, snd, hash) }); // second hash lookup (Poseidon(hash1, Root) = hash2, Poseidon(hash3, hash2) = hash_final) meta.lookup_any("account hash2 and hash_final calc", |meta| { // only enable on row 1 and 2 let s_enable = meta.query_advice(s_enable, Rotation::cur()); - let ctrl_type = meta.query_advice(ctrl_type, Rotation::cur()); - let enable_rows = Self::lagrange_polynomial_for_row::<1>(ctrl_type.clone()) - + Self::lagrange_polynomial_for_row::<2>(ctrl_type); + let enable_rows = meta.query_advice(s_ctrl_type[1], Rotation::cur()) + + meta.query_advice(s_ctrl_type[2], Rotation::cur()); let enable = enable_rows * s_enable; + let fst = meta.query_advice(intermediate_1, Rotation::cur()); + let snd = meta.query_advice(intermediate_2, Rotation::cur()); + let hash = meta.query_advice(intermediate_2, Rotation::prev()); - vec![ - ( - enable.clone() * meta.query_advice(intermediate_1, Rotation::cur()), - meta.query_advice(hash_table.0, Rotation::cur()), - ), - ( - enable.clone() * meta.query_advice(intermediate_2, Rotation::cur()), - meta.query_advice(hash_table.1, Rotation::cur()), - ), - ( - enable * meta.query_advice(intermediate_2, Rotation::prev()), - meta.query_advice(hash_table.2, Rotation::cur()), - ), - ] + hash_table.build_lookup(meta, enable, fst, snd, hash) }); // third hash lookup (Poseidon(nonce, balance) = hash3) meta.lookup_any("account hash3 calc", |meta| { // only enable on row 1 let s_enable = meta.query_advice(s_enable, Rotation::cur()); - let ctrl_type = meta.query_advice(ctrl_type, Rotation::cur()); - let enable_rows = Self::lagrange_polynomial_for_row::<1>(ctrl_type); + let enable_rows = meta.query_advice(s_ctrl_type[1], Rotation::cur()); let enable = enable_rows * s_enable; - vec![ - ( - enable.clone() * meta.query_advice(acc_data_fields, Rotation::prev()), - meta.query_advice(hash_table.0, Rotation::cur()), - ), - ( - enable.clone() * meta.query_advice(acc_data_fields, Rotation::cur()), - meta.query_advice(hash_table.1, Rotation::cur()), - ), - ( - enable * meta.query_advice(intermediate_1, Rotation::cur()), - meta.query_advice(hash_table.2, Rotation::cur()), - ), - ] + let fst = meta.query_advice(acc_data_fields, Rotation::prev()); + let snd = meta.query_advice(acc_data_fields, Rotation::cur()); + let hash = meta.query_advice(intermediate_1, Rotation::cur()); + + hash_table.build_lookup(meta, enable, fst, snd, hash) }); // equality constraint: hash_final and Root meta.create_gate("account calc equalities", |meta| { let s_enable = meta.query_selector(sel) * meta.query_advice(s_enable, Rotation::cur()); - let ctrl_type = meta.query_advice(ctrl_type, Rotation::cur()); let exported_equal1 = meta.query_advice(intermediate_2, Rotation::cur()) - meta.query_advice(acc_data_fields, Rotation::prev()); let exported_equal2 = meta.query_advice(intermediate_2, Rotation::cur()) @@ -501,9 +468,9 @@ impl<'d, Fp: FieldExt> AccountChip<'d, Fp> { // equalities in the circuit vec![ s_enable.clone() - * Self::lagrange_polynomial_for_row::<0>(ctrl_type.clone()) + * meta.query_advice(s_ctrl_type[0], Rotation::cur()) * exported_equal1, // equality of hash_final - s_enable * Self::lagrange_polynomial_for_row::<2>(ctrl_type) * exported_equal2, // equality of state trie root + s_enable * meta.query_advice(s_ctrl_type[2], Rotation::cur()) * exported_equal2, // equality of state trie root ] }); @@ -536,7 +503,7 @@ impl<'d, Fp: FieldExt> AccountChip<'d, Fp> { ), ( config.acc_data_fields_ext, - [Fp::zero(), Fp::zero(), data.codehash.1], + [Fp::ZERO, Fp::ZERO, data.codehash.1], "data field ext", ), ( @@ -546,7 +513,7 @@ impl<'d, Fp: FieldExt> AccountChip<'d, Fp> { ), ( config.intermediate_1, - [Fp::zero(), data.hash_traces(2), data.hash_traces(0)], + [Fp::ZERO, data.hash_traces(2), data.hash_traces(0)], "intermedia 1", ), ] { @@ -571,7 +538,7 @@ impl<'d, Fp: FieldExt> AccountChip<'d, Fp> { || "state root padding", config.acc_data_fields_ext, self.offset + LAST_ROW, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; Ok(self.offset + LAST_ROW) @@ -591,7 +558,7 @@ struct StorageChip<'d, F> { value: Option>, } -impl<'d, Fp: FieldExt> StorageChip<'d, Fp> { +impl<'d, Fp: PrimeField> StorageChip<'d, Fp> { fn configure( meta: &mut ConstraintSystem, _sel: Selector, @@ -602,20 +569,11 @@ impl<'d, Fp: FieldExt> StorageChip<'d, Fp> { ) -> StorageChipConfig { meta.lookup_any("value hash", |meta| { let enable = meta.query_advice(s_enable, Rotation::cur()); - vec![ - ( - enable.clone() * meta.query_advice(v_limbs[0], Rotation::cur()), - meta.query_advice(hash_table.0, Rotation::cur()), - ), - ( - enable.clone() * meta.query_advice(v_limbs[1], Rotation::cur()), - meta.query_advice(hash_table.1, Rotation::cur()), - ), - ( - enable * meta.query_advice(hash, Rotation::prev()), - meta.query_advice(hash_table.2, Rotation::cur()), - ), - ] + let fst = meta.query_advice(v_limbs[0], Rotation::cur()); + let snd = meta.query_advice(v_limbs[1], Rotation::cur()); + let hash = meta.query_advice(hash, Rotation::prev()); + + hash_table.build_lookup(meta, enable, fst, snd, hash) }); StorageChipConfig { v_limbs } @@ -628,14 +586,14 @@ impl<'d, Fp: FieldExt> StorageChip<'d, Fp> { || "val limb 0", config.v_limbs[0], self.offset, - || Value::known(self.value.as_ref().map_or_else(Fp::zero, |v| v.limb_0())), + || Value::known(self.value.as_ref().map_or(Fp::ZERO, |v| v.limb_0())), )?; region.assign_advice( || "val limb 1", config.v_limbs[1], self.offset, - || Value::known(self.value.as_ref().map_or_else(Fp::zero, |v| v.limb_1())), + || Value::known(self.value.as_ref().map_or(Fp::ZERO, |v| v.limb_1())), )?; Ok(self.offset + 1) @@ -649,6 +607,7 @@ pub(crate) struct StorageGadget { key: StorageChipConfig, s_enable: Column, ctrl_type: Column, + s_ctrl_type: Column, } impl StorageGadget { @@ -656,14 +615,19 @@ impl StorageGadget { 6 } + pub fn min_ctrl_types() -> usize { + 1 + } + /// create gadget from assigned cols, we need: /// + circuit selector * 1 /// + exported col * 5 (MUST by following sequence: layout_flag, s_enable, old_val, new_val, key_val) /// + free col * 4 - pub fn configure( + pub fn configure>( meta: &mut ConstraintSystem, sel: Selector, exported: &[Column], + s_ctrl_type: &[Column], _free: &[Column], hash_tbl: mpt::HashTable, ) -> Self { @@ -672,6 +636,7 @@ impl StorageGadget { let s_hash = exported[2]; let e_hash = exported[3]; let k_hash = exported[4]; + let s_ctrl_type = s_ctrl_type[0]; let s_val_limbs = [exported[2], exported[5]]; let e_val_limbs = [exported[3], exported[6]]; let k_val_limbs = [exported[4], exported[7]]; @@ -687,6 +652,7 @@ impl StorageGadget { Self { s_enable, ctrl_type, + s_ctrl_type, key, s_value, e_value, @@ -698,7 +664,7 @@ impl StorageGadget { [].into_iter() } - pub fn assign<'d, Fp: FieldExt>( + pub fn assign<'d, Fp: PrimeField>( &self, region: &mut Region<'_, Fp>, offset: usize, @@ -708,14 +674,21 @@ impl StorageGadget { || "enable storage leaf circuit", self.s_enable, offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; region.assign_advice( || "storage leaf circuit row", self.ctrl_type, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), + )?; + + region.assign_advice( + || "enable s_ctrl", + self.s_ctrl_type, + offset, + || Value::known(Fp::ONE), )?; for (config, value) in [ @@ -753,6 +726,7 @@ mod test { gadget: AccountGadget, sel: Selector, free_cols: [Column; 14], + s_ctrl_cols: [Column; 4], op_tabl: mpt::MPTOpTables, hash_tabl: mpt::HashTable, } @@ -774,6 +748,7 @@ mod test { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let sel = meta.selector(); let free_cols = [(); 14].map(|_| meta.advice_column()); + let s_ctrl_cols = [(); 4].map(|_| meta.advice_column()); let exported_cols = [ free_cols[0], free_cols[1], @@ -791,6 +766,7 @@ mod test { meta, sel, exported_cols.as_slice(), + s_ctrl_cols.as_slice(), &free_cols[8..], None, op_tabl.clone(), @@ -801,6 +777,7 @@ mod test { gadget, sel, free_cols, + s_ctrl_cols, op_tabl, hash_tabl, } @@ -831,10 +808,21 @@ mod test { || "flush top row", col, 0, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; } + for offset in 1..=CIRCUIT_ROW { + for col in config.s_ctrl_cols { + region.assign_advice( + || "flush s_ctrl", + col, + offset, + || Value::known(Fp::ZERO), + )?; + } + } + let till = config.gadget.assign( &mut region, 1, @@ -850,7 +838,7 @@ mod test { || "flush last row", col, till, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; } Ok(()) @@ -865,7 +853,7 @@ mod test { AccountTestCircuit::configure(&mut cs); println!("account gadget degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); } #[test] diff --git a/src/layers.rs b/src/layers.rs index 2d1da433..b519386d 100644 --- a/src/layers.rs +++ b/src/layers.rs @@ -19,8 +19,8 @@ // circuit and we also call the rows for one step an "op block". use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, Region, Value}, + ff::PrimeField, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, TableColumn}, poly::Rotation, }; @@ -37,6 +37,10 @@ pub(crate) struct LayerGadget { // its flag when assigned s_stepflags: Vec>, ctrl_type: Column, + // the s_ctrl_type is supposed to be a series of integers start from 0 and each of the number + // is represented the corresponding items in s_ctrl_type array in which the number is just + // its index + s_ctrl_type: Vec>, // the 3 exported value now can be represented by 2-field and the additional // field is marked as "ext" (most value still use 1 field only) data_0: Column, @@ -74,6 +78,10 @@ impl LayerGadget { ] } + pub fn get_ctrl_type_flags(&self) -> &[Column] { + &self.s_ctrl_type + } + pub fn get_free_cols(&self) -> &[Column] { &self.free_cols } @@ -96,13 +104,19 @@ impl LayerGadget { self.sel } - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, steps: usize, required_cols: usize, + minium_ctrl_types: usize, ) -> Self { - let s_stepflags: Vec> = (0..steps).map(|_| meta.advice_column()).collect(); - let free_cols = (0..required_cols).map(|_| meta.advice_column()).collect(); + assert!(steps > 0, "at least one step is required"); + assert!(minium_ctrl_types > 0, "at least one ctrl type is required"); + let s_stepflags: Vec<_> = (0..steps).map(|_| meta.advice_column()).collect(); + let free_cols: Vec<_> = (0..required_cols).map(|_| meta.advice_column()).collect(); + let s_ctrl_type: Vec<_> = (0..minium_ctrl_types) + .map(|_| meta.advice_column()) + .collect(); let sel = meta.complex_selector(); let series = meta.advice_column(); let op_type = meta.advice_column(); @@ -129,7 +143,7 @@ impl LayerGadget { let series_delta = meta.query_advice(series, Rotation::cur()) - meta.query_advice(series, Rotation::prev()); // delta ∈ {0, 1} - vec![sel * (Expression::Constant(Fp::one()) - series_delta.clone()) * series_delta] + vec![sel * (Expression::Constant(Fp::ONE) - series_delta.clone()) * series_delta] }); meta.create_gate("op transition", |meta| { @@ -138,10 +152,49 @@ impl LayerGadget { - meta.query_advice(op_type, Rotation::prev()); let op_delta_aux = meta.query_advice(op_delta_aux, Rotation::cur()); // map op_delta_aux so we can obtain 1 while delta is not zero - vec![ - sel * (Expression::Constant(Fp::one()) - op_delta_aux * op_delta.clone()) - * op_delta, - ] + vec![sel * (Expression::Constant(Fp::ONE) - op_delta_aux * op_delta.clone()) * op_delta] + }); + + meta.create_gate("s_ctrl flags", |meta| { + let sel = meta.query_selector(sel); + // setting op flags: + // all flags is boolean + // one and at most one flag must be enabled + // the enabled flas must match with op_type + let s_ctrl: Vec<_> = s_ctrl_type + .iter() + .copied() + .map(|col| meta.query_advice(col, Rotation::cur())) + .collect(); + + let bool_cond = s_ctrl.clone().into_iter().map(|col_exp| { + sel.clone() * col_exp.clone() * (Expression::Constant(Fp::ONE) - col_exp) + }); + + let one_flag_cond = s_ctrl + .clone() + .into_iter() + .reduce(|exp, col_exp| exp + col_exp) + .map(|sum_exp| sel.clone() * (Expression::Constant(Fp::ONE) - sum_exp)); + + let ctrl_type_cond = s_ctrl + .into_iter() + .enumerate() + .map(|(idx, col_exp)| Expression::Constant(Fp::from(idx as u64)) * col_exp) + .reduce(|exp, col_exp_with_idx| exp + col_exp_with_idx) + .map(|w_sum_exp| { + sel.clone() * (meta.query_advice(ctrl_type, Rotation::cur()) - w_sum_exp) + }); + + let constraints = bool_cond + .chain(one_flag_cond) + .chain(ctrl_type_cond) + .collect::>(); + if constraints.is_empty() { + vec![sel * Expression::Constant(Fp::ZERO)] + } else { + constraints + } }); meta.create_gate("index identical", |meta| { @@ -154,7 +207,7 @@ impl LayerGadget { Vec::from([old_root_index, new_root_index, address_index].map(|col| { sel.clone() - * (Expression::Constant(Fp::one()) - series_delta.clone()) + * (Expression::Constant(Fp::ONE) - series_delta.clone()) * (meta.query_advice(col, Rotation::cur()) - meta.query_advice(col, Rotation::prev())) })) @@ -183,7 +236,7 @@ impl LayerGadget { // notice all flag is constrainted to boolean, and total_flag is 1, so one and at most one col must be true // and only that flag whose op_type is corresponding to its step code can be true - exps.push(sel * (Expression::Constant(Fp::one()) - total_flag)); + exps.push(sel * (Expression::Constant(Fp::ONE) - total_flag)); exps }); @@ -195,7 +248,7 @@ impl LayerGadget { // lookup from control_table meta.lookup("layer intra-block border rule", |meta| { // condition 1 (intra-block transition) is only actived when series has not change - let series_delta_zero = Expression::Constant(Fp::one()) + let series_delta_zero = Expression::Constant(Fp::ONE) - meta.query_advice(series, Rotation::cur()) + meta.query_advice(series, Rotation::prev()); let op_delta = meta.query_advice(op_type, Rotation::cur()) @@ -213,7 +266,7 @@ impl LayerGadget { (ctrl_cur, control_table[1]), (op_prev, control_table[2]), (ctrl_prev, control_table[3]), - (Expression::Constant(Fp::zero()), control_table[4]), + (Expression::Constant(Fp::ZERO), control_table[4]), ] }); @@ -233,7 +286,7 @@ impl LayerGadget { (ctrl_cur, control_table[1]), (op_prev, control_table[2]), (ctrl_prev, control_table[3]), - (Expression::Constant(Fp::one()), control_table[4]), + (Expression::Constant(Fp::ONE), control_table[4]), ] }); @@ -241,6 +294,7 @@ impl LayerGadget { sel, series, s_stepflags, + s_ctrl_type, op_type, ctrl_type, data_0, @@ -265,7 +319,7 @@ impl LayerGadget { } // LayerGadget must be first assigned, with other gadgets start from the offset it has returned - pub fn assign( + pub fn assign( &self, region: &mut Region<'_, Fp>, max_rows: usize, @@ -274,23 +328,23 @@ impl LayerGadget { // current we flush the first row, and start other circuits's assignation from row 1 self.free_cols.iter().try_for_each(|col| { region - .assign_advice(|| "flushing", *col, 0, || Value::known(Fp::zero())) + .assign_advice(|| "flushing", *col, 0, || Value::known(Fp::ZERO)) .map(|_| ()) })?; self.s_stepflags.iter().try_for_each(|col| { region - .assign_advice(|| "flushing", *col, 0, || Value::known(Fp::zero())) + .assign_advice(|| "flushing", *col, 0, || Value::known(Fp::ZERO)) .map(|_| ()) })?; - region.assign_advice_from_constant(|| "init series", self.series, 0, Fp::zero())?; - region.assign_advice_from_constant(|| "init series", self.series, 1, Fp::one())?; + region.assign_advice_from_constant(|| "init series", self.series, 0, Fp::ZERO)?; + region.assign_advice_from_constant(|| "init series", self.series, 1, Fp::ONE)?; region.assign_advice_from_constant( || "init op", self.op_type, 0, Fp::from(self.start_op_code() as u64), )?; - region.assign_advice_from_constant(|| "init ctrl", self.ctrl_type, 0, Fp::zero())?; + region.assign_advice_from_constant(|| "init ctrl", self.ctrl_type, 0, Fp::ZERO)?; region.assign_advice( || "start root", self.new_root_index, @@ -298,7 +352,7 @@ impl LayerGadget { || Value::known(init_root), )?; for col in [self.old_root_index, self.address_index] { - region.assign_advice(|| "index flush", col, 0, || Value::known(Fp::zero()))?; + region.assign_advice(|| "index flush", col, 0, || Value::known(Fp::ZERO))?; } for offset in 1..max_rows { @@ -312,26 +366,26 @@ impl LayerGadget { || "flushing last", *col, max_rows, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), ) .map(|_| ()) })?; // begin padding and final flush for data_rows for col in [self.data_0, self.data_1, self.data_2] { - region.assign_advice(|| "begin padding", col, 0, || Value::known(Fp::zero()))?; + region.assign_advice(|| "begin padding", col, 0, || Value::known(Fp::ZERO))?; region.assign_advice( || "last row flushing", col, max_rows, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; } region.assign_advice( || "terminalte series", self.series, max_rows, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; Ok(1) @@ -340,7 +394,7 @@ impl LayerGadget { // pace has to be called before a working gadget is assigned on the specified offset, the rows // that working gadget would occpuy, and the result of the new root which gadget has output, // must be known before - pub fn pace_op( + pub fn pace_op( &self, region: &mut Region<'_, Fp>, offset: usize, @@ -362,7 +416,7 @@ impl LayerGadget { offset, || { Value::known(if prev_op == op_type.1 { - Fp::zero() + Fp::ZERO } else { op_delta.invert().unwrap() }) @@ -370,12 +424,18 @@ impl LayerGadget { )?; // flush all cols to avoid unassigned error self.free_cols.iter().try_for_each(|col| { + region + .assign_advice(|| "flushing free", *col, offset, || Value::known(Fp::ZERO)) + .map(|_| ()) + })?; + // flush all cols to avoid unassigned error + self.s_ctrl_type.iter().try_for_each(|col| { region .assign_advice( - || "flushing free", + || "flushing op type flag", *col, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), ) .map(|_| ()) })?; @@ -394,13 +454,13 @@ impl LayerGadget { || "flushing exported", *col, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), ) .map(|_| ()) })?; self.s_stepflags.iter().try_for_each(|col| { region - .assign_advice(|| "flushing", *col, offset, || Value::known(Fp::zero())) + .assign_advice(|| "flushing", *col, offset, || Value::known(Fp::ZERO)) .map(|_| ()) })?; @@ -412,7 +472,7 @@ impl LayerGadget { // complete block is called AFTER all working gadget has been assigned on the specified offset, // this entry fill whole block with series and index value - pub fn complete_block( + pub fn complete_block( &self, region: &mut Region<'_, Fp>, offset: usize, @@ -452,7 +512,7 @@ impl LayerGadget { // set all transition rules // + end_op: is the last op code in your assignation, often just (, 0) - pub fn set_op_border( + pub fn set_op_border( &self, layouter: &mut impl Layouter, inter_op: &[OpBorder], @@ -464,7 +524,7 @@ impl LayerGadget { // set all transition rules // + start_op: all possible starting op code and ctrl code - pub fn set_op_border_ex( + pub fn set_op_border_ex( &self, layouter: &mut impl Layouter, inter_op: &[OpBorder], @@ -479,62 +539,62 @@ impl LayerGadget { || "default", self.control_table[0], 0, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default", self.control_table[1], 0, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default", self.control_table[2], 0, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default", self.control_table[3], 0, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default", self.control_table[4], 0, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default op cur", self.control_table[0], 1, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default ctrl cur", self.control_table[1], 1, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default op prev", self.control_table[2], 1, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "default ctrl prev", self.control_table[3], 1, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "mark", self.control_table[4], 1, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; let mut offset = 2; @@ -563,13 +623,13 @@ impl LayerGadget { || "marking ctrl", self.control_table[3], offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; table.assign_cell( || "mark", self.control_table[4], offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; offset += 1; } @@ -603,7 +663,7 @@ impl LayerGadget { || "mark", self.control_table[4], offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; offset += 1; } @@ -637,7 +697,7 @@ impl LayerGadget { || "mark", self.control_table[4], offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; offset += 1; } @@ -656,21 +716,24 @@ impl LayerGadget { pub(crate) struct PaddingGadget { s_enable: Column, ctrl_type: Column, + s_ctrl_type: Column, } impl PaddingGadget { - pub fn configure( + pub fn configure( _meta: &mut ConstraintSystem, _sel: Selector, exported: &[Column], + s_ctrl_type: &[Column], ) -> Self { Self { ctrl_type: exported[0], s_enable: exported[1], + s_ctrl_type: s_ctrl_type[0], } } - pub fn padding( + pub fn padding( &self, region: &mut Region<'_, Fp>, offset: usize, @@ -681,13 +744,19 @@ impl PaddingGadget { || "ctrl type", self.ctrl_type, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), + )?; + region.assign_advice( + || "enable s_ctrl", + self.s_ctrl_type, + offset, + || Value::known(Fp::ONE), )?; region.assign_advice( || "enable padding", self.s_enable, offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; } Ok(()) @@ -727,9 +796,13 @@ mod test { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let layer = LayerGadget::configure(meta, 1, 3); - let padding = - PaddingGadget::configure(meta, layer.sel, layer.exported_cols(0).as_slice()); + let layer = LayerGadget::configure(meta, 1, 3, 1); + let padding = PaddingGadget::configure( + meta, + layer.sel, + layer.exported_cols(0).as_slice(), + layer.get_ctrl_type_flags(), + ); let cst = meta.fixed_column(); meta.enable_constant(cst); @@ -822,11 +895,19 @@ mod test { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let layer = LayerGadget::configure(meta, 3, 2); - let padding0 = - PaddingGadget::configure(meta, layer.sel, layer.exported_cols(0).as_slice()); - let padding1 = - PaddingGadget::configure(meta, layer.sel, layer.exported_cols(2).as_slice()); + let layer = LayerGadget::configure(meta, 3, 2, 1); + let padding0 = PaddingGadget::configure( + meta, + layer.sel, + layer.exported_cols(0).as_slice(), + layer.get_ctrl_type_flags(), + ); + let padding1 = PaddingGadget::configure( + meta, + layer.sel, + layer.exported_cols(2).as_slice(), + layer.get_ctrl_type_flags(), + ); let cst = meta.fixed_column(); meta.enable_constant(cst); @@ -895,7 +976,7 @@ mod test { MultiOpCircuit::configure(&mut cs); println!("layer gadget degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 5a24321d..5e8c0ac1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,8 +70,8 @@ enum CtrlTransitionKind { use eth::AccountGadget; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner}, + ff::PrimeField, plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression}, }; use hash::Hashable; @@ -80,15 +80,15 @@ use mpt::MPTOpGadget; use operation::{AccountOp, HashTracesSrc, SingleOp}; // building lagrange polynmials L for T so that L(n) = 1 when n = T else 0, n in [0, TO] -fn lagrange_polynomial( +fn lagrange_polynomial( ref_n: Expression, ) -> Expression { let mut denominators: Vec = (0..=TO) .map(|v| Fp::from(T as u64) - Fp::from(v as u64)) .collect(); denominators.swap_remove(T); - let denominator = denominators.into_iter().fold(Fp::one(), |acc, v| v * acc); - assert_ne!(denominator, Fp::zero()); + let denominator = denominators.into_iter().fold(Fp::ONE, |acc, v| v * acc); + assert_ne!(denominator, Fp::ZERO); let mut factors: Vec> = (0..(TO + 1)) .map(|v| ref_n.clone() - Expression::Constant(Fp::from(v as u64))) @@ -110,7 +110,7 @@ pub struct SimpleTrieConfig { /// The chip for op on a simple trie #[derive(Clone, Default)] -pub struct SimpleTrie { +pub struct SimpleTrie { c_size: usize, //how many rows start_root: F, final_root: F, @@ -120,7 +120,7 @@ pub struct SimpleTrie { const OP_MPT: u32 = 1; const OP_PADDING: u32 = 0; -impl SimpleTrie { +impl SimpleTrie { /// create a new, empty circuit with specified size pub fn new(c_size: usize) -> Self { Self { @@ -146,7 +146,7 @@ impl SimpleTrie { } } -impl Circuit for SimpleTrie { +impl Circuit for SimpleTrie { type Config = SimpleTrieConfig; type FloorPlanner = SimpleFloorPlanner; @@ -158,16 +158,23 @@ impl Circuit for SimpleTrie { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let layer = LayerGadget::configure(meta, 2, MPTOpGadget::min_free_cols()); + let layer = LayerGadget::configure( + meta, + 2, + MPTOpGadget::min_free_cols(), + MPTOpGadget::min_ctrl_types(), + ); let padding = PaddingGadget::configure( meta, layer.public_sel(), layer.exported_cols(OP_PADDING).as_slice(), + layer.get_ctrl_type_flags(), ); let mpt = MPTOpGadget::configure_simple( meta, layer.public_sel(), layer.exported_cols(OP_MPT).as_slice(), + layer.get_ctrl_type_flags(), layer.get_free_cols(), Some(layer.get_root_indexs()), ); @@ -288,10 +295,26 @@ pub struct EthTrieConfig { } impl EthTrieConfig { + /// the beginning of hash table index + pub fn hash_tbl_begin(&self) -> usize { + self.hash_tbl.commitment_index()[0] + } + + /// the beginning of mpt table index + pub fn mpt_tbl_begin(&self) -> usize { + self.mpt_tbl + .as_ref() + .expect("only call for non-lite circuit") + .mpt_table_begin_index() + } + /// configure for lite circuit (no mpt table included, for fast testing) - pub fn configure_lite(meta: &mut ConstraintSystem) -> Self { + pub fn configure_base>( + meta: &mut ConstraintSystem, + hash_tbl: [Column; 4], + ) -> Self { let tables = mpt::MPTOpTables::configure_create(meta); - let hash_tbl = mpt::HashTable::configure_create(meta); + let hash_tbl = mpt::HashTable::configure_assign(&hash_tbl); let layer = LayerGadget::configure( meta, @@ -303,16 +326,25 @@ impl EthTrieConfig { StorageGadget::min_free_cols(), ), ), + std::cmp::max( + MPTOpGadget::min_ctrl_types(), + std::cmp::max( + AccountGadget::min_ctrl_types(), + StorageGadget::min_ctrl_types(), + ), + ), ); let padding = PaddingGadget::configure( meta, layer.public_sel(), layer.exported_cols(OP_PADDING).as_slice(), + layer.get_ctrl_type_flags(), ); let account_trie = MPTOpGadget::configure( meta, layer.public_sel(), layer.exported_cols(OP_TRIE_ACCOUNT).as_slice(), + layer.get_ctrl_type_flags(), layer.get_free_cols(), Some(layer.get_root_indexs()), tables.clone(), @@ -322,6 +354,7 @@ impl EthTrieConfig { meta, layer.public_sel(), layer.exported_cols(OP_TRIE_STATE).as_slice(), + layer.get_ctrl_type_flags(), layer.get_free_cols(), None, tables.clone(), @@ -331,6 +364,7 @@ impl EthTrieConfig { meta, layer.public_sel(), layer.exported_cols(OP_ACCOUNT).as_slice(), + layer.get_ctrl_type_flags(), layer.get_free_cols(), Some(layer.get_address_index()), tables.clone(), @@ -340,6 +374,7 @@ impl EthTrieConfig { meta, layer.public_sel(), layer.exported_cols(OP_STORAGE).as_slice(), + layer.get_ctrl_type_flags(), layer.get_free_cols(), hash_tbl.clone(), ); @@ -360,13 +395,22 @@ impl EthTrieConfig { } } + /// configure for lite circuit (no mpt table included, for fast testing) + pub fn configure_lite>( + meta: &mut ConstraintSystem, + ) -> Self { + let hash_tbl = [0; 4].map(|_| meta.advice_column()); + Self::configure_base(meta, hash_tbl) + } + /// configure for full circuit - pub fn configure_sub( + pub fn configure_sub>( meta: &mut ConstraintSystem, mpt_tbl: [Column; 7], + hash_tbl: [Column; 4], randomness: Expression, ) -> Self { - let mut lite_cfg = Self::configure_lite(meta); + let mut lite_cfg = Self::configure_base(meta, hash_tbl); let mpt_tbl = MPTTable::configure(meta, mpt_tbl, randomness); let layer = &lite_cfg.layer; let layer_exported = layer.exported_cols(0); @@ -391,7 +435,7 @@ impl EthTrieConfig { /// synthesize the mpt table part, the randomness also specify /// if the base part of mpt table should be assigned - pub fn load_mpt_table<'d, Fp: Hashable>( + pub fn load_mpt_table<'d, Fp: Hashable + PrimeField>( &self, layouter: &mut impl Layouter, randomness: Option, @@ -400,11 +444,12 @@ impl EthTrieConfig { rows: usize, ) -> Result<(), Error> { let mpt_entries = tbl_tips.into_iter().zip(ops).map(|(proof_type, op)| { - if let Some(rand) = randomness { - MPTEntry::from_op(proof_type, op, rand) - } else { - MPTEntry::from_op_no_base(proof_type, op) - } + MPTEntry::from_op(proof_type, op, randomness.unwrap_or_default()) + //if let Some(rand) = randomness { + // MPTEntry::from_op(proof_type, op, rand) + //} else { + // MPTEntry::from_op_no_base(proof_type, op) + //} }); let mpt_tbl = MPTTable::construct( @@ -415,18 +460,35 @@ impl EthTrieConfig { mpt_tbl.load(layouter) } - /// synthesize core part (without mpt table), require a `Hashable` trait - /// on the working field - pub fn synthesize_core( + /// synthesize the hash table part, the randomness also specify + /// if the base part of mpt table should be assigned + pub fn load_hash_table<'d, Fp: Hashable + PrimeField>( + &self, + layouter: &mut impl Layouter, + hash_traces: impl Iterator + Clone, + rows: usize, + ) -> Result<(), Error> { + self.hash_tbl.fill_with_paddings( + layouter, + HashTracesSrc::from(hash_traces), + (Fp::ZERO, Fp::ZERO, Hashable::hash([Fp::ZERO, Fp::ZERO])), + rows, + ) + } + + /// synthesize core part without advice tables (hash and mpt table), + /// require a `Hashable` trait on the working field + pub fn synthesize_core<'d, Fp: Hashable + PrimeField>( &self, layouter: &mut impl Layouter, - ops: &[AccountOp], + ops: impl Iterator> + Clone, rows: usize, ) -> Result<(), Error> { let start_root = ops - .first() + .clone() + .next() .map(|op| op.account_root_before()) - .unwrap_or_else(Fp::zero); + .unwrap_or(Fp::ZERO); layouter.assign_region( || "main", @@ -436,7 +498,7 @@ impl EthTrieConfig { let mut start = self.layer.assign(&mut region, rows, start_root)?; let empty_account = Default::default(); - for op in ops { + for op in ops.clone() { let block_start = start; self.layer.pace_op( &mut region, @@ -505,18 +567,6 @@ impl EthTrieConfig { }, )?; - let hash_traces_i = ops.iter().flat_map(|op| op.hash_traces()); - self.hash_tbl.fill_with_paddings( - layouter, - HashTracesSrc::from(hash_traces_i), - ( - Fp::zero(), - Fp::zero(), - Hashable::hash([Fp::zero(), Fp::zero()]), - ), - rows, - )?; - self.tables.fill_constant( layouter, MPTOpGadget::transition_rules().chain(AccountGadget::transition_rules()), @@ -556,7 +606,7 @@ impl EthTrieConfig { } /// The chip for op on an storage trie #[derive(Clone, Default)] -pub struct EthTrie { +pub struct EthTrie { start_root: F, final_root: F, ops: Vec>, @@ -567,7 +617,7 @@ const OP_TRIE_STATE: u32 = 2; const OP_ACCOUNT: u32 = 3; const OP_STORAGE: u32 = 4; -impl EthTrie { +impl EthTrie { /// Add an op into the circuit data pub fn add_op(&mut self, op: AccountOp) { if self.ops.is_empty() { @@ -593,8 +643,8 @@ impl EthTrie { } /// the mpt circuit type -#[derive(Clone, Default)] -pub struct EthTrieCircuit { +#[derive(Clone, Default, Debug)] +pub struct EthTrieCircuit { /// the maxium records in circuits (would affect vk) pub calcs: usize, /// the operations in circuits @@ -636,37 +686,44 @@ impl EthTrieCircuit { } } -use hash::HashCircuit as PoseidonHashCircuit; -/// the reform hash circuit -pub struct HashCircuit(PoseidonHashCircuit); +/// hash circuit as the companion of mpt hashes +pub struct HashCircuit(hash::PoseidonHashTable, usize); impl HashCircuit { /// re-warped, all-in-one creation pub fn new(calcs: usize, input_with_check: &[&(Fp, Fp, Fp)]) -> Self { - let mut cr = PoseidonHashCircuit::::new(calcs); - cr.constant_inputs_with_check(input_with_check.iter().copied()); - Self(cr) + let mut tbl = hash::PoseidonHashTable::default(); + tbl.constant_inputs_with_check(input_with_check.iter().copied()); + Self(tbl, calcs) } } impl Circuit for HashCircuit { - type Config = as Circuit>::Config; - type FloorPlanner = as Circuit>::FloorPlanner; + type Config = hash::PoseidonHashConfig; + type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { - Self(self.0.without_witnesses()) + Self(Default::default(), self.1) } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - as Circuit>::configure(meta) + let hash_tbl = [0; 4].map(|_| meta.advice_column()); + hash::PoseidonHashConfig::configure_sub(meta, hash_tbl, hash_circuit::DEFAULT_STEP) } - fn synthesize(&self, config: Self::Config, layouter: impl Layouter) -> Result<(), Error> { - self.0.synthesize(config, layouter) + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let chip = hash::PoseidonHashChip::::construct( + config, &self.0, self.1, + ); + chip.load(&mut layouter) } } -impl EthTrie { +impl> EthTrie { /// Obtain the total required rows for mpt and hash circuits (include the top and bottom padding) pub fn use_rows(&self) -> (usize, usize) { // calc rows for mpt circuit, we need to compare the rows used by adviced region and table region @@ -717,17 +774,17 @@ pub struct CommitmentIndexs([usize; 3], [usize; 3], Option); impl CommitmentIndexs { /// the hash col's pos pub fn hash_pos(&self) -> (usize, usize) { - (self.0[2], self.1[0]) + (self.0[0], self.1[0]) } /// the first input col's pos pub fn left_pos(&self) -> (usize, usize) { - (self.0[0], self.1[1]) + (self.0[1], self.1[1]) } /// the second input col's pos pub fn right_pos(&self) -> (usize, usize) { - (self.0[1], self.1[2]) + (self.0[2], self.1[2]) } /// the beginning of mpt table index @@ -736,7 +793,7 @@ impl CommitmentIndexs { } /// get commitment for lite circuit (no mpt) - pub fn new() -> Self { + pub fn new>() -> Self { let mut cs: ConstraintSystem = Default::default(); let config = EthTrieCircuit::<_, true>::configure(&mut cs); @@ -748,14 +805,14 @@ impl CommitmentIndexs { let hash_circuit_indexs = config.commitment_index(); Self( - trie_circuit_indexs, + trie_circuit_indexs[0..3].try_into().unwrap(), hash_circuit_indexs[0..3].try_into().unwrap(), None, ) } /// get commitment for full circuit - pub fn new_full_circuit() -> Self { + pub fn new_full_circuit>() -> Self { let mut cs: ConstraintSystem = Default::default(); let config = EthTrieCircuit::<_, false>::configure(&mut cs); @@ -771,7 +828,7 @@ impl CommitmentIndexs { let hash_circuit_indexs = config.commitment_index(); Self( - trie_circuit_indexs, + trie_circuit_indexs[0..3].try_into().unwrap(), hash_circuit_indexs[0..3].try_into().unwrap(), Some(mpt_table_start), ) @@ -780,7 +837,9 @@ impl CommitmentIndexs { const TEMP_RANDOMNESS: u64 = 1; -impl Circuit for EthTrieCircuit { +impl, const LITE: bool> Circuit + for EthTrieCircuit +{ type Config = EthTrieConfig; type FloorPlanner = SimpleFloorPlanner; @@ -797,8 +856,9 @@ impl Circuit for EthTrieCircuit { EthTrieConfig::configure_lite(meta) } else { let base = [0; 7].map(|_| meta.advice_column()); + let hash_tbl = [0; 4].map(|_| meta.advice_column()); let randomness = Expression::Constant(Fp::from(get_rand_base())); - EthTrieConfig::configure_sub(meta, base, randomness) + EthTrieConfig::configure_sub(meta, base, hash_tbl, randomness) } } @@ -807,7 +867,12 @@ impl Circuit for EthTrieCircuit { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.synthesize_core(&mut layouter, self.ops.as_slice(), self.calcs)?; + config.load_hash_table( + &mut layouter, + self.ops.iter().flat_map(|op| op.hash_traces()), + self.calcs, + )?; + config.synthesize_core(&mut layouter, self.ops.iter(), self.calcs)?; if LITE { Ok(()) } else { @@ -835,13 +900,13 @@ mod test { EthTrieCircuit::<_, false>::configure(&mut cs); println!("mpt circuit degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); let mut cs: ConstraintSystem = Default::default(); HashCircuit::configure(&mut cs); println!("hash circuit degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); } #[test] diff --git a/src/mpt.rs b/src/mpt.rs index 051ced24..a8b0ed55 100644 --- a/src/mpt.rs +++ b/src/mpt.rs @@ -62,8 +62,8 @@ use super::{CtrlTransitionKind, HashType}; use crate::operation::{MPTPath, SingleOp}; use halo2_proofs::{ - arithmetic::{Field, FieldExt}, circuit::{Chip, Layouter, Region, Value}, + ff::{Field, PrimeField}, plonk::{ Advice, Column, ConstraintSystem, Error, Expression, Selector, TableColumn, VirtualCells, }, @@ -72,23 +72,26 @@ use halo2_proofs::{ use lazy_static::lazy_static; #[derive(Clone, Debug)] -pub(crate) struct MPTOpTables(pub TableColumn, pub TableColumn, pub TableColumn); +pub(crate) struct MPTOpTables( + TableColumn, // op mark + [TableColumn; 3], // op rules +); lazy_static! { - static ref OPMAP : Vec<(HashType, HashType)> = { + static ref OPMAP : Vec<(HashType, HashType, HashType)> = { vec![ - (HashType::Start, HashType::Start), - (HashType::Empty, HashType::Empty), - (HashType::Empty, HashType::Leaf), - (HashType::Leaf, HashType::Empty), - (HashType::Leaf, HashType::Leaf), - (HashType::Middle, HashType::Middle), - (HashType::LeafExt, HashType::Middle), - (HashType::LeafExt, HashType::LeafExt), - (HashType::LeafExtFinal, HashType::Middle), - (HashType::LeafExtFinal, HashType::LeafExtFinal), - (HashType::Middle, HashType::LeafExt), - (HashType::Middle, HashType::LeafExtFinal), + (HashType::Start, HashType::Start, HashType::Start), + (HashType::Empty, HashType::Empty, HashType::Empty), + (HashType::Empty, HashType::Leaf, HashType::Leaf), + (HashType::Leaf, HashType::Empty, HashType::Leaf), + (HashType::Leaf, HashType::Leaf, HashType::Leaf), + (HashType::Middle, HashType::Middle, HashType::Middle), + (HashType::LeafExt, HashType::Middle, HashType::LeafExt), + (HashType::LeafExt, HashType::LeafExt, HashType::LeafExt), + (HashType::LeafExtFinal, HashType::Middle, HashType::LeafExtFinal), + (HashType::LeafExtFinal, HashType::LeafExtFinal, HashType::LeafExtFinal), + (HashType::Middle, HashType::LeafExt, HashType::LeafExt), + (HashType::Middle, HashType::LeafExtFinal, HashType::LeafExtFinal), ] }; static ref TRANSMAP : Vec<(HashType, HashType)> = { @@ -115,59 +118,70 @@ impl MPTOpTables { pub fn configure_create(meta: &mut ConstraintSystem) -> Self { Self( meta.lookup_table_column(), - meta.lookup_table_column(), - meta.lookup_table_column(), + [0; 3].map(|_| meta.lookup_table_column()), ) } - pub fn build_lookup( + pub fn build_lookup_any( + &self, + enable: Expression, + rules: impl IntoIterator>, + mark: u64, + ) -> Vec<(Expression, TableColumn)> { + let mut ret: Vec<_> = rules + .into_iter() + .map(|exp| enable.clone() * exp) + .zip(self.1) + .collect(); + ret.push((enable * Expression::Constant(Fp::from(mark)), self.0)); + ret + } + + pub fn build_lookup( &self, enable: Expression, old: Expression, new: Expression, mark: u64, ) -> Vec<(Expression, TableColumn)> { - vec![ - (enable.clone() * old, self.0), - (enable.clone() * new, self.1), - (enable * Expression::Constant(Fp::from(mark)), self.2), - ] + self.build_lookup_any(enable, [old, new], mark) } - pub fn fill_constant( + pub fn fill_constant( &self, layouter: &mut impl Layouter, - rules: impl Iterator + Clone, + rules: impl Iterator + Clone, ) -> Result<(), Error> { layouter.assign_table( || "op table", |mut table| { - // default: 0, 0, 0 - table.assign_cell(|| "default", self.0, 0, || Value::known(Fp::zero()))?; - table.assign_cell(|| "default", self.1, 0, || Value::known(Fp::zero()))?; - table.assign_cell(|| "default", self.2, 0, || Value::known(Fp::zero()))?; - - for (offset, item) in rules.clone().enumerate() { - let offset = offset + 1; + // default line + table.assign_cell(|| "default mark", self.0, 0, || Value::known(Fp::ZERO))?; + for i in 0..3 { table.assign_cell( - || "cur", - self.0, - offset, - || Value::known(Fp::from(item.0 as u64)), + || "default rule", + self.1[i], + 0, + || Value::known(Fp::ZERO), )?; + } - table.assign_cell( - || "next", - self.1, - offset, - || Value::known(Fp::from(item.1 as u64)), - )?; + for (offset, (items, mark)) in rules.clone().enumerate() { + let offset = offset + 1; + for (rule, col) in items.into_iter().zip(self.1) { + table.assign_cell( + || "rule item", + col, + offset, + || Value::known(Fp::from(rule as u64)), + )?; + } table.assign_cell( || "mark", - self.2, + self.0, offset, - || Value::known(Fp::from(item.2 as u64)), + || Value::known(Fp::from(mark as u64)), )?; } Ok(()) @@ -177,26 +191,22 @@ impl MPTOpTables { } #[derive(Clone, Debug)] -pub(crate) struct HashTable(pub Column, pub Column, pub Column); +pub(crate) struct HashTable(pub [Column; 4]); impl HashTable { pub fn configure_create(meta: &mut ConstraintSystem) -> Self { - Self( - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ) + Self([0; 4].map(|_| meta.advice_column())) } - fn configure_assign(cols: &[Column]) -> Self { - Self(cols[0], cols[1], cols[2]) + pub fn configure_assign(cols: &[Column]) -> Self { + Self([cols[0], cols[1], cols[2], cols[3]]) } - pub fn commitment_index(&self) -> [usize; 3] { - [self.0.index(), self.1.index(), self.2.index()] + pub fn commitment_index(&self) -> [usize; 4] { + self.0.map(|col| col.index()) } - pub fn build_lookup<'d, Fp: FieldExt>( + pub fn build_lookup<'d, Fp: PrimeField>( &self, meta: &mut VirtualCells<'d, Fp>, enable: Expression, @@ -205,21 +215,25 @@ impl HashTable { hash: Expression, ) -> Vec<(Expression, Expression)> { vec![ + ( + enable.clone() * hash, + meta.query_advice(self.0[0], Rotation::cur()), + ), ( enable.clone() * fst, - meta.query_advice(self.0, Rotation::cur()), + meta.query_advice(self.0[1], Rotation::cur()), ), + (enable * snd, meta.query_advice(self.0[2], Rotation::cur())), ( - enable.clone() * snd, - meta.query_advice(self.1, Rotation::cur()), + Expression::Constant(Fp::ZERO), + meta.query_advice(self.0[3], Rotation::cur()), ), - (enable * hash, meta.query_advice(self.2, Rotation::cur())), ] } /// a helper entry to fill hash table with specified rows, use padding record /// when hashing_records is not enough - pub fn fill_with_paddings<'d, Fp: FieldExt>( + pub fn fill_with_paddings<'d, Fp: PrimeField>( &self, layouter: &mut impl Layouter, hashing_records: impl Iterator + Clone, @@ -238,7 +252,7 @@ impl HashTable { } /// a helper entry to fill hash table - pub fn fill<'d, Fp: FieldExt>( + pub fn fill<'d, Fp: PrimeField>( &self, layouter: &mut impl Layouter, hashing_records: impl Iterator + Clone, @@ -247,9 +261,9 @@ impl HashTable { || "hash table", |mut table| { // default: 0, 0, 0 - table.assign_advice(|| "default", self.0, 0, || Value::known(Fp::zero()))?; - table.assign_advice(|| "default", self.1, 0, || Value::known(Fp::zero()))?; - table.assign_advice(|| "default", self.2, 0, || Value::known(Fp::zero()))?; + for col in self.0 { + table.assign_advice(|| "default", col, 0, || Value::known(Fp::ZERO))?; + } hashing_records .clone() @@ -258,11 +272,18 @@ impl HashTable { let (lh, rh, h) = val; let offset = offset + 1; - table.assign_advice(|| "left", self.0, offset, || Value::known(*lh))?; + table.assign_advice(|| "result", self.0[0], offset, || Value::known(*h))?; - table.assign_advice(|| "right", self.1, offset, || Value::known(*rh))?; + table.assign_advice(|| "left", self.0[1], offset, || Value::known(*lh))?; - table.assign_advice(|| "result", self.2, offset, || Value::known(*h))?; + table.assign_advice(|| "right", self.0[2], offset, || Value::known(*rh))?; + + table.assign_advice( + || "ctrl_pad", + self.0[3], + offset, + || Value::known(Fp::ZERO), + )?; Ok(()) }) @@ -279,8 +300,12 @@ struct MPTOpConfig { s_enable: Column, s_path: Column, depth: Column, + ctrl_type: Column, + s_ctrl_type: [Column; HASH_TYPE_CNT], old_hash_type: Column, new_hash_type: Column, + s_hash_match_ctrl: [Column; 2], //[old, new] + s_hash_match_ctrl_aux: [Column; 2], sibling: Column, acc_key: Column, path: Column, @@ -305,38 +330,54 @@ pub(crate) struct MPTOpGadget { impl MPTOpGadget { pub fn min_free_cols() -> usize { - 8 + 11 + } + + pub fn min_ctrl_types() -> usize { + HASH_TYPE_CNT } /// if the gadget would be used only once, this entry is more easy - pub fn configure_simple( + pub fn configure_simple( meta: &mut ConstraintSystem, sel: Selector, exported: &[Column], + s_ctrl_type: &[Column], free: &[Column], root_index: Option<(Column, Column)>, ) -> Self { let tables = MPTOpTables::configure_create(meta); let hash_tbls = HashTable::configure_create(meta); - Self::configure(meta, sel, exported, free, root_index, tables, hash_tbls) + Self::configure( + meta, + sel, + exported, + s_ctrl_type, + free, + root_index, + tables, + hash_tbls, + ) } /// create gadget from assigned cols, we need: /// + circuit selector * 1 /// + exported col * 4 (MUST by following sequence: layout_flag, s_enable, old_val, new_val) + /// + s_op_flags * 6 (corresponding 6 ctrl_types) /// + free col * 8 /// notice the gadget has bi-direction exporting (on top it exporting mpt root and bottom exporting leaf) - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, sel: Selector, exported: &[Column], + s_ctrl_type: &[Column], free: &[Column], root_index: Option<(Column, Column)>, tables: MPTOpTables, hash_tbl: HashTable, ) -> Self { - assert!(free.len() >= 8, "require at least 6 free cols"); + assert!(free.len() >= 8, "require at least 8 free cols"); let g_config = MPTOpConfig { tables, @@ -344,14 +385,18 @@ impl MPTOpGadget { s_path: free[0], depth: free[1], new_hash_type: free[2], - sibling: free[3], - path: free[4], - key_aux: free[5], - old_hash_type: exported[0], + old_hash_type: free[3], + sibling: free[4], + path: free[5], + key_aux: free[6], + s_hash_match_ctrl: [free[7], free[8]], + s_hash_match_ctrl_aux: [free[9], free[10]], + ctrl_type: exported[0], s_enable: exported[1], old_val: exported[2], new_val: exported[3], acc_key: exported[4], + s_ctrl_type: s_ctrl_type[0..6].try_into().expect("same size"), hash_table: hash_tbl, }; @@ -359,20 +404,22 @@ impl MPTOpGadget { let s_row = meta.query_selector(g_config.s_row); let s_enable = meta.query_advice(g_config.s_enable, Rotation::cur()); // s_enable ∈ {0, 1} - vec![s_row * (Expression::Constant(Fp::one()) - s_enable.clone()) * s_enable] + vec![s_row * (Expression::Constant(Fp::ONE) - s_enable.clone()) * s_enable] }); if let Some((old_root_index, new_root_index)) = root_index { meta.create_gate("root index", |meta| { let s_row = meta.query_selector(g_config.s_row); - let hash_type = meta.query_advice(g_config.old_hash_type, Rotation::cur()); let s_enable = s_row * meta.query_advice(g_config.s_enable, Rotation::cur()) - * lagrange_polynomial_for_hashtype::<_, 0>(hash_type); //Start; - // constraint root index: - // the old root in heading row (START) equal to the new_root_index_prev - // the old root in heading row (START) also equal to the old_root_index_cur - // the new root in heading row (START) equal must be equal to new_root_index_cur + * meta.query_advice( + g_config.s_ctrl_type[HashType::Start as usize], + Rotation::cur(), + ); + // constraint root index: + // the old root in heading row (START) equal to the new_root_index_prev + // the old root in heading row (START) also equal to the old_root_index_cur + // the new root in heading row (START) equal must be equal to new_root_index_cur vec![ s_enable.clone() * (meta.query_advice(g_config.old_val, Rotation::cur()) @@ -397,32 +444,37 @@ impl MPTOpGadget { } } - pub fn transition_rules() -> impl Iterator + Clone { + pub fn transition_rules() -> impl Iterator + Clone { let i1 = TRANSMAP .iter() .copied() - .map(|(a, b)| (a as u32, b as u32, CtrlTransitionKind::Mpt as u32)); - let i2 = OPMAP - .iter() - .copied() - .map(|(a, b)| (a as u32, b as u32, CtrlTransitionKind::Operation as u32)); + .map(|(a, b)| ([a as u32, b as u32, 0], CtrlTransitionKind::Mpt as u32)); + let i2 = OPMAP.iter().copied().map(|(a, b, c)| { + ( + [a as u32, b as u32, c as u32], + CtrlTransitionKind::Operation as u32, + ) + }); i1.chain(i2) } - /* pub fn init(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + /* pub fn init(&self, layouter: &mut impl Layouter) -> Result<(), Error> { self.tables .fill_constant(layouter, Self::transition_rules()) }*/ /// assign data and enable flag for MPT circuit - pub fn assign( + pub fn assign( &self, region: &mut Region<'_, Fp>, offset: usize, data: &SingleOp, ) -> Result { - let old_path_chip = PathChip::::construct(self.old_path.clone(), offset, &data.old); - let new_path_chip = PathChip::::construct(self.new_path.clone(), offset, &data.new); + let ctrl_type = data.ctrl_type(); + let old_path_chip = + PathChip::::construct(self.old_path.clone(), offset, &data.old, Some(&ctrl_type)); + let new_path_chip = + PathChip::::construct(self.new_path.clone(), offset, &data.new, Some(&ctrl_type)); let op_chip = OpChip::::construct(self.op.clone(), offset, data); // caution: we made double assignations on key cell so sequence is important @@ -438,7 +490,7 @@ impl MPTOpGadget { || "enable MPT circuit", self.s_enable, offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; } @@ -446,28 +498,36 @@ impl MPTOpGadget { } } -fn lagrange_polynomial_for_hashtype( +/* +fn lagrange_polynomial_for_hashtype( ref_n: Expression, ) -> Expression { super::lagrange_polynomial::(ref_n) } +*/ + +const HASH_TYPE_CNT: usize = 6; #[derive(Clone, Debug)] struct PathChipConfig { s_path: Column, hash_type: Column, + s_hash_type: [Column; HASH_TYPE_CNT], + s_match_ctrl_type: Column, + s_match_ctrl_aux: Column, val: Column, } /// chip for verify mutiple merkle path in MPT /// it do not need any auxiliary cols -struct PathChip<'d, F: FieldExt> { +struct PathChip<'d, F: PrimeField> { offset: usize, config: PathChipConfig, data: &'d MPTPath, + ref_ctrl_type: Option<&'d [HashType]>, } -impl Chip for PathChip<'_, Fp> { +impl Chip for PathChip<'_, Fp> { type Config = PathChipConfig; type Loaded = MPTPath; @@ -480,7 +540,7 @@ impl Chip for PathChip<'_, Fp> { } } -impl<'d, Fp: FieldExt> PathChip<'d, Fp> { +impl<'d, Fp: PrimeField> PathChip<'d, Fp> { fn configure( meta: &mut ConstraintSystem, g_config: &MPTOpConfig, @@ -488,11 +548,22 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { ) -> >::Config { let s_path = g_config.s_path; let s_enable = g_config.s_enable; + let s_hash_type = g_config.s_ctrl_type; let hash_type = if from_old { g_config.old_hash_type } else { g_config.new_hash_type }; + let s_match_ctrl_type = if from_old { + g_config.s_hash_match_ctrl[0] + } else { + g_config.s_hash_match_ctrl[1] + }; + let s_match_ctrl_aux = if from_old { + g_config.s_hash_match_ctrl_aux[0] + } else { + g_config.s_hash_match_ctrl_aux[1] + }; let val = if from_old { g_config.old_val } else { @@ -532,10 +603,20 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { // // from table formed by (left, right, hash) meta.lookup_any("mpt node hash", |meta| { - let hash_type = meta.query_advice(hash_type, Rotation::cur()); - let s_path = meta.query_selector(s_row) - * meta.query_advice(s_enable, Rotation::cur()) - * lagrange_polynomial_for_hashtype::<_, 2>(hash_type); //Middle + let s_hash_type_not_match = Expression::Constant(Fp::ONE) + - meta.query_advice(s_match_ctrl_type, Rotation::cur()); + let s_path = meta.query_advice(s_enable, Rotation::cur()) + * (meta.query_advice(s_hash_type[HashType::Middle as usize], Rotation::cur()) + + s_hash_type_not_match.clone() + * meta.query_advice( + s_hash_type[HashType::LeafExt as usize], + Rotation::cur(), + ) + + s_hash_type_not_match + * meta.query_advice( + s_hash_type[HashType::LeafExtFinal as usize], + Rotation::cur(), + )); //hash type is Middle: i.e ctrl type is Middle or (Ext and ExtFinal and not match) let path_bit = meta.query_advice(path, Rotation::cur()); let val_col = meta.query_advice(val, Rotation::cur()); @@ -553,9 +634,9 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { // calculate part of the leaf hash: hash(key_immediate, val) = hash_of_key_node meta.lookup_any("mpt leaf hash", |meta| { - let hash_type = meta.query_advice(hash_type, Rotation::cur()); let s_leaf = meta.query_advice(s_enable, Rotation::cur()) - * lagrange_polynomial_for_hashtype::<_, 5>(hash_type); //Leaf + * meta.query_advice(s_match_ctrl_type, Rotation::cur()) + * meta.query_advice(s_hash_type[HashType::Leaf as usize], Rotation::cur()); //(actually) Leaf let key_immediate = meta.query_advice(key_immediate, Rotation::cur()); let leaf_val = meta.query_advice(val, Rotation::cur()); @@ -565,10 +646,8 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { //transition, notice the start status is ensured outside of the gadget meta.lookup("mpt type trans", |meta| { - let s_not_begin = Expression::Constant(Fp::one()) - - lagrange_polynomial_for_hashtype::<_, 0>( - meta.query_advice(hash_type, Rotation::cur()), - ); //not Start + let s_not_begin = Expression::Constant(Fp::ONE) + - meta.query_advice(s_hash_type[HashType::Start as usize], Rotation::cur()); //not Start let s_block_enable = meta.query_advice(s_enable, Rotation::cur()) * s_not_begin; @@ -582,8 +661,8 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { meta.create_gate("leaf extended", |meta| { let enable = meta.query_selector(s_row) * meta.query_advice(s_enable, Rotation::cur()); - let hash_type = meta.query_advice(hash_type, Rotation::cur()); - let s_extended = lagrange_polynomial_for_hashtype::<_, 3>(hash_type); //LeafExt + let s_extended = meta.query_advice(s_match_ctrl_type, Rotation::cur()) + * meta.query_advice(s_hash_type[HashType::LeafExt as usize], Rotation::cur()); //(actually) LeafExt let sibling = meta.query_advice(sibling, Rotation::cur()); // + sibling must be 0 when hash_type is leaf extended, or malice // advisor can make arbital sibling which would halt the process of L2 @@ -599,8 +678,11 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { meta.create_gate("last leaf extended", |meta| { let enable = meta.query_selector(s_row) * meta.query_advice(s_enable, Rotation::cur()); - let hash_type = meta.query_advice(hash_type, Rotation::cur()); - let s_last_extended = lagrange_polynomial_for_hashtype::<_, 4>(hash_type); //LeafExtFinal + let s_last_extended = meta.query_advice(s_match_ctrl_type, Rotation::cur()) + * meta.query_advice( + s_hash_type[HashType::LeafExtFinal as usize], + Rotation::cur(), + ); //(actually) LeafExtFinal // + sibling must be previous value of val when hash_type is leaf extended final // (notice the value for leafExtendedFinal can be omitted) @@ -615,16 +697,18 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { // prove the silbing is really a leaf when extended meta.lookup_any("extended sibling proof 1", |meta| { let s_last_extended = meta.query_advice(s_enable, Rotation::cur()) - * lagrange_polynomial_for_hashtype::<_, 4>( - meta.query_advice(hash_type, Rotation::cur()), - ); //LeafExtFinal + * meta.query_advice(s_match_ctrl_type, Rotation::cur()) + * meta.query_advice( + s_hash_type[HashType::LeafExtFinal as usize], + Rotation::cur(), + ); //(actually) LeafExtFinal let key_proof = meta.query_advice(sibling, Rotation::next()); //key is written here let key_proof_immediate = meta.query_advice(key_immediate, Rotation::cur()); hash_table.build_lookup( meta, s_last_extended, - Expression::Constant(Fp::one()), + Expression::Constant(Fp::ONE), key_proof, key_proof_immediate, ) @@ -632,9 +716,11 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { meta.lookup_any("extended sibling proof 2", |meta| { let s_last_extended = meta.query_advice(s_enable, Rotation::cur()) - * lagrange_polynomial_for_hashtype::<_, 4>( - meta.query_advice(hash_type, Rotation::cur()), - ); //LeafExtFinal + * meta.query_advice(s_match_ctrl_type, Rotation::cur()) + * meta.query_advice( + s_hash_type[HashType::LeafExtFinal as usize], + Rotation::cur(), + ); //(actually) LeafExtFinal let extended_sibling = meta.query_advice(sibling, Rotation::cur()); let key_proof_immediate = meta.query_advice(key_immediate, Rotation::cur()); let key_proof_value = meta.query_advice(ext_sibling_val, Rotation::cur()); @@ -651,6 +737,9 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { PathChipConfig { s_path, hash_type, + s_hash_type, + s_match_ctrl_type, + s_match_ctrl_aux, val, } } @@ -659,49 +748,86 @@ impl<'d, Fp: FieldExt> PathChip<'d, Fp> { config: PathChipConfig, offset: usize, data: &'d >::Loaded, + ref_ctrl_type: Option<&'d [HashType]>, ) -> Self { Self { config, offset, data, + ref_ctrl_type, } } fn assign(&self, region: &mut Region<'_, Fp>) -> Result { let config = &self.config; - let mut offset = self.offset; + let offset = self.offset; let vals = &self.data.hashes; let hash_types = &self.data.hash_types; assert_eq!(hash_types.len(), vals.len()); - for (hash_type, val) in hash_types.iter().zip(vals.iter()) { - region.assign_advice(|| "val", config.val, offset, || Value::known(*val))?; + for (index, (hash_type, val)) in hash_types.iter().copied().zip(vals.iter()).enumerate() { + region.assign_advice(|| "val", config.val, offset + index, || Value::known(*val))?; region.assign_advice( - || format!("hash_type {}", *hash_type as u32), + || format!("hash_type {}", hash_type as u32), config.hash_type, - offset, - || Value::known(Fp::from(*hash_type as u64)), + offset + index, + || Value::known(Fp::from(hash_type as u64)), )?; region.assign_advice( || "sel", config.s_path, - offset, + offset + index, || { Value::known(match hash_type { - HashType::Start | HashType::Empty | HashType::Leaf => Fp::zero(), - _ => Fp::one(), + HashType::Start | HashType::Empty | HashType::Leaf => Fp::ZERO, + _ => Fp::ONE, }) }, )?; - offset += 1; } - Ok(offset) + let ref_ctrl_type = self + .ref_ctrl_type + .unwrap_or(&self.data.hash_types) + .iter() + .copied(); + for (index, (hash_type, ref_type)) in + hash_types.iter().copied().zip(ref_ctrl_type).enumerate() + { + region.assign_advice( + || "hash_type match aux", + config.s_match_ctrl_aux, + offset + index, + || { + Value::known( + Fp::from(ref_type as u64 - hash_type as u64) + .invert() + .unwrap_or(Fp::ZERO), + ) + }, + )?; + region.assign_advice( + || "hash_type match", + config.s_match_ctrl_type, + offset + index, + || { + Value::known(if hash_type == ref_type { + Fp::ONE + } else { + Fp::ZERO + }) + }, + )?; + } + + Ok(offset + hash_types.len()) } } #[derive(Clone, Debug)] struct OpChipConfig { + ctrl_type: Column, + s_ctrl_type: [Column; HASH_TYPE_CNT], sibling: Column, path: Column, depth: Column, @@ -711,13 +837,13 @@ struct OpChipConfig { /// chip for verify mutiple merkle path in MPT /// it do not need any auxiliary cols -struct OpChip<'d, F: FieldExt> { +struct OpChip<'d, F: PrimeField> { offset: usize, config: OpChipConfig, data: &'d SingleOp, } -impl Chip for OpChip<'_, Fp> { +impl Chip for OpChip<'_, Fp> { type Config = OpChipConfig; type Loaded = SingleOp; @@ -730,7 +856,7 @@ impl Chip for OpChip<'_, Fp> { } } -impl<'d, Fp: FieldExt> OpChip<'d, Fp> { +impl<'d, Fp: PrimeField> OpChip<'d, Fp> { fn configure( meta: &mut ConstraintSystem, g_config: &MPTOpConfig, @@ -742,6 +868,8 @@ impl<'d, Fp: FieldExt> OpChip<'d, Fp> { let sibling = g_config.sibling; let depth_aux = g_config.depth; let key_aux = g_config.key_aux; + let ctrl_type = g_config.ctrl_type; + let s_ctrl_type = g_config.s_ctrl_type; let s_row = g_config.s_row; let s_enable = g_config.s_enable; @@ -764,10 +892,10 @@ impl<'d, Fp: FieldExt> OpChip<'d, Fp> { meta.create_gate("s_path and path bit", |meta| { let enable = meta.query_selector(s_row) * meta.query_advice(s_enable, Rotation::cur()); let s_path = meta.query_advice(s_path, Rotation::cur()); - let s_path_not_opened = Expression::Constant(Fp::one()) - s_path.clone(); + let s_path_not_opened = Expression::Constant(Fp::ONE) - s_path.clone(); let path = meta.query_advice(path, Rotation::cur()); - let path_bit = (Expression::Constant(Fp::one()) - path.clone()) * path; + let path_bit = (Expression::Constant(Fp::ONE) - path.clone()) * path; let hash_type = meta.query_advice(old_hash_type, Rotation::cur()); let not_path_type = (hash_type.clone() @@ -779,9 +907,7 @@ impl<'d, Fp: FieldExt> OpChip<'d, Fp> { // s_path is not open when hash_type is "start" / "leaf" / "empty" // when s_path is 1, path ∈ {0, 1} vec![ - enable.clone() - * (Expression::Constant(Fp::one()) - s_path.clone()) - * s_path.clone(), + enable.clone() * (Expression::Constant(Fp::ONE) - s_path.clone()) * s_path.clone(), enable.clone() * not_path_type * s_path_not_opened, enable * s_path * path_bit, ] @@ -789,12 +915,10 @@ impl<'d, Fp: FieldExt> OpChip<'d, Fp> { meta.create_gate("depth", |meta| { let enable = meta.query_selector(s_row) * meta.query_advice(s_enable, Rotation::cur()); - let s_begin = lagrange_polynomial_for_hashtype::<_, 0>( - meta.query_advice(old_hash_type, Rotation::cur()), - ); //Start + let s_begin = meta.query_advice(s_ctrl_type[HashType::Start as usize], Rotation::cur()); //Start let path = meta.query_advice(path, Rotation::cur()); let depth_aux_start = meta.query_advice(depth_aux, Rotation::cur()) - - Expression::Constant(Fp::one().double().invert().unwrap()); + - Expression::Constant(Fp::ONE.double().invert().unwrap()); let depth_aux_common = meta.query_advice(depth_aux, Rotation::cur()) - meta.query_advice(depth_aux, Rotation::prev()) * Expression::Constant(Fp::from(2u64)); @@ -809,30 +933,31 @@ impl<'d, Fp: FieldExt> OpChip<'d, Fp> { vec![ enable.clone() * s_begin.clone() * depth_aux_start, enable.clone() - * (Expression::Constant(Fp::one()) - s_begin.clone()) + * (Expression::Constant(Fp::ONE) - s_begin.clone()) * depth_aux_common, enable.clone() * s_begin.clone() * meta.query_advice(acc_key, Rotation::cur()), - enable * (Expression::Constant(Fp::one()) - s_begin) * key_acc, + enable * (Expression::Constant(Fp::ONE) - s_begin) * key_acc, ] }); meta.lookup_any("mpt key pre calc", |meta| { - let hash_type = meta.query_advice(old_hash_type, Rotation::cur()); let s_leaf = meta.query_advice(s_enable, Rotation::cur()) - * lagrange_polynomial_for_hashtype::<_, 5>(hash_type); //Leaf + * meta.query_advice(s_ctrl_type[HashType::Leaf as usize], Rotation::cur()); //Leaf let key = meta.query_advice(acc_key, Rotation::cur()); let key_immediate = meta.query_advice(key_aux, Rotation::cur()); hash_table.build_lookup( meta, s_leaf, - Expression::Constant(Fp::one()), + Expression::Constant(Fp::ONE), key, key_immediate, ) }); OpChipConfig { + ctrl_type, + s_ctrl_type, path, sibling, depth: depth_aux, @@ -858,39 +983,60 @@ impl<'d, Fp: FieldExt> OpChip<'d, Fp> { let paths = &self.data.path; let siblings = &self.data.siblings; assert_eq!(paths.len(), siblings.len()); + let ctrl_type = self.data.ctrl_type(); let mut offset = self.offset; region.assign_advice( || "path padding", config.path, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; region.assign_advice( || "acckey padding", config.acc_key, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; region.assign_advice( || "depth padding", config.depth, offset, - || Value::known(Fp::one().double().invert().unwrap()), + || Value::known(Fp::ONE.double().invert().unwrap()), )?; region.assign_advice( || "sibling padding", config.sibling, offset, - || Value::known(Fp::zero()), + || Value::known(Fp::ZERO), )?; + region.assign_advice( + || "op type start", + config.ctrl_type, + offset, + || Value::known(Fp::from(ctrl_type[0] as u64)), + )?; + region.assign_advice( + || "enabling s_op", + config.s_ctrl_type[ctrl_type[0] as usize], + offset, + || Value::known(Fp::ONE), + )?; + + region.assign_advice( + || "sibling padding", + config.sibling, + offset, + || Value::known(Fp::ZERO), + )?; + offset += 1; - let mut cur_depth = Fp::one(); - let mut acc_key = Fp::zero(); + let mut cur_depth = Fp::ONE; + let mut acc_key = Fp::ZERO; let extend_proof = self.data.extended_proof(); - for (path, sibling) in paths.iter().zip(siblings.iter()) { + for (index, (path, sibling)) in paths.iter().zip(siblings.iter()).enumerate() { acc_key = *path * cur_depth + acc_key; region.assign_advice(|| "path", config.path, offset, || Value::known(*path))?; @@ -914,12 +1060,37 @@ impl<'d, Fp: FieldExt> OpChip<'d, Fp> { offset, || Value::known(extend_proof.map(|pf| pf.1).unwrap_or_default()), )?; + region.assign_advice( + || "ctrl type", + config.ctrl_type, + offset, + || Value::known(Fp::from(ctrl_type[index + 1] as u64)), + )?; + region.assign_advice( + || "enabling s_op", + config.s_ctrl_type[ctrl_type[index + 1] as usize], + offset, + || Value::known(Fp::ONE), + )?; cur_depth = cur_depth.double(); offset += 1; } // final line + let ctrl_type = *ctrl_type.last().expect("always has at least 2 rows"); + region.assign_advice( + || "op type", + config.ctrl_type, + offset, + || Value::known(Fp::from(ctrl_type as u64)), + )?; + region.assign_advice( + || "enabling s_op", + config.s_ctrl_type[ctrl_type as usize], + offset, + || Value::known(Fp::ONE), + )?; region.assign_advice( || "path", config.path, @@ -971,6 +1142,10 @@ mod test { Self { s_row: meta.complex_selector(), s_enable: meta.advice_column(), + ctrl_type: meta.advice_column(), + s_ctrl_type: [(); HASH_TYPE_CNT].map(|_| meta.advice_column()), + s_hash_match_ctrl: [(); 2].map(|_| meta.advice_column()), + s_hash_match_ctrl_aux: [(); 2].map(|_| meta.advice_column()), s_path: meta.advice_column(), sibling: meta.advice_column(), depth: meta.advice_column(), @@ -988,67 +1163,41 @@ mod test { /// simply flush a row with 0 value to avoid gate poisoned / cell error in debug prover, pub fn flush_row(&self, region: &mut Region<'_, Fp>, offset: usize) -> Result<(), Error> { - region.assign_advice( - || "flushing", - self.s_enable, - offset, - || Value::known(Fp::zero()), - )?; - region.assign_advice( - || "flushing", + for rand_flush_col in [ self.s_path, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice( - || "flushing", + self.ctrl_type, self.depth, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice( - || "flushing", self.sibling, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice( - || "flushing", self.key_aux, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice( - || "flushing", self.acc_key, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice(|| "flushing", self.path, offset, || Value::known(rand_fp()))?; - region.assign_advice( - || "flushing", + self.path, self.old_hash_type, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice( - || "flushing", self.new_hash_type, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice( - || "flushing", self.old_val, - offset, - || Value::known(rand_fp()), - )?; - region.assign_advice( - || "flushing", self.new_val, - offset, - || Value::known(rand_fp()), - )?; + ] { + region.assign_advice( + || "rand flushing", + rand_flush_col, + offset, + || Value::known(rand_fp()), + )?; + } + + for zero_flush_col in [self.s_enable] + .into_iter() + .chain(self.s_ctrl_type) + .chain(self.s_hash_match_ctrl) + .chain(self.s_hash_match_ctrl_aux) + { + region.assign_advice( + || "zero flushing", + zero_flush_col, + offset, + || Value::known(Fp::ZERO), + )?; + } + Ok(()) } } @@ -1094,31 +1243,40 @@ mod test { ) -> Result<(), Error> { let offset: usize = 1; let chip_cfg = config.chip.clone(); - let mpt_chip = PathChip::::construct(chip_cfg, offset, &self.data); + let mpt_chip = PathChip::::construct(chip_cfg, offset, &self.data, None); layouter.assign_region( || "main", |mut region| { let config = &config.global; config.flush_row(&mut region, 0)?; let mut working_offset = offset; - config.flush_row(&mut region, working_offset)?; // also flush the firt row of working region + //enable, flush the whole of working region and ctrl flag + for (index, hash_type) in self.data.hash_types.iter().copied().enumerate() { + config.s_row.enable(&mut region, working_offset + index)?; + config.flush_row(&mut region, working_offset + index)?; + region.assign_advice( + || "enable", + config.s_ctrl_type[hash_type as usize], + working_offset + index, + || Value::known(Fp::ONE), + )?; + } region.assign_advice( || "enable", config.s_enable, working_offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; - config.s_row.enable(&mut region, working_offset)?; + working_offset += 1; let next_offset = working_offset + self.siblings.len(); //need to fill some other cols for (index, offset) in (working_offset..next_offset).enumerate() { - config.s_row.enable(&mut region, offset)?; region.assign_advice( || "enable", config.s_enable, offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; region.assign_advice( || "sibling", @@ -1134,32 +1292,14 @@ mod test { )?; } - region.assign_advice( - || "enable", - config.s_enable, - next_offset, - || Value::known(Fp::one()), - )?; - region.assign_advice( - || "path", - config.path, - next_offset, - || Value::known(self.key_residue), - )?; - region.assign_advice( - || "sibling", - config.sibling, - next_offset, - || Value::known(Fp::zero()), - )?; - region.assign_advice( - || "key", - config.key_aux, - next_offset, - || Value::known(self.key_immediate), - )?; - - config.s_row.enable(&mut region, next_offset)?; + for (col, val, tip) in [ + (config.s_enable, Fp::ONE, "enable"), + (config.path, self.key_residue, "path"), + (config.sibling, Fp::ZERO, "sibling"), + (config.key_aux, self.key_immediate, "key"), + ] { + region.assign_advice(|| tip, col, next_offset, || Value::known(val))?; + } let next_offset = next_offset + 1; let chip_next_offset = mpt_chip.assign(&mut region)?; @@ -1179,7 +1319,7 @@ mod test { &mut layouter, TRANSMAP .iter() - .map(|(a, b)| (*a as u32, *b as u32, CtrlTransitionKind::Mpt as u32)), + .map(|(a, b)| ([*a as u32, *b as u32, 0], CtrlTransitionKind::Mpt as u32)), )?; config @@ -1226,7 +1366,7 @@ mod test { ); let path: Vec = path_bits .into_iter() - .map(|not_zero| if not_zero { Fp::one() } else { Fp::zero() }) + .map(|not_zero| if not_zero { Fp::ONE } else { Fp::ZERO }) .collect(); Self { @@ -1245,7 +1385,7 @@ mod test { TestPathCircuit::::configure(&mut cs); println!("mpt path gadget degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); } #[test] @@ -1277,8 +1417,6 @@ mod test { #[derive(Clone)] struct TestOpCircuit { data: SingleOp, - old_hash_types: Vec, - new_hash_types: Vec, } impl Circuit for TestOpCircuit { @@ -1311,8 +1449,12 @@ mod test { || "main", |mut region| { let config = &config.global; - config.flush_row(&mut region, 0)?; - let next_offset = offset + self.old_hash_types.len(); + let next_offset = offset + self.data.old.hash_types.len(); + //flush working region and ctrl flags + for offset in 0..next_offset { + config.flush_row(&mut region, offset)?; + } + //need to fill some other cols for (index, offset) in (offset..next_offset).enumerate() { config.s_row.enable(&mut region, offset)?; @@ -1320,18 +1462,16 @@ mod test { || "enable", config.s_enable, offset, - || Value::known(Fp::one()), + || Value::known(Fp::ONE), )?; region.assign_advice( || "s_path", config.s_path, offset, || { - Value::known(match self.old_hash_types[index] { - HashType::Empty | HashType::Leaf | HashType::Start => { - Fp::zero() - } - _ => Fp::one(), + Value::known(match self.data.old.hash_types[index] { + HashType::Empty | HashType::Leaf | HashType::Start => Fp::ZERO, + _ => Fp::ONE, }) }, )?; @@ -1339,13 +1479,13 @@ mod test { || "old hash_type", config.old_hash_type, offset, - || Value::known(Fp::from(self.old_hash_types[index] as u64)), + || Value::known(Fp::from(self.data.old.hash_types[index] as u64)), )?; region.assign_advice( || "new hash_type", config.new_hash_type, offset, - || Value::known(Fp::from(self.new_hash_types[index] as u64)), + || Value::known(Fp::from(self.data.new.hash_types[index] as u64)), )?; } @@ -1364,17 +1504,23 @@ mod test { config.global.tables.fill_constant( &mut layouter, - OPMAP - .iter() - .map(|(a, b)| (*a as u32, *b as u32, CtrlTransitionKind::Operation as u32)) - .chain(Some((0, 0, CtrlTransitionKind::Operation as u32))), + OPMAP.iter().map(|(a, b, c)| { + ( + [*a as u32, *b as u32, *c as u32], + CtrlTransitionKind::Operation as u32, + ) + }), )?; // op chip now need hash table (for key hash lookup) - config - .global - .hash_table - .fill(&mut layouter, self.data.old.hash_traces.iter())?; + config.global.hash_table.fill( + &mut layouter, + self.data + .old + .hash_traces + .iter() + .chain([(Fp::ONE, self.data.key, Fp::ZERO)].iter()), //dummy for key calc + )?; Ok(()) } @@ -1382,15 +1528,7 @@ mod test { impl TestOpCircuit { fn from_op(op: SingleOp) -> Self { - Self { - old_hash_types: op.old.hash_types, - new_hash_types: op.new.hash_types, - data: SingleOp:: { - path: op.path, - siblings: op.siblings, - ..Default::default() - }, - } + Self { data: op } } } @@ -1400,7 +1538,7 @@ mod test { TestOpCircuit::configure(&mut cs); println!("mpt op gadget degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); } lazy_static! { @@ -1413,13 +1551,16 @@ mod test { key: Fp::from(4u64), key_residual: Fp::from(4u64), old: MPTPath::{ - hash_traces: vec![(Fp::one(), Fp::from(4u64), Fp::zero())], + hash_traces: vec![(Fp::ONE, Fp::from(4u64), Fp::ZERO)], + hash_types: vec![HashType::Start, HashType::Empty], + ..Default::default() + }, + new: MPTPath:: { + hash_types: vec![HashType::Start, HashType::Leaf], ..Default::default() }, ..Default::default() }, - old_hash_types: vec![HashType::Start, HashType::Empty], - new_hash_types: vec![HashType::Start, HashType::Leaf], } }; @@ -1427,47 +1568,53 @@ mod test { TestOpCircuit { data: SingleOp::{ siblings: vec![Fp::from(11u64)], - path: vec![Fp::one()], + path: vec![Fp::ONE], key: Fp::from(17u64), //0b10001u64 key_residual: Fp::from(8u64), old: MPTPath::{ - hash_traces: vec![(Fp::one(), Fp::from(9u64), Fp::zero())], + hash_traces: vec![(Fp::ONE, Fp::from(9u64), Fp::ZERO)], + hash_types: vec![HashType::Start, HashType::LeafExtFinal, HashType::Empty], + ..Default::default() + }, + new: MPTPath:: { + hash_types: vec![HashType::Start, HashType::Middle, HashType::Leaf], ..Default::default() }, ..Default::default() }, - old_hash_types: vec![HashType::Start, HashType::LeafExtFinal, HashType::Empty], - new_hash_types: vec![HashType::Start, HashType::Middle, HashType::Leaf], } }; static ref DEMOCIRCUIT3: TestOpCircuit = { TestOpCircuit { data: SingleOp::{ - siblings: vec![Fp::from(11u64), Fp::zero(), Fp::from(22u64)], - path: vec![Fp::one(), Fp::zero(), Fp::one()], + siblings: vec![Fp::from(11u64), Fp::ZERO, Fp::from(22u64)], + path: vec![Fp::ONE, Fp::ZERO, Fp::ONE], key: Fp::from(45u64), //0b101101u64 key_residual: Fp::from(5u64), old: MPTPath::{ - hash_traces: vec![(Fp::one(), Fp::from(45u64), Fp::zero())], + hash_traces: vec![(Fp::ONE, Fp::from(45u64), Fp::ZERO)], + hash_types: vec![ + HashType::Start, + HashType::Middle, + HashType::LeafExt, + HashType::LeafExtFinal, + HashType::Empty, + ], + ..Default::default() + }, + new: MPTPath:: { + hash_types: vec![ + HashType::Start, + HashType::Middle, + HashType::Middle, + HashType::Middle, + HashType::Leaf, + ], ..Default::default() }, ..Default::default() }, - old_hash_types: vec![ - HashType::Start, - HashType::Middle, - HashType::LeafExt, - HashType::LeafExtFinal, - HashType::Empty, - ], - new_hash_types: vec![ - HashType::Start, - HashType::Middle, - HashType::Middle, - HashType::Middle, - HashType::Leaf, - ], } }; } @@ -1494,11 +1641,7 @@ mod test { let op = SingleOp::::create_rand_op(3, None, None, mock_hash); let k = 5; - let circuit = TestOpCircuit { - old_hash_types: op.old.hash_types.clone(), - new_hash_types: op.new.hash_types.clone(), - data: op, - }; + let circuit = TestOpCircuit { data: op }; let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } @@ -1507,7 +1650,7 @@ mod test { struct GadgetTestConfig { gadget: MPTOpGadget, sel: Selector, - free_cols: [Column; 16], + free_cols: Vec>, } // express for a single path block @@ -1526,24 +1669,21 @@ mod test { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let sel = meta.complex_selector(); - let free_cols = [(); 16].map(|_| meta.advice_column()); - let exported_cols = [ - free_cols[0], - free_cols[1], - free_cols[2], - free_cols[3], - free_cols[4], - free_cols[5], - free_cols[6], - free_cols[7], - ]; + let free_cols: Vec<_> = (0..(8 + //exported + MPTOpGadget::min_ctrl_types() + + MPTOpGadget::min_free_cols())) + .map(|_| meta.advice_column()) + .collect(); + let exported_cols = &free_cols[0..8]; + let op_flag_cols = &free_cols[8..8 + MPTOpGadget::min_ctrl_types()]; GadgetTestConfig { gadget: MPTOpGadget::configure_simple( meta, sel, - &exported_cols[..], - &free_cols[8..], + exported_cols, + op_flag_cols, + &free_cols[8 + MPTOpGadget::min_ctrl_types()..], None, ), free_cols, @@ -1568,12 +1708,20 @@ mod test { layouter.assign_region( || "mpt", |mut region| { - //flush first row, just avoid Cell error ... - config.free_cols.iter().try_for_each(|col| { - region - .assign_advice(|| "flushing", *col, 0, || Value::known(Fp::zero())) - .map(|_| ()) - })?; + //flush all row required by data, just avoid Cell error ... + for offset in 0..(1 + self.data.use_rows()) { + config.free_cols.iter().try_for_each(|col| { + region + .assign_advice( + || "flushing", + *col, + offset, + || Value::known(Fp::ZERO), + ) + .map(|_| ()) + })?; + } + let end = config.gadget.assign(&mut region, 1, &self.data)?; for offset in 1..end { config.sel.enable(&mut region, offset)?; @@ -1599,7 +1747,7 @@ mod test { MPTTestCircuit::configure(&mut cs); println!("mpt full gadget degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); } #[test] diff --git a/src/mpt_table.rs b/src/mpt_table.rs index 8c75d29b..62c02ea8 100644 --- a/src/mpt_table.rs +++ b/src/mpt_table.rs @@ -1,7 +1,7 @@ use crate::operation::{AccountOp, KeyValue}; use halo2_proofs::{ - arithmetic::{Field, FieldExt}, circuit::{Layouter, Value}, + ff::{Field, PrimeField}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells}, poly::Rotation, }; @@ -48,7 +48,7 @@ impl Config { self.address.index() } - pub fn bind_mpt_circuit( + pub fn bind_mpt_circuit( &self, meta: &mut ConstraintSystem, gadget_id: Column, @@ -178,15 +178,17 @@ impl Config { .collect() }); - meta.lookup_any("mpt account not exist entry lookup", |meta| { - let s_enable = meta.query_advice(self.proof_sel[3], Rotation::cur()); + if false { + meta.lookup_any("mpt account not exist entry lookup", |meta| { + let s_enable = meta.query_advice(self.proof_sel[3], Rotation::cur()); - build_entry_lookup_common(meta, (3, 0)) - .into_iter() - .chain(build_entry_lookup_not_exist(meta)) - .map(|(fst, snd)| (fst * s_enable.clone(), snd)) - .collect() - }); + build_entry_lookup_common(meta, (3, 0)) + .into_iter() + .chain(build_entry_lookup_not_exist(meta)) + .map(|(fst, snd)| (fst * s_enable.clone(), snd)) + .collect() + }); + } meta.lookup_any("mpt account destroy entry lookup", |meta| { let s_enable = meta.query_advice(self.proof_sel[4], Rotation::cur()); @@ -252,7 +254,7 @@ pub(crate) struct MPTEntry { old_value: KeyValue, } -impl MPTEntry { +impl> MPTEntry { // detect proof type from op data itself, just mocking, // not always correct pub fn mock_from_op(op: &AccountOp, randomness: F) -> Self { @@ -339,7 +341,7 @@ impl MPTEntry { ret.old_value.u8_rlc(randomness), ret.new_value.u8_rlc(randomness), ), - _ => (F::zero(), F::zero()), + _ => (F::ZERO, F::ZERO), }; ret.base.replace([ @@ -386,7 +388,7 @@ pub(crate) struct MPTTable { rows: usize, } -impl MPTTable { +impl> MPTTable { pub fn construct( config: Config, entries: impl IntoIterator>, @@ -456,7 +458,7 @@ impl MPTTable { // each col is boolean // when enabled, it must equal to proof_type vec![ - sel.clone() * col.clone() * (Expression::Constant(F::one()) - col.clone()), + sel.clone() * col.clone() * (Expression::Constant(F::ONE) - col.clone()), sel * col * (Expression::Constant(F::from(index as u64 + 1)) - proof_type), ] }); @@ -472,7 +474,7 @@ impl MPTTable { .reduce(|acc, col_exp| acc + col_exp) .expect("not null"); - vec![sel * total_enalbed.clone() * (Expression::Constant(F::one()) - total_enalbed)] + vec![sel * total_enalbed.clone() * (Expression::Constant(F::ONE) - total_enalbed)] }); Config { @@ -502,7 +504,7 @@ impl MPTTable { RangeCheckChip::construct(config.range_check_u8.clone()).load(layouter)?; layouter.assign_region( - || "mpt table", + || "mpt table inside", |mut region| { for (offset, entry) in self.entries.iter().enumerate() { for (index, col) in config.proof_sel.as_slice().iter().copied().enumerate() { @@ -512,34 +514,37 @@ impl MPTTable { offset, || { Value::known(if index + 1 == entry.proof_type as usize { - F::one() + F::ONE } else { - F::zero() + F::ZERO }) }, )?; } - if let Some(base_entries) = entry.base { - for (val, col) in base_entries.iter().zip( - [ - config.address, - config.storage_key, - config.proof_type, - config.new_root, - config.old_root, - config.new_value, - config.old_value, - ] - .as_slice(), - ) { - region.assign_advice( - || format!("assign for mpt table offset {}", offset), - *col, - offset, - || Value::known(*val), - )?; - } + let values = match entry.base { + Some(base_entries) => base_entries.map(|x| Value::known(x)), + None => [Value::unknown(); 7], + }; + + for (val, col) in values.iter().zip( + [ + config.address, + config.storage_key, + config.proof_type, + config.new_root, + config.old_root, + config.new_value, + config.old_value, + ] + .as_slice(), + ) { + region.assign_advice( + || format!("assign for mpt table offset {}", offset), + *col, + offset, + || *val, + )?; } config.storage_key_2.assign( @@ -612,7 +617,7 @@ impl MPTTable { || "flush rows", col, row, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; } @@ -683,7 +688,7 @@ mod test { TestMPTTableCircuit::configure(&mut cs); println!("mpt table circuit degree: {}", cs.degree()); - //assert!(cs.degree() <= 9); + assert!(cs.degree() <= 9); } #[test] @@ -770,7 +775,7 @@ mod test { proof_type: MPTProofType::BalanceChanged, base: Some([ address, - Fp::zero(), + Fp::ZERO, Fp::from(MPTProofType::BalanceChanged as u64), rand_fp(), rand_fp(), @@ -803,13 +808,13 @@ mod test { let entry3 = MPTEntry { proof_type: MPTProofType::AccountDoesNotExist, base: Some([ - address + Fp::one(), - Fp::zero(), + address + Fp::ONE, + Fp::ZERO, Fp::from(MPTProofType::AccountDoesNotExist as u64), entry2.base.unwrap()[4], entry2.base.unwrap()[4], - Fp::zero(), - Fp::zero(), + Fp::ZERO, + Fp::ZERO, ]), storage_key: Default::default(), new_value: Default::default(), diff --git a/src/mpt_table/byte32.rs b/src/mpt_table/byte32.rs index c8001cce..4a76e2fe 100644 --- a/src/mpt_table/byte32.rs +++ b/src/mpt_table/byte32.rs @@ -1,6 +1,6 @@ use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Region, Value}, + ff::PrimeField, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, poly::Rotation, }; @@ -12,7 +12,7 @@ pub(crate) struct Config { } impl Config { - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, sel: Selector, rep: &[Column; N], @@ -49,7 +49,7 @@ impl Config { Self { rep_hi, rep_lo } } - pub fn assign( + pub fn assign( &self, region: &mut Region<'_, F>, offset: usize, @@ -67,12 +67,12 @@ impl Config { Ok(true) } - pub fn flush( + pub fn flush( &self, region: &mut Region<'_, F>, offset: usize, ) -> Result { - self.assign(region, offset, &(F::zero(), F::zero())) + self.assign(region, offset, &(F::ZERO, F::ZERO)) } } @@ -124,7 +124,7 @@ mod test { let val = meta.query_advice(val, Rotation::cur()); vec![ meta.query_selector(sel) - * rep.bind_rlc_value(meta, val, Expression::Constant(Fp::one()), None), + * rep.bind_rlc_value(meta, val, Expression::Constant(Fp::ONE), None), ] }); diff --git a/src/mpt_table/range_check.rs b/src/mpt_table/range_check.rs index 10f52602..53701876 100644 --- a/src/mpt_table/range_check.rs +++ b/src/mpt_table/range_check.rs @@ -1,6 +1,7 @@ use halo2_proofs::{ - arithmetic::{Field, FieldExt}, + arithmetic::Field, circuit::{Layouter, Value}, + ff::PrimeField, plonk::{Advice, Column, ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, poly::Rotation, }; @@ -35,7 +36,7 @@ pub(crate) struct Chip { _marker: PhantomData, } -impl Chip { +impl Chip { const RANGE: usize = 1 << N; pub fn construct(config: Config) -> Self { diff --git a/src/mpt_table/value_rep.rs b/src/mpt_table/value_rep.rs index 8797fd0a..da896660 100644 --- a/src/mpt_table/value_rep.rs +++ b/src/mpt_table/value_rep.rs @@ -71,7 +71,7 @@ impl Config { let le_bytes = val.to_repr(); let limb_bytes = EXP / 8; - let mut out = [F::zero(); N]; + let mut out = [F::ZERO; N]; for i in 0..N { out[N - i - 1] = F::from( @@ -109,7 +109,7 @@ impl Config { region: &mut Region<'_, F>, offset: usize, ) -> Result { - self.assign(region, offset, [F::zero(); N].as_slice()) + self.assign(region, offset, [F::ZERO; N].as_slice()) } } diff --git a/src/operation.rs b/src/operation.rs index 0c781cd1..f1c8afe0 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -4,14 +4,14 @@ use super::{eth, serde, HashType}; use crate::hash::Hashable; -use halo2_proofs::arithmetic::FieldExt; +use halo2_proofs::ff::{FromUniformBytes, PrimeField}; use num_bigint::BigUint; use std::cmp::Ordering; use std::convert::TryFrom; /// Indicate the current status of an MPTPath #[derive(Clone, Copy, Debug)] -pub enum MPTPathStatus { +pub enum MPTPathStatus { /// Path has empty leaf node Empty, /// Path has leaf node and the (key, keyImmediate) is tracked @@ -26,7 +26,7 @@ pub enum MPTPathStatus { /// according to the hash_type. It would be used for the layout of MPT /// circuit #[derive(Clone, Debug)] -pub struct MPTPath { +pub struct MPTPath { /// hash types from beginning of a path, start with HashType::Start pub hash_types: Vec, /// hashes from beginning of path, from the root of MPT to leaf node @@ -39,18 +39,18 @@ pub struct MPTPath { pub status: MPTPathStatus, } -impl Default for MPTPath { +impl Default for MPTPath { fn default() -> Self { Self { hash_types: vec![HashType::Start, HashType::Empty], - hashes: vec![Fp::zero(), Fp::zero()], + hashes: vec![Fp::ZERO, Fp::ZERO], hash_traces: Default::default(), status: MPTPathStatus::Empty, } } } -impl MPTPath { +impl MPTPath { /// the root of MPT pub fn root(&self) -> Fp { self.hashes[0] @@ -113,7 +113,7 @@ impl MPTPath { assert!(self.hash_types.len() > 1, "can not extend empty path"); let ins_pos = self.hash_types.len() - 1; // can only extend a path with leaf - let new_key_immediate = hasher(&Fp::one(), &self.key().expect("can only extend leaf")); + let new_key_immediate = hasher(&Fp::ONE, &self.key().expect("can only extend leaf")); let status = match self.status { MPTPathStatus::Leaf((fp, fp_immediate)) => MPTPathStatus::Extended(( (new_key, new_key_immediate), @@ -134,7 +134,7 @@ impl MPTPath { // move the old value at last row to upper (row LeafExtFinal) addi_hashes.push(hashes[ins_pos]); - hashes[ins_pos] = Fp::zero(); + hashes[ins_pos] = Fp::ZERO; drop(hashes.splice(ins_pos..ins_pos, addi_hashes)); Self { @@ -155,7 +155,7 @@ impl MPTPath { assert_eq!(path.len(), siblings.len()); let (status, mut hashes, mut hash_types, mut hash_traces) = if let Some(fp) = leaf { - let one = Fp::one(); + let one = Fp::ONE; let key_immediate = hasher(&one, &key); let leaf_hash = hasher(&key_immediate, &fp); @@ -168,7 +168,7 @@ impl MPTPath { } else { ( MPTPathStatus::Empty, - vec![Fp::zero(), Fp::zero()], + vec![Fp::ZERO, Fp::ZERO], vec![HashType::Empty], Vec::new(), ) @@ -219,7 +219,7 @@ impl MPTPath { /// Represent for a single operation #[derive(Clone, Debug, Default)] -pub struct SingleOp { +pub struct SingleOp { /// the key of operation pub key: Fp, /// the immediate in key hashing @@ -236,12 +236,37 @@ pub struct SingleOp { pub new: MPTPath, } -impl SingleOp { +impl SingleOp { /// indicate rows would take in circuit layout pub fn use_rows(&self) -> usize { self.siblings.len() + 2 } + /// calculate the ctrl_type base on the two hash type of MPTPath + pub fn ctrl_type(&self) -> Vec { + self.old + .hash_types + .iter() + .copied() + .zip(self.new.hash_types.clone()) + .map(|type_pair| match type_pair { + (old, new) if old == new => old, + (HashType::Middle, HashType::LeafExt) | (HashType::LeafExt, HashType::Middle) => { + HashType::LeafExt + } + (HashType::Middle, HashType::LeafExtFinal) + | (HashType::LeafExtFinal, HashType::Middle) => HashType::LeafExtFinal, + (HashType::Empty, HashType::Leaf) | (HashType::Leaf, HashType::Empty) => { + HashType::Leaf + } + _ => unreachable!( + "invalid hash type pair: {:?}, {:?}", + type_pair.0, type_pair.1 + ), + }) + .collect() + } + /// the root of MPT before operation pub fn start_root(&self) -> Fp { self.old.root() @@ -270,7 +295,7 @@ impl SingleOp { ); let mut ret = Vec::new(); let mut tested_key = key; - let invert_2 = Fp::one().double().invert().unwrap(); + let invert_2 = Fp::ONE.double().invert().unwrap(); for _ in 0..layers { if tested_key.is_odd().unwrap_u8() == 1 { tested_key = tested_key * invert_2 - invert_2; @@ -297,7 +322,7 @@ impl SingleOp { .expect("must have immediate value for leaf node"); let path: Vec = path .into_iter() - .map(|b| if b { Fp::one() } else { Fp::zero() }) + .map(|b| if b { Fp::ONE } else { Fp::ZERO }) .collect(); Self { @@ -317,7 +342,7 @@ impl SingleOp { new_leaf: Fp, hasher: impl FnMut(&Fp, &Fp) -> Fp + Clone, ) -> Self { - let path_bool: Vec = self.path.iter().map(|v| *v != Fp::zero()).collect(); + let path_bool: Vec = self.path.iter().map(|v| *v != Fp::ZERO).collect(); let new = MPTPath::::create_with_hasher( &path_bool, &self.siblings, @@ -364,14 +389,14 @@ impl SingleOp { } } -fn bytes_to_fp(mut bt: Vec) -> std::io::Result { +fn bytes_to_fp>(mut bt: Vec) -> std::io::Result { // let expected_size = Fp::NUM_BITS as usize / 8 + if Fp::NUM_BITS % 8 == 0 { 0 } else { 1 }; bt.resize(64, 0u8); let arr: [u8; 64] = bt .as_slice() .try_into() .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; - Ok(Fp::from_bytes_wide(&arr)) + Ok(Fp::from_uniform_bytes(&arr)) } /// Represent for a eth account @@ -389,7 +414,7 @@ pub struct Account { pub hash_traces: Vec<(Fp, Fp, Fp)>, } -impl Account { +impl Account { /// calculating all traces ad-hoc with hasher function pub(crate) fn trace(mut self, mut hasher: impl FnMut(&Fp, &Fp) -> Fp) -> Self { let h1 = hasher(&self.codehash.0, &self.codehash.1); @@ -419,7 +444,7 @@ impl Account { /// there is totally 4 of them and the last one calculate the final hash pub fn hash_traces(&self, i: usize) -> Fp { if self.hash_traces.is_empty() { - Fp::zero() + Fp::ZERO } else { self.hash_traces[i].2 } @@ -428,7 +453,7 @@ impl Account { /// the hash of account, which act as leaf value in account trie pub fn account_hash(&self) -> Fp { if self.hash_traces.is_empty() { - Fp::zero() + Fp::ZERO } else { assert_eq!(self.hash_traces.len(), 4); self.hash_traces[3].2 @@ -456,12 +481,12 @@ pub struct KeyValue { data: (Fp, Fp, Fp), // (the first 16 bytes, the second 16 bytes, hash value) } -impl KeyValue { +impl> KeyValue { /// create object and omit the hash pub fn create_base(bytes32: (Fp, Fp)) -> Self { let (fst, snd) = bytes32; Self { - data: (fst, snd, Fp::zero()), + data: (fst, snd, Fp::ZERO), } } @@ -482,8 +507,8 @@ impl KeyValue { /// the u256 is represented by le bytes and combined with randomness 1, o, o^2 ... o^31 on each /// and we calculate it from be represent pub fn u8_rlc(&self, randomness: Fp) -> Fp { - let u128_hi = self.data.0.get_lower_128(); - let u128_lo = self.data.1.get_lower_128(); + let u128_hi = u128::from_le_bytes(self.data.0.to_repr()[0..16].try_into().unwrap()); + let u128_lo = u128::from_le_bytes(self.data.1.to_repr()[0..16].try_into().unwrap()); u128_hi .to_be_bytes() .into_iter() @@ -521,7 +546,7 @@ impl KeyValue { /// Represent an operation in eth MPT, which update 2 layer of tries (state and account) #[derive(Clone, Debug, Default)] -pub struct AccountOp { +pub struct AccountOp { /// the operation on the account trie (first layer) pub acc_trie: SingleOp, /// the operation on the state trie (second layer) @@ -542,7 +567,7 @@ pub struct AccountOp { pub store_key: Option>, } -impl AccountOp { +impl AccountOp { /// indicate rows would take for whole operation pub fn use_rows(&self) -> usize { self.use_rows_account() @@ -597,11 +622,7 @@ impl AccountOp { impl AccountOp { /// providing the padding record for hash table pub fn padding_hash() -> (Fp, Fp, Fp) { - ( - Fp::zero(), - Fp::zero(), - Hashable::hash([Fp::zero(), Fp::zero()]), - ) + (Fp::ZERO, Fp::ZERO, Hashable::hash([Fp::ZERO, Fp::ZERO])) } /// iter all the hash traces inside an operation (may contain duplications) @@ -632,14 +653,14 @@ pub enum TraceError { } // parse Trace data into MPTPath and additional data (siblings and path) -struct SMTPathParse(MPTPath, Vec, Vec); +struct SMTPathParse(MPTPath, Vec, Vec); impl<'d, Fp: Hashable> TryFrom<&'d serde::SMTPath> for SMTPathParse { type Error = TraceError; fn try_from(path_trace: &'d serde::SMTPath) -> Result { let mut siblings: Vec = Vec::new(); for n in &path_trace.path { - let s = Fp::from_bytes_wide(&n.sibling.cast()); + let s = Fp::from_uniform_bytes(&n.sibling.cast()); siblings.push(s); } @@ -649,20 +670,20 @@ impl<'d, Fp: Hashable> TryFrom<&'d serde::SMTPath> for SMTPathParse { for i in 0..siblings.len() { let bit = (BigUint::from(1u64) << i) & &path_trace.path_part != BigUint::from(0u64); path_bits.push(bit); - path.push(if bit { Fp::one() } else { Fp::zero() }); + path.push(if bit { Fp::ONE } else { Fp::ZERO }); } - let mut key = Fp::zero(); + let mut key = Fp::ZERO; let mut leaf = None; // notice when there is no leaf node, providing 0 key if let Some(leaf_node) = &path_trace.leaf { - key = Fp::from_bytes_wide(&leaf_node.sibling.cast()); - leaf = Some(Fp::from_bytes_wide(&leaf_node.value.cast())); + key = Fp::from_uniform_bytes(&leaf_node.sibling.cast()); + leaf = Some(Fp::from_uniform_bytes(&leaf_node.value.cast())); } let mpt_path = MPTPath::create(&path_bits, &siblings, key, leaf); // sanity check - let root = Fp::from_bytes_wide(&path_trace.root.cast()); + let root = Fp::from_uniform_bytes(&path_trace.root.cast()); assert_eq!(root, mpt_path.root()); Ok(SMTPathParse(mpt_path, siblings, path)) @@ -678,7 +699,7 @@ impl<'d, Fp: Hashable> TryFrom<(&'d serde::SMTPath, &'d serde::SMTPath, serde::H ) -> Result { let (before, after, ref_key) = traces; - let key = Fp::from_bytes_wide(&ref_key.cast()); + let key = Fp::from_uniform_bytes(&ref_key.cast()); let before_parsed: SMTPathParse = before.try_into()?; let after_parsed: SMTPathParse = after.try_into()?; let mut old = before_parsed.0; @@ -722,7 +743,7 @@ impl<'d, Fp: Hashable> TryFrom<(&'d serde::SMTPath, &'d serde::SMTPath, serde::H if let Some(another_key) = old.key() { // we need to make full path extension for both side, manually - let invert_2 = Fp::one().double().invert().unwrap(); + let invert_2 = Fp::ONE.double().invert().unwrap(); let mut k1 = another_key; let mut k2 = key; let mut common_prefix_depth: usize = 0; @@ -740,7 +761,7 @@ impl<'d, Fp: Hashable> TryFrom<(&'d serde::SMTPath, &'d serde::SMTPath, serde::H common_prefix_depth += 1; if common_prefix_depth > path.len() { path.push(Fp::from(k2_bit as u64)); - siblings.push(Fp::zero()); + siblings.push(Fp::ZERO); } assert_ne!(k1, k2); k2_bit = k2.is_odd().unwrap_u8(); @@ -757,8 +778,8 @@ impl<'d, Fp: Hashable> TryFrom<(&'d serde::SMTPath, &'d serde::SMTPath, serde::H } // and also insert the required key hash trace - let key_immediate = ::hash([Fp::one(), key]); - old.hash_traces.push((Fp::one(), key, key_immediate)); + let key_immediate = ::hash([Fp::ONE, key]); + old.hash_traces.push((Fp::ONE, key, key_immediate)); (siblings, path, key_immediate) } else if old.key() == Some(key) { @@ -800,7 +821,7 @@ impl<'d, Fp: Hashable> TryFrom<(&'d serde::AccountData, Fp)> for Account { let balance = bytes_to_fp(acc.balance.to_bytes_le()).map_err(TraceError::DeErr)?; let buf = acc.code_hash.to_bytes_le(); let codehash = if buf.len() < 16 { - (bytes_to_fp(buf).map_err(TraceError::DeErr)?, Fp::zero()) + (bytes_to_fp(buf).map_err(TraceError::DeErr)?, Fp::ZERO) } else { ( bytes_to_fp(Vec::from(&buf[16..])).map_err(TraceError::DeErr)?, @@ -871,16 +892,14 @@ impl<'d, Fp: Hashable> TryFrom<&'d serde::SMTTrace> for AccountOp { }; let comm_state_root = match trace.common_state_root { - Some(h) => Fp::from_bytes_wide(&h.cast()), - None => Fp::zero(), + Some(h) => Fp::from_uniform_bytes(&h.cast()), + None => Fp::ZERO, }; - // TODO: currently we just check if it is creation (no checking for deletion) - let account_before = if let Some(account_data) = &trace.account_update[0] { - let leaf = acc_trie - .old - .leaf() - .expect("leaf should exist when there is account data"); + let account_before = if let Some(leaf) = acc_trie.old.leaf() { + let account_data = trace.account_update[0] + .as_ref() + .expect("account should exist when there is leaf"); let old_state_root = state_trie .as_ref() .map(|s| s.start_root()) @@ -894,11 +913,10 @@ impl<'d, Fp: Hashable> TryFrom<&'d serde::SMTTrace> for AccountOp { None }; - let account_after = if let Some(account_data) = &trace.account_update[1] { - let leaf = acc_trie - .new - .leaf() - .expect("leaf should exist when there is account data"); + let account_after = if let Some(leaf) = acc_trie.new.leaf() { + let account_data = trace.account_update[1] + .as_ref() + .expect("account should exist when there is leaf"); let new_state_root = state_trie .as_ref() .map(|s| s.new_root()) @@ -953,30 +971,32 @@ impl<'d, Fp: Hashable> TryFrom<&'d serde::SMTTrace> for AccountOp { } #[derive(Clone, Debug, PartialEq, Eq)] -struct HashableField(Fp); +struct HashableField>(Fp); -impl std::hash::Hash for HashableField { +impl> std::hash::Hash for HashableField { fn hash(&self, state: &mut H) where H: std::hash::Hasher, { - state.write_u128(self.0.get_lower_128()); + state.write_u128(u128::from_le_bytes( + self.0.to_repr()[0..16].try_into().unwrap(), + )); } } -impl From for HashableField { +impl> From for HashableField { fn from(v: Fp) -> Self { Self(v) } } #[derive(Clone)] -pub(crate) struct HashTracesSrc { +pub(crate) struct HashTracesSrc> { source: T, deduplicator: std::collections::HashSet>, } -impl From for HashTracesSrc { +impl> From for HashTracesSrc { fn from(source: T) -> Self { Self { source, @@ -988,7 +1008,7 @@ impl From for HashTracesSrc { impl<'d, T, Fp> Iterator for HashTracesSrc where T: Iterator, - Fp: FieldExt, + Fp: PrimeField, { type Item = &'d (Fp, Fp, Fp); @@ -1010,7 +1030,7 @@ mod tests { use crate::test_utils::{rand_bytes_array, rand_gen, Fp}; use halo2_proofs::halo2curves::group::ff::{Field, PrimeField}; - impl SingleOp { + impl SingleOp { /// create an fully random update operation with leafs customable pub fn create_rand_op( layers: usize, @@ -1032,7 +1052,7 @@ mod tests { } } - impl KeyValue { + impl KeyValue { /// create an fully random k/v pub fn create_rand(mut hasher: impl FnMut(&Fp, &Fp) -> Fp + Clone) -> Self { let a = Fp::from_u128(u128::from_be_bytes(rand_bytes_array::<16>())); @@ -1043,10 +1063,10 @@ mod tests { } } - fn decompose(inp: Fp, l: usize) -> (Vec, Fp) { + fn decompose(inp: Fp, l: usize) -> (Vec, Fp) { let mut ret = Vec::new(); let mut tested_key = inp; - let invert_2 = Fp::one().double().invert().unwrap(); + let invert_2 = Fp::ONE.double().invert().unwrap(); for _ in 0..l { if tested_key.is_odd().unwrap_u8() == 1 { tested_key = tested_key * invert_2 - invert_2; @@ -1059,12 +1079,12 @@ mod tests { (ret, tested_key) } - fn recover(path: &[bool], res: Fp) -> Fp { - let mut mask = Fp::one(); - let mut ret = Fp::zero(); + fn recover(path: &[bool], res: Fp) -> Fp { + let mut mask = Fp::ONE; + let mut ret = Fp::ZERO; for b in path { - ret += if *b { mask } else { Fp::zero() }; + ret += if *b { mask } else { Fp::ZERO }; mask = mask.double(); } @@ -1171,7 +1191,7 @@ mod tests { let data: Account = Account { balance: Fp::from(0u64), nonce: Fp::from(1u64), - codehash: (Fp::zero(), Fp::zero()), + codehash: (Fp::ZERO, Fp::ZERO), //0x20b24ebee7712fbbe84a15027eba4f1208e2e2df9f925de51b3382b86433e6a5 state_root: Fp::from_str_vartime( "14789053415173694845992038966920525110567435779704439275440571405364058384037", diff --git a/src/test_utils.rs b/src/test_utils.rs index 26e542ae..ea7b80e9 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,4 +1,4 @@ -pub use halo2_proofs::arithmetic::{Field, FieldExt}; +pub use halo2_proofs::ff::{Field, PrimeField}; pub use halo2_proofs::halo2curves::bn256::Fr as Fp; use lazy_static::lazy_static; use rand::{random, SeedableRng}; diff --git a/tests/trace_proving.rs b/tests/trace_proving.rs index 547f01a1..cc337135 100644 --- a/tests/trace_proving.rs +++ b/tests/trace_proving.rs @@ -1,4 +1,5 @@ use halo2_mpt_circuits::{operation::AccountOp, serde, EthTrie}; +use halo2_proofs::SerdeFormat; use halo2_proofs::dev::MockProver; use halo2_proofs::halo2curves::bn256::{Bn256, Fr as Fp, G1Affine}; use halo2_proofs::plonk::{create_proof, keygen_pk, keygen_vk, verify_proof}; @@ -94,14 +95,14 @@ fn vk_validity() { let vk1 = keygen_vk(¶ms, &circuit).unwrap(); let mut vk1_buf: Vec = Vec::new(); - vk1.write(&mut vk1_buf).unwrap(); + vk1.write(&mut vk1_buf, SerdeFormat::RawBytes).unwrap(); let data: EthTrie = Default::default(); let (circuit, _) = data.circuits(200); let vk2 = keygen_vk(¶ms, &circuit).unwrap(); let mut vk2_buf: Vec = Vec::new(); - vk2.write(&mut vk2_buf).unwrap(); + vk2.write(&mut vk2_buf, SerdeFormat::RawBytes).unwrap(); assert_eq!(vk1_buf, vk2_buf); }