From 89c3bbb93e2c86a2821680758044fe570a25f3d5 Mon Sep 17 00:00:00 2001 From: miha-stopar Date: Wed, 10 Apr 2024 12:11:37 +0200 Subject: [PATCH] Wrong extension node: selector constraints; wrong extension node gadget added to storage leaf --- .../gethutil/mpt/witness/prepare_witness.go | 8 +- zkevm-circuits/src/mpt_circuit.rs | 3 +- .../src/mpt_circuit/account_leaf.rs | 45 +++++----- zkevm-circuits/src/mpt_circuit/helpers.rs | 8 +- .../src/mpt_circuit/storage_leaf.rs | 87 +++++++++++++++---- 5 files changed, 98 insertions(+), 53 deletions(-) diff --git a/geth-utils/gethutil/mpt/witness/prepare_witness.go b/geth-utils/gethutil/mpt/witness/prepare_witness.go index 4ddc43e756..83474eb4fe 100644 --- a/geth-utils/gethutil/mpt/witness/prepare_witness.go +++ b/geth-utils/gethutil/mpt/witness/prepare_witness.go @@ -533,29 +533,23 @@ func convertProofToWitness(statedb *state.StateDB, addr common.Address, addrh [] key[keyIndex], key[keyIndex], false, false, isExtension, true) nodes = append(nodes, bNode) - // Let's construct the leaf L1 that will have the correct key (the queried one) if isAccountProof { dummyLeaf := []byte{248,108,157,52,45,53,199,120,18,165,14,109,22,4,141,198,233,128,219,44,247,218,241,231,2,206,125,246,58,246,15,3,184,76,248,74,4,134,85,156,208,108,8,0,160,86,232,31,23,27,204,85,166,255,131,69,230,146,192,248,110,91,72,224,27,153,108,173,192,1,98,47,181,227,99,180,33,160,197,210,70,1,134,247,35,60,146,126,125,178,220,199,3,192,229,0,182,83,202,130,39,59,123,250,216,4,93,133,164,112} node := prepareAccountLeafNode(addr, addrh, dummyLeaf, dummyLeaf, dummyLeaf, nil, addr_nibbles, false, false, false) - node = equipLeafWithWrongExtension(node, keyMiddle, keyAfter, nibblesMiddle, nibblesAfter) - nodes = append(nodes, node) } else { - // The remaining `key` nibbles are to be stored in the constructed leaf - in our example [1 2 4 ...] compact := trie.HexToCompact(key[start:]) // Add RLP: compactLen := byte(len(compact)) rlp2 := 128 + compactLen rlp1 := 192 + compactLen + 1 - // Constructed leaf L1: dummyLeaf := append([]byte{rlp1, rlp2}, compact...) - // Add dummy value: dummyLeaf = append(dummyLeaf, 0) - node := prepareStorageLeafNode(dummyLeaf, dummyLeaf, dummyLeaf, nil, storage_key, key, nonExistingStorageProof, false, false, false, false) + node = equipLeafWithWrongExtension(node, keyMiddle, keyAfter, nibblesMiddle, nibblesAfter) nodes = append(nodes, node) } } diff --git a/zkevm-circuits/src/mpt_circuit.rs b/zkevm-circuits/src/mpt_circuit.rs index 73586f5ede..f6472d6ce3 100644 --- a/zkevm-circuits/src/mpt_circuit.rs +++ b/zkevm-circuits/src/mpt_circuit.rs @@ -253,7 +253,7 @@ impl MPTConfig { let mut state_cm = CellManager::new(50, 0); state_cm.add_columns(meta, &mut cb.base, MptCellType::StoragePhase1, 0, false, 20); - state_cm.add_columns(meta, &mut cb.base, MptCellType::StoragePhase2, 1, false, 7); + state_cm.add_columns(meta, &mut cb.base, MptCellType::StoragePhase2, 1, false, 8); state_cm.add_columns(meta, &mut cb.base, MptCellType::StoragePhase3, 2, false, 5); state_cm.add_columns(meta, &mut cb.base, lu(MptTableType::Byte), 0, false, 4); state_cm.add_columns(meta, &mut cb.base, lu(MptTableType::Fixed), 2, false, 3); @@ -771,7 +771,6 @@ mod tests { #[test] fn test_mpt() { let path = "src/mpt_circuit/tests"; - let files = fs::read_dir(path).unwrap(); files .filter_map(Result::ok) diff --git a/zkevm-circuits/src/mpt_circuit/account_leaf.rs b/zkevm-circuits/src/mpt_circuit/account_leaf.rs index 4b518911b5..c5fec451a2 100644 --- a/zkevm-circuits/src/mpt_circuit/account_leaf.rs +++ b/zkevm-circuits/src/mpt_circuit/account_leaf.rs @@ -287,7 +287,6 @@ impl AccountLeafConfig { // the leaf too - in this case `parent_data.hash` contains the hash of the node above the placeholder // branch. ifx! {not!(or::expr(&[config.is_placeholder_leaf[is_s.idx()].expr(), is_wrong_ext_case.clone()])) => { - // ifx! {not!(config.is_placeholder_leaf[is_s.idx()]) => { let hash = parent_data[is_s.idx()].hash.expr(); require!((1.expr(), leaf_rlc, rlp_key.rlp_list.num_bytes(), hash.lo(), hash.hi()) =>> @KECCAK); } elsex { @@ -374,18 +373,30 @@ impl AccountLeafConfig { &config.is_mod_extension, &cb.key_r.expr(), ); + + let is_wrong_leaf_case = and::expr(&[config.is_non_existing_account_proof.expr(), not!(config.parent_data[1].is_extension), not!(config.is_placeholder_leaf[1].expr())]); + + // When non-existing-proof, it needs to be one of the following cases: + // (1) wrong leaf, (2) wrong extension node, (3) nil leaf - we need to check the sum of these + // three cases is 1. + ifx! {config.is_non_existing_account_proof => { + require!(is_wrong_ext_case.clone() + is_wrong_leaf_case.clone() + config.is_placeholder_leaf[1].expr() => 1.expr()); + }} + + // When is_last_level_and_wrong_ext_case, the proof type needs to be non-existing + ifx! {is_wrong_ext_case => { + require!(config.is_non_existing_account_proof.expr() => 1.expr()); + }} // Wrong leaf handling config.wrong_leaf = WrongLeafGadget::construct( cb, key_item.hash_rlc(), - config.is_non_existing_account_proof.expr(), - &config.rlp_key[true.idx()].key_value, - &key_rlc[true.idx()], + is_wrong_leaf_case, + &config.rlp_key[1].key_value, // C proof is used for non-existing proof + &key_rlc[1], &wrong_bytes, - config.is_placeholder_leaf[true.idx()].expr(), - config.parent_data[true.idx()].is_extension.expr(), - config.key_data[true.idx()].clone(), + config.key_data[1].clone(), &cb.key_r.expr(), ); @@ -403,20 +414,6 @@ impl AccountLeafConfig { // in the case of wrong extension node. // All other extension_branches (above it) need to have it `false` (constraint in // extension_branch.rs) - // TODO: Use is_last_level_and_wrong_ext_case as a flag for one of the three cases - // require!(config.parent_data[1].is_last_level_and_wrong_ext_case.expr() => true.expr()); - // TODO: use C proof everywhere for non-existing proof. - - // TODO: when non-existing-proof, it needs to be one of the following cases: - // wrong leaf, wrong extension node, nil leaf - we need to check the sum of these - // three cases is 1. - // To check whether it's the wrong leaf case - parent is not extension, leaf is not placeholder - // To check whether it's wrong extension node - is_last_level_and_wrong_ext_case = 1, parent is extension - // To check whether it's the nil leaf - use IsPlaceholderLeafGadget - - // TODO: when is_last_level_and_wrong_ext_case, the proof type needs to be non-existing - // config.is_non_existing_account_proof.expr(), - // config.parent_data[true.idx()].is_extension.expr(), config.wrong_ext_node = WrongExtNodeGadget::construct( cb, @@ -426,7 +423,7 @@ impl AccountLeafConfig { &wrong_ext_middle_nibbles, &wrong_ext_after, &wrong_ext_after_nibbles, - config.key_data[true.idx()].clone(), + config.key_data[1].clone(), // C proof is used for non-existing proof config.key_data_prev.clone(), ); @@ -766,7 +763,7 @@ impl AccountLeafConfig { &account.wrong_rlp_bytes, &expected_item, true, - key_data[true.idx()].clone(), + key_data[1].clone(), region.key_r, )?; @@ -777,7 +774,7 @@ impl AccountLeafConfig { offset, wrong_ext_middle, wrong_ext_middle_nibbles, - key_data[true.idx()].clone(), + key_data[1].clone(), key_data_prev.clone(), ); diff --git a/zkevm-circuits/src/mpt_circuit/helpers.rs b/zkevm-circuits/src/mpt_circuit/helpers.rs index d8abcae403..6ffa1c7c14 100644 --- a/zkevm-circuits/src/mpt_circuit/helpers.rs +++ b/zkevm-circuits/src/mpt_circuit/helpers.rs @@ -1241,18 +1241,16 @@ impl WrongLeafGadget { pub(crate) fn construct( cb: &mut MPTConstraintBuilder, expected_key: Expression, - is_non_existing: Expression, + is_wrong_leaf_case: Expression, key_value: &RLPItemView, key_rlc: &Expression, expected_item: &RLPItemView, - is_placeholder: Expression, - is_parent_extension: Expression, key_data: KeyData, r: &Expression, ) -> Self { let mut config = WrongLeafGadget::default(); circuit!([meta, cb.base], { - ifx! {and::expr(&[is_non_existing, not!(is_placeholder), not!(is_parent_extension)]) => { + ifx! {is_wrong_leaf_case => { config.wrong_rlp_key = ListKeyGadget::construct(cb, expected_item); let key_rlc_wrong = key_data.rlc.expr() + config.wrong_rlp_key.key.expr( @@ -1341,7 +1339,7 @@ impl WrongExtNodeGadget { circuit!([meta, cb.base], { ifx! {is_wrong_ext_case => { // We have a key split into three parts, - // meaning that there the first part parity doesn't + // meaning that the first part parity doesn't // tell us about the parity of the second part (depends on the third part as well). let data0 = [wrong_ext_middle.clone(), wrong_ext_middle_nibbles.clone()]; diff --git a/zkevm-circuits/src/mpt_circuit/storage_leaf.rs b/zkevm-circuits/src/mpt_circuit/storage_leaf.rs index 852d962ef8..11a2dd2808 100644 --- a/zkevm-circuits/src/mpt_circuit/storage_leaf.rs +++ b/zkevm-circuits/src/mpt_circuit/storage_leaf.rs @@ -16,9 +16,7 @@ use crate::{ }, mpt_circuit::{ helpers::{ - key_memory, main_memory, num_nibbles, parent_memory, DriftedGadget, - IsPlaceholderLeafGadget, KeyData, MPTConstraintBuilder, MainData, ParentData, - ParentDataWitness, KECCAK, + key_memory, main_memory, num_nibbles, parent_memory, DriftedGadget, IsPlaceholderLeafGadget, KeyData, MPTConstraintBuilder, MainData, ParentData, ParentDataWitness, WrongExtNodeGadget, KECCAK }, param::{EMPTY_TRIE_HASH, KEY_LEN_IN_NIBBLES}, MPTConfig, MPTContext, MptMemory, RlpItemType, @@ -49,6 +47,7 @@ pub(crate) struct StorageLeafConfig { is_placeholder_leaf: [IsPlaceholderLeafGadget; 2], drifted: DriftedGadget, wrong_leaf: WrongLeafGadget, + wrong_ext_node: WrongExtNodeGadget, is_storage_mod_proof: IsEqualGadget, is_non_existing_storage_proof: IsEqualGadget, is_mod_extension: [Cell; 2], @@ -131,6 +130,8 @@ impl StorageLeafConfig { MPTProofType::StorageDoesNotExist.expr(), ); + let is_wrong_ext_case = parent_data[1].is_last_level_and_wrong_ext_case.expr(); + for is_s in [true, false] { ifx! {not!(config.is_mod_extension[is_s.idx()].expr()) => { // Placeholder leaf checks @@ -190,7 +191,9 @@ impl StorageLeafConfig { // Total number of nibbles needs to be KEY_LEN_IN_NIBBLES let num_nibbles = num_nibbles::expr(rlp_key.key_value.len(), key_data[is_s.idx()].is_odd.expr()); - require!(key_data[is_s.idx()].num_nibbles.expr() + num_nibbles => KEY_LEN_IN_NIBBLES); + ifx! {not!(is_wrong_ext_case) => { + require!(key_data[is_s.idx()].num_nibbles.expr() + num_nibbles => KEY_LEN_IN_NIBBLES); + }} // Placeholder leaves default to value `0`. ifx! {is_placeholder_leaf => { @@ -199,14 +202,16 @@ impl StorageLeafConfig { // Make sure the RLP encoding is correct. // storage = [key, "value"] - require!(rlp_key.rlp_list.len() => key_items[is_s.idx()].num_bytes() + config.rlp_value[is_s.idx()].num_bytes()); + ifx! {not!(is_wrong_ext_case) => { + require!(rlp_key.rlp_list.len() => key_items[is_s.idx()].num_bytes() + config.rlp_value[is_s.idx()].num_bytes()); + }} // Check if the leaf is in its parent. // Check is skipped for placeholder leaves which are dummy leaves. // Note that the constraint works for the case when there is the placeholder branch above // the leaf too - in this case `parent_data.hash` contains the hash of the node above the placeholder // branch. - ifx! {not!(is_placeholder_leaf) => { + ifx! {not!(or::expr(&[config.is_placeholder_leaf[is_s.idx()].expr(), is_wrong_ext_case.clone()])) => { config.is_not_hashed[is_s.idx()] = LtGadget::construct(&mut cb.base, rlp_key.rlp_list.num_bytes(), 32.expr()); ifx!{or::expr(&[parent_data[is_s.idx()].is_root.expr(), not!(config.is_not_hashed[is_s.idx()])]) => { // Hashed leaf in parent branch @@ -236,7 +241,9 @@ impl StorageLeafConfig { // Note that this does not hold when there is NonExistingStorageProof wrong leaf scenario, // in this case there is a non-nil leaf. However, in this case the leaf is not a placeholder, // so the check below is not triggered. - require!(parent_data[is_s.idx()].rlc.expr() => 128.expr()); + ifx! {not!(is_wrong_ext_case) => { + require!(parent_data[is_s.idx()].rlc.expr() => 128.expr()); + }} }} }} @@ -287,20 +294,59 @@ impl StorageLeafConfig { &cb.key_r.expr(), ); - // Wrong leaf / extension node handling + let is_wrong_leaf_case = and::expr(&[config.is_non_existing_storage_proof.expr(), not!(config.parent_data[1].is_extension), not!(config.is_placeholder_leaf[1].expr())]); + + // When non-existing-proof, it needs to be one of the following cases: + // (1) wrong leaf, (2) wrong extension node, (3) nil leaf - we need to check the sum of these + // three cases is 1. + ifx! {config.is_non_existing_storage_proof => { + require!(is_wrong_ext_case.clone() + is_wrong_leaf_case.clone() + config.is_placeholder_leaf[1].expr() => 1.expr()); + }} + + // When is_last_level_and_wrong_ext_case, the proof type needs to be non-existing + ifx! {is_wrong_ext_case => { + require!(config.is_non_existing_storage_proof.expr() => 1.expr()); + }} + + // Wrong leaf handling config.wrong_leaf = WrongLeafGadget::construct( cb, key_item.hash_rlc(), - config.is_non_existing_storage_proof.expr(), - &config.rlp_key[true.idx()].key_value, - &key_rlc[true.idx()], + is_wrong_leaf_case, + &config.rlp_key[1].key_value, // C proof is used for non-existing proof + &key_rlc[1], &expected_item, - config.is_placeholder_leaf[true.idx()].expr(), - config.parent_data[true.idx()].is_extension.expr(), - config.key_data[true.idx()].clone(), + config.key_data[1].clone(), &cb.key_r.expr(), ); + // Wrong extension node handling + let wrong_ext_middle = + ctx.rlp_item(meta, cb, StorageRowType::LongExtNodeKey as usize, RlpItemType::Key); + let wrong_ext_middle_nibbles = + ctx.rlp_item(meta, cb, StorageRowType::LongExtNodeNibbles as usize, RlpItemType::Nibbles); + let wrong_ext_after = + ctx.rlp_item(meta, cb, StorageRowType::ShortExtNodeKey as usize, RlpItemType::Key); + let wrong_ext_after_nibbles = + ctx.rlp_item(meta, cb, StorageRowType::ShortExtNodeNibbles as usize, RlpItemType::Nibbles); + + // The extension_branch in the last level needs has `is_last_level_and_wrong_ext_case = true` + // in the case of wrong extension node. + // All other extension_branches (above it) need to have it `false` (constraint in + // extension_branch.rs) + + config.wrong_ext_node = WrongExtNodeGadget::construct( + cb, + key_item.hash_rlc(), + is_wrong_ext_case, + &wrong_ext_middle, + &wrong_ext_middle_nibbles, + &wrong_ext_after, + &wrong_ext_after_nibbles, + config.key_data[1].clone(), // C proof should be used everywhere for non-existing proof + config.key_data_prev.clone(), + ); + // Reset the main memory // This need to be the last node for this proof MainData::store( @@ -573,10 +619,21 @@ impl StorageLeafConfig { &storage.wrong_rlp_bytes, &expected_item, false, - key_data[true.idx()].clone(), + key_data[1].clone(), region.key_r, )?; + let wrong_ext_middle = rlp_values[StorageRowType::LongExtNodeKey as usize].clone(); + let wrong_ext_middle_nibbles = rlp_values[StorageRowType::LongExtNodeNibbles as usize].clone(); + self.wrong_ext_node.assign( + region, + offset, + wrong_ext_middle, + wrong_ext_middle_nibbles, + key_data[1].clone(), + key_data_prev.clone(), + ); + // Reset the main memory MainData::witness_store( region,