diff --git a/zkevm-circuits/src/bytecode_circuit/circuit.rs b/zkevm-circuits/src/bytecode_circuit/circuit.rs index d0540d3126..5b78de6b74 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit.rs @@ -2,14 +2,18 @@ use crate::{ evm_circuit::util::{ and, constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - not, or, rlc, select, + not, or, select, }, table::{BytecodeFieldTag, BytecodeTable, KeccakTable, LookupTable}, - util::{get_push_size, Challenges, Expr, SubCircuit, SubCircuitConfig}, + util::{ + get_push_size, + word::{empty_code_hash_word_value, Word, Word32, WordExpr}, + Challenges, Expr, SubCircuit, SubCircuitConfig, + }, witness, }; use bus_mapping::state_db::EMPTY_CODE_HASH_LE; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use gadgets::is_zero::{IsZeroChip, IsZeroInstruction}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, @@ -31,7 +35,7 @@ use super::{ pub struct BytecodeCircuitRow { offset: usize, last_row_offset: usize, - code_hash: Value, + code_hash: Word>, tag: F, index: F, is_code: F, @@ -272,15 +276,13 @@ impl SubCircuitConfig for BytecodeCircuitConfig { meta.query_advice(length, Rotation::cur()), ); - let empty_hash = rlc::expr( - &EMPTY_CODE_HASH_LE.map(|v| Expression::Constant(F::from(v as u64))), - challenges.evm_word(), - ); + let empty_hash_word: Word> = + Word32::new(*EMPTY_CODE_HASH_LE).to_expr().to_word(); - cb.require_equal( + cb.require_equal_word( "assert cur.hash == EMPTY_HASH", - meta.query_advice(bytecode_table.code_hash, Rotation::cur()), - empty_hash, + bytecode_table.code_hash.query_advice(meta, Rotation::cur()), + empty_hash_word, ); cb.gate(and::expr(vec![ @@ -318,10 +320,12 @@ impl SubCircuitConfig for BytecodeCircuitConfig { 1.expr(), ); - cb.require_equal( + cb.require_equal_word( "next.hash == cur.hash", - meta.query_advice(bytecode_table.code_hash, Rotation::next()), - meta.query_advice(bytecode_table.code_hash, Rotation::cur()), + bytecode_table + .code_hash + .query_advice(meta, Rotation::next()), + bytecode_table.code_hash.query_advice(meta, Rotation::cur()), ); cb.require_equal( @@ -361,10 +365,12 @@ impl SubCircuitConfig for BytecodeCircuitConfig { meta.query_advice(bytecode_table.index, Rotation::cur()) + 1.expr(), ); - cb.require_equal( + cb.require_equal_word( "next.hash == cur.hash", - meta.query_advice(bytecode_table.code_hash, Rotation::next()), - meta.query_advice(bytecode_table.code_hash, Rotation::cur()), + bytecode_table + .code_hash + .query_advice(meta, Rotation::next()), + bytecode_table.code_hash.query_advice(meta, Rotation::cur()), ); cb.require_equal( @@ -427,7 +433,7 @@ impl SubCircuitConfig for BytecodeCircuitConfig { ])) }); meta.lookup_any( - "keccak256_table_lookup(cur.value_rlc, cur.length, cur.hash)", + "keccak256_table_lookup(cur.value_rlc, cur.length, cur.hash_word)", |meta| { let enable = and::expr(vec![ meta.query_fixed(q_enable, Rotation::cur()), @@ -515,7 +521,7 @@ impl BytecodeCircuitConfig { // Padding for idx in offset..=last_row_offset { - self.set_padding_row(&mut region, challenges, idx, last_row_offset)?; + self.set_padding_row(&mut region, idx, last_row_offset)?; } // Overwrite the witness assignment by using the values in the `overwrite` @@ -548,20 +554,12 @@ impl BytecodeCircuitConfig { value_rlc = challenges.keccak_input().map(|_| F::ZERO); } - let code_hash = challenges - .evm_word() - .map(|challenge| rlc::value(&row.code_hash.to_le_bytes(), challenge)); - for (name, column, value) in [ - ("code_hash", self.bytecode_table.code_hash, code_hash), - ("value_rlc", self.value_rlc, value_rlc), - ] { - region.assign_advice( - || format!("assign {} {}", name, offset), - column, - offset, - || value, - )?; - } + region.assign_advice( + || format!("assign value_rlc {}", offset), + self.value_rlc, + offset, + || value_rlc, + )?; } Ok(()) }, @@ -584,11 +582,7 @@ impl BytecodeCircuitConfig { let mut value_rlc = challenges.keccak_input().map(|_| F::ZERO); let length = F::from(bytecode.bytes.len() as u64); - // Code hash with challenge is calculated only using the first row of the - // bytecode (header row), the rest of the code_hash in other rows are ignored. - let code_hash = challenges - .evm_word() - .map(|challenge| rlc::value(&bytecode.rows[0].code_hash.to_le_bytes(), challenge)); + let code_hash = Word::from(bytecode.rows[0].code_hash).into_value(); for (idx, row) in bytecode.rows.iter().enumerate() { if fail_fast && *offset > last_row_offset { @@ -642,7 +636,7 @@ impl BytecodeCircuitConfig { push_data_left = next_push_data_left } if *offset == last_row_offset { - self.set_padding_row(region, challenges, *offset, last_row_offset)?; + self.set_padding_row(region, *offset, last_row_offset)?; } } @@ -652,20 +646,15 @@ impl BytecodeCircuitConfig { fn set_padding_row( &self, region: &mut Region<'_, F>, - challenges: &Challenges>, offset: usize, last_row_offset: usize, ) -> Result<(), Error> { - let empty_hash = challenges - .evm_word() - .map(|challenge| rlc::value(EMPTY_CODE_HASH_LE.as_ref(), challenge)); - self.set_row( region, BytecodeCircuitRow { offset, last_row_offset, - code_hash: empty_hash, + code_hash: empty_code_hash_word_value(), tag: F::from(BytecodeFieldTag::Header as u64), value_rlc: Value::known(F::ZERO), ..Default::default() @@ -720,17 +709,20 @@ impl BytecodeCircuitConfig { || Value::known(value), )?; } - for (name, column, value) in [ - ("code_hash", self.bytecode_table.code_hash, row.code_hash), - ("value_rlc", self.value_rlc, row.value_rlc), - ] { - region.assign_advice( - || format!("assign {} {}", name, offset), - column, - offset, - || value, - )?; - } + + region.assign_advice( + || format!("assign value_rlc {}", offset), + self.value_rlc, + offset, + || row.value_rlc, + )?; + + row.code_hash.assign_advice( + region, + || format!("assign code_hash {}", offset), + self.bytecode_table.code_hash, + offset, + )?; self.push_data_left_is_zero.assign( region, diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index fa8f5ab10a..d452b98654 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -4,7 +4,11 @@ use crate::{ copy_circuit::util::number_or_hash_to_word, evm_circuit::util::rlc, impl_expr, - util::{build_tx_log_address, keccak, word, Challenges}, + util::{ + build_tx_log_address, keccak, + word::{self, Word}, + Challenges, + }, witness::{ Block, BlockContext, Bytecode, MptUpdateRow, MptUpdates, Rw, RwMap, RwRow, Transaction, }, diff --git a/zkevm-circuits/src/table/bytecode_table.rs b/zkevm-circuits/src/table/bytecode_table.rs index 2132106fa8..d96443deb7 100644 --- a/zkevm-circuits/src/table/bytecode_table.rs +++ b/zkevm-circuits/src/table/bytecode_table.rs @@ -14,10 +14,7 @@ impl_expr!(BytecodeFieldTag); #[derive(Clone, Debug)] pub struct BytecodeTable { /// Code Hash - pub code_hash_word: word::Word>, - #[deprecated] - /// Code Hash - pub code_hash: Column, + pub code_hash: word::Word>, /// Tag pub tag: Column, /// Index @@ -32,10 +29,8 @@ impl BytecodeTable { /// Construct a new BytecodeTable pub fn construct(meta: &mut ConstraintSystem) -> Self { let [tag, index, is_code, value] = array::from_fn(|_| meta.advice_column()); - let code_hash_word = word::Word::new([meta.advice_column(), meta.advice_column()]); - let code_hash = meta.advice_column(); + let code_hash = word::Word::new([meta.advice_column(), meta.advice_column()]); Self { - code_hash_word, code_hash, tag, index, @@ -89,8 +84,8 @@ impl BytecodeTable { impl LookupTable for BytecodeTable { fn columns(&self) -> Vec> { vec![ - self.code_hash_word.lo().into(), - self.code_hash_word.hi().into(), + self.code_hash.lo().into(), + self.code_hash.hi().into(), self.tag.into(), self.index.into(), self.is_code.into(), diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index e9c070df43..670aaba375 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -134,12 +134,13 @@ impl KeccakTable { &self, value_rlc: Column, length: Column, - code_hash: Column, + code_hash: Word>, ) -> Vec<(Column, Column)> { vec![ (value_rlc, self.input_rlc), (length, self.input_len), - (code_hash, self.output_rlc), + (code_hash.lo(), self.output.lo()), + (code_hash.hi(), self.output.hi()), ] } } diff --git a/zkevm-circuits/src/util/word.rs b/zkevm-circuits/src/util/word.rs index 4582640586..9c0075ade8 100644 --- a/zkevm-circuits/src/util/word.rs +++ b/zkevm-circuits/src/util/word.rs @@ -3,11 +3,13 @@ // - Limbs: An EVN word is 256 bits. Limbs N means split 256 into N limb. For example, N = 4, each // limb is 256/4 = 64 bits +use bus_mapping::state_db::CodeDB; use eth_types::{Field, ToLittleEndian, H160, H256}; use gadgets::util::{not, or, Expr}; use halo2_proofs::{ circuit::{AssignedCell, Region, Value}, - plonk::{Advice, Column, Error, Expression}, + plonk::{Advice, Column, Error, Expression, VirtualCells}, + poly::Rotation, }; use itertools::Itertools; @@ -46,6 +48,24 @@ impl WordLimbs { } } +impl WordLimbs, N> { + /// Query advice of WordLibs of columns advice + pub fn query_advice( + &self, + meta: &mut VirtualCells, + at: Rotation, + ) -> WordLimbs, N> { + WordLimbs::new(self.limbs.map(|column| meta.query_advice(column, at))) + } +} + +impl WordLimbs { + /// Convert WordLimbs of u8 to WordLimbs of expressions + pub fn to_expr(&self) -> WordLimbs, N> { + WordLimbs::new(self.limbs.map(|v| Expression::Constant(F::from(v as u64)))) + } +} + impl Default for WordLimbs { fn default() -> Self { Self { @@ -363,6 +383,17 @@ impl Word> { } } +impl Word> { + /// Query advice of Word of columns advice + pub fn query_advice( + &self, + meta: &mut VirtualCells, + at: Rotation, + ) -> Word> { + self.0.query_advice(meta, at).to_word() + } +} + impl WordExpr for Word> { fn to_word(&self) -> Word> { self.word_expr().to_word() @@ -428,7 +459,7 @@ impl WordExpr for Word> { impl WordLimbs, N1> { /// to_wordlimbs will aggregate nested expressions, which implies during expression evaluation - /// it need more recursive call. if the converted limbs word will be used in many place, + /// it need more recursive call. if the converted limbs word will be used in many places, /// consider create new low limbs word, have equality constrain, then finally use low limbs /// elsewhere. // TODO static assertion. wordaround https://github.com/nvzqz/static-assertions-rs/issues/40 @@ -481,4 +512,9 @@ impl From> for Word32Cell { } } +/// Return the hash of the empty code as a Word> in little-endian. +pub fn empty_code_hash_word_value() -> Word> { + Word::from(CodeDB::empty_code_hash()).into_value() +} + // TODO unittest