Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.

implement log state circuit #552

Merged
merged 18 commits into from
Jun 30, 2022
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
2 changes: 1 addition & 1 deletion zkevm-circuits/src/evm_circuit/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ pub enum BytecodeFieldTag {
Padding,
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, EnumIter)]
pub enum TxLogFieldTag {
Address = 1,
Topic,
Expand Down
6 changes: 3 additions & 3 deletions zkevm-circuits/src/evm_circuit/util/constraint_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> {
&mut self,
tx_id: Expression<F>,
log_id: Expression<F>,
tag: TxLogFieldTag,
field_tag: TxLogFieldTag,
index: Expression<F>,
value: Expression<F>,
) {
Expand All @@ -1053,8 +1053,8 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> {
RwTableTag::TxLog,
[
tx_id,
index + (1u64 << 8).expr() * log_id,
tag.expr(),
index + (1u64 << 32).expr() * field_tag.expr() + (1u64 << 48).expr() * log_id,
0.expr(),
0.expr(),
value,
0.expr(),
Expand Down
17 changes: 14 additions & 3 deletions zkevm-circuits/src/evm_circuit/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,8 +762,19 @@ impl Rw {
Self::Stack { stack_pointer, .. } => {
Some(U256::from(*stack_pointer as u64).to_address())
}
Self::TxLog { log_id, index, .. } => {
Some((U256::from(*index as u64) + (U256::from(*log_id) << 8)).to_address())
Self::TxLog {
log_id,
field_tag,
index,
..
} => {
// make field_tag fit into one limb (16 bits)
Some(
(U256::from(*index as u64)
+ (U256::from(*field_tag as u64) << 32)
+ (U256::from(*log_id) << 48))
.to_address(),
)
}
Self::Start { .. }
| Self::CallContext { .. }
Expand All @@ -776,7 +787,6 @@ impl Rw {
match self {
Self::Account { field_tag, .. } => Some(*field_tag as u64),
Self::CallContext { field_tag, .. } => Some(*field_tag as u64),
Self::TxLog { field_tag, .. } => Some(*field_tag as u64),
Self::TxReceipt { field_tag, .. } => Some(*field_tag as u64),
Self::Start { .. }
| Self::Memory { .. }
Expand All @@ -785,6 +795,7 @@ impl Rw {
| Self::TxAccessListAccount { .. }
| Self::TxAccessListAccountStorage { .. }
| Self::TxRefund { .. }
| Self::TxLog { .. }
| Self::AccountDestructed { .. } => None,
}
}
Expand Down
102 changes: 102 additions & 0 deletions zkevm-circuits/src/state_circuit/constraint_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ impl<F: Field> ConstraintBuilder<F> {
self.condition(q.tag_matches(RwTableTag::CallContext), |cb| {
cb.build_call_context_constraints(q)
});
self.condition(q.tag_matches(RwTableTag::TxLog), |cb| {
cb.build_tx_log_constraints(q)
});
}

fn build_general_constraints(&mut self, q: &Queries<F>) {
Expand Down Expand Up @@ -234,10 +237,92 @@ impl<F: Field> ConstraintBuilder<F> {
// TODO: Missing constraints
}

fn build_tx_log_constraints(&mut self, q: &Queries<F>) {
self.require_equal(
"is_write is always true for TxLog",
q.is_write.clone(),
1.expr(),
);

// Comment out the following field_tag-related constraints as it is
// duplicated between state circuit and evm circuit. For more information, please refer to https://github.com/privacy-scaling-explorations/zkevm-specs/issues/221
// cb.require_zero(
// "reset log_id to one when tx_id increases",
// q.tx_log_id() - 1.expr(),
// );

// constrain first field_tag is Address when tx id increases
// cb.require_equal(
// "first field_tag is Address when tx changes",
// q.field_tag_matches(TxLogFieldTag::Address),
// 1.expr(),
// );

// increase log_id when tag changes to Address within same tx
// self.condition(
// q.is_id_unchanged.clone()
// * q.is_tag_unchanged.clone()
// * q.field_tag_matches(TxLogFieldTag::Address),
// |cb| {
// cb.require_equal(
// "log_id = pre_log_id + 1",
// q.tx_log_id(),
// q.tx_log_id_prev() + 1.expr(),
// )
// },
// );

// within same tx, log_id will not change if field_tag != Address
// self.condition(
// q.is_id_unchanged.clone()
// * q.is_tag_unchanged.clone()
// * (1.expr() - q.field_tag_matches(TxLogFieldTag::Address)),
// |cb| {
// cb.require_equal(
// "log_id will not change if field_tag != Address within
// tx", q.tx_log_id(),
// q.tx_log_id_prev(),
// )
// },
// );

// constrain index is increasing by 1 when field_tag stay same
// self.condition(
// q.is_tag_unchanged.clone() * q.is_field_tag_unchanged.clone(),
// |cb| {
// cb.require_equal(
// "index = pre_index + 1",
// q.tx_log_index(),
// q.tx_log_index_prev() + 1.expr(),
// )
// },
// );

// self.condition(q.field_tag_matches(TxLogFieldTag::Address), |cb| {
// cb.require_zero("index is zero for address ", q.tx_log_index())
// });

// if tag Topic appear, topic_index in range [0,4)
// self.condition(q.field_tag_matches(TxLogFieldTag::Topic), |cb| {
// let topic_index = q.tx_log_index();
// cb.require_zero(
// "topic_index in range [0,4) ",
// topic_index.clone()
// * (1.expr() - topic_index.clone())
// * (2.expr() - topic_index.clone())
// * (3.expr() - topic_index),
// )
// });
}

fn require_zero(&mut self, name: &'static str, e: Expression<F>) {
self.constraints.push((name, self.condition.clone() * e));
}

fn require_equal(&mut self, name: &'static str, left: Expression<F>, right: Expression<F>) {
self.require_zero(name, left - right)
}

fn require_boolean(&mut self, name: &'static str, e: Expression<F>) {
self.require_zero(name, e.clone() * (1.expr() - e))
}
Expand Down Expand Up @@ -313,11 +398,28 @@ impl<F: Field> Queries<F> {
fn rw_counter_change(&self) -> Expression<F> {
self.rw_counter.value.clone() - self.rw_counter.value_prev.clone()
}

fn tx_log_index(&self) -> Expression<F> {
from_digits(&self.address.limbs[0..2], (1u64 << 16).expr())
}

fn tx_log_index_prev(&self) -> Expression<F> {
from_digits(&self.address.limbs_prev[0..2], (1u64 << 16).expr())
}

fn tx_log_id(&self) -> Expression<F> {
from_digits(&self.address.limbs[3..5], (1u64 << 16).expr())
}

fn tx_log_id_prev(&self) -> Expression<F> {
from_digits(&self.address.limbs_prev[3..5], (1u64 << 16).expr())
}
}

fn from_digits<F: Field>(digits: &[Expression<F>], base: Expression<F>) -> Expression<F> {
digits
.iter()
.rev()
.fold(Expression::Constant(F::zero()), |result, digit| {
digit.clone() + result * base.clone()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub struct Queries<F: Field, const N: usize> {
pub value: Expression<F>,
pub value_prev: Expression<F>, // move this up, as it's not always needed.
pub limbs: [Expression<F>; N],
pub limbs_prev: [Expression<F>; N],
}

impl<F: Field, const N: usize> Queries<F, N> {
Expand All @@ -57,6 +58,9 @@ impl<F: Field, const N: usize> Queries<F, N> {
value: meta.query_advice(c.value, Rotation::cur()),
value_prev: meta.query_advice(c.value, Rotation::prev()),
limbs: c.limbs.map(|limb| meta.query_advice(limb, Rotation::cur())),
limbs_prev: c
.limbs
.map(|limb| meta.query_advice(limb, Rotation::prev())),
}
}
}
Expand Down
78 changes: 77 additions & 1 deletion zkevm-circuits/src/state_circuit/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{StateCircuit, StateConfig};
use crate::evm_circuit::{
table::{AccountFieldTag, CallContextFieldTag, RwTableTag},
table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag},
witness::{Rw, RwMap},
};
use crate::state_circuit::binary_number::AsBits;
Expand Down Expand Up @@ -335,6 +335,82 @@ fn storage_key_rlc() {
assert_eq!(verify(rows), Ok(()));
}

#[test]
fn tx_log_ok() {
let rows = vec![
Rw::Stack {
rw_counter: 1,
is_write: true,
call_id: 1,
stack_pointer: 1023,
value: U256::from(394500u64),
},
Rw::TxLog {
rw_counter: 2,
is_write: true,
tx_id: 1,
log_id: 1,
field_tag: TxLogFieldTag::Address,
index: 0usize,
value: U256::one(),
},
Rw::TxLog {
rw_counter: 3,
is_write: true,
tx_id: 1,
log_id: 1,
field_tag: TxLogFieldTag::Topic,
index: 0usize,
value: U256::one(),
},
Rw::TxLog {
rw_counter: 4,
is_write: true,
tx_id: 1,
log_id: 1,
field_tag: TxLogFieldTag::Topic,
index: 1usize,
value: U256::from(2u64),
},
Rw::TxLog {
rw_counter: 5,
is_write: true,
tx_id: 1,
log_id: 1,
field_tag: TxLogFieldTag::Data,
index: 0usize,
value: U256::from(3u64),
},
Rw::TxLog {
rw_counter: 6,
is_write: true,
tx_id: 1,
log_id: 1,
field_tag: TxLogFieldTag::Data,
index: 1usize,
value: U256::from(3u64),
},
];

assert_eq!(verify(rows), Ok(()));
}

#[test]
fn tx_log_bad() {
// is_write is false
let rows = vec![Rw::TxLog {
rw_counter: 2,
is_write: false,
tx_id: 1,
log_id: 1,
field_tag: TxLogFieldTag::Address,
index: 0usize,
value: U256::one(),
}];

assert_error_matches(verify(rows), "is_write is always true for TxLog");
}

#[test]
fn address_limb_mismatch() {
let rows = vec![Rw::Account {
Expand Down