Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Commit ea5e17a

Browse files
DreamWuGitlispcMason Liangz2trillion
authored
test(mpt circuit): transfer eth to non existing address (#949)
* test mpt for non zero value to non existing addr * Update callop.rs * Generate mock mpt updates in test * Add poseidon table to config for test mpt circuit * assert HASH_SCHEME_DONE when building mock mpt proofs * Simplify loading mpt poseidon hashes * clippy * remove debug prover verify --------- Co-authored-by: Zhang Zhuo <mycinbrin@gmail.com> Co-authored-by: Mason Liang <mason@scroll.io> Co-authored-by: z2trillion <z2trillion@users.noreply.github.com>
1 parent 81cc998 commit ea5e17a

File tree

4 files changed

+110
-60
lines changed

4 files changed

+110
-60
lines changed

zkevm-circuits/src/evm_circuit/execution/callop.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,12 +1158,18 @@ impl<F: Field> ExecutionGadget<F> for CallOpGadget<F> {
11581158
#[cfg(test)]
11591159
mod test {
11601160
use super::*;
1161-
use crate::test_util::CircuitTestBuilder;
1162-
use bus_mapping::circuit_input_builder::CircuitsParams;
1161+
use crate::{
1162+
mpt_circuit::MptCircuit, test_util::CircuitTestBuilder, util::SubCircuit,
1163+
witness::block_convert,
1164+
};
1165+
use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData};
11631166
use eth_types::{
1164-
address, bytecode, evm_types::OpcodeId, geth_types::Account, word, Address, ToWord, Word,
1167+
address, bytecode,
1168+
evm_types::OpcodeId,
1169+
geth_types::{Account, GethData},
1170+
word, Address, ToWord, Word,
11651171
};
1166-
1172+
use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr};
11671173
use itertools::Itertools;
11681174
use mock::{
11691175
test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0},
@@ -1561,6 +1567,43 @@ mod test {
15611567
.run();
15621568
}
15631569

1570+
// maybe consider to move to mpt_circuit module
1571+
#[cfg(feature = "scroll")]
1572+
#[test]
1573+
fn call_non_exist_with_value_mpt_circuit() {
1574+
let callee_code = bytecode! {
1575+
.op_call(0xc350, 0xff, 0x13, 0x0, 0x0, 0x0, 0x0)
1576+
};
1577+
1578+
let ctx = TestContext::<2, 1>::new(
1579+
None,
1580+
account_0_code_account_1_no_code(callee_code),
1581+
tx_from_1_to_0,
1582+
|block, _tx| block.number(0xcafeu64),
1583+
)
1584+
.unwrap();
1585+
1586+
let block: GethData = ctx.into();
1587+
let mut builder = BlockData::new_from_geth_data_with_params(
1588+
block.clone(),
1589+
CircuitsParams {
1590+
max_rws: 1024,
1591+
max_copy_rows: 1024,
1592+
max_mpt_rows: 3500,
1593+
..Default::default()
1594+
},
1595+
)
1596+
.new_circuit_input_builder();
1597+
builder
1598+
.handle_block(&block.eth_block, &block.geth_traces)
1599+
.unwrap();
1600+
let mut block = block_convert::<Fr>(&builder.block, &builder.code_db).unwrap();
1601+
block.mpt_updates.mock_fill_state_roots();
1602+
let mpt_circuit = MptCircuit::new_from_block(&block);
1603+
let prover = MockProver::<Fr>::run(12, &mpt_circuit, vec![]).unwrap();
1604+
assert!(prover.verify().is_ok());
1605+
}
1606+
15641607
// minimal testool case: returndatasize_bug_d0_g0_v0
15651608
#[test]
15661609
fn test_oog_call_with_inner_sstore() {

zkevm-circuits/src/mpt_circuit.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
#![allow(missing_docs)]
22
//! wrapping of mpt-circuit
3+
// #[cfg(test)]
4+
// use crate::mpt_circuit::mpt;
35
use crate::{
46
table::{LookupTable, MptTable, PoseidonTable},
57
util::{Challenges, SubCircuit, SubCircuitConfig},
68
witness,
79
};
810
use eth_types::Field;
11+
#[cfg(test)]
12+
use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit};
913
use halo2_proofs::{
10-
circuit::{Layouter, SimpleFloorPlanner, Value},
14+
circuit::{Layouter, Value},
1115
halo2curves::bn256::Fr,
12-
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed},
16+
plonk::{Advice, Column, ConstraintSystem, Error, Fixed},
1317
};
1418
use itertools::Itertools;
19+
#[cfg(test)]
20+
use mpt_zktrie::mpt_circuits::gadgets::mpt_update::hash_traces;
1521
use mpt_zktrie::mpt_circuits::{gadgets::poseidon::PoseidonLookup, mpt, types::Proof};
1622

1723
impl PoseidonLookup for PoseidonTable {
@@ -135,9 +141,9 @@ impl SubCircuit<Fr> for MptCircuit<Fr> {
135141
}
136142
}
137143

138-
#[cfg(any(feature = "test", test))]
144+
#[cfg(test)]
139145
impl Circuit<Fr> for MptCircuit<Fr> {
140-
type Config = (MptCircuitConfig<Fr>, Challenges);
146+
type Config = (MptCircuitConfig<Fr>, PoseidonTable, Challenges);
141147
type FloorPlanner = SimpleFloorPlanner;
142148

143149
fn without_witnesses(&self) -> Self {
@@ -149,7 +155,7 @@ impl Circuit<Fr> for MptCircuit<Fr> {
149155

150156
fn configure(meta: &mut ConstraintSystem<Fr>) -> Self::Config {
151157
let challenges = Challenges::construct(meta);
152-
let poseidon_table = PoseidonTable::dev_construct(meta);
158+
let poseidon_table = PoseidonTable::construct(meta);
153159
let mpt_table = MptTable::construct(meta);
154160

155161
let config = {
@@ -163,15 +169,22 @@ impl Circuit<Fr> for MptCircuit<Fr> {
163169
)
164170
};
165171

166-
(config, challenges)
172+
(config, poseidon_table, challenges)
167173
}
168174

169175
fn synthesize(
170176
&self,
171-
(config, challenges): Self::Config,
177+
(mpt_config, poseidon_table, challenges): Self::Config,
172178
mut layouter: impl Layouter<Fr>,
173179
) -> Result<(), Error> {
180+
let poseidon_table_rows: Vec<_> = hash_traces(&self.proofs)
181+
.iter()
182+
.map(|([left, right], domain, hash)| {
183+
[*hash, *left, *right, Fr::zero(), *domain, Fr::one()].map(Value::known)
184+
})
185+
.collect();
186+
poseidon_table.load(&mut layouter, &poseidon_table_rows)?;
174187
let challenges = challenges.values(&layouter);
175-
self.synthesize_sub(&config, &challenges, &mut layouter)
188+
self.synthesize_sub(&mpt_config, &challenges, &mut layouter)
176189
}
177190
}

zkevm-circuits/src/table.rs

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -891,59 +891,25 @@ impl PoseidonTable {
891891
}
892892
}
893893

894-
/// Construct a new PoseidonTable for dev
895-
pub(crate) fn dev_construct<F: FieldExt>(meta: &mut ConstraintSystem<F>) -> Self {
896-
Self::construct(meta)
897-
}
898-
899-
pub(crate) fn assign<F: Field>(
900-
&self,
901-
region: &mut Region<'_, F>,
902-
offset: usize,
903-
row: &[Value<F>],
904-
) -> Result<(), Error> {
905-
region.assign_fixed(
906-
|| "assign poseidon table row value",
907-
self.q_enable,
908-
offset,
909-
|| Value::known(F::one()),
910-
)?;
911-
let poseidon_table_columns = <PoseidonTable as LookupTable<F>>::advice_columns(self);
912-
for (column, value) in poseidon_table_columns.iter().zip_eq(row) {
913-
region.assign_advice(
914-
|| "assign poseidon table row value",
915-
*column,
916-
offset,
917-
|| *value,
918-
)?;
919-
}
920-
Ok(())
921-
}
922-
923-
// Is this method used anyhwhere?
924-
pub(crate) fn load<'d, F: Field>(
894+
#[cfg(test)]
895+
/// Load mpt hashes (without the poseidon circuit) for testing purposes.
896+
pub fn load<F: Field>(
925897
&self,
926898
layouter: &mut impl Layouter<F>,
927-
hashes: impl Iterator<Item = &'d [Value<F>]> + Clone,
899+
hashes: &[[Value<F>; 6]],
928900
) -> Result<(), Error> {
929901
layouter.assign_region(
930902
|| "poseidon table",
931-
|mut region| self.load_with_region(&mut region, hashes.clone()),
903+
|mut region| {
904+
self.assign(&mut region, 0, [Value::known(F::zero()); 6])?;
905+
for (offset, row) in hashes.iter().enumerate() {
906+
self.assign(&mut region, offset + 1, *row)?;
907+
}
908+
Ok(())
909+
},
932910
)
933911
}
934912

935-
pub(crate) fn load_with_region<'d, F: Field>(
936-
&self,
937-
region: &mut Region<'_, F>,
938-
hashes: impl Iterator<Item = &'d [Value<F>]>,
939-
) -> Result<(), Error> {
940-
self.assign(region, 0, [Value::known(F::zero()); 6].as_slice())?;
941-
for (offset, row) in hashes.enumerate() {
942-
self.assign(region, offset + 1, row)?;
943-
}
944-
Ok(())
945-
}
946-
947913
/// Provide this function for the case that we want to consume a poseidon
948914
/// table but without running the full poseidon circuit
949915
pub fn dev_load<'a, F: Field>(
@@ -1064,6 +1030,30 @@ impl PoseidonTable {
10641030
},
10651031
)
10661032
}
1033+
1034+
fn assign<F: Field>(
1035+
&self,
1036+
region: &mut Region<'_, F>,
1037+
offset: usize,
1038+
row: [Value<F>; 6],
1039+
) -> Result<(), Error> {
1040+
region.assign_fixed(
1041+
|| "assign poseidon table row value",
1042+
self.q_enable,
1043+
offset,
1044+
|| Value::known(F::one()),
1045+
)?;
1046+
let poseidon_table_columns = <PoseidonTable as LookupTable<F>>::advice_columns(self);
1047+
for (column, value) in poseidon_table_columns.iter().zip_eq(row) {
1048+
region.assign_advice(
1049+
|| "assign poseidon table row value",
1050+
*column,
1051+
offset,
1052+
|| value,
1053+
)?;
1054+
}
1055+
Ok(())
1056+
}
10671057
}
10681058

10691059
/// Tag to identify the field in a Bytecode Table row

zkevm-circuits/src/witness/mpt.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ use crate::{
55
use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word, U256};
66
use halo2_proofs::circuit::Value;
77
use itertools::Itertools;
8+
#[cfg(test)]
9+
use mpt_zktrie::state::builder::HASH_SCHEME_DONE;
810
use mpt_zktrie::{
911
mpt_circuits::{serde::SMTTrace, MPTProofType},
1012
state,
1113
state::witness::WitnessGenerator,
1214
};
15+
1316
use serde::{Deserialize, Serialize};
1417
use std::collections::BTreeMap;
1518

@@ -78,9 +81,10 @@ impl MptUpdates {
7881
})
7982
}
8083

81-
pub(crate) fn mock_fill_state_roots(&mut self) {
82-
// initialize a mock witness generator that is consistent with the old values of
83-
// self.updates
84+
#[cfg(test)]
85+
/// initialize a mock witness generator that is consistent with the old values of self.updates
86+
pub fn mock_fill_state_roots(&mut self) {
87+
assert!(*HASH_SCHEME_DONE);
8488
let mut wit_gen = WitnessGenerator::from(&ZktrieState::default());
8589
for (key, update) in &mut self.updates {
8690
let key = key.set_non_exists(Word::zero(), update.old_value);

0 commit comments

Comments
 (0)