Skip to content

Commit

Permalink
implement log state circuit (privacy-scaling-explorations#552)
Browse files Browse the repository at this point in the history
* test pass for positive and negativ

* fix ci and increase degree

* disable test affected by issue privacy-scaling-explorations#561

* add is_tag_unchanged to reduce degree

* constrain log id not change within tx if field tag != Address

* move index constrains out of tx id remain condition

* merge fixing address expression for log

* rebase and fix comment

* fix pre limbs

* remove field tag specific constrain to simplify

* log_state

* fix ci

* revise comment as suggestion

* Empty-Commit

* Empty-Commit
  • Loading branch information
DreamWuGit authored Jun 30, 2022
1 parent d7e4017 commit d3c6769
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 8 deletions.
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

0 comments on commit d3c6769

Please sign in to comment.