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 5ed86698..03dd4711 100644 --- a/src/gadgets/mpt_update.rs +++ b/src/gadgets/mpt_update.rs @@ -12,6 +12,7 @@ use super::{ RANDOMNESS, }; use crate::{ + challenges::Challenges, constraint_builder::{AdviceColumn, ConstraintBuilder, Query}, types::{ account_key, hash, @@ -28,7 +29,7 @@ use ethers_core::{ }; use halo2_proofs::{ arithmetic::{Field, FieldExt}, - circuit::Region, + circuit::{Region, Value}, halo2curves::bn256::Fr, plonk::ConstraintSystem, }; @@ -133,6 +134,7 @@ impl MptUpdateConfig { fn configure( cs: &mut ConstraintSystem, cb: &mut ConstraintBuilder, + challenges: &Challenges>, poseidon: &impl PoseidonLookup, key_bit: &impl KeyBitLookup, rlc: &impl RlcLookup, @@ -299,8 +301,6 @@ impl MptUpdateConfig { } fn assign(&self, region: &mut Region<'_, Fr>, proofs: &[Proof]) { - let randomness = Fr::from(RANDOMNESS); - let mut offset = 0; for proof in proofs { let proof_type = MPTProofType::from(proof.claim); @@ -1743,6 +1743,7 @@ mod test { KeyBitConfig, ByteBitGadget, ByteRepresentationConfig, + Challenges, ); type FloorPlanner = SimpleFloorPlanner; @@ -1751,9 +1752,9 @@ mod test { } fn configure(cs: &mut ConstraintSystem) -> Self::Config { + let challenges = Challenges::construct(cs); let selector = SelectorColumn(cs.fixed_column()); let mut cb = ConstraintBuilder::new(selector); - let poseidon = PoseidonTable::configure(cs, &mut cb, 4096); let byte_bit = ByteBitGadget::configure(cs, &mut cb); let byte_representation = ByteRepresentationConfig::configure(cs, &mut cb, &byte_bit); @@ -1768,9 +1769,13 @@ mod test { &byte_bit, ); + 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, @@ -1786,6 +1791,7 @@ mod test { key_bit, byte_bit, byte_representation, + challenges, ) } @@ -1802,8 +1808,14 @@ mod test { key_bit, byte_bit, byte_representation, + challenges, ) = config; + let challenges_value = challenges.values(&layouter); + layouter.assign_region( + || "", + |mut region| { + mpt_update.assign(&mut region, &self.updates, &challenge_values); let (u64s, u128s, frs) = self.byte_representations(); layouter.assign_region( diff --git a/src/lib.rs b/src/lib.rs index 2e226ae2..8d390c13 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; @@ -23,7 +24,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}; @@ -153,7 +156,7 @@ impl SimpleTrie { } impl Circuit for SimpleTrie { - type Config = SimpleTrieConfig; + type Config = (SimpleTrieConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -184,20 +187,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( @@ -412,10 +419,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(); @@ -442,14 +449,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) } @@ -839,7 +846,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(); @@ -854,7 +861,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 @@ -875,10 +882,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 { @@ -890,19 +895,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( @@ -914,9 +924,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 b1039f30..d8377baa 100644 --- a/src/mpt_table.rs +++ b/src/mpt_table.rs @@ -269,7 +269,7 @@ impl From for 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, @@ -278,7 +278,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) @@ -328,13 +328,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, @@ -342,45 +342,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 @@ -398,12 +406,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 @@ -551,11 +559,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, @@ -774,16 +778,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] @@ -807,7 +820,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(), @@ -818,15 +831,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))), @@ -835,15 +847,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(), @@ -852,15 +863,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 {