Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 48 additions & 56 deletions zkevm-circuits/src/bytecode_circuit/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -31,7 +35,7 @@ use super::{
pub struct BytecodeCircuitRow<F: Field> {
offset: usize,
last_row_offset: usize,
code_hash: Value<F>,
code_hash: Word<Value<F>>,
tag: F,
index: F,
is_code: F,
Expand Down Expand Up @@ -272,15 +276,13 @@ impl<F: Field> SubCircuitConfig<F> for BytecodeCircuitConfig<F> {
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<Expression<F>> =
Word32::new(*EMPTY_CODE_HASH_LE).to_expr().to_word();

cb.require_equal(
cb.require_equal_word(
Comment thread
leolara marked this conversation as resolved.
"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![
Expand Down Expand Up @@ -318,10 +320,12 @@ impl<F: Field> SubCircuitConfig<F> for BytecodeCircuitConfig<F> {
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(
Expand Down Expand Up @@ -361,10 +365,12 @@ impl<F: Field> SubCircuitConfig<F> for BytecodeCircuitConfig<F> {
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(
Expand Down Expand Up @@ -427,7 +433,7 @@ impl<F: Field> SubCircuitConfig<F> for BytecodeCircuitConfig<F> {
]))
});
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()),
Expand Down Expand Up @@ -515,7 +521,7 @@ impl<F: Field> BytecodeCircuitConfig<F> {

// 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`
Expand Down Expand Up @@ -548,20 +554,12 @@ impl<F: Field> BytecodeCircuitConfig<F> {
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(())
},
Expand All @@ -584,11 +582,7 @@ impl<F: Field> BytecodeCircuitConfig<F> {
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 {
Expand Down Expand Up @@ -642,7 +636,7 @@ impl<F: Field> BytecodeCircuitConfig<F> {
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)?;
}
}

Expand All @@ -652,20 +646,15 @@ impl<F: Field> BytecodeCircuitConfig<F> {
fn set_padding_row(
&self,
region: &mut Region<'_, F>,
challenges: &Challenges<Value<F>>,
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(),
Comment thread
leolara marked this conversation as resolved.
tag: F::from(BytecodeFieldTag::Header as u64),
value_rlc: Value::known(F::ZERO),
..Default::default()
Expand Down Expand Up @@ -720,17 +709,20 @@ impl<F: Field> BytecodeCircuitConfig<F> {
|| 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,
Expand Down
6 changes: 5 additions & 1 deletion zkevm-circuits/src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
13 changes: 4 additions & 9 deletions zkevm-circuits/src/table/bytecode_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ impl_expr!(BytecodeFieldTag);
#[derive(Clone, Debug)]
pub struct BytecodeTable {
/// Code Hash
pub code_hash_word: word::Word<Column<Advice>>,
#[deprecated]
/// Code Hash
pub code_hash: Column<Advice>,
pub code_hash: word::Word<Column<Advice>>,
/// Tag
pub tag: Column<Advice>,
/// Index
Expand All @@ -32,10 +29,8 @@ impl BytecodeTable {
/// Construct a new BytecodeTable
pub fn construct<F: Field>(meta: &mut ConstraintSystem<F>) -> 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,
Expand Down Expand Up @@ -89,8 +84,8 @@ impl BytecodeTable {
impl<F: Field> LookupTable<F> for BytecodeTable {
fn columns(&self) -> Vec<Column<Any>> {
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(),
Expand Down
5 changes: 3 additions & 2 deletions zkevm-circuits/src/table/keccak_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,13 @@ impl KeccakTable {
&self,
value_rlc: Column<Advice>,
length: Column<Advice>,
code_hash: Column<Advice>,
code_hash: Word<Column<Advice>>,
) -> Vec<(Column<Advice>, Column<Advice>)> {
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()),
]
}
}
40 changes: 38 additions & 2 deletions zkevm-circuits/src/util/word.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -46,6 +48,24 @@ impl<T, const N: usize> WordLimbs<T, N> {
}
}

impl<const N: usize> WordLimbs<Column<Advice>, N> {
/// Query advice of WordLibs of columns advice
pub fn query_advice<F: Field>(
&self,
meta: &mut VirtualCells<F>,
at: Rotation,
) -> WordLimbs<Expression<F>, N> {
WordLimbs::new(self.limbs.map(|column| meta.query_advice(column, at)))
}
}

impl<const N: usize> WordLimbs<u8, N> {
/// Convert WordLimbs of u8 to WordLimbs of expressions
pub fn to_expr<F: Field>(&self) -> WordLimbs<Expression<F>, N> {
WordLimbs::new(self.limbs.map(|v| Expression::Constant(F::from(v as u64))))
}
}

impl<T: Default, const N: usize> Default for WordLimbs<T, N> {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -363,6 +383,17 @@ impl<F: Field> Word<Value<F>> {
}
}

impl Word<Column<Advice>> {
/// Query advice of Word of columns advice
pub fn query_advice<F: Field>(
&self,
meta: &mut VirtualCells<F>,
at: Rotation,
) -> Word<Expression<F>> {
self.0.query_advice(meta, at).to_word()
}
}

impl<F: Field> WordExpr<F> for Word<Cell<F>> {
fn to_word(&self) -> Word<Expression<F>> {
self.word_expr().to_word()
Expand Down Expand Up @@ -428,7 +459,7 @@ impl<F: Field> WordExpr<F> for Word<Expression<F>> {

impl<F: Field, const N1: usize> WordLimbs<Expression<F>, 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
Expand Down Expand Up @@ -481,4 +512,9 @@ impl<F: Field> From<WordLegacy<F>> for Word32Cell<F> {
}
}

/// Return the hash of the empty code as a Word<Value<F>> in little-endian.
pub fn empty_code_hash_word_value<F: Field>() -> Word<Value<F>> {
Comment thread
leolara marked this conversation as resolved.
Word::from(CodeDB::empty_code_hash()).into_value()
}

// TODO unittest