Skip to content

Commit

Permalink
Add constraints in state circuit for AccountTransientStorage (#1274)
Browse files Browse the repository at this point in the history
* Add non-compiling switch statement

* Fix build and add transient storage constraints

* fmt

* Add soundness tests and remove redundant constraint

* fmt

---------

Co-authored-by: Mason Liang <mason@scroll.io>
  • Loading branch information
z2trillion and Mason Liang authored May 21, 2024
1 parent 352b3ec commit 8aa34df
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 34 deletions.
87 changes: 53 additions & 34 deletions zkevm-circuits/src/state_circuit/constraint_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,37 +98,31 @@ impl<F: Field> ConstraintBuilder<F> {

pub fn build(&mut self, q: &Queries<F>) {
self.build_general_constraints(q);
self.condition(q.tag_matches(RwTableTag::Start), |cb| {
cb.build_start_constraints(q)
});
self.condition(q.tag_matches(RwTableTag::Memory), |cb| {
cb.build_memory_constraints(q)
});
self.condition(q.tag_matches(RwTableTag::Stack), |cb| {
cb.build_stack_constraints(q)
});
self.condition(q.tag_matches(RwTableTag::AccountStorage), |cb| {
cb.build_account_storage_constraints(q)
});
self.condition(q.tag_matches(RwTableTag::TxAccessListAccount), |cb| {
cb.build_tx_access_list_account_constraints(q)
});
self.condition(
q.tag_matches(RwTableTag::TxAccessListAccountStorage),
|cb| cb.build_tx_access_list_account_storage_constraints(q),
);
self.condition(q.tag_matches(RwTableTag::TxRefund), |cb| {
cb.build_tx_refund_constraints(q)
});
self.condition(q.tag_matches(RwTableTag::Account), |cb| {
cb.build_account_constraints(q)
});
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)
});
self.build_conditional_constraints(q);
}

fn build_conditional_constraints(&mut self, q: &Queries<F>) {
for tag in RwTableTag::iter() {
let build = match tag {
RwTableTag::Start => Self::build_start_constraints,
RwTableTag::Memory => Self::build_memory_constraints,
RwTableTag::Stack => Self::build_stack_constraints,
RwTableTag::AccountStorage => Self::build_account_storage_constraints,
RwTableTag::TxAccessListAccount => Self::build_tx_access_list_account_constraints,
RwTableTag::TxAccessListAccountStorage => {
Self::build_tx_access_list_account_storage_constraints
}
RwTableTag::TxRefund => Self::build_tx_refund_constraints,
RwTableTag::Account => Self::build_account_constraints,
RwTableTag::CallContext => Self::build_call_context_constraints,
RwTableTag::TxLog => Self::build_tx_log_constraints,
RwTableTag::TxReceipt => Self::build_tx_receipt_constraints,
RwTableTag::AccountTransientStorage => {
Self::build_account_transient_storage_constraints
}
};
self.condition(q.tag_matches(tag), |cb| build(cb, q));
}
}

fn build_general_constraints(&mut self, q: &Queries<F>) {
Expand Down Expand Up @@ -509,9 +503,7 @@ impl<F: Field> ConstraintBuilder<F> {
}

fn build_tx_receipt_constraints(&mut self, q: &Queries<F>) {
// TODO: implement TxReceipt constraints
self.require_equal("TxReceipt rows not implemented", 1.expr(), 0.expr());

// TODO: finish TxReceipt constraints
self.require_equal(
"state_root is unchanged for TxReceipt",
q.state_root(),
Expand All @@ -523,6 +515,33 @@ impl<F: Field> ConstraintBuilder<F> {
);
}

fn build_account_transient_storage_constraints(&mut self, q: &Queries<F>) {
// 5.0. `field_tag` is 0
self.require_zero("field_tag is 0 for AccountTransientStorage", q.field_tag());

// 5.1. `initial_value` is 0
self.require_zero(
"initial AccountTransientStorage value is 0",
q.initial_value(),
);

// 5.2. `value` column at previous rotation equals `value_prev` at current rotation
self.condition(q.not_first_access.clone(), |cb| {
cb.require_equal(
"value column at Rotation::prev() equals value_prev at Rotation::cur()",
q.rw_table.value_prev.clone(),
q.value_prev_column(),
);
});

// 5.3. `state root` is the same
self.require_equal(
"state_root is unchanged for AccountTransientStorage",
q.state_root(),
q.state_root_prev(),
);
}

fn require_zero(&mut self, name: &'static str, e: Expression<F>) {
self.constraints.push((name, self.condition.clone() * e));
}
Expand Down
4 changes: 4 additions & 0 deletions zkevm-circuits/src/state_circuit/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ pub enum AdviceColumn {
// NonEmptyWitness is the BatchedIsZero chip witness that contains the
// inverse of the non-zero value if any in [committed_value, value]
NonEmptyWitness,
StateRoot,
FieldTag,
}

impl AdviceColumn {
Expand Down Expand Up @@ -118,6 +120,8 @@ impl AdviceColumn {
Self::InitialValue => config.initial_value,
Self::IsZero => config.is_non_exist.is_zero,
Self::NonEmptyWitness => config.is_non_exist.nonempty_witness,
Self::StateRoot => config.state_root,
Self::FieldTag => config.rw_table.field_tag,
}
}
}
123 changes: 123 additions & 0 deletions zkevm-circuits/src/state_circuit/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,129 @@ fn bad_initial_tx_log_value() {
);
}

#[test]
fn account_transient_storage_nonzero_field_tag() {
let rows = vec![Rw::AccountTransientStorage {
rw_counter: 1,
is_write: false,
account_address: Address::default(),
storage_key: Word::from(2342),
tx_id: 800,
value: U256::zero(),
value_prev: U256::zero(),
}];

let nonzero_field_tag = Fr::one();
let nonempty_witness = (nonzero_field_tag - Fr::from(AccountFieldTag::CodeHash as u64))
.invert()
.unwrap();

let overrides = HashMap::from([
((AdviceColumn::FieldTag, 0), nonzero_field_tag),
((AdviceColumn::IsZero, 0), Fr::zero()),
((AdviceColumn::NonEmptyWitness, 0), nonempty_witness),
]);

assert_error_matches(
verify_with_overrides(rows, overrides),
"field_tag is 0 for AccountTransientStorage",
);
}

#[test]
fn account_transient_storage_nonzero_initial_value() {
let rows = vec![Rw::AccountTransientStorage {
rw_counter: 1,
is_write: true,
account_address: Address::default(),
storage_key: Word::from(2342),
tx_id: 800,
value: U256::one(),
value_prev: U256::zero(),
}];

let overrides = HashMap::from([
((AdviceColumn::InitialValue, 0), Fr::one()),
((AdviceColumn::ValuePrev, 0), Fr::one()),
]);

assert_error_matches(
verify_with_overrides(rows, overrides),
"initial AccountTransientStorage value is 0",
);
}

#[test]
fn account_transient_storage_nonzero_value_first_access_read() {
let rows = vec![Rw::AccountTransientStorage {
rw_counter: 1,
is_write: false,
account_address: Address::default(),
storage_key: Word::from(2342),
tx_id: 800,
value: U256::zero(),
value_prev: U256::zero(),
}];

let overrides = HashMap::from([((AdviceColumn::Value, 0), Fr::from(244))]);

assert_error_matches(
verify_with_overrides(rows, overrides),
"first access reads don't change value",
);
}

#[test]
fn account_transient_storage_value_prev_mismatch() {
let rows = vec![
Rw::AccountTransientStorage {
rw_counter: 1,
is_write: true,
account_address: Address::default(),
storage_key: Word::from(2342),
tx_id: 800,
value: U256::one(),
value_prev: U256::zero(),
},
Rw::AccountTransientStorage {
rw_counter: 2,
is_write: true,
account_address: Address::default(),
storage_key: Word::from(2342),
tx_id: 800,
value: U256::one(),
value_prev: U256::one(),
},
];

let overrides = HashMap::from([((AdviceColumn::ValuePrev, 1), Fr::from(3))]);

assert_error_matches(
verify_with_overrides(rows, overrides),
"value column at Rotation::prev() equals value_prev at Rotation::cur()",
);
}

#[test]
fn account_transient_storage_changed_state_root() {
let rows = vec![Rw::AccountTransientStorage {
rw_counter: 1,
is_write: false,
account_address: Address::default(),
storage_key: Word::from(2342),
tx_id: 800,
value: U256::zero(),
value_prev: U256::zero(),
}];

let overrides = HashMap::from([((AdviceColumn::StateRoot, 0), Fr::from(20))]);

assert_error_matches(
verify_with_overrides(rows, overrides),
"state_root is unchanged for AccountTransientStorage",
);
}

#[test]
fn variadic_size_check() {
let mut rows = vec![
Expand Down

0 comments on commit 8aa34df

Please sign in to comment.