diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 6fc89e48a3..c4ba3743c8 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1328,10 +1328,10 @@ impl ExecutionConfig { } } - let rlc_assignments: BTreeSet<_> = block - .rws - .table_assignments() + let rlc_assignments: BTreeSet<_> = step + .rw_indices .iter() + .map(|rw_idx| block.rws[*rw_idx]) .map(|rw| { rw.table_assignment_aux(evm_randomness) .rlc(lookup_randomness) diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 3b93d4bb1e..a6a025da60 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -237,11 +237,13 @@ pub fn block_convert( block: &circuit_input_builder::Block, code_db: &bus_mapping::state_db::CodeDB, ) -> Result, Error> { + let rws = RwMap::from(&block.container); + rws.check_value(); Ok(Block { // randomness: F::from(0x100), // Special value to reveal elements after RLC randomness: F::from(0xcafeu64), context: block.into(), - rws: RwMap::from(&block.container), + rws, txs: block .txs() .iter() diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index cda056d1ab..b64fe83cea 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use bus_mapping::operation::{self, AccountField, CallContextField, TxLogField, TxReceiptField}; use eth_types::{Address, Field, ToAddress, ToLittleEndian, ToScalar, Word, U256}; use halo2_proofs::circuit::Value; +use halo2_proofs::halo2curves::bn256::Fr; use itertools::Itertools; use crate::evm_circuit::util::rlc; @@ -12,6 +13,8 @@ use crate::table::{ }; use crate::util::build_tx_log_address; +use super::MptUpdates; + /// Rw constainer for a witness block #[derive(Debug, Default, Clone)] pub struct RwMap(pub HashMap>); @@ -38,6 +41,63 @@ impl RwMap { debug_assert_eq!(idx, rw_counter - 1); } } + /// Check value in the same way like StateCircuit + pub fn check_value(&self) { + let mock_rand = Fr::from(0x1000u64); + let err_msg_first = "first access reads don't change value"; + let err_msg_non_first = "non-first access reads don't change value"; + let rows = self.table_assignments(); + let updates = MptUpdates::mock_from(&rows); + let mut errs = Vec::new(); + for idx in 1..rows.len() { + let row = &rows[idx]; + let prev_row = &rows[idx - 1]; + let is_first = { + let key = |row: &Rw| { + ( + row.tag() as u64, + row.id().unwrap_or_default(), + row.address().unwrap_or_default(), + row.field_tag().unwrap_or_default(), + row.storage_key().unwrap_or_default(), + ) + }; + key(prev_row) != key(row) + }; + if !row.is_write() { + let value = row.value_assignment::(mock_rand); + if is_first { + // value == init_value + let init_value = updates + .get(row) + .map(|u| u.value_assignments(mock_rand).1) + .unwrap_or_default(); + if value != init_value { + errs.push((idx, err_msg_first, *row, *prev_row)); + } + } else { + // value == prev_value + let prev_value = prev_row.value_assignment::(mock_rand); + + if value != prev_value { + errs.push((idx, err_msg_non_first, *row, *prev_row)); + } + } + } + } + if !errs.is_empty() { + log::error!("after rw value check, err num: {}", errs.len()); + for (idx, err_msg, row, prev_row) in errs { + log::error!( + "err: rw idx: {}, reason: \"{}\", row: {:?}, prev_row: {:?}", + idx, + err_msg, + row, + prev_row + ); + } + } + } /// Calculates the number of Rw::Start rows needed. /// `target_len` is allowed to be 0 as an "auto" mode, /// then only 1 Rw::Start row will be prepadded.