From f8a3e55f5b82aeb6dc0b51c371c5f7c707bc5376 Mon Sep 17 00:00:00 2001 From: Steven Gu Date: Wed, 26 Apr 2023 15:13:42 +0800 Subject: [PATCH 1/3] Add Challenge API. --- src/challenges.rs | 52 ++++++++++++ src/gadgets/mpt_update.rs | 25 +++++- src/lib.rs | 65 +++++++++------ src/mpt_table.rs | 170 ++++++++++++++++++++------------------ src/operation.rs | 24 +++--- 5 files changed, 214 insertions(+), 122 deletions(-) create mode 100644 src/challenges.rs diff --git a/src/challenges.rs b/src/challenges.rs new file mode 100644 index 00000000..d568afb9 --- /dev/null +++ b/src/challenges.rs @@ -0,0 +1,52 @@ +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Layouter, Value}, + plonk::{Challenge, ConstraintSystem, Expression, FirstPhase, VirtualCells}, +}; + +#[derive(Clone, Copy, Debug)] +pub struct Challenges { + mpt_word: T, +} + +impl Challenges { + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + mpt_word: meta.challenge_usable_after(FirstPhase), + } + } + + /// Return `Expression` of challenges from `ConstraintSystem`. + pub fn exprs(&self, meta: &mut ConstraintSystem) -> Challenges> { + let [mpt_word] = query_expression(meta, |meta| { + [self.mpt_word].map(|challenge| meta.query_challenge(challenge)) + }); + + Challenges { mpt_word } + } + + /// Return `Value` of challenges from `Layouter`. + pub fn values(&self, layouter: &impl Layouter) -> Challenges> { + Challenges { + mpt_word: layouter.get_challenge(self.mpt_word), + } + } +} + +impl Challenges { + pub fn mpt_word(&self) -> T { + self.mpt_word.clone() + } +} + +fn query_expression( + meta: &mut ConstraintSystem, + mut f: impl FnMut(&mut VirtualCells) -> T, +) -> T { + let mut expr = None; + meta.create_gate("Query expression", |meta| { + expr = Some(f(meta)); + Some(Expression::Constant(F::from(0))) + }); + expr.unwrap() +} diff --git a/src/gadgets/mpt_update.rs b/src/gadgets/mpt_update.rs index ff0aafbe..ff98c26a 100644 --- a/src/gadgets/mpt_update.rs +++ b/src/gadgets/mpt_update.rs @@ -5,6 +5,7 @@ use super::{ poseidon::PoseidonLookup, }; use crate::{ + challenges::Challenges, constraint_builder::{AdviceColumn, ConstraintBuilder, Query, SelectorColumn}, serde::SMTTrace, types::Proof, @@ -13,7 +14,10 @@ use crate::{ use ethers_core::k256::elliptic_curve::PrimeField; use ethers_core::types::Address; use halo2_proofs::{ - arithmetic::FieldExt, circuit::Region, halo2curves::bn256::Fr, plonk::ConstraintSystem, + arithmetic::FieldExt, + circuit::{Region, Value}, + halo2curves::bn256::Fr, + plonk::{ConstraintSystem, Expression}, }; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -104,6 +108,7 @@ impl MptUpdateConfig { fn configure( cs: &mut ConstraintSystem, cb: &mut ConstraintBuilder, + challenges: &Challenges>, poseidon: &impl PoseidonLookup, key_bit: &impl KeyBitLookup, rlc: &impl RlcLookup, @@ -178,8 +183,13 @@ impl MptUpdateConfig { config } - fn assign(&self, region: &mut Region<'_, Fr>, updates: &[SMTTrace]) { - let randomness = Fr::from(123123u64); // TODOOOOOOO + fn assign( + &self, + region: &mut Region<'_, Fr>, + updates: &[SMTTrace], + challenges: &Challenges>, + ) { + let randomness = challenges.mpt_word(); let mut offset = 0; for update in updates { @@ -581,6 +591,7 @@ mod test { KeyBitConfig, ByteBitGadget, ByteRepresentationConfig, + Challenges, ); type FloorPlanner = SimpleFloorPlanner; @@ -590,6 +601,7 @@ mod test { fn configure(cs: &mut ConstraintSystem) -> Self::Config { let mut cb = ConstraintBuilder::new(); + let challenges = Challenges::construct(cs); let poseidon = PoseidonConfig::configure(cs, &mut cb); let byte_bit = ByteBitGadget::configure(cs, &mut cb); let byte_representation = ByteRepresentationConfig::configure(cs, &mut cb, &byte_bit); @@ -606,9 +618,11 @@ mod test { let byte_representation = ByteRepresentationConfig::configure(cs, &mut cb, &byte_bit); + let challenges_exprs = challenges.exprs(cs); let mpt_update = MptUpdateConfig::configure( cs, &mut cb, + &challenges_exprs, &poseidon, &key_bit, &byte_representation, @@ -623,6 +637,7 @@ mod test { key_bit, byte_bit, byte_representation, + challenges, ) } @@ -638,12 +653,14 @@ mod test { key_bit, byte_bit, byte_representation, + challenges, ) = config; + let challenges_values = challenges.values(&layouter); layouter.assign_region( || "", |mut region| { - mpt_update.assign(&mut region, &self.updates); + mpt_update.assign(&mut region, &self.updates, &challenges_values); poseidon.assign(&mut region, &self.hash_traces()); canonical_representation.assign(&mut region, &self.keys()); // key_bit.assign(region, &[]); // self. diff --git a/src/lib.rs b/src/lib.rs index d44bd219..f9fd2ae5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub use crate::serde::{Hash, Row, RowDeError}; +mod challenges; mod constraint_builder; mod eth; mod gadgets; @@ -25,7 +26,9 @@ mod util; pub mod operation; pub mod serde; +use challenges::Challenges; use eth::StorageGadget; +use halo2_proofs::circuit::Value; use hash_circuit::hash::PoseidonHashTable; /// re-export required namespace from depened poseidon hash circuit pub use hash_circuit::{hash, poseidon}; @@ -155,7 +158,7 @@ impl SimpleTrie { } impl Circuit for SimpleTrie { - type Config = SimpleTrieConfig; + type Config = (SimpleTrieConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -186,20 +189,24 @@ impl Circuit for SimpleTrie { layer.get_free_cols(), Some(layer.get_root_indexs()), ); + let challenges = Challenges::construct(meta); let cst = meta.fixed_column(); meta.enable_constant(cst); - SimpleTrieConfig { - layer, - padding, - mpt, - } + ( + SimpleTrieConfig { + layer, + padding, + mpt, + }, + challenges, + ) } fn synthesize( &self, - config: Self::Config, + (config, _challenges): Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { layouter.assign_region( @@ -414,10 +421,10 @@ impl EthTrieConfig { meta: &mut ConstraintSystem, mpt_tbl: [Column; 7], hash_tbl: [Column; 5], - randomness: Expression, + challenges: Challenges>, ) -> Self { let mut lite_cfg = Self::configure_base(meta, hash_tbl); - let mpt_tbl = MPTTable::configure(meta, mpt_tbl, randomness); + let mpt_tbl = MPTTable::configure(meta, mpt_tbl, challenges.mpt_word()); let layer = &lite_cfg.layer; let layer_exported = layer.exported_cols(0); let gadget_ind = layer.get_gadget_index(); @@ -444,14 +451,14 @@ impl EthTrieConfig { pub fn load_mpt_table<'d, Fp: Hashable>( &self, layouter: &mut impl Layouter, - randomness: Option, + challenge: Option>, ops: impl IntoIterator>, tbl_tips: impl IntoIterator, 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) + if let Some(challenge) = challenge { + MPTEntry::from_op(proof_type, op, challenge) } else { MPTEntry::from_op_no_base(proof_type, op) } @@ -841,7 +848,7 @@ impl CommitmentIndexs { /// get commitment for lite circuit (no mpt) pub fn new() -> Self { let mut cs: ConstraintSystem = Default::default(); - let config = EthTrieCircuit::<_, true>::configure(&mut cs); + let config = EthTrieCircuit::<_, true>::configure(&mut cs).0; let trie_circuit_indexs = config.hash_tbl.commitment_index(); @@ -856,7 +863,7 @@ impl CommitmentIndexs { /// get commitment for full circuit pub fn new_full_circuit() -> Self { let mut cs: ConstraintSystem = Default::default(); - let config = EthTrieCircuit::<_, false>::configure(&mut cs); + let config = EthTrieCircuit::<_, false>::configure(&mut cs).0; let trie_circuit_indexs = config.hash_tbl.commitment_index(); let mpt_table_start = config @@ -877,10 +884,8 @@ impl CommitmentIndexs { } } -const TEMP_RANDOMNESS: u64 = 1; - impl Circuit for EthTrieCircuit { - type Config = EthTrieConfig; + type Config = (EthTrieConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -892,19 +897,24 @@ impl Circuit for EthTrieCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - if LITE { - EthTrieConfig::configure_lite(meta) - } else { - let base = [0; 7].map(|_| meta.advice_column()); - let hash_tbl = [0; 5].map(|_| meta.advice_column()); - let randomness = Expression::Constant(Fp::from(get_rand_base())); - EthTrieConfig::configure_sub(meta, base, hash_tbl, randomness) - } + let challenges = Challenges::construct(meta); + + ( + if LITE { + EthTrieConfig::configure_lite(meta) + } else { + let base = [0; 7].map(|_| meta.advice_column()); + let hash_tbl = [0; 5].map(|_| meta.advice_column()); + let challenges = challenges.exprs(meta); + EthTrieConfig::configure_sub(meta, base, hash_tbl, challenges) + }, + challenges, + ) } fn synthesize( &self, - config: Self::Config, + (config, challenges): Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { config.dev_load_hash_table( @@ -916,9 +926,10 @@ impl Circuit for EthTrieCircuit { if LITE { Ok(()) } else { + let mpt_word = challenges.values(&layouter).mpt_word(); config.load_mpt_table( &mut layouter, - Some(Fp::from(get_rand_base())), + Some(mpt_word), self.ops.as_slice(), self.mpt_table.iter().copied(), self.calcs, diff --git a/src/mpt_table.rs b/src/mpt_table.rs index d548996d..1345b9dc 100644 --- a/src/mpt_table.rs +++ b/src/mpt_table.rs @@ -253,7 +253,7 @@ pub enum MPTProofType { #[derive(Clone, Debug)] pub(crate) struct MPTEntry { proof_type: MPTProofType, - base: [Option; 7], + base: [Value; 7], storage_key: KeyValue, new_value: KeyValue, old_value: KeyValue, @@ -262,7 +262,7 @@ pub(crate) struct 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 { + pub fn mock_from_op(op: &AccountOp, randomness: Value) -> Self { if op.state_trie.is_some() { return if op.store_after.is_none() && op.store_before.is_none() { Self::from_op(MPTProofType::StorageDoesNotExist, op, randomness) @@ -312,13 +312,13 @@ impl MPTEntry { Self { proof_type, base: [ - Some(op.address), - None, - Some(F::from(proof_type as u64)), - None, - None, - None, - None, + Value::known(op.address), + Value::unknown(), + Value::known(F::from(proof_type as u64)), + Value::unknown(), + Value::unknown(), + Value::unknown(), + Value::unknown(), ], storage_key, new_value, @@ -326,45 +326,53 @@ impl MPTEntry { } } - pub fn from_op(proof_type: MPTProofType, op: &AccountOp, randomness: F) -> Self { + pub fn from_op(proof_type: MPTProofType, op: &AccountOp, randomness: Value) -> Self { let mut ret = Self::from_op_no_base(proof_type, op); - let (old_value_f, new_value_f) = match proof_type { + let (old_value, new_value) = match proof_type { MPTProofType::NonceChanged => ( - op.account_before - .as_ref() - .map(|acc| acc.nonce) - .unwrap_or_default(), - op.account_after - .as_ref() - .map(|acc| acc.nonce) - .unwrap_or_default(), + Value::known( + op.account_before + .as_ref() + .map(|acc| acc.nonce) + .unwrap_or_default(), + ), + Value::known( + op.account_after + .as_ref() + .map(|acc| acc.nonce) + .unwrap_or_default(), + ), ), MPTProofType::BalanceChanged => ( - op.account_before - .as_ref() - .map(|acc| acc.balance) - .unwrap_or_default(), - op.account_after - .as_ref() - .map(|acc| acc.balance) - .unwrap_or_default(), + Value::known( + op.account_before + .as_ref() + .map(|acc| acc.balance) + .unwrap_or_default(), + ), + Value::known( + op.account_after + .as_ref() + .map(|acc| acc.balance) + .unwrap_or_default(), + ), ), MPTProofType::StorageChanged | MPTProofType::CodeHashExists => ( ret.old_value.u8_rlc(randomness), ret.new_value.u8_rlc(randomness), ), - _ => (F::zero(), F::zero()), + _ => (Value::known(F::zero()), Value::known(F::zero())), }; ret.base = [ ret.base[0], - Some(ret.storage_key.u8_rlc(randomness)), + ret.storage_key.u8_rlc(randomness), ret.base[2], - Some(op.account_root()), - Some(op.account_root_before()), - Some(new_value_f), - Some(old_value_f), + Value::known(op.account_root()), + Value::known(op.account_root_before()), + new_value, + old_value, ]; ret @@ -382,12 +390,12 @@ impl MPTEntry { ret.base = [ ret.base[0], - store_key, + store_key.map_or(Value::unknown(), Value::known), ret.base[1], - Some(op.account_root()), - Some(op.account_root_before()), - Some(new_value_f), - Some(old_value_f), + Value::known(op.account_root()), + Value::known(op.account_root_before()), + Value::known(new_value_f), + Value::known(old_value_f), ]; ret @@ -535,11 +543,7 @@ impl MPTTable { )?; } - let base_entries = entry - .base - .map(|entry| entry.map(Value::known).unwrap_or_else(Value::unknown)); - - for (val, col) in base_entries.into_iter().zip([ + for (val, col) in entry.base.into_iter().zip([ config.address, config.storage_key, config.proof_type, @@ -758,16 +762,25 @@ mod test { ..Default::default() }; - let randomness = Fp::from(0x10000u64); + let randomness = Value::known(Fp::from(0x10000u64)); let entry = MPTEntry::from_op(MPTProofType::StorageChanged, &op, randomness); - let base = entry.base.map(|v| v.unwrap()); - - assert_eq!(base[0], address); - assert_eq!(base[1], store_key.u8_rlc(randomness)); - assert_eq!(base[3], op.account_root()); - assert_eq!(base[4], op.account_root_before()); - assert_eq!(base[5], store_after.u8_rlc(randomness)); - assert_eq!(base[6], store_before.u8_rlc(randomness)); + + // TODO: check Value::unknown. + entry.base[0].assert_if_known(|v| v == &address); + entry.base[1] + .zip(store_key.u8_rlc(randomness)) + .assert_if_known(|(v1, v2)| v1 == v2); + entry.base[1] + .zip(store_key.u8_rlc(randomness)) + .assert_if_known(|(v1, v2)| v1 == v2); + entry.base[3].assert_if_known(|v| v == &op.account_root()); + entry.base[4].assert_if_known(|v| v == &op.account_root_before()); + entry.base[5] + .zip(store_after.u8_rlc(randomness)) + .assert_if_known(|(v1, v2)| v1 == v2); + entry.base[6] + .zip(store_before.u8_rlc(randomness)) + .assert_if_known(|(v1, v2)| v1 == v2); } #[test] @@ -791,7 +804,7 @@ mod test { Fp::from(123456789u64), Fp::from(123456790u64), ] - .map(Some), + .map(Value::known), storage_key: Default::default(), new_value: Default::default(), old_value: Default::default(), @@ -802,15 +815,14 @@ mod test { let entry2 = MPTEntry { proof_type: MPTProofType::StorageChanged, base: [ - address, - storage_key.u8_rlc(randomness), - Fp::from(MPTProofType::StorageChanged as u64), - rand_fp(), - entry1.base[4].unwrap(), - Fp::from(10u64) + (Fp::from(3u64) * bit128), - Fp::from(1u64) + (Fp::from(3u64) * bit128), - ] - .map(Some), + Value::known(address), + storage_key.u8_rlc(Value::known(randomness)), + Value::known(Fp::from(MPTProofType::StorageChanged as u64)), + Value::known(rand_fp()), + entry1.base[4], + Value::known(Fp::from(10u64) + (Fp::from(3u64) * bit128)), + Value::known(Fp::from(1u64) + (Fp::from(3u64) * bit128)), + ], storage_key: storage_key.clone(), new_value: KeyValue::create_base((Fp::from(3u64), Fp::from(10u64))), old_value: KeyValue::create_base((Fp::from(3u64), Fp::from(1u64))), @@ -819,15 +831,14 @@ mod test { let entry3 = MPTEntry { proof_type: MPTProofType::AccountDoesNotExist, base: [ - address + Fp::one(), - Fp::zero(), - Fp::from(MPTProofType::AccountDoesNotExist as u64), - entry2.base[4].unwrap(), - entry2.base[4].unwrap(), - Fp::zero(), - Fp::zero(), - ] - .map(Some), + Value::known(address + Fp::one()), + Value::known(Fp::zero()), + Value::known(Fp::from(MPTProofType::AccountDoesNotExist as u64)), + entry2.base[4], + entry2.base[4], + Value::known(Fp::zero()), + Value::known(Fp::zero()), + ], storage_key: Default::default(), new_value: Default::default(), old_value: Default::default(), @@ -836,15 +847,14 @@ mod test { let entry4 = MPTEntry { proof_type: MPTProofType::PoseidonCodeHashExists, base: [ - address + Fp::one(), - Fp::zero(), - Fp::from(MPTProofType::PoseidonCodeHashExists as u64), - rand_fp(), - rand_fp(), - Fp::one(), - Fp::one(), - ] - .map(Some), + Value::known(address + Fp::one()), + Value::known(Fp::zero()), + Value::known(Fp::from(MPTProofType::PoseidonCodeHashExists as u64)), + Value::known(rand_fp()), + Value::known(rand_fp()), + Value::known(Fp::one()), + Value::known(Fp::one()), + ], storage_key: Default::default(), new_value: Default::default(), old_value: Default::default(), diff --git a/src/operation.rs b/src/operation.rs index fe85703c..26ef969a 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -2,7 +2,7 @@ use super::{eth, serde, HashType}; use crate::hash::Hashable; -use halo2_proofs::arithmetic::FieldExt; +use halo2_proofs::{arithmetic::FieldExt, circuit::Value}; use num_bigint::BigUint; use std::cmp::Ordering; use std::convert::TryFrom; @@ -507,23 +507,25 @@ impl KeyValue { self.data.2 } /// obtain the linear combination of two field - pub fn lc(&self, randomness: Fp) -> Fp { - self.data.0 + self.data.1 * randomness + pub fn lc(&self, randomness: Value) -> Value { + randomness.map(|rand| self.data.0 + self.data.1 * rand) } /// obtain the linear combination of the value, in byte represent, which /// is common used in zkevm circuit /// 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 { + pub fn u8_rlc(&self, randomness: Value) -> Value { let u128_hi = self.data.0.get_lower_128(); let u128_lo = self.data.1.get_lower_128(); - u128_hi - .to_be_bytes() - .into_iter() - .chain(u128_lo.to_be_bytes()) - .map(|bt| Fp::from(bt as u64)) - .reduce(|acc, f| acc * randomness + f) - .expect("not empty") + randomness.map(|rand| { + u128_hi + .to_be_bytes() + .into_iter() + .chain(u128_lo.to_be_bytes()) + .map(|bt| Fp::from(bt as u64)) + .reduce(|acc, f| acc * rand + f) + .expect("not empty") + }) } /// obtain the first limb pub fn limb_0(&self) -> Fp { From a97464b13086e940252770fbb4a4dc1d51ece603 Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 27 Apr 2023 14:10:51 +0800 Subject: [PATCH 2/3] Rename `challenges_values` to `challenges_value`. Co-authored-by: z2trillion --- src/gadgets/mpt_update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gadgets/mpt_update.rs b/src/gadgets/mpt_update.rs index ff98c26a..f032a2b9 100644 --- a/src/gadgets/mpt_update.rs +++ b/src/gadgets/mpt_update.rs @@ -656,7 +656,7 @@ mod test { challenges, ) = config; - let challenges_values = challenges.values(&layouter); + let challenges_value = challenges.values(&layouter); layouter.assign_region( || "", |mut region| { From 9bc53993fc18cb9ea6f3c6f55f5ff2988cb9ca22 Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 27 Apr 2023 14:11:02 +0800 Subject: [PATCH 3/3] Rename `challenges_values` to `challenges_value`. Co-authored-by: z2trillion --- src/gadgets/mpt_update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gadgets/mpt_update.rs b/src/gadgets/mpt_update.rs index f032a2b9..e71ccd69 100644 --- a/src/gadgets/mpt_update.rs +++ b/src/gadgets/mpt_update.rs @@ -660,7 +660,7 @@ mod test { layouter.assign_region( || "", |mut region| { - mpt_update.assign(&mut region, &self.updates, &challenges_values); + mpt_update.assign(&mut region, &self.updates, &challenge_values); poseidon.assign(&mut region, &self.hash_traces()); canonical_representation.assign(&mut region, &self.keys()); // key_bit.assign(region, &[]); // self.