diff --git a/Cargo.lock b/Cargo.lock index ae24e482de..63ee35341e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2335,7 +2335,7 @@ dependencies = [ [[package]] name = "halo2-mpt-circuits" version = "0.1.0" -source = "git+https://github.com/scroll-tech/mpt-circuit.git?branch=v0.7#32ab964ff065ee6a0ccc63590b9db73238c70b81" +source = "git+https://github.com/scroll-tech/mpt-circuit.git?branch=v0.8#287a66e56ad3071cddd843c7388ca555ea02ceed" dependencies = [ "env_logger", "ethers-core", diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index f0bb11a84b..bc21e44982 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -284,7 +284,8 @@ pub(crate) fn extract_hash_cells( keccak_packed_multi::get_input_bytes_col_idx_in_cell_manager() + >::columns(&keccak_config.keccak_table) .len() - - 1; + - 1 + - 2; // exclude last word limbs' columns for (offset, keccak_row) in witness.iter().enumerate() { let row = keccak_config.set_row(&mut region, offset, keccak_row)?; diff --git a/eth-types/src/evm_types/opcode_ids.rs b/eth-types/src/evm_types/opcode_ids.rs index 3287b40072..f05a270c3b 100644 --- a/eth-types/src/evm_types/opcode_ids.rs +++ b/eth-types/src/evm_types/opcode_ids.rs @@ -1240,8 +1240,11 @@ impl FromStr for OpcodeId { "BASEFEE" => OpcodeId::BASEFEE, #[cfg(feature = "scroll")] "BASEFEE" => OpcodeId::INVALID(0x48), - "TLOAD" => OpcodeId::INVALID(0xb3), - "TSTORE" => OpcodeId::INVALID(0xb4), + "BLOBHASH" => OpcodeId::INVALID(0x49), + "BLOBBASEFEE" => OpcodeId::INVALID(0x4a), + "TLOAD" => OpcodeId::INVALID(0x5c), + "TSTORE" => OpcodeId::INVALID(0x5d), + "MCOPY" => OpcodeId::INVALID(0x5e), _ => { // Parse an invalid opcode value as reported by geth static RE: LazyLock = LazyLock::new(|| { diff --git a/gadgets/src/comparator.rs b/gadgets/src/comparator.rs index ce9f395604..2cb1dc83c0 100644 --- a/gadgets/src/comparator.rs +++ b/gadgets/src/comparator.rs @@ -4,7 +4,7 @@ use eth_types::Field; use halo2_proofs::{ circuit::{Chip, Region, Value}, - plonk::{ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, + plonk::{Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; @@ -64,7 +64,7 @@ impl ComparatorChip { q_enable: impl FnOnce(&mut VirtualCells) -> Expression + Clone, lhs: impl FnOnce(&mut VirtualCells) -> Expression + Clone, rhs: impl FnOnce(&mut VirtualCells) -> Expression + Clone, - u8_table: TableColumn, + u8_table: Column, ) -> ComparatorConfig { let lt_config = LtChip::configure(meta, q_enable.clone(), lhs.clone(), rhs.clone(), u8_table); diff --git a/gadgets/src/less_than.rs b/gadgets/src/less_than.rs index ebde33a37b..b12f8b3158 100644 --- a/gadgets/src/less_than.rs +++ b/gadgets/src/less_than.rs @@ -3,7 +3,7 @@ use eth_types::Field; use halo2_proofs::{ circuit::{Chip, Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; @@ -37,7 +37,9 @@ pub struct LtConfig { /// Denotes the bytes representation of the difference between lhs and rhs. pub diff: [Column; N_BYTES], /// Denotes the range within which each byte should lie. - pub u8_table: TableColumn, + //pub u8_table: TableColumn, + pub u8_table: Column, + /// Denotes the range within which both lhs and rhs lie. pub range: F, } @@ -68,7 +70,8 @@ impl LtChip { q_enable: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression + Clone, lhs: impl FnOnce(&mut VirtualCells) -> Expression, rhs: impl FnOnce(&mut VirtualCells) -> Expression, - u8_table: TableColumn, + //u8_table: TableColumn, + u8_table: Column, ) -> LtConfig { let lt = meta.advice_column(); let diff = [(); N_BYTES].map(|_| meta.advice_column()); @@ -94,11 +97,11 @@ impl LtChip { }); for cell_column in diff { - meta.lookup("range check for u8", |meta| { + meta.lookup_any("range check for u8", |meta| { let q_enable = q_enable.clone()(meta); vec![( q_enable * meta.query_advice(cell_column, Rotation::cur()), - u8_table, + meta.query_fixed(u8_table, Rotation::cur()), )] }); } @@ -156,11 +159,12 @@ impl LtInstruction for LtChip { ) -> Result<(), Error> { const RANGE: usize = u8::MAX as usize; - layouter.assign_table( + //layouter.assign_table( + layouter.assign_region( || "load u8 range check table", - |mut table| { + |mut region| { for i in 0..=RANGE { - table.assign_cell( + region.assign_fixed( || "assign cell in fixed column", self.config.u8_table, i, @@ -265,7 +269,7 @@ mod test { let q_enable = meta.complex_selector(); let value = meta.advice_column(); let check = meta.advice_column(); - let u8_table = meta.lookup_table_column(); + let u8_table = meta.fixed_column(); let lt = LtChip::configure( meta, @@ -389,7 +393,7 @@ mod test { let q_enable = meta.complex_selector(); let (value_a, value_b) = (meta.advice_column(), meta.advice_column()); let check = meta.advice_column(); - let u16_table = meta.lookup_table_column(); + let u16_table = meta.fixed_column(); let lt = LtChip::configure( meta, diff --git a/gadgets/src/mul_add.rs b/gadgets/src/mul_add.rs index e10fb58183..9834383a6d 100644 --- a/gadgets/src/mul_add.rs +++ b/gadgets/src/mul_add.rs @@ -19,7 +19,7 @@ use eth_types::{Field, Word, ToU16LittleEndian}; use halo2_proofs::{ circuit::{Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; @@ -46,7 +46,8 @@ pub struct MulAddConfig { /// Sum of the parts higher than 256-bit in the product. pub overflow: Expression, /// Lookup table for LtChips and carry_lo/hi. - pub u16_table: TableColumn, + // pub u16_table: TableColumn, + pub u16_table: Column, /// Range check of a, b which needs to be in [0, 2^64) pub range_check_64: UIntRangeCheckChip, /// Range check of c, d which needs to be in [0, 2^128) @@ -118,7 +119,8 @@ impl MulAddChip { pub fn configure( meta: &mut ConstraintSystem, q_enable: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression + Clone, - u16_table: TableColumn, + //u16_table: TableColumn, + u16_table: Column, ) -> MulAddConfig { let col0 = meta.advice_column(); let col1 = meta.advice_column(); @@ -157,9 +159,10 @@ impl MulAddChip { carry_cols.append(&mut carry_hi_cols.clone()); for (col, rot) in carry_cols.into_iter() { - meta.lookup("mul carry range check lo/hi lookup u16", |meta| { + meta.lookup_any("mul carry range check lo/hi lookup u16", |meta| { let q_enable = q_enable.clone()(meta); - vec![(q_enable * meta.query_advice(col, Rotation(rot)), u16_table)] + let u16_expr = meta.query_fixed(u16_table, Rotation::cur()); + vec![(q_enable * meta.query_advice(col, Rotation(rot)), u16_expr)] }); } } @@ -506,7 +509,8 @@ mod test { fn configure(meta: &mut halo2_proofs::plonk::ConstraintSystem) -> Self::Config { let q_enable = meta.complex_selector(); - let u16_table = meta.lookup_table_column(); + //let u16_table = meta.lookup_table_column(); + let u16_table = meta.fixed_column(); let mul_config = MulAddChip::configure(meta, |meta| meta.query_selector(q_enable), u16_table); Self::Config { @@ -522,11 +526,12 @@ mod test { ) -> Result<(), halo2_proofs::plonk::Error> { let chip = MulAddChip::construct(config.mul_config); - layouter.assign_table( + //layouter.assign_table( + layouter.assign_region( || "u16 table", - |mut table| { + |mut region| { for i in 0..=65535 { - table.assign_cell( + region.assign_fixed( || format!("u16 table row {i}"), chip.config.u16_table, i, diff --git a/gadgets/src/range.rs b/gadgets/src/range.rs index 81cdebaba4..4888caca7e 100644 --- a/gadgets/src/range.rs +++ b/gadgets/src/range.rs @@ -10,7 +10,7 @@ use crate::util::Expr; use eth_types::Field; use halo2_proofs::{ circuit::{Chip, Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; @@ -36,7 +36,8 @@ pub struct UIntRangeCheckConfig { /// Denotes the little-endian representation of expression in u16. pub u16_repr: [Column; N_2BYTE], /// Denotes the u16 lookup table. - pub u16_table: TableColumn, + //pub u16_table: TableColumn, + pub u16_table: Column, _marker: std::marker::PhantomData, } @@ -61,7 +62,8 @@ impl UIntRangeCheckChip, q_enable: impl FnOnce(&mut VirtualCells) -> Expression + Clone, expressions: impl FnOnce(&mut VirtualCells) -> [Expression; N_EXPR], - u16_table: TableColumn, + //u16_table: TableColumn, + u16_table: Column, ) -> UIntRangeCheckConfig { let u16_repr = [(); N_2BYTE].map(|_| meta.advice_column()); @@ -83,9 +85,10 @@ impl UIntRangeCheckChip Result<()> { let mut tc = YamlStateTestBuilder::new(&Compiler::default()).load_yaml( @@ -796,6 +797,7 @@ arith: Ok(()) } + #[test] fn bad_balance() -> Result<()> { let mut tc = YamlStateTestBuilder::new(&Compiler::default()).load_yaml( diff --git a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs index 807595c4cd..db7de6ca7c 100644 --- a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs +++ b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs @@ -1,12 +1,17 @@ -use crate::{table::BytecodeFieldTag, util::get_push_size}; +use crate::{ + table::BytecodeFieldTag, + util::{get_push_size, word::Word}, +}; +use halo2_proofs::circuit::Value; + use bus_mapping::state_db::CodeDB; -use eth_types::{Field, ToWord, Word, U256}; +use eth_types::{Field, ToWord, U256}; use std::vec; /// Public data for the bytecode -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Default)] pub(crate) struct BytecodeRow { - pub(crate) code_hash: Word, + pub(crate) code_hash: Word>, pub(crate) tag: F, pub(crate) index: F, pub(crate) is_code: F, @@ -14,7 +19,7 @@ pub(crate) struct BytecodeRow { } /// Unrolled bytecode -#[derive(Clone, Debug, PartialEq, Default)] +#[derive(Clone, Debug, Default)] pub struct UnrolledBytecode { pub(crate) bytes: Vec, pub(crate) rows: Vec>, @@ -23,11 +28,15 @@ pub struct UnrolledBytecode { /// Get unrolled bytecode from raw bytes pub fn unroll(bytes: Vec) -> UnrolledBytecode { let code_hash = CodeDB::hash(&bytes[..]); - unroll_with_codehash(code_hash.to_word(), bytes) + let code_hash_word = Word::from(code_hash.to_word()).map(Value::known); + unroll_with_codehash(code_hash_word, bytes) } /// Get unrolled bytecode from raw bytes and codehash -pub fn unroll_with_codehash(code_hash: U256, bytes: Vec) -> UnrolledBytecode { +pub fn unroll_with_codehash( + code_hash: Word>, + bytes: Vec, +) -> UnrolledBytecode { let mut rows = vec![BytecodeRow:: { code_hash, tag: F::from(BytecodeFieldTag::Header as u64), diff --git a/zkevm-circuits/src/bytecode_circuit/circuit.rs b/zkevm-circuits/src/bytecode_circuit/circuit.rs index 0c38655b9c..25aa050eac 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit.rs @@ -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::{Word, Word32, WordExpr}, + Challenges, Expr, SubCircuit, SubCircuitConfig, + }, witness, }; use bus_mapping::{state_db::EMPTY_CODE_HASH_LE, util::POSEIDON_CODE_HASH_EMPTY}; -use eth_types::{Field, ToLittleEndian, ToScalar, ToWord}; +use eth_types::{Field, ToLittleEndian, ToScalar, ToWord, U256}; use gadgets::is_zero::{IsZeroChip, IsZeroConfig, IsZeroInstruction}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, @@ -274,19 +278,18 @@ impl SubCircuitConfig for BytecodeCircuitConfig { meta.query_advice(length, Rotation::cur()), ); - let empty_hash = if cfg!(feature = "poseidon-codehash") { - Expression::Constant(POSEIDON_CODE_HASH_EMPTY.to_word().to_scalar().unwrap()) + let empty_hash_word = if cfg!(feature = "poseidon-codehash") { + Word32::new(POSEIDON_CODE_HASH_EMPTY.to_word().to_le_bytes()) + .to_expr::() + .to_word() } else { - rlc::expr( - &EMPTY_CODE_HASH_LE.map(|v| Expression::Constant(F::from(v as u64))), - challenges.evm_word(), - ) + Word32::new(*EMPTY_CODE_HASH_LE).to_expr::().to_word() }; - cb.require_equal( + cb.require_equal_word( "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![ @@ -324,10 +327,12 @@ impl SubCircuitConfig for BytecodeCircuitConfig { 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( @@ -367,10 +372,12 @@ impl SubCircuitConfig for BytecodeCircuitConfig { 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( @@ -549,14 +556,16 @@ impl BytecodeCircuitConfig { last_row_offset ); - let empty_hash = challenges.evm_word().map(|challenge| { - if cfg!(feature = "poseidon-codehash") { - POSEIDON_CODE_HASH_EMPTY.to_word().to_scalar().unwrap() - } else { - rlc::value(EMPTY_CODE_HASH_LE.as_ref(), challenge) - } - }); - + let empty_hash_word = if cfg!(feature = "poseidon-codehash") { + //POSEIDON_CODE_HASH_EMPTY.to_word() + Word::new([ + POSEIDON_CODE_HASH_EMPTY.to_word().to_scalar().unwrap(), + F::zero(), + ]) + .map(Value::known) + } else { + Word::from(U256::from_little_endian(&*EMPTY_CODE_HASH_LE)).map(Value::known) + }; let mut is_first_time = true; layouter.assign_region( || "assign bytecode", @@ -567,7 +576,7 @@ impl BytecodeCircuitConfig { &mut region, &push_data_left_is_zero_chip, &index_length_diff_is_zero_chip, - empty_hash, + empty_hash_word, last_row_offset, last_row_offset, )?; @@ -584,7 +593,7 @@ impl BytecodeCircuitConfig { challenges, &push_data_left_is_zero_chip, &index_length_diff_is_zero_chip, - empty_hash, + empty_hash_word, &mut offset, last_row_offset, fail_fast, @@ -597,7 +606,7 @@ impl BytecodeCircuitConfig { &mut region, &push_data_left_is_zero_chip, &index_length_diff_is_zero_chip, - empty_hash, + empty_hash_word, idx, last_row_offset, )?; @@ -644,20 +653,19 @@ impl BytecodeCircuitConfig { 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, + )?; + + row.code_hash.assign_advice( + region, + || format!("assign code_hash {offset}"), + self.bytecode_table.code_hash, + offset, + )?; } Ok(()) } @@ -670,7 +678,7 @@ impl BytecodeCircuitConfig { challenges: &Challenges>, push_data_left_is_zero_chip: &IsZeroChip, index_length_diff_is_zero_chip: &IsZeroChip, - empty_hash: Value, + empty_hash: Word>, offset: &mut usize, last_row_offset: usize, fail_fast: bool, @@ -686,13 +694,7 @@ impl BytecodeCircuitConfig { // 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| { - if cfg!(feature = "poseidon-codehash") { - bytecode.rows[0].code_hash.to_scalar().unwrap() - } else { - rlc::value(&bytecode.rows[0].code_hash.to_le_bytes(), challenge) - } - }); + let code_hash = bytecode.rows[0].code_hash; for (idx, row) in bytecode.rows.iter().enumerate() { if fail_fast && *offset > last_row_offset { @@ -809,7 +811,7 @@ impl BytecodeCircuitConfig { region: &mut Region<'_, F>, push_data_left_is_zero_chip: &IsZeroChip, index_length_diff_is_zero_chip: &IsZeroChip, - empty_hash: Value, + empty_hash: Word>, offset: usize, last_row_offset: usize, ) -> Result<(), Error> { @@ -843,7 +845,7 @@ impl BytecodeCircuitConfig { offset: usize, enable: bool, last: bool, - code_hash: Value, + code_hash: Word>, tag: F, index: F, is_code: F, @@ -902,7 +904,6 @@ impl BytecodeCircuitConfig { )?; } for (name, column, value) in [ - ("code_hash", self.bytecode_table.code_hash, code_hash), ("push_acc", self.push_acc, push_acc), ("push_rlc", self.bytecode_table.push_rlc, push_rlc), ("value_rlc", self.value_rlc, value_rlc), @@ -915,6 +916,13 @@ impl BytecodeCircuitConfig { )?; } + code_hash.assign_advice( + region, + || format!("assign code_hash {offset}"), + self.bytecode_table.code_hash, + offset, + )?; + push_data_left_is_zero_chip.assign( region, offset, @@ -1010,7 +1018,10 @@ impl BytecodeCircuit { let bytecodes: Vec> = block .bytecodes .iter() - .map(|(codehash, b)| unroll_with_codehash(*codehash, b.bytes.clone())) + .map(|(codehash, b)| { + let codehash_word = Word::from(codehash.to_word()).map(Value::known); + unroll_with_codehash(codehash_word, b.bytes.clone()) + }) .collect(); Self::new(bytecodes, bytecode_size) } diff --git a/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs b/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs index 374d20193e..2663ef44bf 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs @@ -5,13 +5,14 @@ use crate::{ not, or, select, }, table::{BytecodeFieldTag, KeccakTable, PoseidonTable}, - util::{Challenges, Expr, SubCircuitConfig}, + util::{word, Challenges, Expr, SubCircuitConfig}, }; use bus_mapping::util::POSEIDON_CODE_HASH_EMPTY; -use eth_types::{Field, ToScalar, ToWord}; +use eth_types::{Field, ToWord}; use gadgets::is_zero::IsZeroChip; use halo2_proofs::{ circuit::{Layouter, Region, Value}, + dev::unwrap_value, plonk::{Advice, Column, ConstraintSystem, Error, Expression, VirtualCells}, poly::Rotation, }; @@ -41,6 +42,7 @@ pub struct ToHashBlockCircuitConfig { padding_shift: Column, field_index: Column, field_index_inv: Column, + poseidon_code_hash: Column, // External table pub(crate) poseidon_table: PoseidonTable, pub(crate) keccak_table: KeccakTable, @@ -65,6 +67,7 @@ impl ToHashBlockCircuitConfig ToHashBlockCircuitConfig ToHashBlockCircuitConfig, inp_i: usize| { debug_assert_eq!(PoseidonTable::INPUT_WIDTH, 2); [ @@ -335,7 +345,8 @@ impl ToHashBlockCircuitConfig ToHashBlockCircuitConfig ToHashBlockCircuitConfig ToHashBlockCircuitConfig ToHashBlockCircuitConfig ToHashBlockCircuitConfig>, Value not implemented `PartialEq`. + assert_eq!(bytecode.to_vec(), unrolled.bytes,); // Verify the unrolling in the circuit test_bytecode_circuit_unrolled::(k, vec![unrolled], true); } @@ -194,7 +191,8 @@ fn bytecode_invalid_hash_data() { // Change the code_hash on the first position (header row) { let mut invalid = unrolled; - invalid.rows[0].code_hash += Word::one(); + invalid.rows[0].code_hash = + word::Word::new([Value::known(Fr::one()), Value::known(Fr::zero())]); log::trace!("bytecode_invalid_hash_data: Change the code_hash on the first position"); test_bytecode_circuit_unrolled::(k, vec![invalid], false); } diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 95bd01bd5a..438eb6a093 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -11,6 +11,7 @@ mod test; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use dev::CopyCircuit as TestCopyCircuit; +use crate::util::word; use array_init::array_init; use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent}; use eth_types::{Field, Word}; @@ -45,10 +46,10 @@ use crate::{ use self::copy_gadgets::{ constrain_address, constrain_bytes_left, constrain_event_rlc_acc, constrain_first_last, - constrain_forward_parameters, constrain_is_pad, constrain_mask, constrain_masked_value, - constrain_must_terminate, constrain_non_pad_non_mask, constrain_rw_counter, - constrain_rw_word_complete, constrain_tag, constrain_value_rlc, constrain_word_index, - constrain_word_rlc, + constrain_forward_parameters, constrain_id, constrain_is_pad, constrain_mask, + constrain_masked_value, constrain_must_terminate, constrain_non_pad_non_mask, + constrain_rw_counter, constrain_rw_word_complete, constrain_tag, constrain_value_rlc, + constrain_word_index, }; /// The current row. @@ -76,9 +77,11 @@ pub struct CopyCircuitConfig { /// The value before the write. pub value_prev: Column, /// The word value for memory lookup. - pub value_word_rlc: Column, + //pub value_word_rlc: Column, + pub value_word: word::Word>, /// The word value for memory lookup, before the write. - pub value_word_rlc_prev: Column, + // pub value_word_rlc_prev: Column, + pub value_word_prev: word::Word>, /// The index of the current byte within a word [0..31]. pub word_index: Column, /// mask indicates when a row is not part of the copy, but it is needed to complete the front @@ -162,8 +165,10 @@ impl SubCircuitConfig for CopyCircuitConfig { let value_prev = meta.advice_column(); // RLC accumulators in the second phase. - let value_word_rlc = meta.advice_column_in(SecondPhase); - let value_word_rlc_prev = meta.advice_column_in(SecondPhase); + // let code_hash = word::Word::new([meta.advice_column(), meta.advice_column()]); + + let value_word = word::Word::new([meta.advice_column(), meta.advice_column()]); + let value_word_prev = word::Word::new([meta.advice_column(), meta.advice_column()]); let value_acc = meta.advice_column_in(SecondPhase); let [is_pad, is_tx_calldata, is_bytecode, is_memory, is_tx_log, is_access_list_address, is_access_list_storage_key] = @@ -236,6 +241,18 @@ impl SubCircuitConfig for CopyCircuitConfig { // Detect the rows which process the last byte of a word. The next word starts at // NEXT_STEP. let is_word_end = is_word_end.expr(); + + constrain_id( + cb, + meta, + //is_bytecode, + is_tx_log, + is_tx_calldata, + is_memory, + id, + is_pad, + ); + let is_tx_log = meta.query_advice(is_tx_log, CURRENT); let is_access_list = meta.query_advice(is_access_list_address, CURRENT) + meta.query_advice(is_access_list_storage_key, CURRENT); @@ -295,23 +312,21 @@ impl SubCircuitConfig for CopyCircuitConfig { constrain_event_rlc_acc(cb, meta, is_last_col, value_acc, rlc_acc, is_bytecode, tag); + // no word_rlc required after word hi lo // Apply the same constraints for the RLCs of words before and after the write. - let word_rlc_both = [(value_word_rlc, value), (value_word_rlc_prev, value_prev)]; - for (word_rlc, value) in word_rlc_both { - constrain_word_rlc( - cb, - meta, - // Not constrain word rlc for access list, since `value` - // saves access list address and `value_prev` saves storage - // key. - is_first.expr() * (1.expr() - is_access_list.expr()), - is_continue.expr() * (1.expr() - is_access_list.expr()), - is_word_end.expr(), - word_rlc, - value, - challenges.evm_word(), - ); - } + // let word_rlc_both = [(value_word, value), (value_word_prev, value_prev)]; + // for (word_rlc, value) in word_rlc_both { + // constrain_word_rlc( + // cb, + // meta, + // is_first.expr(), + // is_continue.expr(), + // is_word_end.expr(), + // word_rlc, + // value, + // challenges.evm_word(), + // ); + // } constrain_word_index( cb, @@ -373,12 +388,17 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(rw_counter, CURRENT), not::expr(meta.query_selector(q_step)), RwTableTag::Memory.expr(), - meta.query_advice(id, CURRENT), // call_id + meta.query_advice(id.lo(), Rotation::cur()), // call_id addr_slot, 0.expr(), 0.expr(), - meta.query_advice(value_word_rlc, CURRENT), - meta.query_advice(value_word_rlc_prev, CURRENT), + 0.expr(), + meta.query_advice(value_word.lo(), CURRENT), + meta.query_advice(value_word.hi(), CURRENT), + meta.query_advice(value_word_prev.lo(), CURRENT), + meta.query_advice(value_word_prev.hi(), CURRENT), + 0.expr(), + 0.expr(), 0.expr(), 0.expr(), ] @@ -400,11 +420,17 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(rw_counter, CURRENT), 1.expr(), RwTableTag::TxLog.expr(), - meta.query_advice(id, CURRENT), // tx_id - addr_slot, // byte_index || field_tag || log_id + //meta.query_advice(id, CURRENT), // tx_id + meta.query_advice(id.lo(), CURRENT), // tx_id + addr_slot, // byte_index || field_tag || log_id + 0.expr(), + 0.expr(), + 0.expr(), + meta.query_advice(value_word.lo(), CURRENT), + meta.query_advice(value_word.hi(), CURRENT), + 0.expr(), 0.expr(), 0.expr(), - meta.query_advice(value_word_rlc, CURRENT), 0.expr(), 0.expr(), 0.expr(), @@ -422,7 +448,8 @@ impl SubCircuitConfig for CopyCircuitConfig { vec![ 1.expr(), - meta.query_advice(id, CURRENT), + meta.query_advice(id.lo(), CURRENT), + meta.query_advice(id.hi(), CURRENT), BytecodeFieldTag::Byte.expr(), meta.query_advice(addr, CURRENT), meta.query_advice(value, CURRENT), @@ -440,7 +467,7 @@ impl SubCircuitConfig for CopyCircuitConfig { vec![ 1.expr(), - meta.query_advice(id, CURRENT), + meta.query_advice(id.lo(), CURRENT), TxContextFieldTag::CallData.expr(), meta.query_advice(addr, CURRENT), meta.query_advice(value, CURRENT), @@ -455,9 +482,10 @@ impl SubCircuitConfig for CopyCircuitConfig { let cond = meta.query_fixed(q_enable, CURRENT) * meta.query_advice(is_access_list_address, CURRENT); - let tx_id = meta.query_advice(id, CURRENT); + let tx_id = meta.query_advice(id.lo(), CURRENT); let index = meta.query_advice(addr, CURRENT); - let address = meta.query_advice(value_word_rlc, CURRENT); + //TODO: update per value_word columns + let address = meta.query_advice(value_word.lo(), CURRENT); vec![ 1.expr(), @@ -465,6 +493,7 @@ impl SubCircuitConfig for CopyCircuitConfig { TxContextFieldTag::AccessListAddress.expr(), index, address, + // address ? ] .into_iter() .zip(tx_table.table_exprs(meta)) @@ -476,8 +505,9 @@ impl SubCircuitConfig for CopyCircuitConfig { let cond = meta.query_fixed(q_enable, CURRENT) * meta.query_advice(is_access_list_address, CURRENT); - let tx_id = meta.query_advice(id, CURRENT); - let address = meta.query_advice(value_word_rlc, CURRENT); + // let address = meta.query_advice(value, CURRENT); + let tx_id = meta.query_advice(id.lo(), CURRENT); + let address = meta.query_advice(value_word.lo(), CURRENT); let is_warm_prev = meta.query_advice(value_prev, CURRENT); vec![ @@ -489,8 +519,13 @@ impl SubCircuitConfig for CopyCircuitConfig { address, // access list address 0.expr(), 0.expr(), - 1.expr(), // is_warm - is_warm_prev, // is_warm_prev + 0.expr(), + 1.expr(), // is_warm_lo + 0.expr(), // is_warm_hi + is_warm_prev, // is_warm_prev_lo + 0.expr(), // is_warm_prev_hi + 0.expr(), + 0.expr(), 0.expr(), 0.expr(), ] @@ -504,9 +539,11 @@ impl SubCircuitConfig for CopyCircuitConfig { let cond = meta.query_fixed(q_enable, CURRENT) * meta.query_advice(is_access_list_storage_key, CURRENT); - let tx_id = meta.query_advice(id, CURRENT); + let tx_id = meta.query_advice(id.lo(), CURRENT); + // index in copy table 'value' column ? let index = meta.query_advice(value, CURRENT); - let storage_key = meta.query_advice(value_word_rlc_prev, CURRENT); + // value_word column? put address in ? TODO: check lo() covers storage key ? + let storage_key = meta.query_advice(value_word_prev.lo(), CURRENT); vec![ 1.expr(), @@ -525,9 +562,12 @@ impl SubCircuitConfig for CopyCircuitConfig { let cond = meta.query_fixed(q_enable, CURRENT) * meta.query_advice(is_access_list_storage_key, CURRENT); - let tx_id = meta.query_advice(id, CURRENT); - let address = meta.query_advice(value_word_rlc, CURRENT); - let storage_key = meta.query_advice(value_word_rlc_prev, CURRENT); + let tx_id = meta.query_advice(id.lo(), CURRENT); + let address = meta.query_advice(value_word.lo(), CURRENT); + let storage_key_lo = meta.query_advice(value_word_prev.lo(), CURRENT); + let storage_key_hi = meta.query_advice(value_word_prev.hi(), CURRENT); + + // reserve ? let is_warm_prev = meta.query_advice(value_prev, CURRENT); vec![ @@ -538,9 +578,14 @@ impl SubCircuitConfig for CopyCircuitConfig { tx_id, address, // access list address 0.expr(), - storage_key, // access list storage key - 1.expr(), // is_warm - is_warm_prev, // is_warm_prev + storage_key_lo, // access list storage_key_lo + storage_key_hi, // access list storage_key_hi + 1.expr(), // is_warm_lo + 0.expr(), // is_warm_hi + is_warm_prev, // is_warm_prev_lo + 0.expr(), // is_warm_prev_hi + 0.expr(), + 0.expr(), 0.expr(), 0.expr(), ] @@ -555,8 +600,8 @@ impl SubCircuitConfig for CopyCircuitConfig { is_last, value, value_prev, - value_word_rlc, - value_word_rlc_prev, + value_word, + value_word_prev, word_index, mask, front_mask, @@ -631,8 +676,10 @@ impl CopyCircuitConfig { self.is_last, self.value, self.value_prev, - self.value_word_rlc, - self.value_word_rlc_prev, + self.value_word.lo(), + self.value_word.hi(), + self.value_word_prev.lo(), + self.value_word_prev.hi(), self.value_acc, self.is_pad, self.mask, @@ -655,7 +702,7 @@ impl CopyCircuitConfig { // lt chip if is_read { - let addr = table_row[2].0; + let addr = table_row[3].0; is_src_end_chip.assign( region, *offset, @@ -671,8 +718,8 @@ impl CopyCircuitConfig { Value::known(F::from(31u64)), )?; - let pad = unwrap_value(circuit_row[6].0); - let mask = unwrap_value(circuit_row[7].0); + let pad = unwrap_value(circuit_row[8].0); + let mask = unwrap_value(circuit_row[9].0); let non_pad_non_mask = pad.is_zero_vartime() && mask.is_zero_vartime(); region.assign_advice( || format!("non_pad_non_mask at row: {offset}"), @@ -758,8 +805,10 @@ impl CopyCircuitConfig { region.name_column(|| "is_last", self.is_last); region.name_column(|| "value", self.value); region.name_column(|| "value_prev", self.value_prev); - region.name_column(|| "value_word_rlc", self.value_word_rlc); - region.name_column(|| "value_word_rlc_prev", self.value_word_rlc_prev); + region.name_column(|| "value_word_lo", self.value_word.lo()); + region.name_column(|| "value_word_hi", self.value_word.hi()); + region.name_column(|| "value_word_lo", self.value_word_prev.lo()); + region.name_column(|| "value_word_hi", self.value_word_prev.hi()); region.name_column(|| "word_index", self.word_index); region.name_column(|| "mask", self.mask); region.name_column(|| "front_mask", self.front_mask); @@ -858,8 +907,14 @@ impl CopyCircuitConfig { )?; // id region.assign_advice( - || format!("assign id {}", *offset), - self.copy_table.id, + || format!("assign id lo {}", *offset), + self.copy_table.id.lo(), + *offset, + || Value::known(F::zero()), + )?; + region.assign_advice( + || format!("assign id hi {}", *offset), + self.copy_table.id.hi(), *offset, || Value::known(F::zero()), )?; @@ -900,15 +955,27 @@ impl CopyCircuitConfig { )?; // value_word_rlc region.assign_advice( - || format!("assign value_word_rlc {}", *offset), - self.value_word_rlc, + || format!("assign value_word_lo {}", *offset), + self.value_word.lo(), + *offset, + || Value::known(F::zero()), + )?; + region.assign_advice( + || format!("assign value_word_hi {}", *offset), + self.value_word.hi(), *offset, || Value::known(F::zero()), )?; // value_word_rlc_prev region.assign_advice( - || format!("assign value_word_rlc_prev {}", *offset), - self.value_word_rlc_prev, + || format!("assign value_word_prev_lo {}", *offset), + self.value_word_prev.lo(), + *offset, + || Value::known(F::zero()), + )?; + region.assign_advice( + || format!("assign value_word_prev_hi {}", *offset), + self.value_word_prev.hi(), *offset, || Value::known(F::zero()), )?; diff --git a/zkevm-circuits/src/copy_circuit/copy_gadgets.rs b/zkevm-circuits/src/copy_circuit/copy_gadgets.rs index f9e3040b7e..cb2658ddb6 100644 --- a/zkevm-circuits/src/copy_circuit/copy_gadgets.rs +++ b/zkevm-circuits/src/copy_circuit/copy_gadgets.rs @@ -4,11 +4,14 @@ use eth_types::Field; use gadgets::{ binary_number::BinaryNumberConfig, is_equal::IsEqualConfig, - util::{and, not, select, sum, Expr}, + util::{and, not, or, select, sum, Expr}, }; use halo2_proofs::plonk::{Advice, Column, ConstraintSystem, Expression, Fixed, VirtualCells}; -use crate::evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}; +use crate::{ + evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + util::word, +}; #[allow(clippy::too_many_arguments)] pub fn constrain_tag( @@ -95,16 +98,16 @@ pub fn constrain_forward_parameters( cb: &mut BaseConstraintBuilder, meta: &mut VirtualCells<'_, F>, is_continue: Expression, - id: Column, + id: word::Word>, tag: BinaryNumberConfig, src_addr_end: Column, ) { cb.condition(is_continue.expr(), |cb| { // Forward other fields to the next step. - cb.require_equal( + cb.require_equal_word( "rows[0].id == rows[2].id", - meta.query_advice(id, CURRENT), - meta.query_advice(id, NEXT_STEP), + id.map(|limb| meta.query_advice(limb, CURRENT)), + id.map(|limb| meta.query_advice(limb, NEXT_STEP)), ); cb.require_equal( "rows[0].tag == rows[2].tag", @@ -489,6 +492,28 @@ pub fn constrain_address( }); } +/// constrain id(src_id, dest_id). id_hi = 0 +pub fn constrain_id( + cb: &mut BaseConstraintBuilder, + meta: &mut VirtualCells<'_, F>, + //_is_bytecode: Column, + is_tx_log: Column, + is_tx_calldata: Column, + is_memory: Column, + id: word::Word>, + is_pad: Column, +) { + let cond = or::expr([ + //meta.query_advice(is_bytecode, CURRENT), + meta.query_advice(is_tx_log, CURRENT), + meta.query_advice(is_tx_calldata, CURRENT), + meta.query_advice(is_memory, CURRENT), + ]) * not::expr(meta.query_advice(is_pad, CURRENT)); + cb.condition(cond, |cb| { + cb.require_zero("id_hi == 0", meta.query_advice(id.hi(), CURRENT)) + }); +} + /// Update the RW counter and verify that all RWs requested by the event are consumed. #[allow(clippy::too_many_arguments)] pub fn constrain_rw_counter( diff --git a/zkevm-circuits/src/copy_circuit/util.rs b/zkevm-circuits/src/copy_circuit/util.rs index 9bb2b152ed..b4efa2b22e 100644 --- a/zkevm-circuits/src/copy_circuit/util.rs +++ b/zkevm-circuits/src/copy_circuit/util.rs @@ -1,4 +1,4 @@ -use crate::evm_circuit::util::rlc; +use crate::{evm_circuit::util::rlc, util::word}; use bus_mapping::circuit_input_builder::NumberOrHash; use eth_types::Field; use halo2_proofs::circuit::Value; @@ -29,3 +29,11 @@ pub fn number_or_hash_to_field(v: &NumberOrHash, challenge: Value) } } } + +/// Encode the type `NumberOrHash` into a field element +pub fn number_or_hash_to_word(v: &NumberOrHash) -> word::Word> { + match v { + NumberOrHash::Number(n) => word::Word::from(*n as u64).into_value(), + NumberOrHash::Hash(h) => word::Word::from(*h).into_value(), + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index dae0031d99..39edc60191 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -592,6 +592,11 @@ impl ExecutionConfig { address_gadget: configure_gadget!(), balance_gadget: configure_gadget!(), blockhash_gadget: configure_gadget!(), + block_ctx_u64_gadget: configure_gadget!(), + block_ctx_u160_gadget: configure_gadget!(), + block_ctx_u256_gadget: configure_gadget!(), + #[cfg(feature = "scroll")] + difficulty_gadget: configure_gadget!(), exp_gadget: configure_gadget!(), sar_gadget: configure_gadget!(), extcodecopy_gadget: configure_gadget!(), @@ -608,11 +613,6 @@ impl ExecutionConfig { sstore_gadget: configure_gadget!(), stop_gadget: configure_gadget!(), swap_gadget: configure_gadget!(), - block_ctx_u64_gadget: configure_gadget!(), - block_ctx_u160_gadget: configure_gadget!(), - block_ctx_u256_gadget: configure_gadget!(), - #[cfg(feature = "scroll")] - difficulty_gadget: configure_gadget!(), // error gadgets error_oog_constant: configure_gadget!(), error_oog_static_memory_gadget: configure_gadget!(), @@ -1587,6 +1587,7 @@ impl ExecutionConfig { #[cfg(feature = "scroll")] ExecutionState::DIFFICULTY => assign_exec_step!(self.difficulty_gadget), ExecutionState::BLOCKHASH => assign_exec_step!(self.blockhash_gadget), + ExecutionState::SELFBALANCE => assign_exec_step!(self.selfbalance_gadget), ExecutionState::CREATE => assign_exec_step!(self.create_gadget), ExecutionState::CREATE2 => assign_exec_step!(self.create2_gadget), @@ -1773,10 +1774,7 @@ impl ExecutionConfig { .rw_indices .iter() .map(|rw_idx| block.rws[*rw_idx]) - .map(|rw| { - rw.table_assignment_aux(evm_randomness) - .rlc(lookup_randomness) - }) + .map(|rw| rw.table_assignment_aux().unwrap().rlc(lookup_randomness)) .fold(BTreeSet::::new(), |mut set, value| { set.insert(value); set @@ -1797,7 +1795,8 @@ impl ExecutionConfig { idx, block.rws[*rw_idx], block.rws[*rw_idx] - .table_assignment_aux(evm_randomness) + .table_assignment() + .unwrap() .rlc(lookup_randomness) ); } @@ -1860,8 +1859,8 @@ impl ExecutionConfig { }; let rw_idx = step.rw_indices[idx]; let rw = block.rws[rw_idx]; - let table_assignments = rw.table_assignment_aux(evm_randomness); - let rlc = table_assignments.rlc(lookup_randomness); + let table_assignments = rw.table_assignment_aux(); + let rlc = table_assignments.unwrap().rlc(lookup_randomness); if !rlc_assignments.contains(value) { log_ctx(&assigned_rw_values); diff --git a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs index fb6e457e27..ba9f506b9d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs +++ b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs @@ -6,11 +6,14 @@ use crate::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, math_gadget::{AddWordsGadget, PairSelectGadget}, - select, CachedRegion, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -35,9 +38,9 @@ impl ExecutionGadget for AddSubGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let c = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); + let c = cb.query_word32(); let add_words = AddWordsGadget::construct(cb, [a.clone(), b.clone()], c.clone()); // Swap a and c if opcode is SUB @@ -50,9 +53,9 @@ impl ExecutionGadget for AddSubGadget { // ADD: Pop a and b from the stack, push c on the stack // SUB: Pop c and b from the stack, push a on the stack - cb.stack_pop(select::expr(is_sub.expr().0, c.expr(), a.expr())); - cb.stack_pop(b.expr()); - cb.stack_push(select::expr(is_sub.expr().0, a.expr(), c.expr())); + cb.stack_pop(Word::select(is_sub.expr().0, c.to_word(), a.to_word())); + cb.stack_pop(b.to_word()); + cb.stack_push(Word::select(is_sub.expr().0, a.to_word(), c.to_word())); // State transition let step_state_transition = StepStateTransition { diff --git a/zkevm-circuits/src/evm_circuit/execution/addmod.rs b/zkevm-circuits/src/evm_circuit/execution/addmod.rs index d5c9c55e39..c548e5b357 100644 --- a/zkevm-circuits/src/evm_circuit/execution/addmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/addmod.rs @@ -9,42 +9,48 @@ use crate::{ Transition::Delta, }, math_gadget::{ - AddWordsGadget, CmpWordsGadget, IsZeroGadget, MulAddWords512Gadget, + AddWordsGadget, CmpWordsGadget, IsZeroWordGadget, MulAddWords512Gadget, MulAddWordsGadget, }, - not, CachedRegion, Word, + not, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian, U256, U512}; -use halo2_proofs::plonk::Error; +use eth_types::{Field, U256, U512}; +use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct AddModGadget { same_context: SameContextGadget, - a: Word, - b: Word, - r: Word, - n: Word, + a: Word32Cell, + b: Word32Cell, + r: Word32Cell, + n: Word32Cell, - k: Word, - d: Word, - a_reduced: Word, + k: Word32Cell, + d: Word32Cell, + a_reduced: Word32Cell, muladd_k_n_areduced: MulAddWordsGadget, sum_areduced_b: AddWordsGadget, - sum_areduced_b_overflow: Word, + sum_areduced_b_overflow: Word32Cell, muladd_d_n_r: MulAddWords512Gadget, - n_is_zero: IsZeroGadget, - cmp_r_n: CmpWordsGadget, - cmp_areduced_n: CmpWordsGadget, + //n_is_zero: IsZeroGadget, + n_is_zero: IsZeroWordGadget>, + //cmp_r_n: CmpWordsGadget, + cmp_r_n: CmpWordsGadget, Word32Cell>, + //cmp_areduced_n: CmpWordsGadget, + cmp_areduced_n: CmpWordsGadget, Word32Cell>, } impl ExecutionGadget for AddModGadget { @@ -56,20 +62,21 @@ impl ExecutionGadget for AddModGadget { let opcode = cb.query_cell(); // values got from stack (original r is modified if n==0) - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let n = cb.query_word_rlc(); - let r = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); + let n = cb.query_word32(); + let r = cb.query_word32(); // auxiliar witness - let k = cb.query_word_rlc(); - let a_reduced = cb.query_word_rlc(); - let d = cb.query_word_rlc(); + let k = cb.query_word32(); + let a_reduced = cb.query_word32(); + let d = cb.query_word32(); - let n_is_zero = IsZeroGadget::construct(cb, n.clone().expr()); + let n_is_zero = IsZeroWordGadget::construct(cb, &n); // 1. check k * N + a_reduced == a without overflow - let muladd_k_n_areduced = MulAddWordsGadget::construct(cb, [&k, &n, &a_reduced, &a]); + let muladd_k_n_areduced = + MulAddWordsGadget::construct(cb, [&k, &n.clone(), &a_reduced, &a]); cb.require_zero( "k * N + a_reduced does not overflow", muladd_k_n_areduced.overflow(), @@ -77,24 +84,25 @@ impl ExecutionGadget for AddModGadget { // 2. check d * N + r == a_reduced + b, only checking carry if n != 0 let sum_areduced_b = { - let sum = cb.query_word_rlc(); + let sum = cb.query_word32(); AddWordsGadget::construct(cb, [a_reduced.clone(), b.clone()], sum) }; - let sum_areduced_b_overflow = cb.query_word_rlc(); + let sum_areduced_b_overflow = cb.query_word32(); let muladd_d_n_r = MulAddWords512Gadget::construct( cb, [&d, &n, &sum_areduced_b_overflow, sum_areduced_b.sum()], Some(&r), ); - cb.require_equal( + cb.require_equal_word( "check a_reduced + b 512 bit carry if n != 0", - sum_areduced_b_overflow.expr(), - sum_areduced_b.carry().clone().unwrap().expr() * not::expr(n_is_zero.expr()), + sum_areduced_b_overflow.to_word(), + Word::from_lo_unchecked(sum_areduced_b.carry().clone().unwrap().expr()) + .mul_selector(not::expr(n_is_zero.expr())), ); - let cmp_r_n = CmpWordsGadget::construct(cb, &r, &n); - let cmp_areduced_n = CmpWordsGadget::construct(cb, &a_reduced, &n); + let cmp_r_n = CmpWordsGadget::construct(cb, r.clone(), n.clone()); + let cmp_areduced_n = CmpWordsGadget::construct(cb, a_reduced.clone(), n.clone()); // 3. r < n and a_reduced < n if n > 0 cb.require_zero( @@ -104,10 +112,14 @@ impl ExecutionGadget for AddModGadget { // pop/push values // take care that if n==0 pushed value for r should be zero also - cb.stack_pop(a.expr()); - cb.stack_pop(b.expr()); - cb.stack_pop(n.expr()); - cb.stack_push(r.expr() * not::expr(n_is_zero.expr())); + cb.stack_pop(a.to_word()); + cb.stack_pop(b.to_word()); + cb.stack_pop(n.clone().to_word()); + cb.stack_push( + r.clone() + .to_word() + .mul_selector(not::expr(n_is_zero.expr())), + ); // State transition let step_state_transition = StepStateTransition { @@ -155,9 +167,9 @@ impl ExecutionGadget for AddModGadget { .map(|idx| block.rws[idx].stack_value()); // assing a,b & n stack values - self.a.assign(region, offset, Some(a.to_le_bytes()))?; - self.b.assign(region, offset, Some(b.to_le_bytes()))?; - self.n.assign(region, offset, Some(n.to_le_bytes()))?; + self.a.assign_u256(region, offset, a)?; + self.b.assign_u256(region, offset, b)?; + self.n.assign_u256(region, offset, n)?; // compute a_reduced,k,d,a_reduced_plus_b,a_reduced_plus_b_overflow,r values let a_reduced; @@ -189,11 +201,10 @@ impl ExecutionGadget for AddModGadget { // rest of values and gadgets - self.r.assign(region, offset, Some(r.to_le_bytes()))?; - self.k.assign(region, offset, Some(k.to_le_bytes()))?; - self.d.assign(region, offset, Some(d.to_le_bytes()))?; - self.a_reduced - .assign(region, offset, Some(a_reduced.to_le_bytes()))?; + self.r.assign_u256(region, offset, r)?; + self.k.assign_u256(region, offset, k)?; + self.d.assign_u256(region, offset, d)?; + self.a_reduced.assign_u256(region, offset, a_reduced)?; self.muladd_k_n_areduced .assign(region, offset, [k, n, a_reduced, a])?; @@ -201,11 +212,8 @@ impl ExecutionGadget for AddModGadget { self.sum_areduced_b .assign(region, offset, [a_reduced, b], a_reduced_plus_b)?; - self.sum_areduced_b_overflow.assign( - region, - offset, - Some(a_reduced_plus_b_overflow.to_le_bytes()), - )?; + self.sum_areduced_b_overflow + .assign_u256(region, offset, a_reduced_plus_b_overflow)?; self.muladd_d_n_r.assign( region, offset, @@ -217,7 +225,7 @@ impl ExecutionGadget for AddModGadget { self.cmp_areduced_n.assign(region, offset, a_reduced, n)?; self.n_is_zero - .assign_value(region, offset, region.word_rlc(n))?; + .assign_value(region, offset, Value::known(Word::from(n)))?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/address.rs b/zkevm-circuits/src/evm_circuit/execution/address.rs index 508cabcd61..df6ef22788 100644 --- a/zkevm-circuits/src/evm_circuit/execution/address.rs +++ b/zkevm-circuits/src/evm_circuit/execution/address.rs @@ -1,27 +1,28 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState, util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToAddress, ToLittleEndian}; +use eth_types::{Field, ToAddress}; use halo2_proofs::plonk::Error; -use std::convert::TryInto; #[derive(Clone, Debug)] pub(crate) struct AddressGadget { same_context: SameContextGadget, - address: RandomLinearCombination, + address: WordCell, } impl ExecutionGadget for AddressGadget { @@ -30,17 +31,11 @@ impl ExecutionGadget for AddressGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ADDRESS; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let address = cb.query_word_rlc(); + let address = cb.query_word_unchecked(); // Lookup callee address in call context. - cb.call_context_lookup( - false.expr(), - None, - CallContextFieldTag::CalleeAddress, - from_bytes::expr(&address.cells), - ); - - cb.stack_push(address.expr()); + cb.call_context_lookup_read(None, CallContextFieldTag::CalleeAddress, address.to_word()); + cb.stack_push(address.to_word()); let step_state_transition = StepStateTransition { rw_counter: Delta(2.expr()), @@ -73,15 +68,8 @@ impl ExecutionGadget for AddressGadget { let address = block.rws[step.rw_indices[1]].stack_value(); debug_assert_eq!(call.callee_address, address.to_address()); - self.address.assign( - region, - offset, - Some( - address.to_le_bytes()[..N_BYTES_ACCOUNT_ADDRESS] - .try_into() - .unwrap(), - ), - )?; + self.address + .assign_h160(region, offset, address.to_address())?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/balance.rs b/zkevm-circuits/src/evm_circuit/execution/balance.rs index 3a1995d664..3e162c1172 100644 --- a/zkevm-circuits/src/evm_circuit/execution/balance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/balance.rs @@ -10,27 +10,32 @@ use crate::{ Transition::Delta, }, from_bytes, - math_gadget::IsZeroGadget, - not, select, CachedRegion, Cell, Word, + math_gadget::IsZeroWordGadget, + not, select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, - util::Expr, + util::{ + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian}; +use eth_types::{evm_types::GasCost, Field}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct BalanceGadget { same_context: SameContextGadget, - address_word: Word, + // use Word32 instead of AccountAddress since address popped from stack can be bigger than + // normal H160 type. + address: Word32Cell, reversion_info: ReversionInfo, tx_id: Cell, is_warm: Cell, - code_hash: Cell, - not_exists: IsZeroGadget, - balance: Cell, + code_hash: WordCell, + not_exists: IsZeroWordGadget>, + balance: Word32Cell, } impl ExecutionGadget for BalanceGadget { @@ -39,34 +44,47 @@ impl ExecutionGadget for BalanceGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BALANCE; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let address_word = cb.query_word_rlc(); - let address = from_bytes::expr(&address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); - cb.stack_pop(address_word.expr()); + // use Word32 instead of AccountAddress since address popped from stack can be bigger than + // normal H160 type. + let address_word = cb.query_word32(); + + cb.stack_pop(address_word.to_word()); + // valid address + let address = from_bytes::expr(&address_word.limbs[..N_BYTES_ACCOUNT_ADDRESS]); let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let mut reversion_info = cb.reversion_info_read(None); let is_warm = cb.query_bool(); - cb.account_access_list_write( + cb.account_access_list_write_unchecked( tx_id.expr(), - address.expr(), + Word::from_lo_unchecked(address.clone()), 1.expr(), is_warm.expr(), Some(&mut reversion_info), ); - let code_hash = cb.query_cell_phase2(); + let code_hash = cb.query_word_unchecked(); // For non-existing accounts the code_hash must be 0 in the rw_table. - cb.account_read(address.expr(), AccountFieldTag::CodeHash, code_hash.expr()); - let not_exists = IsZeroGadget::construct(cb, code_hash.expr()); + cb.account_read( + Word::from_lo_unchecked(address.clone()), + AccountFieldTag::CodeHash, + code_hash.to_word(), + ); + + let not_exists = IsZeroWordGadget::construct(cb, &code_hash); let exists = not::expr(not_exists.expr()); - let balance = cb.query_cell_phase2(); + let balance = cb.query_word32(); cb.condition(exists.expr(), |cb| { - cb.account_read(address.expr(), AccountFieldTag::Balance, balance.expr()); + cb.account_read( + Word::from_lo_unchecked(address), + AccountFieldTag::Balance, + balance.to_word(), + ); }); cb.condition(not_exists.expr(), |cb| { - cb.require_zero("balance is zero when non_exists", balance.expr()); + cb.require_zero_word("balance is zero when non_exists", balance.to_word()); }); - cb.stack_push(balance.expr()); + cb.stack_push(balance.to_word()); let gas_cost = select::expr( is_warm.expr(), @@ -88,7 +106,7 @@ impl ExecutionGadget for BalanceGadget { Self { same_context, - address_word, + address: address_word, reversion_info, tx_id, is_warm, @@ -110,9 +128,9 @@ impl ExecutionGadget for BalanceGadget { self.same_context.assign_exec_step(region, offset, step)?; let address = block.rws[step.rw_indices[0]].stack_value(); - self.address_word - .assign(region, offset, Some(address.to_le_bytes()))?; - + // self.address + // .assign_h160(region, offset, address.to_address())?; + self.address.assign_u256(region, offset, address)?; self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; @@ -128,18 +146,15 @@ impl ExecutionGadget for BalanceGadget { .assign(region, offset, Value::known(F::from(is_warm)))?; let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; - self.code_hash - .assign(region, offset, region.code_hash(code_hash))?; - self.not_exists - .assign_value(region, offset, region.code_hash(code_hash))?; + self.code_hash.assign_u256(region, offset, code_hash)?; + self.not_exists.assign_u256(region, offset, code_hash)?; let balance = if code_hash.is_zero() { eth_types::Word::zero() } else { block.rws[step.rw_indices[6]].account_value_pair().0 }; - self.balance - .assign(region, offset, region.word_rlc(balance))?; + self.balance.assign_u256(region, offset, balance)?; Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index 7f6a794386..3a22ed5657 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_U64, N_BYTES_WORD}, + param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_U64}, step::ExecutionState, util::{ and, @@ -15,11 +15,11 @@ use crate::{ }, from_bytes, math_gadget::{ - ConstantDivisionGadget, ContractCreateGadget, IsEqualGadget, IsZeroGadget, - LtGadget, MulWordByU64Gadget, RangeCheckGadget, + ConstantDivisionGadget, ContractCreateGadget, IsEqualWordGadget, IsZeroGadget, + IsZeroWordGadget, LtWordGadget, MulWordByU64Gadget, RangeCheckGadget, }, precompile_gadget::PrecompileGadget, - CachedRegion, Cell, StepRws, Word, + AccountAddress, CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -27,16 +27,24 @@ use crate::{ AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, RwTableTag, TxFieldTag as TxContextFieldTag, }, + util::{ + word::{self, Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; use array_init::array_init; use bus_mapping::{ circuit_input_builder::CopyDataType, precompile::{is_precompiled, PrecompileCalls}, + state_db::CodeDB, }; -use eth_types::{Address, Field, ToLittleEndian, ToScalar, U256}; +use eth_types::{Address, Field, ToScalar, ToWord, U256}; use ethers_core::utils::{get_contract_address, keccak256, rlp::RlpStream}; -use gadgets::util::{expr_from_bytes, not, select, Expr}; -use halo2_proofs::{circuit::Value, plonk::Error}; +use gadgets::util::{not, select}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; // For Shanghai, EIP-3651 (Warm COINBASE) adds 1 write op for coinbase. #[cfg(feature = "shanghai")] @@ -53,16 +61,16 @@ pub(crate) struct BeginTxGadget { sender_nonce: Cell, tx_nonce: Cell, tx_gas: Cell, - tx_gas_price: Word, + tx_gas_price: Word32Cell, mul_gas_fee_by_gas: MulWordByU64Gadget, - tx_fee: Word, - tx_caller_address: Cell, - tx_caller_address_is_zero: IsZeroGadget, - tx_callee_address: Cell, - tx_callee_address_is_zero: IsZeroGadget, - call_callee_address: Cell, + tx_fee: Word32Cell, + tx_caller_address: WordCell, + tx_caller_address_is_zero: IsZeroWordGadget>, + tx_callee_address: WordCell, + tx_callee_address_is_zero: IsZeroWordGadget>, + call_callee_address: AccountAddress, tx_is_create: Cell, - tx_value: Word, + tx_value: Word32Cell, tx_call_data_length: Cell, is_call_data_empty: IsZeroGadget, tx_call_data_word_length: ConstantDivisionGadget, @@ -73,21 +81,24 @@ pub(crate) struct BeginTxGadget { intrinsic_gas_cost: Cell, sufficient_gas_left: RangeCheckGadget, transfer_with_gas_fee: TransferWithGasFeeGadget, - account_code_hash: Cell, - account_code_hash_is_empty: IsEqualGadget, - account_code_hash_is_zero: IsZeroGadget, + account_code_hash: WordCell, + account_code_hash_is_empty: IsEqualWordGadget>, Word>>, + account_code_hash_is_zero: IsZeroWordGadget>, #[cfg(feature = "scroll")] - account_keccak_code_hash: Cell, - call_code_hash: Cell, - call_code_hash_is_empty: IsEqualGadget, - call_code_hash_is_zero: IsZeroGadget, - is_precompile_lt: LtGadget, + account_keccak_code_hash: WordCell, // poseidon hash + call_code_hash: WordCell, + call_code_hash_is_empty: IsEqualWordGadget>, Word>>, + call_code_hash_is_zero: IsZeroWordGadget>, + //is_precompile_lt: LtGadget, + is_precompile_lt: LtWordGadget, precompile_gadget: PrecompileGadget, precompile_input_len: Cell, // the number of input bytes taken for the precompile call. precompile_input_bytes_rlc: Cell, // input bytes to precompile call. /// Keccak256(RLP([tx_caller_address, tx_nonce])) - caller_nonce_hash_bytes: [Cell; N_BYTES_WORD], - keccak_code_hash: Cell, + caller_nonce_hash_bytes: Word32Cell, + + // keccak_code_hash: WordCell, + keccak_code_hash: Word32Cell, init_code_rlc: Cell, /// RLP gadget for CREATE address. create: ContractCreateGadget, @@ -99,6 +110,7 @@ pub(crate) struct BeginTxGadget { is_coinbase_warm: Cell, are_precompile_warm: [Cell; PRECOMPILE_COUNT], // EIP-3651 (Warm COINBASE) for Shanghai + //coinbase: WordCell, coinbase: Cell, tx_l1_fee: TxL1FeeGadget, tx_l1_msg: TxL1MsgGadget, @@ -120,18 +132,15 @@ impl ExecutionGadget for BeginTxGadget { 1.expr(), Some(call_id.expr()), CallContextFieldTag::TxId, - tx_id.expr(), + Word::from_lo_unchecked(tx_id.expr()), ); // rwc_delta += 1 let sender_nonce = cb.query_cell(); - - let [tx_type, tx_nonce, tx_gas, tx_caller_address, tx_callee_address, tx_is_create, tx_call_data_length, tx_call_data_gas_cost, tx_data_gas_cost] = + let [tx_type, tx_nonce, tx_gas, tx_is_create, tx_call_data_length, tx_call_data_gas_cost, tx_data_gas_cost] = [ TxContextFieldTag::TxType, TxContextFieldTag::Nonce, TxContextFieldTag::Gas, - TxContextFieldTag::CallerAddress, - TxContextFieldTag::CalleeAddress, TxContextFieldTag::IsCreate, TxContextFieldTag::CallDataLength, TxContextFieldTag::CallDataGasCost, @@ -139,10 +148,13 @@ impl ExecutionGadget for BeginTxGadget { ] .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); + let [tx_caller_address, tx_callee_address] = + [cb.query_word_unchecked(), cb.query_word_unchecked()]; + let tx_access_list = TxAccessListGadget::construct(cb, tx_id.expr(), tx_type.expr()); let is_call_data_empty = IsZeroGadget::construct(cb, tx_call_data_length.expr()); - let tx_l1_msg = TxL1MsgGadget::construct(cb, tx_type.expr(), tx_caller_address.expr()); + let tx_l1_msg = TxL1MsgGadget::construct(cb, tx_type.expr(), tx_caller_address.to_word()); let tx_l1_fee = cb.condition(not::expr(tx_l1_msg.is_l1_msg()), |cb| { cb.require_equal( "tx.nonce == sender.nonce", @@ -163,29 +175,41 @@ impl ExecutionGadget for BeginTxGadget { // the cost caused by l1 let l1_fee_cost = select::expr(tx_l1_msg.is_l1_msg(), 0.expr(), tx_l1_fee.tx_l1_fee()); + // change to call_context_lookup_write helper ? cb.call_context_lookup( 1.expr(), Some(call_id.expr()), CallContextFieldTag::L1Fee, - l1_fee_cost.expr(), + Word::from_lo_unchecked(l1_fee_cost.expr()), ); // rwc_delta += 1 - let mut reversion_info = cb.reversion_info_write(None); // rwc_delta += 2 - let is_persistent = reversion_info.is_persistent(); - cb.call_context_lookup( - 1.expr(), + let mut reversion_info = cb.reversion_info_write_unchecked(None); + // rwc_delta += 2 + cb.call_context_lookup_write( Some(call_id.expr()), CallContextFieldTag::IsSuccess, - is_persistent.expr(), + Word::from_lo_unchecked(reversion_info.is_persistent()), ); // rwc_delta += 1 - let tx_caller_address_is_zero = IsZeroGadget::construct(cb, tx_caller_address.expr()); + // enable this after tx table changed to word type + let tx_value = cb.tx_context_as_word32(tx_id.expr(), TxContextFieldTag::Value, None); + + let tx_gas_price = cb.query_word32(); + let tx_gas_price_rlc = cb.word_rlc(tx_gas_price.limbs.clone().map(|cell| cell.expr())); + cb.tx_context_lookup( + tx_id.expr(), + TxContextFieldTag::GasPrice, + None, + word::Word::from_lo_unchecked(tx_gas_price_rlc), + ); + + let tx_caller_address_is_zero = IsZeroWordGadget::construct(cb, &tx_caller_address); cb.require_equal( "CallerAddress != 0 (not a padding tx)", tx_caller_address_is_zero.expr(), false.expr(), ); - let tx_callee_address_is_zero = IsZeroGadget::construct(cb, tx_callee_address.expr()); + let tx_callee_address_is_zero = IsZeroWordGadget::construct(cb, &tx_callee_address); cb.condition(tx_is_create.expr(), |cb| { cb.require_equal( "Contract creation tx expects callee address to be zero", @@ -193,15 +217,13 @@ impl ExecutionGadget for BeginTxGadget { true.expr(), ) }); - let [tx_gas_price, tx_value] = [TxContextFieldTag::GasPrice, TxContextFieldTag::Value] - .map(|field_tag| cb.tx_context_as_word(tx_id.expr(), field_tag, None)); - let call_callee_address = cb.query_cell(); + let call_callee_address = cb.query_account_address(); cb.condition(not::expr(tx_is_create.expr()), |cb| { - cb.require_equal( + cb.require_equal_word( "Tx to non-zero address", - tx_callee_address.expr(), - call_callee_address.expr(), + tx_callee_address.to_word(), + call_callee_address.to_word(), ); }); @@ -212,11 +234,12 @@ impl ExecutionGadget for BeginTxGadget { // Increase caller's nonce. // (tx caller's nonce always increases even when tx ends with error) + // tx_nonce == sender_nonce cb.account_write( - tx_caller_address.expr(), + tx_caller_address.to_word(), AccountFieldTag::Nonce, - sender_nonce.expr() + 1.expr(), - sender_nonce.expr(), + Word::from_lo_unchecked(tx_nonce.expr() + 1.expr()), + Word::from_lo_unchecked(tx_nonce.expr()), None, ); // rwc_delta += 1 @@ -225,20 +248,24 @@ impl ExecutionGadget for BeginTxGadget { // Calculate transaction gas fee let mul_gas_fee_by_gas = MulWordByU64Gadget::construct(cb, tx_gas_price.clone(), tx_gas.expr()); - let tx_fee = cb.query_word_rlc(); + let tx_fee = cb.query_word32(); let l2_fee = select::expr( tx_l1_msg.is_l1_msg(), 0.expr(), - from_bytes::expr(&mul_gas_fee_by_gas.product().cells[..16]), + from_bytes::expr(&mul_gas_fee_by_gas.product().limbs[..16]), ); cb.require_equal( "tx_fee == l1_fee + l2_fee", l1_fee_cost + l2_fee, - from_bytes::expr(&tx_fee.cells[..16]), + from_bytes::expr(&tx_fee.limbs[..16]), ); // a valid precompile address is: 1 <= addr <= 9 (addr != 0 && addr < 0xA) - let is_precompile_lt = LtGadget::construct(cb, tx_callee_address.expr(), 0xA.expr()); + let is_precompile_lt = LtWordGadget::construct( + cb, + &tx_callee_address.to_word(), + &Word::from_lo_unchecked(0xA.expr()), + ); let is_precompile = and::expr([ not::expr(tx_callee_address_is_zero.expr()), is_precompile_lt.expr(), @@ -286,20 +313,28 @@ impl ExecutionGadget for BeginTxGadget { let are_precompile_warm = array_init(|_| cb.query_bool()); for (addr, is_warm) in (1..=PRECOMPILE_COUNT).zip(are_precompile_warm.iter()) { - cb.account_access_list_write(tx_id.expr(), addr.expr(), 1.expr(), is_warm.expr(), None); + // cb.account_access_list_write(tx_id.expr(), addr.expr(), 1.expr(), is_warm.expr(), + // None); + cb.account_access_list_write_unchecked( + tx_id.expr(), + Word::new([addr.expr(), 0.expr()]), + 1.expr(), + is_warm.expr(), + None, + ); } // rwc_delta += PRECOMPILE_COUNT // Prepare access list of caller and callee - cb.account_access_list_write( + cb.account_access_list_write_unchecked( tx_id.expr(), - tx_caller_address.expr(), + tx_caller_address.to_word(), 1.expr(), is_caller_warm.expr(), None, ); // rwc_delta += 1 cb.account_access_list_write( tx_id.expr(), - call_callee_address.expr(), + call_callee_address.to_word(), 1.expr(), is_callee_warm.expr(), None, @@ -309,56 +344,60 @@ impl ExecutionGadget for BeginTxGadget { let coinbase = cb.query_cell(); cb.block_lookup( BlockContextFieldTag::Coinbase.expr(), - cb.curr.state.block_number.expr(), + Some(cb.curr.state.block_number.expr()), + //coinbase.to_word(), coinbase.expr(), ); #[cfg(feature = "shanghai")] - cb.account_access_list_write( + cb.account_access_list_write_unchecked( tx_id.expr(), - coinbase.expr(), + Word::from_lo_unchecked(coinbase.expr()), 1.expr(), is_coinbase_warm.expr(), None, ); // rwc_delta += 1 - let account_code_hash = cb.query_cell_phase2(); + //let account_code_hash = cb.query_cell_phase2(); + let account_code_hash = cb.query_word_unchecked(); + let account_code_hash_is_empty = - IsEqualGadget::construct(cb, account_code_hash.expr(), cb.empty_code_hash_rlc()); - let account_code_hash_is_zero = IsZeroGadget::construct(cb, account_code_hash.expr()); + IsEqualWordGadget::construct(cb, &account_code_hash.to_word(), &cb.empty_code_hash()); + let account_code_hash_is_zero = IsZeroWordGadget::construct(cb, &account_code_hash); let account_code_hash_is_empty_or_zero = account_code_hash_is_empty.expr() + account_code_hash_is_zero.expr(); #[cfg(feature = "scroll")] - let account_keccak_code_hash = cb.query_cell_phase2(); + let account_keccak_code_hash = cb.query_word_unchecked(); - let call_code_hash = cb.query_cell_phase2(); + let call_code_hash = cb.query_word_unchecked(); let call_code_hash_is_empty = - IsEqualGadget::construct(cb, call_code_hash.expr(), cb.empty_code_hash_rlc()); - let call_code_hash_is_zero = IsZeroGadget::construct(cb, call_code_hash.expr()); + IsEqualWordGadget::construct(cb, &call_code_hash.to_word(), &cb.empty_code_hash()); + let call_code_hash_is_zero = IsZeroWordGadget::construct(cb, &call_code_hash); let call_code_hash_is_empty_or_zero = call_code_hash_is_empty.expr() + call_code_hash_is_zero.expr(); cb.account_read( - call_callee_address.expr(), + call_callee_address.to_word(), AccountFieldTag::CodeHash, - account_code_hash.expr(), + account_code_hash.to_word(), ); // rwc_delta += 1 // Transfer value from caller to callee, creating account if necessary. let transfer_with_gas_fee = TransferWithGasFeeGadget::construct( cb, - tx_caller_address.expr(), - call_callee_address.expr(), + tx_caller_address.to_word(), + call_callee_address.to_word(), not::expr(account_code_hash_is_zero.expr()), tx_is_create.expr(), - account_code_hash.expr(), + account_code_hash.to_word(), #[cfg(feature = "scroll")] - account_keccak_code_hash.expr(), + account_keccak_code_hash.to_word(), tx_value.clone(), tx_fee.clone(), &mut reversion_info, ); + let caller_nonce_hash_bytes = cb.query_word32(); // Construct EIP-1559 gadget to check sender balance. let tx_eip1559 = TxEip1559Gadget::construct( cb, @@ -370,18 +409,23 @@ impl ExecutionGadget for BeginTxGadget { transfer_with_gas_fee.sender_balance_prev(), ); - let caller_nonce_hash_bytes = array_init::array_init(|_| cb.query_byte()); let create = ContractCreateGadget::construct(cb); - cb.require_equal( + cb.require_equal_word( "tx caller address equivalence", - tx_caller_address.expr(), + tx_caller_address.to_word(), create.caller_address(), ); cb.condition(tx_is_create.expr(), |cb| { - cb.require_equal( + cb.require_equal_word( "call callee address equivalence", - call_callee_address.expr(), - expr_from_bytes(&caller_nonce_hash_bytes[0..N_BYTES_ACCOUNT_ADDRESS]), + call_callee_address.to_word(), + AccountAddress::::new( + caller_nonce_hash_bytes.limbs[0..N_BYTES_ACCOUNT_ADDRESS] + .to_vec() + .try_into() + .unwrap(), + ) + .to_word(), ); }); cb.require_equal( @@ -390,32 +434,36 @@ impl ExecutionGadget for BeginTxGadget { create.caller_nonce(), ); cb.condition(not::expr(call_code_hash_is_empty_or_zero.expr()), |cb| { - cb.require_equal( + cb.require_equal_word( "code hash equivalence", - cb.curr.state.code_hash.expr(), - call_code_hash.expr(), + cb.curr.state.code_hash.to_word(), + call_code_hash.to_word(), ); }); // 1. Handle contract creation transaction. let (init_code_rlc, keccak_code_hash) = cb.condition(tx_is_create.expr(), |cb| { - let output_rlc = cb.word_rlc::( - caller_nonce_hash_bytes - .iter() - .map(Expr::expr) - .collect::>() - .try_into() - .unwrap(), + let caller_nonce_hash_bytes_rlc = + cb.word_rlc(caller_nonce_hash_bytes.limbs.clone().map(|l| l.expr())); + cb.keccak_table_lookup( + create.input_rlc(cb), + create.input_length(), + caller_nonce_hash_bytes_rlc, + caller_nonce_hash_bytes.to_word(), ); - cb.keccak_table_lookup(create.input_rlc(cb), create.input_length(), output_rlc); - let keccak_code_hash = cb.query_cell_phase2(); + //let keccak_code_hash = cb.query_cell_phase2(); + let keccak_code_hash = cb.query_word32(); + let init_code_rlc = cb.query_cell_phase2(); + let keccak_code_hash_rlc = + cb.word_rlc(keccak_code_hash.limbs.clone().map(|l| l.expr())); // keccak table lookup for init code. cb.keccak_table_lookup( init_code_rlc.expr(), tx_call_data_length.expr(), - keccak_code_hash.expr(), + keccak_code_hash_rlc, + keccak_code_hash.to_word(), ); // copy table lookup for init code. cb.condition(is_call_data_empty.expr(), |cb| { @@ -426,45 +474,54 @@ impl ExecutionGadget for BeginTxGadget { }); cb.condition(not::expr(is_call_data_empty.expr()), |cb| { cb.copy_table_lookup( - tx_id.expr(), // src_id - CopyDataType::TxCalldata.expr(), // src_tag - cb.curr.state.code_hash.expr(), // dst_id - CopyDataType::Bytecode.expr(), // dst_tag - 0.expr(), // src_addr - tx_call_data_length.expr(), // src_addr_end - 0.expr(), // dst_addr - tx_call_data_length.expr(), // length - init_code_rlc.expr(), // rlc_acc - 0.expr(), // rwc increase + Word::from_lo_unchecked(tx_id.expr()), // src_id + CopyDataType::TxCalldata.expr(), // src_tag + cb.curr.state.code_hash.to_word(), // dst_id + CopyDataType::Bytecode.expr(), // dst_tag + 0.expr(), // src_addr + tx_call_data_length.expr(), // src_addr_end + 0.expr(), // dst_addr + tx_call_data_length.expr(), // length + init_code_rlc.expr(), // rlc_acc + 0.expr(), // rwc increase ) }); cb.account_write( - call_callee_address.expr(), + call_callee_address.to_word(), AccountFieldTag::Nonce, - 1.expr(), - 0.expr(), + Word::one(), + Word::zero(), Some(&mut reversion_info), ); for (field_tag, value) in [ - (CallContextFieldTag::Depth, 1.expr()), - (CallContextFieldTag::CallerAddress, tx_caller_address.expr()), + (CallContextFieldTag::Depth, Word::one()), + ( + CallContextFieldTag::CallerAddress, + tx_caller_address.to_word(), + ), ( CallContextFieldTag::CalleeAddress, - call_callee_address.expr(), + call_callee_address.to_word(), + ), + (CallContextFieldTag::CallDataOffset, Word::zero()), + (CallContextFieldTag::CallDataLength, Word::zero()), + (CallContextFieldTag::Value, tx_value.to_word()), + (CallContextFieldTag::IsStatic, Word::zero()), + (CallContextFieldTag::LastCalleeId, Word::zero()), + ( + CallContextFieldTag::LastCalleeReturnDataOffset, + Word::zero(), + ), + ( + CallContextFieldTag::LastCalleeReturnDataLength, + Word::zero(), ), - (CallContextFieldTag::CallDataOffset, 0.expr()), - (CallContextFieldTag::CallDataLength, 0.expr()), - (CallContextFieldTag::Value, tx_value.expr()), - (CallContextFieldTag::IsStatic, 0.expr()), - (CallContextFieldTag::LastCalleeId, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataOffset, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataLength, 0.expr()), - (CallContextFieldTag::IsRoot, 1.expr()), - (CallContextFieldTag::IsCreate, 1.expr()), + (CallContextFieldTag::IsRoot, Word::one()), + (CallContextFieldTag::IsCreate, Word::one()), ( CallContextFieldTag::CodeHash, - cb.curr.state.code_hash.expr(), + cb.curr.state.code_hash.to_word(), ), ] { cb.call_context_lookup(true.expr(), Some(call_id.expr()), field_tag, value); @@ -509,7 +566,7 @@ impl ExecutionGadget for BeginTxGadget { call_id: To(call_id.expr()), is_root: To(true.expr()), is_create: To(tx_is_create.expr()), - code_hash: To(cb.curr.state.code_hash.expr()), + code_hash: To(cb.curr.state.code_hash.to_word()), gas_left: To(gas_left.clone()), // There are a + 1 reversible writes: // - a TransferWithGasFeeGadget @@ -539,33 +596,45 @@ impl ExecutionGadget for BeginTxGadget { // call's dummy context for precompile. for (field_tag, value) in [ - (CallContextFieldTag::ReturnDataOffset, 0.expr()), - (CallContextFieldTag::ReturnDataLength, 0.expr()), + (CallContextFieldTag::ReturnDataOffset, Word::zero()), + (CallContextFieldTag::ReturnDataLength, Word::zero()), ] { cb.call_context_lookup(true.expr(), Some(call_id.expr()), field_tag, value); } // Setup first call's context. for (field_tag, value) in [ - (CallContextFieldTag::Depth, 1.expr()), - (CallContextFieldTag::CallerAddress, tx_caller_address.expr()), + (CallContextFieldTag::Depth, Word::one()), + ( + CallContextFieldTag::CallerAddress, + tx_caller_address.to_word(), + ), ( CallContextFieldTag::CalleeAddress, - call_callee_address.expr(), + Word::from_lo_unchecked(call_callee_address.expr()), ), - (CallContextFieldTag::CallDataOffset, 0.expr()), + (CallContextFieldTag::CallDataOffset, Word::zero()), ( CallContextFieldTag::CallDataLength, - tx_call_data_length.expr(), + Word::from_lo_unchecked(tx_call_data_length.expr()), ), - (CallContextFieldTag::Value, tx_value.expr()), - (CallContextFieldTag::IsStatic, 0.expr()), - (CallContextFieldTag::LastCalleeId, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataOffset, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataLength, 0.expr()), - (CallContextFieldTag::IsRoot, 1.expr()), - (CallContextFieldTag::IsCreate, tx_is_create.expr()), - (CallContextFieldTag::CodeHash, account_code_hash.expr()), + (CallContextFieldTag::Value, tx_value.to_word()), + (CallContextFieldTag::IsStatic, Word::zero()), + (CallContextFieldTag::LastCalleeId, Word::zero()), + ( + CallContextFieldTag::LastCalleeReturnDataOffset, + Word::zero(), + ), + ( + CallContextFieldTag::LastCalleeReturnDataLength, + Word::zero(), + ), + (CallContextFieldTag::IsRoot, Word::one()), + ( + CallContextFieldTag::IsCreate, + Word::from_lo_unchecked(tx_is_create.expr()), + ), + (CallContextFieldTag::CodeHash, account_code_hash.to_word()), ] { cb.call_context_lookup(true.expr(), Some(call_id.expr()), field_tag, value); } @@ -575,9 +644,9 @@ impl ExecutionGadget for BeginTxGadget { // - to the precompile input. let precompile_input_bytes_rlc = cb.query_cell_phase2(); cb.copy_table_lookup( - tx_id.expr(), //cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(tx_id.expr()), CopyDataType::TxCalldata.expr(), - call_id.expr(), + Word::from_lo_unchecked(call_id.expr()), CopyDataType::RlcAcc.expr(), 0.expr(), precompile_input_len.expr(), @@ -637,7 +706,7 @@ impl ExecutionGadget for BeginTxGadget { let precompile_gadget = PrecompileGadget::construct( cb, - call_callee_address.expr(), + call_callee_address.to_word(), precompile_input_bytes_rlc.expr(), None, None, @@ -703,25 +772,37 @@ impl ExecutionGadget for BeginTxGadget { |cb| { // Setup first call's context. for (field_tag, value) in [ - (CallContextFieldTag::Depth, 1.expr()), - (CallContextFieldTag::CallerAddress, tx_caller_address.expr()), + (CallContextFieldTag::Depth, Word::one()), + ( + CallContextFieldTag::CallerAddress, + tx_caller_address.to_word(), + ), ( CallContextFieldTag::CalleeAddress, - call_callee_address.expr(), + call_callee_address.to_word(), ), - (CallContextFieldTag::CallDataOffset, 0.expr()), + (CallContextFieldTag::CallDataOffset, Word::zero()), ( CallContextFieldTag::CallDataLength, - tx_call_data_length.expr(), + Word::from_lo_unchecked(tx_call_data_length.expr()), + ), + (CallContextFieldTag::Value, tx_value.to_word()), + (CallContextFieldTag::IsStatic, Word::zero()), + (CallContextFieldTag::LastCalleeId, Word::zero()), + ( + CallContextFieldTag::LastCalleeReturnDataOffset, + Word::zero(), + ), + ( + CallContextFieldTag::LastCalleeReturnDataLength, + Word::zero(), + ), + (CallContextFieldTag::IsRoot, Word::one()), + ( + CallContextFieldTag::IsCreate, + Word::from_lo_unchecked(tx_is_create.expr()), ), - (CallContextFieldTag::Value, tx_value.expr()), - (CallContextFieldTag::IsStatic, 0.expr()), - (CallContextFieldTag::LastCalleeId, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataOffset, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataLength, 0.expr()), - (CallContextFieldTag::IsRoot, 1.expr()), - (CallContextFieldTag::IsCreate, tx_is_create.expr()), - (CallContextFieldTag::CodeHash, account_code_hash.expr()), + (CallContextFieldTag::CodeHash, account_code_hash.to_word()), ] { cb.call_context_lookup(true.expr(), Some(call_id.expr()), field_tag, value); } @@ -764,7 +845,7 @@ impl ExecutionGadget for BeginTxGadget { call_id: To(call_id.expr()), is_root: To(true.expr()), is_create: To(tx_is_create.expr()), - code_hash: To(account_code_hash.expr()), + code_hash: To(account_code_hash.to_word()), gas_left: To(gas_left), reversible_write_counter: To(transfer_with_gas_fee.reversible_w_delta()), log_id: To(0.expr()), @@ -932,34 +1013,29 @@ impl ExecutionGadget for BeginTxGadget { )?; let tx_fee = transfer_assign_result.gas_fee.unwrap(); - self.tx_fee - .assign(region, offset, Some(tx_fee.to_le_bytes()))?; + self.tx_fee.assign_u256(region, offset, tx_fee)?; log::debug!( "tx_fee assigned {:?}, gas price {:?}, gas {}", tx_fee, tx.gas_price, tx.gas ); - self.account_code_hash.assign( + self.account_code_hash.assign_u256( region, offset, - region.code_hash( - transfer_assign_result - .account_code_hash - .unwrap_or(account_code_hash), - ), + transfer_assign_result + .account_code_hash + .unwrap_or(account_code_hash), )?; #[cfg(feature = "scroll")] { - self.account_keccak_code_hash.assign( + self.account_keccak_code_hash.assign_u256( region, offset, - region.word_rlc( - transfer_assign_result - .account_keccak_code_hash - .unwrap_or_default(), - ), + transfer_assign_result + .account_keccak_code_hash + .unwrap_or_default(), )?; } @@ -974,9 +1050,8 @@ impl ExecutionGadget for BeginTxGadget { self.tx_gas .assign(region, offset, Value::known(F::from(tx.gas)))?; self.tx_gas_price - .assign(region, offset, Some(tx.gas_price.to_le_bytes()))?; - self.tx_value - .assign(region, offset, Some(tx.value.to_le_bytes()))?; + .assign_u256(region, offset, tx.gas_price)?; + self.tx_value.assign_u256(region, offset, tx.value)?; self.mul_gas_fee_by_gas.assign( region, offset, @@ -984,25 +1059,20 @@ impl ExecutionGadget for BeginTxGadget { tx.gas, tx.gas_price * tx.gas, )?; - let caller_address = tx - .caller_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"); - let callee_address = tx - .callee_address - .unwrap_or(Address::zero()) - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"); + let caller_address = tx.caller_address; + let callee_address = tx.callee_address.unwrap_or(Address::zero()); + self.tx_caller_address - .assign(region, offset, Value::known(caller_address))?; + .assign_h160(region, offset, caller_address)?; self.tx_caller_address_is_zero - .assign(region, offset, caller_address)?; + .assign_u256(region, offset, caller_address.to_word())?; self.tx_callee_address - .assign(region, offset, Value::known(callee_address))?; + .assign_h160(region, offset, callee_address)?; self.tx_callee_address_is_zero - .assign(region, offset, callee_address)?; + .assign_u256(region, offset, callee_address.to_word())?; self.is_precompile_lt - .assign(region, offset, callee_address, F::from(0xA))?; + .assign(region, offset, callee_address.to_word(), U256::from(0xA))?; + // precompile related assignment. let (precompile_input_len, precompile_input_bytes_rlc) = if tx .callee_address @@ -1041,18 +1111,14 @@ impl ExecutionGadget for BeginTxGadget { self.precompile_input_bytes_rlc .assign(region, offset, precompile_input_bytes_rlc)?; - self.call_callee_address.assign( + self.call_callee_address.assign_h160( region, offset, - Value::known( - if tx.is_create { - get_contract_address(tx.caller_address, tx.nonce) - } else { - tx.callee_address.unwrap_or(Address::zero()) - } - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), + if tx.is_create { + get_contract_address(tx.caller_address, tx.nonce) + } else { + tx.callee_address.unwrap_or(Address::zero()) + }, )?; self.tx_is_create .assign(region, offset, Value::known(F::from(tx.is_create as u64)))?; @@ -1083,7 +1149,7 @@ impl ExecutionGadget for BeginTxGadget { self.sufficient_gas_left .assign(region, offset, F::from(tx.gas - step.gas_cost))?; self.call_code_hash - .assign(region, offset, region.code_hash(call.code_hash))?; + .assign_u256(region, offset, call.code_hash)?; let untrimmed_contract_addr = { let mut stream = RlpStream::new(); stream.begin_list(2); @@ -1092,64 +1158,42 @@ impl ExecutionGadget for BeginTxGadget { let rlp_encoding = stream.out().to_vec(); keccak256(rlp_encoding) }; - for (c, v) in self - .caller_nonce_hash_bytes - .iter() - .rev() - .zip(untrimmed_contract_addr.iter()) - { - c.assign(region, offset, Value::known(F::from(*v as u64)))?; - } - self.account_code_hash_is_empty.assign_value( - region, - offset, - region.code_hash(account_code_hash), - region.empty_code_hash_rlc(), - )?; - self.account_code_hash_is_zero.assign_value( + + self.caller_nonce_hash_bytes.assign_u256( region, offset, - region.code_hash(account_code_hash), + U256::from_big_endian(&untrimmed_contract_addr), )?; - self.call_code_hash_is_empty.assign_value( + self.account_code_hash_is_empty.assign_u256( region, offset, - region.code_hash(call.code_hash), - region.empty_code_hash_rlc(), + account_code_hash, + CodeDB::empty_code_hash().to_word(), )?; - self.call_code_hash_is_zero.assign_value( + self.account_code_hash_is_zero + .assign_u256(region, offset, account_code_hash)?; + self.call_code_hash_is_empty.assign_u256( region, offset, - region.code_hash(call.code_hash), + call.code_hash, + CodeDB::empty_code_hash().to_word(), )?; + self.call_code_hash_is_zero + .assign_u256(region, offset, call.code_hash)?; - let untrimmed_contract_addr = { - let mut stream = ethers_core::utils::rlp::RlpStream::new(); - stream.begin_list(2); - stream.append(&tx.caller_address); - stream.append(ð_types::U256::from(tx.nonce)); - let rlp_encoding = stream.out().to_vec(); - keccak256(rlp_encoding) - }; - for (c, v) in self - .caller_nonce_hash_bytes - .iter() - .rev() - .zip(untrimmed_contract_addr.iter()) - { - c.assign(region, offset, Value::known(F::from(*v as u64)))?; - } - let (init_code_rlc, keccak_code_hash_rlc) = if tx.is_create { + let (init_code_rlc, keccak_code_hash) = if tx.is_create { let init_code_rlc = region.keccak_rlc(&tx.call_data.iter().cloned().rev().collect::>()); - let keccak_code_hash_rlc = region.word_rlc(U256::from(keccak256(&tx.call_data))); - (init_code_rlc, keccak_code_hash_rlc) + // keccak_code_hash_rlc: rename to keccak_code_hash for word type, no need to use rlc, + // two words can hold 256 bytes codehash now. + let keccak_code_hash = U256::from(keccak256(&tx.call_data)); + (init_code_rlc, keccak_code_hash) } else { - (Value::known(F::zero()), Value::known(F::zero())) + (Value::known(F::zero()), U256::zero()) }; self.init_code_rlc.assign(region, offset, init_code_rlc)?; self.keccak_code_hash - .assign(region, offset, keccak_code_hash_rlc)?; + .assign_u256(region, offset, keccak_code_hash)?; self.create.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs index 9a8addaa1b..f82240265d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs +++ b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs @@ -7,21 +7,24 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - CachedRegion, Word, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; +use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct BitwiseGadget { same_context: SameContextGadget, - a: Word, - b: Word, - c: Word, + a: Word32Cell, + b: Word32Cell, + c: Word32Cell, } impl ExecutionGadget for BitwiseGadget { @@ -32,13 +35,13 @@ impl ExecutionGadget for BitwiseGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let c = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); + let c = cb.query_word32(); - cb.stack_pop(a.expr()); - cb.stack_pop(b.expr()); - cb.stack_push(c.expr()); + cb.stack_pop(a.to_word()); + cb.stack_pop(b.to_word()); + cb.stack_push(c.to_word()); // Because opcode AND, OR, and XOR are continuous, so we can make the // FixedTableTag of them also continuous, and use the opcode delta from @@ -51,9 +54,9 @@ impl ExecutionGadget for BitwiseGadget { Lookup::Fixed { tag: tag.clone(), values: [ - a.cells[idx].expr(), - b.cells[idx].expr(), - c.cells[idx].expr(), + a.limbs[idx].expr(), + b.limbs[idx].expr(), + c.limbs[idx].expr(), ], }, ); @@ -90,9 +93,9 @@ impl ExecutionGadget for BitwiseGadget { let [a, b, c] = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] .map(|idx| block.rws[idx].stack_value()); - self.a.assign(region, offset, Some(a.to_le_bytes()))?; - self.b.assign(region, offset, Some(b.to_le_bytes()))?; - self.c.assign(region, offset, Some(c.to_le_bytes()))?; + self.a.assign_u256(region, offset, a)?; + self.b.assign_u256(region, offset, b)?; + self.c.assign_u256(region, offset, c)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs index a388529a3f..a485dfadc9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs @@ -6,29 +6,33 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, CachedRegion, RandomLinearCombination, + from_bytes, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::BlockContextFieldTag, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct BlockCtxGadget { same_context: SameContextGadget, - value: RandomLinearCombination, + //value: RandomLinearCombination, + value: Word32Cell, } impl BlockCtxGadget { fn construct(cb: &mut EVMConstraintBuilder) -> Self { - let value = cb.query_word_rlc(); + let value = cb.query_word32(); // Push the const generic parameter N_BYTES value to the stack - cb.stack_push(value.expr()); + cb.stack_push(value.to_word()); // Get op's FieldTag let opcode = cb.query_cell(); @@ -38,11 +42,16 @@ impl BlockCtxGadget { // Lookup block table with block context ops // TIMESTAMP/NUMBER/GASLIMIT, COINBASE and DIFFICULTY/BASEFEE let value_expr = if N_BYTES == N_BYTES_WORD { - value.expr() + cb.word_rlc(value.limbs.clone().map(|l| l.expr())) } else { - from_bytes::expr(&value.cells) + from_bytes::expr(&value.limbs[..N_BYTES]) }; - cb.block_lookup(blockctx_tag, cb.curr.state.block_number.expr(), value_expr); + + cb.block_lookup( + blockctx_tag, + Some(cb.curr.state.block_number.expr()), + value_expr, + ); // State transition let step_state_transition = StepStateTransition { @@ -92,11 +101,7 @@ impl ExecutionGadget for BlockCtxU64Gadget { let value = block.rws[step.rw_indices[0]].stack_value(); - self.value_u64.value.assign( - region, - offset, - Some(u64::try_from(value).unwrap().to_le_bytes()), - )?; + self.value_u64.value.assign_u256(region, offset, value)?; Ok(()) } @@ -133,15 +138,7 @@ impl ExecutionGadget for BlockCtxU160Gadget { let value = block.rws[step.rw_indices[0]].stack_value(); - self.value_u160.value.assign( - region, - offset, - Some( - value.to_le_bytes()[..N_BYTES_ACCOUNT_ADDRESS] - .try_into() - .unwrap(), - ), - )?; + self.value_u160.value.assign_u256(region, offset, value)?; Ok(()) } @@ -188,9 +185,7 @@ impl ExecutionGadget for BlockCtxU256Gadget { let value = block.rws[step.rw_indices[0]].stack_value(); - self.value_u256 - .value - .assign(region, offset, Some(value.to_le_bytes()))?; + self.value_u256.value.assign_u256(region, offset, value)?; Ok(()) } @@ -210,7 +205,7 @@ impl ExecutionGadget for DifficultyGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { // Always returns 0 for scroll. - cb.stack_push(0.expr()); + cb.stack_push(Word::from_lo_unchecked(0.expr())); let opcode = cb.query_cell(); // State transition let step_state_transition = StepStateTransition { diff --git a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs index 392b1770f5..f77566854c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs @@ -12,19 +12,19 @@ use crate::{ }, from_bytes, math_gadget::LtGadget, - CachedRegion, Cell, U64Word, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::BlockContextFieldTag, - util::Expr, + util::word::{Word32Cell, WordExpr}, }; use bus_mapping::evm::OpcodeId; use eth_types::{ evm_types::block_utils::{is_valid_block_number, NUM_PREV_BLOCK_ALLOWED}, - Field, ToScalar, + Field, ToScalar, U256, }; -use gadgets::util::not; +use gadgets::util::{not, Expr}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -32,8 +32,9 @@ pub(crate) struct BlockHashGadget { same_context: SameContextGadget, block_number: WordByteCapGadget, current_block_number: Cell, - block_hash: Cell, - chain_id: U64Word, + //block_hash: Word>, + block_hash: Word32Cell, + chain_id: Word32Cell, diff_lt: LtGadget, } @@ -46,18 +47,23 @@ impl ExecutionGadget for BlockHashGadget { let current_block_number = cb.query_cell(); cb.block_lookup( BlockContextFieldTag::Number.expr(), - cb.curr.state.block_number.expr(), + Some(cb.curr.state.block_number.expr()), + // TODO: enable this in word hi stage2. + //Word::from_lo_unchecked(current_block_number.expr()), current_block_number.expr(), ); - let block_number = WordByteCapGadget::construct(cb, current_block_number.expr()); - cb.stack_pop(block_number.original_word()); + let block_number: WordByteCapGadget = + WordByteCapGadget::construct(cb, current_block_number.expr()); + cb.stack_pop(block_number.original_word().to_word()); + + let chain_id = cb.query_word32(); - let chain_id = cb.query_word_rlc(); cb.block_lookup( BlockContextFieldTag::ChainId.expr(), - cb.curr.state.block_number.expr(), - from_bytes::expr(&chain_id.cells), + Some(cb.curr.state.block_number.expr()), + //chain_id.to_word(), + from_bytes::expr(&chain_id.limbs[..N_BYTES_U64]), ); let diff_lt = cb.condition(block_number.not_overflow(), |cb| { @@ -71,14 +77,18 @@ impl ExecutionGadget for BlockHashGadget { }); let is_valid = and::expr([block_number.lt_cap(), diff_lt.expr()]); - let block_hash = cb.query_cell_phase2(); + //let block_hash = cb.query_word_unchecked(); // use in stage2 after block table --> word + let block_hash = cb.query_word32(); + let block_hash_rlc = cb.word_rlc(block_hash.limbs.clone().map(|ref l| l.expr())); + cb.condition(is_valid.expr(), |cb| { // For non-scroll, lookup for the block hash. #[cfg(not(feature = "scroll"))] cb.block_lookup( BlockContextFieldTag::BlockHash.expr(), - block_number.valid_value(), - block_hash.expr(), + Some(block_number.valid_value()), + // block_hash.to_word(), + block_hash_rlc, ); // For scroll, the block hash is calculated by Keccak256. The input @@ -87,14 +97,13 @@ impl ExecutionGadget for BlockHashGadget { #[cfg(feature = "scroll")] cb.keccak_table_lookup( cb.keccak_rlc::<{ 2 * N_BYTES_U64 }>( - chain_id - .cells + chain_id.limbs[..N_BYTES_U64] .iter() .rev() .chain( block_number - .original_ref() - .cells + .original_word() + .limbs .iter() .take(N_BYTES_U64) .rev(), @@ -106,18 +115,19 @@ impl ExecutionGadget for BlockHashGadget { .unwrap(), ), (2 * N_BYTES_U64).expr(), - block_hash.expr(), + block_hash_rlc, + block_hash.to_word(), ); }); cb.condition(not::expr(is_valid), |cb| { - cb.require_zero( + cb.require_zero_word( "Invalid block number for block hash lookup", - block_hash.expr(), + block_hash.to_word(), ); }); - cb.stack_push(block_hash.expr()); + cb.stack_push(block_hash.to_word()); let step_state_transition = StepStateTransition { rw_counter: Delta(2.expr()), @@ -171,10 +181,9 @@ impl ExecutionGadget for BlockHashGadget { .assign(region, offset, block_number, current_block_number)?; self.current_block_number .assign(region, offset, Value::known(current_block_number))?; - self.block_hash - .assign(region, offset, region.word_rlc(block_hash))?; + self.block_hash.assign_u256(region, offset, block_hash)?; self.chain_id - .assign(region, offset, Some(chain_id.to_le_bytes()))?; + .assign_u256(region, offset, U256::from(chain_id))?; // Block number overflow should be constrained by WordByteCapGadget. let block_number: F = block_number diff --git a/zkevm-circuits/src/evm_circuit/execution/byte.rs b/zkevm-circuits/src/evm_circuit/execution/byte.rs index dd4b4317d7..5f52985a01 100644 --- a/zkevm-circuits/src/evm_circuit/execution/byte.rs +++ b/zkevm-circuits/src/evm_circuit/execution/byte.rs @@ -6,11 +6,14 @@ use crate::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, math_gadget::{IsEqualGadget, IsZeroGadget}, - sum, CachedRegion, Word, + sum, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use array_init::array_init; use bus_mapping::evm::OpcodeId; @@ -20,8 +23,8 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct ByteGadget { same_context: SameContextGadget, - index: Word, - value: Word, + index: Word32Cell, + value: Word32Cell, is_msb_sum_zero: IsZeroGadget, is_byte_selected: [IsEqualGadget; 32], } @@ -32,14 +35,14 @@ impl ExecutionGadget for ByteGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::BYTE; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let index = cb.query_word_rlc(); - let value = cb.query_word_rlc(); + let index = cb.query_word32(); + let value = cb.query_word32(); // If any of the non-LSB bytes of the index word are non-zero we never // need to copy any bytes. So just sum all the non-LSB byte // values here and then check if it's non-zero so we can use // that as an additional condition when to copy the byte value. - let is_msb_sum_zero = IsZeroGadget::construct(cb, sum::expr(&index.cells[1..32])); + let is_msb_sum_zero = IsZeroGadget::construct(cb, sum::expr(&index.limbs[1..32])); // Now we just need to check that `result[0]` is the sum of all copied // bytes. We go byte by byte and check if `idx == index[0]`. @@ -50,11 +53,11 @@ impl ExecutionGadget for ByteGadget { let is_byte_selected = array_init(|idx| { // Check if this byte is selected looking only at the LSB of the // index word - IsEqualGadget::construct(cb, index.cells[0].expr(), (31 - idx).expr()) + IsEqualGadget::construct(cb, index.limbs[0].expr(), (31 - idx).expr()) }); // Sum all possible selected bytes - let selected_byte = value.cells.iter().zip(is_byte_selected.iter()).fold( + let selected_byte = value.limbs.iter().zip(is_byte_selected.iter()).fold( 0.expr(), |acc, (cell, is_selected)| { acc + is_selected.expr() * is_msb_sum_zero.expr() * cell.expr() @@ -65,9 +68,9 @@ impl ExecutionGadget for ByteGadget { // push the selected byte on the stack // We can push the selected byte here directly because // it only uses the LSB of a word. - cb.stack_pop(index.expr()); - cb.stack_pop(value.expr()); - cb.stack_push(selected_byte); + cb.stack_pop(index.to_word()); + cb.stack_pop(value.to_word()); + cb.stack_push(Word::from_lo_unchecked(selected_byte)); // State transition let step_state_transition = StepStateTransition { @@ -101,21 +104,21 @@ impl ExecutionGadget for ByteGadget { self.same_context.assign_exec_step(region, offset, step)?; // Inputs/Outputs - let index = block.rws[step.rw_indices[0]].stack_value().to_le_bytes(); - let value = block.rws[step.rw_indices[1]].stack_value().to_le_bytes(); - self.index.assign(region, offset, Some(index))?; - self.value.assign(region, offset, Some(value))?; - + let index = block.rws[step.rw_indices[0]].stack_value(); + let value = block.rws[step.rw_indices[1]].stack_value(); + let index_bytes = index.to_le_bytes(); + self.index.assign_u256(region, offset, index)?; + self.value.assign_u256(region, offset, value)?; // Set `is_msb_sum_zero` self.is_msb_sum_zero - .assign(region, offset, sum::value(&index[1..32]))?; + .assign(region, offset, sum::value(&index_bytes[1..32]))?; // Set `is_byte_selected` for i in 0..32 { self.is_byte_selected[i].assign( region, offset, - F::from(index[0] as u64), + F::from(index_bytes[0] as u64), F::from((31 - i) as u64), )?; } diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs index 4cabced4ab..1af1740910 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs @@ -18,7 +18,10 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordExpr}, + Expr, + }, }; use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId}; use eth_types::{evm_types::GasCost, Field, ToScalar}; @@ -49,24 +52,27 @@ impl ExecutionGadget for CallDataCopyGadget { let call_data_length = cb.query_cell(); let call_data_offset = cb.query_cell(); - let length = cb.query_word_rlc(); - let memory_offset = cb.query_cell_phase2(); + let length = cb.query_memory_address(); + let memory_offset = cb.query_word_unchecked(); let data_offset = WordByteCapGadget::construct(cb, call_data_length.expr()); // Pop memory_offset, data_offset, length from stack - cb.stack_pop(memory_offset.expr()); - cb.stack_pop(data_offset.original_word()); - cb.stack_pop(length.expr()); + cb.stack_pop(memory_offset.to_word()); + cb.stack_pop(data_offset.original_word().to_word()); + cb.stack_pop(length.to_word()); // Lookup the calldata_length and caller_address in Tx context table or // Call context table cb.condition(cb.curr.state.is_root.expr(), |cb| { - cb.call_context_lookup(false.expr(), None, CallContextFieldTag::TxId, src_id.expr()); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( + None, + CallContextFieldTag::TxId, + Word::from_lo_unchecked(src_id.expr()), + ); + cb.call_context_lookup_read( None, CallContextFieldTag::CallDataLength, - call_data_length.expr(), + Word::from_lo_unchecked(call_data_length.expr()), ); cb.require_zero( "call_data_offset == 0 in the root call", @@ -74,30 +80,27 @@ impl ExecutionGadget for CallDataCopyGadget { ); }); cb.condition(1.expr() - cb.curr.state.is_root.expr(), |cb| { - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::CallerId, - src_id.expr(), + Word::from_lo_unchecked(src_id.expr()), ); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::CallDataLength, - call_data_length.expr(), + Word::from_lo_unchecked(call_data_length.expr()), ); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::CallDataOffset, - call_data_offset.expr(), + Word::from_lo_unchecked(call_data_offset.expr()), ); }); // Calculate the next memory size and the gas cost for this memory // access let memory_address = MemoryAddressGadget::construct(cb, memory_offset, length); - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, memory_address.length(), @@ -122,9 +125,9 @@ impl ExecutionGadget for CallDataCopyGadget { let src_addr_end = call_data_offset.expr() + call_data_length.expr(); cb.copy_table_lookup( - src_id.expr(), + Word::from_lo_unchecked(src_id.expr()), src_tag, - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), src_addr, src_addr_end, diff --git a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs index 03778170c2..2755080b9e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs @@ -1,6 +1,6 @@ use array_init::array_init; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian}; +use eth_types::{Field, ToLittleEndian, U256}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -20,12 +20,15 @@ use crate::{ }, from_bytes, memory_gadget::{BufferReaderGadget, MemoryMask, MemoryWordAddress}, - not, select, CachedRegion, Cell, Word, + not, select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, - util::Expr, + util::{ + word::{Word, Word32, Word32Cell, WordExpr}, + Expr, + }, }; use super::ExecutionGadget; @@ -57,9 +60,9 @@ pub(crate) struct CallDataLoadGadget { // memory_address: MemoryWordAddress, address_align: MemoryWordAddress, mask: MemoryMask, - value: Word, - value_left: Word, - value_right: Word, + value: Word32Cell, + value_left: Word32Cell, + value_right: Word32Cell, } impl ExecutionGadget for CallDataLoadGadget { @@ -74,27 +77,25 @@ impl ExecutionGadget for CallDataLoadGadget { let call_data_length = cb.query_cell(); let call_data_offset = cb.query_cell(); - let value = cb.query_word_rlc(); - let value_left = cb.query_word_rlc(); - let value_right = cb.query_word_rlc(); + let value = cb.query_word32(); + let value_left = cb.query_word32(); + let value_right = cb.query_word32(); let data_offset = WordByteCapGadget::construct(cb, call_data_length.expr()); - cb.stack_pop(data_offset.original_word()); + cb.stack_pop(data_offset.original_word().to_word()); cb.condition( and::expr([data_offset.not_overflow(), cb.curr.state.is_root.expr()]), |cb| { - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::TxId, - src_id.expr(), + Word::from_lo_unchecked(src_id.expr()), ); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::CallDataLength, - call_data_length.expr(), + Word::from_lo_unchecked(call_data_length.expr()), ); cb.require_equal( "if is_root then call_data_offset == 0", @@ -110,23 +111,20 @@ impl ExecutionGadget for CallDataLoadGadget { not::expr(cb.curr.state.is_root.expr()), ]), |cb| { - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::CallerId, - src_id.expr(), + Word::from_lo_unchecked(src_id.expr()), ); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::CallDataLength, - call_data_length.expr(), + Word::from_lo_unchecked(call_data_length.expr()), ); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::CallDataOffset, - call_data_offset.expr(), + Word::from_lo_unchecked(call_data_offset.expr()), ); }, ); @@ -145,7 +143,7 @@ impl ExecutionGadget for CallDataLoadGadget { // Read the call data word, potentially including trailing zeros // when reading out-of-bound. - let mut calldata_word: Vec<_> = (0..N_BYTES_WORD) + let mut calldata_word_bytes: Vec<_> = (0..N_BYTES_WORD) .map(|idx| { // For a root call, the call data comes from tx's data field. cb.condition( @@ -159,7 +157,8 @@ impl ExecutionGadget for CallDataLoadGadget { src_id.expr(), TxContextFieldTag::CallData, Some(src_addr.expr() + idx.expr()), - buffer_reader.byte(idx), + Word::from_lo_unchecked(buffer_reader.byte(idx)), + // buffer_reader.byte(idx), ); }, ); @@ -167,16 +166,16 @@ impl ExecutionGadget for CallDataLoadGadget { }) .collect(); // Since the stack is interpreted as MSB-first in CALLDATALOAD, we reverse the bytes here. - calldata_word.reverse(); - let calldata_word: [Expression; N_BYTES_WORD] = calldata_word.try_into().unwrap(); - let calldata_word = cb.word_rlc(calldata_word); + calldata_word_bytes.reverse(); + let calldata_word: [Expression; N_BYTES_WORD] = calldata_word_bytes.try_into().unwrap(); + let calldata_word_rlc = cb.word_rlc(calldata_word.clone()); // Now that we have the address in the caller’s memory, decompose it to work out the // alignment. - let address = cb.query_word_rlc(); + let address = cb.query_memory_address(); cb.require_equal( "memory address decomposition", - from_bytes::expr(&address.cells), + from_bytes::expr(&address.limbs), src_addr, ); @@ -194,31 +193,31 @@ impl ExecutionGadget for CallDataLoadGadget { // zero. The MLOAD value is MSB-first, the reverse order of the buffer_reader. let mem_calldata_overlap: [Expression; N_BYTES_WORD] = array_init(|i| { let reversed_i = N_BYTES_WORD - 1 - i; - value.cells[i].expr() * buffer_reader.read_flag(reversed_i) + value.limbs[i].expr() * buffer_reader.read_flag(reversed_i) }); cb.require_equal( "calldata equals memory data", - calldata_word.clone(), + calldata_word_rlc, cb.word_rlc(mem_calldata_overlap), ); // Get the memory word the same way as MLOAD. // Check the bytes that are read from the left and right memory words. - mask.require_equal_unaligned_word(cb, value.expr(), &value_left, &value_right); + mask.require_equal_unaligned_word(cb, &value, &value_left, &value_right); // Read the left and right words. cb.memory_lookup( 0.expr(), address_align.addr_left(), - value_left.expr(), - value_left.expr(), + value_left.to_word(), + value_left.to_word(), Some(src_id.expr()), ); cb.memory_lookup( 0.expr(), address_align.addr_right(), - value_right.expr(), - value_right.expr(), + value_right.to_word(), + value_right.to_word(), Some(src_id.expr()), ); }, @@ -226,13 +225,16 @@ impl ExecutionGadget for CallDataLoadGadget { // Add a lookup constraint for the 32-bytes that should have been pushed // to the stack. - cb.require_zero( + let calldata_word_limbs = Word32::new(calldata_word); + cb.stack_push(calldata_word_limbs.to_word()); + + cb.require_zero_word( "Stack push result must be 0 if stack pop offset is Uint64 overflow", - data_offset.overflow() * calldata_word.expr(), + calldata_word_limbs + .to_word() + .mul_selector(data_offset.overflow()), ); - cb.stack_push(calldata_word); - let step_state_transition = StepStateTransition { rw_counter: Delta(cb.rw_counter_offset()), program_counter: Delta(1.expr()), @@ -316,20 +318,17 @@ impl ExecutionGadget for CallDataLoadGadget { if !call.is_root && offset_not_overflow { // assign value_left, value_right word - value_left_bytes = block.rws[step.rw_indices[4]] - .memory_word_pair() - .0 - .to_le_bytes(); - value_right_bytes = block.rws[step.rw_indices[5]] - .memory_word_pair() - .0 - .to_le_bytes(); + let value_left_word = block.rws[step.rw_indices[4]].memory_word_pair().0; + let value_right_word = block.rws[step.rw_indices[5]].memory_word_pair().0; + + value_left_bytes = value_left_word.to_le_bytes(); + value_right_bytes = value_right_word.to_le_bytes(); // Here we write exactly what is in the RW table. self.value_left - .assign(region, offset, Some(value_left_bytes))?; + .assign_u256(region, offset, value_left_word)?; self.value_right - .assign(region, offset, Some(value_right_bytes))?; + .assign_u256(region, offset, value_right_word)?; // The RW values were BE order (see bus-mapping), so go back to normal memory order. value_left_bytes.reverse(); @@ -339,7 +338,9 @@ impl ExecutionGadget for CallDataLoadGadget { // reconstruct the unaligned word. let value_bytes = MemoryMask::::make_unaligned_word(shift, &value_left_bytes, &value_right_bytes); - self.value.assign(region, offset, Some(value_bytes))?; + // use assign_u256 for `value` + self.value + .assign_u256(region, offset, U256::from_little_endian(&value_bytes))?; let mut calldata_bytes = vec![0u8; N_BYTES_WORD]; diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs index c9b5029c26..c2501414b3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs @@ -1,26 +1,29 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_CALLDATASIZE, + //param::N_BYTES_CALLDATASIZE, step::ExecutionState, util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct CallDataSizeGadget { same_context: SameContextGadget, - call_data_size: RandomLinearCombination, + call_data_size: WordCell, } impl ExecutionGadget for CallDataSizeGadget { @@ -32,16 +35,15 @@ impl ExecutionGadget for CallDataSizeGadget { let opcode = cb.query_cell(); // Add lookup constraint in the call context for the calldatasize field. - let call_data_size = cb.query_word_rlc(); - cb.call_context_lookup( - false.expr(), + let call_data_size = cb.query_word_unchecked(); + cb.call_context_lookup_read( None, CallContextFieldTag::CallDataLength, - from_bytes::expr(&call_data_size.cells), + call_data_size.to_word(), ); // The calldatasize should be pushed to the top of the stack. - cb.stack_push(call_data_size.expr()); + cb.stack_push(call_data_size.to_word()); let step_state_transition = StepStateTransition { rw_counter: Delta(2.expr()), @@ -72,15 +74,8 @@ impl ExecutionGadget for CallDataSizeGadget { let call_data_size = block.rws[step.rw_indices[1]].stack_value(); - self.call_data_size.assign( - region, - offset, - Some( - call_data_size.to_le_bytes()[..N_BYTES_CALLDATASIZE] - .try_into() - .unwrap(), - ), - )?; + self.call_data_size + .assign_u64(region, offset, call_data_size.as_u64())?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/caller.rs b/zkevm-circuits/src/evm_circuit/execution/caller.rs index 19bc8f79ae..af43998977 100644 --- a/zkevm-circuits/src/evm_circuit/execution/caller.rs +++ b/zkevm-circuits/src/evm_circuit/execution/caller.rs @@ -1,27 +1,31 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_ACCOUNT_ADDRESS, + //param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState, util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; + use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct CallerGadget { same_context: SameContextGadget, // Using RLC to match against rw_table->stack_op value - caller_address: RandomLinearCombination, + caller_address: WordCell, } impl ExecutionGadget for CallerGadget { @@ -30,18 +34,17 @@ impl ExecutionGadget for CallerGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CALLER; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let caller_address = cb.query_word_rlc(); + let caller_address = cb.query_word_unchecked(); // Lookup rw_table -> call_context with caller address - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, // cb.curr.state.call_id CallContextFieldTag::CallerAddress, - from_bytes::expr(&caller_address.cells), + caller_address.to_word(), ); // Push the value to the stack - cb.stack_push(caller_address.expr()); + cb.stack_push(caller_address.to_word()); // State transition let opcode = cb.query_cell(); @@ -73,15 +76,7 @@ impl ExecutionGadget for CallerGadget { let caller = block.rws[step.rw_indices[1]].stack_value(); - self.caller_address.assign( - region, - offset, - Some( - caller.to_le_bytes()[..N_BYTES_ACCOUNT_ADDRESS] - .try_into() - .unwrap(), - ), - )?; + self.caller_address.assign_u256(region, offset, caller)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 6a201f440b..16c3db3756 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_MEMORY_ADDRESS, N_BYTES_U64}, + param::{N_BYTES_GAS, N_BYTES_MEMORY_ADDRESS, N_BYTES_U64}, step::ExecutionState, util::{ and, @@ -11,17 +11,21 @@ use crate::{ Transition::{Delta, To}, }, math_gadget::{ - ConstantDivisionGadget, IsZeroGadget, LtGadget, LtWordGadget, MinMaxGadget, + ConstantDivisionGadget, IsZeroGadget, IsZeroWordGadget, LtGadget, LtWordGadget, + MinMaxGadget, }, memory_gadget::{CommonMemoryAddressGadget, MemoryAddressGadget}, not, or, precompile_gadget::PrecompileGadget, - rlc, select, CachedRegion, Cell, StepRws, Word, + rlc, select, CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::{ circuit_input_builder::CopyDataType, @@ -30,9 +34,12 @@ use bus_mapping::{ }; use eth_types::{ evm_types::{memory::MemoryWordRange, GAS_STIPEND_CALL_WITH_VALUE}, - Field, ToAddress, ToBigEndian, ToLittleEndian, ToScalar, U256, + Field, ToAddress, ToBigEndian, ToScalar, ToWord, U256, +}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, }; -use halo2_proofs::{circuit::Value, plonk::Error}; use log::trace; use std::cmp::min; @@ -49,29 +56,30 @@ pub(crate) struct CallOpGadget { is_staticcall: IsZeroGadget, tx_id: Cell, reversion_info: ReversionInfo, - current_callee_address: Cell, - current_caller_address: Cell, + current_callee_address: WordCell, + current_caller_address: WordCell, is_static: Cell, depth: Cell, call: CommonCallGadget, true>, - current_value: Word, + current_value: WordCell, is_warm: Cell, is_warm_prev: Cell, callee_reversion_info: ReversionInfo, transfer: TransferGadget, - code_hash_previous: Cell, + code_hash_previous: WordCell, #[cfg(feature = "scroll")] - keccak_code_hash_previous: Cell, + keccak_code_hash_previous: WordCell, // current handling Call* opcode's caller balance - caller_balance_word: Word, + caller_balance: WordCell, // check if insufficient balance case is_insufficient_balance: LtWordGadget, is_depth_ok: LtGadget, one_64th_gas: ConstantDivisionGadget, capped_callee_gas_left: MinMaxGadget, // to handle precompile calls - is_code_address_zero: IsZeroGadget, - is_precompile_lt: LtGadget, + is_code_address_zero: IsZeroWordGadget>>, + + is_precompile_lt: LtWordGadget, precompile_gadget: PrecompileGadget, precompile_return_length: Cell, precompile_return_length_zero: IsZeroGadget, @@ -108,18 +116,16 @@ impl ExecutionGadget for CallOpGadget { // rwc_delta = 1 let mut reversion_info = cb.reversion_info_read(None); // rwc_delta = 3 - let [is_static, depth, current_callee_address] = [ - CallContextFieldTag::IsStatic, - CallContextFieldTag::Depth, - CallContextFieldTag::CalleeAddress, - ] - .map(|field_tag| cb.call_context(None, field_tag)); + let [is_static, depth] = [CallContextFieldTag::IsStatic, CallContextFieldTag::Depth] + .map(|field_tag| cb.call_context(None, field_tag)); + let current_callee_address = + cb.call_context_read_as_word(None, CallContextFieldTag::CalleeAddress); // rwc_delta = 6 let (current_caller_address, current_value) = cb.condition(is_delegatecall.expr(), |cb| { ( - cb.call_context(None, CallContextFieldTag::CallerAddress), - cb.call_context_as_word(None, CallContextFieldTag::Value), + cb.call_context_read_as_word(None, CallContextFieldTag::CallerAddress), + cb.call_context_read_as_word(None, CallContextFieldTag::Value), ) }); // rwc_delta = 6 + is_delegatecall * 2 @@ -133,30 +139,30 @@ impl ExecutionGadget for CallOpGadget { ); // rwc_delta = 6 + is_delegatecall * 2 + call_gadget.rw_delta() cb.condition(not::expr(is_call.expr() + is_callcode.expr()), |cb| { - cb.require_zero( + cb.require_zero_word( "for non call/call code, value is zero", - call_gadget.value.expr(), + call_gadget.value.to_word(), ); }); - let caller_address = select::expr( + let caller_address = Word::select( is_delegatecall.expr(), - current_caller_address.expr(), - current_callee_address.expr(), + current_caller_address.to_word(), + current_callee_address.to_word(), ); - let callee_address = select::expr( + let callee_address = Word::select( is_callcode.expr() + is_delegatecall.expr(), - current_callee_address.expr(), - call_gadget.callee_address_expr(), + current_callee_address.to_word(), + call_gadget.callee_address(), ); // Add callee to access list let is_warm = cb.query_bool(); let is_warm_prev = cb.query_bool(); cb.require_true("callee add should be updated to warm", is_warm.expr()); - cb.account_access_list_write( + cb.account_access_list_write_unchecked( tx_id.expr(), - call_gadget.callee_address_expr(), + call_gadget.callee_address(), is_warm.expr(), is_warm_prev.expr(), Some(&mut reversion_info), @@ -164,7 +170,8 @@ impl ExecutionGadget for CallOpGadget { // rwc_delta = 7 + is_delegatecall * 2 + call_gadget.rw_delta() // Propagate rw_counter_end_of_reversion and is_persistent - let mut callee_reversion_info = cb.reversion_info_write(Some(callee_call_id.expr())); + let mut callee_reversion_info = + cb.reversion_info_write_unchecked(Some(callee_call_id.expr())); // rwc_delta = 7 + is_delegatecall * 2 + call_gadget.rw_delta() + // callee_reversion_info.rw_delta() cb.require_equal( @@ -187,16 +194,16 @@ impl ExecutionGadget for CallOpGadget { ); }); - let caller_balance_word = cb.query_word_rlc(); + let caller_balance = cb.query_word_unchecked(); cb.account_read( - caller_address.expr(), + caller_address.to_word(), AccountFieldTag::Balance, - caller_balance_word.expr(), + caller_balance.to_word(), ); // rwc_delta = 8 + is_delegatecall * 2 + call_gadget.rw_delta() + // callee_reversion_info.rw_delta() let is_insufficient_balance = - LtWordGadget::construct(cb, &caller_balance_word, &call_gadget.value); + LtWordGadget::construct(cb, &caller_balance.to_word(), &call_gadget.value.to_word()); // depth < 1025 let is_depth_ok = LtGadget::construct(cb, depth.expr(), 1025.expr()); @@ -215,9 +222,12 @@ impl ExecutionGadget for CallOpGadget { // whether the call is to a precompiled contract. // precompile contracts are stored from address 0x01 to 0x09. - let is_code_address_zero = IsZeroGadget::construct(cb, call_gadget.callee_address_expr()); - let is_precompile_lt = - LtGadget::construct(cb, call_gadget.callee_address_expr(), 0x0A.expr()); + let is_code_address_zero = IsZeroWordGadget::construct(cb, &call_gadget.callee_address()); + let is_precompile_lt = LtWordGadget::construct( + cb, + &call_gadget.callee_address(), + &Word::from_lo_unchecked(0xA.expr()), + ); let is_precompile = and::expr([ not::expr(is_code_address_zero.expr()), is_precompile_lt.expr(), @@ -240,19 +250,19 @@ impl ExecutionGadget for CallOpGadget { // skip the transfer (this is necessary for non-existing accounts, which // will not be crated when value is 0 and so the callee balance lookup // would be invalid). - let code_hash_previous = cb.query_cell(); + let code_hash_previous = cb.query_word_unchecked(); #[cfg(feature = "scroll")] - let keccak_code_hash_previous = cb.query_cell_phase2(); + let keccak_code_hash_previous = cb.query_word_unchecked(); let transfer = cb.condition(and::expr(&[is_call.expr(), is_precheck_ok.expr()]), |cb| { TransferGadget::construct( cb, - caller_address.expr(), - callee_address.expr(), + caller_address.to_word(), + callee_address.to_word(), not::expr(call_gadget.callee_not_exists.expr()), 0.expr(), - code_hash_previous.expr(), + code_hash_previous.to_word(), #[cfg(feature = "scroll")] - keccak_code_hash_previous.expr(), + keccak_code_hash_previous.to_word(), call_gadget.value.clone(), &mut callee_reversion_info, ) @@ -329,37 +339,35 @@ impl ExecutionGadget for CallOpGadget { for (field_tag, value) in [ ( CallContextFieldTag::IsSuccess, - call_gadget.is_success.expr(), + Word::from_lo_unchecked(call_gadget.is_success.expr()), ), ( CallContextFieldTag::CalleeAddress, - call_gadget.callee_address_expr(), + call_gadget.callee_address(), ), - (CallContextFieldTag::CallerId, cb.curr.state.call_id.expr()), - (CallContextFieldTag::IsRoot, 0.expr()), + ( + CallContextFieldTag::CallerId, + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), + ), + (CallContextFieldTag::IsRoot, Word::zero()), ( CallContextFieldTag::CallDataOffset, - call_gadget.cd_address.offset(), + Word::from_lo_unchecked(call_gadget.cd_address.offset()), ), ( CallContextFieldTag::CallDataLength, - call_gadget.cd_address.length(), + Word::from_lo_unchecked(call_gadget.cd_address.length()), ), ( CallContextFieldTag::ReturnDataOffset, - call_gadget.rd_address.offset(), + Word::from_lo_unchecked(call_gadget.rd_address.offset()), ), ( CallContextFieldTag::ReturnDataLength, - call_gadget.rd_address.length(), + Word::from_lo_unchecked(call_gadget.rd_address.length()), ), ] { - cb.call_context_lookup( - true.expr(), - Some(callee_call_id.expr()), - field_tag, - value, - ); + cb.call_context_lookup_write(Some(callee_call_id.expr()), field_tag, value); } // rwc_delta = 26 + is_call_or_callcode + transfer + is_delegatecall * 2 @@ -367,33 +375,51 @@ impl ExecutionGadget for CallOpGadget { for (field_tag, value) in [ ( CallContextFieldTag::ProgramCounter, - cb.curr.state.program_counter.expr() + 1.expr(), + Word::from_lo_unchecked(cb.curr.state.program_counter.expr() + 1.expr()), ), ( CallContextFieldTag::StackPointer, - cb.curr.state.stack_pointer.expr() - + select::expr(is_call.expr() + is_callcode.expr(), 6.expr(), 5.expr()), + Word::from_lo_unchecked( + cb.curr.state.stack_pointer.expr() + + select::expr( + is_call.expr() + is_callcode.expr(), + 6.expr(), + 5.expr(), + ), + ), ), ( CallContextFieldTag::GasLeft, - cb.curr.state.gas_left.expr() - gas_cost.expr() - callee_gas_left.clone(), + Word::from_lo_unchecked( + cb.curr.state.gas_left.expr() + - gas_cost.expr() + - callee_gas_left.clone(), + ), ), ( CallContextFieldTag::MemorySize, - memory_expansion.next_memory_word_size(), + Word::from_lo_unchecked(memory_expansion.next_memory_word_size()), ), ( CallContextFieldTag::ReversibleWriteCounter, - cb.curr.state.reversible_write_counter.expr() + 1.expr(), + Word::from_lo_unchecked( + cb.curr.state.reversible_write_counter.expr() + 1.expr(), + ), + ), + ( + CallContextFieldTag::LastCalleeId, + Word::from_lo_unchecked(callee_call_id.expr()), + ), + ( + CallContextFieldTag::LastCalleeReturnDataOffset, + Word::zero(), ), - (CallContextFieldTag::LastCalleeId, callee_call_id.expr()), - (CallContextFieldTag::LastCalleeReturnDataOffset, 0.expr()), ( CallContextFieldTag::LastCalleeReturnDataLength, - precompile_return_length.expr(), + Word::from_lo_unchecked(precompile_return_length.expr()), ), ] { - cb.call_context_lookup(true.expr(), None, field_tag, value); + cb.call_context_lookup_write(None, field_tag, value); } // rwc_delta = 34 + is_call_or_callcode + transfer + is_delegatecall * 2 @@ -404,9 +430,9 @@ impl ExecutionGadget for CallOpGadget { cb.condition(call_gadget.cd_address.has_length(), |cb| { let precompile_input_bytes_rlc = cb.query_cell_phase2(); cb.copy_table_lookup( - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), CopyDataType::RlcAcc.expr(), call_gadget.cd_address.offset(), call_gadget.cd_address.offset() + precompile_input_len.expr(), @@ -429,9 +455,9 @@ impl ExecutionGadget for CallOpGadget { |cb| { let precompile_output_bytes_rlc = cb.query_cell_phase2(); cb.copy_table_lookup( - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), CopyDataType::RlcAcc.expr(), - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), CopyDataType::Memory.expr(), 0.expr(), precompile_return_length.expr(), @@ -458,9 +484,9 @@ impl ExecutionGadget for CallOpGadget { |cb| { let precompile_return_bytes_rlc = cb.query_cell_phase2(); cb.copy_table_lookup( - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), CopyDataType::Memory.expr(), // refer u64::from(CopyDataType) - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), 0.expr(), precompile_return_data_copy_size.min(), @@ -489,7 +515,7 @@ impl ExecutionGadget for CallOpGadget { call_id: To(callee_call_id.expr()), is_root: To(false.expr()), is_create: To(false.expr()), - code_hash: To(cb.empty_code_hash_rlc()), + code_hash: To(cb.empty_code_hash()), program_counter: Delta(1.expr()), gas_left: To(callee_gas_left.expr()), memory_word_size: To(precompile_output_rws.expr()), @@ -499,7 +525,7 @@ impl ExecutionGadget for CallOpGadget { let precompile_gadget = PrecompileGadget::construct( cb, - call_gadget.callee_address_expr(), + call_gadget.callee_address(), precompile_input_bytes_rlc.expr(), Some(precompile_output_bytes_rlc.expr()), Some(precompile_return_bytes_rlc.expr()), @@ -523,17 +549,16 @@ impl ExecutionGadget for CallOpGadget { ]), |cb| { // Save caller's call state - cb.call_context_lookup( - true.expr(), + cb.call_context_lookup_write( None, CallContextFieldTag::LastCalleeId, - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), ); for field_tag in [ CallContextFieldTag::LastCalleeReturnDataOffset, CallContextFieldTag::LastCalleeReturnDataLength, ] { - cb.call_context_lookup(true.expr(), None, field_tag, 0.expr()); + cb.call_context_lookup_write(None, field_tag, Word::zero()); } // rwc_delta = 21 + is_call_or_callcode + transfer + is_delegatecall * 2 @@ -565,17 +590,16 @@ impl ExecutionGadget for CallOpGadget { // 4. handle ErrDepth or ErrInsufficientBalance step transition cb.condition(not::expr(is_precheck_ok.expr()), |cb| { // Save caller's call state - cb.call_context_lookup( - true.expr(), + cb.call_context_lookup_write( None, CallContextFieldTag::LastCalleeId, - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), ); for field_tag in [ CallContextFieldTag::LastCalleeReturnDataOffset, CallContextFieldTag::LastCalleeReturnDataLength, ] { - cb.call_context_lookup(true.expr(), None, field_tag, 0.expr()); + cb.call_context_lookup_write(None, field_tag, Word::zero()); } // rwc_delta = 21 + is_call_or_callcode + transfer + is_delegatecall * 2 @@ -626,7 +650,7 @@ impl ExecutionGadget for CallOpGadget { cb.curr.state.reversible_write_counter.expr() + 1.expr(), ), ] { - cb.call_context_lookup(true.expr(), None, field_tag, value); + cb.call_context_lookup_write(None, field_tag, Word::from_lo_unchecked(value)); } // rwc_delta = 23 + is_call_or_callcode + transfer + is_delegatecall * 2 @@ -634,47 +658,69 @@ impl ExecutionGadget for CallOpGadget { let cd_address = call_gadget.cd_address.clone(); let rd_address = call_gadget.rd_address.clone(); for (field_tag, value) in [ - (CallContextFieldTag::CallerId, cb.curr.state.call_id.expr()), - (CallContextFieldTag::TxId, tx_id.expr()), - (CallContextFieldTag::Depth, depth.expr() + 1.expr()), + ( + CallContextFieldTag::CallerId, + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), + ), + ( + CallContextFieldTag::TxId, + Word::from_lo_unchecked(tx_id.expr()), + ), + ( + CallContextFieldTag::Depth, + Word::from_lo_unchecked(depth.expr() + 1.expr()), + ), (CallContextFieldTag::CallerAddress, caller_address), (CallContextFieldTag::CalleeAddress, callee_address), - (CallContextFieldTag::CallDataOffset, cd_address.offset()), - (CallContextFieldTag::CallDataLength, cd_address.length()), - (CallContextFieldTag::ReturnDataOffset, rd_address.offset()), - (CallContextFieldTag::ReturnDataLength, rd_address.length()), + ( + CallContextFieldTag::CallDataOffset, + Word::from_lo_unchecked(cd_address.offset()), + ), + ( + CallContextFieldTag::CallDataLength, + Word::from_lo_unchecked(cd_address.length()), + ), + ( + CallContextFieldTag::ReturnDataOffset, + Word::from_lo_unchecked(rd_address.offset()), + ), + ( + CallContextFieldTag::ReturnDataLength, + Word::from_lo_unchecked(rd_address.length()), + ), ( CallContextFieldTag::Value, - select::expr( + Word::select( is_delegatecall.expr(), - current_value.expr(), - call_gadget.value.expr(), + current_value.to_word(), + call_gadget.value.to_word(), ), ), ( CallContextFieldTag::IsSuccess, - call_gadget.is_success.expr(), + Word::from_lo_unchecked(call_gadget.is_success.expr()), ), ( CallContextFieldTag::IsStatic, - or::expr([is_static.expr(), is_staticcall.expr()]), + Word::from_lo_unchecked(or::expr([is_static.expr(), is_staticcall.expr()])), ), - (CallContextFieldTag::LastCalleeId, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataOffset, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataLength, 0.expr()), - (CallContextFieldTag::IsRoot, 0.expr()), - (CallContextFieldTag::IsCreate, 0.expr()), + (CallContextFieldTag::LastCalleeId, Word::zero()), + ( + CallContextFieldTag::LastCalleeReturnDataOffset, + Word::zero(), + ), + ( + CallContextFieldTag::LastCalleeReturnDataLength, + Word::zero(), + ), + (CallContextFieldTag::IsRoot, Word::zero()), + (CallContextFieldTag::IsCreate, Word::zero()), ( CallContextFieldTag::CodeHash, - call_gadget.phase2_callee_code_hash.expr(), + call_gadget.callee_code_hash.to_word(), ), ] { - cb.call_context_lookup( - true.expr(), - Some(callee_call_id.expr()), - field_tag, - value, - ); + cb.call_context_lookup_write(Some(callee_call_id.expr()), field_tag, value); } // rwc_delta = 41 + is_call_or_callcode + transfer + is_delegatecall * 2 @@ -698,7 +744,7 @@ impl ExecutionGadget for CallOpGadget { call_id: To(callee_call_id.expr()), is_root: To(false.expr()), is_create: To(false.expr()), - code_hash: To(call_gadget.phase2_callee_code_hash.expr()), + code_hash: To(call_gadget.callee_code_hash.to_word()), gas_left: To(callee_gas_left), reversible_write_counter: To(callee_reversible_rwc_delta.expr()), ..StepStateTransition::new_context() @@ -727,7 +773,7 @@ impl ExecutionGadget for CallOpGadget { code_hash_previous, #[cfg(feature = "scroll")] keccak_code_hash_previous, - caller_balance_word, + caller_balance, is_insufficient_balance, is_depth_ok, one_64th_gas, @@ -803,8 +849,8 @@ impl ExecutionGadget for CallOpGadget { // check if it is insufficient balance case. // get caller balance let (caller_balance, _) = rws.next().account_balance_pair(); - self.caller_balance_word - .assign(region, offset, Some(caller_balance.to_le_bytes()))?; + self.caller_balance + .assign_u256(region, offset, caller_balance)?; self.is_insufficient_balance .assign(region, offset, caller_balance, value)?; @@ -822,20 +868,22 @@ impl ExecutionGadget for CallOpGadget { &mut rws, )?; if let Some(account_code_hash) = transfer_assign_result.account_code_hash { - self.code_hash_previous.assign( + self.code_hash_previous.assign_u256( region, offset, - region.code_hash(account_code_hash), + //region.code_hash(account_code_hash), + account_code_hash, )?; } #[cfg(feature = "scroll")] if let Some(account_keccak_code_hash) = transfer_assign_result.account_keccak_code_hash { - self.keccak_code_hash_previous.assign( + self.keccak_code_hash_previous.assign_u256( region, offset, - region.word_rlc(account_keccak_code_hash), + //region.word_rlc(account_keccak_code_hash), + account_keccak_code_hash, )?; } } @@ -870,26 +918,19 @@ impl ExecutionGadget for CallOpGadget { call.rw_counter_end_of_reversion, call.is_persistent, )?; - self.current_callee_address.assign( + self.current_callee_address.assign_h160( region, offset, - Value::known( - current_callee_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), + current_callee_address.to_address(), )?; - self.current_caller_address.assign( + self.current_caller_address.assign_h160( region, offset, - Value::known( - current_caller_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), + current_caller_address.to_address(), )?; + self.current_value - .assign(region, offset, Some(current_value.to_le_bytes()))?; + .assign_u256(region, offset, current_value)?; self.is_static .assign(region, offset, Value::known(F::from(is_static.low_u64())))?; self.depth @@ -907,7 +948,8 @@ impl ExecutionGadget for CallOpGadget { rd_offset, rd_length, step.memory_word_size(), - region.code_hash(callee_code_hash), + //region.code_hash(callee_code_hash), + callee_code_hash, )?; self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; @@ -944,11 +986,19 @@ impl ExecutionGadget for CallOpGadget { let is_precompiled_call = is_precompiled(&precompile_addr); (is_precompiled_call, precompile_addr) }; - let code_address: F = callee_address.to_address().to_scalar().unwrap(); - self.is_code_address_zero - .assign(region, offset, code_address)?; - self.is_precompile_lt - .assign(region, offset, code_address, 0x0Au64.into())?; + let code_address = callee_address; + self.is_code_address_zero.assign_value( + region, + offset, + Value::known(Word::from(code_address.to_address())), + )?; + self.is_precompile_lt.assign( + region, + offset, + callee_address.to_address().to_word(), + 0x0Au64.into(), + )?; + log::trace!("callop is precompile call {}", is_precompile_call); let precompile_return_length = if is_precompile_call && is_precheck_ok { rws.offset_add(15); // skip @@ -1141,7 +1191,6 @@ impl ExecutionGadget for CallOpGadget { self.precompile_gadget .assign(region, offset, precompile_addr.0[19].into())?; } - Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs index 12a9e95f2c..71c983bba9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs @@ -5,12 +5,15 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - CachedRegion, Cell, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -21,7 +24,7 @@ pub(crate) struct CallValueGadget { same_context: SameContextGadget, // Value in rw_table->stack_op and call_context->call_value are both RLC // encoded, so no need to decode. - call_value: Cell, + call_value: WordCell, } impl ExecutionGadget for CallValueGadget { @@ -30,18 +33,17 @@ impl ExecutionGadget for CallValueGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CALLVALUE; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let call_value = cb.query_cell_phase2(); + let call_value = cb.query_word_unchecked(); // Lookup rw_table -> call_context with call value - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, // cb.curr.state.call_id CallContextFieldTag::Value, - call_value.expr(), + call_value.to_word(), ); // Push the value to the stack - cb.stack_push(call_value.expr()); + cb.stack_push(call_value.to_word()); // State transition let opcode = cb.query_cell(); @@ -73,8 +75,7 @@ impl ExecutionGadget for CallValueGadget { let call_value = block.rws[step.rw_indices[1]].stack_value(); - self.call_value - .assign(region, offset, region.word_rlc(call_value))?; + self.call_value.assign_u256(region, offset, call_value)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/chainid.rs b/zkevm-circuits/src/evm_circuit/execution/chainid.rs index 0c368d8bbf..3e36ce6de3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/chainid.rs +++ b/zkevm-circuits/src/evm_circuit/execution/chainid.rs @@ -5,12 +5,15 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, CachedRegion, U64Word, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::BlockContextFieldTag, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -19,7 +22,7 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct ChainIdGadget { same_context: SameContextGadget, - chain_id: U64Word, + chain_id: WordCell, } impl ExecutionGadget for ChainIdGadget { @@ -28,16 +31,17 @@ impl ExecutionGadget for ChainIdGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::CHAINID; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let chain_id = cb.query_word_rlc(); + let chain_id = cb.query_word_unchecked(); // Push the value to the stack - cb.stack_push(chain_id.expr()); + cb.stack_push(chain_id.to_word()); // Lookup block table with chain_id cb.block_lookup( BlockContextFieldTag::ChainId.expr(), - cb.curr.state.block_number.expr(), - from_bytes::expr(&chain_id.cells), + Some(cb.curr.state.block_number.expr()), + //chain_id.to_word(), + chain_id.lo().expr(), ); // State transition @@ -68,9 +72,8 @@ impl ExecutionGadget for ChainIdGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let chain_id = block.rws[step.rw_indices[0]].stack_value().as_u64(); - self.chain_id - .assign(region, offset, Some(chain_id.to_le_bytes()))?; + let chain_id = block.rws[step.rw_indices[0]].stack_value(); + self.chain_id.assign_u256(region, offset, chain_id)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs index d55435f17a..31b2df24a9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs @@ -19,7 +19,10 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, WordExpr}, + Expr, + }, }; use super::ExecutionGadget; @@ -56,14 +59,14 @@ impl ExecutionGadget for CodeCopyGadget { let code_size = cb.query_cell(); - let size = cb.query_word_rlc(); - let dst_memory_offset = cb.query_cell_phase2(); + let size = cb.query_memory_address(); + let dst_memory_offset = cb.query_word_unchecked(); let code_offset = WordByteCapGadget::construct(cb, code_size.expr()); // Pop items from stack. - cb.stack_pop(dst_memory_offset.expr()); - cb.stack_pop(code_offset.original_word()); - cb.stack_pop(size.expr()); + cb.stack_pop(dst_memory_offset.to_word()); + cb.stack_pop(code_offset.original_word().to_word()); + cb.stack_pop(Word::from_lo_unchecked(size.expr())); // Construct memory address in the destionation (memory) to which we copy code. let dst_memory_addr = MemoryAddressGadget::construct(cb, dst_memory_offset, size); @@ -72,12 +75,12 @@ impl ExecutionGadget for CodeCopyGadget { let code_hash = cb.curr.state.code_hash.clone(); // Fetch the bytecode length from the bytecode table. - cb.bytecode_length(code_hash.expr(), code_size.expr()); + cb.bytecode_length(code_hash.to_word(), code_size.expr()); // Calculate the next memory size and the gas cost for this memory // access. This also accounts for the dynamic gas required to copy bytes to // memory. - let memory_expansion = MemoryExpansionGadget::construct(cb, [dst_memory_addr.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [dst_memory_addr.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, dst_memory_addr.length(), @@ -94,9 +97,9 @@ impl ExecutionGadget for CodeCopyGadget { ); cb.copy_table_lookup( - code_hash.expr(), + code_hash.to_word(), CopyDataType::Bytecode.expr(), - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), src_addr, code_size.expr(), diff --git a/zkevm-circuits/src/evm_circuit/execution/codesize.rs b/zkevm-circuits/src/evm_circuit/execution/codesize.rs index bd98f3ea76..277f9dc682 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codesize.rs @@ -1,4 +1,3 @@ -use array_init::array_init; use bus_mapping::evm::OpcodeId; use eth_types::Field; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -11,11 +10,11 @@ use crate::{ constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, }, - from_bytes, CachedRegion, Cell, + CachedRegion, Cell, U64Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{word::WordExpr, Expr}, }; use super::ExecutionGadget; @@ -23,7 +22,7 @@ use super::ExecutionGadget; #[derive(Clone, Debug)] pub(crate) struct CodesizeGadget { same_context: SameContextGadget, - codesize_bytes: [Cell; 8], + codesize_bytes: U64Cell, codesize: Cell, } @@ -35,19 +34,19 @@ impl ExecutionGadget for CodesizeGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let codesize_bytes = array_init(|_| cb.query_byte()); + let codesize_bytes = cb.query_u64(); let code_hash = cb.curr.state.code_hash.clone(); let codesize = cb.query_cell(); - cb.bytecode_length(code_hash.expr(), codesize.expr()); + cb.bytecode_length(code_hash.to_word(), codesize.expr()); cb.require_equal( "Constraint: bytecode length lookup == codesize", - from_bytes::expr(&codesize_bytes), + codesize_bytes.expr(), codesize.expr(), ); - cb.stack_push(cb.word_rlc(codesize_bytes.clone().map(|c| c.expr()))); + cb.stack_push(codesize_bytes.to_word()); let step_state_transition = StepStateTransition { gas_left: Transition::Delta(-OpcodeId::CODESIZE.constant_gas_cost().expr()), @@ -78,13 +77,8 @@ impl ExecutionGadget for CodesizeGadget { let codesize = block.rws[step.rw_indices[0]].stack_value().as_u64(); - for (c, b) in self - .codesize_bytes - .iter() - .zip(codesize.to_le_bytes().iter()) - { - c.assign(region, offset, Value::known(F::from(*b as u64)))?; - } + self.codesize_bytes + .assign(region, offset, Some(codesize.to_le_bytes()))?; self.codesize .assign(region, offset, Value::known(F::from(codesize)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/comparator.rs b/zkevm-circuits/src/evm_circuit/execution/comparator.rs index c11803c352..c1660bf767 100644 --- a/zkevm-circuits/src/evm_circuit/execution/comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/comparator.rs @@ -5,27 +5,28 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, - math_gadget::{ComparisonGadget, IsEqualGadget}, - select, CachedRegion, Cell, Word, + math_gadget::{CmpWordsGadget, IsEqualGadget}, + select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; +use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ComparatorGadget { same_context: SameContextGadget, - a: Word, - b: Word, + a: WordCell, + b: WordCell, result: Cell, - comparison_lo: ComparisonGadget, - comparison_hi: ComparisonGadget, is_eq: IsEqualGadget, is_gt: IsEqualGadget, + word_comparison: CmpWordsGadget, WordCell>, } impl ExecutionGadget for ComparatorGadget { @@ -36,8 +37,8 @@ impl ExecutionGadget for ComparatorGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); + let a = cb.query_word_unchecked(); + let b = cb.query_word_unchecked(); // Check if opcode is EQ let is_eq = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::EQ.expr()); @@ -45,42 +46,25 @@ impl ExecutionGadget for ComparatorGadget { // actually do greater than instead of smaller than. let is_gt = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::GT.expr()); - // `a[0..16] <= b[0..16]` - let comparison_lo = ComparisonGadget::construct( - cb, - from_bytes::expr(&a.cells[0..16]), - from_bytes::expr(&b.cells[0..16]), - ); - let (lt_lo, eq_lo) = comparison_lo.expr(); - - // `a[16..32] <= b[16..32]` - let comparison_hi = ComparisonGadget::construct( - cb, - from_bytes::expr(&a.cells[16..32]), - from_bytes::expr(&b.cells[16..32]), - ); - let (lt_hi, eq_hi) = comparison_hi.expr(); - - // `a < b` when: - // - `a[16..32] < b[16..32]` OR - // - `a[16..32] == b[16..32]` AND `a[0..16] < b[0..16]` - let lt = select::expr(lt_hi, 1.expr(), eq_hi.clone() * lt_lo); - // `a == b` when both parts are equal - let eq = eq_hi * eq_lo; + let word_comparison = CmpWordsGadget::construct(cb, a.clone(), b.clone()); // The result is: // - `lt` when LT or GT // - `eq` when EQ // Use copy to avoid degree too high for stack_push below. - let result = cb.copy(select::expr(is_eq.expr(), eq, lt)); + let result = cb.copy(select::expr( + is_eq.expr(), + word_comparison.eq.clone(), + word_comparison.lt.clone(), + )); // Pop a and b from the stack, push the result on the stack. // When swap is enabled we swap stack places between a and b. // We can push result here directly because // it only uses the LSB of a word. - cb.stack_pop(select::expr(is_gt.expr(), b.expr(), a.expr())); - cb.stack_pop(select::expr(is_gt.expr(), a.expr(), b.expr())); - cb.stack_push(result.expr()); + cb.stack_pop(Word::select(is_gt.expr(), b.to_word(), a.to_word())); + cb.stack_pop(Word::select(is_gt.expr(), a.to_word(), b.to_word())); + cb.stack_push(Word::from_lo_unchecked(result.expr())); // State transition let step_state_transition = StepStateTransition { @@ -97,8 +81,7 @@ impl ExecutionGadget for ComparatorGadget { a, b, result, - comparison_lo, - comparison_hi, + word_comparison, is_eq, is_gt, } @@ -138,27 +121,13 @@ impl ExecutionGadget for ComparatorGadget { } else { [step.rw_indices[0], step.rw_indices[1]] }; - let [a, b] = indices.map(|idx| block.rws[idx].stack_value().to_le_bytes()); + let [a, b] = indices.map(|idx| block.rws[idx].stack_value()); let result = block.rws[step.rw_indices[2]].stack_value(); - // `a[0..16] <= b[0..16]` - self.comparison_lo.assign( - region, - offset, - from_bytes::value(&a[0..16]), - from_bytes::value(&b[0..16]), - )?; - - // `a[16..32] <= b[16..32]` - self.comparison_hi.assign( - region, - offset, - from_bytes::value(&a[16..32]), - from_bytes::value(&b[16..32]), - )?; + self.word_comparison.assign(region, offset, a, b)?; + self.a.assign_u256(region, offset, a)?; + self.b.assign_u256(region, offset, b)?; - self.a.assign(region, offset, Some(a))?; - self.b.assign(region, offset, Some(b))?; self.result .assign(region, offset, Value::known(F::from(result.low_u64())))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index 5bc94c6420..1a9796a186 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -13,26 +13,33 @@ use crate::{ Transition::{Delta, To}, }, math_gadget::{ - ConstantDivisionGadget, ContractCreateGadget, IsZeroGadget, LtGadget, LtWordGadget, + ConstantDivisionGadget, ContractCreateGadget, IsEqualWordGadget, IsZeroGadget, + IsZeroWordGadget, LtGadget, LtWordGadget, }, memory_gadget::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryExpansionGadget, }, - not, CachedRegion, Cell, StepRws, Word, + not, or, AccountAddress, CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag, RwTableTag}, - util::Expr, + util::{ + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId, state_db::CodeDB}; use eth_types::{ evm_types::{GasCost, CREATE2_GAS_PER_CODE_WORD, CREATE_GAS_PER_CODE_WORD, MAX_INIT_CODE_SIZE}, - Field, ToBigEndian, ToLittleEndian, ToScalar, ToWord, H256, KECCAK_CODE_HASH_EMPTY, U256, + Field, ToBigEndian, ToScalar, ToWord, H256, KECCAK_CODE_HASH_EMPTY, U256, }; use ethers_core::utils::keccak256; -use gadgets::util::{and, expr_from_bytes}; -use halo2_proofs::{circuit::Value, plonk::Error}; +use gadgets::util::and; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; use log::trace; use std::iter::once; @@ -46,13 +53,13 @@ pub(crate) struct CreateGadget, was_warm: Cell, - value: Word, + value: Word32Cell, - caller_balance: Word, + caller_balance: Word32Cell, callee_reversion_info: ReversionInfo, callee_nonce: Cell, - prev_code_hash: Cell, - prev_code_hash_is_zero: IsZeroGadget, + prev_code_hash: WordCell, + prev_code_hash_is_zero: IsZeroWordGadget>>, transfer: TransferGadget, create: ContractCreateGadget, @@ -63,19 +70,21 @@ pub(crate) struct CreateGadget, init_code_rlc: Cell, - keccak_output: Word, + keccak_output: Word32Cell, is_depth_in_range: LtGadget, is_insufficient_balance: LtWordGadget, is_nonce_in_range: LtGadget, - not_address_collision: IsZeroGadget, + // both is_callee_once_zero and is_empty_callee_prev_hash are for checking address collision. + is_callee_once_zero: IsZeroGadget, + is_empty_callee_prev_hash: IsEqualWordGadget>, Word>>, memory_expansion: MemoryExpansionGadget, gas_left: ConstantDivisionGadget, // check address collision use - keccak_code_hash: Cell, + keccak_code_hash: Word32Cell, #[cfg(feature = "scroll")] - prev_keccak_code_hash: Cell, + prev_keccak_code_hash: WordCell, copy_rw_increase: Cell, } @@ -116,28 +125,25 @@ impl ExecutionGadget< let is_static = cb.call_context(None, CallContextFieldTag::IsStatic); cb.require_zero("is_static is false", is_static.expr()); - let keccak_output = cb.query_word_rlc(); + // query_word_rlc take use of 32 bytes to rlc. + let keccak_output = cb.query_word32(); let create = ContractCreateGadget::construct(cb); - let contract_addr_rlc = cb.word_rlc::( - keccak_output - .cells - .iter() - .take(N_BYTES_ACCOUNT_ADDRESS) - .map(Expr::expr) - .collect::>() + let contract_addr = AccountAddress::new( + keccak_output.limbs[..N_BYTES_ACCOUNT_ADDRESS] + .to_vec() .try_into() .unwrap(), ); - let contract_addr = expr_from_bytes(&keccak_output.cells[..N_BYTES_ACCOUNT_ADDRESS]); // stack operations - let value = cb.query_word_rlc(); + let value = cb.query_word32(); - let init_code_memory_offset = cb.query_cell_phase2(); - let init_code_length = cb.query_word_rlc(); + // init_code_length + let length = cb.query_memory_address(); + // init_code_memory_offset + let offset = cb.query_word_unchecked(); - let init_code = - MemoryAddressGadget::construct(cb, init_code_memory_offset, init_code_length); + let init_code = MemoryAddressGadget::construct(cb, offset, length); let init_code_size_not_overflow = LtGadget::construct(cb, init_code.length(), MAX_INIT_CODE_SIZE.expr() + 1.expr()); @@ -149,33 +155,33 @@ impl ExecutionGadget< 1.expr(), ); - cb.stack_pop(value.expr()); - cb.stack_pop(init_code.offset_rlc()); - cb.stack_pop(init_code.length_rlc()); + cb.stack_pop(value.to_word()); + cb.stack_pop(init_code.offset_word()); + cb.stack_pop(init_code.length_word()); if IS_CREATE2 { - cb.stack_pop(create.salt_word_rlc(cb)); + cb.stack_pop(create.salt()); } - cb.stack_push(is_success.expr() * contract_addr_rlc); + cb.stack_push(contract_addr.to_word().mul_selector(is_success.expr())); let (init_code_rlc, keccak_code_hash) = cb.condition(init_code.has_length(), |cb| { // the init code is being copied from memory to bytecode, so a copy table lookup to // verify that the associated fields for the copy event. - let keccak_code_hash = cb.query_cell_phase2(); + let keccak_code_hash = cb.query_word32(); let init_code_rlc = cb.query_cell_phase2(); (init_code_rlc, keccak_code_hash) }); cb.condition(not::expr(init_code.has_length()), |cb| { - cb.require_equal( + cb.require_equal_word( "keccak hash of empty bytes", - keccak_code_hash.expr(), - cb.empty_keccak_hash_rlc(), + keccak_code_hash.to_word(), + cb.empty_keccak_hash(), ); - cb.require_equal( + cb.require_equal_word( "code hash of empty bytes", - create.code_hash_word_rlc(), - cb.empty_code_hash_rlc(), + create.code_hash(), + cb.empty_code_hash(), ); }); @@ -188,21 +194,22 @@ impl ExecutionGadget< // read caller's balance and nonce let caller_nonce = create.caller_nonce(); - let caller_balance = cb.query_word_rlc(); + let caller_balance = cb.query_word32(); cb.account_read( create.caller_address(), AccountFieldTag::Balance, - caller_balance.expr(), + caller_balance.to_word(), ); cb.account_read( create.caller_address(), AccountFieldTag::Nonce, - caller_nonce.expr(), + Word::from_lo_unchecked(caller_nonce.expr()), ); // Pre-check: call depth, user's nonce and user's balance let is_depth_in_range = LtGadget::construct(cb, depth.expr(), 1025.expr()); - let is_insufficient_balance = LtWordGadget::construct(cb, &caller_balance, &value); + let is_insufficient_balance = + LtWordGadget::construct(cb, &caller_balance.to_word(), &value.to_word()); let is_nonce_in_range = LtGadget::construct(cb, caller_nonce.expr(), u64::MAX.expr()); cb.condition(is_insufficient_balance.expr(), |cb| { @@ -231,7 +238,7 @@ impl ExecutionGadget< ]); // verify gas cost - let memory_expansion = MemoryExpansionGadget::construct(cb, [init_code.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [init_code.address()]); let init_code_word_size = ConstantDivisionGadget::construct( cb, init_code.length() + (N_BYTES_WORD - 1).expr(), @@ -251,79 +258,88 @@ impl ExecutionGadget< let callee_gas_left = gas_remaining - gas_left.quotient(); let was_warm = cb.query_bool(); - let prev_code_hash = cb.query_cell(); + let prev_code_hash = cb.query_word_unchecked(); #[cfg(feature = "scroll")] - let prev_keccak_code_hash = cb.query_cell_phase2(); + let prev_keccak_code_hash = cb.query_word_unchecked(); let callee_nonce = cb.query_cell(); + let is_callee_once_zero = IsZeroGadget::construct(cb, callee_nonce.expr()); + let is_empty_callee_prev_hash = + IsEqualWordGadget::construct(cb, &prev_code_hash.to_word(), &cb.empty_code_hash()); // callee address's nonce - let (prev_code_hash_is_zero, not_address_collision) = - cb.condition(is_precheck_ok.expr(), |cb| { - // increase caller's nonce - cb.account_write( - create.caller_address(), - AccountFieldTag::Nonce, - caller_nonce.expr() + 1.expr(), - caller_nonce, - Some(&mut reversion_info), - ); + let prev_code_hash_is_zero = cb.condition(is_precheck_ok.expr(), |cb| { + // increase caller's nonce + cb.account_write( + create.caller_address(), + AccountFieldTag::Nonce, + Word::from_lo_unchecked(caller_nonce.expr() + 1.expr()), + Word::from_lo_unchecked(caller_nonce.expr()), + Some(&mut reversion_info), + ); - // add callee to access list - cb.account_access_list_write( - tx_id.expr(), - contract_addr.clone(), - 1.expr(), - was_warm.expr(), - Some(&mut reversion_info), - ); + // add callee to access list + cb.account_access_list_write_unchecked( + tx_id.expr(), + contract_addr.to_word(), + 1.expr(), + was_warm.expr(), + Some(&mut reversion_info), + ); - // read contract's previous hash + // read contract's previous hash + cb.account_read( + contract_addr.to_word(), + AccountFieldTag::CodeHash, + prev_code_hash.to_word(), + ); + + let prev_code_hash_is_zero = IsZeroWordGadget::construct(cb, &prev_code_hash); + cb.condition(not::expr(prev_code_hash_is_zero.expr()), |cb| { cb.account_read( - contract_addr.clone(), - AccountFieldTag::CodeHash, - prev_code_hash.expr(), + contract_addr.to_word(), + AccountFieldTag::Nonce, + Word::from_lo_unchecked(callee_nonce.expr()), ); - let prev_code_hash_is_zero = IsZeroGadget::construct(cb, prev_code_hash.expr()); - cb.condition(not::expr(prev_code_hash_is_zero.expr()), |cb| { - cb.account_read( - contract_addr.clone(), - AccountFieldTag::Nonce, - callee_nonce.expr(), - ); - }); - // ErrContractAddressCollision, if any one of following criteria meets. - // Nonce is not zero or account code hash is not either 0 or EMPTY_CODE_HASH. - // Here use `isZeroGadget(callee_nonce + prev_code_hash * (prev_code_hash - - // empty_code_hash))` to represent `(callee_nonce == 0 && (prev_code_hash == 0 - // or prev_code_hash == empty_code_hash))` - ( - prev_code_hash_is_zero, - IsZeroGadget::construct( - cb, - callee_nonce.expr() - + prev_code_hash.expr() - * (prev_code_hash.expr() - cb.empty_code_hash_rlc()), - ), - ) }); + prev_code_hash_is_zero + }); + + // ErrContractAddressCollision, if any one of following criteria meets. + // Nonce is not zero or account code hash is not either 0 or EMPTY_CODE_HASH. + // Here use `isZeroGadget(callee_nonce + prev_code_hash * (prev_code_hash - + // empty_code_hash))` to represent `(callee_nonce == 0 && (prev_code_hash == 0 + // or prev_code_hash == empty_code_hash))` + let not_address_collision = and::expr([ + is_callee_once_zero.expr(), + or::expr([ + is_empty_callee_prev_hash.expr(), + prev_code_hash_is_zero.expr(), + ]), + ]); + for (field_tag, value) in [ ( CallContextFieldTag::ProgramCounter, - cb.curr.state.program_counter.expr() + 1.expr(), + Word::from_lo_unchecked(cb.curr.state.program_counter.expr() + 1.expr()), ), ( CallContextFieldTag::StackPointer, - cb.curr.state.stack_pointer.expr() + 2.expr() + IS_CREATE2.expr(), + Word::from_lo_unchecked( + cb.curr.state.stack_pointer.expr() + 2.expr() + IS_CREATE2.expr(), + ), + ), + ( + CallContextFieldTag::GasLeft, + Word::from_lo_unchecked(gas_left.quotient()), ), - (CallContextFieldTag::GasLeft, gas_left.quotient()), ( CallContextFieldTag::MemorySize, - memory_expansion.next_memory_word_size(), + Word::from_lo_unchecked(memory_expansion.next_memory_word_size()), ), ( CallContextFieldTag::ReversibleWriteCounter, - cb.curr.state.reversible_write_counter.expr() + 2.expr(), + Word::from_lo_unchecked(cb.curr.state.reversible_write_counter.expr() + 2.expr()), ), ] { cb.call_context_lookup(true.expr(), None, field_tag, value); @@ -334,12 +350,12 @@ impl ExecutionGadget< // the init code is being copied from memory to bytecode, so a copy table lookup // to verify that the associated fields for the copy event. cb.copy_table_lookup( - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), - create.code_hash_word_rlc(), + create.code_hash(), CopyDataType::Bytecode.expr(), init_code.offset(), - init_code.end_offset(), + init_code.address(), 0.expr(), init_code.length(), init_code_rlc.expr(), @@ -358,13 +374,13 @@ impl ExecutionGadget< true.expr(), None, CallContextFieldTag::LastCalleeId, - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), ); for field_tag in [ CallContextFieldTag::LastCalleeReturnDataOffset, CallContextFieldTag::LastCalleeReturnDataLength, ] { - cb.call_context_lookup(true.expr(), None, field_tag, 0.expr()); + cb.call_context_lookup(true.expr(), None, field_tag, Word::zero()); } cb.require_step_state_transition(StepStateTransition { @@ -382,18 +398,27 @@ impl ExecutionGadget< and::expr([is_precheck_ok.clone(), not_address_collision.expr()]), |cb| { cb.condition(init_code.has_length(), |cb| { + let keccak_code_hash_rlc = + cb.word_rlc(keccak_code_hash.limbs.clone().map(|l| l.expr())); cb.keccak_table_lookup( + //create.input_rlc(cb), init_code_rlc.expr(), + //create.input_length(), init_code.length(), - keccak_code_hash.expr(), + keccak_code_hash_rlc, + keccak_code_hash.to_word(), ); }); // keccak table lookup to verify contract address. + let kecck_output_exprs = keccak_output.limbs.clone().map(|l| l.expr()); + let keccak_output_rlc = cb.word_rlc(kecck_output_exprs); cb.keccak_table_lookup( create.input_rlc(cb), create.input_length(), - keccak_output.expr(), + keccak_output_rlc, + //keccak_output.expr(), + keccak_output.to_word(), ); // propagate is_persistent @@ -407,45 +432,68 @@ impl ExecutionGadget< let transfer = TransferGadget::construct( cb, create.caller_address(), - contract_addr.clone(), + contract_addr.to_word(), 0.expr(), 1.expr(), - prev_code_hash.expr(), + prev_code_hash.to_word(), #[cfg(feature = "scroll")] - prev_keccak_code_hash.expr(), + prev_keccak_code_hash.to_word(), value.clone(), &mut callee_reversion_info, ); // EIP 161, the nonce of a newly created contract is 1 cb.account_write( - contract_addr.clone(), + contract_addr.to_word(), AccountFieldTag::Nonce, - 1.expr(), - 0.expr(), + Word::one(), + Word::zero(), Some(&mut callee_reversion_info), ); cb.condition(init_code.has_length(), |cb| { for (field_tag, value) in [ - (CallContextFieldTag::CallerId, current_call_id.expr()), - (CallContextFieldTag::IsSuccess, is_success.expr()), + ( + CallContextFieldTag::CallerId, + Word::from_lo_unchecked(current_call_id.expr()), + ), + ( + CallContextFieldTag::IsSuccess, + Word::from_lo_unchecked(is_success.expr()), + ), ( CallContextFieldTag::IsPersistent, - callee_reversion_info.is_persistent(), + Word::from_lo_unchecked(callee_reversion_info.is_persistent()), + ), + ( + CallContextFieldTag::TxId, + Word::from_lo_unchecked(tx_id.expr()), ), - (CallContextFieldTag::TxId, tx_id.expr()), (CallContextFieldTag::CallerAddress, create.caller_address()), - (CallContextFieldTag::CalleeAddress, contract_addr), + (CallContextFieldTag::CalleeAddress, contract_addr.to_word()), ( CallContextFieldTag::RwCounterEndOfReversion, - callee_reversion_info.rw_counter_end_of_reversion(), + Word::from_lo_unchecked( + callee_reversion_info.rw_counter_end_of_reversion(), + ), + ), + ( + CallContextFieldTag::Depth, + Word::from_lo_unchecked(depth.expr() + 1.expr()), + ), + ( + CallContextFieldTag::IsRoot, + Word::from_lo_unchecked(false.expr()), + ), + ( + CallContextFieldTag::IsStatic, + Word::from_lo_unchecked(false.expr()), ), - (CallContextFieldTag::Depth, depth.expr() + 1.expr()), - (CallContextFieldTag::IsRoot, false.expr()), - (CallContextFieldTag::IsStatic, false.expr()), - (CallContextFieldTag::IsCreate, true.expr()), - (CallContextFieldTag::CodeHash, create.code_hash_word_rlc()), - (CallContextFieldTag::Value, value.expr()), + ( + CallContextFieldTag::IsCreate, + Word::from_lo_unchecked(true.expr()), + ), + (CallContextFieldTag::CodeHash, create.code_hash()), + (CallContextFieldTag::Value, value.to_word()), ] { cb.call_context_lookup( true.expr(), @@ -459,7 +507,7 @@ impl ExecutionGadget< call_id: To(callee_call_id.expr()), is_root: To(false.expr()), is_create: To(true.expr()), - code_hash: To(create.code_hash_word_rlc()), + code_hash: To(create.code_hash()), gas_left: To(callee_gas_left), reversible_write_counter: To(1.expr() + transfer.reversible_w_delta()), ..StepStateTransition::new_context() @@ -471,13 +519,13 @@ impl ExecutionGadget< true.expr(), None, CallContextFieldTag::LastCalleeId, - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), ); for field_tag in [ CallContextFieldTag::LastCalleeReturnDataOffset, CallContextFieldTag::LastCalleeReturnDataLength, ] { - cb.call_context_lookup(true.expr(), None, field_tag, 0.expr()); + cb.call_context_lookup(true.expr(), None, field_tag, Word::zero()); } cb.require_step_state_transition(StepStateTransition { rw_counter: Delta(cb.rw_counter_offset()), @@ -512,13 +560,13 @@ impl ExecutionGadget< true.expr(), None, CallContextFieldTag::LastCalleeId, - callee_call_id.expr(), + Word::from_lo_unchecked(callee_call_id.expr()), ); for field_tag in [ CallContextFieldTag::LastCalleeReturnDataOffset, CallContextFieldTag::LastCalleeReturnDataLength, ] { - cb.call_context_lookup(true.expr(), None, field_tag, 0.expr()); + cb.call_context_lookup(true.expr(), None, field_tag, Word::zero()); } cb.require_step_state_transition(StepStateTransition { rw_counter: Delta(cb.rw_counter_offset()), @@ -555,7 +603,9 @@ impl ExecutionGadget< callee_nonce, keccak_code_hash, keccak_output, - not_address_collision, + //not_address_collision, + is_callee_once_zero, + is_empty_callee_prev_hash, is_success, prev_code_hash, prev_code_hash_is_zero, @@ -576,6 +626,7 @@ impl ExecutionGadget< ) -> Result<(), Error> { let opcode = step.opcode.unwrap(); let is_create2 = opcode == OpcodeId::CREATE2; + self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -599,8 +650,7 @@ impl ExecutionGadget< rws.offset_add(5); let [value, init_code_start, init_code_length] = [(); 3].map(|_| rws.next().stack_value()); - self.value - .assign(region, offset, Some(value.to_le_bytes()))?; + self.value.assign_u256(region, offset, value)?; let salt = if is_create2 { rws.next().stack_value() } else { @@ -616,7 +666,7 @@ impl ExecutionGadget< call.depth < 1025 && caller_balance >= value && caller_nonce < u64::MAX; self.caller_balance - .assign(region, offset, Some(caller_balance.to_le_bytes()))?; + .assign_u256(region, offset, caller_balance)?; let (was_warm, callee_prev_code_hash, callee_nonce) = if is_precheck_ok { rws.next(); // caller nonce += 1 @@ -643,18 +693,20 @@ impl ExecutionGadget< && callee_prev_code_hash != CodeDB::empty_code_hash().to_word(); let is_address_collision = !callee_nonce_is_zero || codehash_non_empty; - let code_hash_previous_rlc = region.code_hash(callee_prev_code_hash); + //let code_hash_previous_rlc = region.code_hash(callee_prev_code_hash); self.prev_code_hash - .assign(region, offset, code_hash_previous_rlc)?; + .assign_u256(region, offset, callee_prev_code_hash)?; self.prev_code_hash_is_zero - .assign_value(region, offset, code_hash_previous_rlc)?; - self.not_address_collision.assign_value( + .assign_u256(region, offset, callee_prev_code_hash)?; + + self.is_callee_once_zero + .assign(region, offset, F::from(callee_nonce))?; + self.is_empty_callee_prev_hash.assign_u256( region, offset, - Value::known(F::from(callee_nonce)) - + code_hash_previous_rlc * (code_hash_previous_rlc - region.empty_code_hash_rlc()), + callee_prev_code_hash, + CodeDB::empty_code_hash().to_word(), )?; - let shift = init_code_start.low_u64() % 32; let copy_rw_increase: u64 = step.copy_rw_counter_delta; let values: Vec = if is_precheck_ok { @@ -721,17 +773,17 @@ impl ExecutionGadget< self.callee_nonce .assign(region, offset, Value::known(F::from(callee_nonce)))?; } - let (code_hash, keccak_code_hash) = if is_precheck_ok { + let (_code_hash, keccak_code_hash) = if is_precheck_ok { if !is_address_collision { let _transfer_assign_result = self .transfer .assign_from_rws(region, offset, false, true, value, &mut rws)?; #[cfg(feature = "scroll")] - self.prev_keccak_code_hash.assign( + self.prev_keccak_code_hash.assign_u256( region, offset, - region.word_rlc(_transfer_assign_result.account_keccak_code_hash.unwrap()), + _transfer_assign_result.account_keccak_code_hash.unwrap(), )?; } @@ -758,9 +810,11 @@ impl ExecutionGadget< }; let mut keccak_output = keccak256(keccak_input); keccak_output.reverse(); - - self.keccak_output - .assign(region, offset, Some(keccak_output))?; + self.keccak_output.assign_u256( + region, + offset, + U256::from_little_endian(&keccak_output), + )?; self.init_code_rlc.assign( region, @@ -772,6 +826,7 @@ impl ExecutionGadget< (CodeDB::empty_code_hash(), *KECCAK_CODE_HASH_EMPTY) }; + let code_hash = CodeDB::hash(&values); self.create.assign( region, offset, @@ -802,11 +857,8 @@ impl ExecutionGadget< }), )?; - self.keccak_code_hash.assign( - region, - offset, - region.word_rlc(keccak_code_hash.to_word()), - )?; + self.keccak_code_hash + .assign_u256(region, offset, keccak_code_hash.to_word())?; self.copy_rw_increase.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/dummy.rs b/zkevm-circuits/src/evm_circuit/execution/dummy.rs index 653a9acf7d..431c9e9b2f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dummy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dummy.rs @@ -1,19 +1,22 @@ use std::marker::PhantomData; -use crate::evm_circuit::{ - execution::ExecutionGadget, - step::ExecutionState, - util::{constraint_builder::EVMConstraintBuilder, CachedRegion, Word}, - witness::{Block, Call, ExecStep, Transaction}, +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + step::ExecutionState, + util::{constraint_builder::EVMConstraintBuilder, CachedRegion}, + witness::{Block, Call, ExecStep, Transaction}, + }, + util::word::WordCell, }; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct DummyGadget { - pops: [Word; N_POP], - pushes: [Word; N_PUSH], + pops: [WordCell; N_POP], + pushes: [WordCell; N_PUSH], _marker: PhantomData, } @@ -25,8 +28,8 @@ impl const EXECUTION_STATE: ExecutionState = S; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let pops: [Word; N_POP] = [(); N_POP].map(|_| cb.query_word_rlc()); - let pushes: [Word; N_PUSH] = [(); N_PUSH].map(|_| cb.query_word_rlc()); + let pops: [WordCell; N_POP] = [(); N_POP].map(|_| cb.query_word_unchecked()); + let pushes: [WordCell; N_PUSH] = [(); N_PUSH].map(|_| cb.query_word_unchecked()); for _pop in pops.iter() { // cb.stack_pop(pop.expr()); } @@ -63,11 +66,11 @@ impl for i in 0..N_POP { let value = block.rws[step.rw_indices[i]].stack_value(); - self.pops[i].assign(region, offset, Some(value.to_le_bytes()))?; + self.pops[i].assign_u256(region, offset, value)?; } for i in 0..N_PUSH { let value = block.rws[step.rw_indices[N_POP + i]].stack_value(); - self.pushes[i].assign(region, offset, Some(value.to_le_bytes()))?; + self.pushes[i].assign_u256(region, offset, value)?; } Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/dup.rs b/zkevm-circuits/src/evm_circuit/execution/dup.rs index 51c808b978..1ea31ccc47 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dup.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dup.rs @@ -5,11 +5,14 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - CachedRegion, Cell, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::plonk::Error; @@ -17,7 +20,7 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct DupGadget { same_context: SameContextGadget, - value: Cell, + value: WordCell, } impl ExecutionGadget for DupGadget { @@ -28,15 +31,15 @@ impl ExecutionGadget for DupGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let value = cb.query_cell_phase2(); + let value = cb.query_word_unchecked(); // The stack index we have to peek, deduced from the 'x' value of 'dupx' // The offset starts at 0 for DUP1 let dup_offset = opcode.expr() - OpcodeId::DUP1.expr(); // Peek the value at `dup_offset` and push the value on the stack - cb.stack_lookup(false.expr(), dup_offset, value.expr()); - cb.stack_push(value.expr()); + cb.stack_lookup(false.expr(), dup_offset, value.to_word()); + cb.stack_push(value.to_word()); // State transition let step_state_transition = StepStateTransition { @@ -66,7 +69,7 @@ impl ExecutionGadget for DupGadget { self.same_context.assign_exec_step(region, offset, step)?; let value = block.rws[step.rw_indices[0]].stack_value(); - self.value.assign(region, offset, region.word_rlc(value))?; + self.value.assign_u256(region, offset, value)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index dc32fd1333..494f0fb556 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -14,7 +14,7 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, - util::Expr, + util::{word::Word, Expr}, }; use bus_mapping::l2_predeployed::message_queue::{ ADDRESS as MESSAGE_QUEUE, WITHDRAW_TRIE_ROOT_SLOT, @@ -86,21 +86,28 @@ impl ExecutionGadget for EndBlockGadget { }); cb.condition(not::expr(is_empty_block.expr()), |cb| { // 1b. total_txs matches the tx_id that corresponds to the final step. - cb.call_context_lookup(0.expr(), None, CallContextFieldTag::TxId, total_txs.expr()); + cb.call_context_lookup_read( + None, + CallContextFieldTag::TxId, + Word::from_lo_unchecked(total_txs.expr()), + ); }); let mut withdraw_trie_root_slot_le = [0u8; 32]; WITHDRAW_TRIE_ROOT_SLOT.to_little_endian(withdraw_trie_root_slot_le.as_mut_slice()); // 1.1 constraint withdraw_root - cb.account_storage_read( + cb.account_storage_read_address( Expression::Constant(MESSAGE_QUEUE.to_scalar().expect( "unexpected Address for message_queue precompile -> Scalar conversion failure", )), - cb.word_rlc(withdraw_trie_root_slot_le.map(|byte| byte.expr())), - phase2_withdraw_root.expr(), + // TODO: check if remove word_rlc and directly use withdraw_trie_root_slot_le as word + Word::from_lo_unchecked( + cb.word_rlc(withdraw_trie_root_slot_le.map(|byte| byte.expr())), + ), + Word::from_lo_unchecked(phase2_withdraw_root.expr()), total_txs.expr(), - phase2_withdraw_root_prev.expr(), + Word::from_lo_unchecked(phase2_withdraw_root_prev.expr()), ); // 2. If total_txs == max_txs, we know we have covered all txs from the @@ -114,7 +121,8 @@ impl ExecutionGadget for EndBlockGadget { total_txs.expr() + 1.expr(), TxContextFieldTag::CallerAddress, None, - 0.expr(), + Word::zero(), + // 0.expr(), ); // Since every tx lookup done in the EVM circuit must succeed // and uses a unique tx_id, we know that at diff --git a/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs index cd34c14e9f..2a115ceb93 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_inner_block.rs @@ -12,6 +12,7 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::{BlockContextFieldTag, TxFieldTag::BlockNumber}, + util::word::Word, }; use eth_types::Field; use gadgets::util::{not, Expr}; @@ -49,12 +50,14 @@ impl ExecutionGadget for EndInnerBlockGadget { let cum_num_txs = cb.query_cell(); cb.block_lookup( BlockContextFieldTag::NumTxs.expr(), - cb.curr.state.block_number.expr(), + Some(cb.curr.state.block_number.expr()), + //Word::from_lo_unchecked(num_txs.expr()), num_txs.expr(), ); cb.block_lookup( BlockContextFieldTag::CumNumTxs.expr(), - cb.curr.state.block_number.expr(), + Some(cb.curr.state.block_number.expr()), + //Word::from_lo_unchecked(cum_num_txs.expr()), cum_num_txs.expr(), ); @@ -72,7 +75,8 @@ impl ExecutionGadget for EndInnerBlockGadget { last_tx_id.expr(), BlockNumber, None, - cb.curr.state.block_number.expr(), + Word::from_lo_unchecked(cb.curr.state.block_number.expr()), + // cb.curr.state.block_number.expr(), ); }); diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index 0efdd75664..be48ded197 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -11,10 +11,10 @@ use crate::{ }, from_bytes, math_gadget::{ - AddWordsGadget, ConstantDivisionGadget, IsEqualGadget, IsZeroGadget, MinMaxGadget, - MulWordByU64Gadget, + AddWordsGadget, ConstantDivisionGadget, IsEqualGadget, IsZeroWordGadget, + MinMaxGadget, MulWordByU64Gadget, }, - CachedRegion, Cell, StepRws, Word, + CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -22,11 +22,12 @@ use crate::{ AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, RwTableTag, TxContextFieldTag, TxReceiptFieldTag, }, - util::Expr, -}; -use eth_types::{ - evm_types::MAX_REFUND_QUOTIENT_OF_GAS_USED, geth_types::TxType, Field, ToLittleEndian, ToScalar, + util::{ + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; +use eth_types::{evm_types::MAX_REFUND_QUOTIENT_OF_GAS_USED, geth_types::TxType, Field, ToScalar}; use gadgets::util::{not, select}; use halo2_proofs::{circuit::Value, plonk::Error}; use strum::EnumCount; @@ -39,18 +40,19 @@ pub(crate) struct EndTxGadget { max_refund: ConstantDivisionGadget, refund: Cell, effective_refund: MinMaxGadget, - effective_fee: Word, + effective_fee: Word32Cell, mul_gas_price_by_refund: MulWordByU64Gadget, - tx_caller_address: Cell, + tx_caller_address: WordCell, tx_data_gas_cost: Cell, gas_fee_refund: UpdateBalanceGadget, sub_gas_price_by_base_fee: AddWordsGadget, mul_effective_tip_by_gas_used: MulWordByU64Gadget, + //coinbase: WordCell, coinbase: Cell, - coinbase_codehash: Cell, + coinbase_codehash: WordCell, #[cfg(feature = "scroll")] - coinbase_keccak_codehash: Cell, - coinbase_codehash_is_zero: IsZeroGadget, + coinbase_keccak_codehash: WordCell, + coinbase_codehash_is_zero: IsZeroWordGadget>, coinbase_transfer: TransferToGadget, current_cumulative_gas_used: Cell, is_first_tx: IsEqualGadget, @@ -69,15 +71,23 @@ impl ExecutionGadget for EndTxGadget { let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let is_persistent = cb.call_context(None, CallContextFieldTag::IsPersistent); let tx_l1_fee = cb.call_context(None, CallContextFieldTag::L1Fee); - // rwc_delta = 3 - let [tx_gas, tx_caller_address, tx_type, tx_data_gas_cost] = [ + let tx_caller_address = cb.query_word_unchecked(); + + let [tx_gas, tx_type, tx_data_gas_cost] = [ TxContextFieldTag::Gas, - TxContextFieldTag::CallerAddress, TxContextFieldTag::TxType, TxContextFieldTag::TxDataGasCost, ] .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); - let tx_gas_price = cb.tx_context_as_word(tx_id.expr(), TxContextFieldTag::GasPrice, None); + + let tx_gas_price = cb.query_word32(); + let tx_gas_price_rlc = cb.word_rlc(tx_gas_price.limbs.clone().map(|cell| cell.expr())); + cb.tx_context_lookup( + tx_id.expr(), + TxContextFieldTag::GasPrice, + None, + Word::from_lo_unchecked(tx_gas_price_rlc), + ); let tx_is_l1msg = IsEqualGadget::construct(cb, tx_type.expr(), (TxType::L1Msg as u64).expr()); @@ -90,9 +100,7 @@ impl ExecutionGadget for EndTxGadget { MAX_REFUND_QUOTIENT_OF_GAS_USED as u64, ); let refund = cb.query_cell(); - cb.tx_refund_read(tx_id.expr(), refund.expr()); - // rwc_delta = 4 - + cb.tx_refund_read(tx_id.expr(), Word::from_lo_unchecked(refund.expr())); let effective_refund = MinMaxGadget::construct(cb, max_refund.quotient(), refund.expr()); // Add effective_refund * tx_gas_price back to caller's balance @@ -104,7 +112,7 @@ impl ExecutionGadget for EndTxGadget { let gas_fee_refund = cb.condition(not::expr(tx_is_l1msg.expr()), |cb| { UpdateBalanceGadget::construct( cb, - tx_caller_address.expr(), + tx_caller_address.to_word(), vec![mul_gas_price_by_refund.product().clone()], None, ) @@ -113,14 +121,16 @@ impl ExecutionGadget for EndTxGadget { // Add gas_used * effective_tip to coinbase's balance let coinbase = cb.query_cell(); - let base_fee = cb.query_word_rlc(); + let base_fee = cb.query_word32(); + let base_fee_rlc = cb.word_rlc(base_fee.limbs.clone().map(|l| l.expr())); for (tag, value) in [ + // (BlockContextFieldTag::BaseFee, base_fee.to_word()), (BlockContextFieldTag::Coinbase, coinbase.expr()), - (BlockContextFieldTag::BaseFee, base_fee.expr()), + (BlockContextFieldTag::BaseFee, base_fee_rlc), ] { - cb.block_lookup(tag.expr(), cb.curr.state.block_number.expr(), value); + cb.block_lookup(tag.expr(), Some(cb.curr.state.block_number.expr()), value); } - let effective_tip = cb.query_word_rlc(); + let effective_tip = cb.query_word32(); let sub_gas_price_by_base_fee = AddWordsGadget::construct(cb, [effective_tip.clone(), base_fee], tx_gas_price); @@ -132,7 +142,7 @@ impl ExecutionGadget for EndTxGadget { ) }); - let effective_fee = cb.query_word_rlc(); + let effective_fee = cb.query_word32(); cb.condition(tx_is_l1msg.expr(), |cb| { cb.require_zero("l1fee is 0 for l1msg", tx_l1_fee.expr()); @@ -143,40 +153,39 @@ impl ExecutionGadget for EndTxGadget { + select::expr( tx_is_l1msg.expr(), 0.expr(), - from_bytes::expr(&mul_effective_tip_by_gas_used.product().cells[..16]), + from_bytes::expr(&mul_effective_tip_by_gas_used.product().limbs[..16]), ), - from_bytes::expr(&effective_fee.cells[..16]), + from_bytes::expr(&effective_fee.limbs[..16]), ); cb.condition(tx_is_l1msg.expr(), |cb| { - cb.require_zero("effective fee is zero for l1 msg", effective_fee.expr()); + cb.require_zero_word("effective fee is zero for l1 msg", effective_fee.to_word()); }); - let coinbase_codehash = cb.query_cell_phase2(); + let coinbase_codehash = cb.query_word_unchecked(); + let coinbase_codehash_is_zero = IsZeroWordGadget::construct(cb, &coinbase_codehash); + cb.account_read( - coinbase.expr(), + Word::from_lo_unchecked(coinbase.expr()), AccountFieldTag::CodeHash, - coinbase_codehash.expr(), + coinbase_codehash.to_word(), ); + // rwc_delta = 5 + !tx_is_l1msg #[cfg(feature = "scroll")] - let coinbase_keccak_codehash = cb.query_cell_phase2(); - - let coinbase_codehash_is_zero = cb.annotation("coinbase_codehash_is_zero", |cb| { - IsZeroGadget::construct(cb, coinbase_codehash.expr()) - }); + let coinbase_keccak_codehash = cb.query_word_unchecked(); // If coinbase account balance will become positive because of this tx, update its codehash // from 0 to the empty codehash. let coinbase_transfer = cb.condition(not::expr(tx_is_l1msg.expr()), |cb| { TransferToGadget::construct( cb, - coinbase.expr(), + Word::from_lo_unchecked(coinbase.expr()), not::expr(coinbase_codehash_is_zero.expr()), false.expr(), - coinbase_codehash.expr(), + coinbase_codehash.to_word(), #[cfg(feature = "scroll")] - coinbase_keccak_codehash.expr(), + coinbase_keccak_codehash.to_word(), effective_fee.clone(), None, ) @@ -239,8 +248,7 @@ impl ExecutionGadget for EndTxGadget { next_step_rwc.clone(), Some(next_step_rwc), CallContextFieldTag::TxId, - // tx_id has been lookup and range_check above - tx_id.expr() + 1.expr(), + Word::from_lo_unchecked(tx_id.expr() + 1.expr()), ); cb.require_step_state_transition(StepStateTransition { @@ -339,15 +347,9 @@ impl ExecutionGadget for EndTxGadget { effective_refund + step.gas_left, gas_fee_refund, )?; - self.tx_caller_address.assign( - region, - offset, - Value::known( - tx.caller_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), - )?; + self.tx_caller_address + .assign_h160(region, offset, tx.caller_address)?; + if !tx.tx_type.is_l1_msg() { let (caller_balance, caller_balance_prev) = rws.next().account_value_pair(); self.gas_fee_refund.assign( @@ -372,11 +374,11 @@ impl ExecutionGadget for EndTxGadget { effective_tip * (gas_used - effective_refund) }; let (coinbase_codehash, _) = rws.next().account_codehash_pair(); - let coinbase_codehash_rlc = region.code_hash(coinbase_codehash); + self.coinbase_codehash - .assign(region, offset, coinbase_codehash_rlc)?; + .assign_u256(region, offset, coinbase_codehash)?; self.coinbase_codehash_is_zero - .assign_value(region, offset, coinbase_codehash_rlc)?; + .assign_u256(region, offset, coinbase_codehash)?; if !tx.tx_type.is_l1_msg() { self.mul_effective_tip_by_gas_used.assign( @@ -421,6 +423,13 @@ impl ExecutionGadget for EndTxGadget { coinbase_reward + tx_l1_fee, &mut rws, )?; + #[cfg(feature = "scroll")] + self.coinbase_keccak_codehash.assign_u256( + region, + offset, + result.account_keccak_code_hash.unwrap_or_default(), + )?; + let coinbase_balance_pair = result.receiver_balance_pair.unwrap(); coinbase_balance_pair.0 - coinbase_balance_pair.1 }; @@ -435,7 +444,7 @@ impl ExecutionGadget for EndTxGadget { self.tx_l1_fee .assign(region, offset, Value::known(F::from(tx_l1_fee)))?; self.effective_fee - .assign(region, offset, Some(effective_fee.to_le_bytes()))?; + .assign_u256(region, offset, effective_fee)?; let current_cumulative_gas_used: u64 = if tx.id == 1 { 0 diff --git a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs index 9f3f0bcf13..7a922380ad 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs @@ -12,7 +12,7 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{word::WordExpr, Expr}, }; use eth_types::{ @@ -50,11 +50,10 @@ impl ExecutionGadget for ErrorCodeStoreGadget { OpcodeId::RETURN.expr(), ); - let offset = cb.query_cell_phase2(); - let length = cb.query_word_rlc(); - - cb.stack_pop(offset.expr()); - cb.stack_pop(length.expr()); + let offset = cb.query_word_unchecked(); + let length = cb.query_memory_address(); + cb.stack_pop(offset.to_word()); + cb.stack_pop(length.to_word()); let memory_address = MemoryAddressGadget::construct(cb, offset, length); cb.require_true("is_create is true", cb.curr.state.is_create.expr()); diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs index 55d120070c..e318ad8cd0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs @@ -9,11 +9,14 @@ use crate::{ from_bytes, math_gadget::IsEqualGadget, memory_gadget::{MemoryMask, MemoryWordAddress}, - CachedRegion, Cell, RandomLinearCombination, Word, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; @@ -24,8 +27,8 @@ use halo2_proofs::{circuit::Value, plonk::Error}; pub(crate) struct ErrorInvalidCreationCodeGadget { opcode: Cell, memory_address: MemoryWordAddress, - length: RandomLinearCombination, - value_left: Word, + length: Word32Cell, + value_left: Word32Cell, first_byte: Cell, is_first_byte_invalid: IsEqualGadget, mask: MemoryMask, @@ -47,14 +50,13 @@ impl ExecutionGadget for ErrorInvalidCreationCodeGadget { ); let first_byte = cb.query_cell(); - //let address = cb.query_word_rlc(); + let offset = cb.query_memory_address(); - let offset = cb.query_word_rlc(); - let length = cb.query_word_rlc(); - let value_left = cb.query_word_rlc(); + let length = cb.query_word32(); + let value_left = cb.query_word32(); - cb.stack_pop(offset.expr()); - cb.stack_pop(length.expr()); + cb.stack_pop(offset.to_word()); + cb.stack_pop(length.to_word()); cb.require_true("is_create is true", cb.curr.state.is_create.expr()); let address_word = MemoryWordAddress::construct(cb, offset.clone()); @@ -62,8 +64,8 @@ impl ExecutionGadget for ErrorInvalidCreationCodeGadget { cb.memory_lookup( 0.expr(), address_word.addr_left(), - value_left.expr(), - value_left.expr(), + value_left.to_word(), + value_left.to_word(), None, ); @@ -82,8 +84,8 @@ impl ExecutionGadget for ErrorInvalidCreationCodeGadget { cb, opcode.expr(), 5.expr(), - from_bytes::expr(&offset.cells[..N_BYTES_MEMORY_ADDRESS]), - from_bytes::expr(&length.cells[..N_BYTES_MEMORY_ADDRESS]), + from_bytes::expr(&offset.limbs[..N_BYTES_MEMORY_ADDRESS]), + from_bytes::expr(&length.limbs[..N_BYTES_MEMORY_ADDRESS]), ); Self { @@ -115,19 +117,10 @@ impl ExecutionGadget for ErrorInvalidCreationCodeGadget { self.memory_address .assign(region, offset, memory_offset.as_u64())?; - self.length.assign( - region, - offset, - Some( - length.to_le_bytes()[..N_BYTES_MEMORY_ADDRESS] - .try_into() - .unwrap(), - ), - )?; + self.length.assign_u256(region, offset, length)?; let word_left = block.rws[step.rw_indices[2]].memory_word_pair().0; - self.value_left - .assign(region, offset, Some(word_left.to_le_bytes()))?; + self.value_left.assign_u256(region, offset, word_left)?; let mut bytes = word_left.to_le_bytes(); bytes.reverse(); diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs index 8e4e6143f7..688411c2d1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs @@ -6,12 +6,15 @@ use crate::{ util::{ common_gadget::{CommonErrorGadget, WordByteCapGadget}, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - math_gadget::{IsEqualGadget, IsZeroGadget}, + math_gadget::{IsEqualGadget, IsZeroWordGadget}, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field, U256}; @@ -27,8 +30,8 @@ pub(crate) struct ErrorInvalidJumpGadget { push_rlc: Cell, is_jump_dest: IsEqualGadget, is_jumpi: IsEqualGadget, - phase2_condition: Cell, - is_condition_zero: IsZeroGadget, + condition: WordCell, + is_condition_zero: IsZeroWordGadget>, common_error_gadget: CommonErrorGadget, } @@ -45,7 +48,7 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { let value = cb.query_cell(); let is_code = cb.query_cell(); let push_rlc = cb.query_cell_phase2(); - let phase2_condition = cb.query_cell_phase2(); + let condition = cb.query_word_unchecked(); cb.require_in_set( "ErrorInvalidJump only happend in JUMP or JUMPI", @@ -60,24 +63,24 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { // first default this condition, if use will re-construct with real condition // value - let is_condition_zero = IsZeroGadget::construct(cb, phase2_condition.expr()); + let is_condition_zero = IsZeroWordGadget::construct(cb, &condition); // Pop the value from the stack - cb.stack_pop(dest.original_word()); + cb.stack_pop(dest.original_word().to_word()); cb.condition(is_jumpi.expr(), |cb| { - cb.stack_pop(phase2_condition.expr()); + cb.stack_pop(condition.to_word()); // if condition is zero, jump will not happen, so constrain condition not zero cb.require_zero("condition is not zero", is_condition_zero.expr()); }); // Look up bytecode length - cb.bytecode_length(cb.curr.state.code_hash.expr(), code_len.expr()); + cb.bytecode_length(cb.curr.state.code_hash.to_word(), code_len.expr()); // If destination is in valid range, lookup for the value. cb.condition(dest.lt_cap(), |cb| { cb.bytecode_lookup( - cb.curr.state.code_hash.expr(), + cb.curr.state.code_hash.to_word(), dest.valid_value(), is_code.expr(), value.expr(), @@ -101,7 +104,7 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { push_rlc, is_jump_dest, is_jumpi, - phase2_condition, + condition, is_condition_zero, common_error_gadget, } @@ -118,6 +121,7 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { ) -> Result<(), Error> { let opcode = step.opcode.unwrap(); let is_jumpi = opcode == OpcodeId::JUMPI; + self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -126,7 +130,6 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { } else { U256::zero() }; - let condition_rlc = region.word_rlc(condition); let code = block .bytecodes @@ -166,11 +169,10 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { F::from(OpcodeId::JUMPI.as_u64()), )?; - self.phase2_condition - .assign(region, offset, condition_rlc)?; - self.is_condition_zero - .assign_value(region, offset, condition_rlc)?; + self.condition.assign_u256(region, offset, condition)?; + self.is_condition_zero + .assign_value(region, offset, Value::known(Word::from(condition)))?; self.common_error_gadget.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs index d8aa5383f5..70c640f055 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs @@ -1,23 +1,22 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS}, + param::N_BYTES_GAS, step::ExecutionState, util::{ common_gadget::CommonErrorGadget, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, math_gadget::LtGadget, - select, CachedRegion, Cell, Word, + select, AccountAddress, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{word::WordExpr, Expr}, }; use eth_types::{ evm_types::{GasCost, OpcodeId}, - Field, ToLittleEndian, + Field, ToAddress, }; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -26,7 +25,7 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ErrorOOGAccountAccessGadget { opcode: Cell, - address_word: Word, + address: AccountAddress, tx_id: Cell, is_warm: Cell, insufficient_gas_cost: LtGadget, @@ -50,14 +49,13 @@ impl ExecutionGadget for ErrorOOGAccountAccessGadget { ], ); - let address_word = cb.query_word_rlc(); - let address = from_bytes::expr(&address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); - cb.stack_pop(address_word.expr()); + let address = cb.query_account_address(); + cb.stack_pop(address.to_word()); let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let is_warm = cb.query_bool(); // read is_warm - cb.account_access_list_read(tx_id.expr(), address.expr(), is_warm.expr()); + cb.account_access_list_read(tx_id.expr(), address.to_word(), is_warm.expr()); let gas_cost = select::expr( is_warm.expr(), @@ -77,7 +75,7 @@ impl ExecutionGadget for ErrorOOGAccountAccessGadget { let common_error_gadget = CommonErrorGadget::construct(cb, opcode.expr(), 5.expr()); Self { opcode, - address_word, + address, tx_id, is_warm, insufficient_gas_cost, @@ -99,9 +97,8 @@ impl ExecutionGadget for ErrorOOGAccountAccessGadget { .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; let address = block.rws[step.rw_indices[0]].stack_value(); - self.address_word - .assign(region, offset, Some(address.to_le_bytes()))?; - + self.address + .assign_h160(region, offset, address.to_address())?; self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs index 5ab338a8d1..44ec0fde29 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs @@ -66,11 +66,7 @@ impl ExecutionGadget for ErrorOOGCallGadget { // Add callee to access list let is_warm = cb.query_bool(); - cb.account_access_list_read( - tx_id.expr(), - call_gadget.callee_address_expr(), - is_warm.expr(), - ); + cb.account_access_list_read(tx_id.expr(), call_gadget.callee_address(), is_warm.expr()); cb.condition(is_call.expr() * call_gadget.has_value.expr(), |cb| { cb.require_zero( @@ -164,7 +160,7 @@ impl ExecutionGadget for ErrorOOGCallGadget { rd_offset, rd_length, step.memory_word_size(), - region.code_hash(callee_code_hash), + callee_code_hash, )?; self.opcode diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs index 2961fc3286..37ebae57a9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs @@ -11,25 +11,28 @@ use crate::{ CommonMemoryAddressGadget, MemoryExpandedAddressGadget, MemoryExpansionGadget, MemoryWordSizeGadget, }, - or, select, CachedRegion, Cell, Word, + or, select, CachedRegion, Cell, }, }, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, witness::{Block, Call, ExecStep, Transaction}, }; use eth_types::{ evm_types::{ GasCost, OpcodeId, CREATE2_GAS_PER_CODE_WORD, CREATE_GAS_PER_CODE_WORD, MAX_INIT_CODE_SIZE, }, - Field, ToLittleEndian, U256, + Field, U256, }; -use gadgets::util::Expr; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ErrorOOGCreateGadget { opcode: Cell, - value: Word, - salt: Word, + value: Word32Cell, + salt: Word32Cell, is_create2: PairSelectGadget, minimum_word_size: MemoryWordSizeGadget, memory_address: MemoryExpandedAddressGadget, @@ -58,21 +61,21 @@ impl ExecutionGadget for ErrorOOGCreateGadget { OpcodeId::CREATE.expr(), ); - let value = cb.query_word_rlc(); - let salt = cb.query_word_rlc(); + let value = cb.query_word32(); + let salt = cb.query_word32(); let memory_address = MemoryExpandedAddressGadget::construct_self(cb); - cb.stack_pop(value.expr()); - cb.stack_pop(memory_address.offset_rlc()); - cb.stack_pop(memory_address.length_rlc()); - cb.condition(is_create2.expr().0, |cb| cb.stack_pop(salt.expr())); + cb.stack_pop(value.to_word()); + cb.stack_pop(memory_address.offset_word()); + cb.stack_pop(memory_address.length_word()); + cb.condition(is_create2.expr().0, |cb| cb.stack_pop(salt.to_word())); let init_code_size_overflow = LtGadget::construct(cb, MAX_INIT_CODE_SIZE.expr(), memory_address.length()); let minimum_word_size = MemoryWordSizeGadget::construct(cb, memory_address.length()); - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let keccak_gas_cost = minimum_word_size.expr() * select::expr( @@ -148,9 +151,8 @@ impl ExecutionGadget for ErrorOOGCreateGadget { U256::zero() }; - self.value - .assign(region, offset, Some(value.to_le_bytes()))?; - self.salt.assign(region, offset, Some(salt.to_le_bytes()))?; + self.value.assign_u256(region, offset, value)?; + self.salt.assign_u256(region, offset, salt)?; let memory_address = self.memory_address diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs index b988073a14..ba670bd74a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs @@ -44,10 +44,10 @@ impl ExecutionGadget for ErrorOOGDynamicMemoryGadget { ); let memory_address = MemoryExpandedAddressGadget::construct_self(cb); - cb.stack_pop(memory_address.offset_rlc()); - cb.stack_pop(memory_address.length_rlc()); + cb.stack_pop(memory_address.offset_word()); + cb.stack_pop(memory_address.length_word()); - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let insufficient_gas = LtGadget::construct( cb, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs index cade397da8..de56400803 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs @@ -7,15 +7,18 @@ use crate::{ common_gadget::CommonErrorGadget, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{ByteOrWord, ByteSizeGadget, LtGadget}, - CachedRegion, Cell, Word, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, }; use eth_types::{ evm_types::{GasCost, OpcodeId}, - Field, ToLittleEndian, + Field, }; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -24,8 +27,8 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ErrorOOGExpGadget { opcode: Cell, - base: Word, - exponent: Word, + base: Word32Cell, + exponent: Word32Cell, exponent_byte_size: ByteSizeGadget, insufficient_gas_cost: LtGadget, common_error_gadget: CommonErrorGadget, @@ -45,15 +48,15 @@ impl ExecutionGadget for ErrorOOGExpGadget { OpcodeId::EXP.expr(), ); - let base = cb.query_word_rlc(); - let exponent = cb.query_word_rlc(); - cb.stack_pop(base.expr()); - cb.stack_pop(exponent.expr()); + let base = cb.query_word32(); + let exponent = cb.query_word32(); + cb.stack_pop(base.to_word()); + cb.stack_pop(exponent.to_word()); let exponent_byte_size = ByteSizeGadget::construct( cb, exponent - .cells + .limbs .iter() .map(Expr::expr) .collect::>() @@ -108,9 +111,8 @@ impl ExecutionGadget for ErrorOOGExpGadget { self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; - self.base.assign(region, offset, Some(base.to_le_bytes()))?; - self.exponent - .assign(region, offset, Some(exponent.to_le_bytes()))?; + self.base.assign_u256(region, offset, base)?; + self.exponent.assign_u256(region, offset, exponent)?; self.exponent_byte_size .assign(region, offset, ByteOrWord::Word(exponent))?; self.insufficient_gas_cost.assign_value( diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs index 1a673191e8..b563947603 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs @@ -47,8 +47,8 @@ impl ExecutionGadget for ErrorOOGLogGadget { let memory_address = MemoryExpandedAddressGadget::construct_self(cb); // Pop mstart_address, msize from stack - cb.stack_pop(memory_address.offset_rlc()); - cb.stack_pop(memory_address.length_rlc()); + cb.stack_pop(memory_address.offset_word()); + cb.stack_pop(memory_address.length_word()); // constrain not in static call let is_static_call = cb.call_context(None, CallContextFieldTag::IsStatic); @@ -64,7 +64,7 @@ impl ExecutionGadget for ErrorOOGLogGadget { // Calculate the next memory size and the gas cost for this memory // access - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let gas_cost = GasCost::LOG.as_u64().expr() + GasCost::LOG.as_u64().expr() * topic_count diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs index ca3d279434..f024230388 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs @@ -1,27 +1,29 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE}, + param::{N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE}, step::ExecutionState, util::{ common_gadget::CommonErrorGadget, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, math_gadget::{IsZeroGadget, LtGadget}, memory_gadget::{ CommonMemoryAddressGadget, MemoryCopierGasGadget, MemoryExpandedAddressGadget, MemoryExpansionGadget, }, - or, select, CachedRegion, Cell, Word, + or, select, AccountAddress, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use eth_types::{ evm_types::{GasCost, OpcodeId}, - Field, ToLittleEndian, U256, + Field, ToAddress, U256, }; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -35,9 +37,9 @@ pub(crate) struct ErrorOOGMemoryCopyGadget { is_warm: Cell, tx_id: Cell, /// Extra stack pop for `EXTCODECOPY` - external_address: Word, + external_address: AccountAddress, /// Source offset - src_offset: Word, + src_offset: WordCell, /// Destination offset and size to copy dst_memory_addr: MemoryExpandedAddressGadget, memory_expansion: MemoryExpansionGadget, @@ -65,8 +67,8 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { ], ); - let src_offset = cb.query_word_rlc(); - let external_address = cb.query_word_rlc(); + let src_offset = cb.query_word_unchecked(); + let external_address = cb.query_account_address(); let is_warm = cb.query_bool(); let tx_id = cb.query_cell(); @@ -74,26 +76,25 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::EXTCODECOPY.expr()); cb.condition(is_extcodecopy.expr(), |cb| { - cb.call_context_lookup(false.expr(), None, CallContextFieldTag::TxId, tx_id.expr()); + cb.call_context_lookup_read( + None, + CallContextFieldTag::TxId, + Word::from_lo_unchecked(tx_id.expr()), + ); // Check if EXTCODECOPY external address is warm. - cb.account_access_list_read( - tx_id.expr(), - from_bytes::expr(&external_address.cells[..N_BYTES_ACCOUNT_ADDRESS]), - is_warm.expr(), - ); + cb.account_access_list_read(tx_id.expr(), external_address.to_word(), is_warm.expr()); // EXTCODECOPY has an extra stack pop for external address. - cb.stack_pop(external_address.expr()); + cb.stack_pop(external_address.to_word()); }); - let dst_memory_addr = MemoryExpandedAddressGadget::construct_self(cb); - cb.stack_pop(dst_memory_addr.offset_rlc()); - cb.stack_pop(src_offset.expr()); - cb.stack_pop(dst_memory_addr.length_rlc()); + cb.stack_pop(dst_memory_addr.offset_word()); + cb.stack_pop(src_offset.to_word()); + cb.stack_pop(dst_memory_addr.length_word()); - let memory_expansion = MemoryExpansionGadget::construct(cb, [dst_memory_addr.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [dst_memory_addr.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, dst_memory_addr.length(), @@ -187,9 +188,8 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { self.tx_id .assign(region, offset, Value::known(F::from(transaction.id as u64)))?; self.external_address - .assign(region, offset, Some(external_address.to_le_bytes()))?; - self.src_offset - .assign(region, offset, Some(src_offset.to_le_bytes()))?; + .assign_h160(region, offset, external_address.to_address())?; + self.src_offset.assign_u256(region, offset, src_offset)?; let memory_addr = self .dst_memory_addr .assign(region, offset, dst_offset, copy_size)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs index 559ff73543..60c354c33c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs @@ -49,10 +49,10 @@ impl ExecutionGadget for ErrorOOGSha3Gadget { ); let memory_address = MemoryExpandedAddressGadget::construct_self(cb); - cb.stack_pop(memory_address.offset_rlc()); - cb.stack_pop(memory_address.length_rlc()); + cb.stack_pop(memory_address.offset_word()); + cb.stack_pop(memory_address.length_word()); - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, memory_address.length(), diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs index bb455402b0..63b44d56fd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs @@ -16,11 +16,14 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use eth_types::{ evm_types::{GasCost, OpcodeId}, - Field, ToScalar, U256, + Field, U256, }; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -31,14 +34,14 @@ pub(crate) struct ErrorOOGSloadSstoreGadget { opcode: Cell, tx_id: Cell, is_static: Cell, - callee_address: Cell, - phase2_key: Cell, - phase2_value: Cell, - phase2_value_prev: Cell, - phase2_original_value: Cell, + callee_address: WordCell, + key: WordCell, + value: WordCell, + value_prev: WordCell, + original_value: WordCell, is_warm: Cell, is_sstore: PairSelectGadget, - sstore_gas_cost: SstoreGasGadget, + sstore_gas_cost: SstoreGasGadget>, insufficient_gas_cost: LtGadget, // Constrain for SSTORE reentrancy sentry. insufficient_gas_sentry: LtGadget, @@ -62,43 +65,43 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let is_static = cb.call_context(None, CallContextFieldTag::IsStatic); - let callee_address = cb.call_context(None, CallContextFieldTag::CalleeAddress); + let callee_address = cb.call_context_read_as_word(None, CallContextFieldTag::CalleeAddress); // Constrain `is_static` must be false for SSTORE. // cb.require_zero("is_static == false", is_static.expr() * is_sstore.expr().0); - let phase2_key = cb.query_cell_phase2(); - let phase2_value = cb.query_cell_phase2(); - let phase2_value_prev = cb.query_cell_phase2(); - let phase2_original_value = cb.query_cell_phase2(); + let key = cb.query_word_unchecked(); + let value = cb.query_word_unchecked(); + let value_prev = cb.query_word_unchecked(); + let original_value = cb.query_word_unchecked(); let is_warm = cb.query_bool(); - cb.stack_pop(phase2_key.expr()); + cb.stack_pop(key.to_word()); cb.account_storage_access_list_read( tx_id.expr(), - callee_address.expr(), - phase2_key.expr(), - is_warm.expr(), + callee_address.to_word(), + key.to_word(), + Word::from_lo_unchecked(is_warm.expr()), ); let sload_gas_cost = SloadGasGadget::construct(cb, is_warm.expr()); let sstore_gas_cost = cb.condition(is_sstore.expr().0, |cb| { - cb.stack_pop(phase2_value.expr()); + cb.stack_pop(value.to_word()); cb.account_storage_read( - callee_address.expr(), - phase2_key.expr(), - phase2_value_prev.expr(), + callee_address.to_word(), + key.to_word(), + value_prev.to_word(), tx_id.expr(), - phase2_original_value.expr(), + original_value.to_word(), ); SstoreGasGadget::construct( cb, - phase2_value.clone(), - phase2_value_prev.clone(), - phase2_original_value.clone(), is_warm.clone(), + value.clone(), + value_prev.clone(), + original_value.clone(), ) }); @@ -137,10 +140,10 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { tx_id, is_static, callee_address, - phase2_key, - phase2_value, - phase2_value_prev, - phase2_original_value, + key, + value, + value_prev, + original_value, is_warm, is_sstore, sstore_gas_cost, @@ -190,23 +193,14 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { .assign(region, offset, Value::known(F::from(tx.id as u64)))?; self.is_static .assign(region, offset, Value::known(F::from(call.is_static as u64)))?; - self.callee_address.assign( - region, - offset, - Value::known( - call.callee_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), - )?; - self.phase2_key - .assign(region, offset, region.word_rlc(key))?; - self.phase2_value - .assign(region, offset, region.word_rlc(value))?; - self.phase2_value_prev - .assign(region, offset, region.word_rlc(value_prev))?; - self.phase2_original_value - .assign(region, offset, region.word_rlc(original_value))?; + self.callee_address + .assign_h160(region, offset, call.callee_address)?; + self.key.assign_u256(region, offset, key)?; + self.value.assign_u256(region, offset, value)?; + self.value_prev.assign_u256(region, offset, value_prev)?; + self.original_value + .assign_u256(region, offset, original_value)?; + self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs index 5da343e435..ef78c62196 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs @@ -4,23 +4,23 @@ use crate::{ param::{N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE}, step::ExecutionState, util::{ - common_gadget::RestoreContextGadget, + common_gadget::CommonErrorGadget, constraint_builder::{ - ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, - Transition::{Delta, Same}, + ConstrainBuilderCommon, + EVMConstraintBuilder, + //Transition::{Delta, Same}, }, math_gadget::{IsEqualGadget, LtGadget}, memory_gadget::{ CommonMemoryAddressGadget, MemoryExpandedAddressGadget, MemoryExpansionGadget, }, - not, or, select, CachedRegion, Cell, + or, select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - table::CallContextFieldTag, - util::Expr, + util::{word::Word, Expr}, }; -use eth_types::{evm_types::OpcodeId, Field}; +use eth_types::{evm_types::OpcodeId, Field, ToWord}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -31,8 +31,7 @@ pub(crate) struct ErrorOOGStaticMemoryGadget { insufficient_gas: LtGadget, is_mload: IsEqualGadget, is_mstore8: IsEqualGadget, - rw_counter_end_of_reversion: Cell, - restore_context: RestoreContextGadget, + common_error_gadget: CommonErrorGadget, } impl ExecutionGadget for ErrorOOGStaticMemoryGadget { @@ -43,10 +42,10 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { // Support other OOG due to pure memory including MSTORE, MSTORE8 and MLOAD fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - cb.opcode_lookup(opcode.expr(), 1.expr()); + //CommonErrorGadget will do opcode lookup; let memory_address = MemoryExpandedAddressGadget::construct_self(cb); - cb.stack_pop(memory_address.offset_rlc()); + cb.stack_pop(memory_address.offset_word()); // Check if this is an MSTORE8 let is_mload = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::MLOAD.expr()); @@ -66,14 +65,14 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { ), ); - cb.require_equal( + cb.require_equal_word( "Memory length must be 32 for MLOAD and MSTORE, and 1 for MSTORE8", - memory_address.length_rlc(), - select::expr(is_mstore8.expr(), 1.expr(), 32.expr()), + memory_address.length_word(), + Word::from_lo_unchecked(select::expr(is_mstore8.expr(), 1.expr(), 32.expr())), ); // Get the next memory size and the gas cost for this memory access - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); // Check if the amount of gas available is less than the amount of gas // required @@ -89,36 +88,7 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { 1.expr(), ); - // Current call must fail. - cb.call_context_lookup(false.expr(), None, CallContextFieldTag::IsSuccess, 0.expr()); - - let rw_counter_end_of_reversion = cb.query_cell(); - cb.call_context_lookup( - false.expr(), - None, - CallContextFieldTag::RwCounterEndOfReversion, - rw_counter_end_of_reversion.expr(), - ); - - cb.condition(cb.curr.state.is_root.expr(), |cb| { - cb.require_step_state_transition(StepStateTransition { - call_id: Same, - rw_counter: Delta(3.expr() + cb.curr.state.reversible_write_counter.expr()), - ..StepStateTransition::any() - }); - }); - - let restore_context = cb.condition(not::expr(cb.curr.state.is_root.expr()), |cb| { - RestoreContextGadget::construct( - cb, - 0.expr(), - 0.expr(), - 0.expr(), - 0.expr(), - 0.expr(), - 0.expr(), - ) - }); + let common_error_gadget = CommonErrorGadget::construct(cb, opcode.expr(), 3.expr()); Self { opcode, @@ -127,8 +97,7 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { insufficient_gas, is_mload, is_mstore8, - rw_counter_end_of_reversion, - restore_context, + common_error_gadget, } } @@ -150,8 +119,8 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { // MSTORE8 in go-ethereum. // let memory_length = match opcode { - OpcodeId::MLOAD | OpcodeId::MSTORE => 32, - OpcodeId::MSTORE8 => 1, + OpcodeId::MLOAD | OpcodeId::MSTORE => 32.to_word(), + OpcodeId::MSTORE8 => 1.to_word(), _ => unreachable!(), }; @@ -172,7 +141,7 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { let memory_address = self.memory_address - .assign(region, offset, memory_offset, memory_length.into())?; + .assign(region, offset, memory_offset, memory_length)?; let memory_expansion_cost = self .memory_expansion @@ -188,12 +157,7 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { F::from(OpcodeId::MLOAD.constant_gas_cost().0 + memory_expansion_cost), )?; - self.rw_counter_end_of_reversion.assign( - region, - offset, - Value::known(F::from(call.rw_counter_end_of_reversion as u64)), - )?; - self.restore_context + self.common_error_gadget .assign(region, offset, block, call, step, 3)?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs b/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs index ea6be965bb..33229d20f1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs @@ -6,15 +6,18 @@ use crate::{ constraint_builder::EVMConstraintBuilder, math_gadget::IsZeroGadget, memory_gadget::{CommonMemoryAddressGadget, MemoryAddressGadget}, - sum, CachedRegion, Cell, Word, + sum, CachedRegion, Cell, }, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, witness::{Block, Call, ExecStep, Transaction}, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian, U256}; +use eth_types::{Field, U256}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -24,9 +27,9 @@ pub(crate) struct ErrorPrecompileFailedGadget { is_callcode: IsZeroGadget, is_delegatecall: IsZeroGadget, is_staticcall: IsZeroGadget, - gas: Word, - callee_address: Word, - value: Word, + gas: WordCell, + callee_address: Cell, + value: WordCell, cd_address: MemoryAddressGadget, rd_address: MemoryAddressGadget, } @@ -64,38 +67,48 @@ impl ExecutionGadget for ErrorPrecompileFailedGadget { // Use rw_counter of the step which triggers next call as its call_id. let callee_call_id = cb.curr.state.rw_counter.clone(); - let gas = cb.query_word_rlc(); - let callee_address = cb.query_word_rlc(); - let value = cb.query_word_rlc(); - let cd_offset = cb.query_cell_phase2(); - let cd_length = cb.query_word_rlc(); - let rd_offset = cb.query_cell_phase2(); - let rd_length = cb.query_word_rlc(); + let gas = cb.query_word_unchecked(); + let callee_address = cb.query_cell(); + let value = cb.query_word_unchecked(); + + let cd_offset = cb.query_word_unchecked(); + let cd_length = cb.query_memory_address(); + let rd_offset = cb.query_word_unchecked(); + let rd_length = cb.query_memory_address(); + + let cd_address = MemoryAddressGadget::construct(cb, cd_offset, cd_length); + let rd_address = MemoryAddressGadget::construct(cb, rd_offset, rd_length); - cb.stack_pop(gas.expr()); - cb.stack_pop(callee_address.expr()); + cb.stack_pop(gas.to_word()); + cb.stack_pop(Word::from_lo_unchecked(callee_address.expr())); // `CALL` and `CALLCODE` opcodes have an additional stack pop `value`. cb.condition(is_call.expr() + is_callcode.expr(), |cb| { - cb.stack_pop(value.expr()) + cb.stack_pop(value.to_word()) }); - cb.stack_pop(cd_offset.expr()); - cb.stack_pop(cd_length.expr()); - cb.stack_pop(rd_offset.expr()); - cb.stack_pop(rd_length.expr()); - cb.stack_push(0.expr()); + cb.stack_pop(cd_address.offset_word()); + cb.stack_pop(cd_address.length_word()); + cb.stack_pop(rd_address.offset_word()); + cb.stack_pop(rd_address.length_word()); + cb.stack_push(Word::zero()); for (field_tag, value) in [ - (CallContextFieldTag::LastCalleeId, callee_call_id.expr()), - (CallContextFieldTag::LastCalleeReturnDataOffset, 0.expr()), - (CallContextFieldTag::LastCalleeReturnDataLength, 0.expr()), + ( + CallContextFieldTag::LastCalleeId, + Word::from_lo_unchecked(callee_call_id.expr()), + ), + ( + CallContextFieldTag::LastCalleeReturnDataOffset, + Word::zero(), + ), + ( + CallContextFieldTag::LastCalleeReturnDataLength, + Word::zero(), + ), ] { cb.call_context_lookup(true.expr(), None, field_tag, value); } - let cd_address = MemoryAddressGadget::construct(cb, cd_offset, cd_length); - let rd_address = MemoryAddressGadget::construct(cb, rd_offset, rd_length); - Self { opcode, is_call, @@ -160,11 +173,13 @@ impl ExecutionGadget for ErrorPrecompileFailedGadget { offset, F::from(opcode.as_u64()) - F::from(OpcodeId::STATICCALL.as_u64()), )?; - self.gas.assign(region, offset, Some(gas.to_le_bytes()))?; - self.callee_address - .assign(region, offset, Some(callee_address.to_le_bytes()))?; - self.value - .assign(region, offset, Some(value.to_le_bytes()))?; + self.gas.assign_u256(region, offset, gas)?; + self.callee_address.assign( + region, + offset, + Value::known(F::from(callee_address.as_u64())), + )?; + self.value.assign_u256(region, offset, value)?; self.cd_address .assign(region, offset, cd_offset, cd_length)?; self.rd_address diff --git a/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs b/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs index 5670d8cf57..962c550460 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs @@ -5,12 +5,15 @@ use crate::{ util::{ common_gadget::{CommonErrorGadget, CommonReturnDataCopyGadget}, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - CachedRegion, Cell, Word, + CachedRegion, Cell, U64Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian, ToScalar}; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -18,7 +21,7 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ErrorReturnDataOutOfBoundGadget { opcode: Cell, - memory_offset: Word, + memory_offset: U64Cell, // Hold the size of the last callee return data. return_data_length: Cell, overflow_gadget: CommonReturnDataCopyGadget, @@ -32,7 +35,7 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let memory_offset = cb.query_word_rlc(); + let memory_offset = cb.query_u64(); let return_data_length = cb.query_cell(); cb.require_equal( @@ -45,16 +48,15 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { CommonReturnDataCopyGadget::construct(cb, return_data_length.expr(), 1.expr()); // Pop memory_offset, offset, size from stack - cb.stack_pop(memory_offset.expr()); - cb.stack_pop(overflow_gadget.data_offset().expr()); - cb.stack_pop(overflow_gadget.size().expr()); + cb.stack_pop(memory_offset.to_word()); + cb.stack_pop(overflow_gadget.data_offset().to_word()); + cb.stack_pop(overflow_gadget.size().to_word()); // Read last callee return data length - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::LastCalleeReturnDataLength, - return_data_length.expr(), + Word::from_lo_unchecked(return_data_length.expr()), ); // Check if `data_offset` is Uint64 overflow. diff --git a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs index 8ba1620693..222d7e00b9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs @@ -5,25 +5,28 @@ use crate::{ util::{ common_gadget::CommonErrorGadget, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - math_gadget::IsZeroGadget, - CachedRegion, Cell, Word as RLCWord, + math_gadget::{IsZeroGadget, IsZeroWordGadget}, + AccountAddress, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian, U256}; +use eth_types::{evm_types::OpcodeId, Field, ToAddress, U256}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ErrorWriteProtectionGadget { opcode: Cell, is_call: IsZeroGadget, - gas: RLCWord, - code_address: RLCWord, - value: RLCWord, - is_value_zero: IsZeroGadget, + gas: WordCell, + code_address: AccountAddress, + value: WordCell, + is_value_zero: IsZeroWordGadget>, common_error_gadget: CommonErrorGadget, } @@ -35,10 +38,10 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); let is_call = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CALL.expr()); - let gas_word = cb.query_word_rlc(); - let code_address_word = cb.query_word_rlc(); - let value = cb.query_word_rlc(); - let is_value_zero = IsZeroGadget::construct(cb, value.expr()); + let gas_word = cb.query_word_unchecked(); + let code_address = cb.query_account_address(); + let value = cb.query_word_unchecked(); + let is_value_zero = IsZeroWordGadget::construct(cb, &value); // require_in_set method will spilit into more low degree expressions if exceed // max_degree. otherwise need to do fixed lookup for these opcodes @@ -63,15 +66,14 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { // Lookup values from stack if opcode is call // Precondition: If there's a StackUnderflow CALL, is handled before this error cb.condition(is_call.expr(), |cb| { - cb.stack_pop(gas_word.expr()); - cb.stack_pop(code_address_word.expr()); - cb.stack_pop(value.expr()); - //cb.require_zero("value of call is not zero", - // is_value_zero.expr()); + cb.stack_pop(gas_word.to_word()); + cb.stack_pop(code_address.to_word()); + cb.stack_pop(value.to_word()); + cb.require_zero("value of call is not zero", is_value_zero.expr()); }); // current call context is readonly - cb.call_context_lookup(false.expr(), None, CallContextFieldTag::IsStatic, 1.expr()); + cb.call_context_lookup_read(None, CallContextFieldTag::IsStatic, Word::one()); // constrain not root call as at least one previous staticcall preset. cb.require_zero( @@ -85,7 +87,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { opcode, is_call, gas: gas_word, - code_address: code_address_word, + code_address, value, is_value_zero, common_error_gadget, @@ -113,11 +115,10 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { .map(|idx| block.rws[idx].stack_value()); } - self.gas.assign(region, offset, Some(gas.to_le_bytes()))?; + self.gas.assign_u256(region, offset, gas)?; self.code_address - .assign(region, offset, Some(code_address.to_le_bytes()))?; - self.value - .assign(region, offset, Some(value.to_le_bytes()))?; + .assign_h160(region, offset, code_address.to_address())?; + self.value.assign_u256(region, offset, value)?; self.is_call.assign( region, @@ -125,8 +126,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { F::from(opcode.as_u64()) - F::from(OpcodeId::CALL.as_u64()), )?; self.is_value_zero - .assign_value(region, offset, region.word_rlc(value))?; - + .assign(region, offset, Word::from(value))?; self.common_error_gadget.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/exp.rs b/zkevm-circuits/src/evm_circuit/execution/exp.rs index 62b2fba5c1..d669ffcc79 100644 --- a/zkevm-circuits/src/evm_circuit/execution/exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/exp.rs @@ -1,20 +1,23 @@ use bus_mapping::evm::OpcodeId; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar}; +use eth_types::{evm_types::GasCost, Field, ToScalar}; use gadgets::util::{and, not, split_u256, Expr}; use halo2_proofs::plonk::Error; -use crate::evm_circuit::{ - step::ExecutionState, - util::{ - common_gadget::SameContextGadget, - constraint_builder::{ - ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, +use crate::{ + evm_circuit::{ + step::ExecutionState, + util::{ + common_gadget::SameContextGadget, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, + }, + from_bytes, + math_gadget::{ByteOrWord, ByteSizeGadget, IsEqualGadget, IsZeroGadget}, + CachedRegion, }, - from_bytes, - math_gadget::{ByteOrWord, ByteSizeGadget, IsEqualGadget, IsZeroGadget}, - CachedRegion, Word, + witness::{Block, Call, ExecStep, Transaction}, }, - witness::{Block, Call, ExecStep, Transaction}, + util::word::{Word32Cell, WordExpr}, }; use super::ExecutionGadget; @@ -24,11 +27,13 @@ pub(crate) struct ExponentiationGadget { /// Gadget to check that we stay within the same context. same_context: SameContextGadget, /// RLC-encoded integer base that will be exponentiated. - base: Word, + base: Word32Cell, + // /// RLC-encoded representation for base * base, i.e. base^2 + // base_sq: Word32Cell, /// RLC-encoded exponent for the exponentiation operation. - exponent: Word, + exponent: Word32Cell, /// RLC-encoded result of the exponentiation. - exponentiation: Word, + exponentiation: Word32Cell, /// Gadget to check if low 128-bit part of exponent is zero or not. exponent_lo_is_zero: IsZeroGadget, /// Gadget to check if high 128-bit part of exponent is zero or not. @@ -49,32 +54,23 @@ impl ExecutionGadget for ExponentiationGadget { // Query RLC-encoded values for base, exponent and exponentiation, where: // base^exponent == exponentiation (mod 2^256). - let base_rlc = cb.query_word_rlc(); - let exponent_rlc = cb.query_word_rlc(); - let exponentiation_rlc = cb.query_word_rlc(); + let base = cb.query_word32(); + let exponent = cb.query_word32(); + let exponentiation = cb.query_word32(); // Pop RLC-encoded base and exponent from the stack. - cb.stack_pop(base_rlc.expr()); - cb.stack_pop(exponent_rlc.expr()); + cb.stack_pop(base.to_word()); + cb.stack_pop(exponent.to_word()); // Push RLC-encoded exponentiation to the stack. - cb.stack_push(exponentiation_rlc.expr()); + cb.stack_push(exponentiation.to_word()); // Extract low and high bytes of the base. - let (base_lo, base_hi) = ( - from_bytes::expr(&base_rlc.cells[0x00..0x10]), - from_bytes::expr(&base_rlc.cells[0x10..0x20]), - ); + let (base_lo, base_hi) = base.to_word().to_lo_hi(); // Extract low and high bytes of the exponent. - let (exponent_lo, exponent_hi) = ( - from_bytes::expr(&exponent_rlc.cells[0x00..0x10]), - from_bytes::expr(&exponent_rlc.cells[0x10..0x20]), - ); + let (exponent_lo, exponent_hi) = exponent.to_word().to_lo_hi(); // Extract low and high bytes of the exponentiation result. - let (exponentiation_lo, exponentiation_hi) = ( - from_bytes::expr(&exponentiation_rlc.cells[0x00..0x10]), - from_bytes::expr(&exponentiation_rlc.cells[0x10..0x20]), - ); + let (exponentiation_lo, exponentiation_hi) = exponentiation.to_word().to_lo_hi(); // We simplify constraints depending on whether or not the exponent is 0 or 1. // In order to do this, we build some utility expressions. @@ -86,6 +82,8 @@ impl ExecutionGadget for ExponentiationGadget { let exponent_is_one_expr = and::expr([exponent_lo_is_one.expr(), exponent_hi_is_zero.expr()]); + // let base_sq = cb.query_word32(); + // If exponent == 0, base^exponent == 1, which implies: // 1. Low bytes of exponentiation == 1 // 2. High bytes of exponentiation == 0 @@ -124,10 +122,10 @@ impl ExecutionGadget for ExponentiationGadget { ]), |cb| { let base_limbs = [ - from_bytes::expr(&base_rlc.cells[0x00..0x08]), - from_bytes::expr(&base_rlc.cells[0x08..0x10]), - from_bytes::expr(&base_rlc.cells[0x10..0x18]), - from_bytes::expr(&base_rlc.cells[0x18..0x20]), + from_bytes::expr(&base.limbs[0x00..0x08]), + from_bytes::expr(&base.limbs[0x08..0x10]), + from_bytes::expr(&base.limbs[0x10..0x18]), + from_bytes::expr(&base.limbs[0x18..0x20]), ]; // lookup (base, exponent, exponentiation) cb.exp_table_lookup( @@ -143,8 +141,8 @@ impl ExecutionGadget for ExponentiationGadget { // bytes that can represent the exponent value. let exponent_byte_size = ByteSizeGadget::construct( cb, - exponent_rlc - .cells + exponent + .limbs .iter() .map(Expr::expr) .collect::>() @@ -169,9 +167,9 @@ impl ExecutionGadget for ExponentiationGadget { Self { same_context, - base: base_rlc, - exponent: exponent_rlc, - exponentiation: exponentiation_rlc, + base, + exponent, + exponentiation, exponent_lo_is_zero, exponent_hi_is_zero, exponent_lo_is_one, @@ -194,11 +192,10 @@ impl ExecutionGadget for ExponentiationGadget { [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] .map(|idx| block.rws[idx].stack_value()); - self.base.assign(region, offset, Some(base.to_le_bytes()))?; - self.exponent - .assign(region, offset, Some(exponent.to_le_bytes()))?; + self.base.assign_u256(region, offset, base)?; + self.exponent.assign_u256(region, offset, exponent)?; self.exponentiation - .assign(region, offset, Some(exponentiation.to_le_bytes()))?; + .assign_u256(region, offset, exponentiation)?; let (exponent_lo, exponent_hi) = split_u256(&exponent); let exponent_lo_scalar = exponent_lo diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index df0030b642..fe0e06badd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -8,36 +8,39 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition, }, - from_bytes, - math_gadget::IsZeroGadget, + math_gadget::IsZeroWordGadget, memory_gadget::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget, }, - not, select, CachedRegion, Cell, Word, + not, select, AccountAddress, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, + util::word::{Word, Word32Cell, WordExpr}, }; use bus_mapping::circuit_input_builder::CopyDataType; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar}; +use eth_types::{evm_types::GasCost, Field, ToScalar}; use gadgets::util::Expr; -use halo2_proofs::{circuit::Value, plonk::Error}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; use super::ExecutionGadget; #[derive(Clone, Debug)] pub(crate) struct ExtcodecopyGadget { same_context: SameContextGadget, - external_address_word: Word, + external_address_word: Word32Cell, memory_address: MemoryAddressGadget, code_offset: WordByteCapGadget, tx_id: Cell, reversion_info: ReversionInfo, is_warm: Cell, - code_hash: Cell, - not_exists: IsZeroGadget, + code_hash: Word32Cell, + not_exists: IsZeroWordGadget>>, code_size: Cell, copy_rwc_inc: Cell, memory_expansion: MemoryExpansionGadget, @@ -52,49 +55,53 @@ impl ExecutionGadget for ExtcodecopyGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let external_address_word = cb.query_word_rlc(); - let external_address = - from_bytes::expr(&external_address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); - + let external_address_word = cb.query_word32(); + let external_address = AccountAddress::new( + external_address_word.limbs[..N_BYTES_ACCOUNT_ADDRESS] + .to_vec() + .try_into() + .unwrap(), + ); let code_size = cb.query_cell(); - let memory_length = cb.query_word_rlc(); - let memory_offset = cb.query_cell_phase2(); + let memory_length = cb.query_memory_address(); + let memory_offset = cb.query_word_unchecked(); let code_offset = WordByteCapGadget::construct(cb, code_size.expr()); - cb.stack_pop(external_address_word.expr()); - cb.stack_pop(memory_offset.expr()); - cb.stack_pop(code_offset.original_word()); - cb.stack_pop(memory_length.expr()); + cb.stack_pop(external_address_word.to_word()); + cb.stack_pop(memory_offset.to_word()); + cb.stack_pop(code_offset.original_word().to_word()); + cb.stack_pop(memory_length.to_word()); let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let mut reversion_info = cb.reversion_info_read(None); let is_warm = cb.query_bool(); - cb.account_access_list_write( + cb.account_access_list_write_unchecked( tx_id.expr(), - external_address.expr(), + external_address.to_word(), 1.expr(), is_warm.expr(), Some(&mut reversion_info), ); - let code_hash = cb.query_cell_phase2(); + let code_hash = cb.query_word32(); cb.account_read( - external_address.expr(), + external_address.to_word(), AccountFieldTag::CodeHash, - code_hash.expr(), + code_hash.to_word(), ); - let not_exists = IsZeroGadget::construct(cb, code_hash.expr()); + + let not_exists = IsZeroWordGadget::construct(cb, &code_hash.to_word()); let exists = not::expr(not_exists.expr()); cb.condition(exists.expr(), |cb| { - cb.bytecode_length(code_hash.expr(), code_size.expr()); + cb.bytecode_length(code_hash.to_word(), code_size.expr()); }); cb.condition(not_exists.expr(), |cb| { cb.require_zero("code_size is zero when non_exists", code_size.expr()); }); let memory_address = MemoryAddressGadget::construct(cb, memory_offset, memory_length); - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, memory_address.length(), @@ -117,9 +124,9 @@ impl ExecutionGadget for ExtcodecopyGadget { ); cb.copy_table_lookup( - code_hash.expr(), + code_hash.to_word(), CopyDataType::Bytecode.expr(), - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), src_addr, code_size.expr(), @@ -178,7 +185,7 @@ impl ExecutionGadget for ExtcodecopyGadget { let [external_address, memory_offset, code_offset, memory_length] = [0, 1, 2, 3].map(|idx| block.rws[step.rw_indices[idx]].stack_value()); self.external_address_word - .assign(region, offset, Some(external_address.to_le_bytes()))?; + .assign_u256(region, offset, external_address)?; let memory_address = self.memory_address .assign(region, offset, memory_offset, memory_length)?; @@ -197,10 +204,8 @@ impl ExecutionGadget for ExtcodecopyGadget { .assign(region, offset, Value::known(F::from(is_warm as u64)))?; let code_hash = block.rws[step.rw_indices[8]].account_value_pair().0; - self.code_hash - .assign(region, offset, region.code_hash(code_hash))?; - self.not_exists - .assign_value(region, offset, region.code_hash(code_hash))?; + self.code_hash.assign_u256(region, offset, code_hash)?; + self.not_exists.assign_u256(region, offset, code_hash)?; let code_size = if code_hash.is_zero() { 0 diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs index 3e36360501..f08245ab7e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs @@ -8,24 +8,27 @@ use crate::{ constraint_builder::{ EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, }, - from_bytes, select, CachedRegion, Cell, Word, + select, AccountAddress, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, - util::Expr, + util::{ + word::{Word32Cell, WordCell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian}; +use eth_types::{evm_types::GasCost, Field}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ExtcodehashGadget { same_context: SameContextGadget, - address_word: Word, + address_word: Word32Cell, tx_id: Cell, reversion_info: ReversionInfo, is_warm: Cell, - code_hash: Cell, + code_hash: WordCell, } impl ExecutionGadget for ExtcodehashGadget { @@ -34,34 +37,39 @@ impl ExecutionGadget for ExtcodehashGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EXTCODEHASH; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let address_word = cb.query_word_rlc(); - let address = from_bytes::expr(&address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); - cb.stack_pop(address_word.expr()); + let address_word = cb.query_word32(); + let address = AccountAddress::new( + address_word.limbs[..N_BYTES_ACCOUNT_ADDRESS] + .to_vec() + .try_into() + .unwrap(), + ); + cb.stack_pop(address_word.to_word()); let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let mut reversion_info = cb.reversion_info_read(None); let is_warm = cb.query_bool(); - cb.account_access_list_write( + cb.account_access_list_write_unchecked( tx_id.expr(), - address.expr(), + address.to_word(), 1.expr(), is_warm.expr(), Some(&mut reversion_info), ); - let code_hash = cb.query_cell_phase2(); + let code_hash = cb.query_word_unchecked(); // For non-existing accounts the code_hash must be 0 in the rw_table. cb.account_read( - address, + address.to_word(), if cfg!(feature = "scroll") { AccountFieldTag::KeccakCodeHash } else { AccountFieldTag::CodeHash }, - code_hash.expr(), + code_hash.to_word(), ); - cb.stack_push(code_hash.expr()); + cb.stack_push(code_hash.to_word()); let gas_cost = select::expr( is_warm.expr(), @@ -102,8 +110,7 @@ impl ExecutionGadget for ExtcodehashGadget { self.same_context.assign_exec_step(region, offset, step)?; let address = block.rws[step.rw_indices[0]].stack_value(); - self.address_word - .assign(region, offset, Some(address.to_le_bytes()))?; + self.address_word.assign_u256(region, offset, address)?; self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; @@ -119,8 +126,7 @@ impl ExecutionGadget for ExtcodehashGadget { .assign(region, offset, Value::known(F::from(is_warm as u64)))?; let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; - self.code_hash - .assign(region, offset, region.word_rlc(code_hash))?; + self.code_hash.assign_u256(region, offset, code_hash)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs index dbfce3b460..99356bcfd6 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_U64}, + param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -9,28 +9,33 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, }, - from_bytes, - math_gadget::IsZeroGadget, - not, select, CachedRegion, Cell, RandomLinearCombination, Word, + math_gadget::IsZeroWordGadget, + not, select, AccountAddress, CachedRegion, Cell, U64Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, - util::Expr, + util::{ + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian}; + +#[cfg(not(feature = "scroll"))] +use crate::evm_circuit::util::from_bytes; +use eth_types::{evm_types::GasCost, Field}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ExtcodesizeGadget { same_context: SameContextGadget, - address_word: Word, + address_word: Word32Cell, reversion_info: ReversionInfo, tx_id: Cell, is_warm: Cell, - code_hash: Cell, - not_exists: IsZeroGadget, - code_size: RandomLinearCombination, + code_hash: WordCell, + not_exists: IsZeroWordGadget>, + code_size: U64Cell, } impl ExecutionGadget for ExtcodesizeGadget { @@ -39,44 +44,54 @@ impl ExecutionGadget for ExtcodesizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::EXTCODESIZE; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let address_word = cb.query_word_rlc(); - let address = from_bytes::expr(&address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]); - cb.stack_pop(address_word.expr()); + let address_word = cb.query_word32(); + let address = AccountAddress::new( + address_word.limbs[..N_BYTES_ACCOUNT_ADDRESS] + .to_vec() + .try_into() + .unwrap(), + ); + cb.stack_pop(address_word.to_word()); let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let mut reversion_info = cb.reversion_info_read(None); let is_warm = cb.query_bool(); - cb.account_access_list_write( + cb.account_access_list_write_unchecked( tx_id.expr(), - address.expr(), + address.to_word(), 1.expr(), is_warm.expr(), Some(&mut reversion_info), ); - let code_hash = cb.query_cell_phase2(); + // range check will be cover by account code_hash lookup + let code_hash = cb.query_word_unchecked(); // For non-existing accounts the code_hash must be 0 in the rw_table. - cb.account_read(address.expr(), AccountFieldTag::CodeHash, code_hash.expr()); - let not_exists = IsZeroGadget::construct(cb, code_hash.expr()); + cb.account_read( + address.to_word(), + AccountFieldTag::CodeHash, + code_hash.to_word(), + ); + let not_exists = IsZeroWordGadget::construct(cb, &code_hash); let exists = not::expr(not_exists.expr()); - let code_size = cb.query_word_rlc(); + let code_size = cb.query_u64(); cb.condition(exists.expr(), |cb| { #[cfg(feature = "scroll")] cb.account_read( - address.expr(), + address.to_word(), AccountFieldTag::CodeSize, - from_bytes::expr(&code_size.cells), + code_size.to_word(), ); #[cfg(not(feature = "scroll"))] - cb.bytecode_length(code_hash.expr(), from_bytes::expr(&code_size.cells)); + cb.bytecode_length(code_hash.to_word(), from_bytes::expr(&code_size.limbs)); }); cb.condition(not_exists.expr(), |cb| { cb.require_zero("code_size is zero when non_exists", code_size.expr()); }); - cb.stack_push(code_size.expr()); + cb.stack_push(code_size.to_word()); let gas_cost = select::expr( is_warm.expr(), @@ -123,8 +138,7 @@ impl ExecutionGadget for ExtcodesizeGadget { self.same_context.assign_exec_step(region, offset, step)?; let address = block.rws[step.rw_indices[0]].stack_value(); - self.address_word - .assign(region, offset, Some(address.to_le_bytes()))?; + self.address_word.assign_u256(region, offset, address)?; self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; @@ -141,10 +155,10 @@ impl ExecutionGadget for ExtcodesizeGadget { .assign(region, offset, Value::known(F::from(is_warm)))?; let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; - self.code_hash - .assign(region, offset, region.code_hash(code_hash))?; + self.code_hash.assign_u256(region, offset, code_hash)?; + self.not_exists - .assign_value(region, offset, region.code_hash(code_hash))?; + .assign(region, offset, Word::from(code_hash))?; let rw_offset = 6; #[cfg(feature = "scroll")] @@ -153,9 +167,10 @@ impl ExecutionGadget for ExtcodesizeGadget { } else { rw_offset + 1 }; - let code_size = block.rws[step.rw_indices[rw_offset]].stack_value().as_u64(); - self.code_size - .assign(region, offset, Some(code_size.to_le_bytes()))?; + let code_size = block.rws[step.rw_indices[rw_offset]].stack_value(); + // self.code_size + // .assign(region, offset, Some(code_size.to_le_bytes()))?; + self.code_size.assign_u256(region, offset, code_size)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/gas.rs b/zkevm-circuits/src/evm_circuit/execution/gas.rs index 7985c76e8a..460fba160a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gas.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gas.rs @@ -1,7 +1,6 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_GAS, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -9,11 +8,11 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, U64Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{word::WordExpr, Expr}, }; use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::plonk::Error; @@ -21,7 +20,7 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct GasGadget { same_context: SameContextGadget, - gas_left: RandomLinearCombination, + gas_left: U64Cell, } impl ExecutionGadget for GasGadget { @@ -31,18 +30,18 @@ impl ExecutionGadget for GasGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { // The gas passed to a transaction is a 64-bit number. - let gas_left = cb.query_word_rlc(); + let gas_left = cb.query_u64(); // The `gas_left` in the current state has to be deducted by the gas // used by the `GAS` opcode itself. cb.require_equal( "Constraint: gas left equal to stack value", - from_bytes::expr(&gas_left.cells), + gas_left.expr(), cb.curr.state.gas_left.expr() - OpcodeId::GAS.constant_gas_cost().expr(), ); // Construct the value and push it to stack. - cb.stack_push(gas_left.expr()); + cb.stack_push(gas_left.to_word()); let step_state_transition = StepStateTransition { rw_counter: Delta(1.expr()), diff --git a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs index 8ea149060b..7a4d594818 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs @@ -10,7 +10,10 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -19,7 +22,8 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct GasPriceGadget { tx_id: Cell, - gas_price: Cell, + gas_price: Word32Cell, + // gas_price_rlc: Cell, same_context: SameContextGadget, } @@ -30,7 +34,9 @@ impl ExecutionGadget for GasPriceGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { // Query gasprice value - let gas_price = cb.query_cell_phase2(); + let gas_price = cb.query_word32(); + // let gas_price_rlc = cb.query_cell(); + let gas_price_rlc = cb.word_rlc(gas_price.limbs.clone().map(|l| l.expr())); // Lookup in call_ctx the TxId let tx_id = cb.call_context(None, CallContextFieldTag::TxId); @@ -39,11 +45,13 @@ impl ExecutionGadget for GasPriceGadget { tx_id.expr(), TxContextFieldTag::GasPrice, None, - gas_price.expr(), + // gas_price.to_word(), + // gas_price_rlc.expr(), + Word::from_lo_unchecked(gas_price_rlc.expr()), ); // Push the value to the stack - cb.stack_push(gas_price.expr()); + cb.stack_push(gas_price.to_word()); // State transition let opcode = cb.query_cell(); @@ -59,6 +67,7 @@ impl ExecutionGadget for GasPriceGadget { Self { tx_id, gas_price, + //gas_price_rlc, same_context, } } @@ -77,8 +86,10 @@ impl ExecutionGadget for GasPriceGadget { self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; - self.gas_price - .assign(region, offset, region.word_rlc(gas_price))?; + // self.gas_price_rlc + // .assign(region, offset, region.word_rlc(gas_price))?; + + self.gas_price.assign_u256(region, offset, gas_price)?; self.same_context.assign_exec_step(region, offset, step)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs index a0d5e12520..6513d811e8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs @@ -5,21 +5,24 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - math_gadget, CachedRegion, Cell, + math_gadget, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; -use halo2_proofs::plonk::Error; +use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct IsZeroGadget { same_context: SameContextGadget, - value: Cell, - is_zero: math_gadget::IsZeroGadget, + value: WordCell, + is_zero_word: math_gadget::IsZeroWordGadget>, } impl ExecutionGadget for IsZeroGadget { @@ -30,11 +33,11 @@ impl ExecutionGadget for IsZeroGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let value = cb.query_cell_phase2(); - let is_zero = math_gadget::IsZeroGadget::construct(cb, value.expr()); + let value = cb.query_word_unchecked(); + let is_zero_word = math_gadget::IsZeroWordGadget::construct(cb, &value); - cb.stack_pop(value.expr()); - cb.stack_push(is_zero.expr()); + cb.stack_pop(value.to_word()); + cb.stack_push(Word::from_lo_unchecked(is_zero_word.expr())); // State transition let step_state_transition = StepStateTransition { @@ -49,7 +52,7 @@ impl ExecutionGadget for IsZeroGadget { Self { same_context, value, - is_zero, + is_zero_word, } } @@ -65,9 +68,9 @@ impl ExecutionGadget for IsZeroGadget { self.same_context.assign_exec_step(region, offset, step)?; let value = block.rws[step.rw_indices[0]].stack_value(); - let value = region.word_rlc(value); - self.value.assign(region, offset, value)?; - self.is_zero.assign_value(region, offset, value)?; + self.value.assign_u256(region, offset, value)?; + self.is_zero_word + .assign_value(region, offset, Value::known(Word::from(value)))?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/jump.rs b/zkevm-circuits/src/evm_circuit/execution/jump.rs index ead6133a5d..b8a43fc75a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jump.rs @@ -1,7 +1,6 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_PROGRAM_COUNTER, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -9,19 +8,19 @@ use crate::{ EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, U64Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{word::WordExpr, Expr}, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; +use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct JumpGadget { same_context: SameContextGadget, - destination: RandomLinearCombination, + destination: U64Cell, } impl ExecutionGadget for JumpGadget { @@ -30,23 +29,19 @@ impl ExecutionGadget for JumpGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::JUMP; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let destination = cb.query_word_rlc(); + let destination = cb.query_u64(); // Pop the value from the stack - cb.stack_pop(destination.expr()); + cb.stack_pop(destination.to_word()); // Lookup opcode at destination - cb.opcode_lookup_at( - from_bytes::expr(&destination.cells), - OpcodeId::JUMPDEST.expr(), - 1.expr(), - ); + cb.opcode_lookup_at(destination.expr(), OpcodeId::JUMPDEST.expr(), 1.expr()); // State transition let opcode = cb.query_cell(); let step_state_transition = StepStateTransition { rw_counter: Delta(1.expr()), - program_counter: To(from_bytes::expr(&destination.cells)), + program_counter: To(destination.expr()), stack_pointer: Delta(1.expr()), gas_left: Delta(-OpcodeId::JUMP.constant_gas_cost().expr()), ..Default::default() @@ -71,15 +66,7 @@ impl ExecutionGadget for JumpGadget { self.same_context.assign_exec_step(region, offset, step)?; let destination = block.rws[step.rw_indices[0]].stack_value(); - self.destination.assign( - region, - offset, - Some( - destination.to_le_bytes()[..N_BYTES_PROGRAM_COUNTER] - .try_into() - .unwrap(), - ), - )?; + self.destination.assign_u256(region, offset, destination)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs index 4a463bfd5a..f4a838be73 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs @@ -9,22 +9,25 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, }, - math_gadget::IsZeroGadget, - select, CachedRegion, Cell, + math_gadget::IsZeroWordGadget, + select, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field}; -use halo2_proofs::plonk::Error; +use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct JumpiGadget { same_context: SameContextGadget, dest: WordByteRangeGadget, - phase2_condition: Cell, - is_condition_zero: IsZeroGadget, + condition: WordCell, + is_condition_zero: IsZeroWordGadget>, } impl ExecutionGadget for JumpiGadget { @@ -34,14 +37,14 @@ impl ExecutionGadget for JumpiGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let dest = WordByteRangeGadget::construct(cb); - let phase2_condition = cb.query_cell_phase2(); + let condition = cb.query_word_unchecked(); // Pop the value from the stack - cb.stack_pop(dest.original_word()); - cb.stack_pop(phase2_condition.expr()); + cb.stack_pop(dest.original()); + cb.stack_pop(condition.to_word()); // Determine if the jump condition is met - let is_condition_zero = IsZeroGadget::construct(cb, phase2_condition.expr()); + let is_condition_zero = IsZeroWordGadget::construct(cb, &condition); let should_jump = 1.expr() - is_condition_zero.expr(); // Lookup opcode at destination when should_jump @@ -77,7 +80,7 @@ impl ExecutionGadget for JumpiGadget { Self { same_context, dest, - phase2_condition, + condition, is_condition_zero, } } @@ -95,12 +98,11 @@ impl ExecutionGadget for JumpiGadget { let [destination, condition] = [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); - let condition = region.word_rlc(condition); self.dest.assign(region, offset, destination)?; - self.phase2_condition.assign(region, offset, condition)?; + self.condition.assign_u256(region, offset, condition)?; self.is_condition_zero - .assign_value(region, offset, condition)?; + .assign_value(region, offset, Value::known(Word::from(condition)))?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/logs.rs b/zkevm-circuits/src/evm_circuit/execution/logs.rs index 29ac31bbf2..7b009f8bfb 100644 --- a/zkevm-circuits/src/evm_circuit/execution/logs.rs +++ b/zkevm-circuits/src/evm_circuit/execution/logs.rs @@ -1,10 +1,10 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, + param::N_BYTES_MEMORY_WORD_SIZE, step::ExecutionState, util::{ - common_gadget::{SameContextGadget, WordByteRangeGadget}, + common_gadget::SameContextGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::{Delta, To}, @@ -17,7 +17,11 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::{CallContextFieldTag, RwTableTag, TxLogFieldTag}, - util::{build_tx_log_expression, Expr}, + util::{ + build_tx_log_expression, + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; use array_init::array_init; use bus_mapping::circuit_input_builder::CopyDataType; @@ -30,14 +34,12 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct LogGadget { same_context: SameContextGadget, - // TODO: It has a duplicate word with `memory_address`. - mstart_word: WordByteRangeGadget, // memory address memory_address: MemoryAddressGadget, - phase2_topics: [Cell; 4], + topics: [Word32Cell; 4], topic_selectors: [Cell; 4], - contract_address: Cell, + contract_address: WordCell, is_static_call: Cell, is_persistent: Cell, tx_id: Cell, @@ -52,12 +54,12 @@ impl ExecutionGadget for LogGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::LOG; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let mstart_word = WordByteRangeGadget::construct(cb); - let msize = cb.query_word_rlc(); + let mstart = cb.query_word_unchecked(); + let msize = cb.query_memory_address(); // Pop mstart_address, msize from stack - cb.stack_pop(mstart_word.original_word()); - cb.stack_pop(msize.expr()); + cb.stack_pop(mstart.to_word()); + cb.stack_pop(msize.to_word()); // read tx id let tx_id = cb.call_context(None, CallContextFieldTag::TxId); @@ -67,7 +69,8 @@ impl ExecutionGadget for LogGadget { // check contract_address in CallContext & TxLog // use call context's callee address as contract address - let contract_address = cb.call_context(None, CallContextFieldTag::CalleeAddress); + let contract_address = + cb.call_context_read_as_word(None, CallContextFieldTag::CalleeAddress); let is_persistent = cb.call_context(None, CallContextFieldTag::IsPersistent); cb.require_boolean("is_persistent is bool", is_persistent.expr()); @@ -77,16 +80,16 @@ impl ExecutionGadget for LogGadget { cb.curr.state.log_id.expr() + 1.expr(), TxLogFieldTag::Address, 0.expr(), - contract_address.expr(), + contract_address.to_word(), ); }); // constrain topics in logs - let phase2_topics = array_init(|_| cb.query_cell_phase2()); + let topics = array_init(|_| cb.query_word32()); let topic_selectors: [Cell; 4] = array_init(|_| cb.query_cell()); - for (idx, topic) in phase2_topics.iter().enumerate() { + for (idx, topic) in topics.iter().enumerate() { cb.condition(topic_selectors[idx].expr(), |cb| { - cb.stack_pop(topic.expr()); + cb.stack_pop(topic.to_word()); }); cb.condition(topic_selectors[idx].expr() * is_persistent.expr(), |cb| { cb.tx_log_lookup( @@ -94,7 +97,7 @@ impl ExecutionGadget for LogGadget { cb.curr.state.log_id.expr() + 1.expr(), TxLogFieldTag::Topic, idx.expr(), - topic.expr(), + topic.to_word(), ); }); } @@ -123,23 +126,12 @@ impl ExecutionGadget for LogGadget { } // check memory copy - let mstart = cb.query_cell_phase2(); - let memory_address = MemoryAddressGadget::construct(cb, mstart.clone(), msize); - cb.require_equal( - "mstart == mstart_word value", - mstart.expr(), - mstart_word.original_word(), - ); - cb.condition(mstart_word.overflow(), |cb| { - cb.require_zero( - "Memory size must be zero if memory start is overflow", - memory_address.has_length(), - ); - }); + //let mstart = cb.query_cell_phase2(); + let memory_address = MemoryAddressGadget::construct(cb, mstart, msize); // Calculate the next memory size and the gas cost for this memory // access - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let copy_rwc_inc = cb.query_cell(); let dst_addr = build_tx_log_expression( @@ -150,12 +142,12 @@ impl ExecutionGadget for LogGadget { let cond = memory_address.has_length() * is_persistent.expr(); cb.condition(cond.clone(), |cb| { cb.copy_table_lookup( - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), - tx_id.expr(), + Word::from_lo_unchecked(tx_id.expr()), CopyDataType::TxLog.expr(), memory_address.offset(), - memory_address.end_offset(), + memory_address.address(), dst_addr, memory_address.length(), 0.expr(), // for LOGN, rlc_acc is 0 @@ -189,9 +181,8 @@ impl ExecutionGadget for LogGadget { Self { same_context, - mstart_word, memory_address, - phase2_topics, + topics, topic_selectors, contract_address, is_static_call, @@ -216,8 +207,6 @@ impl ExecutionGadget for LogGadget { let [memory_start, msize] = [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); - self.mstart_word.assign(region, offset, memory_start)?; - let memory_address = self .memory_address .assign(region, offset, memory_start, msize)?; @@ -239,26 +228,19 @@ impl ExecutionGadget for LogGadget { }; for i in 0..4 { - let mut topic = region.word_rlc(U256::zero()); + let mut topic = U256::zero(); if i < topic_count { - topic = region.word_rlc(block.rws[topic_stack_entry].stack_value()); + topic = block.rws[topic_stack_entry].stack_value(); self.topic_selectors[i].assign(region, offset, Value::known(F::one()))?; topic_stack_entry.1 += 1; } else { self.topic_selectors[i].assign(region, offset, Value::known(F::zero()))?; } - self.phase2_topics[i].assign(region, offset, topic)?; + self.topics[i].assign_u256(region, offset, topic)?; } - self.contract_address.assign( - region, - offset, - Value::known( - call.callee_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), - )?; + self.contract_address + .assign_h160(region, offset, call.callee_address)?; self.is_static_call .assign(region, offset, Value::known(F::from(call.is_static as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/memory.rs b/zkevm-circuits/src/evm_circuit/execution/memory.rs index 7eec30c750..cb3ac0045b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/memory.rs @@ -12,14 +12,17 @@ use crate::{ from_bytes, math_gadget::IsEqualGadget, memory_gadget::{MemoryExpansionGadget, MemoryMask, MemoryWordAddress}, - not, CachedRegion, Word, + not, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian, U256}; +use eth_types::{evm_types::OpcodeId, Field, U256}; use halo2_proofs::plonk::Error; // MemoryGadget handles mload/mstore/mstore8 op codes gadget @@ -30,15 +33,15 @@ pub(crate) struct MemoryGadget { mask: MemoryMask, // consider move to MemoryWordAddress ? /// The value poped from or pushed to the stack. - value: Word, + value: Word32Cell, /// The left memory word read or written. - value_left: Word, + value_left: Word32Cell, /// The left memory word before the write. - value_left_prev: Word, + value_left_prev: Word32Cell, /// The right memory word read or written. - value_right: Word, + value_right: Word32Cell, /// The right memory word before the write. - value_right_prev: Word, + value_right_prev: Word32Cell, memory_expansion: MemoryExpansionGadget, is_mload: IsEqualGadget, is_mstore8: IsEqualGadget, @@ -53,13 +56,14 @@ impl ExecutionGadget for MemoryGadget { let opcode = cb.query_cell(); // In successful case the address must be in 5 bytes - let address = cb.query_word_rlc(); + + let address = cb.query_memory_address(); let address_word = MemoryWordAddress::construct(cb, address.clone()); - let value = cb.query_word_rlc(); - let value_left = cb.query_word_rlc(); - let value_left_prev = cb.query_word_rlc(); - let value_right = cb.query_word_rlc(); - let value_right_prev = cb.query_word_rlc(); + let value = cb.query_word32(); + let value_left = cb.query_word32(); + let value_left_prev = cb.query_word32(); + let value_right = cb.query_word32(); + let value_right_prev = cb.query_word32(); // Optimization possible: MSTORE does not need the value bytes, only the RLC. MSTORE8 does // not need the right value. So we could repurpose the same cells. @@ -76,7 +80,7 @@ impl ExecutionGadget for MemoryGadget { // access let memory_expansion = MemoryExpansionGadget::construct( cb, - [from_bytes::expr(&address.cells) + 1.expr() + (is_not_mstore8.clone() * 31.expr())], + [from_bytes::expr(&address.limbs) + 1.expr() + (is_not_mstore8.clone() * 31.expr())], ); let mask = MemoryMask::construct(cb, &address_word.shift_bits(), is_mstore8.expr()); @@ -87,40 +91,40 @@ impl ExecutionGadget for MemoryGadget { // Stack operations // Pop the address from the stack - cb.stack_pop(address.expr()); + cb.stack_pop(address.to_word()); // For MLOAD push the value to the stack // FOR MSTORE pop the value from the stack cb.stack_lookup( is_mload.expr(), cb.stack_pointer_offset().expr() - is_mload.expr(), - value.expr(), + value.to_word(), ); // Read or update the left word. cb.memory_lookup( is_store.clone(), address_word.addr_left(), - value_left.expr(), - value_left_prev.expr(), + value_left.to_word(), + value_left_prev.to_word(), None, ); cb.condition(is_mstore8.expr(), |cb| { // Check the byte that is written. - let first_byte = value.cells[0].expr(); + let first_byte = value.limbs[0].expr(); mask.require_equal_unaligned_byte(cb, first_byte, &value_left); }); cb.condition(is_not_mstore8, |cb| { // Check the bytes that are read or written from the left and right words. - mask.require_equal_unaligned_word(cb, value.expr(), &value_left, &value_right); + mask.require_equal_unaligned_word(cb, &value, &value_left, &value_right); // Read or update the right word. cb.memory_lookup( is_store.clone(), address_word.addr_right(), - value_right.expr(), - value_right_prev.expr(), + value_right.to_word(), + value_right_prev.to_word(), None, ); }); @@ -133,7 +137,6 @@ impl ExecutionGadget for MemoryGadget { // - `memory_size` needs to be set to `next_memory_size` let gas_cost = OpcodeId::MLOAD.constant_gas_cost().expr() + memory_expansion.gas_cost(); let step_state_transition = StepStateTransition { - //TODO: update rw_counter rw_counter: Delta(4.expr() - is_mstore8.expr()), program_counter: Delta(1.expr()), stack_pointer: Delta(is_store * 2.expr()), @@ -177,8 +180,7 @@ impl ExecutionGadget for MemoryGadget { let address = address.as_u64(); self.address.assign(region, offset, address)?; - self.value - .assign(region, offset, Some(value.to_le_bytes()))?; + self.value.assign_u256(region, offset, value)?; // Check if this is an MLOAD self.is_mload.assign( @@ -215,15 +217,13 @@ impl ExecutionGadget for MemoryGadget { block.rws[step.rw_indices[3]].memory_word_pair() }; - self.value_left - .assign(region, offset, Some(value_left.to_le_bytes()))?; + self.value_left.assign_u256(region, offset, value_left)?; self.value_left_prev - .assign(region, offset, Some(value_left_prev.to_le_bytes()))?; + .assign_u256(region, offset, value_left_prev)?; - self.value_right - .assign(region, offset, Some(value_right.to_le_bytes()))?; + self.value_right.assign_u256(region, offset, value_right)?; self.value_right_prev - .assign(region, offset, Some(value_right_prev.to_le_bytes()))?; + .assign_u256(region, offset, value_right_prev)?; Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/msize.rs b/zkevm-circuits/src/evm_circuit/execution/msize.rs index 37d04e91fe..86ee14eaf2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/msize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/msize.rs @@ -9,20 +9,20 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{word::Word, Expr}, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; -use halo2_proofs::plonk::Error; +use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct MsizeGadget { same_context: SameContextGadget, - value: RandomLinearCombination, + value: Cell, } impl ExecutionGadget for MsizeGadget { @@ -31,17 +31,16 @@ impl ExecutionGadget for MsizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::MSIZE; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let value = cb.query_word_rlc(); + let value = cb.query_cell(); // memory_size is limited to 64 bits so we only consider 8 bytes cb.require_equal( "Constrain memory_size equal to stack value", - from_bytes::expr(&value.cells), + value.expr(), cb.curr.state.memory_word_size.expr() * N_BYTES_WORD.expr(), ); - // Push the value on the stack - cb.stack_push(value.expr()); + cb.stack_push(Word::from_lo_unchecked(value.expr())); // State transition let step_state_transition = StepStateTransition { @@ -71,7 +70,7 @@ impl ExecutionGadget for MsizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; self.value - .assign(region, offset, Some((step.memory_size).to_le_bytes()))?; + .assign(region, offset, Value::known(F::from(step.memory_size)))?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs index e38ffd9d80..0d088044fe 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs @@ -3,21 +3,23 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{ - self, common_gadget::SameContextGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - math_gadget::{IsZeroGadget, LtWordGadget, MulAddWordsGadget}, - select, sum, CachedRegion, + math_gadget::{IsZeroWordGadget, LtWordGadget, MulAddWordsGadget}, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian, U256}; +use eth_types::{Field, U256}; use halo2_proofs::plonk::Error; /// MulGadget verifies opcode MUL, DIV, and MOD. @@ -29,11 +31,11 @@ use halo2_proofs::plonk::Error; pub(crate) struct MulDivModGadget { same_context: SameContextGadget, /// Words a, b, c, d - pub words: [util::Word; 4], + pub words: [Word32Cell; 4], /// Gadget that verifies a * b + c = d mul_add_words: MulAddWordsGadget, /// Check if divisor is zero for DIV and MOD - divisor_is_zero: IsZeroGadget, + divisor_is_zero: IsZeroWordGadget>, /// Check if residue < divisor when divisor != 0 for DIV and MOD lt_word: LtWordGadget, } @@ -55,32 +57,39 @@ impl ExecutionGadget for MulDivModGadget { let is_mod = (opcode.expr() - OpcodeId::MUL.expr()) * (opcode.expr() - OpcodeId::DIV.expr()) * F::from(8).invert().unwrap(); - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let c = cb.query_word_rlc(); - let d = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); + let c = cb.query_word32(); + let d = cb.query_word32(); let mul_add_words = MulAddWordsGadget::construct(cb, [&a, &b, &c, &d]); - let divisor_is_zero = IsZeroGadget::construct(cb, sum::expr(&b.cells)); - let lt_word = LtWordGadget::construct(cb, &c, &b); + let divisor_is_zero = IsZeroWordGadget::construct(cb, &b); + let lt_word = LtWordGadget::construct(cb, &c.to_word(), &b.to_word()); // Pop a and b from the stack, push result on the stack // The first pop is multiplier for MUL and dividend for DIV/MOD // The second pop is multiplicand for MUL and divisor for DIV/MOD // The push is product for MUL, quotient for DIV, and residue for MOD // Note that for DIV/MOD, when divisor == 0, the push value is also 0. - cb.stack_pop(select::expr(is_mul.clone(), a.expr(), d.expr())); - cb.stack_pop(b.expr()); + cb.stack_pop(Word::select(is_mul.clone(), a.to_word(), d.to_word())); + cb.stack_pop(b.to_word()); cb.stack_push( - is_mul.clone() * d.expr() - + is_div * a.expr() * (1.expr() - divisor_is_zero.expr()) - + is_mod * c.expr() * (1.expr() - divisor_is_zero.expr()), + d.to_word() + .mul_selector(is_mul.clone()) + .add_unchecked( + a.to_word() + .mul_selector(is_div * (1.expr() - divisor_is_zero.expr())), + ) + .add_unchecked( + c.to_word() + .mul_selector(is_mod * (1.expr() - divisor_is_zero.expr())), + ), ); // Constraint for MUL case - cb.add_constraint( + cb.require_zero_word( "c == 0 for opcode MUL", - is_mul.clone() * sum::expr(&c.cells), + c.to_word().mul_selector(is_mul.clone()), ); // Constraints for DIV and MOD cases @@ -138,15 +147,15 @@ impl ExecutionGadget for MulDivModGadget { ), _ => unreachable!(), }; - self.words[0].assign(region, offset, Some(a.to_le_bytes()))?; - self.words[1].assign(region, offset, Some(b.to_le_bytes()))?; - self.words[2].assign(region, offset, Some(c.to_le_bytes()))?; - self.words[3].assign(region, offset, Some(d.to_le_bytes()))?; + self.words[0].assign_u256(region, offset, a)?; + self.words[1].assign_u256(region, offset, b)?; + self.words[2].assign_u256(region, offset, c)?; + self.words[3].assign_u256(region, offset, d)?; self.mul_add_words.assign(region, offset, [a, b, c, d])?; self.lt_word.assign(region, offset, c, b)?; - let b_sum = (0..32).fold(0, |acc, idx| acc + b.byte(idx) as u64); - self.divisor_is_zero - .assign(region, offset, F::from(b_sum))?; + // let b_sum = (0..32).fold(0, |acc, idx| acc + b.byte(idx) as u64); + self.divisor_is_zero.assign(region, offset, Word::from(b))?; + Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs index f1347b73c9..8c4c9df796 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs @@ -3,21 +3,23 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{ - self, common_gadget::SameContextGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - math_gadget::{IsZeroGadget, LtWordGadget, ModGadget, MulAddWords512Gadget}, - sum, CachedRegion, + math_gadget::{IsZeroWordGadget, LtWordGadget, ModGadget, MulAddWords512Gadget}, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian, U256}; +use eth_types::{Field, U256}; use halo2_proofs::plonk::Error; /// MulModGadget verifies opcode MULMOD @@ -27,15 +29,15 @@ use halo2_proofs::plonk::Error; pub(crate) struct MulModGadget { same_context: SameContextGadget, // a, b, n, r - pub words: [util::Word; 4], - k: util::Word, - a_reduced: util::Word, - d: util::Word, - e: util::Word, - modword: ModGadget, + pub words: [Word32Cell; 4], + k: Word32Cell, + a_reduced: Word32Cell, + d: Word32Cell, + e: Word32Cell, + modword: ModGadget, mul512_left: MulAddWords512Gadget, mul512_right: MulAddWords512Gadget, - n_is_zero: IsZeroGadget, + n_is_zero: IsZeroWordGadget>, lt: LtWordGadget, } @@ -47,16 +49,16 @@ impl ExecutionGadget for MulModGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let n = cb.query_word_rlc(); - let r = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); + let n = cb.query_word32(); + let r = cb.query_word32(); - let k = cb.query_word_rlc(); + let k = cb.query_word32(); - let a_reduced = cb.query_word_rlc(); - let d = cb.query_word_rlc(); - let e = cb.query_word_rlc(); + let a_reduced = cb.query_word32(); + let d = cb.query_word32(); + let e = cb.query_word32(); // 1. k1 * n + a_reduced == a let modword = ModGadget::construct(cb, [&a, &n, &a_reduced]); @@ -68,17 +70,17 @@ impl ExecutionGadget for MulModGadget { let mul512_right = MulAddWords512Gadget::construct(cb, [&k, &n, &d, &e], Some(&r)); // (r < n ) or n == 0 - let n_is_zero = IsZeroGadget::construct(cb, sum::expr(&n.cells)); - let lt = LtWordGadget::construct(cb, &r, &n); + let n_is_zero = IsZeroWordGadget::construct(cb, &n); + let lt = LtWordGadget::construct(cb, &r.to_word(), &n.to_word()); cb.add_constraint( " (1 - (r < n) - (n==0)) ", 1.expr() - lt.expr() - n_is_zero.expr(), ); - cb.stack_pop(a.expr()); - cb.stack_pop(b.expr()); - cb.stack_pop(n.expr()); - cb.stack_push(r.expr()); + cb.stack_pop(a.to_word()); + cb.stack_pop(b.to_word()); + cb.stack_pop(n.to_word()); + cb.stack_push(r.to_word()); // State transition let step_state_transition = StepStateTransition { @@ -119,10 +121,10 @@ impl ExecutionGadget for MulModGadget { let [r, n, b, a] = [3, 2, 1, 0] .map(|idx| step.rw_indices[idx]) .map(|idx| block.rws[idx].stack_value()); - self.words[0].assign(region, offset, Some(a.to_le_bytes()))?; - self.words[1].assign(region, offset, Some(b.to_le_bytes()))?; - self.words[2].assign(region, offset, Some(n.to_le_bytes()))?; - self.words[3].assign(region, offset, Some(r.to_le_bytes()))?; + self.words[0].assign_u256(region, offset, a)?; + self.words[1].assign_u256(region, offset, b)?; + self.words[2].assign_u256(region, offset, n)?; + self.words[3].assign_u256(region, offset, r)?; // 1. quotient and reduction of a mod n let (k1, a_reduced) = if n.is_zero() { (U256::zero(), U256::zero()) @@ -144,11 +146,10 @@ impl ExecutionGadget for MulModGadget { (r, U256::try_from(prod / n).unwrap()) }; - self.k.assign(region, offset, Some(k2.to_le_bytes()))?; - self.a_reduced - .assign(region, offset, Some(a_reduced.to_le_bytes()))?; - self.d.assign(region, offset, Some(d.to_le_bytes()))?; - self.e.assign(region, offset, Some(e.to_le_bytes()))?; + self.k.assign_u256(region, offset, k2)?; + self.a_reduced.assign_u256(region, offset, a_reduced)?; + self.d.assign_u256(region, offset, d)?; + self.e.assign_u256(region, offset, e)?; self.modword.assign(region, offset, a, n, a_reduced, k1)?; self.mul512_left @@ -158,8 +159,7 @@ impl ExecutionGadget for MulModGadget { self.lt.assign(region, offset, r, n)?; - let n_sum = (0..32).fold(0, |acc, idx| acc + n.byte(idx) as u64); - self.n_is_zero.assign(region, offset, F::from(n_sum))?; + self.n_is_zero.assign(region, offset, Word::from(n))?; Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/not.rs b/zkevm-circuits/src/evm_circuit/execution/not.rs index 8d72ebbf24..32a01e6034 100644 --- a/zkevm-circuits/src/evm_circuit/execution/not.rs +++ b/zkevm-circuits/src/evm_circuit/execution/not.rs @@ -9,20 +9,23 @@ use crate::{ EVMConstraintBuilder, StepStateTransition, Transition::{Delta, Same}, }, - CachedRegion, Word, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; +use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct NotGadget { same_context: SameContextGadget, - input: Word, - output: Word, + input: Word32Cell, + output: Word32Cell, } impl ExecutionGadget for NotGadget { @@ -32,14 +35,13 @@ impl ExecutionGadget for NotGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); + let input = cb.query_word32(); + let output = cb.query_word32(); - let input = cb.query_word_rlc(); - let output = cb.query_word_rlc(); - - cb.stack_pop(input.expr()); - cb.stack_push(output.expr()); + cb.stack_pop(input.to_word()); + cb.stack_push(output.to_word()); - for (i, o) in input.cells.iter().zip(output.cells.iter()) { + for (i, o) in input.limbs.iter().zip(output.limbs.iter()) { cb.add_lookup( "input XOR output is all 1's", Lookup::Fixed { @@ -79,10 +81,8 @@ impl ExecutionGadget for NotGadget { let [input, output] = [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); - self.input - .assign(region, offset, Some(input.to_le_bytes()))?; - self.output - .assign(region, offset, Some(output.to_le_bytes()))?; + self.input.assign_u256(region, offset, input)?; + self.output.assign_u256(region, offset, output)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/origin.rs b/zkevm-circuits/src/evm_circuit/execution/origin.rs index f47f186463..a3c2d1bad1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/origin.rs +++ b/zkevm-circuits/src/evm_circuit/execution/origin.rs @@ -1,26 +1,28 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState, util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, CachedRegion, Cell, RandomLinearCombination, + from_bytes, AccountAddress, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, - util::Expr, + util::{ + word::{Word, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct OriginGadget { tx_id: Cell, - origin: RandomLinearCombination, + origin: AccountAddress, same_context: SameContextGadget, } @@ -30,20 +32,23 @@ impl ExecutionGadget for OriginGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::ORIGIN; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let origin = cb.query_word_rlc::(); + let origin = cb.query_account_address(); + // let address_rlc = cb.word_rlc(origin.limbs.clone().map(|l| l.expr())); // Lookup in call_ctx the TxId let tx_id = cb.call_context(None, CallContextFieldTag::TxId); + let address_value = from_bytes::expr(&origin.limbs); // Lookup rw_table -> call_context with tx origin address cb.tx_context_lookup( tx_id.expr(), TxContextFieldTag::CallerAddress, None, // None because unrelated to calldata - from_bytes::expr(&origin.cells), + //origin.to_word(), + Word::from_lo_unchecked(address_value), ); // Push the value to the stack - cb.stack_push(origin.expr()); + cb.stack_push(origin.to_word()); // State transition let opcode = cb.query_cell(); @@ -78,16 +83,8 @@ impl ExecutionGadget for OriginGadget { self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; - // Assign Origin addr RLC. - self.origin.assign( - region, - offset, - Some( - origin.to_le_bytes()[..N_BYTES_ACCOUNT_ADDRESS] - .try_into() - .unwrap(), - ), - )?; + // Assign Origin addr word. + self.origin.assign_u256(region, offset, origin)?; // Assign SameContextGadget witnesses. self.same_context.assign_exec_step(region, offset, step)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/pc.rs b/zkevm-circuits/src/evm_circuit/execution/pc.rs index 9180cb5452..2e8a172f7c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pc.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pc.rs @@ -1,7 +1,6 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_PROGRAM_COUNTER, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -9,11 +8,11 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, U64Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{word::WordExpr, Expr}, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -22,7 +21,7 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct PcGadget { same_context: SameContextGadget, - value: RandomLinearCombination, + value: U64Cell, } impl ExecutionGadget for PcGadget { @@ -31,17 +30,17 @@ impl ExecutionGadget for PcGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::PC; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let value = cb.query_word_rlc(); + let value = cb.query_u64(); // program_counter is limited to 64 bits so we only consider 8 bytes cb.require_equal( "Constrain program_counter equal to stack value", - from_bytes::expr(&value.cells), + value.expr(), cb.curr.state.program_counter.expr(), ); // Push the value on the stack - cb.stack_push(value.expr()); + cb.stack_push(value.to_word()); // State transition let step_state_transition = StepStateTransition { diff --git a/zkevm-circuits/src/evm_circuit/execution/pop.rs b/zkevm-circuits/src/evm_circuit/execution/pop.rs index 77c4ad3c3f..83750759b0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pop.rs @@ -5,11 +5,14 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - CachedRegion, Cell, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -18,7 +21,7 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct PopGadget { same_context: SameContextGadget, - phase2_value: Cell, + value: WordCell, } impl ExecutionGadget for PopGadget { @@ -27,10 +30,10 @@ impl ExecutionGadget for PopGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::POP; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let phase2_value = cb.query_cell_phase2(); + let value = cb.query_word_unchecked(); // Pop the value from the stack - cb.stack_pop(phase2_value.expr()); + cb.stack_pop(value.to_word()); // State transition let step_state_transition = StepStateTransition { @@ -45,7 +48,7 @@ impl ExecutionGadget for PopGadget { Self { same_context, - phase2_value, + value, } } @@ -61,8 +64,7 @@ impl ExecutionGadget for PopGadget { self.same_context.assign_exec_step(region, offset, step)?; let value = block.rws[step.rw_indices[0]].stack_value(); - self.phase2_value - .assign(region, offset, region.word_rlc(value))?; + self.value.assign_u256(region, offset, value)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs index b4fd0d6370..9a17ba98fd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs @@ -1,17 +1,18 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_MEMORY_ADDRESS, + param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_WORD}, step::ExecutionState, util::{ common_gadget::RestoreContextGadget, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{AddWordsGadget, IsEqualGadget, IsZeroGadget, LtGadget, ModGadget}, padding_gadget::PaddingGadget, - rlc, CachedRegion, Cell, Word, + rlc, CachedRegion, Cell, }, }, table::CallContextFieldTag, + util::word::Word32Cell, witness::{Block, Call, ExecStep, Transaction}, }; use bus_mapping::precompile::{PrecompileAuxData, PrecompileCalls}; @@ -60,18 +61,18 @@ pub struct EcMulGadget { p_y_is_zero: IsZeroGadget, s_is_zero: IsZeroGadget, s_is_fr_mod_minus_1: IsEqualGadget, - point_p_y_raw: Word, - point_r_y_raw: Word, - fq_modulus: Word, + point_p_y_raw: Word32Cell, + point_r_y_raw: Word32Cell, + fq_modulus: Word32Cell, p_y_plus_r_y: AddWordsGadget, // Two Words (s_raw, scalar_s) that satisfies // k * Fr::MODULUS + scalar_s = s_raw // Used for proving correct modulo by Fr - scalar_s_raw: Word, // raw - scalar_s: Word, // mod by Fr::MODULUS - fr_modulus: Word, // Fr::MODULUS - modword: ModGadget, + scalar_s_raw: Word32Cell, // raw + scalar_s: Word32Cell, // mod by Fr::MODULUS + fr_modulus: Word32Cell, // Fr::MODULUS + modword: ModGadget, is_success: Cell, callee_address: Cell, @@ -108,22 +109,20 @@ impl ExecutionGadget for EcMulGadget { cb.query_cell_phase2(), ); - let (scalar_s_raw, scalar_s, fr_modulus) = ( - cb.query_keccak_rlc(), - cb.query_keccak_rlc(), - cb.query_keccak_rlc(), - ); + let (scalar_s_raw, scalar_s, fr_modulus) = + (cb.query_word32(), cb.query_word32(), cb.query_word32()); cb.require_equal( "Scalar s (raw 32-bytes) equality", scalar_s_raw_rlc.expr(), - scalar_s_raw.expr(), + cb.keccak_rlc::(scalar_s_raw.clone().limbs.map(|cell| cell.expr())), ); // we know that `scalar_s` fits in the scalar field. So we don't compute an RLC // of that value. Instead we use the native value. let scalar_s_native = rlc::expr( &scalar_s - .cells + .clone() + .limbs .iter() .map(Expr::expr) .collect::>>(), @@ -141,7 +140,10 @@ impl ExecutionGadget for EcMulGadget { }); let p_is_zero = and::expr([p_x_is_zero.expr(), p_y_is_zero.expr()]); let s_is_zero = cb.annotation("ecMul(s == 0)", |cb| { - IsZeroGadget::construct(cb, scalar_s.expr()) + IsZeroGadget::construct( + cb, + cb.keccak_rlc::(scalar_s.clone().limbs.map(|cell| cell.expr())), + ) }); let s_is_fr_mod_minus_1 = cb.annotation("ecMul(s == Fr::MODULUS - 1)", |cb| { IsEqualGadget::construct(cb, scalar_s_native.expr(), { @@ -152,31 +154,28 @@ impl ExecutionGadget for EcMulGadget { Expression::Constant(fr_mod_minus_1) }) }); - let (point_p_y_raw, point_r_y_raw, fq_modulus) = ( - cb.query_keccak_rlc(), - cb.query_keccak_rlc(), - cb.query_keccak_rlc(), - ); + let (point_p_y_raw, point_r_y_raw, fq_modulus) = + (cb.query_word32(), cb.query_word32(), cb.query_word32()); cb.require_equal( "ecMul(P_y): equality", - point_p_y_raw.expr(), + cb.keccak_rlc::(point_p_y_raw.clone().limbs.map(|cell| cell.expr())), point_p_y_rlc.expr(), ); cb.require_equal( "ecMul(R_y): equality", - point_r_y_raw.expr(), + cb.keccak_rlc::(point_r_y_raw.clone().limbs.map(|cell| cell.expr())), point_r_y_rlc.expr(), ); let (fq_modulus_lo, fq_modulus_hi) = split_u256(&FQ_MODULUS); cb.require_equal( "fq_modulus(lo) equality", - sum::expr(&fq_modulus.cells[0x00..0x10]), + sum::expr(&fq_modulus.limbs[0x00..0x10]), sum::expr(fq_modulus_lo.to_le_bytes()), ); cb.require_equal( "fq_modulus(hi) equality", - sum::expr(&fq_modulus.cells[0x10..0x20]), + sum::expr(&fq_modulus.limbs[0x10..0x20]), sum::expr(fq_modulus_hi.to_le_bytes()), ); @@ -398,10 +397,9 @@ impl ExecutionGadget for EcMulGadget { (&self.scalar_s_raw, aux_data.s_raw), (&self.fr_modulus, *FR_MODULUS), ] { - col.assign(region, offset, Some(word_value.to_le_bytes()))?; + col.assign_u256(region, offset, word_value)?; } - self.scalar_s - .assign(region, offset, Some(aux_data.s.to_le_bytes()))?; + self.scalar_s.assign_u256(region, offset, aux_data.s)?; self.s_is_zero.assign_value( region, offset, @@ -420,17 +418,16 @@ impl ExecutionGadget for EcMulGadget { .expect("Fr::MODULUS - 1 fits in scalar field"), )?; self.point_p_y_raw - .assign(region, offset, Some(aux_data.p_y.to_le_bytes()))?; + .assign_u256(region, offset, aux_data.p_y)?; self.point_r_y_raw - .assign(region, offset, Some(aux_data.r_y.to_le_bytes()))?; + .assign_u256(region, offset, aux_data.r_y)?; self.p_y_plus_r_y.assign( region, offset, [aux_data.p_y, aux_data.r_y], aux_data.p_y.add(&aux_data.r_y), )?; - self.fq_modulus - .assign(region, offset, Some(FQ_MODULUS.to_le_bytes()))?; + self.fq_modulus.assign_u256(region, offset, *FQ_MODULUS)?; let (k, _) = aux_data.s_raw.div_mod(*FR_MODULUS); self.modword diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index 9ce0b116a8..6864d88b10 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -1,12 +1,3 @@ -use bus_mapping::precompile::{PrecompileAuxData, PrecompileCalls}; -use eth_types::{evm_types::GasCost, word, Field, ToLittleEndian, ToScalar, U256}; -use gadgets::util::{and, not, or, select, sum, Expr}; -use halo2_proofs::{ - circuit::Value, - plonk::{Error, Expression}, -}; -use std::sync::LazyLock; - use crate::{ evm_circuit::{ execution::ExecutionGadget, @@ -18,12 +9,21 @@ use crate::{ from_bytes, math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget, LtWordGadget, ModGadget}, padding_gadget::PaddingGadget, - rlc, CachedRegion, Cell, RandomLinearCombination, Word, + rlc, AccountAddress, CachedRegion, Cell, }, }, table::CallContextFieldTag, + util::word::{Word32Cell, WordExpr}, witness::{Block, Call, ExecStep, Transaction}, }; +use bus_mapping::precompile::{PrecompileAuxData, PrecompileCalls}; +use eth_types::{evm_types::GasCost, word, Field, ToLittleEndian, ToScalar, U256}; +use gadgets::util::{and, not, or, select, sum, Expr}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; +use std::sync::LazyLock; static FQ_MODULUS: LazyLock = LazyLock::new(|| word!("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")); @@ -42,19 +42,20 @@ pub struct EcrecoverGadget { sig_v_keccak_rlc: Cell, sig_r_keccak_rlc: Cell, sig_s_keccak_rlc: Cell, - recovered_addr_keccak_rlc: RandomLinearCombination, + //recovered_addr_keccak_rlc: Word32Cell, + recovered_addr_keccak: AccountAddress, - msg_hash_raw: Word, - msg_hash: Word, - fq_modulus: Word, - msg_hash_mod: ModGadget, + msg_hash_raw: Word32Cell, + msg_hash: Word32Cell, + fq_modulus: Word32Cell, + msg_hash_mod: ModGadget, - sig_r: Word, + sig_r: Word32Cell, sig_r_canonical: LtWordGadget, - sig_s: Word, + sig_s: Word32Cell, sig_s_canonical: LtWordGadget, - sig_v: Word, + sig_v: Word32Cell, sig_v_one_byte: IsZeroGadget, sig_v_eq27: IsEqualGadget, sig_v_eq28: IsEqualGadget, @@ -86,33 +87,34 @@ impl ExecutionGadget for EcrecoverGadget { sig_v_keccak_rlc, sig_r_keccak_rlc, sig_s_keccak_rlc, - recovered_addr_keccak_rlc, + recovered_addr_keccak, ) = ( cb.query_bool(), cb.query_cell_phase2(), cb.query_cell_phase2(), cb.query_cell_phase2(), cb.query_cell_phase2(), - cb.query_keccak_rlc(), + //cb.query_word32(), + cb.query_account_address(), ); - let msg_hash_raw = cb.query_word_rlc(); - let msg_hash = cb.query_word_rlc(); - let fq_modulus = cb.query_word_rlc(); + let msg_hash_raw = cb.query_word32(); + let msg_hash = cb.query_word32(); + let fq_modulus = cb.query_word32(); let msg_hash_mod = ModGadget::construct(cb, [&msg_hash_raw, &fq_modulus, &msg_hash]); - let sig_r = cb.query_word_rlc(); - let sig_r_canonical = LtWordGadget::construct(cb, &sig_r, &fq_modulus); - let sig_s = cb.query_word_rlc(); - let sig_s_canonical = LtWordGadget::construct(cb, &sig_s, &fq_modulus); + let sig_r = cb.query_word32(); + let sig_r_canonical = LtWordGadget::construct(cb, &sig_r.to_word(), &fq_modulus.to_word()); + let sig_s = cb.query_word32(); + let sig_s_canonical = LtWordGadget::construct(cb, &sig_s.to_word(), &fq_modulus.to_word()); let r_s_canonical = and::expr([sig_r_canonical.expr(), sig_s_canonical.expr()]); // sig_v is valid if sig_v == 27 || sig_v == 28 - let sig_v = cb.query_word_rlc(); - let sig_v_rest_bytes = sum::expr(&sig_v.cells[1..]); + let sig_v = cb.query_word32(); + let sig_v_rest_bytes = sum::expr(&sig_v.limbs[1..]); let sig_v_one_byte = IsZeroGadget::construct(cb, sig_v_rest_bytes); - let sig_v_eq27 = IsEqualGadget::construct(cb, sig_v.cells[0].expr(), 27.expr()); - let sig_v_eq28 = IsEqualGadget::construct(cb, sig_v.cells[0].expr(), 28.expr()); + let sig_v_eq27 = IsEqualGadget::construct(cb, sig_v.limbs[0].expr(), 27.expr()); + let sig_v_eq28 = IsEqualGadget::construct(cb, sig_v.limbs[0].expr(), 28.expr()); let sig_v_valid = and::expr([ or::expr([sig_v_eq27.expr(), sig_v_eq28.expr()]), sig_v_one_byte.expr(), @@ -123,7 +125,7 @@ impl ExecutionGadget for EcrecoverGadget { msg_hash_keccak_rlc.expr(), cb.keccak_rlc::( msg_hash_raw - .cells + .limbs .iter() .map(Expr::expr) .collect::>>() @@ -136,7 +138,7 @@ impl ExecutionGadget for EcrecoverGadget { sig_r_keccak_rlc.expr(), cb.keccak_rlc::( sig_r - .cells + .limbs .iter() .map(Expr::expr) .collect::>>() @@ -149,7 +151,7 @@ impl ExecutionGadget for EcrecoverGadget { sig_s_keccak_rlc.expr(), cb.keccak_rlc::( sig_s - .cells + .limbs .iter() .map(Expr::expr) .collect::>>() @@ -162,7 +164,7 @@ impl ExecutionGadget for EcrecoverGadget { sig_v_keccak_rlc.expr(), cb.keccak_rlc::( sig_v - .cells + .limbs .iter() .map(Expr::expr) .collect::>>() @@ -170,9 +172,10 @@ impl ExecutionGadget for EcrecoverGadget { .expect("sig_v is 32 bytes"), ), ); + cb.require_equal( "Secp256k1::Fq modulus assigned correctly", - fq_modulus.expr(), + cb.word_rlc::(fq_modulus.clone().limbs.map(|cell| cell.expr())), cb.word_rlc::(FQ_MODULUS.to_le_bytes().map(|b| b.expr())), ); @@ -201,13 +204,13 @@ impl ExecutionGadget for EcrecoverGadget { and::expr([r_s_canonical.expr(), sig_v_valid.expr()]), |cb| { cb.sig_table_lookup( - msg_hash.expr(), - sig_v.cells[0].expr() - 27.expr(), - sig_r.expr(), - sig_s.expr(), + cb.word_rlc(msg_hash.clone().limbs.map(|cell| cell.expr())), + sig_v.limbs[0].expr() - 27.expr(), + cb.word_rlc(sig_r.clone().limbs.map(|cell| cell.expr())), + cb.word_rlc(sig_s.clone().limbs.map(|cell| cell.expr())), select::expr( recovered.expr(), - from_bytes::expr(&recovered_addr_keccak_rlc.cells), + from_bytes::expr(&recovered_addr_keccak.limbs[..N_BYTES_ACCOUNT_ADDRESS]), 0.expr(), ), recovered.expr(), @@ -224,9 +227,9 @@ impl ExecutionGadget for EcrecoverGadget { cb.require_zero("recovered == false if sig_v != 27 or 28", recovered.expr()); }); cb.condition(not::expr(recovered.expr()), |cb| { - cb.require_zero( + cb.require_zero_word( "address == 0 if address could not be recovered", - recovered_addr_keccak_rlc.expr(), + recovered_addr_keccak.to_word(), ); }); @@ -273,7 +276,9 @@ impl ExecutionGadget for EcrecoverGadget { cb.require_equal( "output bytes (RLC) = recovered address", output_bytes_rlc.expr(), - recovered_addr_keccak_rlc.expr(), + cb.keccak_rlc::( + recovered_addr_keccak.clone().limbs.map(|cell| cell.expr()), + ), ); // If the address was not recovered, RLC(address) == RLC(output) == 0. cb.condition(not::expr(recovered.expr()), |cb| { @@ -301,7 +306,7 @@ impl ExecutionGadget for EcrecoverGadget { sig_v_keccak_rlc, sig_r_keccak_rlc, sig_s_keccak_rlc, - recovered_addr_keccak_rlc, + recovered_addr_keccak, msg_hash_raw, msg_hash, @@ -404,13 +409,11 @@ impl ExecutionGadget for EcrecoverGadget { (&self.sig_s, aux_data.sig_s), (&self.sig_v, aux_data.sig_v), ] { - word_rlc.assign(region, offset, Some(value.to_le_bytes()))?; + word_rlc.assign_u256(region, offset, value)?; } let (quotient, remainder) = aux_data.msg_hash.div_mod(*FQ_MODULUS); - self.msg_hash - .assign(region, offset, Some(remainder.to_le_bytes()))?; - self.fq_modulus - .assign(region, offset, Some(FQ_MODULUS.to_le_bytes()))?; + self.msg_hash.assign_u256(region, offset, remainder)?; + self.fq_modulus.assign_u256(region, offset, *FQ_MODULUS)?; self.msg_hash_mod.assign( region, offset, @@ -448,15 +451,10 @@ impl ExecutionGadget for EcrecoverGadget { F::from(aux_data.sig_v.to_le_bytes()[0] as u64), F::from(28), )?; - self.recovered_addr_keccak_rlc.assign( - region, - offset, - Some({ - let mut recovered_addr = aux_data.recovered_addr.to_fixed_bytes(); - recovered_addr.reverse(); - recovered_addr - }), - )?; + let mut recovered_addr = aux_data.recovered_addr.to_fixed_bytes(); + recovered_addr.reverse(); + self.recovered_addr_keccak + .assign_h160(region, offset, aux_data.recovered_addr)?; self.pad_right .assign(region, offset, call.call_data_length.into(), 128.into())?; self.padding.assign( diff --git a/zkevm-circuits/src/evm_circuit/execution/push.rs b/zkevm-circuits/src/evm_circuit/execution/push.rs index 477f829198..e81192fc16 100644 --- a/zkevm-circuits/src/evm_circuit/execution/push.rs +++ b/zkevm-circuits/src/evm_circuit/execution/push.rs @@ -6,11 +6,14 @@ use crate::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, math_gadget::IsZeroGadget, - rlc, select, CachedRegion, Cell, + rlc, select, CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -19,7 +22,7 @@ use halo2_proofs::{circuit::Value, plonk::Error}; pub(crate) struct PushGadget { same_context: SameContextGadget, is_push0: IsZeroGadget, - value: Cell, + value: Word32Cell, } impl ExecutionGadget for PushGadget { @@ -31,9 +34,9 @@ impl ExecutionGadget for PushGadget { let opcode = cb.query_cell(); let is_push0 = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::PUSH0.expr()); - - let value = cb.query_cell_phase2(); - cb.stack_push(value.expr()); + // value use WordCell better ? + let value = cb.query_word32(); + cb.stack_push(value.to_word()); // State transition let step_state_transition = StepStateTransition { @@ -47,8 +50,10 @@ impl ExecutionGadget for PushGadget { )), ..Default::default() }; + + let push_rlc = cb.word_rlc(value.limbs.clone().map(|ref l| l.expr())); let same_context = - SameContextGadget::construct2(cb, opcode, step_state_transition, value.expr()); + SameContextGadget::construct2(cb, opcode, step_state_transition, push_rlc); Self { same_context, @@ -75,8 +80,8 @@ impl ExecutionGadget for PushGadget { F::from(opcode.as_u64() - OpcodeId::PUSH0.as_u64()), )?; - let value_rlc = if opcode.is_push_with_data() { - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.rws[step.rw_indices[0]].stack_value(); + let _value_rlc = if opcode.is_push_with_data() { region .challenges() .evm_word() @@ -84,7 +89,7 @@ impl ExecutionGadget for PushGadget { } else { Value::known(F::zero()) }; - self.value.assign(region, offset, value_rlc)?; + self.value.assign_u256(region, offset, value)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs index 4c241e74f2..e9018b0c61 100644 --- a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs +++ b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs @@ -18,7 +18,10 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, - util::Expr, + util::{ + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::{circuit_input_builder::CopyDataType, state_db::CodeDB}; use eth_types::{ @@ -48,14 +51,14 @@ pub(crate) struct ReturnRevertGadget { return_data_length: Cell, memory_expansion: MemoryExpansionGadget, - code_hash: Cell, - prev_code_hash: Cell, - keccak_code_hash: Cell, - prev_keccak_code_hash: Cell, + code_hash: Word32Cell, + prev_code_hash: Word32Cell, + keccak_code_hash: WordCell, + prev_keccak_code_hash: WordCell, code_size: Cell, caller_id: Cell, - address: Cell, + address: WordCell, reversion_info: ReversionInfo, } @@ -77,10 +80,10 @@ impl ExecutionGadget for ReturnRevertGadget { vec![OpcodeId::RETURN.expr(), OpcodeId::REVERT.expr()], ); - let offset = cb.query_cell_phase2(); - let length = cb.query_word_rlc(); - cb.stack_pop(offset.expr()); - cb.stack_pop(length.expr()); + let offset = cb.query_word_unchecked(); + let length = cb.query_memory_address(); + cb.stack_pop(offset.to_word()); + cb.stack_pop(length.to_word()); let range = MemoryAddressGadget::construct(cb, offset, length); let is_success = cb.call_context(None, CallContextFieldTag::IsSuccess); @@ -97,7 +100,7 @@ impl ExecutionGadget for ReturnRevertGadget { let copy_rw_increase = cb.query_cell(); let copy_rw_increase_is_zero = IsZeroGadget::construct(cb, copy_rw_increase.expr()); - let memory_expansion = MemoryExpansionGadget::construct(cb, [range.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [range.address()]); // Case A in the specs. // not work for memory word rw counter increase now. @@ -132,61 +135,58 @@ impl ExecutionGadget for ReturnRevertGadget { // copy table ensures the bytes are copied correctly. And the copy circuit ensures // those bytes are in fact assigned in the bytecode circuit layout. The bytecode // circuit does the lookup to poseidon table. - let code_hash = cb.query_cell_phase2(); + let code_hash = cb.query_word32(); let deployed_bytecode_rlc = cb.query_cell_phase2(); cb.copy_table_lookup( - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), - code_hash.expr(), + code_hash.to_word(), CopyDataType::Bytecode.expr(), range.offset(), - range.end_offset(), + range.address(), 0.expr(), range.length(), deployed_bytecode_rlc.expr(), copy_rw_increase.expr(), ); - let [caller_id, address] = [ - CallContextFieldTag::CallerId, - CallContextFieldTag::CalleeAddress, - ] - .map(|tag| cb.call_context(None, tag)); + let caller_id = cb.call_context(None, CallContextFieldTag::CallerId); + let address = cb.call_context_read_as_word(None, CallContextFieldTag::CalleeAddress); let mut reversion_info = cb.reversion_info_read(None); // TODO: prev_code_hash must be empty_code_hash instead of 0? // since Nonce 0->1 is updated when begin_tx. // So we should optimize it later. - let prev_code_hash = cb.query_cell_phase2(); + let prev_code_hash = cb.query_word32(); cb.account_read( - address.expr(), + address.to_word(), AccountFieldTag::CodeHash, - prev_code_hash.expr(), + prev_code_hash.to_word(), ); cb.account_write( - address.expr(), + address.to_word(), AccountFieldTag::CodeHash, - code_hash.expr(), - prev_code_hash.expr(), + code_hash.to_word(), + prev_code_hash.to_word(), Some(&mut reversion_info), ); // keccak hash of code. - let keccak_code_hash = cb.query_cell_phase2(); - let prev_keccak_code_hash = cb.query_cell_phase2(); + let keccak_code_hash = cb.query_word_unchecked(); + let prev_keccak_code_hash = cb.query_word_unchecked(); #[cfg(feature = "scroll")] { cb.account_read( - address.expr(), + address.to_word(), AccountFieldTag::KeccakCodeHash, - prev_keccak_code_hash.expr(), + prev_keccak_code_hash.to_word(), ); cb.account_write( - address.expr(), + address.to_word(), AccountFieldTag::KeccakCodeHash, - keccak_code_hash.expr(), - prev_keccak_code_hash.expr(), + keccak_code_hash.to_word(), + prev_keccak_code_hash.to_word(), Some(&mut reversion_info), ); } @@ -196,10 +196,10 @@ impl ExecutionGadget for ReturnRevertGadget { cb.require_equal("range == code size", range.length(), code_size.expr()); #[cfg(feature = "scroll")] cb.account_write( - address.expr(), + address.to_word(), AccountFieldTag::CodeSize, - code_size.expr(), - 0.expr(), + Word::from_lo_unchecked(code_size.expr()), + Word::zero(), Some(&mut reversion_info), ); @@ -219,11 +219,10 @@ impl ExecutionGadget for ReturnRevertGadget { // Case B in the specs. cb.condition(is_root.expr(), |cb| { cb.require_next_state(ExecutionState::EndTx); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::IsPersistent, - is_success.expr(), + Word::from_lo_unchecked(is_success.expr()), ); cb.require_step_state_transition(StepStateTransition { program_counter: To(0.expr()), @@ -283,12 +282,12 @@ impl ExecutionGadget for ReturnRevertGadget { * not::expr(copy_rw_increase_is_zero.expr()), |cb| { cb.copy_table_lookup( - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), - cb.next.state.call_id.expr(), + Word::from_lo_unchecked(cb.next.state.call_id.expr()), CopyDataType::Memory.expr(), range.offset(), - range.end_offset(), + range.address(), return_data_offset.expr(), copy_length.min(), 0.expr(), @@ -411,20 +410,17 @@ impl ExecutionGadget for ReturnRevertGadget { // keccak hash of code. let keccak_code_hash = keccak256(&deployed_bytecode); - self.keccak_code_hash.assign( + self.keccak_code_hash.assign_u256( region, offset, - region.word_rlc(U256::from_big_endian(&keccak_code_hash)), + U256::from_big_endian(&keccak_code_hash), )?; // poseidon hash of code. let mut code_hash = CodeDB::hash(&deployed_bytecode).to_fixed_bytes(); code_hash.reverse(); - self.code_hash.assign( - region, - offset, - region.code_hash(U256::from_little_endian(&code_hash)), - )?; + self.code_hash + .assign_u256(region, offset, U256::from_little_endian(&code_hash))?; // code size. self.code_size.assign( @@ -437,15 +433,16 @@ impl ExecutionGadget for ReturnRevertGadget { rws.offset_add(5); let prev_code_hash = rws.next().account_codehash_pair().1; self.prev_code_hash - .assign(region, offset, region.code_hash(prev_code_hash))?; + .assign_u256(region, offset, prev_code_hash)?; #[cfg(feature = "scroll")] { rws.next(); let prev_keccak_code_hash = rws.next().account_keccak_codehash_pair().1; - self.prev_keccak_code_hash.assign( + self.prev_keccak_code_hash.assign_u256( region, offset, - region.word_rlc(prev_keccak_code_hash), + //region.word_rlc(prev_keccak_code_hash), + prev_keccak_code_hash, )?; } } @@ -482,11 +479,8 @@ impl ExecutionGadget for ReturnRevertGadget { Value::known(call.caller_id.to_scalar().unwrap()), )?; - self.address.assign( - region, - offset, - Value::known(call.callee_address.to_scalar().unwrap()), - )?; + self.address + .assign_h160(region, offset, call.callee_address)?; self.reversion_info.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs index 7643c804d3..eb103e0f64 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, + param::{N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, step::ExecutionState, util::{ common_gadget::{CommonReturnDataCopyGadget, SameContextGadget}, @@ -14,12 +14,15 @@ use crate::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget, }, - CachedRegion, Cell, RandomLinearCombination, + CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId}; use eth_types::{evm_types::GasCost, Field, ToScalar}; @@ -33,6 +36,8 @@ pub(crate) struct ReturnDataCopyGadget { last_callee_id: Cell, /// Holds the memory address for return data from where we read. return_data_offset: Cell, + /// dest memory offset + dest_offset: WordCell, /// Holds the size of the return data. return_data_size: Cell, /// The data is copied to memory. To verify this @@ -59,54 +64,52 @@ impl ExecutionGadget for ReturnDataCopyGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let dest_offset = cb.query_cell_phase2(); - let return_data_size: Cell = cb.query_cell(); + let dest_offset = cb.query_word_unchecked(); + let size = cb.query_memory_address(); + let return_data_size = cb.query_cell(); - let size: RandomLinearCombination = cb.query_word_rlc(); // enusre no other out of bound errors occur, otherwise go to `ErrorReturnDataOutOfBound` // state let check_overflow_gadget = CommonReturnDataCopyGadget::construct(cb, return_data_size.expr(), false.expr()); // in normal case, size = CommonReturnDataCopyGadget::size - cb.require_equal( + cb.require_equal_word( "size = CommonReturnDataCopyGadget::size", - size.expr(), - check_overflow_gadget.size().expr(), + size.to_word(), + check_overflow_gadget.size().to_word(), ); // 1. Pop dest_offset, offset, length from stack - cb.stack_pop(dest_offset.expr()); - cb.stack_pop(check_overflow_gadget.data_offset().expr()); - cb.stack_pop(size.expr()); + cb.stack_pop(dest_offset.to_word()); + cb.stack_pop(check_overflow_gadget.data_offset().to_word()); + cb.stack_pop(check_overflow_gadget.size().to_word()); // 2. Add lookup constraint in the call context for the returndatacopy field. let last_callee_id = cb.query_cell(); let return_data_offset = cb.query_cell(); - cb.call_context_lookup( - false.expr(), + + cb.call_context_lookup_read( None, CallContextFieldTag::LastCalleeId, - last_callee_id.expr(), + Word::from_lo_unchecked(last_callee_id.expr()), ); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::LastCalleeReturnDataOffset, - return_data_offset.expr(), + Word::from_lo_unchecked(return_data_offset.expr()), ); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::LastCalleeReturnDataLength, - return_data_size.expr(), + Word::from_lo_unchecked(return_data_size.expr()), ); // 4. memory copy // Construct memory address in the destination (memory) to which we copy memory. - let dst_memory_addr = MemoryAddressGadget::construct(cb, dest_offset, size); + let dst_memory_addr = MemoryAddressGadget::construct(cb, dest_offset.clone(), size); // Calculate the next memory size and the gas cost for this memory // access. This also accounts for the dynamic gas required to copy bytes to // memory. - let memory_expansion = MemoryExpansionGadget::construct(cb, [dst_memory_addr.end_offset()]); + let memory_expansion = MemoryExpansionGadget::construct(cb, [dst_memory_addr.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, dst_memory_addr.length(), @@ -116,12 +119,12 @@ impl ExecutionGadget for ReturnDataCopyGadget { let copy_rwc_inc = cb.query_cell(); cb.condition(dst_memory_addr.has_length(), |cb| { cb.copy_table_lookup( - last_callee_id.expr(), + Word::from_lo_unchecked(last_callee_id.expr()), CopyDataType::Memory.expr(), - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), return_data_offset.expr() - + from_bytes::expr(&check_overflow_gadget.data_offset().cells[..N_BYTES_U64]), + + from_bytes::expr(&check_overflow_gadget.data_offset().limbs[..N_BYTES_U64]), return_data_offset.expr() + return_data_size.expr(), dst_memory_addr.offset(), dst_memory_addr.length(), @@ -154,6 +157,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { same_context, last_callee_id, return_data_offset, + dest_offset, return_data_size, dst_memory_addr, check_overflow_gadget, @@ -177,6 +181,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { let [dest_offset, data_offset, size] = [0, 1, 2].map(|i| block.rws[step.rw_indices[i as usize]].stack_value()); + self.dest_offset.assign_u256(region, offset, dest_offset)?; let [last_callee_id, return_data_offset, return_data_size] = [ (3, CallContextFieldTag::LastCalleeId), (4, CallContextFieldTag::LastCalleeReturnDataOffset), diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs index 51aac27a97..a583396d46 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs @@ -1,26 +1,29 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_U64, + //param::N_BYTES_U64, step::ExecutionState, util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - from_bytes, CachedRegion, RandomLinearCombination, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian}; +use eth_types::Field; use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct ReturnDataSizeGadget { same_context: SameContextGadget, - return_data_size: RandomLinearCombination, + return_data_size: WordCell, } impl ExecutionGadget for ReturnDataSizeGadget { @@ -32,16 +35,15 @@ impl ExecutionGadget for ReturnDataSizeGadget { let opcode = cb.query_cell(); // Add lookup constraint in the call context for the returndatasize field. - let return_data_size = cb.query_word_rlc(); - cb.call_context_lookup( - false.expr(), + let return_data_size = cb.query_word_unchecked(); + cb.call_context_lookup_read( None, CallContextFieldTag::LastCalleeReturnDataLength, - from_bytes::expr(&return_data_size.cells), + return_data_size.to_word(), ); // The returndatasize should be pushed to the top of the stack. - cb.stack_push(return_data_size.expr()); + cb.stack_push(return_data_size.to_word()); let step_state_transition = StepStateTransition { rw_counter: Delta(2.expr()), @@ -70,15 +72,8 @@ impl ExecutionGadget for ReturnDataSizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; let return_data_size = block.rws[step.rw_indices[1]].stack_value(); - self.return_data_size.assign( - region, - offset, - Some( - return_data_size.to_le_bytes()[..N_BYTES_U64] - .try_into() - .expect("could not encode return_data_size as byte array in little endian"), - ), - )?; + self.return_data_size + .assign_u64(region, offset, return_data_size.as_u64())?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/sar.rs b/zkevm-circuits/src/evm_circuit/execution/sar.rs index 47879fda67..a2f93e4cd9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sar.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sar.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::N_BYTES_U64, + //param::N_BYTES_U64, step::ExecutionState, table::{FixedTableTag, Lookup}, util::{ @@ -10,18 +10,23 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - from_bytes, math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, - select, sum, CachedRegion, Cell, Word, + select, sum, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32Cell, Word4, WordExpr}, + Expr, + }, }; use array_init::array_init; use bus_mapping::evm::OpcodeId; use eth_types::{Field, ToLittleEndian}; -use halo2_proofs::{circuit::Value, plonk::Error}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; /// SarGadget verifies SAR opcode. /// Verify signed word shift right as `signed(a) >> shift == signed(b)`; @@ -29,9 +34,9 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct SarGadget { same_context: SameContextGadget, - shift: Word, - a: Word, - b: Word, + shift: Word32Cell, + a: Word32Cell, + b: Word32Cell, // Each of the four `a64s` limbs is split into two parts (`a64s_lo` and `a64s_hi`) at position // `shf_mod64`, `a64s_lo` is the lower `shf_mod64` bits. a64s_lo: [Cell; 4], @@ -77,13 +82,16 @@ impl ExecutionGadget for SarGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let shift = cb.query_word_rlc(); - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); + let shift = cb.query_word32(); + let a = cb.query_word32(); + let b = cb.query_word32(); + + cb.stack_pop(shift.to_word()); + cb.stack_pop(a.to_word()); + cb.stack_push(b.to_word()); - cb.stack_pop(shift.expr()); - cb.stack_pop(a.expr()); - cb.stack_push(b.expr()); + let a64s: Word4> = a.to_word_n(); + let b64s: Word4> = b.to_word_n(); let a64s_lo = array_init(|_| cb.query_cell()); let a64s_hi = array_init(|_| cb.query_cell()); @@ -92,13 +100,13 @@ impl ExecutionGadget for SarGadget { let p_lo = cb.query_cell(); let p_hi = cb.query_cell(); let p_top = cb.query_cell(); - let is_neg = LtGadget::construct(cb, 127.expr(), a.cells[31].expr()); - let shf_lt256 = IsZeroGadget::construct(cb, sum::expr(&shift.cells[1..32])); + let is_neg = LtGadget::construct(cb, 127.expr(), a.limbs[31].expr()); + let shf_lt256 = IsZeroGadget::construct(cb, sum::expr(&shift.limbs[1..32])); for idx in 0..4 { cb.require_equal( "a64s[idx] == a64s_lo[idx] + a64s_hi[idx] * p_lo", - from_bytes::expr(&a.cells[N_BYTES_U64 * idx..N_BYTES_U64 * (idx + 1)]), + a64s.limbs[idx].clone(), a64s_lo[idx].expr() + a64s_hi[idx].expr() * p_lo.expr(), ); } @@ -129,7 +137,7 @@ impl ExecutionGadget for SarGadget { cb.require_equal( "Constrain merged b64s[0] value", - from_bytes::expr(&b.cells[0..N_BYTES_U64]), + b64s.limbs[0].expr(), (a64s_hi[0].expr() + a64s_lo[1].expr() * p_hi.expr()) * shf_div64_eq0.expr() + (a64s_hi[1].expr() + a64s_lo[2].expr() * p_hi.expr()) * shf_div64_eq1.expr() + (a64s_hi[2].expr() + a64s_lo[3].expr() * p_hi.expr()) * shf_div64_eq2.expr() @@ -144,7 +152,7 @@ impl ExecutionGadget for SarGadget { ); cb.require_equal( "Constrain merged b64s[1] value", - from_bytes::expr(&b.cells[N_BYTES_U64..N_BYTES_U64 * 2]), + b64s.limbs[1].expr(), (a64s_hi[1].expr() + a64s_lo[2].expr() * p_hi.expr()) * shf_div64_eq0.expr() + (a64s_hi[2].expr() + a64s_lo[3].expr() * p_hi.expr()) * shf_div64_eq1.expr() + (a64s_hi[3].expr() + p_top.expr()) * shf_div64_eq2.expr() @@ -157,7 +165,7 @@ impl ExecutionGadget for SarGadget { ); cb.require_equal( "Constrain merged b64s[2] value", - from_bytes::expr(&b.cells[N_BYTES_U64 * 2..N_BYTES_U64 * 3]), + b64s.limbs[2].expr(), (a64s_hi[2].expr() + a64s_lo[3].expr() * p_hi.expr()) * shf_div64_eq0.expr() + (a64s_hi[3].expr() + p_top.expr()) * shf_div64_eq1.expr() + is_neg.expr() @@ -166,7 +174,7 @@ impl ExecutionGadget for SarGadget { ); cb.require_equal( "Constrain merged b64s[3] value", - from_bytes::expr(&b.cells[N_BYTES_U64 * 3..]), + b64s.limbs[3].expr(), (a64s_hi[3].expr() + p_top.expr()) * shf_div64_eq0.expr() + is_neg.expr() * u64::MAX.expr() * (1.expr() - shf_div64_eq0.expr()), ); @@ -178,7 +186,7 @@ impl ExecutionGadget for SarGadget { cb.require_equal("shf_mod64 < 64", shf_mod64_lt_64.expr(), 1.expr()); cb.require_equal( "shift[0] == shf_mod64 + shf_div64 * 64", - shift.cells[0].expr(), + shift.limbs[0].expr(), shf_mod64.expr() + shf_div64.expr() * 64.expr(), ); @@ -189,7 +197,7 @@ impl ExecutionGadget for SarGadget { Lookup::Fixed { tag: FixedTableTag::SignByte.expr(), values: [ - a.cells[31].expr(), + a.limbs[31].expr(), select::expr(is_neg.expr(), 255.expr(), 0.expr()), 0.expr(), ], @@ -269,10 +277,9 @@ impl ExecutionGadget for SarGadget { let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; let [shift, a, b] = indices.map(|idx| block.rws[idx].stack_value()); - self.shift - .assign(region, offset, Some(shift.to_le_bytes()))?; - self.a.assign(region, offset, Some(a.to_le_bytes()))?; - self.b.assign(region, offset, Some(b.to_le_bytes()))?; + self.shift.assign_u256(region, offset, shift)?; + self.a.assign_u256(region, offset, a)?; + self.b.assign_u256(region, offset, b)?; let is_neg = 127 < a.to_le_bytes()[31]; let shf0 = u64::from(shift.to_le_bytes()[0]); diff --git a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs index 0c0fa3f9bd..2620340101 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs @@ -8,12 +8,17 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - math_gadget::{AbsWordGadget, IsZeroGadget, LtGadget, LtWordGadget, MulAddWordsGadget}, - select, sum, CachedRegion, + math_gadget::{ + AbsWordGadget, IsZeroWordGadget, LtGadget, LtWordGadget, MulAddWordsGadget, + }, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::{Field, ToLittleEndian, U256}; @@ -22,16 +27,16 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct SignedDivModGadget { same_context: SameContextGadget, - quotient_abs_word: AbsWordGadget, - divisor_abs_word: AbsWordGadget, - remainder_abs_word: AbsWordGadget, - dividend_abs_word: AbsWordGadget, + quotient_abs: AbsWordGadget, + divisor_abs: AbsWordGadget, + remainder_abs: AbsWordGadget, + dividend_abs: AbsWordGadget, mul_add_words: MulAddWordsGadget, remainder_abs_lt_divisor_abs: LtWordGadget, dividend_is_signed_overflow: LtGadget, - quotient_is_zero: IsZeroGadget, - divisor_is_zero: IsZeroGadget, - remainder_is_zero: IsZeroGadget, + quotient_is_zero: IsZeroWordGadget>, + divisor_is_zero: IsZeroWordGadget>, + remainder_is_zero: IsZeroWordGadget>, } impl ExecutionGadget for SignedDivModGadget { @@ -43,37 +48,45 @@ impl ExecutionGadget for SignedDivModGadget { let opcode = cb.query_cell(); let is_sdiv = (OpcodeId::SMOD.expr() - opcode.expr()) * F::from(2).invert().unwrap(); - let quotient_abs_word = AbsWordGadget::construct(cb); - let divisor_abs_word = AbsWordGadget::construct(cb); - let remainder_abs_word = AbsWordGadget::construct(cb); - let dividend_abs_word = AbsWordGadget::construct(cb); - let quotient_is_zero = IsZeroGadget::construct(cb, sum::expr("ient_abs_word.x().cells)); - let divisor_is_zero = IsZeroGadget::construct(cb, sum::expr(&divisor_abs_word.x().cells)); - let remainder_is_zero = - IsZeroGadget::construct(cb, sum::expr(&remainder_abs_word.x().cells)); + let quotient_abs = AbsWordGadget::construct(cb); + let divisor_abs = AbsWordGadget::construct(cb); + let remainder_abs = AbsWordGadget::construct(cb); + let dividend_abs = AbsWordGadget::construct(cb); + let quotient_is_zero = IsZeroWordGadget::construct(cb, quotient_abs.x()); + let divisor_is_zero = IsZeroWordGadget::construct(cb, divisor_abs.x()); + let remainder_is_zero = IsZeroWordGadget::construct(cb, remainder_abs.x()); - cb.stack_pop(dividend_abs_word.x().expr()); - cb.stack_pop(divisor_abs_word.x().expr()); - cb.stack_push(select::expr( + cb.stack_pop(dividend_abs.x().to_word()); + cb.stack_pop(divisor_abs.x().to_word()); + cb.stack_push(Word::select( is_sdiv, - quotient_abs_word.x().expr() * (1.expr() - divisor_is_zero.expr()), - remainder_abs_word.x().expr() * (1.expr() - divisor_is_zero.expr()), + quotient_abs + .x() + .to_word() + .mul_selector(1.expr() - divisor_is_zero.expr()), + remainder_abs + .x() + .to_word() + .mul_selector(1.expr() - divisor_is_zero.expr()), )); // Constrain `|quotient| * |divisor| + |remainder| = |dividend|`. let mul_add_words = MulAddWordsGadget::construct( cb, [ - quotient_abs_word.x_abs(), - divisor_abs_word.x_abs(), - remainder_abs_word.x_abs(), - dividend_abs_word.x_abs(), + quotient_abs.x_abs(), + divisor_abs.x_abs(), + remainder_abs.x_abs(), + dividend_abs.x_abs(), ], ); cb.add_constraint("overflow == 0", mul_add_words.overflow()); - let remainder_abs_lt_divisor_abs = - LtWordGadget::construct(cb, remainder_abs_word.x_abs(), divisor_abs_word.x_abs()); + let remainder_abs_lt_divisor_abs = LtWordGadget::construct( + cb, + &remainder_abs.x_abs().to_word(), + &divisor_abs.x_abs().to_word(), + ); cb.add_constraint( "abs(remainder) < abs(divisor) when divisor != 0", (1.expr() - remainder_abs_lt_divisor_abs.expr()) * (1.expr() - divisor_is_zero.expr()), @@ -85,7 +98,7 @@ impl ExecutionGadget for SignedDivModGadget { * (1.expr() - remainder_is_zero.expr()), |cb| cb.add_constraint( "sign(dividend) == sign(remainder) when quotient, divisor and remainder are all non-zero", - dividend_abs_word.is_neg().expr() - remainder_abs_word.is_neg().expr(), + dividend_abs.is_neg().expr() - remainder_abs.is_neg().expr(), ) ); @@ -96,7 +109,7 @@ impl ExecutionGadget for SignedDivModGadget { // `sign(dividend) == sign(divisor) ^ sign(quotient)` cannot be applied // for this case. let dividend_is_signed_overflow = - LtGadget::construct(cb, 127.expr(), dividend_abs_word.x_abs().cells[31].expr()); + LtGadget::construct(cb, 127.expr(), dividend_abs.x_abs().limbs[31].expr()); // Constrain sign(dividend) == sign(divisor) ^ sign(quotient) when both // quotient and divisor are non-zero and dividend is not signed overflow. @@ -107,11 +120,9 @@ impl ExecutionGadget for SignedDivModGadget { |cb| { cb.add_constraint( "sign(dividend) == sign(divisor) ^ sign(quotient)", - quotient_abs_word.is_neg().expr() + divisor_abs_word.is_neg().expr() - - dividend_abs_word.is_neg().expr() - - 2.expr() - * quotient_abs_word.is_neg().expr() - * divisor_abs_word.is_neg().expr(), + quotient_abs.is_neg().expr() + divisor_abs.is_neg().expr() + - dividend_abs.is_neg().expr() + - 2.expr() * quotient_abs.is_neg().expr() * divisor_abs.is_neg().expr(), ) }, ); @@ -127,10 +138,10 @@ impl ExecutionGadget for SignedDivModGadget { Self { same_context, - quotient_abs_word, - divisor_abs_word, - remainder_abs_word, - dividend_abs_word, + quotient_abs, + divisor_abs, + remainder_abs, + dividend_abs, mul_add_words, remainder_abs_lt_divisor_abs, dividend_is_signed_overflow, @@ -186,13 +197,13 @@ impl ExecutionGadget for SignedDivModGadget { let divisor_abs = get_abs(divisor); let remainder_abs = get_abs(remainder); let dividend_abs = get_abs(dividend); - self.quotient_abs_word + self.quotient_abs .assign(region, offset, quotient, quotient_abs)?; - self.divisor_abs_word + self.divisor_abs .assign(region, offset, divisor, divisor_abs)?; - self.remainder_abs_word + self.remainder_abs .assign(region, offset, remainder, remainder_abs)?; - self.dividend_abs_word + self.dividend_abs .assign(region, offset, dividend, dividend_abs)?; self.mul_add_words.assign( region, @@ -207,15 +218,12 @@ impl ExecutionGadget for SignedDivModGadget { 127.into(), u64::from(dividend_abs.to_le_bytes()[31]).into(), )?; - let quotient_sum = (0..32).fold(0, |acc, idx| acc + quotient.byte(idx) as u64); - let divisor_sum = (0..32).fold(0, |acc, idx| acc + divisor.byte(idx) as u64); - let remainder_sum = (0..32).fold(0, |acc, idx| acc + remainder.byte(idx) as u64); self.quotient_is_zero - .assign(region, offset, F::from(quotient_sum))?; + .assign(region, offset, Word::from(quotient))?; self.divisor_is_zero - .assign(region, offset, F::from(divisor_sum))?; + .assign(region, offset, Word::from(divisor))?; self.remainder_is_zero - .assign(region, offset, F::from(remainder_sum))?; + .assign(region, offset, Word::from(remainder))?; Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs index 584fcee949..bc26029089 100644 --- a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs @@ -5,22 +5,25 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - CachedRegion, Cell, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToScalar}; -use halo2_proofs::{circuit::Value, plonk::Error}; +use eth_types::Field; +use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct SelfbalanceGadget { same_context: SameContextGadget, - callee_address: Cell, - phase2_self_balance: Cell, + callee_address: WordCell, + self_balance: WordCell, } impl ExecutionGadget for SelfbalanceGadget { @@ -29,16 +32,16 @@ impl ExecutionGadget for SelfbalanceGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SELFBALANCE; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let callee_address = cb.call_context(None, CallContextFieldTag::CalleeAddress); + let callee_address = cb.call_context_read_as_word(None, CallContextFieldTag::CalleeAddress); - let phase2_self_balance = cb.query_cell_phase2(); + let self_balance = cb.query_word_unchecked(); cb.account_read( - callee_address.expr(), + callee_address.to_word(), AccountFieldTag::Balance, - phase2_self_balance.expr(), + self_balance.to_word(), ); - cb.stack_push(phase2_self_balance.expr()); + cb.stack_push(self_balance.to_word()); let opcode = cb.query_cell(); let step_state_transition = StepStateTransition { @@ -52,8 +55,8 @@ impl ExecutionGadget for SelfbalanceGadget { Self { same_context, - phase2_self_balance, callee_address, + self_balance, } } @@ -68,19 +71,12 @@ impl ExecutionGadget for SelfbalanceGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - self.callee_address.assign( - region, - offset, - Value::known( - call.callee_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), - )?; + self.callee_address + .assign_h160(region, offset, call.callee_address)?; let self_balance = block.rws[step.rw_indices[2]].stack_value(); - self.phase2_self_balance - .assign(region, offset, region.word_rlc(self_balance))?; + self.self_balance + .assign_u256(region, offset, self_balance)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/sha3.rs b/zkevm-circuits/src/evm_circuit/execution/sha3.rs index 62a3ac155e..381b75215d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sha3.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sha3.rs @@ -1,23 +1,26 @@ use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId}; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar}; +use eth_types::{evm_types::GasCost, Field, ToScalar}; use gadgets::util::{not, Expr}; use halo2_proofs::{circuit::Value, plonk::Error}; -use crate::evm_circuit::{ - param::N_BYTES_MEMORY_WORD_SIZE, - step::ExecutionState, - util::{ - common_gadget::{get_copy_bytes, SameContextGadget}, - constraint_builder::{ - ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, +use crate::{ + evm_circuit::{ + param::N_BYTES_MEMORY_WORD_SIZE, + step::ExecutionState, + util::{ + common_gadget::{get_copy_bytes, SameContextGadget}, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition, + }, + memory_gadget::{ + CommonMemoryAddressGadget, MemoryAddressGadget, MemoryCopierGasGadget, + MemoryExpansionGadget, + }, + rlc, CachedRegion, Cell, StepRws, }, - memory_gadget::{ - CommonMemoryAddressGadget, MemoryAddressGadget, MemoryCopierGasGadget, - MemoryExpansionGadget, - }, - rlc, CachedRegion, Cell, StepRws, Word, + witness::{Block, Call, ExecStep, Transaction}, }, - witness::{Block, Call, ExecStep, Transaction}, + util::word::{Word, Word32Cell, WordExpr}, }; use super::ExecutionGadget; @@ -26,7 +29,8 @@ use super::ExecutionGadget; pub(crate) struct Sha3Gadget { same_context: SameContextGadget, memory_address: MemoryAddressGadget, - sha3_rlc: Word, + //sha3_digest: WordCell, + sha3_digest: Word32Cell, copy_rwc_inc: Cell, rlc_acc: Cell, memory_expansion: MemoryExpansionGadget, @@ -41,13 +45,13 @@ impl ExecutionGadget for Sha3Gadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let offset = cb.query_cell_phase2(); - let size = cb.query_word_rlc(); - let sha3_rlc = cb.query_word_rlc(); + let offset = cb.query_word_unchecked(); + let size = cb.query_memory_address(); + let sha3_digest = cb.query_word32(); - cb.stack_pop(offset.expr()); - cb.stack_pop(size.expr()); - cb.stack_push(sha3_rlc.expr()); + cb.stack_pop(offset.to_word()); + cb.stack_pop(size.to_word()); + cb.stack_push(sha3_digest.to_word()); let memory_address = MemoryAddressGadget::construct(cb, offset, size); @@ -56,12 +60,12 @@ impl ExecutionGadget for Sha3Gadget { cb.condition(memory_address.has_length(), |cb| { cb.copy_table_lookup( - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::Memory.expr(), - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), CopyDataType::RlcAcc.expr(), memory_address.offset(), - memory_address.end_offset(), + memory_address.address(), 0.expr(), // dst_addr for CopyDataType::RlcAcc is 0. memory_address.length(), rlc_acc.expr(), @@ -73,9 +77,16 @@ impl ExecutionGadget for Sha3Gadget { cb.require_zero("copy_rwc_inc == 0 for size = 0", copy_rwc_inc.expr()); cb.require_zero("rlc_acc == 0 for size = 0", rlc_acc.expr()); }); - cb.keccak_table_lookup(rlc_acc.expr(), memory_address.length(), sha3_rlc.expr()); - let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.end_offset()]); + let output_rlc = cb.word_rlc(sha3_digest.limbs.clone().map(|l| l.expr())); + cb.keccak_table_lookup( + rlc_acc.expr(), + memory_address.length(), + output_rlc, + sha3_digest.to_word(), + ); + + let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); let memory_copier_gas = MemoryCopierGasGadget::construct( cb, memory_address.length(), @@ -97,7 +108,7 @@ impl ExecutionGadget for Sha3Gadget { Self { same_context, memory_address, - sha3_rlc, + sha3_digest, copy_rwc_inc, rlc_acc, memory_expansion, @@ -122,8 +133,7 @@ impl ExecutionGadget for Sha3Gadget { let memory_address = self .memory_address .assign(region, offset, memory_offset, size)?; - self.sha3_rlc - .assign(region, offset, Some(sha3_output.to_le_bytes()))?; + self.sha3_digest.assign_u256(region, offset, sha3_output)?; let shift = memory_offset.low_u64() % 32; let copy_rwc_inc = step.copy_rw_counter_delta; diff --git a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs index 785cae9507..df3f921230 100644 --- a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs +++ b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs @@ -4,19 +4,23 @@ use crate::{ step::ExecutionState, table::{FixedTableTag, Lookup}, util::{ - self, common_gadget::SameContextGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - from_bytes, - math_gadget::{IsZeroGadget, LtWordGadget, MulAddWordsGadget}, - sum, CachedRegion, Cell, + //from_bytes, + math_gadget::{IsZeroGadget, IsZeroWordGadget, LtWordGadget, MulAddWordsGadget}, + sum, + CachedRegion, + Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::{Field, ToLittleEndian, U256}; @@ -29,12 +33,12 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ShlShrGadget { same_context: SameContextGadget, - quotient: util::Word, - divisor: util::Word, - remainder: util::Word, - dividend: util::Word, + quotient: Word32Cell, + divisor: Word32Cell, + remainder: Word32Cell, + dividend: Word32Cell, /// Shift word - shift: util::Word, + shift: Word32Cell, /// First byte of shift word shf0: Cell, /// Gadget that verifies quotient * divisor + remainder = dividend @@ -42,9 +46,9 @@ pub(crate) struct ShlShrGadget { /// Identify if `shift` is less than 256 or not shf_lt256: IsZeroGadget, /// Check if divisor is zero - divisor_is_zero: IsZeroGadget, + divisor_is_zero: IsZeroWordGadget>, /// Check if remainder is zero - remainder_is_zero: IsZeroGadget, + remainder_is_zero: IsZeroWordGadget>, /// Check if remainder < divisor when divisor != 0 remainder_lt_divisor: LtWordGadget, } @@ -59,38 +63,49 @@ impl ExecutionGadget for ShlShrGadget { let is_shl = OpcodeId::SHR.expr() - opcode.expr(); let is_shr = 1.expr() - is_shl.expr(); - let quotient = cb.query_word_rlc(); - let divisor = cb.query_word_rlc(); - let remainder = cb.query_word_rlc(); - let dividend = cb.query_word_rlc(); - let shift = cb.query_word_rlc(); + let quotient = cb.query_word32(); + let divisor = cb.query_word32(); + let remainder = cb.query_word32(); + let dividend = cb.query_word32(); + let shift = cb.query_word32(); let shf0 = cb.query_cell(); let mul_add_words = MulAddWordsGadget::construct(cb, ["ient, &divisor, &remainder, ÷nd]); - let shf_lt256 = IsZeroGadget::construct(cb, sum::expr(&shift.cells[1..32])); - let divisor_is_zero = IsZeroGadget::construct(cb, sum::expr(&divisor.cells)); - let remainder_is_zero = IsZeroGadget::construct(cb, sum::expr(&remainder.cells)); - let remainder_lt_divisor = LtWordGadget::construct(cb, &remainder, &divisor); - + let shf_lt256 = IsZeroGadget::construct(cb, sum::expr(&shift.limbs[1..32])); + let divisor_is_zero = IsZeroWordGadget::construct(cb, &divisor); + let remainder_is_zero = IsZeroWordGadget::construct(cb, &remainder); + let remainder_lt_divisor = + LtWordGadget::construct(cb, &remainder.to_word(), &divisor.to_word()); // Constrain stack pops and pushes as: // - for SHL, two pops are shift and quotient, and push is dividend. // - for SHR, two pops are shift and dividend, and push is quotient. - cb.stack_pop(shift.expr()); - cb.stack_pop(is_shl.expr() * quotient.expr() + is_shr.expr() * dividend.expr()); + cb.stack_pop(shift.to_word()); + cb.stack_pop( + quotient + .to_word() + .mul_selector(is_shl.expr()) + .add_unchecked(dividend.to_word().mul_selector(is_shr.expr())), + ); cb.stack_push( - (is_shl.expr() * dividend.expr() + is_shr.expr() * quotient.expr()) - * (1.expr() - divisor_is_zero.expr()), + (dividend + .to_word() + .mul_selector(is_shl.expr()) + .add_unchecked(quotient.to_word().mul_selector(is_shr.expr()))) + .mul_selector(1.expr() - divisor_is_zero.expr()), ); cb.require_zero( "shf0 == shift.cells[0]", - shf0.expr() - shift.cells[0].expr(), + shf0.expr() - shift.limbs[0].expr(), ); - cb.require_zero( + cb.require_zero_word( "shift == shift.cells[0] when divisor != 0", - (1.expr() - divisor_is_zero.expr()) * (shift.expr() - shift.cells[0].expr()), + shift + .to_word() + .sub_unchecked(Word::from_lo_unchecked(shift.limbs[0].expr())) + .mul_selector(1.expr() - divisor_is_zero.expr()), ); cb.require_zero( @@ -115,8 +130,7 @@ impl ExecutionGadget for ShlShrGadget { // Constrain divisor_lo == 2^shf0 when shf0 < 128, and // divisor_hi == 2^(128 - shf0) otherwise. - let divisor_lo = from_bytes::expr(&divisor.cells[..16]); - let divisor_hi = from_bytes::expr(&divisor.cells[16..]); + let (divisor_lo, divisor_hi) = divisor.to_word().to_lo_hi(); cb.condition(1.expr() - divisor_is_zero.expr(), |cb| { cb.add_lookup( "Pow2 lookup of shf0, divisor_lo and divisor_hi", @@ -183,27 +197,21 @@ impl ExecutionGadget for ShlShrGadget { OpcodeId::SHR => (push, pop2 - push * divisor, pop2), _ => unreachable!(), }; - self.quotient - .assign(region, offset, Some(quotient.to_le_bytes()))?; - self.divisor - .assign(region, offset, Some(divisor.to_le_bytes()))?; - self.remainder - .assign(region, offset, Some(remainder.to_le_bytes()))?; - self.dividend - .assign(region, offset, Some(dividend.to_le_bytes()))?; - self.shift - .assign(region, offset, Some(pop1.to_le_bytes()))?; + + self.quotient.assign_u256(region, offset, quotient)?; + self.divisor.assign_u256(region, offset, divisor)?; + self.remainder.assign_u256(region, offset, remainder)?; + self.dividend.assign_u256(region, offset, dividend)?; + self.shift.assign_u256(region, offset, pop1)?; self.shf0 .assign(region, offset, Value::known(F::from(shf0)))?; self.mul_add_words .assign(region, offset, [quotient, divisor, remainder, dividend])?; self.shf_lt256.assign(region, offset, F::from(shf_lt256))?; - let divisor_sum = (0..32).fold(0, |acc, idx| acc + divisor.byte(idx) as u64); self.divisor_is_zero - .assign(region, offset, F::from(divisor_sum))?; - let remainder_sum = (0..32).fold(0, |acc, idx| acc + remainder.byte(idx) as u64); + .assign(region, offset, Word::from(divisor))?; self.remainder_is_zero - .assign(region, offset, F::from(remainder_sum))?; + .assign(region, offset, Word::from(remainder))?; self.remainder_lt_divisor .assign(region, offset, remainder, divisor) } diff --git a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs index 11284033a1..8645f425c5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs @@ -7,11 +7,14 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, from_bytes, math_gadget::{ComparisonGadget, IsEqualGadget, LtGadget}, - select, CachedRegion, Cell, Word, + select, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field, ToLittleEndian}; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -22,8 +25,8 @@ use halo2_proofs::{circuit::Value, plonk::Error}; pub(crate) struct SignedComparatorGadget { same_context: SameContextGadget, - a: Word, - b: Word, + a: Word32Cell, + b: Word32Cell, sign_check_a: LtGadget, sign_check_b: LtGadget, @@ -42,8 +45,8 @@ impl ExecutionGadget for SignedComparatorGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); // The Signed Comparator gadget is used for both opcodes SLT and SGT. // Depending on whether the opcode is SLT or SGT, we @@ -56,8 +59,8 @@ impl ExecutionGadget for SignedComparatorGadget { // number is negative if the most significant cell >= 128 // (0b10000000). a and b being in the little-endian notation, the // most-significant byte is the last byte. - let sign_check_a = LtGadget::construct(cb, a.cells[31].expr(), 128.expr()); - let sign_check_b = LtGadget::construct(cb, b.cells[31].expr(), 128.expr()); + let sign_check_a = LtGadget::construct(cb, a.limbs[31].expr(), 128.expr()); + let sign_check_b = LtGadget::construct(cb, b.limbs[31].expr(), 128.expr()); // sign_check_a_lt expression implies a is positive since its MSB < 2**7 // sign_check_b_lt expression implies b is positive since its MSB < 2**7 @@ -72,16 +75,11 @@ impl ExecutionGadget for SignedComparatorGadget { // significant bytes. While only considering the absolute // values, we have: a < b == 1 iff ((a_hi < b_hi) || ((a_hi == // b_hi) && (a_lo < b_lo))) - let lt_lo = LtGadget::construct( - cb, - from_bytes::expr(&a.cells[0..16]), - from_bytes::expr(&b.cells[0..16]), - ); - let comparison_hi = ComparisonGadget::construct( - cb, - from_bytes::expr(&a.cells[16..32]), - from_bytes::expr(&b.cells[16..32]), - ); + let (a_lo, a_hi) = a.to_word().to_lo_hi(); + let (b_lo, b_hi) = b.to_word().to_lo_hi(); + + let lt_lo = LtGadget::construct(cb, a_lo, b_lo); + let comparison_hi = ComparisonGadget::construct(cb, a_hi, b_hi); let a_lt_b_lo = lt_lo.expr(); let (a_lt_b_hi, a_eq_b_hi) = comparison_hi.expr(); @@ -112,9 +110,9 @@ impl ExecutionGadget for SignedComparatorGadget { let result = a_neg_b_pos.clone() + (1.expr() - a_neg_b_pos - b_neg_a_pos) * a_lt_b.expr(); // Pop a and b from the stack, push the result on the stack. - cb.stack_pop(select::expr(is_sgt.expr(), b.expr(), a.expr())); - cb.stack_pop(select::expr(is_sgt.expr(), a.expr(), b.expr())); - cb.stack_push(result); + cb.stack_pop(Word::select(is_sgt.expr(), b.to_word(), a.to_word())); + cb.stack_pop(Word::select(is_sgt.expr(), a.to_word(), b.to_word())); + cb.stack_push(Word::from_lo_unchecked(result)); // The read-write counter changes by three since we're reading two words // from stack and writing one. The program counter shifts only by one @@ -209,8 +207,8 @@ impl ExecutionGadget for SignedComparatorGadget { Value::known(if a < b { F::one() } else { F::zero() }), )?; - self.a.assign(region, offset, Some(a_le_bytes))?; - self.b.assign(region, offset, Some(b_le_bytes))?; + self.a.assign_u256(region, offset, a)?; + self.b.assign_u256(region, offset, b)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/signextend.rs b/zkevm-circuits/src/evm_circuit/execution/signextend.rs index f42960e998..70f425297f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signextend.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signextend.rs @@ -11,11 +11,14 @@ use crate::{ Transition::Delta, }, math_gadget::{IsEqualGadget, IsZeroGadget}, - rlc, select, sum, CachedRegion, Cell, Word, + select, sum, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{Word32, Word32Cell, WordExpr}, + Expr, + }, }; use array_init::array_init; use bus_mapping::evm::OpcodeId; @@ -25,8 +28,8 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct SignextendGadget { same_context: SameContextGadget, - index: Word, - value: Word, + index: Word32Cell, + value: Word32Cell, sign_byte: Cell, is_msb_sum_zero: IsZeroGadget, is_byte_selected: [IsEqualGadget; 31], @@ -39,8 +42,8 @@ impl ExecutionGadget for SignextendGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::SIGNEXTEND; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let index = cb.query_word_rlc(); - let value = cb.query_word_rlc(); + let index = cb.query_word32(); + let value = cb.query_word32(); let sign_byte = cb.query_cell(); let selectors = array_init(|_| cb.query_bool()); @@ -49,12 +52,12 @@ impl ExecutionGadget for SignextendGadget { // need to do any changes. So just sum all the non-LSB byte // values here and then check if it's non-zero so we can use // that as an additional condition to enable the selector. - let is_msb_sum_zero = IsZeroGadget::construct(cb, sum::expr(&index.cells[1..32])); + let is_msb_sum_zero = IsZeroGadget::construct(cb, sum::expr(&index.limbs[1..32])); // Check if this byte is selected looking only at the LSB of the index // word let is_byte_selected = - array_init(|idx| IsEqualGadget::construct(cb, index.cells[0].expr(), idx.expr())); + array_init(|idx| IsEqualGadget::construct(cb, index.limbs[0].expr(), idx.expr())); // We need to find the byte we have to get the sign from so we can // extend correctly. We go byte by byte and check if `idx == @@ -72,7 +75,7 @@ impl ExecutionGadget for SignextendGadget { let is_selected = and::expr(&[is_byte_selected[idx].expr(), is_msb_sum_zero.expr()]); // Add the byte to the sum when this byte is selected - selected_byte = selected_byte + (is_selected.clone() * value.cells[idx].expr()); + selected_byte = selected_byte + (is_selected.clone() * value.limbs[idx].expr()); // Verify the selector. // Cells are used here to store intermediate results, otherwise @@ -111,26 +114,23 @@ impl ExecutionGadget for SignextendGadget { // enabled need to be changed to the sign byte. // When a byte was selected all the **following** bytes need to be // replaced (hence the `selectors[idx - 1]`). - let result = rlc::expr( - &array_init::<_, _, 32>(|idx| { - if idx == 0 { - value.cells[idx].expr() - } else { - select::expr( - selectors[idx - 1].expr(), - sign_byte.expr(), - value.cells[idx].expr(), - ) - } - }), - cb.challenges().evm_word(), - ); + let result = Word32::new(array_init::<_, _, 32>(|idx| { + if idx == 0 { + value.limbs[idx].expr() + } else { + select::expr( + selectors[idx - 1].expr(), + sign_byte.expr(), + value.limbs[idx].expr(), + ) + } + })); // Pop the byte index and the value from the stack, push the result on // the stack - cb.stack_pop(index.expr()); - cb.stack_pop(value.expr()); - cb.stack_push(result); + cb.stack_pop(index.to_word()); + cb.stack_pop(value.to_word()); + cb.stack_push(result.to_word()); // State transition let step_state_transition = StepStateTransition { @@ -166,11 +166,13 @@ impl ExecutionGadget for SignextendGadget { self.same_context.assign_exec_step(region, offset, step)?; // Inputs/Outputs - let index = block.rws[step.rw_indices[0]].stack_value().to_le_bytes(); - let value = block.rws[step.rw_indices[1]].stack_value().to_le_bytes(); - self.index.assign(region, offset, Some(index))?; - self.value.assign(region, offset, Some(value))?; + let index_word = block.rws[step.rw_indices[0]].stack_value(); + let value_word = block.rws[step.rw_indices[1]].stack_value(); + let index = index_word.to_le_bytes(); + let value = value_word.to_le_bytes(); + self.index.assign_u256(region, offset, index_word)?; + self.value.assign_u256(region, offset, value_word)?; // Generate the selectors let msb_sum_zero = self.is_msb_sum_zero diff --git a/zkevm-circuits/src/evm_circuit/execution/sload.rs b/zkevm-circuits/src/evm_circuit/execution/sload.rs index b27318aacc..fe5a2b893a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sload.rs @@ -12,9 +12,12 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; -use eth_types::{Field, ToScalar}; +use eth_types::Field; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -22,10 +25,13 @@ pub(crate) struct SloadGadget { same_context: SameContextGadget, tx_id: Cell, reversion_info: ReversionInfo, - callee_address: Cell, - phase2_key: Cell, - phase2_value: Cell, - phase2_committed_value: Cell, + callee_address: WordCell, + // phase2_key + key: WordCell, + // phase2_value + value: WordCell, + // phase2_committed_value + committed_value: WordCell, is_warm: Cell, } @@ -39,37 +45,37 @@ impl ExecutionGadget for SloadGadget { let tx_id = cb.call_context(None, CallContextFieldTag::TxId); let mut reversion_info = cb.reversion_info_read(None); - let callee_address = cb.call_context(None, CallContextFieldTag::CalleeAddress); + let callee_address = cb.call_context_read_as_word(None, CallContextFieldTag::CalleeAddress); - let phase2_key = cb.query_cell_phase2(); + let key = cb.query_word_unchecked(); // Pop the key from the stack - cb.stack_pop(phase2_key.expr()); + cb.stack_pop(key.to_word()); - let phase2_value = cb.query_cell_phase2(); - let phase2_committed_value = cb.query_cell_phase2(); + let value = cb.query_word_unchecked(); + let committed_value = cb.query_word_unchecked(); cb.account_storage_read( - callee_address.expr(), - phase2_key.expr(), - phase2_value.expr(), + callee_address.to_word(), + key.to_word(), + value.to_word(), tx_id.expr(), - phase2_committed_value.expr(), + committed_value.to_word(), ); - cb.stack_push(phase2_value.expr()); + cb.stack_push(value.to_word()); let is_warm = cb.query_bool(); cb.account_storage_access_list_read( tx_id.expr(), - callee_address.expr(), - phase2_key.expr(), - is_warm.expr(), + callee_address.to_word(), + key.to_word(), + Word::from_lo_unchecked(is_warm.expr()), ); cb.account_storage_access_list_write( tx_id.expr(), - callee_address.expr(), - phase2_key.expr(), - true.expr(), - is_warm.expr(), + callee_address.to_word(), + key.to_word(), + Word::from_lo_unchecked(true.expr()), + Word::from_lo_unchecked(is_warm.expr()), Some(&mut reversion_info), ); @@ -88,9 +94,9 @@ impl ExecutionGadget for SloadGadget { tx_id, reversion_info, callee_address, - phase2_key, - phase2_value, - phase2_committed_value, + key, + value, + committed_value, is_warm, } } @@ -114,26 +120,17 @@ impl ExecutionGadget for SloadGadget { call.rw_counter_end_of_reversion, call.is_persistent, )?; - self.callee_address.assign( - region, - offset, - Value::known( - call.callee_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), - )?; + self.callee_address + .assign_h160(region, offset, call.callee_address)?; let [key, value] = [step.rw_indices[4], step.rw_indices[6]].map(|idx| block.rws[idx].stack_value()); - self.phase2_key - .assign(region, offset, region.word_rlc(key))?; - self.phase2_value - .assign(region, offset, region.word_rlc(value))?; + self.key.assign_u256(region, offset, key)?; + self.value.assign_u256(region, offset, value)?; let (_, committed_value) = block.rws[step.rw_indices[5]].aux_pair(); - self.phase2_committed_value - .assign(region, offset, region.word_rlc(committed_value))?; + self.committed_value + .assign_u256(region, offset, committed_value)?; let (_, is_warm) = block.rws[step.rw_indices[7]].tx_access_list_value_pair(); self.is_warm diff --git a/zkevm-circuits/src/evm_circuit/execution/sstore.rs b/zkevm-circuits/src/evm_circuit/execution/sstore.rs index c78f65ea25..2ccd5c6d71 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sstore.rs @@ -11,16 +11,19 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, }, - math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, - not, CachedRegion, Cell, + math_gadget::{IsEqualWordGadget, IsZeroWordGadget, LtGadget}, + not, CachedRegion, Cell, U64Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; -use eth_types::{evm_types::GasCost, Field, ToScalar}; +use eth_types::{evm_types::GasCost, Field}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -32,16 +35,20 @@ pub(crate) struct SstoreGadget { tx_id: Cell, is_static: Cell, reversion_info: ReversionInfo, - callee_address: Cell, - phase2_key: Cell, - phase2_value: Cell, - phase2_value_prev: Cell, - phase2_original_value: Cell, + callee_address: WordCell, + //phase2_key + key: Word32Cell, + //phase2_value + value: Word32Cell, + // phase2_value_prev + value_prev: Word32Cell, + // phase2_original_value + original_value: Word32Cell, is_warm: Cell, - tx_refund_prev: Cell, + tx_refund_prev: U64Cell, // Constrain for SSTORE reentrancy sentry. sufficient_gas_sentry: LtGadget, - gas_cost: SstoreGasGadget, + gas_cost: SstoreGasGadget>, tx_refund: SstoreTxRefundGadget, } @@ -60,41 +67,41 @@ impl ExecutionGadget for SstoreGadget { cb.require_zero("is_static is false", is_static.expr()); let mut reversion_info = cb.reversion_info_read(None); - let callee_address = cb.call_context(None, CallContextFieldTag::CalleeAddress); + let callee_address = cb.call_context_read_as_word(None, CallContextFieldTag::CalleeAddress); - let phase2_key = cb.query_cell_phase2(); + let key = cb.query_word32(); // Pop the key from the stack - cb.stack_pop(phase2_key.expr()); + cb.stack_pop(key.to_word()); - let phase2_value = cb.query_cell_phase2(); + let value = cb.query_word32(); // Pop the value from the stack - cb.stack_pop(phase2_value.expr()); + cb.stack_pop(value.to_word()); - let phase2_value_prev = cb.query_cell_phase2(); - let phase2_original_value = cb.query_cell_phase2(); + let value_prev = cb.query_word32(); + let original_value = cb.query_word32(); cb.account_storage_write( - callee_address.expr(), - phase2_key.expr(), - phase2_value.expr(), - phase2_value_prev.expr(), + callee_address.to_word(), + key.to_word(), + value.to_word(), + value_prev.to_word(), tx_id.expr(), - phase2_original_value.expr(), + original_value.to_word(), Some(&mut reversion_info), ); let is_warm = cb.query_bool(); cb.account_storage_access_list_read( tx_id.expr(), - callee_address.expr(), - phase2_key.expr(), - is_warm.expr(), + callee_address.to_word(), + key.to_word(), + Word::from_lo_unchecked(is_warm.expr()), ); cb.account_storage_access_list_write( tx_id.expr(), - callee_address.expr(), - phase2_key.expr(), - true.expr(), - is_warm.expr(), + callee_address.to_word(), + key.to_word(), + Word::from_lo_unchecked(true.expr()), + Word::from_lo_unchecked(is_warm.expr()), Some(&mut reversion_info), ); @@ -112,24 +119,24 @@ impl ExecutionGadget for SstoreGadget { let gas_cost = SstoreGasGadget::construct( cb, - phase2_value.clone(), - phase2_value_prev.clone(), - phase2_original_value.clone(), is_warm.clone(), + value.clone(), + value_prev.clone(), + original_value.clone(), ); - let tx_refund_prev = cb.query_cell(); + let tx_refund_prev = cb.query_u64(); let tx_refund = SstoreTxRefundGadget::construct( cb, tx_refund_prev.clone(), - phase2_value.clone(), - phase2_value_prev.clone(), - phase2_original_value.clone(), + value.clone(), + value_prev.clone(), + original_value.clone(), ); cb.tx_refund_write( tx_id.expr(), - tx_refund.expr(), - tx_refund_prev.expr(), + Word::from_lo_unchecked(tx_refund.expr()), + tx_refund_prev.to_word(), Some(&mut reversion_info), ); @@ -149,10 +156,10 @@ impl ExecutionGadget for SstoreGadget { is_static, reversion_info, callee_address, - phase2_key, - phase2_value, - phase2_value_prev, - phase2_original_value, + key, + value, + value_prev, + original_value, is_warm, tx_refund_prev, sufficient_gas_sentry, @@ -182,28 +189,18 @@ impl ExecutionGadget for SstoreGadget { call.rw_counter_end_of_reversion, call.is_persistent, )?; - self.callee_address.assign( - region, - offset, - Value::known( - call.callee_address - .to_scalar() - .expect("unexpected Address -> Scalar conversion failure"), - ), - )?; + self.callee_address + .assign_h160(region, offset, call.callee_address)?; let [key, value] = [step.rw_indices[5], step.rw_indices[6]].map(|idx| block.rws[idx].stack_value()); - self.phase2_key - .assign(region, offset, region.word_rlc(key))?; - self.phase2_value - .assign(region, offset, region.word_rlc(value))?; + self.key.assign_u256(region, offset, key)?; + self.value.assign_u256(region, offset, value)?; let (_, value_prev, _, original_value) = block.rws[step.rw_indices[7]].storage_value_aux(); - self.phase2_value_prev - .assign(region, offset, region.word_rlc(value_prev))?; - self.phase2_original_value - .assign(region, offset, region.word_rlc(original_value))?; + self.value_prev.assign_u256(region, offset, value_prev)?; + self.original_value + .assign_u256(region, offset, original_value)?; let (_, is_warm) = block.rws[step.rw_indices[8]].tx_access_list_value_pair(); self.is_warm @@ -211,7 +208,7 @@ impl ExecutionGadget for SstoreGadget { let (tx_refund, tx_refund_prev) = block.rws[step.rw_indices[10]].tx_refund_value_pair(); self.tx_refund_prev - .assign(region, offset, Value::known(F::from(tx_refund_prev)))?; + .assign(region, offset, Some(tx_refund_prev.to_le_bytes()))?; self.sufficient_gas_sentry.assign_value( region, @@ -244,36 +241,34 @@ impl ExecutionGadget for SstoreGadget { #[derive(Clone, Debug)] pub(crate) struct SstoreTxRefundGadget { - tx_refund_old: Cell, + tx_refund_old: U64Cell, tx_refund_new: Expression, - value: Cell, - value_prev: Cell, - original_value: Cell, - value_prev_is_zero_gadget: IsZeroGadget, - value_is_zero_gadget: IsZeroGadget, - original_is_zero_gadget: IsZeroGadget, - original_eq_value_gadget: IsEqualGadget, - prev_eq_value_gadget: IsEqualGadget, - original_eq_prev_gadget: IsEqualGadget, + value_prev_is_zero_gadget: IsZeroWordGadget>>, + value_is_zero_gadget: IsZeroWordGadget>>, + original_is_zero_gadget: IsZeroWordGadget>>, + original_eq_value_gadget: IsEqualWordGadget>, Word>>, + prev_eq_value_gadget: IsEqualWordGadget>, Word>>, + original_eq_prev_gadget: IsEqualWordGadget>, Word>>, } impl SstoreTxRefundGadget { - pub(crate) fn construct( + pub(crate) fn construct>( cb: &mut EVMConstraintBuilder, - tx_refund_old: Cell, - value: Cell, - value_prev: Cell, - original_value: Cell, + tx_refund_old: U64Cell, + value: T, + value_prev: T, + original_value: T, ) -> Self { - let value_prev_is_zero_gadget = IsZeroGadget::construct(cb, value_prev.expr()); - let value_is_zero_gadget = IsZeroGadget::construct(cb, value.expr()); - let original_is_zero_gadget = IsZeroGadget::construct(cb, original_value.expr()); + let value_prev_is_zero_gadget = IsZeroWordGadget::construct(cb, &value_prev.to_word()); + let value_is_zero_gadget = IsZeroWordGadget::construct(cb, &value.to_word()); + let original_is_zero_gadget = IsZeroWordGadget::construct(cb, &original_value.to_word()); let original_eq_value_gadget = - IsEqualGadget::construct(cb, original_value.expr(), value.expr()); - let prev_eq_value_gadget = IsEqualGadget::construct(cb, value_prev.expr(), value.expr()); + IsEqualWordGadget::construct(cb, &original_value.to_word(), &value.to_word()); + let prev_eq_value_gadget = + IsEqualWordGadget::construct(cb, &value_prev.to_word(), &value.to_word()); let original_eq_prev_gadget = - IsEqualGadget::construct(cb, original_value.expr(), value_prev.expr()); + IsEqualWordGadget::construct(cb, &original_value.to_word(), &value_prev.to_word()); let value_prev_is_zero = value_prev_is_zero_gadget.expr(); let value_is_zero = value_is_zero_gadget.expr(); @@ -283,21 +278,21 @@ impl SstoreTxRefundGadget { let prev_eq_value = prev_eq_value_gadget.expr(); let original_eq_prev = original_eq_prev_gadget.expr(); - // (value_prev != value) && (original_value != 0) && (value == - // 0) + // (value_prev != value) && (original_value != value) && (value == + // Word::from(0)) let delete_slot = not::expr(prev_eq_value.clone()) * not::expr(original_is_zero.clone()) * value_is_zero; // (value_prev != value) && (original_value == value) && (original_value != - // 0) + // Word::from(0)) let reset_existing = not::expr(prev_eq_value.clone()) * original_eq_value.clone() * not::expr(original_is_zero.clone()); // (value_prev != value) && (original_value == value) && (original_value == - // 0) + // Word::from(0)) let reset_inexistent = not::expr(prev_eq_value.clone()) * original_eq_value * (original_is_zero); // (value_prev != value) && (original_value != value_prev) && (value_prev == - // 0) + // Word::from(0)) let recreate_slot = not::expr(prev_eq_value) * not::expr(original_eq_prev) * (value_prev_is_zero); @@ -308,9 +303,6 @@ impl SstoreTxRefundGadget { - recreate_slot * (GasCost::SSTORE_CLEARS_SCHEDULE.expr()); Self { - value, - value_prev, - original_value, tx_refund_old, tx_refund_new, value_prev_is_zero_gadget, @@ -339,38 +331,30 @@ impl SstoreTxRefundGadget { original_value: eth_types::Word, ) -> Result<(), Error> { self.tx_refund_old - .assign(region, offset, Value::known(F::from(tx_refund_old)))?; - self.value.assign(region, offset, region.word_rlc(value))?; - self.value_prev - .assign(region, offset, region.word_rlc(value_prev))?; - self.original_value - .assign(region, offset, region.word_rlc(original_value))?; + .assign(region, offset, Some(tx_refund_old.to_le_bytes()))?; self.value_prev_is_zero_gadget - .assign_value(region, offset, region.word_rlc(value_prev))?; + .assign(region, offset, Word::from(value_prev))?; self.value_is_zero_gadget - .assign_value(region, offset, region.word_rlc(value))?; - self.original_is_zero_gadget.assign_value( - region, - offset, - region.word_rlc(original_value), - )?; - self.original_eq_value_gadget.assign_value( + .assign(region, offset, Word::from(value))?; + self.original_is_zero_gadget + .assign(region, offset, Word::from(original_value))?; + self.original_eq_value_gadget.assign( region, offset, - region.word_rlc(original_value), - region.word_rlc(value), + Word::from(original_value), + Word::from(value), )?; - self.prev_eq_value_gadget.assign_value( + self.prev_eq_value_gadget.assign( region, offset, - region.word_rlc(value_prev), - region.word_rlc(value), + Word::from(value_prev), + Word::from(value), )?; - self.original_eq_prev_gadget.assign_value( + self.original_eq_prev_gadget.assign( region, offset, - region.word_rlc(original_value), - region.word_rlc(value_prev), + Word::from(original_value), + Word::from(value_prev), )?; debug_assert_eq!( calc_expected_tx_refund(tx_refund_old, value, value_prev, original_value), diff --git a/zkevm-circuits/src/evm_circuit/execution/stop.rs b/zkevm-circuits/src/evm_circuit/execution/stop.rs index c3248ffe18..05bc0c86db 100644 --- a/zkevm-circuits/src/evm_circuit/execution/stop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/stop.rs @@ -15,7 +15,10 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::CallContextFieldTag, - util::Expr, + util::{ + word::{Word, WordExpr}, + Expr, + }, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -36,7 +39,7 @@ impl ExecutionGadget for StopGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let code_length = cb.query_cell(); - cb.bytecode_length(cb.curr.state.code_hash.expr(), code_length.expr()); + cb.bytecode_length(cb.curr.state.code_hash.to_word(), code_length.expr()); let is_within_range = LtGadget::construct(cb, cb.curr.state.program_counter.expr(), code_length.expr()); let opcode = cb.query_cell(); @@ -53,7 +56,7 @@ impl ExecutionGadget for StopGadget { ); // Call ends with STOP must be successful - cb.call_context_lookup(false.expr(), None, CallContextFieldTag::IsSuccess, 1.expr()); + cb.call_context_lookup_read(None, CallContextFieldTag::IsSuccess, Word::one()); let is_to_end_tx = cb.next.execution_state_selector([ExecutionState::EndTx]); cb.require_equal( diff --git a/zkevm-circuits/src/evm_circuit/execution/swap.rs b/zkevm-circuits/src/evm_circuit/execution/swap.rs index 951d8922f7..d745c40d39 100644 --- a/zkevm-circuits/src/evm_circuit/execution/swap.rs +++ b/zkevm-circuits/src/evm_circuit/execution/swap.rs @@ -5,11 +5,14 @@ use crate::{ util::{ common_gadget::SameContextGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, - CachedRegion, Cell, + CachedRegion, }, witness::{Block, Call, ExecStep, Transaction}, }, - util::Expr, + util::{ + word::{WordCell, WordExpr}, + Expr, + }, }; use eth_types::{evm_types::OpcodeId, Field}; use halo2_proofs::plonk::Error; @@ -17,7 +20,8 @@ use halo2_proofs::plonk::Error; #[derive(Clone, Debug)] pub(crate) struct SwapGadget { same_context: SameContextGadget, - phase2_values: [Cell; 2], + // phase2_values + values: [WordCell; 2], } impl ExecutionGadget for SwapGadget { @@ -28,20 +32,20 @@ impl ExecutionGadget for SwapGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let phase2_values = [cb.query_cell_phase2(), cb.query_cell_phase2()]; + let values = [cb.query_word_unchecked(), cb.query_word_unchecked()]; // The stack index we have to peek, deduced from the 'x' value of // 'swapx' The offset starts at 1 for SWAP1 let swap_offset = opcode.expr() - (OpcodeId::SWAP1.as_u64() - 1).expr(); // Peek the value at `swap_offset` - cb.stack_lookup(false.expr(), swap_offset.clone(), phase2_values[0].expr()); + cb.stack_lookup(false.expr(), swap_offset.clone(), values[0].to_word()); // Peek the value at the top of the stack - cb.stack_lookup(false.expr(), 0.expr(), phase2_values[1].expr()); + cb.stack_lookup(false.expr(), 0.expr(), values[1].to_word()); // Write the value previously at the top of the stack to `swap_offset` - cb.stack_lookup(true.expr(), swap_offset, phase2_values[1].expr()); + cb.stack_lookup(true.expr(), swap_offset, values[1].to_word()); // Write the value previously at `swap_offset` to the top of the stack - cb.stack_lookup(true.expr(), 0.expr(), phase2_values[0].expr()); + cb.stack_lookup(true.expr(), 0.expr(), values[0].to_word()); // State transition let step_state_transition = StepStateTransition { @@ -54,7 +58,7 @@ impl ExecutionGadget for SwapGadget { Self { same_context, - phase2_values, + values, } } @@ -69,12 +73,12 @@ impl ExecutionGadget for SwapGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - for (cell, value) in self.phase2_values.iter().zip( + for (cell, value) in self.values.iter().zip( [step.rw_indices[0], step.rw_indices[1]] .map(|idx| block.rws[idx].stack_value()) .iter(), ) { - cell.assign(region, offset, region.word_rlc(*value))?; + cell.assign_u256(region, offset, *value)?; } Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index e4190abc68..4cb0030535 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -6,10 +6,11 @@ use halo2_proofs::{ }; use std::{collections::HashMap, sync::LazyLock}; -// Step dimension -pub(crate) const STEP_WIDTH: usize = 141; +/// Step dimension +pub(crate) const STEP_WIDTH: usize = 153; /// Step height -pub const MAX_STEP_HEIGHT: usize = 21; +pub const MAX_STEP_HEIGHT: usize = 22; + /// The height of the state of a step, used by gates that connect two /// consecutive steps. We target 1, which is also convenient for padding with /// EndBlock steps. @@ -17,6 +18,7 @@ pub(crate) const STEP_STATE_HEIGHT: usize = 1; /// Number of Advice Phase2 columns in the EVM circuit pub(crate) const N_PHASE2_COLUMNS: usize = 7; +//pub(crate) const N_PHASE2_COLUMNS: usize = 1; /// Number of Advice Phase1 columns in the EVM circuit pub(crate) const N_PHASE1_COLUMNS: usize = @@ -27,7 +29,7 @@ pub(crate) const N_COPY_COLUMNS: usize = 2; // Number of copy columns for phase2 pub(crate) const N_PHASE2_COPY_COLUMNS: usize = 1; -pub(crate) const N_BYTE_LOOKUPS: usize = 36; +pub(crate) const N_BYTE_LOOKUPS: usize = 43; /// Amount of lookup columns in the EVM circuit dedicated to lookups. pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS @@ -71,7 +73,7 @@ pub const TX_TABLE_LOOKUPS: usize = 4; pub const RW_TABLE_LOOKUPS: usize = 8; /// Bytecode Table lookups done in EVMCircuit -pub const BYTECODE_TABLE_LOOKUPS: usize = 1; +pub const BYTECODE_TABLE_LOOKUPS: usize = 4; /// Block Table lookups done in EVMCircuit pub const BLOCK_TABLE_LOOKUPS: usize = 1; @@ -106,6 +108,8 @@ pub(crate) const MAX_N_BYTES_INTEGER: usize = 31; // Number of bytes an EVM word has. pub(crate) const N_BYTES_WORD: usize = 32; +pub(crate) const N_BYTES_HALF_WORD: usize = N_BYTES_WORD / 2; + pub(crate) const N_BYTES_EC_PAIR: usize = 192; // Number of bytes an u64 has. diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index 953493a2cd..424f74fcc8 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -5,11 +5,15 @@ use crate::{ util::Cell, witness::{Block, Call, ExecStep}, }, - util::Expr, + util::{ + //cell_manager::{CMFixedWidthStrategy, CellManager}, + word::{Word, WordCell}, + Expr, + }, witness::Transaction, }; use bus_mapping::{evm::OpcodeId, precompile::PrecompileCalls}; -use eth_types::{evm_types::GasCost, Field}; +use eth_types::{evm_types::GasCost, Field, ToWord}; use halo2_proofs::{ circuit::Value, plonk::{Advice, Column, ConstraintSystem, Error, Expression}, @@ -549,7 +553,7 @@ pub(crate) struct StepState { /// In the case of a contract creation internal call, this denotes the hash /// of the chunk of bytes from caller's memory that represent the /// contract init code. - pub(crate) code_hash: Cell, + pub(crate) code_hash: WordCell, /// The program counter pub(crate) program_counter: Cell, /// The stack pointer @@ -596,7 +600,10 @@ impl Step { tx_id: cell_manager.query_cell(CellType::StoragePhase1), is_root: cell_manager.query_cell(CellType::StoragePhase1), is_create: cell_manager.query_cell(CellType::StoragePhase1), - code_hash: cell_manager.query_cell(CellType::StoragePhase2), + code_hash: Word::new([ + cell_manager.query_cell(CellType::StoragePhase1), + cell_manager.query_cell(CellType::StoragePhase1), + ]), block_number: cell_manager.query_cell(CellType::StoragePhase1), program_counter: cell_manager.query_cell(CellType::StoragePhase1), stack_pointer: cell_manager.query_cell(CellType::StoragePhase1), @@ -656,9 +663,10 @@ impl Step { self.state .block_number .assign(region, offset, Value::known(F::from(step.block_num)))?; + self.state .code_hash - .assign(region, offset, region.code_hash(call.code_hash))?; + .assign_u256(region, offset, call.code_hash.to_word())?; self.state.program_counter.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index 62f7da7426..535a8b32af 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -2,6 +2,7 @@ pub use crate::table::TxContextFieldTag; use crate::{ evm_circuit::step::{ExecutionState, ResponsibleOp}, impl_expr, + util::word::Word, }; use bus_mapping::{evm::OpcodeId, precompile::PrecompileCalls}; use eth_types::Field; @@ -167,11 +168,11 @@ pub struct RwValues { pub id: Expression, pub address: Expression, pub field_tag: Expression, - pub storage_key: Expression, - pub value: Expression, - pub value_prev: Expression, - pub aux1: Expression, - pub aux2: Expression, + pub storage_key: Word>, + pub value: Word>, + pub value_prev: Word>, + pub aux1: Word>, + pub aux2: Word>, } impl RwValues { @@ -180,11 +181,11 @@ impl RwValues { id: Expression, address: Expression, field_tag: Expression, - storage_key: Expression, - value: Expression, - value_prev: Expression, - aux1: Expression, - aux2: Expression, + storage_key: Word>, + value: Word>, + value_prev: Word>, + aux1: Word>, + aux2: Word>, ) -> Self { Self { id, @@ -197,6 +198,15 @@ impl RwValues { aux2, } } + + pub(crate) fn revert_value(&self) -> Self { + let new_self = self.clone(); + Self { + value_prev: new_self.value, + value: new_self.value_prev, + ..new_self + } + } } #[derive(Clone, Debug)] @@ -219,7 +229,8 @@ pub(crate) enum Lookup { /// field_tag is Calldata, otherwise should be set to 0. index: Expression, /// Value of the field. - value: Expression, + value: Word>, + // value: Expression, }, /// Lookup to read-write table, which contains read-write access records of /// time-aware data. @@ -239,7 +250,7 @@ pub(crate) enum Lookup { /// contract code. Bytecode { /// Hash to specify which code to read. - hash: Expression, + hash: Word>, /// Tag to specify whether its the bytecode length or byte value in the /// bytecode. tag: Expression, @@ -263,17 +274,18 @@ pub(crate) enum Lookup { number: Expression, /// Value of the field. value: Expression, + // value: Word>, }, /// Lookup to copy table. CopyTable { /// Whether the row is the first row of the copy event. is_first: Expression, /// The source ID for the copy event. - src_id: Expression, + src_id: Word>, /// The source tag for the copy event. src_tag: Expression, /// The destination ID for the copy event. - dst_id: Expression, + dst_id: Word>, /// The destination tag for the copy event. dst_tag: Expression, /// The source address where bytes are copied from. @@ -303,6 +315,7 @@ pub(crate) enum Lookup { /// Output (hash) until this state. This is the RLC representation of /// the final output keccak256 hash of the input. output_rlc: Expression, + output: Word>, }, /// Lookup to sha256 table. Sha256Table { @@ -390,7 +403,9 @@ impl Lookup { id.clone(), field_tag.clone(), index.clone(), - value.clone(), + value.lo(), + value.hi(), + // value.clone(), ], Self::Rw { counter, @@ -406,11 +421,16 @@ impl Lookup { values.id.clone(), values.address.clone(), values.field_tag.clone(), - values.storage_key.clone(), - values.value.clone(), - values.value_prev.clone(), - values.aux1.clone(), - values.aux2.clone(), + values.storage_key.lo(), + values.storage_key.hi(), + values.value.lo(), + values.value.hi(), + values.value_prev.lo(), + values.value_prev.hi(), + values.aux1.lo(), + values.aux1.hi(), + values.aux2.lo(), + values.aux2.hi(), ] } Self::Bytecode { @@ -423,7 +443,8 @@ impl Lookup { } => { vec![ 1.expr(), // q_enable - hash.clone(), + hash.lo(), + hash.hi(), tag.clone(), index.clone(), is_code.clone(), @@ -436,6 +457,8 @@ impl Lookup { number, value, } => { + // used in stage2 after block table to word + //vec![field_tag.clone(), number.clone(), value.lo(), value.hi()] vec![field_tag.clone(), number.clone(), value.clone()] } Self::CopyTable { @@ -454,9 +477,11 @@ impl Lookup { } => vec![ 1.expr(), is_first.clone(), - src_id.clone(), + src_id.lo(), + src_id.hi(), src_tag.clone(), - dst_id.clone(), + dst_id.lo(), + dst_id.hi(), dst_tag.clone(), src_addr.clone(), src_addr_end.clone(), @@ -470,12 +495,15 @@ impl Lookup { input_rlc, input_len, output_rlc, + output, } => vec![ 1.expr(), // q_enable 1.expr(), // is_final input_rlc.clone(), input_len.clone(), output_rlc.clone(), + output.lo(), + output.hi(), ], Self::Sha256Table { input_rlc, diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index aeb6c2db5a..78b3d7d182 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -1,13 +1,15 @@ use crate::{ evm_circuit::{ param::{ - LOOKUP_CONFIG, N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, N_BYTE_LOOKUPS, N_COPY_COLUMNS, - N_PHASE2_COLUMNS, N_PHASE2_COPY_COLUMNS, + LOOKUP_CONFIG, N_BYTES_ACCOUNT_ADDRESS, N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, + N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE2_COLUMNS, N_PHASE2_COPY_COLUMNS, }, table::Table, }, table::RwTableTag, - util::{query_expression, Challenges, Expr}, + //util::{cell_manager::CMFixedWidthStrategyDistribution, int_decomposition::IntDecomposition}, + util::int_decomposition::IntDecomposition, + util::{query_expression, word::Word, Challenges, Expr}, witness::{Block, ExecStep, Rw, RwMap}, }; use bus_mapping::state_db::CodeDB; @@ -35,7 +37,7 @@ pub(crate) mod precompile_gadget; pub use gadgets::util::{and, not, or, select, sum}; #[derive(Clone, Debug)] -pub(crate) struct Cell { +pub struct Cell { // expression for constraint expression: Expression, column: Column, @@ -560,9 +562,11 @@ impl Expr for RandomLinearCombination { } } -pub(crate) type Word = RandomLinearCombination; -pub(crate) type U64Word = RandomLinearCombination; -pub(crate) type MemoryAddress = RandomLinearCombination; +pub(crate) type MemoryAddress = IntDecomposition; + +pub(crate) type AccountAddress = IntDecomposition; + +pub(crate) type U64Cell = IntDecomposition; /// Decodes a field element from its byte representation pub(crate) mod from_bytes { @@ -721,6 +725,15 @@ pub(crate) fn is_precompiled(address: &Address) -> bool { address.0[0..19] == [0u8; 19] && (1..=9).contains(&address.0[19]) } +const BASE_128_BYTES: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +/// convert address (h160) to single expression. +pub fn address_word_to_expr(address: Word>) -> Expression { + address.lo() + address.hi() * Expression::Constant(F::from_repr(BASE_128_BYTES).unwrap()) +} + /// Helper struct to read rw operations from a step sequentially. pub(crate) struct StepRws<'a> { rws: &'a RwMap, diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index e69fd7f511..dc8e11a72c 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -1,13 +1,13 @@ use super::{ constraint_builder::ConstrainBuilderCommon, from_bytes, - math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, + math_gadget::{IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget, LtGadget}, memory_gadget::{CommonMemoryAddressGadget, MemoryExpansionGadget}, CachedRegion, }; use crate::{ evm_circuit::{ - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, + param::{N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE, N_BYTES_U64}, step::ExecutionState, table::{FixedTableTag, Lookup}, util::{ @@ -16,15 +16,19 @@ use crate::{ Transition::{Delta, Same, To}, }, math_gadget::{AddWordsGadget, RangeCheckGadget}, - not, or, Cell, CellType, StepRws, Word, + not, or, Cell, StepRws, }, }, table::{AccountFieldTag, CallContextFieldTag}, - util::Expr, + util::{ + word::{Word, Word32, Word32Cell, WordCell, WordExpr}, + Expr, + }, witness::{Block, Call, ExecStep}, }; +use bus_mapping::state_db::CodeDB; use either::Either; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar, U256}; +use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar, ToWord, U256}; use gadgets::util::{select, sum}; use halo2_proofs::{ circuit::Value, @@ -113,7 +117,7 @@ pub(crate) struct RestoreContextGadget { caller_id: Cell, caller_is_root: Cell, caller_is_create: Cell, - caller_code_hash: Cell, + caller_code_hash: WordCell, caller_program_counter: Cell, caller_stack_pointer: Cell, caller_gas_left: Cell, @@ -159,11 +163,16 @@ impl RestoreContextGadget { ) -> Self { // Read caller's context for restore let caller_id = cb.call_context(None, CallContextFieldTag::CallerId); - let [caller_is_root, caller_is_create, caller_code_hash, caller_program_counter, caller_stack_pointer, caller_gas_left, caller_memory_word_size, caller_reversible_write_counter] = + + let [caller_is_root, caller_is_create] = + [CallContextFieldTag::IsRoot, CallContextFieldTag::IsCreate] + .map(|field_tag| cb.call_context(Some(caller_id.expr()), field_tag)); + + let caller_code_hash = + cb.call_context_read_as_word(Some(caller_id.expr()), CallContextFieldTag::CodeHash); + + let [caller_program_counter, caller_stack_pointer, caller_gas_left, caller_memory_word_size, caller_reversible_write_counter] = [ - CallContextFieldTag::IsRoot, - CallContextFieldTag::IsCreate, - CallContextFieldTag::CodeHash, CallContextFieldTag::ProgramCounter, CallContextFieldTag::StackPointer, CallContextFieldTag::GasLeft, @@ -189,15 +198,23 @@ impl RestoreContextGadget { for (field_tag, value) in [ ( CallContextFieldTag::LastCalleeId, - cb.curr.state.call_id.expr(), + Word::from_lo_unchecked(cb.curr.state.call_id.expr()), ), ( CallContextFieldTag::LastCalleeReturnDataOffset, - select::expr(discard_return_data.clone(), 0.expr(), return_data_offset), + Word::from_lo_unchecked(select::expr( + discard_return_data.clone(), + 0.expr(), + return_data_offset, + )), ), ( CallContextFieldTag::LastCalleeReturnDataLength, - select::expr(discard_return_data, 0.expr(), return_data_length.clone()), + Word::from_lo_unchecked(select::expr( + discard_return_data, + 0.expr(), + return_data_length.clone(), + )), ), ] { cb.call_context_lookup(true.expr(), Some(caller_id.expr()), field_tag, value); @@ -240,7 +257,7 @@ impl RestoreContextGadget { call_id: To(caller_id.expr()), is_root: To(caller_is_root.expr()), is_create: To(caller_is_create.expr()), - code_hash: To(caller_code_hash.expr()), + code_hash: To(caller_code_hash.to_word()), program_counter: To(caller_program_counter.expr()), stack_pointer: To(caller_stack_pointer.expr()), gas_left: To(gas_left), @@ -326,7 +343,7 @@ impl RestoreContextGadget { } self.caller_code_hash - .assign(region, offset, region.code_hash(caller_code_hash))?; + .assign_u256(region, offset, caller_code_hash)?; Ok(()) } @@ -342,19 +359,19 @@ impl { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - address: Expression, - updates: Vec>, + address: Word>, + updates: Vec>, reversion_info: Option<&mut ReversionInfo>, ) -> Self { debug_assert!(updates.len() == N_ADDENDS - 1); - let balance_addend = cb.query_word_rlc(); - let balance_sum = cb.query_word_rlc(); + let balance_addend = cb.query_word32(); + let balance_sum = cb.query_word32(); - let [value, value_prev] = if INCREASE { - [balance_sum.expr(), balance_addend.expr()] + let [value, value_prev]: [Word32>; 2] = if INCREASE { + [balance_sum.to_word_n(), balance_addend.to_word_n()] } else { - [balance_addend.expr(), balance_sum.expr()] + [balance_addend.to_word_n(), balance_sum.to_word_n()] }; let add_words = AddWordsGadget::construct( @@ -370,15 +387,15 @@ impl cb.account_write( address, AccountFieldTag::Balance, - value, - value_prev, + value.to_word(), + value_prev.to_word(), reversion_info, ); Self { add_words } } - pub(crate) fn balance(&self) -> &Word { + pub(crate) fn balance(&self) -> &Word32Cell { if INCREASE { self.add_words.sum() } else { @@ -386,7 +403,7 @@ impl } } - pub(crate) fn balance_prev(&self) -> &Word { + pub(crate) fn balance_prev(&self) -> &Word32Cell { if INCREASE { &self.add_words.addends()[0] } else { @@ -429,7 +446,7 @@ pub(crate) struct TransferGadgetAssignResult { #[derive(Clone, Debug)] pub(crate) struct TransferFromGadgetImpl { - pub(crate) value_is_zero: Either, Expression>, + pub(crate) value_is_zero: Either>, Expression>, sender_sub_fee: WithFeeGadget, sender_sub_value: UpdateBalanceGadget, } @@ -455,12 +472,12 @@ pub(crate) trait TransferGadgetInfo { impl TransferFromGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - sender_address: Expression, - value: Word, + sender_address: Word>, + value: Word32Cell, reversion_info: &mut ReversionInfo, ) -> Self { let value_is_zero = cb.annotation("transfer from is zero value", |cb| { - IsZeroGadget::construct(cb, value.expr()) + IsZeroWordGadget::construct(cb, &value) }); Self::construct_with_is_zero( cb, @@ -473,9 +490,9 @@ impl TransferFromGadget { pub(crate) fn construct_with_is_zero( cb: &mut EVMConstraintBuilder, - sender_address: Expression, - value: Word, - value_is_zero: Either, Expression>, + sender_address: Word>, + value: Word32Cell, + value_is_zero: Either>, Expression>, reversion_info: &mut ReversionInfo, ) -> Self { let value_is_zero_expr = value_is_zero @@ -504,7 +521,7 @@ impl TransferFromGadget { value: U256, ) -> Result<(), Error> { if let Either::Left(value_is_zero) = &self.value_is_zero { - value_is_zero.assign_value(region, offset, region.word_rlc(value))?; + value_is_zero.assign_value(region, offset, Value::known(Word::from(value)))?; } self.sender_sub_value.assign( region, @@ -540,12 +557,12 @@ impl TransferFromAssign for TransferFromGadget { impl TransferFromWithGasFeeGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - sender_address: Expression, - value: Word, - gas_fee: Word, + sender_address: Word>, + value: Word32Cell, + gas_fee: Word32Cell, reversion_info: &mut ReversionInfo, ) -> Self { - let value_is_zero = IsZeroGadget::construct(cb, value.expr()); + let value_is_zero = IsZeroWordGadget::construct(cb, &value); Self::construct_with_is_zero( cb, sender_address, @@ -558,17 +575,17 @@ impl TransferFromWithGasFeeGadget { pub(crate) fn construct_with_is_zero( cb: &mut EVMConstraintBuilder, - sender_address: Expression, - value: Word, - gas_fee: Word, - value_is_zero: Either, Expression>, + sender_address: Word>, + value: Word32Cell, + gas_fee: Word32Cell, + value_is_zero: Either>, Expression>, reversion_info: &mut ReversionInfo, ) -> Self { let value_is_zero_expr = value_is_zero .as_ref() .either(|gadget| gadget.expr(), |expr| expr.clone()); let sender_sub_fee = - UpdateBalanceGadget::construct(cb, sender_address.expr(), vec![gas_fee], None); + UpdateBalanceGadget::construct(cb, sender_address.clone(), vec![gas_fee], None); let sender_sub_value = cb.condition(not::expr(value_is_zero_expr), |cb| { UpdateBalanceGadget::construct( cb, @@ -599,7 +616,7 @@ impl TransferFromWithGasFeeGadget { ); if let Either::Left(value_is_zero) = &self.value_is_zero { - value_is_zero.assign_value(region, offset, region.word_rlc(value))?; + value_is_zero.assign_value(region, offset, Value::known(Word::from(value)))?; } self.sender_sub_fee.assign( region, @@ -618,7 +635,7 @@ impl TransferFromWithGasFeeGadget { } /// Return sender balance before subtracting fee and value. - pub(crate) fn sender_balance_prev(&self) -> &Word { + pub(crate) fn sender_balance_prev(&self) -> &Word32Cell { // Fee is subtracted before value. self.sender_sub_fee.balance_prev() } @@ -691,7 +708,7 @@ impl TransferGadgetInfo for TransferFromGadget { #[derive(Clone, Debug)] pub(crate) struct TransferToGadget { - pub(crate) value_is_zero: Either, Expression>, + pub(crate) value_is_zero: Either>, Expression>, receiver: UpdateBalanceGadget, receiver_exists: Expression, must_create: Expression, @@ -701,16 +718,17 @@ impl TransferToGadget { #[allow(clippy::too_many_arguments)] pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - receiver_address: Expression, + receiver_address: Word>, receiver_exists: Expression, must_create: Expression, - prev_code_hash: Expression, - #[cfg(feature = "scroll")] prev_keccak_code_hash: Expression, - value: Word, + prev_code_hash: Word>, + // scroll feature take use of poseidon hash + #[cfg(feature = "scroll")] prev_keccak_code_hash: Word>, + value: Word32Cell, reversion_info: Option<&mut ReversionInfo>, ) -> Self { let value_is_zero = cb.annotation("transfer to is zero value", |cb| { - IsZeroGadget::construct(cb, value.expr()) + IsZeroWordGadget::construct(cb, &value) }); Self::construct_with_is_zero( cb, @@ -729,13 +747,13 @@ impl TransferToGadget { #[allow(clippy::too_many_arguments)] pub(crate) fn construct_with_is_zero( cb: &mut EVMConstraintBuilder, - receiver_address: Expression, + receiver_address: Word>, receiver_exists: Expression, must_create: Expression, - prev_code_hash: Expression, - #[cfg(feature = "scroll")] prev_keccak_code_hash: Expression, - value: Word, - value_is_zero: Either, Expression>, + prev_code_hash: Word>, + #[cfg(feature = "scroll")] prev_keccak_code_hash: Word>, + value: Word32Cell, + value_is_zero: Either>, Expression>, mut reversion_info: Option<&mut ReversionInfo>, ) -> Self { let value_is_zero_expr = value_is_zero @@ -751,13 +769,13 @@ impl TransferToGadget { cb.account_read( receiver_address.clone(), AccountFieldTag::CodeHash, - prev_code_hash.expr(), + prev_code_hash.to_word(), ); cb.account_write( receiver_address.clone(), AccountFieldTag::CodeHash, - cb.empty_code_hash_rlc(), - prev_code_hash.expr(), + cb.empty_code_hash(), + prev_code_hash.to_word(), reversion_info.as_deref_mut(), ); #[cfg(feature = "scroll")] @@ -765,14 +783,14 @@ impl TransferToGadget { cb.account_read( receiver_address.clone(), AccountFieldTag::KeccakCodeHash, - prev_keccak_code_hash.expr(), + prev_keccak_code_hash.clone(), ); cb.account_write( receiver_address.clone(), AccountFieldTag::KeccakCodeHash, - cb.empty_keccak_hash_rlc(), - prev_keccak_code_hash.expr(), + cb.empty_keccak_hash(), + prev_keccak_code_hash, reversion_info.as_deref_mut(), ); } @@ -797,8 +815,9 @@ impl TransferToGadget { value: U256, ) -> Result<(), Error> { if let Either::Left(value_is_zero) = &self.value_is_zero { - value_is_zero.assign_value(region, offset, region.word_rlc(value))?; + value_is_zero.assign_value(region, offset, Value::known(Word::from(value)))?; } + self.receiver.assign( region, offset, @@ -818,7 +837,7 @@ impl TransferToGadget { rws: &mut StepRws, ) -> Result { if let Either::Left(value_is_zero) = &self.value_is_zero { - value_is_zero.assign_value(region, offset, region.word_rlc(value))?; + value_is_zero.assign_value(region, offset, Value::known(Word::from(value)))?; } let result = if (!receiver_exists && !value.is_zero()) || must_create { TransferGadgetAssignResult { @@ -891,7 +910,7 @@ impl TransferGadgetInfo for TransferToGadget { /// unconditionally if must_create is true. This gadget is used in BeginTx. #[derive(Clone, Debug)] pub(crate) struct TransferGadgetImpl { - value_is_zero: IsZeroGadget, + value_is_zero: IsZeroWordGadget>, from: TransferFromGadget, to: TransferToGadget, } @@ -904,17 +923,17 @@ impl TransferWithGasFeeGadget { #[allow(clippy::too_many_arguments)] pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - sender_address: Expression, - receiver_address: Expression, + sender_address: Word>, + receiver_address: Word>, receiver_exists: Expression, must_create: Expression, - prev_code_hash: Expression, - #[cfg(feature = "scroll")] prev_keccak_code_hash: Expression, - value: Word, - gas_fee: Word, + prev_code_hash: Word>, + #[cfg(feature = "scroll")] prev_keccak_code_hash: Word>, + value: Word32Cell, + gas_fee: Word32Cell, reversion_info: &mut ReversionInfo, ) -> Self { - let value_is_zero = IsZeroGadget::construct(cb, value.expr()); + let value_is_zero = IsZeroWordGadget::construct(cb, &value); let from = TransferFromWithGasFeeGadget::construct_with_is_zero( cb, sender_address, @@ -943,7 +962,7 @@ impl TransferWithGasFeeGadget { } /// Return sender balance before subtracting fee and value. - pub(crate) fn sender_balance_prev(&self) -> &Word { + pub(crate) fn sender_balance_prev(&self) -> &Word32Cell { self.from.sender_balance_prev() } } @@ -952,21 +971,21 @@ impl TransferGadget { #[allow(clippy::too_many_arguments)] pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - sender_address: Expression, - receiver_address: Expression, + sender_address: Word>, + receiver_address: Word>, receiver_exists: Expression, must_create: Expression, - prev_code_hash: Expression, - #[cfg(feature = "scroll")] prev_keccak_code_hash: Expression, - value: Word, + prev_code_hash: Word>, + #[cfg(feature = "scroll")] prev_keccak_code_hash: Word>, + value: Word32Cell, reversion_info: &mut ReversionInfo, ) -> Self { let value_is_zero = cb.annotation("transfer is zero value", |cb| { - IsZeroGadget::construct(cb, value.expr()) + IsZeroWordGadget::construct(cb, &value) }); let from = TransferFromGadget::construct_with_is_zero( cb, - sender_address.expr(), + sender_address, value.clone(), Either::Right(value_is_zero.expr()), reversion_info, @@ -1002,7 +1021,7 @@ impl + TransferGadgetInfo> TransferGadgetI rws: &mut StepRws, ) -> Result { self.value_is_zero - .assign_value(region, offset, region.word_rlc(value))?; + .assign_value(region, offset, Value::known(Word::from(value)))?; let TransferGadgetAssignResult { gas_fee, sender_balance_sub_fee_pair, @@ -1054,20 +1073,23 @@ impl + TransferGadgetInfo> TransferGadgetI pub(crate) struct CommonCallGadget { pub is_success: Cell, - pub gas: Word, + pub gas: Word32Cell, pub gas_is_u64: IsZeroGadget, - pub callee_address: Word, - pub value: Word, + pub callee_address_word: Word32Cell, + pub value: Word32Cell, pub cd_address: MemAddrGadget, pub rd_address: MemAddrGadget, pub memory_expansion: MemoryExpansionGadget, - value_is_zero: IsZeroGadget, + value_is_zero: IsZeroWordGadget>, + // value_is_zero: IsZeroGadget, pub has_value: Expression, - pub phase2_callee_code_hash: Cell, - pub is_empty_code_hash: IsEqualGadget, + // phase2_callee_code_hash + pub callee_code_hash: WordCell, + pub is_empty_code_hash: IsEqualWordGadget, Word>>, - pub callee_not_exists: IsZeroGadget, + //pub callee_not_exists: IsZeroGadget, + pub callee_not_exists: IsZeroWordGadget>, // save information is_call: Expression, @@ -1093,9 +1115,9 @@ impl, const IS_SUCCESS_CAL 1.expr(), ); - let gas_word = cb.query_word_rlc(); - let callee_address_word = cb.query_word_rlc(); - let value = cb.query_word_rlc(); + let gas_word = cb.query_word32(); + let callee_address_word = cb.query_word32(); + let value = cb.query_word32(); let is_success = cb.query_bool(); let cd_address = MemAddrGadget::construct_self(cb); @@ -1109,51 +1131,55 @@ impl, const IS_SUCCESS_CAL // callee address is `current_callee_address`. // For both CALL and STATICCALL, caller address is // `current_callee_address` and callee address is `callee_address`. - cb.stack_pop(gas_word.expr()); - cb.stack_pop(callee_address_word.expr()); + cb.stack_pop(gas_word.to_word()); + cb.stack_pop(callee_address_word.to_word()); // `CALL` and `CALLCODE` opcodes have an additional stack pop `value`. - cb.condition(is_call.expr() + is_callcode.expr(), |cb| { - cb.stack_pop(value.expr()) + cb.condition(is_call.clone() + is_callcode.clone(), |cb| { + cb.stack_pop(value.to_word()) }); - cb.stack_pop(cd_address.offset_rlc()); - cb.stack_pop(cd_address.length_rlc()); - cb.stack_pop(rd_address.offset_rlc()); - cb.stack_pop(rd_address.length_rlc()); + cb.stack_pop(cd_address.offset_word()); + cb.stack_pop(cd_address.length_word()); + cb.stack_pop(rd_address.offset_word()); + cb.stack_pop(rd_address.length_word()); cb.stack_push(if IS_SUCCESS_CALL { - is_success.expr() + Word::from_lo_unchecked(is_success.expr()) // is_success is bool } else { - 0.expr() + Word::zero() }); // Recomposition of random linear combination to integer - let gas_is_u64 = IsZeroGadget::construct(cb, sum::expr(&gas_word.cells[N_BYTES_GAS..])); - let memory_expansion = MemoryExpansionGadget::construct( - cb, - [cd_address.end_offset(), rd_address.end_offset()], - ); + let gas_is_u64 = IsZeroGadget::construct(cb, sum::expr(&gas_word.limbs[N_BYTES_GAS..])); + + let memory_expansion = + MemoryExpansionGadget::construct(cb, [cd_address.address(), rd_address.address()]); // construct common gadget - let value_is_zero = IsZeroGadget::construct(cb, sum::expr(&value.cells)); + //let value_is_zero = IsZeroGadget::construct(cb, sum::expr(&value.cells)); + let value_is_zero = IsZeroWordGadget::construct(cb, &value); + let has_value = select::expr( is_delegatecall.expr() + is_staticcall.expr(), 0.expr(), 1.expr() - value_is_zero.expr(), ); - let phase2_callee_code_hash = cb.query_cell_with_type(CellType::StoragePhase2); + let callee_code_hash = cb.query_word_unchecked(); + let callee_address_normal = Self::callee_address_valid(&callee_address_word); + cb.account_read( - from_bytes::expr(&callee_address_word.cells[..N_BYTES_ACCOUNT_ADDRESS]), + callee_address_normal, AccountFieldTag::CodeHash, - phase2_callee_code_hash.expr(), + callee_code_hash.to_word(), ); let is_empty_code_hash = - IsEqualGadget::construct(cb, phase2_callee_code_hash.expr(), cb.empty_code_hash_rlc()); - let callee_not_exists = IsZeroGadget::construct(cb, phase2_callee_code_hash.expr()); + IsEqualWordGadget::construct(cb, &callee_code_hash, &cb.empty_code_hash()); + + let callee_not_exists = IsZeroWordGadget::construct(cb, &callee_code_hash); Self { is_success, - callee_address: callee_address_word, + callee_address_word, gas: gas_word, gas_is_u64, value, @@ -1162,7 +1188,8 @@ impl, const IS_SUCCESS_CAL memory_expansion, value_is_zero, has_value, - phase2_callee_code_hash, + //phase2_callee_code_hash, + callee_code_hash, is_empty_code_hash, callee_not_exists, is_call, @@ -1172,12 +1199,19 @@ impl, const IS_SUCCESS_CAL } } - pub fn callee_address_expr(&self) -> Expression { - from_bytes::expr(&self.callee_address.cells[..N_BYTES_ACCOUNT_ADDRESS]) + pub fn callee_address(&self) -> Word> { + Self::callee_address_valid(&self.callee_address_word) + } + + pub fn callee_address_valid(callee_address_word: &Word32Cell) -> Word> { + let limbs: [Expression; 32] = callee_address_word.limbs.clone().map(|l| l.expr()); + let callee_address_lo = from_bytes::expr(&limbs[0..16]); + let callee_address_hi = from_bytes::expr(&limbs[16..20]); + Word::new([callee_address_lo, callee_address_hi]) } pub fn gas_expr(&self) -> Expression { - from_bytes::expr(&self.gas.cells[..N_BYTES_GAS]) + from_bytes::expr(&self.gas.limbs[..N_BYTES_GAS]) } pub fn gas_cost_expr( @@ -1210,13 +1244,14 @@ impl, const IS_SUCCESS_CAL rd_offset: U256, rd_length: U256, memory_word_size: u64, - phase2_callee_code_hash: Value, + // callee_code_hash in phase2 + callee_code_hash: U256, ) -> Result { - self.gas.assign(region, offset, Some(gas.to_le_bytes()))?; - self.callee_address - .assign(region, offset, Some(callee_address.to_le_bytes()))?; - self.value - .assign(region, offset, Some(value.to_le_bytes()))?; + self.gas.assign_u256(region, offset, gas)?; + self.callee_address_word + .assign_u256(region, offset, callee_address)?; + + self.value.assign_u256(region, offset, value)?; if IS_SUCCESS_CALL { self.is_success .assign(region, offset, Value::known(F::from(is_success.low_u64())))?; @@ -1240,17 +1275,22 @@ impl, const IS_SUCCESS_CAL )?; self.value_is_zero - .assign(region, offset, sum::value(&value.to_le_bytes()))?; - self.phase2_callee_code_hash - .assign(region, offset, phase2_callee_code_hash)?; + .assign(region, offset, Word::from(value))?; + self.callee_code_hash + .assign_u256(region, offset, callee_code_hash)?; + self.is_empty_code_hash.assign_value( region, offset, - phase2_callee_code_hash, - region.empty_code_hash_rlc(), + Value::known(Word::from(callee_code_hash)), + Value::known(Word::from(CodeDB::empty_code_hash().to_word())), )?; - self.callee_not_exists - .assign_value(region, offset, phase2_callee_code_hash)?; + self.callee_not_exists.assign_value( + region, + offset, + Value::known(Word::from(callee_code_hash)), + )?; + Ok(memory_expansion_gas_cost) } @@ -1312,29 +1352,29 @@ impl SloadGasGadget { } #[derive(Clone, Debug)] -pub(crate) struct SstoreGasGadget { - value: Cell, - value_prev: Cell, - original_value: Cell, +pub(crate) struct SstoreGasGadget { + value: T, + value_prev: T, + original_value: T, is_warm: Cell, gas_cost: Expression, - value_eq_prev: IsEqualGadget, - original_eq_prev: IsEqualGadget, - original_is_zero: IsZeroGadget, + value_eq_prev: IsEqualWordGadget, + original_eq_prev: IsEqualWordGadget, + original_is_zero: IsZeroWordGadget, } -impl SstoreGasGadget { +impl + Clone> SstoreGasGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - value: Cell, - value_prev: Cell, - original_value: Cell, is_warm: Cell, + value: T, + value_prev: T, + original_value: T, ) -> Self { - let value_eq_prev = IsEqualGadget::construct(cb, value.expr(), value_prev.expr()); - let original_eq_prev = - IsEqualGadget::construct(cb, original_value.expr(), value_prev.expr()); - let original_is_zero = IsZeroGadget::construct(cb, original_value.expr()); + let value_eq_prev = IsEqualWordGadget::construct(cb, &value, &value_prev); + let original_eq_prev = IsEqualWordGadget::construct(cb, &original_value, &value_prev); + let original_is_zero = IsZeroWordGadget::construct(cb, &original_value); + let warm_case_gas = select::expr( value_eq_prev.expr(), GasCost::WARM_ACCESS.expr(), @@ -1348,6 +1388,7 @@ impl SstoreGasGadget { GasCost::WARM_ACCESS.expr(), ), ); + let gas_cost = select::expr( is_warm.expr(), warm_case_gas.expr(), @@ -1379,27 +1420,25 @@ impl SstoreGasGadget { original_value: eth_types::Word, is_warm: bool, ) -> Result<(), Error> { - self.value.assign(region, offset, region.word_rlc(value))?; - self.value_prev - .assign(region, offset, region.word_rlc(value_prev))?; - self.original_value - .assign(region, offset, region.word_rlc(original_value))?; self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; self.value_eq_prev.assign_value( region, offset, - region.word_rlc(value), - region.word_rlc(value_prev), + Value::known(Word::from(value)), + Value::known(Word::from(value_prev)), )?; self.original_eq_prev.assign_value( region, offset, - region.word_rlc(original_value), - region.word_rlc(value_prev), + Value::known(Word::from(original_value)), + Value::known(Word::from(value_prev)), + )?; + self.original_is_zero.assign_value( + region, + offset, + Value::known(Word::from(original_value)), )?; - self.original_is_zero - .assign_value(region, offset, region.word_rlc(original_value))?; Ok(()) } } @@ -1440,7 +1479,7 @@ pub(crate) fn cal_sstore_gas_cost_for_assignment( #[derive(Clone, Debug)] pub(crate) struct CommonErrorGadget { - rw_counter_end_of_reversion: Cell, + rw_counter_end_of_reversion: WordCell, restore_context: RestoreContextGadget, } @@ -1489,16 +1528,16 @@ impl CommonErrorGadget { ) -> Self { cb.opcode_lookup_rlc(opcode.expr(), push_rlc); - let rw_counter_end_of_reversion = cb.query_cell(); + // rw_counter_end_of_reversion just used for read lookup, therefore skip range check + let rw_counter_end_of_reversion = cb.query_word_unchecked(); // current call must be failed. - cb.call_context_lookup(false.expr(), None, CallContextFieldTag::IsSuccess, 0.expr()); + cb.call_context_lookup_read(None, CallContextFieldTag::IsSuccess, Word::zero()); - cb.call_context_lookup( - false.expr(), + cb.call_context_lookup_read( None, CallContextFieldTag::RwCounterEndOfReversion, - rw_counter_end_of_reversion.expr(), + rw_counter_end_of_reversion.to_word(), ); // Go to EndTx only when is_root @@ -1539,7 +1578,7 @@ impl CommonErrorGadget { cb.curr.state.rw_counter.expr() + cb.rw_counter_offset() - 1.expr(); cb.require_equal( "rw_counter_end_of_reversion = rw_counter_end_of_step + reversible_counter", - rw_counter_end_of_reversion.expr(), + rw_counter_end_of_reversion.lo().expr(), rw_counter_end_of_step + cb.curr.state.reversible_write_counter.expr(), ); @@ -1559,10 +1598,10 @@ impl CommonErrorGadget { step: &ExecStep, rw_offset: usize, ) -> Result { - self.rw_counter_end_of_reversion.assign( + self.rw_counter_end_of_reversion.assign_u64( region, offset, - Value::known(F::from(call.rw_counter_end_of_reversion as u64)), + call.rw_counter_end_of_reversion as u64, )?; self.restore_context .assign(region, offset, block, call, step, rw_offset)?; @@ -1632,12 +1671,13 @@ impl WordByteCapGadget { self.lt_cap.expr() } - pub(crate) fn original_ref(&self) -> &Word { + // orginal ref of word limbs + pub(crate) fn original_ref(&self) -> &Word32Cell { self.word.original_ref() } - pub(crate) fn original_word(&self) -> Expression { - self.word.original_word() + pub(crate) fn original_word(&self) -> Word32Cell { + self.word.original.clone() } pub(crate) fn overflow(&self) -> Expression { @@ -1656,7 +1696,7 @@ impl WordByteCapGadget { /// Check if the passed in word is within the specified byte range (not overflow). #[derive(Clone, Debug)] pub(crate) struct WordByteRangeGadget { - original: Word, + original: Word32Cell, not_overflow: IsZeroGadget, } @@ -1664,8 +1704,8 @@ impl WordByteRangeGadget { pub(crate) fn construct(cb: &mut EVMConstraintBuilder) -> Self { debug_assert!(VALID_BYTES < 32); - let original = cb.query_word_rlc(); - let not_overflow = IsZeroGadget::construct(cb, sum::expr(&original.cells[VALID_BYTES..])); + let original = cb.query_word32(); + let not_overflow = IsZeroGadget::construct(cb, sum::expr(&original.limbs[VALID_BYTES..])); Self { original, @@ -1680,8 +1720,7 @@ impl WordByteRangeGadget { offset: usize, original: U256, ) -> Result { - self.original - .assign(region, offset, Some(original.to_le_bytes()))?; + self.original.assign_u256(region, offset, original)?; let overflow_hi = original.to_le_bytes()[VALID_BYTES..] .iter() @@ -1692,12 +1731,12 @@ impl WordByteRangeGadget { Ok(overflow_hi == 0) } - pub(crate) fn original_ref(&self) -> &Word { + pub(crate) fn original_ref(&self) -> &Word32Cell { &self.original } - pub(crate) fn original_word(&self) -> Expression { - self.original.expr() + pub(crate) fn original(&self) -> Word> { + self.original.to_word() } pub(crate) fn overflow(&self) -> Expression { @@ -1705,7 +1744,7 @@ impl WordByteRangeGadget { } pub(crate) fn valid_value(&self) -> Expression { - from_bytes::expr(&self.original.cells[..VALID_BYTES]) + from_bytes::expr(&self.original.limbs[..VALID_BYTES]) } pub(crate) fn not_overflow(&self) -> Expression { @@ -1731,12 +1770,12 @@ impl CommonReturnDataCopyGadget { return_data_length: Expression, is_overflow: Expression, ) -> Self { - let data_offset = cb.query_word_rlc(); - let size_word = cb.query_word_rlc(); - let remainder_end = cb.query_word_rlc(); + let data_offset = cb.query_word32(); + let size_word = cb.query_word32(); + let remainder_end = cb.query_word32(); // Check if `data_offset` is Uint64 overflow. - let data_offset_larger_u64 = sum::expr(&data_offset.cells[N_BYTES_U64..]); + let data_offset_larger_u64 = sum::expr(&data_offset.limbs[N_BYTES_U64..]); let is_data_offset_within_u64 = IsZeroGadget::construct(cb, data_offset_larger_u64); let sum: AddWordsGadget = @@ -1745,14 +1784,14 @@ impl CommonReturnDataCopyGadget { // Need to check if `data_offset + size` is U256 overflow via `AddWordsGadget` carry. If // yes, it should be also an error of return data out of bound. let is_end_u256_overflow = sum.carry().as_ref().unwrap(); - let remainder_end_larger_u64 = sum::expr(&remainder_end.cells[N_BYTES_U64..]); + let remainder_end_larger_u64 = sum::expr(&remainder_end.limbs[N_BYTES_U64..]); let is_remainder_end_within_u64 = IsZeroGadget::construct(cb, remainder_end_larger_u64); // check if `remainder_end` exceeds return data length. let is_remainder_end_exceed_len = LtGadget::construct( cb, return_data_length.expr(), - from_bytes::expr(&remainder_end.cells[..N_BYTES_U64]), + from_bytes::expr(&remainder_end.limbs[..N_BYTES_U64]), ); // enusre it is expected overflow condition. @@ -1780,12 +1819,12 @@ impl CommonReturnDataCopyGadget { } /// the first addend is data_offset - pub(crate) fn data_offset(&self) -> &Word { + pub(crate) fn data_offset(&self) -> &Word32Cell { &self.sum.addends()[0] } /// the second added is size - pub(crate) fn size(&self) -> &Word { + pub(crate) fn size(&self) -> &Word32Cell { &self.sum.addends()[1] } diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_access_list.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_access_list.rs index 5b686e0fe8..adc8dd7340 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_access_list.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_access_list.rs @@ -6,7 +6,7 @@ use crate::{ not, or, select, }, table::TxFieldTag, - util::Expr, + util::{word::Word, Expr}, witness::Transaction, }; use bus_mapping::circuit_input_builder::CopyDataType; @@ -57,9 +57,9 @@ impl TxAccessListGadget { cb.condition(not::expr(is_address_len_zero.expr()), |cb| { // Let copy-circuit to write the tx-table's access list addresses into rw-table. cb.copy_table_lookup( - tx_id.expr(), + Word::from_lo_unchecked(tx_id.expr()), CopyDataType::AccessListAddresses.expr(), - tx_id.expr(), + Word::from_lo_unchecked(tx_id.expr()), CopyDataType::AccessListAddresses.expr(), // Access list address index starts from 1 in tx-table. 1.expr(), @@ -73,9 +73,9 @@ impl TxAccessListGadget { cb.condition(not::expr(is_storage_key_len_zero.expr()), |cb| { // Let copy-circuit to write the tx-table's access list storage keys into rw-table. cb.copy_table_lookup( - tx_id.expr(), + Word::from_lo_unchecked(tx_id.expr()), CopyDataType::AccessListStorageKeys.expr(), - tx_id.expr(), + Word::from_lo_unchecked(tx_id.expr()), CopyDataType::AccessListStorageKeys.expr(), // Access list storage key index starts from 0 in tx-table. 0.expr(), diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip1559.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip1559.rs index 183c5aeb09..19c8611118 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip1559.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip1559.rs @@ -15,8 +15,9 @@ use crate::{ witness::Transaction, }, table::TxFieldTag, + util::word::{Word32Cell, WordExpr}, }; -use eth_types::{geth_types::TxType, Field, ToLittleEndian, U256}; +use eth_types::{geth_types::TxType, Field, U256}; use halo2_proofs::plonk::{Error, Expression}; /// Transaction EIP-1559 gadget to check sender balance before transfer @@ -24,9 +25,9 @@ use halo2_proofs::plonk::{Error, Expression}; pub(crate) struct TxEip1559Gadget { is_eip1559_tx: IsEqualGadget, // MaxFeePerGas - gas_fee_cap: Word, + gas_fee_cap: Word32Cell, // MaxPriorityFeePerGas - gas_tip_cap: Word, + gas_tip_cap: Word32Cell, mul_gas_fee_cap_by_gas: MulWordByU64Gadget, balance_check: AddWordsGadget, // Error condition @@ -43,15 +44,26 @@ impl TxEip1559Gadget { tx_id: Expression, tx_type: Expression, tx_gas: Expression, - tx_l1_fee: &Word, - value: &Word, - sender_balance: &Word, + tx_l1_fee: &Word32Cell, + value: &Word32Cell, + sender_balance: &Word32Cell, ) -> Self { let is_eip1559_tx = IsEqualGadget::construct(cb, tx_type, (TxType::Eip1559 as u64).expr()); - let [gas_fee_cap, gas_tip_cap] = - [TxFieldTag::MaxFeePerGas, TxFieldTag::MaxPriorityFeePerGas] - .map(|field_tag| cb.tx_context_as_word(tx_id.expr(), field_tag, None)); + let [gas_fee_cap, gas_tip_cap] = [cb.query_word32(), cb.query_word32()]; + let gas_fee_cap_rlc = cb.word_rlc(gas_fee_cap.limbs.clone().map(|l| l.expr())); + let gas_tip_cap_rlc = cb.word_rlc(gas_tip_cap.limbs.clone().map(|cell| cell.expr())); + for (field_tag, value) in [ + (TxFieldTag::MaxFeePerGas, gas_fee_cap_rlc), + (TxFieldTag::MaxPriorityFeePerGas, gas_tip_cap_rlc), + ] { + cb.tx_context_lookup( + tx_id.expr(), + field_tag, + None, + Word::from_lo_unchecked(value), + ); + } let ( mul_gas_fee_cap_by_gas, @@ -62,7 +74,7 @@ impl TxEip1559Gadget { let mul_gas_fee_cap_by_gas = MulWordByU64Gadget::construct(cb, gas_fee_cap.clone(), tx_gas); - let min_balance = cb.query_word_rlc(); + let min_balance = cb.query_word32(); let balance_check = AddWordsGadget::construct( cb, [ @@ -73,9 +85,10 @@ impl TxEip1559Gadget { min_balance.clone(), ); - let is_insufficient_balance = LtWordGadget::construct(cb, sender_balance, &min_balance); + let is_insufficient_balance = + LtWordGadget::construct(cb, &sender_balance.to_word(), &min_balance.to_word()); let gas_fee_cap_lt_gas_tip_cap = - LtWordGadget::construct(cb, &gas_fee_cap, &gas_tip_cap); + LtWordGadget::construct(cb, &gas_fee_cap.to_word(), &gas_tip_cap.to_word()); cb.require_zero( "Sender balance must be sufficient, and gas_fee_cap >= gas_tip_cap", @@ -119,12 +132,9 @@ impl TxEip1559Gadget { F::from(TxType::Eip1559 as u64), )?; self.gas_fee_cap - .assign(region, offset, Some(tx.max_fee_per_gas.to_le_bytes()))?; - self.gas_tip_cap.assign( - region, - offset, - Some(tx.max_priority_fee_per_gas.to_le_bytes()), - )?; + .assign_u256(region, offset, tx.max_fee_per_gas)?; + self.gas_tip_cap + .assign_u256(region, offset, tx.max_priority_fee_per_gas)?; let mul_gas_fee_cap_by_gas = tx.max_fee_per_gas * tx.gas; self.mul_gas_fee_cap_by_gas.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs new file mode 100644 index 0000000000..fa58a9bc8a --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs @@ -0,0 +1,130 @@ +use super::{CachedRegion, Cell}; +use crate::{ + evm_circuit::util::{ + constraint_builder::EVMConstraintBuilder, math_gadget::IsEqualGadget, select, + }, + table::TxFieldTag, + util::{word::Word, Expr}, + witness::Transaction, +}; +use bus_mapping::circuit_input_builder::CopyDataType; +use eth_types::{ + evm_types::GasCost, + geth_types::{access_list_size, TxType}, + Field, +}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; + +/// Transaction EIP-2930 gadget to handle optional access-list +#[derive(Clone, Debug)] +pub(crate) struct TxEip2930Gadget { + is_eip2930_tx: IsEqualGadget, + access_list_address_len: Cell, + access_list_storage_key_len: Cell, +} + +impl TxEip2930Gadget { + pub(crate) fn construct( + cb: &mut EVMConstraintBuilder, + tx_id: Expression, + tx_type: Expression, + ) -> Self { + let is_eip2930_tx = IsEqualGadget::construct(cb, tx_type, (TxType::Eip2930 as u64).expr()); + + let [access_list_address_len, access_list_storage_key_len] = + cb.condition(is_eip2930_tx.expr(), |cb| { + let [address_len, storage_key_len] = [ + TxFieldTag::AccessListAddressesLen, + TxFieldTag::AccessListStorageKeysLen, + ] + .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); + + // Let copy-circuit to write the tx-table's access list addresses into rw-table. + let tx_id = Word::from_lo_unchecked(tx_id.expr()); + cb.copy_table_lookup( + tx_id.clone(), + CopyDataType::AccessListAddresses.expr(), + tx_id.clone(), + CopyDataType::AccessListAddresses.expr(), + 0.expr(), + address_len.expr(), + 0.expr(), + address_len.expr(), + 0.expr(), + // TODO + 0.expr(), + ); + + // Let copy-circuit to write the tx-table's access list storage keys into rw-table. + cb.copy_table_lookup( + tx_id.clone(), + CopyDataType::AccessListStorageKeys.expr(), + tx_id, + CopyDataType::AccessListStorageKeys.expr(), + 0.expr(), + storage_key_len.expr(), + 0.expr(), + storage_key_len.expr(), + 0.expr(), + // TODO + 0.expr(), + ); + + [address_len, storage_key_len] + }); + + Self { + is_eip2930_tx, + access_list_address_len, + access_list_storage_key_len, + } + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + tx: &Transaction, + ) -> Result<(), Error> { + self.is_eip2930_tx.assign( + region, + offset, + F::from(tx.tx_type as u64), + F::from(TxType::Eip2930 as u64), + )?; + + let (access_list_address_len, access_list_storage_key_len) = + access_list_size(&tx.access_list); + + self.access_list_address_len.assign( + region, + offset, + Value::known(F::from(access_list_address_len)), + )?; + self.access_list_storage_key_len.assign( + region, + offset, + Value::known(F::from(access_list_storage_key_len)), + )?; + + Ok(()) + } + + pub(crate) fn gas_cost(&self) -> Expression { + select::expr( + self.is_eip2930_tx.expr(), + self.access_list_address_len.expr() * GasCost::ACCESS_LIST_PER_ADDRESS.expr() + + self.access_list_storage_key_len.expr() + * GasCost::ACCESS_LIST_PER_STORAGE_KEY.expr(), + 0.expr(), + ) + } + + pub(crate) fn rw_delta(&self) -> Expression { + // TODO + 0.expr() + } +} diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs index 78c4e935e2..aaae869a90 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs @@ -4,16 +4,19 @@ use crate::{ param::N_BYTES_U64, util::{ constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, U64Word, Word, + from_bytes, }, }, - util::Expr, + util::{ + word::{Word, Word32, Word32Cell, WordExpr}, + Expr, + }, }; use bus_mapping::{ circuit_input_builder::{TxL1Fee, TX_L1_COMMIT_EXTRA_COST, TX_L1_FEE_PRECISION}, l2_predeployed::l1_gas_price_oracle, }; -use eth_types::{Field, ToLittleEndian, ToScalar, U256}; +use eth_types::{Field, ToLittleEndian, ToScalar, Word as U256Word}; use halo2_proofs::plonk::{Error, Expression}; /// Transaction L1 fee gadget for L1GasPriceOracle contract @@ -22,15 +25,15 @@ pub(crate) struct TxL1FeeGadget { /// Transaction L1 fee /// It should be an Uint64, but it's also used to check sender balance which /// needs to be added as a Word. - tx_l1_fee_word: Word, + tx_l1_fee_word: Word32Cell, /// Remainder when calculating L1 fee - remainder_word: U64Word, + remainder_word: Word32Cell, /// Current value of L1 base fee - base_fee_word: U64Word, + base_fee_word: Word32Cell, /// Current value of L1 fee overhead - fee_overhead_word: U64Word, + fee_overhead_word: Word32Cell, /// Current value of L1 fee scalar - fee_scalar_word: U64Word, + fee_scalar_word: Word32Cell, /// Committed value of L1 base fee base_fee_committed: Cell, /// Committed value of L1 fee overhead @@ -52,37 +55,37 @@ impl TxL1FeeGadget { )); let [base_fee_slot, overhead_slot, scalar_slot] = [ - &l1_gas_price_oracle::BASE_FEE_SLOT, - &l1_gas_price_oracle::OVERHEAD_SLOT, - &l1_gas_price_oracle::SCALAR_SLOT, + *l1_gas_price_oracle::BASE_FEE_SLOT, + *l1_gas_price_oracle::OVERHEAD_SLOT, + *l1_gas_price_oracle::SCALAR_SLOT, ] - .map(|slot| cb.word_rlc(slot.to_le_bytes().map(|b| b.expr()))); + .map(|slot| slot); // Read L1 base fee - cb.account_storage_read( + cb.account_storage_read_address( l1_fee_address.expr(), - base_fee_slot, - this.base_fee_word.expr(), + Word32::new(base_fee_slot.to_le_bytes()).to_expr().to_word(), + this.base_fee_word.to_word(), tx_id.expr(), - this.base_fee_committed.expr(), + Word::from_lo_unchecked(this.base_fee_committed.expr()), ); // Read L1 fee overhead - cb.account_storage_read( + cb.account_storage_read_address( l1_fee_address.expr(), - overhead_slot, - this.fee_overhead_word.expr(), + Word32::new(overhead_slot.to_le_bytes()).to_expr().to_word(), + this.fee_overhead_word.to_word(), tx_id.expr(), - this.fee_overhead_committed.expr(), + Word::from_lo_unchecked(this.fee_overhead_committed.expr()), ); // Read L1 fee scalar - cb.account_storage_read( + cb.account_storage_read_address( l1_fee_address, - scalar_slot, - this.fee_scalar_word.expr(), + Word32::new(scalar_slot.to_le_bytes()).to_expr().to_word(), + this.fee_scalar_word.to_word(), tx_id, - this.fee_scalar_committed.expr(), + Word::from_lo_unchecked(this.fee_scalar_committed.expr()), ); this @@ -98,15 +101,15 @@ impl TxL1FeeGadget { ) -> Result<(), Error> { let (tx_l1_fee, remainder) = l1_fee.tx_l1_fee(tx_data_gas_cost); self.tx_l1_fee_word - .assign(region, offset, Some(U256::from(tx_l1_fee).to_le_bytes()))?; + .assign_u256(region, offset, U256Word::from(tx_l1_fee))?; self.remainder_word - .assign(region, offset, Some(remainder.to_le_bytes()))?; + .assign_u256(region, offset, U256Word::from(remainder))?; self.base_fee_word - .assign(region, offset, Some(l1_fee.base_fee.to_le_bytes()))?; + .assign_u256(region, offset, U256Word::from(l1_fee.base_fee))?; self.fee_overhead_word - .assign(region, offset, Some(l1_fee.fee_overhead.to_le_bytes()))?; + .assign_u256(region, offset, U256Word::from(l1_fee.fee_overhead))?; self.fee_scalar_word - .assign(region, offset, Some(l1_fee.fee_scalar.to_le_bytes()))?; + .assign_u256(region, offset, U256Word::from(l1_fee.fee_scalar))?; self.base_fee_committed.assign( region, offset, @@ -134,29 +137,29 @@ impl TxL1FeeGadget { } pub(crate) fn tx_l1_fee(&self) -> Expression { - from_bytes::expr(&self.tx_l1_fee_word.cells[..N_BYTES_U64]) + from_bytes::expr(&self.tx_l1_fee_word.limbs[..N_BYTES_U64]) } - pub(crate) fn tx_l1_fee_word(&self) -> &Word { + pub(crate) fn tx_l1_fee_word(&self) -> &Word32Cell { &self.tx_l1_fee_word } fn raw_construct(cb: &mut EVMConstraintBuilder, tx_data_gas_cost: Expression) -> Self { - let tx_l1_fee_word = cb.query_word_rlc(); - let remainder_word = cb.query_word_rlc(); + let tx_l1_fee_word = cb.query_word32(); + let remainder_word = cb.query_word32(); - let base_fee_word = cb.query_word_rlc(); - let fee_overhead_word = cb.query_word_rlc(); - let fee_scalar_word = cb.query_word_rlc(); + let base_fee_word = cb.query_word32(); + let fee_overhead_word = cb.query_word32(); + let fee_scalar_word = cb.query_word32(); - let tx_l1_fee = from_bytes::expr(&tx_l1_fee_word.cells[..N_BYTES_U64]); + let tx_l1_fee = from_bytes::expr(&tx_l1_fee_word.limbs[..N_BYTES_U64]); let [remainder, base_fee, fee_overhead, fee_scalar] = [ &remainder_word, &base_fee_word, &fee_overhead_word, &fee_scalar_word, ] - .map(|word| from_bytes::expr(&word.cells[..N_BYTES_U64])); + .map(|word| from_bytes::expr(&word.limbs[..N_BYTES_U64])); // let tx_l1_gas = tx_data_gas_cost + TX_L1_COMMIT_EXTRA_COST.expr() + fee_overhead; diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs index f85387023c..13a1f55463 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs @@ -1,13 +1,16 @@ -use super::{CachedRegion, Cell}; +use super::CachedRegion; use crate::{ evm_circuit::util::{ and, constraint_builder::EVMConstraintBuilder, - math_gadget::{IsEqualGadget, IsZeroGadget}, + math_gadget::{IsEqualGadget, IsZeroWordGadget}, select, }, table::AccountFieldTag, - util::Expr, + util::{ + word::{Word, WordCell, WordExpr}, + Expr, + }, }; use eth_types::{geth_types::TxType, Field, U256}; use halo2_proofs::plonk::{Error, Expression}; @@ -18,28 +21,29 @@ pub(crate) struct TxL1MsgGadget { /// tx is l1 msg tx tx_is_l1msg: IsEqualGadget, /// caller is empty - is_caller_empty: IsZeroGadget, - caller_codehash: Cell, + is_caller_empty: IsZeroWordGadget>, + //caller_codehash: Cell, + caller_codehash: WordCell, } impl TxL1MsgGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, tx_type: Expression, - caller_address: Expression, + caller_address: Word>, ) -> Self { let tx_is_l1msg = IsEqualGadget::construct(cb, tx_type.expr(), (TxType::L1Msg as u64).expr()); - let caller_codehash = cb.query_cell_phase2(); + let caller_codehash = cb.query_word_unchecked(); let is_caller_empty = cb.annotation("is caller address not existed", |cb| { - IsZeroGadget::construct(cb, caller_codehash.expr()) + IsZeroWordGadget::construct(cb, &caller_codehash) }); cb.condition(tx_is_l1msg.expr(), |cb| { cb.account_read( - caller_address.expr(), + caller_address.clone(), AccountFieldTag::CodeHash, - caller_codehash.expr(), + caller_codehash.to_word(), ); }); @@ -47,18 +51,19 @@ impl TxL1MsgGadget { and::expr([tx_is_l1msg.expr(), is_caller_empty.expr()]), |cb| { cb.account_write( - caller_address.expr(), + caller_address.to_word(), AccountFieldTag::CodeHash, - cb.empty_code_hash_rlc(), - 0.expr(), + cb.empty_code_hash(), + Word::zero(), None, ); #[cfg(feature = "scroll")] cb.account_write( - caller_address.expr(), + caller_address.to_word(), AccountFieldTag::KeccakCodeHash, - cb.empty_keccak_hash_rlc(), - 0.expr(), + //cb.empty_keccak_hash_rlc(), + cb.empty_code_hash(), + Word::zero(), None, ); }, @@ -84,10 +89,11 @@ impl TxL1MsgGadget { F::from(tx_type as u64), F::from(TxType::L1Msg as u64), )?; - let code_hash = region.code_hash(code_hash); - self.caller_codehash.assign(region, offset, code_hash)?; + + self.caller_codehash + .assign_u256(region, offset, code_hash)?; self.is_caller_empty - .assign_value(region, offset, code_hash)?; + .assign_u256(region, offset, code_hash)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 2279a0f9cf..4ddfad1118 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -3,19 +3,23 @@ use crate::{ param::STACK_CAPACITY, step::{ExecutionState, Step}, table::{FixedTableTag, Lookup, RwValues}, - util::{Cell, RandomLinearCombination, Word}, + util::{Cell, RandomLinearCombination}, }, table::{ AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, RwTableTag, TxContextFieldTag, TxLogFieldTag, TxReceiptFieldTag, }, - util::{build_tx_log_expression, Challenges, Expr}, + util::{ + build_tx_log_expression, + word::{Word, Word32, Word32Cell, WordCell, WordExpr}, + Challenges, Expr, + }, }; use bus_mapping::{ state_db::EMPTY_CODE_HASH_LE, util::{KECCAK_CODE_HASH_EMPTY, POSEIDON_CODE_HASH_EMPTY}, }; -use eth_types::{Field, ToLittleEndian, ToScalar, ToWord}; +use eth_types::{Field, ToLittleEndian, ToWord}; use gadgets::util::{and, not}; use halo2_proofs::{ circuit::Value, @@ -26,7 +30,10 @@ use halo2_proofs::{ }; use itertools::Itertools; -use super::{rlc, CachedRegion, CellType, StoredExpression}; +use super::{ + address_word_to_expr, rlc, AccountAddress, CachedRegion, CellType, MemoryAddress, + StoredExpression, U64Cell, +}; // Max degree allowed in all expressions passing through the ConstraintBuilder. // It aims to cap `extended_k` to 2, which allows constraint degree to 2^2+1, @@ -54,7 +61,7 @@ pub(crate) struct StepStateTransition { pub(crate) call_id: Transition>, pub(crate) is_root: Transition>, pub(crate) is_create: Transition>, - pub(crate) code_hash: Transition>, + pub(crate) code_hash: Transition>>, pub(crate) program_counter: Transition>, pub(crate) stack_pointer: Transition>, pub(crate) gas_left: Transition>, @@ -181,6 +188,22 @@ pub(crate) trait ConstrainBuilderCommon { self.add_constraint(name, constraint); } + fn require_zero_word(&mut self, name: &'static str, word: Word>) { + self.require_equal_word(name, word, Word::zero()); + } + + fn require_equal_word( + &mut self, + name: &'static str, + lhs: Word>, + rhs: Word>, + ) { + let (lhs_lo, lhs_hi) = lhs.to_lo_hi(); + let (rhs_lo, rhs_hi) = rhs.to_lo_hi(); + self.add_constraint(name, lhs_lo - rhs_lo); + self.add_constraint(name, lhs_hi - rhs_hi); + } + fn require_equal(&mut self, name: &'static str, lhs: Expression, rhs: Expression) { self.add_constraint(name, lhs - rhs); } @@ -452,10 +475,37 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.query_cells(CellType::LookupByte, count) } + pub(crate) fn query_account_address(&mut self) -> AccountAddress { + AccountAddress::::new(self.query_bytes()) + } + + pub(crate) fn query_memory_address(&mut self) -> MemoryAddress { + MemoryAddress::::new(self.query_bytes()) + } + + // default query_word is 2 limbs. Each limb is not guaranteed to be 128 bits. + pub fn query_word_unchecked(&mut self) -> WordCell { + Word::new( + self.query_cells(CellType::StoragePhase1, 2) + .try_into() + .unwrap(), + ) + } + + // query_word32 each limb is 8 bits, and any conversion to smaller limbs inherits the type + // check. + pub(crate) fn query_word32(&mut self) -> Word32Cell { + Word32::new(self.query_bytes()) + } + pub(crate) fn query_cell(&mut self) -> Cell { self.query_cell_with_type(CellType::StoragePhase1) } + pub(crate) fn query_u64(&mut self) -> U64Cell { + U64Cell::new(self.query_bytes()) + } + #[allow(clippy::let_and_return)] pub(crate) fn query_cell_phase2(&mut self) -> Cell { let cell = self.query_cell_with_type(CellType::StoragePhase2); @@ -508,12 +558,23 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.word_rlc(bytes.map(|byte| byte.expr())) } - pub(crate) fn empty_code_hash_rlc(&self) -> Expression { + pub(crate) fn empty_keccak_hash(&self) -> Word> { + let bytes = KECCAK_CODE_HASH_EMPTY.to_word().to_le_bytes(); + Word32::new(bytes.map(|l| l.expr())).to_word() + } + + // poseidon codehash if enalbe scroll or poseidon-codehash, else keccak_hash. + pub(crate) fn empty_code_hash(&self) -> Word> { if cfg!(feature = "poseidon-codehash") { - let codehash = POSEIDON_CODE_HASH_EMPTY.to_word().to_scalar().unwrap(); - Expression::Constant(codehash) + Word32::new( + POSEIDON_CODE_HASH_EMPTY + .to_word() + .to_le_bytes() + .map(|byte| byte.expr()), + ) + .to_word() } else { - self.word_rlc((*EMPTY_CODE_HASH_LE).map(|byte| byte.expr())) + Word32::new(EMPTY_CODE_HASH_LE.map(|byte| byte.expr())).to_word() } } @@ -561,11 +622,29 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { }; } + macro_rules! constrain_word { + ($name:tt) => { + match step_state_transition.$name { + Transition::Same => self.require_equal_word( + concat!("State transition (same) constraint of ", stringify!($name)), + self.next.state.$name.to_word(), + self.curr.state.$name.to_word(), + ), + Transition::To(to) => self.require_equal_word( + concat!("State transition (to) constraint of ", stringify!($name)), + self.next.state.$name.to_word(), + to, + ), + _ => {} + } + }; + } + constrain!(rw_counter); constrain!(call_id); constrain!(is_root); constrain!(is_create); - constrain!(code_hash); + constrain_word!(code_hash); constrain!(program_counter); constrain!(stack_pointer); constrain!(gas_left); @@ -663,7 +742,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.add_lookup( "Opcode lookup", Lookup::Bytecode { - hash: self.curr.state.code_hash.expr(), + hash: self.curr.state.code_hash.to_word(), tag: BytecodeFieldTag::Byte.expr(), index, is_code: 1.expr(), @@ -678,7 +757,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn bytecode_lookup( &mut self, - code_hash: Expression, + code_hash: Word>, index: Expression, is_code: Expression, value: Expression, @@ -697,7 +776,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ) } - pub(crate) fn bytecode_length(&mut self, code_hash: Expression, value: Expression) { + pub(crate) fn bytecode_length(&mut self, code_hash: Word>, value: Expression) { self.add_lookup( "Bytecode (length)", Lookup::Bytecode { @@ -720,7 +799,8 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { index: Option>, ) -> Cell { let cell = self.query_cell(); - self.tx_context_lookup(id, field_tag, index, cell.expr()); + self.tx_context_lookup(id, field_tag, index, Word::from_lo_unchecked(cell.expr())); + // self.tx_context_lookup(id, field_tag, index, cell.expr()); cell } @@ -729,9 +809,20 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { id: Expression, field_tag: TxContextFieldTag, index: Option>, - ) -> Word { - let word = self.query_word_rlc(); - self.tx_context_lookup(id, field_tag, index, word.expr()); + ) -> WordCell { + let word = self.query_word_unchecked(); + self.tx_context_lookup(id, field_tag, index, word.to_word()); + word + } + + pub(crate) fn tx_context_as_word32( + &mut self, + id: Expression, + field_tag: TxContextFieldTag, + index: Option>, + ) -> Word32Cell { + let word = self.query_word32(); + self.tx_context_lookup(id, field_tag, index, word.to_word()); word } @@ -740,7 +831,8 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { id: Expression, field_tag: TxContextFieldTag, index: Option>, - value: Expression, + value: Word>, + //value: Expression, ) { self.add_lookup( "Tx lookup", @@ -757,14 +849,15 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn block_lookup( &mut self, tag: Expression, - number: Expression, + number: Option>, + //val: Word>, val: Expression, ) { self.add_lookup( "Block lookup", Lookup::Block { field_tag: tag, - number, + number: number.unwrap_or_else(|| 0.expr()), value: val, }, ); @@ -849,22 +942,42 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { reversion_info.rw_counter_of_reversion(reversible_write_counter_inc_selector), true.expr(), tag, - RwValues { - value_prev: values.value, - value: values.value_prev, - ..values - }, + values.revert_value(), ) }); } } // Access list + pub(crate) fn account_access_list_write_unchecked( + &mut self, + tx_id: Expression, + account_address: Word>, + value: Expression, + value_prev: Expression, + reversion_info: Option<&mut ReversionInfo>, + ) { + self.reversible_write( + "TxAccessListAccount write", + RwTableTag::TxAccessListAccount, + RwValues::new( + tx_id, + address_word_to_expr(account_address), + 0.expr(), + Word::zero(), + Word::from_lo_unchecked(value), + Word::from_lo_unchecked(value_prev), + Word::zero(), + Word::zero(), + ), + reversion_info, + ); + } pub(crate) fn account_access_list_write( &mut self, tx_id: Expression, - account_address: Expression, + account_address: Word>, value: Expression, value_prev: Expression, reversion_info: Option<&mut ReversionInfo>, @@ -874,13 +987,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { RwTableTag::TxAccessListAccount, RwValues::new( tx_id, - account_address, - 0.expr(), - 0.expr(), - value, - value_prev, - 0.expr(), + address_word_to_expr(account_address), 0.expr(), + Word::zero(), + Word::from_lo_unchecked(value), + Word::from_lo_unchecked(value_prev), + Word::zero(), + Word::zero(), ), reversion_info, ); @@ -889,7 +1002,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn account_access_list_read( &mut self, tx_id: Expression, - account_address: Expression, + account_address: Word>, value: Expression, ) { self.rw_lookup( @@ -898,13 +1011,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { RwTableTag::TxAccessListAccount, RwValues::new( tx_id, - account_address, - 0.expr(), - 0.expr(), - value.clone(), - value, - 0.expr(), + address_word_to_expr(account_address), 0.expr(), + Word::zero(), + Word::from_lo_unchecked(value.clone()), + Word::from_lo_unchecked(value), + Word::zero(), + Word::zero(), ), ); } @@ -912,10 +1025,10 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn account_storage_access_list_write( &mut self, tx_id: Expression, - account_address: Expression, - storage_key: Expression, - value: Expression, - value_prev: Expression, + account_address: Word>, + storage_key: Word>, + value: Word>, + value_prev: Word>, reversion_info: Option<&mut ReversionInfo>, ) { self.reversible_write( @@ -923,13 +1036,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { RwTableTag::TxAccessListAccountStorage, RwValues::new( tx_id, - account_address, + address_word_to_expr(account_address), 0.expr(), storage_key, value, value_prev, - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), ), reversion_info, ); @@ -938,9 +1051,9 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn account_storage_access_list_read( &mut self, tx_id: Expression, - account_address: Expression, - storage_key: Expression, - value: Expression, + account_address: Word>, + storage_key: Word>, + value: Word>, ) { self.rw_lookup( "TxAccessListAccountStorage read", @@ -948,20 +1061,20 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { RwTableTag::TxAccessListAccountStorage, RwValues::new( tx_id, - account_address, + address_word_to_expr(account_address), 0.expr(), storage_key, value.clone(), value, - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), ), ); } // Tx Refund - pub(crate) fn tx_refund_read(&mut self, tx_id: Expression, value: Expression) { + pub(crate) fn tx_refund_read(&mut self, tx_id: Expression, value: Word>) { self.rw_lookup( "TxRefund read", false.expr(), @@ -970,11 +1083,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { tx_id, 0.expr(), 0.expr(), - 0.expr(), + Word::zero(), value.clone(), value, - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), ), ); } @@ -982,8 +1095,8 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn tx_refund_write( &mut self, tx_id: Expression, - value: Expression, - value_prev: Expression, + value: Word>, + value_prev: Word>, reversion_info: Option<&mut ReversionInfo>, ) { self.reversible_write( @@ -993,11 +1106,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { tx_id, 0.expr(), 0.expr(), - 0.expr(), + Word::zero(), value, value_prev, - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), ), reversion_info, ); @@ -1007,9 +1120,9 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn account_read( &mut self, - account_address: Expression, + account_address: Word>, field_tag: AccountFieldTag, - value: Expression, + value: Word>, ) { self.rw_lookup( "Account read", @@ -1017,23 +1130,23 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { RwTableTag::Account, RwValues::new( 0.expr(), - account_address, + address_word_to_expr(account_address), field_tag.expr(), - 0.expr(), + Word::zero(), value.clone(), value, - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), ), ); } pub(crate) fn account_write( &mut self, - account_address: Expression, + account_address: Word>, field_tag: AccountFieldTag, - value: Expression, - value_prev: Expression, + value: Word>, + value_prev: Word>, reversion_info: Option<&mut ReversionInfo>, ) { self.reversible_write( @@ -1041,13 +1154,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { RwTableTag::Account, RwValues::new( 0.expr(), - account_address, + address_word_to_expr(account_address), field_tag.expr(), - 0.expr(), + Word::zero(), value, value_prev, - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), ), reversion_info, ); @@ -1056,12 +1169,38 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { // Account Storage pub(crate) fn account_storage_read( + &mut self, + account_address: Word>, + key: Word>, + value: Word>, + tx_id: Expression, + committed_value: Word>, + ) { + self.rw_lookup( + "account_storage_read", + false.expr(), + RwTableTag::AccountStorage, + RwValues::new( + tx_id, + address_word_to_expr(account_address), + AccountFieldTag::CodeHash.expr(), + key, + value.clone(), + value, + Word::zero(), + committed_value, + ), + ); + } + + // this method take use of `account_address` Expression as parameter + pub(crate) fn account_storage_read_address( &mut self, account_address: Expression, - key: Expression, - value: Expression, + key: Word>, + value: Word>, tx_id: Expression, - committed_value: Expression, + committed_value: Word>, ) { self.rw_lookup( "account_storage_read", @@ -1074,7 +1213,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { key, value.clone(), value, - 0.expr(), + Word::zero(), committed_value, ), ); @@ -1083,12 +1222,12 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { #[allow(clippy::too_many_arguments)] pub(crate) fn account_storage_write( &mut self, - account_address: Expression, - key: Expression, - value: Expression, - value_prev: Expression, + account_address: Word>, + key: Word>, + value: Word>, + value_prev: Word>, tx_id: Expression, - committed_value: Expression, + committed_value: Word>, reversion_info: Option<&mut ReversionInfo>, ) { self.reversible_write( @@ -1096,12 +1235,12 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { RwTableTag::AccountStorage, RwValues::new( tx_id, - account_address, + address_word_to_expr(account_address), AccountFieldTag::CodeHash.expr(), key, value, value_prev, - 0.expr(), + Word::zero(), committed_value, ), reversion_info, @@ -1120,7 +1259,12 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { _ => CellType::StoragePhase1, }; let cell = self.query_cell_with_type(phase); - self.call_context_lookup(false.expr(), call_id, field_tag, cell.expr()); + self.call_context_lookup_read( + call_id, + field_tag, + Word::from_lo_unchecked(cell.expr()), // lookup read, unchecked is safe + ); + cell } @@ -1128,9 +1272,20 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { &mut self, call_id: Option>, field_tag: CallContextFieldTag, - ) -> Word { - let word = self.query_word_rlc(); - self.call_context_lookup(false.expr(), call_id, field_tag, word.expr()); + ) -> Word32Cell { + let word = self.query_word32(); + self.call_context_lookup(false.expr(), call_id, field_tag, word.to_word()); + word + } + + pub(crate) fn call_context_read_as_word( + &mut self, + call_id: Option>, + field_tag: CallContextFieldTag, + ) -> Word> { + let word = self.query_word_unchecked(); + + self.call_context_lookup_read(call_id, field_tag, word.to_word()); word } @@ -1141,7 +1296,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { rw_counter: Expression, call_id: Option>, field_tag: CallContextFieldTag, - value: Expression, + value: Word>, ) { self.rw_lookup_with_counter( "CallContext lookup", @@ -1152,11 +1307,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), 0.expr(), field_tag.expr(), - 0.expr(), + Word::zero(), value, - 0.expr(), - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), + Word::zero(), ), ); } @@ -1166,7 +1321,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { is_write: Expression, call_id: Option>, field_tag: CallContextFieldTag, - value: Expression, + value: Word>, ) { self.rw_lookup( "CallContext lookup", @@ -1176,15 +1331,33 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), 0.expr(), field_tag.expr(), - 0.expr(), + Word::zero(), value, - 0.expr(), - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), + Word::zero(), ), ); } + pub(crate) fn call_context_lookup_read( + &mut self, + call_id: Option>, + field_tag: CallContextFieldTag, + value: Word>, + ) { + self.call_context_lookup(0.expr(), call_id, field_tag, value) + } + + pub(crate) fn call_context_lookup_write( + &mut self, + call_id: Option>, + field_tag: CallContextFieldTag, + value: Word>, + ) { + self.call_context_lookup(1.expr(), call_id, field_tag, value) + } + fn reversion_info( &mut self, call_id: Option>, @@ -1196,7 +1369,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ] .map(|field_tag| { let cell = self.query_cell(); - self.call_context_lookup(is_write.expr(), call_id.clone(), field_tag, cell.expr()); + self.call_context_lookup( + is_write.expr(), + call_id.clone(), + field_tag, + Word::from_lo_unchecked(cell.expr()), + ); + cell }); @@ -1225,14 +1404,21 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.reversion_info(call_id, true) } + pub(crate) fn reversion_info_write_unchecked( + &mut self, + call_id: Option>, + ) -> ReversionInfo { + self.reversion_info(call_id, true) + } + // Stack - pub(crate) fn stack_pop(&mut self, value: Expression) { + pub(crate) fn stack_pop(&mut self, value: Word>) { self.stack_lookup(false.expr(), self.stack_pointer_offset.clone(), value); self.stack_pointer_offset = self.stack_pointer_offset.clone() + self.condition_expr(); } - pub(crate) fn stack_push(&mut self, value: Expression) { + pub(crate) fn stack_push(&mut self, value: Word>) { self.stack_pointer_offset = self.stack_pointer_offset.clone() - self.condition_expr(); self.stack_lookup(true.expr(), self.stack_pointer_offset.expr(), value); } @@ -1241,7 +1427,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { &mut self, is_write: Expression, stack_pointer_offset: Expression, - value: Expression, + value: Word>, ) { self.rw_lookup( "Stack lookup", @@ -1251,23 +1437,23 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.curr.state.call_id.expr(), self.curr.state.stack_pointer.expr() + stack_pointer_offset, 0.expr(), - 0.expr(), + Word::zero(), value, - 0.expr(), - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), + Word::zero(), ), ); } // Memory - + // TODO: last to update memory lookup as we apply memory word lookup feature pub(crate) fn memory_lookup( &mut self, is_write: Expression, memory_address: Expression, // slot - value: Expression, - value_prev: Expression, + value: Word>, + value_prev: Word>, call_id: Option>, ) { self.rw_lookup( @@ -1278,11 +1464,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), memory_address, 0.expr(), - 0.expr(), + Word::zero(), value, value_prev, - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), ), ); } @@ -1293,7 +1479,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { log_id: Expression, field_tag: TxLogFieldTag, index: Expression, - value: Expression, + value: Word>, ) { self.rw_lookup( "log data lookup", @@ -1303,11 +1489,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { tx_id, build_tx_log_expression(index, field_tag.expr(), log_id), 0.expr(), - 0.expr(), + Word::zero(), value, - 0.expr(), - 0.expr(), - 0.expr(), + Word::zero(), + Word::zero(), + Word::zero(), ), ); } @@ -1329,11 +1515,12 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { tx_id, 0.expr(), tag.expr(), - 0.expr(), - value, - 0.expr(), - 0.expr(), - 0.expr(), + Word::zero(), + // TODO assure range check since write=true also possible + Word::from_lo_unchecked(value), + Word::zero(), + Word::zero(), + Word::zero(), ), ); } @@ -1350,11 +1537,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { id: 0.expr(), address: 0.expr(), field_tag: 0.expr(), - storage_key: 0.expr(), - value: 0.expr(), - value_prev: 0.expr(), - aux1: 0.expr(), - aux2: 0.expr(), + storage_key: Word::zero(), + value: Word::zero(), + value_prev: Word::zero(), + aux1: Word::zero(), + aux2: Word::zero(), }, ); } @@ -1364,9 +1551,9 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { #[allow(clippy::too_many_arguments)] pub(crate) fn copy_table_lookup( &mut self, - src_id: Expression, + src_id: Word>, src_tag: Expression, - dst_id: Expression, + dst_id: Word>, dst_tag: Expression, src_addr: Expression, src_addr_end: Expression, @@ -1492,6 +1679,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { input_rlc: Expression, input_len: Expression, output_rlc: Expression, + output: Word>, ) { self.add_lookup( "keccak lookup", @@ -1499,6 +1687,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { input_rlc, input_len, output_rlc, + output, }, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget.rs index 250797c1bd..d4ed3a6f63 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget.rs @@ -11,7 +11,9 @@ mod cmp_words; mod comparison; mod constant_division; mod is_equal; +mod is_equal_word; mod is_zero; +mod is_zero_word; mod lt; mod lt_word; mod min_max; @@ -35,7 +37,11 @@ pub(crate) use cmp_words::CmpWordsGadget; pub(crate) use comparison::ComparisonGadget; pub(crate) use constant_division::ConstantDivisionGadget; pub(crate) use is_equal::IsEqualGadget; +pub(crate) use is_equal_word::IsEqualWordGadget; + pub(crate) use is_zero::IsZeroGadget; +pub(crate) use is_zero_word::IsZeroWordGadget; + pub(crate) use lt::LtGadget; pub(crate) use lt_word::LtWordGadget; pub(crate) use min_max::MinMaxGadget; diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs index e7bbe5dc8e..e807a627ff 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs @@ -1,14 +1,17 @@ use crate::{ - evm_circuit::util::{ - self, - constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, - math_gadget::*, - CachedRegion, + evm_circuit::{ + param::N_BYTES_WORD, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, + math_gadget::*, + CachedRegion, + }, }, - util::Expr, + util::{word::Word32Cell, Expr}, }; use eth_types::{Field, ToLittleEndian, Word}; +use gadgets::util::sum; use halo2_proofs::plonk::Error; /// Construction of 256-bit word original and absolute values, which is useful @@ -19,23 +22,23 @@ use halo2_proofs::plonk::Error; /// (expressed as an U256 of `2^255`). #[derive(Clone, Debug)] pub(crate) struct AbsWordGadget { - x: util::Word, - x_abs: util::Word, - sum: util::Word, + x: Word32Cell, + x_abs: Word32Cell, + sum: Word32Cell, is_neg: LtGadget, add_words: AddWordsGadget, } impl AbsWordGadget { pub(crate) fn construct(cb: &mut EVMConstraintBuilder) -> Self { - let x = cb.query_word_rlc(); - let x_abs = cb.query_word_rlc(); - let sum = cb.query_word_rlc(); - let x_lo = from_bytes::expr(&x.cells[0..16]); - let x_hi = from_bytes::expr(&x.cells[16..32]); - let x_abs_lo = from_bytes::expr(&x_abs.cells[0..16]); - let x_abs_hi = from_bytes::expr(&x_abs.cells[16..32]); - let is_neg = LtGadget::construct(cb, 127.expr(), x.cells[31].expr()); + let x = cb.query_word32(); + let x_abs = cb.query_word32(); + let sum = cb.query_word32(); + let x_lo = from_bytes::expr(&x.limbs[0..16]); + let x_hi = from_bytes::expr(&x.limbs[16..32]); + let x_abs_lo = from_bytes::expr(&x_abs.limbs[0..16]); + let x_abs_hi = from_bytes::expr(&x_abs.limbs[16..32]); + let is_neg = LtGadget::construct(cb, 127.expr(), x.limbs[31].expr()); cb.add_constraint( "x_abs_lo == x_lo when x >= 0", @@ -51,7 +54,7 @@ impl AbsWordGadget { let add_words = AddWordsGadget::construct(cb, [x.clone(), x_abs.clone()], sum.clone()); cb.add_constraint( "sum == 0 when x < 0", - is_neg.expr() * add_words.sum().expr(), + is_neg.expr() * sum::expr(add_words.sum().to_word_n::().limbs), ); cb.add_constraint( "carry_hi == 1 when x < 0", @@ -74,9 +77,8 @@ impl AbsWordGadget { x: Word, x_abs: Word, ) -> Result<(), Error> { - self.x.assign(region, offset, Some(x.to_le_bytes()))?; - self.x_abs - .assign(region, offset, Some(x_abs.to_le_bytes()))?; + self.x.assign_u256(region, offset, x)?; + self.x_abs.assign_u256(region, offset, x_abs)?; self.is_neg.assign( region, offset, @@ -84,15 +86,15 @@ impl AbsWordGadget { u64::from(x.to_le_bytes()[31]).into(), )?; let sum = x.overflowing_add(x_abs).0; - self.sum.assign(region, offset, Some(sum.to_le_bytes()))?; + self.sum.assign_u256(region, offset, sum)?; self.add_words.assign(region, offset, [x, x_abs], sum) } - pub(crate) fn x(&self) -> &util::Word { + pub(crate) fn x(&self) -> &Word32Cell { &self.x } - pub(crate) fn x_abs(&self) -> &util::Word { + pub(crate) fn x_abs(&self) -> &Word32Cell { &self.x_abs } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs index ac2c26fd6e..a23199d141 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs @@ -2,19 +2,22 @@ use crate::{ evm_circuit::util::{ self, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, pow_of_two_expr, split_u256, sum, CachedRegion, Cell, + pow_of_two_expr, split_u256, sum, CachedRegion, Cell, + }, + util::{ + word::{Word32Cell, WordExpr}, + Expr, }, - util::Expr, }; -use eth_types::{Field, ToLittleEndian, ToScalar, Word}; +use eth_types::{Field, ToScalar, Word}; use halo2_proofs::{circuit::Value, plonk::Error}; /// Construction of 2 256-bit words addition and result, which is useful for /// opcode ADD, SUB and balance operation #[derive(Clone, Debug)] pub(crate) struct AddWordsGadget { - addends: [util::Word; N_ADDENDS], - sum: util::Word, + addends: [Word32Cell; N_ADDENDS], + sum: Word32Cell, carry_lo: Cell, carry_hi: Option>, } @@ -24,8 +27,8 @@ impl { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - addends: [util::Word; N_ADDENDS], - sum: util::Word, + addends: [Word32Cell; N_ADDENDS], + sum: Word32Cell, ) -> Self { let carry_lo = cb.query_cell(); let carry_hi = if CHECK_OVERFLOW { @@ -36,14 +39,14 @@ impl let addends_lo = &addends .iter() - .map(|addend| from_bytes::expr(&addend.cells[..16])) + .map(|addend| addend.to_word().lo()) .collect::>(); let addends_hi = &addends .iter() - .map(|addend| from_bytes::expr(&addend.cells[16..])) + .map(|addend| addend.to_word().hi()) .collect::>(); - let sum_lo = from_bytes::expr(&sum.cells[..16]); - let sum_hi = from_bytes::expr(&sum.cells[16..]); + let sum_lo = sum.to_word().lo(); + let sum_hi = sum.to_word().hi(); cb.require_equal( "sum(addends_lo) == sum_lo + carry_lo ⋅ 2^128", @@ -92,9 +95,9 @@ impl sum: Word, ) -> Result<(), Error> { for (word, value) in self.addends.iter().zip(addends.iter()) { - word.assign(region, offset, Some(value.to_le_bytes()))?; + word.assign_u256(region, offset, *value)?; } - self.sum.assign(region, offset, Some(sum.to_le_bytes()))?; + self.sum.assign_u256(region, offset, sum)?; let (addends_lo, addends_hi): (Vec<_>, Vec<_>) = addends.iter().map(split_u256).unzip(); let (sum_lo, sum_hi) = split_u256(&sum); @@ -133,11 +136,11 @@ impl Ok(()) } - pub(crate) fn addends(&self) -> &[util::Word] { + pub(crate) fn addends(&self) -> &[Word32Cell] { &self.addends } - pub(crate) fn sum(&self) -> &util::Word { + pub(crate) fn sum(&self) -> &Word32Cell { &self.sum } @@ -161,16 +164,16 @@ mod tests { const CHECK_OVERFLOW: bool, > { addwords_gadget: AddWordsGadget, - addends: [util::Word; N_ADDENDS], - sum: util::Word, + addends: [Word32Cell; N_ADDENDS], + sum: Word32Cell, } impl MathGadgetContainer for AddWordsTestContainer { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let addends = [(); N_ADDENDS].map(|_| cb.query_word_rlc()); - let sum = cb.query_word_rlc(); + let addends = [(); N_ADDENDS].map(|_| cb.query_word32()); + let sum = cb.query_word32(); let addwords_gadget = AddWordsGadget::::construct( cb, addends.clone(), @@ -201,10 +204,10 @@ mod tests { let offset = 0; for (i, addend) in self.addends.iter().enumerate() { let a = witnesses[i]; - addend.assign(region, offset, Some(a.to_le_bytes()))?; + addend.assign_u256(region, offset, a)?; } let sum = witnesses[N_ADDENDS]; - self.sum.assign(region, offset, Some(sum.to_le_bytes()))?; + self.sum.assign_u256(region, offset, sum)?; let addends = witnesses[0..N_ADDENDS].try_into().unwrap(); self.addwords_gadget.assign(region, 0, addends, sum)?; diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs index 9138b61bb4..41f8a4e86b 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs @@ -185,7 +185,7 @@ pub(crate) type BitLengthGadget = ByteOrBitSizeGadget; #[cfg(test)] mod tests { use super::{super::test_util::*, *}; - use crate::evm_circuit::util; + use crate::util::word::Word32Cell; use eth_types::Word; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; @@ -193,24 +193,16 @@ mod tests { /// ByteSizeGadgetContainer: require(N = byte_size(a)) struct ByteSizeGadgetContainerM { bytesize_gadget: ByteSizeGadget, - a: util::Word, + a: Word32Cell, } impl MathGadgetContainer for ByteSizeGadgetContainerM { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let value_rlc = cb.query_word_rlc(); - let bytesize_gadget = ByteSizeGadget::::construct( - cb, - value_rlc - .cells - .iter() - .map(Expr::expr) - .collect::>() - .try_into() - .unwrap(), - ); + let value_word32 = cb.query_word32(); + let bytesize_gadget = + ByteSizeGadget::::construct(cb, value_word32.to_word_n().limbs); if TEST_MSB { cb.require_equal( @@ -228,7 +220,7 @@ mod tests { Self { bytesize_gadget, - a: value_rlc, + a: value_word32, } } @@ -239,7 +231,7 @@ mod tests { ) -> Result<(), Error> { let offset = 0; let x = witnesses[0]; - self.a.assign(region, offset, Some(x.to_le_bytes()))?; + self.a.assign_u256(region, offset, x)?; self.bytesize_gadget .assign(region, offset, ByteOrWord::Word(x))?; diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs index c2eb0cc596..a16854d303 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs @@ -1,43 +1,34 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::EVMConstraintBuilder, from_bytes, math_gadget::*, select, - CachedRegion, + constraint_builder::EVMConstraintBuilder, from_bytes, math_gadget::*, select, CachedRegion, }, - util::Expr, + util::{word::WordExpr, Expr}, }; use eth_types::{Field, ToLittleEndian, Word}; use halo2_proofs::plonk::{Error, Expression}; +use std::marker::PhantomData; #[derive(Clone, Debug)] /// CmpWordsGadget compares two words, exposing `eq` and `lt` -pub(crate) struct CmpWordsGadget { +pub(crate) struct CmpWordsGadget { comparison_lo: ComparisonGadget, comparison_hi: ComparisonGadget, pub eq: Expression, pub lt: Expression, + _marker: PhantomData<(T1, T2)>, } -impl CmpWordsGadget { - pub(crate) fn construct( - cb: &mut EVMConstraintBuilder, - a: &util::Word, - b: &util::Word, - ) -> Self { +impl, T2: WordExpr> CmpWordsGadget { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, a: T1, b: T2) -> Self { // `a[0..16] <= b[0..16]` - let comparison_lo = ComparisonGadget::construct( - cb, - from_bytes::expr(&a.cells[0..16]), - from_bytes::expr(&b.cells[0..16]), - ); + let (a_lo, a_hi) = a.to_word().to_lo_hi(); + let (b_lo, b_hi) = b.to_word().to_lo_hi(); + let comparison_lo = ComparisonGadget::construct(cb, a_lo, b_lo); let (lt_lo, eq_lo) = comparison_lo.expr(); // `a[16..32] <= b[16..32]` - let comparison_hi = ComparisonGadget::construct( - cb, - from_bytes::expr(&a.cells[16..32]), - from_bytes::expr(&b.cells[16..32]), - ); + let comparison_hi = ComparisonGadget::construct(cb, a_hi, b_hi); let (lt_hi, eq_hi) = comparison_hi.expr(); // `a < b` when: @@ -53,6 +44,7 @@ impl CmpWordsGadget { comparison_hi, lt, eq, + _marker: Default::default(), } } @@ -86,25 +78,29 @@ impl CmpWordsGadget { #[cfg(test)] mod tests { use super::{test_util::*, *}; - use crate::evm_circuit::util::constraint_builder::ConstrainBuilderCommon; + use crate::{ + evm_circuit::util::constraint_builder::ConstrainBuilderCommon, + util::{word::WordCell, Expr}, + }; use eth_types::Word; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; #[derive(Clone)] /// CmpWordGadgetTestContainer: require(a == b if CHECK_EQ else a < b) struct CmpWordGadgetTestContainer { - cmp_gadget: CmpWordsGadget, - a: util::Word, - b: util::Word, + cmp_gadget: CmpWordsGadget, WordCell>, + a_word: WordCell, + b_word: WordCell, } impl MathGadgetContainer for CmpWordGadgetTestContainer { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let cmp_gadget = CmpWordsGadget::::construct(cb, &a, &b); + let a_word = cb.query_word_unchecked(); + let b_word = cb.query_word_unchecked(); + + let cmp_gadget = CmpWordsGadget::construct(cb, a_word.clone(), b_word.clone()); cb.require_equal( "(a < b) * (a == b) == 0", cmp_gadget.eq.clone() * cmp_gadget.lt.clone(), @@ -117,7 +113,11 @@ mod tests { cb.require_equal("a < b", cmp_gadget.lt.clone(), 1.expr()); } - CmpWordGadgetTestContainer { cmp_gadget, a, b } + CmpWordGadgetTestContainer { + cmp_gadget, + a_word, + b_word, + } } fn assign_gadget_container( @@ -129,8 +129,8 @@ mod tests { let b = witnesses[1]; let offset = 0; - self.a.assign(region, offset, Some(a.to_le_bytes()))?; - self.b.assign(region, offset, Some(b.to_le_bytes()))?; + self.a_word.assign_u256(region, offset, a)?; + self.b_word.assign_u256(region, offset, b)?; self.cmp_gadget.assign(region, offset, a, b)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal_word.rs new file mode 100644 index 0000000000..5109d47061 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal_word.rs @@ -0,0 +1,83 @@ +use std::marker::PhantomData; + +use eth_types::Field; +use gadgets::util::and; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; + +use crate::{ + evm_circuit::util::{ + constraint_builder::EVMConstraintBuilder, transpose_val_ret, CachedRegion, + }, + util::word::{Word, WordExpr}, +}; + +use super::IsZeroGadget; + +/// Returns `1` when `lhs == rhs`, and returns `0` otherwise. +#[derive(Clone, Debug)] +pub struct IsEqualWordGadget { + is_zero_lo: IsZeroGadget, + is_zero_hi: IsZeroGadget, + _marker: PhantomData<(T1, T2)>, +} + +impl, T2: WordExpr> IsEqualWordGadget { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, lhs: &T1, rhs: &T2) -> Self { + let (lhs_lo, lhs_hi) = lhs.to_word().to_lo_hi(); + let (rhs_lo, rhs_hi) = rhs.to_word().to_lo_hi(); + let is_zero_lo = IsZeroGadget::construct(cb, lhs_lo - rhs_lo); + let is_zero_hi = IsZeroGadget::construct(cb, lhs_hi - rhs_hi); + + Self { + is_zero_lo, + is_zero_hi, + _marker: Default::default(), + } + } + + pub(crate) fn expr(&self) -> Expression { + and::expr([self.is_zero_lo.expr(), self.is_zero_hi.expr()]) + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + lhs: Word, + rhs: Word, + ) -> Result { + let (lhs_lo, lhs_hi) = lhs.to_lo_hi(); + let (rhs_lo, rhs_hi) = rhs.to_lo_hi(); + self.is_zero_lo.assign(region, offset, lhs_lo - rhs_lo)?; + self.is_zero_hi.assign(region, offset, lhs_hi - rhs_hi)?; + Ok(F::from(2)) + } + + pub(crate) fn assign_value( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + lhs: Value>, + rhs: Value>, + ) -> Result, Error> { + transpose_val_ret( + lhs.zip(rhs) + .map(|(lhs, rhs)| self.assign(region, offset, lhs, rhs)), + ) + } + + pub(crate) fn assign_u256( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + lhs: eth_types::Word, + rhs: eth_types::Word, + ) -> Result { + self.assign(region, offset, Word::from(lhs), Word::from(rhs)) + } +} + +// TODO add unittest diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero_word.rs new file mode 100644 index 0000000000..f74ccf6a96 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero_word.rs @@ -0,0 +1,107 @@ +use std::marker::PhantomData; + +use eth_types::Field; +use gadgets::util::{and, Expr}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; + +use crate::{ + evm_circuit::util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + transpose_val_ret, CachedRegion, Cell, CellType, + }, + util::word::{Word, WordExpr}, +}; + +/// Returns `1` when `word == 0`, and returns `0` otherwise. +#[derive(Clone, Debug)] +pub struct IsZeroWordGadget { + inverse_lo: Cell, + inverse_hi: Cell, + is_zero: Expression, + _marker: PhantomData, +} + +impl> IsZeroWordGadget { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, word: &T) -> Self { + let (word_lo, word_hi) = word.to_word().to_lo_hi(); + let inverse_lo = cb.query_cell_with_type(CellType::storage_for_expr(&word_lo)); + let inverse_hi = cb.query_cell_with_type(CellType::storage_for_expr(&word_hi)); + + let is_zero_lo = 1.expr() - (word_lo.clone() * inverse_lo.expr()); + let is_zero_hi = 1.expr() - (word_hi.clone() * inverse_hi.expr()); + // when `value != 0` check `inverse = a.invert()`: value * (1 - value * + // inverse) + cb.add_constraint( + "word_lo ⋅ (1 - word_lo ⋅ word_lo_inv)", + word_lo * is_zero_lo.clone(), + ); + cb.add_constraint( + "word_hi ⋅ (1 - word_hi ⋅ word_hi_inv)", + word_hi * is_zero_hi.clone(), + ); + // when `value == 0` check `inverse = 0`: `inverse ⋅ (1 - value * + // inverse)` + cb.add_constraint( + "word_lo_inv ⋅ (1 - word_lo ⋅ word_lo_inv)", + inverse_lo.expr() * is_zero_lo.clone(), + ); + cb.add_constraint( + "word_hi_inv ⋅ (1 - word_hi ⋅ word_hi_inv)", + inverse_hi.expr() * is_zero_hi.clone(), + ); + + Self { + inverse_lo, + inverse_hi, + is_zero: and::expr([is_zero_lo, is_zero_hi]), + _marker: Default::default(), + } + } + + pub(crate) fn expr(&self) -> Expression { + self.is_zero.clone() + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: Word, + ) -> Result { + let (value_lo, value_hi) = value.to_lo_hi(); + let inverse_lo = value_lo.invert().unwrap_or(F::from(0)); + self.inverse_lo + .assign(region, offset, Value::known(inverse_lo))?; + let inverse_hi = value_hi.invert().unwrap_or(F::from(0)); + self.inverse_hi + .assign(region, offset, Value::known(inverse_hi))?; + Ok(if value_lo.is_zero().into() && value_hi.is_zero().into() { + F::from(2) + } else { + F::from(0) + }) + } + + pub(crate) fn assign_u256( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: eth_types::Word, + ) -> Result { + self.assign(region, offset, Word::from(value)) + } + + pub(crate) fn assign_value( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: Value>, + ) -> Result, Error> { + transpose_val_ret(value.map(|value| self.assign(region, offset, value))) + } +} + +// TODO adding unittest diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs index a19c62dbcd..53bd25fabf 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs @@ -1,6 +1,8 @@ -use crate::evm_circuit::util::{ - self, constraint_builder::EVMConstraintBuilder, from_bytes, math_gadget::*, split_u256, - CachedRegion, +use crate::{ + evm_circuit::util::{ + constraint_builder::EVMConstraintBuilder, math_gadget::*, split_u256, CachedRegion, + }, + util::word::{self}, }; use eth_types::{Field, Word}; use halo2_proofs::plonk::{Error, Expression}; @@ -14,21 +16,16 @@ pub struct LtWordGadget { } impl LtWordGadget { - pub(crate) fn construct( + pub(crate) fn construct + Clone>( cb: &mut EVMConstraintBuilder, - lhs: &util::Word, - rhs: &util::Word, + lhs: &word::Word, + rhs: &word::Word, ) -> Self { - let comparison_hi = ComparisonGadget::construct( - cb, - from_bytes::expr(&lhs.cells[16..]), - from_bytes::expr(&rhs.cells[16..]), - ); - let lt_lo = LtGadget::construct( - cb, - from_bytes::expr(&lhs.cells[..16]), - from_bytes::expr(&rhs.cells[..16]), - ); + let (lhs_lo, lhs_hi) = lhs.to_lo_hi(); + let (rhs_lo, rhs_hi) = rhs.to_lo_hi(); + let comparison_hi = ComparisonGadget::construct(cb, lhs_hi.expr(), rhs_hi.expr()); + let lt_lo = LtGadget::construct(cb, lhs_lo.expr(), rhs_lo.expr()); + Self { comparison_hi, lt_lo, @@ -70,22 +67,22 @@ mod tests { use crate::evm_circuit::util::constraint_builder::ConstrainBuilderCommon; use super::{test_util::*, *}; - use eth_types::*; + use crate::util::word::{Word32Cell, WordExpr}; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; #[derive(Clone)] /// LtWordTestContainer: require(a < b) struct LtWordTestContainer { ltword_gadget: LtWordGadget, - a: util::Word, - b: util::Word, + a: Word32Cell, + b: Word32Cell, } impl MathGadgetContainer for LtWordTestContainer { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let ltword_gadget = LtWordGadget::::construct(cb, &a, &b); + let a = cb.query_word32(); + let b = cb.query_word32(); + let ltword_gadget = LtWordGadget::::construct(cb, &a.to_word(), &b.to_word()); cb.require_equal("a < b", ltword_gadget.expr(), 1.expr()); LtWordTestContainer { ltword_gadget, @@ -103,8 +100,8 @@ mod tests { let b = witnesses[1]; let offset = 0; - self.a.assign(region, offset, Some(a.to_le_bytes()))?; - self.b.assign(region, offset, Some(b.to_le_bytes()))?; + self.a.assign_u256(region, offset, a)?; + self.b.assign_u256(region, offset, b)?; self.ltword_gadget.assign(region, 0, a, b)?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs index bd9726bebf..369d78dee8 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs @@ -1,13 +1,15 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::*, - select, sum, CachedRegion, + CachedRegion, + }, + util::{ + word::{self, Word, Word32Cell, WordExpr}, + Expr, }, - util::Expr, }; -use eth_types::{Field, ToLittleEndian, Word}; +use eth_types::{Field, Word as U256}; use halo2_proofs::plonk::Error; /// Constraints for the words a, n, r: @@ -20,36 +22,34 @@ use halo2_proofs::plonk::Error; /// case of n=0. Unlike the usual k * n + r = a, which forces r = a when n=0, /// this equation assures that r { - k: util::Word, - a_or_zero: util::Word, +pub(crate) struct ModGadget { + k: Word32Cell, + a_or_zero: Word32Cell, mul_add_words: MulAddWordsGadget, - n_is_zero: IsZeroGadget, - a_or_is_zero: IsZeroGadget, + n_is_zero: IsZeroWordGadget>, + a_or_is_zero: IsZeroWordGadget>, lt: LtWordGadget, } -impl ModGadget { - pub(crate) fn construct(cb: &mut EVMConstraintBuilder, words: [&util::Word; 3]) -> Self { +impl ModGadget { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, words: [&Word32Cell; 3]) -> Self { let (a, n, r) = (words[0], words[1], words[2]); - let k = if IS_EVM { - cb.query_word_rlc() - } else { - cb.query_keccak_rlc() - }; - let a_or_zero = if IS_EVM { - cb.query_word_rlc() - } else { - cb.query_keccak_rlc() - }; - let n_is_zero = IsZeroGadget::construct(cb, sum::expr(&n.cells)); - let a_or_is_zero = IsZeroGadget::construct(cb, sum::expr(&a_or_zero.cells)); + let k = cb.query_word32(); + let a_or_zero = cb.query_word32(); + let n_is_zero = IsZeroWordGadget::construct(cb, n); + let a_or_is_zero = IsZeroWordGadget::construct(cb, &a_or_zero); + let mul_add_words = MulAddWordsGadget::construct(cb, [&k, n, r, &a_or_zero]); - let lt = LtWordGadget::construct(cb, r, n); + let lt = LtWordGadget::construct(cb, &r.to_word(), &n.to_word()); // Constrain the aux variable a_or_zero to be =a or =0 if n==0: - cb.require_equal( + cb.require_equal_word( "a_or_zero == if n == 0 { 0 } else { a }", - a_or_zero.expr(), - select::expr(n_is_zero.expr(), 0.expr(), a.expr()), + a_or_zero.to_word(), + Word::select( + n_is_zero.expr(), + Word::zero(), + a.to_word() + ) + // select::expr(n_is_zero.expr(), 0.expr(), a.expr()), ); // Constrain the result r to be valid: (r ModGadget { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - a: Word, - n: Word, - r: Word, - k: Word, + a: U256, + n: U256, + r: U256, + k: U256, ) -> Result<(), Error> { - let a_or_zero = if n.is_zero() { Word::zero() } else { a }; - - self.k.assign(region, offset, Some(k.to_le_bytes()))?; - self.a_or_zero - .assign(region, offset, Some(a_or_zero.to_le_bytes()))?; - let n_sum = (0..32).fold(0, |acc, idx| acc + n.byte(idx) as u64); - let a_or_zero_sum = (0..32).fold(0, |acc, idx| acc + a_or_zero.byte(idx) as u64); - self.n_is_zero.assign(region, offset, F::from(n_sum))?; + let a_or_zero = if n.is_zero() { U256::zero() } else { a }; + + self.k.assign_u256(region, offset, k)?; + self.a_or_zero.assign_u256(region, offset, a_or_zero)?; + + self.n_is_zero.assign(region, offset, word::Word::from(n))?; self.a_or_is_zero - .assign(region, offset, F::from(a_or_zero_sum))?; + .assign(region, offset, word::Word::from(a_or_zero))?; self.mul_add_words .assign(region, offset, [k, n, r, a_or_zero])?; self.lt.assign(region, offset, r, n)?; @@ -107,17 +105,17 @@ mod tests { #[derive(Clone)] /// ModGadgetTestContainer: require(a % n == r) struct ModGadgetTestContainer { - mod_gadget: ModGadget, - a: util::Word, - n: util::Word, - r: util::Word, + mod_gadget: ModGadget, + a: Word32Cell, + n: Word32Cell, + r: Word32Cell, } impl MathGadgetContainer for ModGadgetTestContainer { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let a = cb.query_word_rlc(); - let n = cb.query_word_rlc(); - let r = cb.query_word_rlc(); + let a = cb.query_word32(); + let n = cb.query_word32(); + let r = cb.query_word32(); let mod_gadget = ModGadget::construct(cb, [&a, &n, &r]); ModGadgetTestContainer { mod_gadget, @@ -143,9 +141,9 @@ mod tests { let offset = 0; - self.a.assign(region, offset, Some(a.to_le_bytes()))?; - self.n.assign(region, offset, Some(n.to_le_bytes()))?; - self.r.assign(region, offset, Some(r.to_le_bytes()))?; + self.a.assign_u256(region, offset, a)?; + self.n.assign_u256(region, offset, n)?; + self.r.assign_u256(region, offset, r)?; self.mod_gadget.assign(region, 0, a, n, r, k) } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs index 9caa9b687f..56d89f7750 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs @@ -1,12 +1,14 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, from_bytes, pow_of_two_expr, split_u256, split_u256_limb64, CachedRegion, Cell, }, - util::Expr, + util::{ + word::{Word, Word32Cell, Word4, WordExpr}, + Expr, + }, }; -use eth_types::{Field, ToLittleEndian, Word}; +use eth_types::{Field, ToLittleEndian, Word as U256Word}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -50,7 +52,7 @@ pub(crate) struct MulAddWordsGadget { } impl MulAddWordsGadget { - pub(crate) fn construct(cb: &mut EVMConstraintBuilder, words: [&util::Word; 4]) -> Self { + pub(crate) fn construct(cb: &mut EVMConstraintBuilder, words: [&Word32Cell; 4]) -> Self { let (a, b, c, d) = (words[0], words[1], words[2], words[3]); let carry_lo = cb.query_bytes(); let carry_hi = cb.query_bytes(); @@ -59,15 +61,15 @@ impl MulAddWordsGadget { let mut a_limbs = vec![]; let mut b_limbs = vec![]; - for trunk in 0..4 { - let idx = (trunk * 8) as usize; - a_limbs.push(from_bytes::expr(&a.cells[idx..idx + 8])); - b_limbs.push(from_bytes::expr(&b.cells[idx..idx + 8])); + let word4_a: Word4> = a.to_word_n(); + let word4_b: Word4> = b.to_word_n(); + for i in 0..4 { + a_limbs.push(word4_a.limbs[i].expr()); + b_limbs.push(word4_b.limbs[i].expr()); } - let c_lo = from_bytes::expr(&c.cells[0..16]); - let c_hi = from_bytes::expr(&c.cells[16..32]); - let d_lo = from_bytes::expr(&d.cells[0..16]); - let d_hi = from_bytes::expr(&d.cells[16..32]); + + let word_c: Word> = c.to_word(); + let word_d: Word> = d.to_word(); let t0 = a_limbs[0].clone() * b_limbs[0].clone(); let t1 = a_limbs[0].clone() * b_limbs[1].clone() + a_limbs[1].clone() * b_limbs[0].clone(); @@ -88,13 +90,13 @@ impl MulAddWordsGadget { cb.require_equal( "(a * b)_lo + c_lo == d_lo + carry_lo ⋅ 2^128", - t0.expr() + t1.expr() * pow_of_two_expr(64) + c_lo, - d_lo + carry_lo_expr.clone() * pow_of_two_expr(128), + t0.expr() + t1.expr() * pow_of_two_expr(64) + word_c.lo().expr(), + word_d.lo().expr() + carry_lo_expr.clone() * pow_of_two_expr(128), ); cb.require_equal( "(a * b)_hi + c_hi + carry_lo == d_hi + carry_hi ⋅ 2^128", - t2.expr() + t3.expr() * pow_of_two_expr(64) + c_hi + carry_lo_expr, - d_hi + carry_hi_expr * pow_of_two_expr(128), + t2.expr() + t3.expr() * pow_of_two_expr(64) + word_c.hi().expr() + carry_lo_expr, + word_d.hi().expr() + carry_hi_expr * pow_of_two_expr(128), ); Self { @@ -108,7 +110,7 @@ impl MulAddWordsGadget { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - words: [Word; 4], + words: [U256Word; 4], ) -> Result<(), Error> { let (a, b, c, d) = (words[0], words[1], words[2], words[3]); @@ -158,19 +160,19 @@ mod tests { /// MulAddGadgetContainer: require(a*b + c == d + carry*(2**256)) struct MulAddGadgetContainer { muladd_words_gadget: MulAddWordsGadget, - a: util::Word, - b: util::Word, - c: util::Word, - d: util::Word, + a: Word32Cell, + b: Word32Cell, + c: Word32Cell, + d: Word32Cell, carry: Cell, } impl MathGadgetContainer for MulAddGadgetContainer { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let c = cb.query_word_rlc(); - let d = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); + let c = cb.query_word32(); + let d = cb.query_word32(); let carry = cb.query_cell(); let math_gadget = MulAddWordsGadget::::construct(cb, [&a, &b, &c, &d]); cb.require_equal("carry is correct", math_gadget.overflow(), carry.expr()); @@ -190,14 +192,10 @@ mod tests { region: &mut CachedRegion<'_, '_, F>, ) -> Result<(), Error> { let offset = 0; - self.a - .assign(region, offset, Some(witnesses[0].to_le_bytes()))?; - self.b - .assign(region, offset, Some(witnesses[1].to_le_bytes()))?; - self.c - .assign(region, offset, Some(witnesses[2].to_le_bytes()))?; - self.d - .assign(region, offset, Some(witnesses[3].to_le_bytes()))?; + self.a.assign_u256(region, offset, witnesses[0])?; + self.b.assign_u256(region, offset, witnesses[1])?; + self.c.assign_u256(region, offset, witnesses[2])?; + self.d.assign_u256(region, offset, witnesses[3])?; self.carry.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs index eef61035b4..f2578da46a 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs @@ -1,13 +1,18 @@ use crate::{ evm_circuit::util::{ - self, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, from_bytes, pow_of_two_expr, split_u256, split_u256_limb64, CachedRegion, Cell, }, - util::Expr, + util::{ + word::{self, Word4, WordExpr}, + Expr, + }, }; use eth_types::{Field, ToLittleEndian, Word}; -use halo2_proofs::{circuit::Value, plonk::Error}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; /// Construct the gadget that checks a * b + c == d * 2**256 + e /// where a, b, c, d, e are 256-bit words. @@ -52,8 +57,8 @@ impl MulAddWords512Gadget { /// Addend is the optional c. pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - words: [&util::Word; 4], - addend: Option<&util::Word>, + words: [&word::Word32Cell; 4], + addend: Option<&word::Word32Cell>, ) -> Self { let carry_0 = cb.query_bytes(); let carry_1 = cb.query_bytes(); @@ -65,16 +70,15 @@ impl MulAddWords512Gadget { // Split input words in limbs let mut a_limbs = vec![]; let mut b_limbs = vec![]; - for trunk in 0..4 { - let idx = (trunk * 8) as usize; - a_limbs.push(from_bytes::expr(&words[0].cells[idx..idx + 8])); - b_limbs.push(from_bytes::expr(&words[1].cells[idx..idx + 8])); + let word4_a: Word4> = words[0].to_word_n(); + let word4_b: Word4> = words[1].to_word_n(); + for i in 0..4 { + a_limbs.push(word4_a.limbs[i].expr()); + b_limbs.push(word4_b.limbs[i].expr()); } - let d_lo = from_bytes::expr(&words[2].cells[0..16]); - let d_hi = from_bytes::expr(&words[2].cells[16..32]); - let e_lo = from_bytes::expr(&words[3].cells[0..16]); - let e_hi = from_bytes::expr(&words[3].cells[16..32]); + let (d_lo, d_hi) = words[2].to_word().to_lo_hi(); + let (e_lo, e_hi) = words[3].to_word().to_lo_hi(); // Limb multiplication let t0 = a_limbs[0].clone() * b_limbs[0].clone(); @@ -93,8 +97,8 @@ impl MulAddWords512Gadget { let t6 = a_limbs[3].clone() * b_limbs[3].clone(); if let Some(c) = addend { - let c_lo = from_bytes::expr(&c.cells[0..16]); - let c_hi = from_bytes::expr(&c.cells[16..32]); + let c = c.to_word(); + let (c_lo, c_hi) = c.to_lo_hi(); cb.require_equal( "(t0 + t1 ⋅ 2^64) + c_lo == e_lo + carry_0 ⋅ 2^128", t0.expr() + t1.expr() * pow_of_two_expr(64) + c_lo, @@ -203,20 +207,20 @@ mod tests { /// MulAddWords512GadgetContainer: require(a * b + c == d * 2**256 + e) struct MulAddWords512GadgetContainer { math_gadget: MulAddWords512Gadget, - a: util::Word, - b: util::Word, - d: util::Word, - e: util::Word, - addend: util::Word, + a: word::Word32Cell, + b: word::Word32Cell, + d: word::Word32Cell, + e: word::Word32Cell, + addend: word::Word32Cell, } impl MathGadgetContainer for MulAddWords512GadgetContainer { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let a = cb.query_word_rlc(); - let b = cb.query_word_rlc(); - let d = cb.query_word_rlc(); - let e = cb.query_word_rlc(); - let addend = cb.query_word_rlc(); + let a = cb.query_word32(); + let b = cb.query_word32(); + let d = cb.query_word32(); + let e = cb.query_word32(); + let addend = cb.query_word32(); let math_gadget = MulAddWords512Gadget::::construct(cb, [&a, &b, &d, &e], Some(&addend)); MulAddWords512GadgetContainer { @@ -235,16 +239,11 @@ mod tests { region: &mut CachedRegion<'_, '_, F>, ) -> Result<(), Error> { let offset = 0; - self.a - .assign(region, offset, Some(witnesses[0].to_le_bytes()))?; - self.b - .assign(region, offset, Some(witnesses[1].to_le_bytes()))?; - self.d - .assign(region, offset, Some(witnesses[2].to_le_bytes()))?; - self.e - .assign(region, offset, Some(witnesses[3].to_le_bytes()))?; - self.addend - .assign(region, offset, Some(witnesses[4].to_le_bytes()))?; + self.a.assign_u256(region, offset, witnesses[0])?; + self.b.assign_u256(region, offset, witnesses[1])?; + self.d.assign_u256(region, offset, witnesses[2])?; + self.e.assign_u256(region, offset, witnesses[3])?; + self.addend.assign_u256(region, offset, witnesses[4])?; self.math_gadget.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs index 04942e96e3..88fc40b37d 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs @@ -4,9 +4,12 @@ use crate::{ constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, from_bytes, pow_of_two_expr, split_u256, CachedRegion, }, - util::Expr, + util::{ + word::{Word32Cell, WordExpr}, + Expr, + }, }; -use eth_types::{Field, ToLittleEndian, Word}; +use eth_types::{Field, Word}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -16,28 +19,26 @@ use halo2_proofs::{ /// which disallows overflow. #[derive(Clone, Debug)] pub(crate) struct MulWordByU64Gadget { - multiplicand: util::Word, - product: util::Word, + multiplicand: Word32Cell, + product: Word32Cell, carry_lo: [util::Cell; 8], } impl MulWordByU64Gadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - multiplicand: util::Word, + multiplicand: Word32Cell, multiplier: Expression, ) -> Self { let gadget = Self { multiplicand, - product: cb.query_word_rlc(), + product: cb.query_word32(), carry_lo: cb.query_bytes(), }; - let multiplicand_lo = from_bytes::expr(&gadget.multiplicand.cells[..16]); - let multiplicand_hi = from_bytes::expr(&gadget.multiplicand.cells[16..]); + let (multiplicand_lo, multiplicand_hi) = gadget.multiplicand.to_word().to_lo_hi(); - let product_lo = from_bytes::expr(&gadget.product.cells[..16]); - let product_hi = from_bytes::expr(&gadget.product.cells[16..]); + let (product_lo, product_hi) = gadget.product.to_word().to_lo_hi(); let carry_lo = from_bytes::expr(&gadget.carry_lo[..8]); @@ -65,9 +66,8 @@ impl MulWordByU64Gadget { product: Word, ) -> Result<(), Error> { self.multiplicand - .assign(region, offset, Some(multiplicand.to_le_bytes()))?; - self.product - .assign(region, offset, Some(product.to_le_bytes()))?; + .assign_u256(region, offset, multiplicand)?; + self.product.assign_u256(region, offset, product)?; let (multiplicand_lo, _) = split_u256(&multiplicand); let (product_lo, _) = split_u256(&product); @@ -85,7 +85,7 @@ impl MulWordByU64Gadget { Ok(()) } - pub(crate) fn product(&self) -> &util::Word { + pub(crate) fn product(&self) -> &Word32Cell { &self.product } } @@ -94,23 +94,23 @@ impl MulWordByU64Gadget { mod tests { use super::{super::test_util::*, *}; use crate::evm_circuit::util::Cell; - use eth_types::Word; + use eth_types::{ToLittleEndian, Word}; use halo2_proofs::{halo2curves::bn256::Fr, plonk::Error}; #[derive(Clone)] /// MulWordByU64TestContainer: require(product = a*(b as u64)) struct MulWordByU64TestContainer { mulwords_u64_gadget: MulWordByU64Gadget, - a: util::Word, + a: Word32Cell, b: Cell, - product: util::Word, + product: Word32Cell, } impl MathGadgetContainer for MulWordByU64TestContainer { fn configure_gadget_container(cb: &mut EVMConstraintBuilder) -> Self { - let a = cb.query_word_rlc(); + let a = cb.query_word32(); let b = cb.query_cell(); - let product = cb.query_word_rlc(); + let product = cb.query_word32(); let mulwords_u64_gadget = MulWordByU64Gadget::::construct(cb, a.clone(), b.expr()); MulWordByU64TestContainer { mulwords_u64_gadget, @@ -130,10 +130,9 @@ mod tests { let product = witnesses[2]; let offset = 0; - self.a.assign(region, offset, Some(a.to_le_bytes()))?; + self.a.assign_u256(region, offset, a)?; self.b.assign(region, offset, Value::known(F::from(b)))?; - self.product - .assign(region, offset, Some(product.to_le_bytes()))?; + self.product.assign_u256(region, offset, product)?; self.mulwords_u64_gadget.assign(region, 0, a, b, product)?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs index 0a4ef65a3e..d10ed145f8 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs @@ -1,16 +1,19 @@ -use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word}; +use eth_types::{Address, Field, ToScalar, Word as U256Word}; use gadgets::util::{and, expr_from_bytes, not, select, sum, Expr}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, }; -use crate::evm_circuit::{ - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_U64, N_BYTES_WORD}, - util::{ - constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - CachedRegion, Cell, RandomLinearCombination, +use crate::{ + evm_circuit::{ + param::{N_BYTES_U64, N_BYTES_WORD}, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + rlc, AccountAddress, CachedRegion, Cell, RandomLinearCombination, + }, }, + util::word::{Word, Word32Cell, WordExpr}, }; use super::IsZeroGadget; @@ -197,7 +200,8 @@ impl RlpU64Gadget { #[derive(Clone, Debug)] pub struct ContractCreateGadget { /// Sender address of the contract creation tx. - caller_address: RandomLinearCombination, + /// AccountAddress + caller_address: AccountAddress, /// Sender nonce of the contract creation tx. nonce: RlpU64Gadget, /// Keccak256 hash of init code, used for CREATE2. We don't use a @@ -205,27 +209,32 @@ pub struct ContractCreateGadget { /// RLC in the case of init code hash, for BeginTx and /// CREATE2 respectively. Instead, we store just the bytes and calculate the /// appropriate RLC wherever needed. - keccak_code_hash: [Cell; N_BYTES_WORD], + keccak_code_hash: Word32Cell, + /// RLC of the init code's hash. The value of this field is feature gated and can be the keccak /// or the poseidon hash. code_hash_rlc: Cell, + code_hash: Word32Cell, /// Random salt for CREATE2. - salt: [Cell; N_BYTES_WORD], + salt: Word32Cell, } impl ContractCreateGadget { /// Configure and construct the gadget. pub(crate) fn construct(cb: &mut EVMConstraintBuilder) -> Self { - let caller_address = cb.query_keccak_rlc(); + let caller_address = cb.query_account_address(); let nonce = RlpU64Gadget::construct(cb); - let keccak_code_hash = array_init::array_init(|_| cb.query_byte()); + + let keccak_code_hash = cb.query_word32(); + let code_hash = cb.query_word32(); let code_hash_rlc = cb.query_cell_phase2(); - let salt = array_init::array_init(|_| cb.query_byte()); + let salt = cb.query_word32(); #[cfg(not(feature = "poseidon-codehash"))] { let word_rlc = cb.word_rlc::( keccak_code_hash + .limbs .iter() .map(Expr::expr) .collect::>() @@ -243,6 +252,7 @@ impl ContractCreateGadget { nonce, keccak_code_hash, code_hash_rlc, + code_hash, salt, } } @@ -255,9 +265,9 @@ impl ContractCreateGadget { offset: usize, caller_address: Address, caller_nonce: u64, - keccak_code_hash: Option, - code_hash: Option, - salt: Option, + keccak_code_hash: Option, + code_hash: Option, + salt: Option, ) -> Result<(), Error> { let mut caller_address_bytes = caller_address.to_fixed_bytes(); caller_address_bytes.reverse(); @@ -275,32 +285,27 @@ impl ContractCreateGadget { debug_assert_eq!(code_hash, keccak_code_hash); } - for (c, v) in self.keccak_code_hash.iter().zip( - keccak_code_hash - .map(|v| v.to_le_bytes()) - .unwrap_or_default(), - ) { - c.assign(region, offset, Value::known(F::from(v as u64)))?; - } self.code_hash_rlc.assign( region, offset, region.code_hash(code_hash.unwrap_or_default()), )?; - for (c, v) in self - .salt - .iter() - .zip(salt.map(|v| v.to_le_bytes()).unwrap_or_default()) - { - c.assign(region, offset, Value::known(F::from(v as u64)))?; - } + self.code_hash + .assign_u256(region, offset, code_hash.unwrap_or_default())?; + + self.keccak_code_hash + .assign_u256(region, offset, keccak_code_hash.unwrap_or_default())?; + + self.salt + .assign_u256(region, offset, salt.unwrap_or_default())?; Ok(()) } /// Caller address' value. - pub(crate) fn caller_address(&self) -> Expression { - expr_from_bytes(&self.caller_address.cells) + pub(crate) fn caller_address(&self) -> Word> { + //expr_from_bytes(&self.caller_address.limbs) + self.caller_address.to_word() } /// Caller nonce's value. @@ -309,20 +314,8 @@ impl ContractCreateGadget { } /// Dynamic code hash in RLC form. - pub(crate) fn code_hash_word_rlc(&self) -> Expression { - self.code_hash_rlc.expr() - } - - /// Init Code's keccak hash word RLC. - pub(crate) fn keccak_code_hash_word_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { - cb.word_rlc::( - self.keccak_code_hash - .iter() - .map(Expr::expr) - .collect::>() - .try_into() - .unwrap(), - ) + pub(crate) fn code_hash(&self) -> Word> { + self.code_hash.to_word() } /// Init Code's keccak hash keccak RLC. @@ -332,6 +325,7 @@ impl ContractCreateGadget { ) -> Expression { cb.keccak_rlc::( self.keccak_code_hash + .limbs .iter() .map(Expr::expr) .collect::>() @@ -340,10 +334,15 @@ impl ContractCreateGadget { ) } + pub(crate) fn salt(&self) -> Word> { + self.salt.to_word() + } + /// Salt EVM word RLC. pub(crate) fn salt_word_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { cb.word_rlc::( self.salt + .limbs .iter() .map(Expr::expr) .collect::>() @@ -356,6 +355,7 @@ impl ContractCreateGadget { pub(crate) fn salt_keccak_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { cb.keccak_rlc::( self.salt + .limbs .iter() .map(Expr::expr) .collect::>() @@ -365,8 +365,12 @@ impl ContractCreateGadget { } /// Caller address' RLC value. - pub(crate) fn caller_address_rlc(&self) -> Expression { - self.caller_address.expr() + pub(crate) fn caller_address_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { + //self.caller_address.expr() + rlc::expr( + &self.caller_address.limbs.clone().map(|x| x.expr()), + cb.challenges().keccak_input(), + ) } /// Caller nonce's RLC value. @@ -404,13 +408,13 @@ impl ContractCreateGadget { let challenge_power_64 = challenge_power_32.clone().square(); let challenge_power_84 = challenge_power_64.clone() * challenge_power_20; (0xff.expr() * challenge_power_84) - + (self.caller_address_rlc() * challenge_power_64) + + (self.caller_address_rlc(cb) * challenge_power_64) + (self.salt_keccak_rlc(cb) * challenge_power_32) + self.keccak_code_hash_keccak_rlc(cb) } else { // RLC(RLP([caller_address, caller_nonce])) let challenge_power_21 = challenges[20].clone(); - ((self.caller_address_rlc() + ((self.caller_address_rlc(cb) + (148.expr() * challenge_power_20) + ((213.expr() + self.nonce.rlp_length()) * challenge_power_21)) * self.nonce.challenge_power_rlp_length(cb)) diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index 56335318c6..36215ceddf 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -1,4 +1,4 @@ -use super::{constraint_builder::ConstrainBuilderCommon, from_bits, CachedRegion, Word}; +use super::{constraint_builder::ConstrainBuilderCommon, from_bits, CachedRegion}; use crate::{ evm_circuit::{ param::{ @@ -13,11 +13,15 @@ use crate::{ AddWordsGadget, ConstantDivisionGadget, IsZeroGadget, LtGadget, MinMaxGadget, RangeCheckGadget, }, - not, or, select, sum, Cell, CellType, MemoryAddress, + not, or, select, sum, Cell, MemoryAddress, }, }, - util::Expr, + util::{ + word::{Word, Word32Cell, WordCell, WordExpr}, + Expr, + }, }; + use array_init::array_init; use eth_types::{ evm_types::{GasCost, MAX_EXPANDED_MEMORY_ADDRESS}, @@ -54,16 +58,16 @@ pub(crate) trait CommonMemoryAddressGadget { ) -> Result; /// Return original word of memory offset. - fn offset_rlc(&self) -> Expression; + fn offset_word(&self) -> Word>; /// Return original word of memory length. - fn length_rlc(&self) -> Expression; + fn length_word(&self) -> Word>; /// Return valid memory length of Uint64. fn length(&self) -> Expression; /// Return valid memory offset plus length. - fn end_offset(&self) -> Expression; + fn address(&self) -> Expression; } /// Convert the dynamic memory offset and length from random linear combination @@ -72,16 +76,17 @@ pub(crate) trait CommonMemoryAddressGadget { /// the RLC value for `memory_offset` need not match the bytes. #[derive(Clone, Debug)] pub(crate) struct MemoryAddressGadget { - memory_offset: Cell, memory_offset_bytes: MemoryAddress, + memory_offset: WordCell, memory_length: MemoryAddress, memory_length_is_zero: IsZeroGadget, } impl CommonMemoryAddressGadget for MemoryAddressGadget { fn construct_self(cb: &mut EVMConstraintBuilder) -> Self { - let offset = cb.query_cell_phase2(); - let length = cb.query_word_rlc(); + let offset = cb.query_word_unchecked(); + //let length = cb.query_word_rlc(); + let length = cb.query_memory_address(); Self::construct(cb, offset, length) } @@ -95,11 +100,7 @@ impl CommonMemoryAddressGadget for MemoryAddressGadget { let memory_offset_bytes = memory_offset.to_le_bytes(); let memory_length_bytes = memory_length.to_le_bytes(); let memory_length_is_zero = memory_length.is_zero(); - self.memory_offset.assign( - region, - offset, - region.word_rlc(U256::from_little_endian(&memory_offset_bytes)), - )?; + self.memory_offset_bytes.assign( region, offset, @@ -111,15 +112,10 @@ impl CommonMemoryAddressGadget for MemoryAddressGadget { .unwrap() }), )?; - self.memory_length.assign( - region, - offset, - Some( - memory_length_bytes[..N_BYTES_MEMORY_ADDRESS] - .try_into() - .unwrap(), - ), - )?; + self.memory_offset + .assign_u256(region, offset, memory_offset)?; + self.memory_length + .assign_u256(region, offset, memory_length)?; self.memory_length_is_zero .assign(region, offset, sum::value(&memory_length_bytes))?; Ok(if memory_length_is_zero { @@ -129,19 +125,19 @@ impl CommonMemoryAddressGadget for MemoryAddressGadget { }) } - fn offset_rlc(&self) -> Expression { - self.memory_offset.expr() + fn offset_word(&self) -> Word> { + self.memory_offset.to_word() } - fn length_rlc(&self) -> Expression { - self.memory_length.expr() + fn length_word(&self) -> Word> { + self.memory_length.to_word() } fn length(&self) -> Expression { - from_bytes::expr(&self.memory_length.cells) + from_bytes::expr(&self.memory_length.limbs) } - fn end_offset(&self) -> Expression { + fn address(&self) -> Expression { self.offset() + self.length() } } @@ -149,22 +145,18 @@ impl CommonMemoryAddressGadget for MemoryAddressGadget { impl MemoryAddressGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - memory_offset: Cell, + memory_offset: WordCell, memory_length: MemoryAddress, ) -> Self { - debug_assert_eq!( - CellType::StoragePhase2, - cb.curr.cell_manager.columns()[memory_offset.cell_column_index].cell_type - ); - let memory_length_is_zero = IsZeroGadget::construct(cb, sum::expr(&memory_length.cells)); - let memory_offset_bytes = cb.query_word_rlc(); + let memory_length_is_zero = IsZeroGadget::construct(cb, sum::expr(&memory_length.limbs)); + let memory_offset_bytes = cb.query_memory_address(); let has_length = 1.expr() - memory_length_is_zero.expr(); cb.condition(has_length, |cb| { - cb.require_equal( + cb.require_equal_word( "Offset decomposition into 5 bytes", - memory_offset_bytes.expr(), - memory_offset.expr(), + Word::from_lo_unchecked(memory_offset_bytes.expr()), + memory_offset.to_word(), ); }); @@ -181,7 +173,7 @@ impl MemoryAddressGadget { } pub(crate) fn offset(&self) -> Expression { - self.has_length() * from_bytes::expr(&self.memory_offset_bytes.cells) + self.has_length() * from_bytes::expr(&self.memory_offset_bytes.limbs) } } @@ -202,20 +194,20 @@ pub(crate) struct MemoryExpandedAddressGadget { impl CommonMemoryAddressGadget for MemoryExpandedAddressGadget { fn construct_self(cb: &mut EVMConstraintBuilder) -> Self { - let offset = cb.query_word_rlc(); - let length = cb.query_word_rlc(); - let sum = cb.query_word_rlc(); + let offset = cb.query_word32(); + let length = cb.query_word32(); + let sum = cb.query_word32(); let sum_lt_cap = LtGadget::construct( cb, - from_bytes::expr(&sum.cells[..N_BYTES_U64]), + from_bytes::expr(&sum.limbs[..N_BYTES_U64]), (MAX_EXPANDED_MEMORY_ADDRESS + 1).expr(), ); - let sum_overflow_hi = sum::expr(&sum.cells[N_BYTES_U64..]); + let sum_overflow_hi = sum::expr(&sum.limbs[N_BYTES_U64..]); let sum_within_u64 = IsZeroGadget::construct(cb, sum_overflow_hi); - let length_is_zero = IsZeroGadget::construct(cb, sum::expr(&length.cells)); + let length_is_zero = IsZeroGadget::construct(cb, sum::expr(&length.limbs)); let offset_length_sum = AddWordsGadget::construct(cb, [offset, length], sum); Self { @@ -270,33 +262,35 @@ impl CommonMemoryAddressGadget for MemoryExpandedAddressGadget { Ok(address) } - fn offset_rlc(&self) -> Expression { + // change name to offset_word ? + fn offset_word(&self) -> Word> { let addends = self.offset_length_sum.addends(); - addends[0].expr() + addends[0].to_word() } - fn length_rlc(&self) -> Expression { + // change name to length_word ? + fn length_word(&self) -> Word> { let addends = self.offset_length_sum.addends(); - addends[1].expr() + addends[1].to_word() } fn length(&self) -> Expression { let addends = self.offset_length_sum.addends(); select::expr( self.within_range(), - from_bytes::expr(&addends[1].cells[..N_BYTES_U64]), + from_bytes::expr(&addends[1].limbs[..N_BYTES_U64]), 0.expr(), ) } /// Return expanded address if within range, otherwise return 0. - fn end_offset(&self) -> Expression { + fn address(&self) -> Expression { select::expr( self.length_is_zero.expr(), 0.expr(), select::expr( self.within_range(), - from_bytes::expr(&self.offset_length_sum.sum().cells[..N_BYTES_U64]), + from_bytes::expr(&self.offset_length_sum.sum().limbs[..N_BYTES_U64]), 0.expr(), ), ) @@ -392,10 +386,10 @@ impl MemoryWordAddress { cb.require_equal( "shift bits match the address", from_bits::expr(&address_first_bits[..]), - address.cells[0].expr(), + address.limbs[0].expr(), ); - let address_int = from_bytes::expr(&address.cells[..]); + let address_int = from_bytes::expr(&address.limbs[..]); let slot = cb.query_cell(); let shift = from_bits::expr(&address_first_bits[..5]); @@ -520,8 +514,8 @@ impl MemoryMask { pub(crate) fn require_left_equal( &self, cb: &mut EVMConstraintBuilder, - value_left: &Word, - value_left_prev: &Word, + value_left: &Word32Cell, + value_left_prev: &Word32Cell, ) { let a = self.left_rlc(cb, value_left); let a_prev = self.left_rlc(cb, value_left_prev); @@ -531,8 +525,8 @@ impl MemoryMask { pub(crate) fn require_right_equal( &self, cb: &mut EVMConstraintBuilder, - value_right: &Word, - value_right_prev: &Word, + value_right: &Word32Cell, + value_right_prev: &Word32Cell, ) { let d = self.right_rlc(cb, value_right); let d_prev = self.right_rlc(cb, value_right_prev); @@ -543,7 +537,7 @@ impl MemoryMask { &self, cb: &mut EVMConstraintBuilder, byte: Expression, - value_left: &Word, + value_left: &Word32Cell, ) { let b = self.right_rlc(cb, value_left); @@ -557,10 +551,11 @@ impl MemoryMask { pub(crate) fn require_equal_unaligned_word( &self, cb: &mut EVMConstraintBuilder, - value_rlc: Expression, - value_left: &Word, - value_right: &Word, + value: &Word32Cell, + value_left: &Word32Cell, + value_right: &Word32Cell, ) { + let value_rlc = cb.word_rlc(value.limbs.clone().map(|l| l.expr())); let b = self.right_rlc(cb, value_left); let c = self.left_rlc(cb, value_right); @@ -573,20 +568,20 @@ impl MemoryMask { /// Return the RLC of the left part of a word, called "A" or "C". The right part is zeroed. /// The value is MSB-first so we read the mask in reverse. - fn left_rlc(&self, cb: &mut EVMConstraintBuilder, word: &Word) -> Expression { + fn left_rlc(&self, cb: &mut EVMConstraintBuilder, word: &Word32Cell) -> Expression { let masked: [Expression; N_BYTES_WORD] = array_init(|i| { let reversed_i = N_BYTES_WORD - 1 - i; - word.cells[i].expr() * self.mask[reversed_i].expr() + word.limbs[i].expr() * self.mask[reversed_i].expr() }); cb.word_rlc(masked) } /// Return the RLC of the right part of a word, called "B" or "D". The left part is zeroed. /// The value is MSB-first so we read the mask in reverse. - fn right_rlc(&self, cb: &mut EVMConstraintBuilder, word: &Word) -> Expression { + fn right_rlc(&self, cb: &mut EVMConstraintBuilder, word: &Word32Cell) -> Expression { let masked: [Expression; N_BYTES_WORD] = array_init(|i| { let reversed_i = N_BYTES_WORD - 1 - i; - word.cells[i].expr() * (1.expr() - self.mask[reversed_i].expr()) + word.limbs[i].expr() * (1.expr() - self.mask[reversed_i].expr()) }); cb.word_rlc(masked) } diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index ee6df2696b..be8444d4c8 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -3,13 +3,15 @@ use eth_types::Field; use gadgets::util::{and, not, Expr}; use halo2_proofs::plonk::Expression; -use crate::evm_circuit::step::{ExecutionState, ExecutionState::ErrorOutOfGasPrecompile}; - use super::{ constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::BinaryNumberGadget, CachedRegion, }; +use crate::evm_circuit::{ + step::{ExecutionState, ExecutionState::ErrorOutOfGasPrecompile}, + util::Word, +}; #[derive(Clone, Debug)] pub struct PrecompileGadget { @@ -19,13 +21,14 @@ pub struct PrecompileGadget { impl PrecompileGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - callee_address: Expression, + //callee_address: Expression, + callee_address: Word>, input_bytes_rlc: Expression, // for root call we do not need to constraint output and return output_bytes_rlc: Option>, return_bytes_rlc: Option>, ) -> Self { - let address = BinaryNumberGadget::construct(cb, callee_address.expr()); + let address = BinaryNumberGadget::construct(cb, callee_address.lo().expr()); macro_rules! constrain_next_state { ($cb:ident, $precompile_call_type:ident, $precompile_exec_state:ident) => { diff --git a/zkevm-circuits/src/exp_circuit.rs b/zkevm-circuits/src/exp_circuit.rs index afc3582b4c..fc0eba1c26 100644 --- a/zkevm-circuits/src/exp_circuit.rs +++ b/zkevm-circuits/src/exp_circuit.rs @@ -10,7 +10,7 @@ pub use dev::ExpCircuit as TestExpCircuit; use crate::{ evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - table::{ExpTable, LookupTable, U16Table}, + table::{ExpTable, LookupTable, UXTable}, util::{Challenges, SubCircuit, SubCircuitConfig}, witness, }; @@ -38,7 +38,8 @@ pub struct ExpCircuitConfig { /// The Exponentiation circuit's table. pub exp_table: ExpTable, /// u16 lookup table, - pub u16_table: U16Table, + // pub u16_table: U16Table, + pub u16_table: UXTable<16>, /// Multiplication gadget for verification of each step. pub mul_gadget: MulAddConfig, /// Multiplication gadget to perform 2*n + k. @@ -50,7 +51,8 @@ pub struct ExpCircuitArgs { /// The Exponentiation circuit's table. pub exp_table: ExpTable, /// u16 lookup table, - pub u16_table: U16Table, + // pub u16_table: U16Table, + pub u16_table: UXTable<16>, } impl SubCircuitConfig for ExpCircuitConfig { @@ -74,7 +76,7 @@ impl SubCircuitConfig for ExpCircuitConfig { meta.query_fixed(exp_table.is_step, Rotation::cur()), ]) }, - u16_table.into(), + u16_table.col, ); let parity_check = MulAddChip::configure( meta, @@ -84,7 +86,7 @@ impl SubCircuitConfig for ExpCircuitConfig { meta.query_fixed(exp_table.is_step, Rotation::cur()), ]) }, - u16_table.into(), + u16_table.col, ); // multiplier <- 2^64 diff --git a/zkevm-circuits/src/exp_circuit/dev.rs b/zkevm-circuits/src/exp_circuit/dev.rs index 1c2114186b..779cae8372 100644 --- a/zkevm-circuits/src/exp_circuit/dev.rs +++ b/zkevm-circuits/src/exp_circuit/dev.rs @@ -2,7 +2,7 @@ pub use super::ExpCircuit; use crate::{ exp_circuit::{ExpCircuitArgs, ExpCircuitConfig}, - table::{ExpTable, U16Table}, + table::{ExpTable, UXTable}, util::{Challenges, SubCircuit, SubCircuitConfig}, }; use eth_types::Field; @@ -24,7 +24,7 @@ impl Circuit for ExpCircuit { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let exp_table = ExpTable::construct(meta); let challenges = Challenges::construct(meta); - let u16_table = U16Table::construct(meta); + let u16_table = UXTable::construct(meta); ( ExpCircuitConfig::new( meta, diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index d776358895..2bc37d6aa2 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -34,7 +34,12 @@ use crate::{ split, split_uniform, transform, transform_to, Part, }, table::{KeccakTable, LookupTable}, - util::{Challenges, SubCircuit, SubCircuitConfig}, + util::{ + // word::{self, WordExpr}, + Challenges, + SubCircuit, + SubCircuitConfig, + }, witness, }; use eth_types::Field; @@ -108,6 +113,7 @@ impl SubCircuitConfig for KeccakCircuitConfig { let length = keccak_table.input_len; let data_rlc = keccak_table.input_rlc; let hash_rlc = keccak_table.output_rlc; + //let hash_word = keccak_table.output; let normalize_3 = array_init::array_init(|_| meta.lookup_table_column()); let normalize_4 = array_init::array_init(|_| meta.lookup_table_column()); @@ -576,6 +582,11 @@ impl SubCircuitConfig for KeccakCircuitConfig { let hash_bytes_le = hash_bytes.into_iter().rev().collect::>(); let rlc = compose_rlc::expr(&hash_bytes_le, challenges.evm_word()); cb.condition(start_new_hash, |cb| { + // cb.require_equal_word( + // "hash output check", + // word::Word32::new(hash_bytes_le.try_into().expect("32 limbs")).to_word(), + // hash_word.map(|col| meta.query_advice(col, Rotation::cur())), + // ); cb.require_equal( "hash rlc check", rlc, @@ -935,13 +946,20 @@ impl KeccakCircuitConfig { region, offset, [ - Value::known(F::from(row.is_final)), + Value::known(F::from(row.is_final as u64)), row.data_rlc, Value::known(F::from(row.length as u64)), row.hash_rlc, + row.hash.lo(), + row.hash.hi(), ], )?; + // work around to remove last two items(hi + lo parts), avoid to mistake hash output cells + // in method `extract_hash_cells` of aggregation circuit. + res.remove(res.len() - 1); + res.remove(res.len() - 1); + // Cell values for (idx, (bit, column)) in row .cell_values diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index 7c15785a68..6a7b5651b5 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -1,5 +1,8 @@ use super::{cell_manager::*, param::*, util::*}; -use crate::{evm_circuit::util::rlc, util::Challenges}; +use crate::{ + evm_circuit::util::rlc, + util::{word::Word, Challenges}, +}; use eth_types::Field; use halo2_proofs::{ circuit::Value, @@ -112,7 +115,9 @@ pub struct KeccakRow { /// The input length of the hash function pub length: usize, pub(crate) data_rlc: Value, + // TODO:finally remove hash_rlc, purely use last word hi lo pub(crate) hash_rlc: Value, + pub(crate) hash: Word>, } /// Part @@ -582,6 +587,7 @@ pub(crate) fn keccak( let mut cell_managers = Vec::new(); let mut regions = Vec::new(); + let mut hash = Word::default(); let mut hash_rlc = Value::known(F::zero()); let mut round_lengths = Vec::new(); let mut round_data_rlcs = Vec::new(); @@ -809,18 +815,24 @@ pub(crate) fn keccak( // The rlc of the hash let is_final = is_final_block && round == NUM_ROUNDS; - hash_rlc = if is_final { + (hash_rlc, hash) = if is_final { let hash_bytes_le = s .into_iter() .take(4) .flat_map(|a| to_bytes::value(&unpack(a[0]))) .rev() .collect::>(); - challenges + + let hash_rlc = challenges .evm_word() - .map(|challenge_value| rlc::value(&hash_bytes_le, challenge_value)) + .map(|challenge_value| rlc::value(&hash_bytes_le, challenge_value)); + let word: Word> = Word::from(eth_types::Word::from_little_endian( + hash_bytes_le.as_slice(), + )) + .map(Value::known); + (hash_rlc, word) } else { - Value::known(F::zero()) + (Value::known(F::zero()), Word::default().into_value()) }; // The words to squeeze out @@ -863,6 +875,7 @@ pub(crate) fn keccak( length: round_lengths[round], data_rlc: round_data_rlcs[round][row_idx], hash_rlc, + hash, cell_values: regions[round].rows[row_idx].clone(), }); } @@ -913,6 +926,7 @@ pub fn multi_keccak( length: 0usize, data_rlc: Value::known(F::zero()), hash_rlc: Value::known(F::zero()), + hash: Word::default().into_value(), cell_values: Vec::new(), }); } diff --git a/zkevm-circuits/src/mpt_circuit.rs b/zkevm-circuits/src/mpt_circuit.rs index 6900752e00..eef7b2da41 100644 --- a/zkevm-circuits/src/mpt_circuit.rs +++ b/zkevm-circuits/src/mpt_circuit.rs @@ -51,8 +51,8 @@ pub struct MptCircuitConfigArgs { pub poseidon_table: PoseidonTable, /// MptTable pub mpt_table: MptTable, - /// Challenges - pub challenges: Challenges, + // Challenges no need after word hi lo + // pub challenges: Challenges, } /// re-wrapping for mpt config @@ -71,10 +71,9 @@ impl SubCircuitConfig for MptCircuitConfig { Self::ConfigArgs { poseidon_table, mpt_table, - challenges, }: Self::ConfigArgs, ) -> Self { - let conf = mpt::MptCircuitConfig::configure(meta, challenges.evm_word(), &poseidon_table); + let conf = mpt::MptCircuitConfig::configure(meta, &poseidon_table); meta.lookup_any("updates in mpt table proven in mpt circuit", |meta| { mpt_table .table_exprs(meta) @@ -146,7 +145,7 @@ impl SubCircuit for MptCircuit { layouter, &self.mpt_updates, self.row_limit, - challenges.evm_word(), + //challenges.evm_word(), )?; } else { config.1.load( @@ -189,7 +188,6 @@ impl Circuit for MptCircuit { MptCircuitConfigArgs { poseidon_table, mpt_table, - challenges, }, ) }; diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index 40c95973bb..731a45a29b 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -33,6 +33,7 @@ use crate::{ }, state_circuit::StateCircuitExports, tx_circuit::{CHAIN_ID_OFFSET as CHAIN_ID_OFFSET_IN_TX, TX_HASH_OFFSET, TX_LEN}, + util::word, witness::{self, Block, BlockContext, BlockContexts, Transaction}, }; use bus_mapping::util::read_env_var; @@ -342,6 +343,8 @@ pub struct PiCircuitConfig { rpi_field_bytes: Column, // rpi in bytes rpi_field_bytes_acc: Column, rpi_rlc_acc: Column, // RLC(rpi) as the input to Keccak table + // the output word type to Keccak table, especially for data hash & pi hash. + rpi_word: word::Word>, rpi_length_acc: Column, // columns for padding in block context and tx hashes @@ -420,6 +423,8 @@ impl SubCircuitConfig for PiCircuitConfig { let rpi_bytes_acc = meta.advice_column_in(SecondPhase); // hold the accumulated value of rlc(rpi_bytes, keccak_input) let rpi_rlc_acc = meta.advice_column_in(SecondPhase); + let rpi_word = word::Word::new([meta.advice_column(), meta.advice_column()]); + // hold the accumulated length of rpi_bytes for looking into keccak table let rpi_length_acc = meta.advice_column(); @@ -460,9 +465,12 @@ impl SubCircuitConfig for PiCircuitConfig { meta.enable_equality(real_rpi); meta.enable_equality(block_table.value); // copy block to rpi meta.enable_equality(block_table.index); - meta.enable_equality(tx_table.value); // copy tx hashes to rpi + meta.enable_equality(tx_table.value.lo()); // copy tx hashes to rpi + meta.enable_equality(tx_table.value.hi()); // copy tx hashes to rpi meta.enable_equality(cum_num_txs); meta.enable_equality(pi); + meta.enable_equality(rpi_word.lo()); + meta.enable_equality(rpi_word.hi()); // 1. constrain rpi_bytes, rpi_bytes_acc, and rpi for each field meta.create_gate( @@ -639,6 +647,7 @@ impl SubCircuitConfig for PiCircuitConfig { let rpi_rlc = meta.query_advice(rpi, Rotation::cur()); let rpi_length = meta.query_advice(rpi_length_acc, Rotation::cur()); let output = meta.query_advice(rpi_rlc_acc, Rotation::cur()); + let output_word = rpi_word.query_advice(meta, Rotation::cur()); let input_exprs = vec![ 1.expr(), // q_enable = true @@ -646,6 +655,8 @@ impl SubCircuitConfig for PiCircuitConfig { rpi_rlc, rpi_length, output, + output_word.lo(), + output_word.hi(), ]; let keccak_table_exprs = keccak_table.table_exprs(meta); assert_eq!(input_exprs.len(), keccak_table_exprs.len()); @@ -698,6 +709,7 @@ impl SubCircuitConfig for PiCircuitConfig { rpi_field_bytes: rpi_bytes, rpi_field_bytes_acc: rpi_bytes_acc, rpi_rlc_acc, + rpi_word, rpi_length_acc, is_rpi_padding, real_rpi, @@ -722,8 +734,10 @@ type PiHashExport = Vec>; #[derive(Debug, Clone)] struct Connections { - start_state_root: AssignedCell, - end_state_root: AssignedCell, + start_state_root: word::Word>, + //start_state_root: AssignedCell, + end_state_root: word::Word>, + //end_state_root: AssignedCell, withdraw_root: AssignedCell, } @@ -790,7 +804,7 @@ impl PiCircuitConfig { region: &mut Region<'_, F>, public_data: &PublicData, block_value_cells: &[AssignedCell], - tx_value_cells: &[AssignedCell], + tx_value_cells: &[word::Word>], challenges: &Challenges>, ) -> Result<(PiHashExport, Connections), Error> { // 1. Assign data bytes. @@ -837,7 +851,7 @@ impl PiCircuitConfig { offset: usize, public_data: &PublicData, block_value_cells: &[AssignedCell], - tx_value_cells: &[AssignedCell], + tx_value_cells: &[word::Word>], challenges: &Challenges>, ) -> Result<(usize, AssignedCell), Error> { // Initialise the RLC accumulator and length values. @@ -971,6 +985,7 @@ impl PiCircuitConfig { rpi_rlc_acc = tmp_rpi_rlc_acc; rpi_length = tmp_rpi_length; tx_copy_cells.push(cells[RPI_CELL_IDX].clone()); + if i == public_data.max_txs - 1 { data_bytes_rlc = Some(cells[RPI_RLC_ACC_CELL_IDX].clone()); data_bytes_length = Some(cells[RPI_LENGTH_ACC_CELL_IDX].clone()); @@ -981,9 +996,10 @@ impl PiCircuitConfig { log::trace!("tx_value_cells: {:?}", tx_value_cells); for (i, tx_hash_cell) in tx_copy_cells.into_iter().enumerate() { + // tx hash value still use rlc format in tx circuit. region.constrain_equal( tx_hash_cell.cell(), - tx_value_cells[i * TX_LEN + TX_HASH_OFFSET - 1].cell(), + tx_value_cells[i * TX_LEN + TX_HASH_OFFSET - 1].lo().cell(), )?; } @@ -1006,6 +1022,10 @@ impl PiCircuitConfig { &public_data.get_data_hash().to_fixed_bytes(), challenges.evm_word(), ); + + let data_hash_word = + word::Word::from(public_data.get_data_hash().to_word()).map(Value::known); + data_hash_word.assign_advice(region, || "assign rpi_word", self.rpi_word, offset)?; region.assign_advice( || "data_hash_rlc", self.rpi_rlc_acc, @@ -1028,7 +1048,7 @@ impl PiCircuitConfig { offset: usize, public_data: &PublicData, block_value_cells: &[AssignedCell], - tx_value_cells: &[AssignedCell], + tx_value_cells: &[word::Word>], data_hash_rlc_cell: &AssignedCell, challenges: &Challenges>, ) -> Result<(usize, AssignedCell, Connections), Error> { @@ -1059,29 +1079,43 @@ impl PiCircuitConfig { rpi_length, challenges, )?; - Ok(cells[RPI_CELL_IDX].clone()) + Ok((cells[0].clone(), cells[3].clone(), cells[4].clone())) }) .collect::, Error>>()?; // copy chain_id to block table for block_idx in 0..public_data.max_inner_blocks { region.constrain_equal( - rpi_cells[0].cell(), + rpi_cells[0].0.cell(), block_value_cells[block_idx * BLOCK_LEN + CHAIN_ID_OFFSET].cell(), )?; } // copy chain_id to tx table for tx_id in 0..public_data.max_txs { + // region.constrain_equal( + // rpi_cells[0].0.cell(), + // tx_value_cells[tx_id * TX_LEN + CHAIN_ID_OFFSET_IN_TX - 1].cell(), + // )?; region.constrain_equal( - rpi_cells[0].cell(), - tx_value_cells[tx_id * TX_LEN + CHAIN_ID_OFFSET_IN_TX - 1].cell(), + rpi_cells[0].1.cell(), + tx_value_cells[tx_id * TX_LEN + CHAIN_ID_OFFSET_IN_TX - 1] + .lo() + .cell(), + )?; + region.constrain_equal( + rpi_cells[0].2.cell(), + tx_value_cells[tx_id * TX_LEN + CHAIN_ID_OFFSET_IN_TX - 1] + .hi() + .cell(), )?; } // connections to be done with other sub-circuits. let connections = Connections { - start_state_root: rpi_cells[1].clone(), - end_state_root: rpi_cells[2].clone(), - withdraw_root: rpi_cells[3].clone(), + //start_state_root: rpi_cells[1].clone(), + start_state_root: word::Word::new([rpi_cells[1].1.clone(), rpi_cells[1].2.clone()]), + // end_state_root: rpi_cells[2].clone(), + end_state_root: word::Word::new([rpi_cells[2].1.clone(), rpi_cells[2].2.clone()]), + withdraw_root: rpi_cells[3].0.clone(), }; // Assign data_hash @@ -1116,6 +1150,9 @@ impl PiCircuitConfig { self.rpi_length_acc, offset, )?; + + let pi_hash_word = word::Word::from(public_data.get_pi().to_word()).map(Value::known); + pi_hash_word.assign_advice(region, || "assign rpi_word", self.rpi_word, offset)?; let pi_hash_rlc_cell = { let pi_hash_rlc = rlc_be_bytes( &public_data.get_pi().to_fixed_bytes(), @@ -1156,7 +1193,7 @@ impl PiCircuitConfig { challenges, )?; (offset, rpi_rlc_acc, rpi_length) = (tmp_offset, tmp_rpi_rlc_acc, tmp_rpi_length); - let pi_hash_hi_cells = cells[3..].to_vec(); + let pi_hash_hi_cells = cells[5..].to_vec(); // the low 16 bytes of keccak output let (tmp_offset, _, _, cells) = self.assign_field( @@ -1170,7 +1207,7 @@ impl PiCircuitConfig { challenges, )?; offset = tmp_offset; - let pi_hash_lo_cells = cells[3..].to_vec(); + let pi_hash_lo_cells = cells[5..].to_vec(); // Copy pi_hash value we collected from assigning pi bytes. region.constrain_equal(pi_hash_rlc_cell.cell(), cells[RPI_RLC_ACC_CELL_IDX].cell())?; @@ -1244,6 +1281,7 @@ impl PiCircuitConfig { offset, F::zero(), )?; + region.assign_advice_from_constant( || "rpi_length_acc[0]", self.rpi_length_acc, @@ -1309,9 +1347,13 @@ impl PiCircuitConfig { self.q_field_step.enable(region, q_offset)?; } - let (mut final_rpi_cell, mut final_rpi_rlc_cell, mut final_rpi_length_cell) = - (None, None, None); - let cells = + let ( + mut final_rpi_cell, + mut final_rpi_rlc_cell, + mut final_rpi_word_cells, + mut final_rpi_length_cell, + ) = (None, None, None, None); + let cells: Vec<(AssignedCell, (AssignedCell, AssignedCell))> = value_be_bytes .iter() .zip(rpi_bytes_acc.iter()) @@ -1375,6 +1417,30 @@ impl PiCircuitConfig { row_offset, || rpi_rlc_acc, )?; + + let rpi_word = if value_be_bytes.len() >= 32 { + let value_word = Word::from_big_endian(&value_be_bytes[..32]); + word::Word::from(value_word).map(Value::known) + } else { + // no meaningful, just for final_rpi_word_cells dummy value + let len = value_be_bytes.len(); + let zero_iter = std::iter::repeat(0); // 生成一个无限重复0的迭代器 + let value_bytes_with_zero = zero_iter + .take(32 - len) + .chain(value_be_bytes.to_vec()) + .collect::>(); + // pad zero and take 32 bytes + let value_word = Word::from_big_endian(&value_bytes_with_zero[..32]); + word::Word::from(value_word).map(Value::known) + }; + + final_rpi_word_cells = Some(rpi_word.assign_advice( + region, + || "assign rpi_word", + self.rpi_word, + row_offset, + )?); + let rpi_length_cell = region.assign_advice( || "rpi_length_acc", self.rpi_length_acc, @@ -1441,6 +1507,7 @@ impl PiCircuitConfig { // ... // byte_cell[n_bytes - 1] // ] + let final_rpi_word_cells_unwrap = final_rpi_word_cells.unwrap(); Ok(( offset + n_bytes, rpi_rlc_acc, @@ -1450,6 +1517,8 @@ impl PiCircuitConfig { final_rpi_cell.unwrap(), final_rpi_rlc_cell.unwrap(), final_rpi_length_cell.unwrap(), + final_rpi_word_cells_unwrap.lo(), + final_rpi_word_cells_unwrap.hi(), ], byte_cells, ] @@ -1616,7 +1685,8 @@ pub struct PiCircuit { _marker: PhantomData, connections: RefCell>>, - tx_value_cells: RefCell>>>, + #[allow(clippy::type_complexity)] + tx_value_cells: RefCell>>>>, } impl PiCircuit { @@ -1668,7 +1738,7 @@ impl PiCircuit { } /// Import tx value cells from Tx circuit - pub fn import_tx_values(&self, values: Vec>) { + pub fn import_tx_values(&self, values: Vec>>) { *self.tx_value_cells.borrow_mut() = Some(values); } @@ -1696,12 +1766,20 @@ impl PiCircuit { ); region.constrain_equal( - local_conn.start_state_root.cell(), - state_roots.start_state_root.0, + local_conn.start_state_root.lo().cell(), + state_roots.start_state_root.0.lo(), + )?; + region.constrain_equal( + local_conn.start_state_root.hi().cell(), + state_roots.start_state_root.0.hi(), + )?; + region.constrain_equal( + local_conn.end_state_root.lo().cell(), + state_roots.end_state_root.0.lo(), )?; region.constrain_equal( - local_conn.end_state_root.cell(), - state_roots.end_state_root.0, + local_conn.end_state_root.hi().cell(), + state_roots.end_state_root.0.hi(), )?; } else { log::warn!("state roots are not set, skip connection with state circuit"); diff --git a/zkevm-circuits/src/rlp_circuit_fsm.rs b/zkevm-circuits/src/rlp_circuit_fsm.rs index 47cdede5e9..c412645ad0 100644 --- a/zkevm-circuits/src/rlp_circuit_fsm.rs +++ b/zkevm-circuits/src/rlp_circuit_fsm.rs @@ -7,7 +7,7 @@ mod test; use crate::{ evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - table::{LookupTable, RlpFsmRlpTable, U8Table}, + table::{LookupTable, RlpFsmRlpTable, UXTable}, util::{ is_zero::{IsZeroChip, IsZeroConfig}, Challenges, SubCircuit, SubCircuitConfig, @@ -377,7 +377,8 @@ pub struct RlpCircuitConfig { /// ROM table rom_table: RlpFsmRomTable, /// Range u8 table - u8_table: U8Table, + // u8_table: U8Table, + u8_table: UXTable<8>, } impl RlpCircuitConfig { @@ -386,7 +387,8 @@ impl RlpCircuitConfig { meta: &mut ConstraintSystem, rom_table: RlpFsmRomTable, data_table: RlpFsmDataTable, - u8_table: U8Table, + //u8_table: U8Table, + u8_table: UXTable<8>, rlp_table: RlpFsmRlpTable, rlp_decoding_table: RlpDecodingTable, challenges: &Challenges>, @@ -666,7 +668,7 @@ impl RlpCircuitConfig { cb.gate(meta.query_fixed(q_enabled, Rotation::cur())) }); - meta.lookup("byte value check", |meta| { + meta.lookup_any("byte value check", |meta| { let cond = and::expr([ meta.query_fixed(q_enabled, Rotation::cur()), not::expr(is_padding_in_dt.expr(Rotation::cur())(meta)), @@ -674,7 +676,7 @@ impl RlpCircuitConfig { vec![( cond * meta.query_advice(data_table.byte_value, Rotation::cur()), - u8_table.into(), + meta.query_fixed(u8_table.col, Rotation::cur()), )] }); @@ -747,7 +749,7 @@ impl RlpCircuitConfig { cmp_enabled, |meta| meta.query_advice(byte_value, Rotation::cur()), |_| $value.expr(), - u8_table.into(), + u8_table.col, ); }; } @@ -758,7 +760,7 @@ impl RlpCircuitConfig { cmp_enabled, |_| $value.expr(), |meta| meta.query_advice(byte_value, Rotation::cur()), - u8_table.into(), + u8_table.col, ); }; } @@ -841,21 +843,21 @@ impl RlpCircuitConfig { cmp_enabled, |meta| meta.query_advice(tag_idx, Rotation::cur()), |meta| meta.query_advice(tag_length, Rotation::cur()), - u8_table.into(), + u8_table.col, ); let mlength_lte_0x20 = ComparatorChip::configure( meta, cmp_enabled, |meta| meta.query_advice(max_length, Rotation::cur()), |_meta| 0x20.expr(), - u8_table.into(), + u8_table.col, ); let tlength_lte_mlength = ComparatorChip::configure( meta, cmp_enabled, |meta| meta.query_advice(tag_length, Rotation::cur()), |meta| meta.query_advice(max_length, Rotation::cur()), - u8_table.into(), + u8_table.col, ); let depth_check = IsEqualChip::configure( meta, @@ -2969,7 +2971,8 @@ pub struct RlpCircuitConfigArgs { /// RLP table. pub rlp_table: RlpFsmRlpTable, /// u8 table - pub u8_table: U8Table, + // pub u8_table: U8Table, + pub u8_table: UXTable<8>, /// Challenge API. pub challenges: Challenges>, } diff --git a/zkevm-circuits/src/rlp_circuit_fsm/dev.rs b/zkevm-circuits/src/rlp_circuit_fsm/dev.rs index ec775f397c..f2fc56f943 100644 --- a/zkevm-circuits/src/rlp_circuit_fsm/dev.rs +++ b/zkevm-circuits/src/rlp_circuit_fsm/dev.rs @@ -1,6 +1,6 @@ use crate::{ rlp_circuit_fsm::{RlpCircuit, RlpCircuitConfig, RlpCircuitConfigArgs}, - table::{RlpFsmRlpTable, U8Table}, + table::{RlpFsmRlpTable, UXTable}, util::{Challenges, SubCircuit, SubCircuitConfig}, witness::Transaction, }; @@ -23,8 +23,9 @@ impl Circuit for RlpCircuit { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let rlp_table = RlpFsmRlpTable::construct(meta); let challenges = Challenges::construct(meta); - let challenge_exprs = challenges.exprs(meta); - let u8_table = U8Table::construct(meta); + let challenge_exprs: Challenges> = + challenges.exprs(meta); + let u8_table = UXTable::construct(meta); let config = RlpCircuitConfig::new( meta, diff --git a/zkevm-circuits/src/sig_circuit.rs b/zkevm-circuits/src/sig_circuit.rs index 62a83f605f..1349ff0ea0 100644 --- a/zkevm-circuits/src/sig_circuit.rs +++ b/zkevm-circuits/src/sig_circuit.rs @@ -22,12 +22,12 @@ use crate::{ keccak_circuit::KeccakCircuit, sig_circuit::ecdsa::ecdsa_verify_no_pubkey_check, table::{KeccakTable, SigTable}, - util::{Challenges, Expr, SubCircuit, SubCircuitConfig}, + util::{word, Challenges, Expr, SubCircuit, SubCircuitConfig}, }; use eth_types::{ self, sign_types::{pk_bytes_le, pk_bytes_swap_endianness, SignData}, - Field, + Field, Word, }; use halo2_base::{ gates::{range::RangeConfig, GateInstructions, RangeInstructions}, @@ -58,7 +58,7 @@ use halo2_proofs::{ use ethers_core::utils::keccak256; use itertools::Itertools; use log::error; -use std::{iter, marker::PhantomData}; +use std::{iter, iter::Iterator, marker::PhantomData, vec::Vec}; /// Circuit configuration arguments pub struct SigCircuitConfigArgs { @@ -77,6 +77,8 @@ pub struct SigCircuitConfig { ecdsa_config: FpChip, /// An advice column to store RLC witnesses rlc_column: Column, + /// An advice column to store word keccak result + rlc_column_word: word::Word>, /// selector for keccak lookup table q_keccak: Selector, /// Used to lookup pk->pk_hash(addr) @@ -143,7 +145,7 @@ impl SubCircuitConfig for SigCircuitConfig { let rlc_column = meta.advice_column_in(halo2_proofs::plonk::FirstPhase); #[cfg(not(feature = "onephase"))] let rlc_column = meta.advice_column_in(halo2_proofs::plonk::SecondPhase); - + let rlc_column_word = word::Word::new([meta.advice_column(), meta.advice_column()]); meta.enable_equality(rlc_column); meta.enable_equality(sig_table.recovered_addr); @@ -152,6 +154,9 @@ impl SubCircuitConfig for SigCircuitConfig { meta.enable_equality(sig_table.sig_v); meta.enable_equality(sig_table.is_valid); meta.enable_equality(sig_table.msg_hash_rlc); + // consider to change msg_hash_rlc to word hi lo type in the future. + // meta.enable_equality(sig_table.msg_hash_word.lo()); + // meta.enable_equality(sig_table.msg_hash_word.hi()); // Ref. spec SignVerifyChip 1. Verify that keccak(pub_key_bytes) = pub_key_hash // by keccak table lookup, where pub_key_bytes is built from the pub_key @@ -177,7 +182,9 @@ impl SubCircuitConfig for SigCircuitConfig { is_enable.clone(), is_enable.clone() * meta.query_advice(rlc_column, Rotation(1)), is_enable.clone() * 64usize.expr(), - is_enable * meta.query_advice(rlc_column, Rotation(2)), + is_enable.clone() * meta.query_advice(rlc_column, Rotation(2)), + is_enable.clone() * meta.query_advice(rlc_column_word.lo(), Rotation::cur()), + is_enable * meta.query_advice(rlc_column_word.hi(), Rotation::cur()), ]; let table = [ meta.query_fixed(keccak_table.q_enable, Rotation::cur()), @@ -185,6 +192,8 @@ impl SubCircuitConfig for SigCircuitConfig { meta.query_advice(keccak_table.input_rlc, Rotation::cur()), meta.query_advice(keccak_table.input_len, Rotation::cur()), meta.query_advice(keccak_table.output_rlc, Rotation::cur()), + meta.query_advice(keccak_table.output.lo(), Rotation::cur()), + meta.query_advice(keccak_table.output.hi(), Rotation::cur()), ]; input.into_iter().zip(table).collect() @@ -196,6 +205,7 @@ impl SubCircuitConfig for SigCircuitConfig { sig_table, q_keccak, rlc_column, + rlc_column_word, } } } @@ -473,6 +483,14 @@ impl SigCircuit { offset, || is_address_zero.value, )?; + let word_zero = word::Word::new([Value::known(F::zero()), Value::known(F::zero())]); + word_zero.assign_advice( + &mut ctx.region, + || "assign rlc_column_word", + config.rlc_column_word, + offset + 1, + )?; + ctx.region .constrain_equal(is_address_zero.cell, tmp_cell.cell())?; @@ -483,6 +501,14 @@ impl SigCircuit { offset + 1, || pk_rlc.value, )?; + + word_zero.assign_advice( + &mut ctx.region, + || "assign rlc_column_word", + config.rlc_column_word, + offset + 2, + )?; + ctx.region.constrain_equal(pk_rlc.cell, tmp_cell.cell())?; // pk_hash_rlc @@ -507,7 +533,7 @@ impl SigCircuit { ecdsa_chip: &FpChip, sign_data: &SignData, assigned_data: &AssignedECDSA>, - ) -> Result, Error> { + ) -> Result<(SignDataDecomposed, Word), Error> { // build ecc chip from Fp chip let ecc_chip = EccChip::>::construct(ecdsa_chip.clone()); @@ -528,6 +554,7 @@ impl SigCircuit { let pk_le = pk_bytes_le(&sign_data.pk); let pk_be = pk_bytes_swap_endianness(&pk_le); let pk_hash = keccak256(pk_be).map(|byte| Value::known(F::from(byte as u64))); + let pk_hash_word = Word::from_big_endian(&keccak256(pk_be)); log::trace!("pk hash {:0x?}", pk_hash); let pk_hash_cells = pk_hash @@ -575,6 +602,7 @@ impl SigCircuit { &byte_cells, &powers_of_256_cells, )?; + Ok(byte_cells) }; @@ -634,15 +662,18 @@ impl SigCircuit { &assigned_data.integer_s, )?; - Ok(SignDataDecomposed { - pk_hash_cells, - msg_hash_cells: assigned_msg_hash_le, - pk_cells: assigned_pk_le_selected, - address, - is_address_zero, - r_cells, - s_cells, - }) + Ok(( + SignDataDecomposed { + pk_hash_cells, + msg_hash_cells: assigned_msg_hash_le, + pk_cells: assigned_pk_le_selected, + address, + is_address_zero, + r_cells, + s_cells, + }, + pk_hash_word, + )) } #[allow(clippy::too_many_arguments)] @@ -737,6 +768,7 @@ impl SigCircuit { s_rlc, v: assigned_ecdsa.v, }; + Ok((to_be_keccak_checked, assigned_sig_verif)) } @@ -759,7 +791,7 @@ impl SigCircuit { let mut first_pass = SKIP_FIRST_PASS; let ecdsa_chip = &config.ecdsa_config; - let assigned_sig_verifs = layouter.assign_region( + let assigned_sig_verifys = layouter.assign_region( || "ecdsa chip verification", |region| { if first_pass { @@ -795,7 +827,7 @@ impl SigCircuit { assigned_ecdsa, ) }) - .collect::>, Error>>()?; + .collect::, Word)>, Error>>()?; // IMPORTANT: Move to Phase2 before RLC log::info!("before proceeding to the next phase"); @@ -814,34 +846,38 @@ impl SigCircuit { let (assigned_keccak_values, assigned_sig_values): ( Vec<[AssignedValue; 3]>, Vec>, - ) = signatures - .iter() - .chain(std::iter::repeat(&SignData::default())) - .take(self.max_verif) - .zip_eq(assigned_ecdsas.iter()) - .zip_eq(sign_data_decomposed.iter()) - .map(|((sign_data, assigned_ecdsa), sign_data_decomp)| { - self.assign_sig_verify( - &mut ctx, - &ecdsa_chip.range, - sign_data, - sign_data_decomp, - challenges, - assigned_ecdsa, - ) - }) - .collect::; 3], AssignedSignatureVerify)>, - Error, - >>()? - .into_iter() - .unzip(); + ) = + signatures + .iter() + .chain(std::iter::repeat(&SignData::default())) + .take(self.max_verif) + .zip_eq(assigned_ecdsas.iter()) + .zip_eq(sign_data_decomposed.iter()) + .map(|((sign_data, assigned_ecdsa), sign_data_decomp)| { + self.assign_sig_verify( + &mut ctx, + &ecdsa_chip.range, + sign_data, + &sign_data_decomp.0, + challenges, + assigned_ecdsa, + ) + }) + .collect::; 3], AssignedSignatureVerify)>, + Error, + >>()? + .into_iter() + .unzip(); // ================================================ // step 4: deferred keccak checks // ================================================ - for (i, [is_address_zero, pk_rlc, pk_hash_rlc]) in - assigned_keccak_values.iter().enumerate() + for (i, ([is_address_zero, pk_rlc, pk_hash_rlc], sign_data)) in + assigned_keccak_values + .iter() + .zip_eq(sign_data_decomposed.iter()) + .enumerate() { let offset = i * 3; self.enable_keccak_lookup( @@ -852,6 +888,14 @@ impl SigCircuit { pk_rlc, pk_hash_rlc, )?; + + let pk_hash_word = word::Word::from(sign_data.1).map(Value::known); + pk_hash_word.assign_advice( + &mut ctx.region, + || "assign rlc_column_word", + config.rlc_column_word, + offset, + )?; } // IMPORTANT: this assigns all constants to the fixed columns @@ -871,7 +915,7 @@ impl SigCircuit { || "expose sig table", |mut region| { // step 5: export as a lookup table - for (idx, assigned_sig_verif) in assigned_sig_verifs.iter().enumerate() { + for (idx, assigned_sig_verif) in assigned_sig_verifys.iter().enumerate() { region.assign_fixed( || "assign sig_table selector", config.sig_table.q_enable, @@ -917,7 +961,7 @@ impl SigCircuit { }, )?; - Ok(assigned_sig_verifs) + Ok(assigned_sig_verifys) } /// Assert an CRTInteger's byte representation is correct. diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 94f5427468..6fc5158a01 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -4,7 +4,7 @@ mod lexicographic_ordering; mod lookups; mod multiple_precision_integer; mod param; -mod random_linear_combination; +// mod random_linear_combination; #[cfg(any(feature = "test", test, feature = "test-circuits"))] mod dev; @@ -19,13 +19,13 @@ use self::{ lexicographic_ordering::LimbIndex, }; use crate::{ - evm_circuit::{param::N_BYTES_WORD, util::rlc}, - table::{AccountFieldTag, LookupTable, MptTable, RwTable, RwTableTag}, - util::{Challenges, Expr, SubCircuit, SubCircuitConfig}, + evm_circuit::param::N_BYTES_WORD, + table::{AccountFieldTag, LookupTable, MptTable, RwTable, RwTableTag, UXTable}, + util::{word, Challenges, Expr, SubCircuit, SubCircuitConfig}, witness::{self, MptUpdates, Rw, RwMap}, }; use constraint_builder::{ConstraintBuilder, Queries}; -use eth_types::{Address, Field, ToLittleEndian}; +use eth_types::{Address, Field, Word}; use gadgets::{ batched_is_zero::{BatchedIsZeroChip, BatchedIsZeroConfig}, binary_number::{BinaryNumberChip, BinaryNumberConfig}, @@ -40,7 +40,7 @@ use lexicographic_ordering::Config as LexicographicOrderingConfig; use lookups::{Chip as LookupsChip, Config as LookupsConfig, Queries as LookupsQueries}; use multiple_precision_integer::{Chip as MpiChip, Config as MpiConfig, Queries as MpiQueries}; use param::*; -use random_linear_combination::{Chip as RlcChip, Config as RlcConfig, Queries as RlcQueries}; +// use random_linear_combination::{Chip as RlcChip, Config as RlcConfig, Queries as RlcQueries}; use std::marker::PhantomData; #[cfg(feature = "onephase")] @@ -62,14 +62,14 @@ pub struct StateCircuitConfig { // Assigned value at the start of the block. For Rw::Account and // Rw::AccountStorage rows this is the committed value in the MPT, for // others, it is 0. - initial_value: Column, + initial_value: word::Word>, // For Rw::AccountStorage, identify non-existing if both committed value and // new value are zero. Will do lookup for MPTProofType::NonExistingStorageProof if // non-existing, otherwise do lookup for MPTProofType::StorageMod. is_non_exist: BatchedIsZeroConfig, // Intermediary witness used to reduce mpt lookup expression degree mpt_proof_type: Column, - state_root: Column, + state_root: word::Word>, lexicographic_ordering: LexicographicOrderingConfig, not_first_access: Column, lookups: LookupsConfig, @@ -84,17 +84,26 @@ pub struct StateCircuitConfigArgs { pub rw_table: RwTable, /// MptTable pub mpt_table: MptTable, + /// U8Table + pub u8_table: UXTable<8>, + /// U10Table + pub u10_table: UXTable<10>, + /// U16Table + pub u16_table: UXTable<16>, /// Challenges pub challenges: Challenges>, } /// Circuit exported cells after synthesis, used for subcircuit #[derive(Clone, Debug)] +//pub struct StateCircuitExports { pub struct StateCircuitExports { /// start state root - pub start_state_root: (Cell, Value), + //pub start_state_root: (Cell, Value), + pub start_state_root: (word::Word, word::Word>), /// final state root - pub end_state_root: (Cell, Value), + //pub end_state_root: (Cell, Value), + pub end_state_root: (word::Word, word::Word>), } impl SubCircuitConfig for StateCircuitConfig { @@ -106,28 +115,32 @@ impl SubCircuitConfig for StateCircuitConfig { Self::ConfigArgs { rw_table, mpt_table, + u8_table, + u10_table, + u16_table, challenges, }: Self::ConfigArgs, ) -> Self { let selector = rw_table.q_enable; log::debug!("state circuit selector {:?}", selector); - let lookups = LookupsChip::configure(meta); - let power_of_randomness: [Expression; 31] = challenges.evm_word_powers_of_randomness(); + let lookups = LookupsChip::configure(meta, u8_table, u10_table, u16_table); - let rw_counter = MpiChip::configure(meta, selector, rw_table.rw_counter, lookups); + let rw_counter = MpiChip::configure(meta, selector, [rw_table.rw_counter], lookups); let tag = BinaryNumberChip::configure(meta, selector, Some(rw_table.tag.into())); - let id = MpiChip::configure(meta, selector, rw_table.id, lookups); - let address = MpiChip::configure(meta, selector, rw_table.address, lookups); + let id = MpiChip::configure(meta, selector, [rw_table.id], lookups); + let address = MpiChip::configure(meta, selector, [rw_table.address], lookups); - let storage_key = RlcChip::configure( + let storage_key = MpiChip::configure( meta, selector, - rw_table.storage_key, + [rw_table.storage_key.lo(), rw_table.storage_key.hi()], lookups, - challenges.evm_word(), ); - let initial_value = meta.advice_column_in(SecondPhase); + //let storage_key_hi = MpiChip::configure(meta, selector, rw_table.storage_key.hi(), + // lookups); + + let initial_value = word::Word::new([meta.advice_column(), meta.advice_column()]); // If the rw lookup is for an Account with field tag = CodeHash and both values are 0, we // actually want to do an mpt lookup for an non-existing account instead of an mpt lookup // for the code hash. Similarly, if the rw lookup is for an storage key with both values = @@ -142,14 +155,17 @@ impl SubCircuitConfig for StateCircuitConfig { [ meta.query_advice(rw_table.field_tag, Rotation::cur()) - AccountFieldTag::CodeHash.expr(), - meta.query_advice(initial_value, Rotation::cur()), - meta.query_advice(rw_table.value, Rotation::cur()), + meta.query_advice(initial_value.lo(), Rotation::cur()) + + meta.query_advice(initial_value.hi(), Rotation::cur()), + meta.query_advice(rw_table.value.lo(), Rotation::cur()) + + meta.query_advice(rw_table.value.hi(), Rotation::cur()), ] }, ); let mpt_proof_type = meta.advice_column_in(SecondPhase); - let state_root = meta.advice_column_in(SecondPhase); - meta.enable_equality(state_root); + let state_root = word::Word::new([meta.advice_column(), meta.advice_column()]); + meta.enable_equality(state_root.hi()); + meta.enable_equality(state_root.lo()); let sort_keys = SortKeysConfig { tag, @@ -159,7 +175,8 @@ impl SubCircuitConfig for StateCircuitConfig { storage_key, rw_counter, }; - + // challenges.evm_word_powers_of_randomness(); + let power_of_randomness: [Expression; 31] = challenges.keccak_powers_of_randomness(); let lexicographic_ordering = LexicographicOrderingConfig::configure( meta, sort_keys, @@ -170,6 +187,9 @@ impl SubCircuitConfig for StateCircuitConfig { // annotate columns rw_table.annotate_columns(meta); mpt_table.annotate_columns(meta); + u8_table.annotate_columns(meta); + u10_table.annotate_columns(meta); + u16_table.annotate_columns(meta); let config = Self { selector, @@ -212,14 +232,12 @@ impl StateCircuitConfig { layouter: &mut impl Layouter, rows: &[Rw], updates: &MptUpdates, - n_rows: usize, // 0 means dynamically calculated from `rows`. - challenges: &Challenges>, + n_rows: usize, /* 0 means dynamically calculated from `rows`. + *challenges: &Challenges>, */ ) -> Result<(), Error> { layouter.assign_region( || "state circuit (StateCircuitConfig)", - |mut region| { - self.assign_with_region(&mut region, rows, updates, n_rows, challenges.evm_word()) - }, + |mut region| self.assign_with_region(&mut region, rows, updates, n_rows), )?; Ok(()) } @@ -229,8 +247,8 @@ impl StateCircuitConfig { region: &mut Region<'_, F>, rows: &[Rw], updates: &MptUpdates, - n_rows: usize, // 0 means dynamically calculated from `rows`. - randomness: Value, + n_rows: usize, /* 0 means dynamically calculated from `rows`. + * randomness: Value, */ ) -> Result>, Error> { let tag_chip = BinaryNumberChip::construct(self.sort_keys.tag); @@ -243,11 +261,10 @@ impl StateCircuitConfig { ); let rows_len = rows.len(); - let mut state_root = - randomness.map(|randomness| rlc::value(&updates.old_root().to_le_bytes(), randomness)); + let mut state_root = updates.old_root(); - let mut start_state_root: Option> = None; - let mut end_state_root: Option> = None; + let mut start_state_root: Option>> = None; + let mut end_state_root: Option>> = None; // annotate columns self.annotate_circuit_in_region(region); @@ -280,7 +297,7 @@ impl StateCircuitConfig { if let Some(storage_key) = row.storage_key() { self.sort_keys .storage_key - .assign(region, offset, randomness, storage_key)?; + .assign(region, offset, storage_key)?; } if offset > 0 { @@ -300,134 +317,151 @@ impl StateCircuitConfig { if is_first_access { // If previous row was a last access, we need to update the state root. - state_root = randomness - .zip(state_root) - .map(|(randomness, mut state_root)| { - if let Some(update) = updates.get(prev_row) { - let (new_root, old_root) = update.root_assignments(randomness); - if state_root != old_root { - log::error!("invalid root randomness {:?}, state_root {:?}, prev_row {:?} update {:?}", - randomness, state_root, prev_row, update); - assert_eq!(state_root, old_root); - } - state_root = new_root; - } - if matches!(row.tag(), RwTableTag::CallContext) - && !row.is_write() - && row.value_assignment(randomness) != F::zero() - { - log::error!("invalid call context: {:?}", row); - } - state_root - }); + if let Some(update) = updates.get(row) { + state_root = { + let (new_root, old_root) = update.root_assignments(); + assert_eq!(state_root, old_root); + new_root + }; + } + + word::Word::::from(state_root) + .into_value() + .assign_advice(region, || "last row state_root", self.state_root, offset)?; + + if matches!(row.tag(), RwTableTag::CallContext) + && !row.is_write() + && row.value_assignment() != Word::zero() + { + log::error!("invalid call context: {:?}", row); + } } } // The initial value can be determined from the mpt updates or is 0. - let initial_value = randomness.map(|randomness| { + let initial_value = word::Word::::from( updates .get(row) - .map(|u| u.value_assignments(randomness).1) - .unwrap_or_default() - }); - region.assign_advice( + .map(|u| u.value_assignments().1) + .unwrap_or_default(), + ); + + initial_value.into_value().assign_advice( + region, || "initial_value", self.initial_value, offset, - || initial_value, )?; // Identify non-existing if both committed value and new value are zero and field tag is // CodeHash - let is_non_exist_inputs = randomness.map(|randomness| { + let (committed_value, value) = { let (_, committed_value) = updates .get(row) - .map(|u| u.value_assignments(randomness)) + .map(|u| u.value_assignments()) .unwrap_or_default(); - let value = row.value_assignment(randomness); - [ - F::from(row.field_tag().unwrap_or_default()) - - F::from(AccountFieldTag::CodeHash as u64), - committed_value, - value, - ] - }); + let value = row.value_assignment(); + ( + word::Word::::from(committed_value), + word::Word::::from(value), + ) + }; BatchedIsZeroChip::construct(self.is_non_exist.clone()).assign( region, offset, - is_non_exist_inputs, + Value::known([ + //F::from(row.field_tag().unwrap() - (AccountFieldTag::CodeHash as u64)), + committed_value.lo(), + committed_value.hi(), + value.lo(), + value.hi(), + ]), )?; - let mpt_proof_type = is_non_exist_inputs.map(|[_field_tag, committed_value, value]| { - F::from(match row { - Rw::AccountStorage { .. } => { - if committed_value.is_zero_vartime() && value.is_zero_vartime() { - MPTProofType::StorageDoesNotExist as u64 - } else { - MPTProofType::StorageChanged as u64 - } + + let mpt_proof_type = match row { + Rw::AccountStorage { .. } => { + if committed_value.is_zero_vartime() && value.is_zero_vartime() { + MPTProofType::StorageDoesNotExist as u64 + } else { + MPTProofType::StorageChanged as u64 } - Rw::Account { field_tag, .. } => { - if committed_value.is_zero_vartime() - && value.is_zero_vartime() - && matches!(field_tag, AccountFieldTag::CodeHash) - { - MPTProofType::AccountDoesNotExist as u64 - } else { - *field_tag as u64 - } + } + Rw::Account { field_tag, .. } => { + if committed_value.is_zero_vartime() + && value.is_zero_vartime() + && matches!(field_tag, AccountFieldTag::CodeHash) + { + MPTProofType::AccountDoesNotExist as u64 + } else { + *field_tag as u64 } - _ => 0, - }) - }); + } + _ => 0, + }; + region.assign_advice( || "mpt_proof_type", self.mpt_proof_type, offset, - || mpt_proof_type, + || Value::known(F::from(mpt_proof_type)), )?; // TODO: Switch from Rw::Start -> Rw::Padding to simplify this logic. // State root assignment is at previous row (offset - 1) because the state root // changes on the last access row. if offset != 0 { - let assigned = region.assign_advice( - || "state_root", - self.state_root, - offset - 1, - || state_root, - )?; + let assigned = word::Word::::from(state_root) + .into_value() + .assign_advice(region, || "state root", self.state_root, offset - 1)?; + if start_state_root.is_none() { start_state_root.replace(assigned); } } - if offset + 1 == rows_len { + if offset == rows_len - 1 { // The last row is always a last access, so we need to handle the case where the // state root changes because of an mpt lookup on the last row. if let Some(update) = updates.get(row) { - state_root = randomness.zip(state_root).map(|(randomness, state_root)| { - let (new_root, old_root) = update.root_assignments(randomness); - if !state_root.is_zero_vartime() { - assert_eq!(state_root, old_root); - } + state_root = { + let (new_root, old_root) = update.root_assignments(); + assert_eq!(state_root, old_root); new_root - }); + }; } - let assigned = region.assign_advice( - || "last row state_root", - self.state_root, - offset, - || state_root, - )?; + + let assigned = word::Word::::from(state_root) + .into_value() + .assign_advice(region, || "last row state_root", self.state_root, offset)?; + end_state_root.replace(assigned); } } let start_state_root = start_state_root.expect("should be assigned"); let end_state_root = end_state_root.expect("should be assigned"); + + // Ok(StateCircuitExports { + // start_state_root: (start_state_root.cell(), start_state_root.value_field()), + // end_state_root: (end_state_root.cell(), end_state_root.value_field()), + Ok(StateCircuitExports { - start_state_root: (start_state_root.cell(), start_state_root.value_field()), - end_state_root: (end_state_root.cell(), end_state_root.value_field()), + start_state_root: ( + //start_state_root, + word::Word::new([start_state_root.lo().cell(), start_state_root.hi().cell()]), + word::Word::new([ + start_state_root.lo().value_field(), + start_state_root.hi().value_field(), + ]), + //start_state_root.into_value(), + ), + end_state_root: ( + word::Word::new([end_state_root.lo().cell(), end_state_root.hi().cell()]), + word::Word::new([ + end_state_root.lo().value_field(), + end_state_root.hi().value_field(), + ]), + ), }) } @@ -437,7 +471,7 @@ impl StateCircuitConfig { rows: &[Rw], indices: &[usize], updates: &MptUpdates, - randomness: Value, + _randomness: Value, ) -> Result, Error> { let mut is_first_access_vec = Vec::with_capacity(indices.len()); let tag_chip = BinaryNumberChip::construct(self.sort_keys.tag); @@ -472,7 +506,7 @@ impl StateCircuitConfig { if let Some(storage_key) = row.storage_key() { self.sort_keys .storage_key - .assign(region, offset, randomness, storage_key)?; + .assign(region, offset, storage_key)?; } if idx > 0 { @@ -493,68 +527,74 @@ impl StateCircuitConfig { } // The initial value can be determined from the mpt updates or is 0. - let initial_value = randomness.map(|randomness| { + let initial_value = word::Word::::from( updates .get(row) - .map(|u| u.value_assignments(randomness).1) - .unwrap_or_default() - }); - region.assign_advice( + .map(|u| u.value_assignments().1) + .unwrap_or_default(), + ); + + initial_value.into_value().assign_advice( + region, || "initial_value", self.initial_value, offset, - || initial_value, )?; // Identify non-existing if both committed value and new value are zero and field tag is // CodeHash - let is_non_exist_inputs = randomness.map(|randomness| { + let (committed_value, value) = { let (_, committed_value) = updates .get(row) - .map(|u| u.value_assignments(randomness)) + .map(|u| u.value_assignments()) .unwrap_or_default(); - let value = row.value_assignment(randomness); - [ - F::from(row.field_tag().unwrap_or_default()) - - F::from(AccountFieldTag::CodeHash as u64), - committed_value, - value, - ] - }); + let value = row.value_assignment(); + ( + word::Word::::from(committed_value), + word::Word::::from(value), + ) + }; + BatchedIsZeroChip::construct(self.is_non_exist.clone()).assign( region, offset, - is_non_exist_inputs, + Value::known([ + F::from(row.field_tag().unwrap_or_default()) + - F::from(AccountFieldTag::CodeHash as u64), + committed_value.lo() + committed_value.hi(), + value.lo() + value.hi(), + ]), )?; - let mpt_proof_type = is_non_exist_inputs.map(|[_field_tag, committed_value, value]| { - F::from(match row { - Rw::AccountStorage { .. } => { - if committed_value.is_zero_vartime() && value.is_zero_vartime() { - MPTProofType::StorageDoesNotExist as u64 - } else { - MPTProofType::StorageChanged as u64 - } + + let mpt_proof_type = match row { + Rw::AccountStorage { .. } => { + if committed_value.is_zero_vartime() && value.is_zero_vartime() { + MPTProofType::StorageDoesNotExist as u64 + } else { + MPTProofType::StorageChanged as u64 } - Rw::Account { field_tag, .. } => { - if committed_value.is_zero_vartime() - && value.is_zero_vartime() - && matches!(field_tag, AccountFieldTag::CodeHash) - { - MPTProofType::AccountDoesNotExist as u64 - } else { - *field_tag as u64 - } + } + Rw::Account { field_tag, .. } => { + if committed_value.is_zero_vartime() + && value.is_zero_vartime() + && matches!(field_tag, AccountFieldTag::CodeHash) + { + MPTProofType::AccountDoesNotExist as u64 + } else { + *field_tag as u64 } - _ => 0, - }) - }); + } + _ => 0, + }; + region.assign_advice( || "mpt_proof_type", self.mpt_proof_type, offset, - || mpt_proof_type, + || Value::known(F::from(mpt_proof_type)), )?; } + Ok(is_first_access_vec) } @@ -566,15 +606,14 @@ impl StateCircuitConfig { padding_length: usize, is_first_access_vec: &[bool], updates: &MptUpdates, - randomness: Value, + _randomness: Value, ) -> Result>, Error> { let rows_len = rows.len(); - let mut state_root = - randomness.map(|randomness| rlc::value(&updates.old_root().to_le_bytes(), randomness)); + let mut state_root = updates.old_root(); - let mut start_state_root: Option> = None; - let mut end_state_root: Option> = None; + let mut start_state_root: Option>> = None; + let mut end_state_root: Option>> = None; for (offset, (row, is_first_access)) in rows.iter().zip_eq(is_first_access_vec.iter()).enumerate() @@ -588,26 +627,15 @@ impl StateCircuitConfig { if *is_first_access { // If previous row was a last access, we need to update the state root. - state_root = randomness - .zip(state_root) - .map(|(randomness, mut state_root)| { - if let Some(update) = updates.get(prev_row) { - let (new_root, old_root) = update.root_assignments(randomness); - if state_root != old_root { - log::error!("invalid root randomness {:?}, state_root {:?}, prev_row {:?} update {:?}", - randomness, state_root, prev_row, update); - assert_eq!(state_root, old_root); - } - state_root = new_root; - } - if matches!(row.tag(), RwTableTag::CallContext) - && !row.is_write() - && row.value_assignment(randomness) != F::zero() - { - log::error!("invalid call context: {:?}", row); - } - state_root - }); + // let (new_root, old_root) = updates.root_assignments(); + if let Some(update) = updates.get(prev_row) { + let (new_root, old_root) = update.root_assignments(); + assert_eq!(state_root, old_root); + state_root = new_root; + } + if matches!(row.tag(), RwTableTag::CallContext) && !row.is_write() { + assert_eq!(row.value_assignment(), 0.into(), "{row:?}"); + } } } @@ -615,35 +643,29 @@ impl StateCircuitConfig { // State root assignment is at previous row (offset - 1) because the state root // changes on the last access row. if offset != 0 { - let assigned = region.assign_advice( - || "state_root", - self.state_root, - offset - 1, - || state_root, - )?; + let assigned = word::Word::::from(state_root) + .into_value() + .assign_advice(region, || "state root", self.state_root, offset - 1)?; + if start_state_root.is_none() { start_state_root.replace(assigned); } } - if offset + 1 == rows_len { + if offset == rows_len - 1 { // The last row is always a last access, so we need to handle the case where the // state root changes because of an mpt lookup on the last row. if let Some(update) = updates.get(row) { - state_root = randomness.zip(state_root).map(|(randomness, state_root)| { - let (new_root, old_root) = update.root_assignments(randomness); - if !state_root.is_zero_vartime() { - assert_eq!(state_root, old_root); - } + state_root = { + let (new_root, old_root) = update.root_assignments(); + assert_eq!(state_root, old_root); new_root - }); + }; } - let assigned = region.assign_advice( - || "last row state_root", - self.state_root, - offset, - || state_root, - )?; + let assigned = word::Word::::from(state_root) + .into_value() + .assign_advice(region, || "last row state_root", self.state_root, offset)?; + end_state_root.replace(assigned); } } @@ -652,8 +674,20 @@ impl StateCircuitConfig { let end_state_root = end_state_root.expect("should be assigned"); Ok(StateCircuitExports { - start_state_root: (start_state_root.cell(), start_state_root.value_field()), - end_state_root: (end_state_root.cell(), end_state_root.value_field()), + start_state_root: ( + word::Word::new([start_state_root.lo().cell(), start_state_root.hi().cell()]), + word::Word::new([ + start_state_root.lo().value_field(), + start_state_root.hi().value_field(), + ]), + ), + end_state_root: ( + word::Word::new([end_state_root.lo().cell(), end_state_root.hi().cell()]), + word::Word::new([ + end_state_root.lo().value_field(), + end_state_root.hi().value_field(), + ]), + ), }) } @@ -727,7 +761,6 @@ impl StateCircuitConfig { )?; let mut is_first_time_vec = vec![true; chunk_num]; - let column = self.initial_value; // Each sub-region handle a chunk of RW rows. In part 2, since we need to read previous row, // we pass the rw_rows to each sub-region. let is_first_access_chunks = layouter.assign_regions( @@ -741,13 +774,20 @@ impl StateCircuitConfig { move |mut region: Region<'_, F>| { if *is_first_time { *is_first_time = false; - region.assign_advice( + word::Word::default().into_value().assign_advice( + &mut region, || "initial_value", - column, - // indices won't be empty + self.initial_value, indices.len() - 1, - || Value::known(F::zero()), )?; + // upstream rename to initial_value, disable for scroll + // region.assign_advice( + // || "initial_value", + // column, + // // indices won't be empty + // indices.len() - 1, + // || Value::known(F::zero()), + // )?; return Ok(vec![]); } @@ -829,9 +869,11 @@ impl StateCircuitConfig { self.sort_keys.annotate_columns_in_region(region, "STATE"); region.name_column(|| "STATE_selector", self.selector); region.name_column(|| "STATE_not_first_access", self.not_first_access); - region.name_column(|| "STATE_phase2_initial_value", self.initial_value); - region.name_column(|| "STATE_phase2_mpt_proof_type", self.mpt_proof_type); - region.name_column(|| "STATE_phase2_state_root", self.state_root); + region.name_column(|| "STATE_initial_value lo", self.initial_value.lo()); + region.name_column(|| "STATE_initial_value hi", self.initial_value.hi()); + region.name_column(|| "STATE_mpt_proof_type", self.mpt_proof_type); + region.name_column(|| "STATE_state_root lo", self.state_root.lo()); + region.name_column(|| "STATE_state_root hi", self.state_root.hi()); } } @@ -842,7 +884,7 @@ pub struct SortKeysConfig { id: MpiConfig, address: MpiConfig, field_tag: Column, - storage_key: RlcConfig, + storage_key: MpiConfig, rw_counter: MpiConfig, } @@ -999,7 +1041,6 @@ impl SubCircuit for StateCircuit { &self.rows, &self.updates, self.n_rows, - randomness, )?; if self.exports.borrow().is_none() { self.exports.borrow_mut().replace(exports); @@ -1038,6 +1079,17 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) let final_bits_sum = meta.query_advice(first_different_limb.bits[3], Rotation::cur()) + meta.query_advice(first_different_limb.bits[4], Rotation::cur()); + let mpt_update_table_expressions = c.mpt_table.table_exprs(meta); + assert_eq!(mpt_update_table_expressions.len(), 13); + + let meta_query_word = + |metap: &mut VirtualCells<'_, F>, word_column: word::Word>, at: Rotation| { + word::Word::new([ + metap.query_advice(word_column.lo(), at), + metap.query_advice(word_column.hi(), at), + ]) + }; + Queries { selector: meta.query_fixed(c.selector, Rotation::cur()), // TODO: use LookupTable trait here. @@ -1051,20 +1103,35 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) address: meta.query_advice(c.rw_table.address, Rotation::cur()), prev_address: meta.query_advice(c.rw_table.address, Rotation::prev()), field_tag: meta.query_advice(c.rw_table.field_tag, Rotation::cur()), - storage_key: meta.query_advice(c.rw_table.storage_key, Rotation::cur()), - value: meta.query_advice(c.rw_table.value, Rotation::cur()), - value_prev: meta.query_advice(c.rw_table.value, Rotation::prev()), - value_prev_column: meta.query_advice(c.rw_table.value_prev, Rotation::cur()), + storage_key: meta_query_word(meta, c.rw_table.storage_key, Rotation::cur()), + value: meta_query_word(meta, c.rw_table.value, Rotation::cur()), + value_prev: meta_query_word(meta, c.rw_table.value, Rotation::prev()), + value_prev_column: meta_query_word(meta, c.rw_table.value_prev, Rotation::cur()), }, mpt_update_table: MptUpdateTableQueries { - q_enable: meta.query_fixed(c.mpt_table.q_enable, Rotation::cur()), - address: meta.query_advice(c.mpt_table.address, Rotation::cur()), - storage_key: meta.query_advice(c.mpt_table.storage_key, Rotation::cur()), - proof_type: meta.query_advice(c.mpt_table.proof_type, Rotation::cur()), - new_root: meta.query_advice(c.mpt_table.new_root, Rotation::cur()), - old_root: meta.query_advice(c.mpt_table.old_root, Rotation::cur()), - new_value: meta.query_advice(c.mpt_table.new_value, Rotation::cur()), - old_value: meta.query_advice(c.mpt_table.old_value, Rotation::cur()), + q_enable: mpt_update_table_expressions[0].clone(), + address: mpt_update_table_expressions[1].clone(), + storage_key: word::Word::new([ + mpt_update_table_expressions[2].clone(), + mpt_update_table_expressions[3].clone(), + ]), + proof_type: mpt_update_table_expressions[4].clone(), + new_root: word::Word::new([ + mpt_update_table_expressions[5].clone(), + mpt_update_table_expressions[6].clone(), + ]), + old_root: word::Word::new([ + mpt_update_table_expressions[7].clone(), + mpt_update_table_expressions[8].clone(), + ]), + new_value: word::Word::new([ + mpt_update_table_expressions[9].clone(), + mpt_update_table_expressions[10].clone(), + ]), + old_value: word::Word::new([ + mpt_update_table_expressions[11].clone(), + mpt_update_table_expressions[12].clone(), + ]), }, lexicographic_ordering_selector: meta .query_fixed(c.lexicographic_ordering.selector, Rotation::cur()), @@ -1085,9 +1152,9 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) + meta.query_advice(first_different_limb.bits[2], Rotation::cur())) + final_bits_sum.clone() * (1.expr() - final_bits_sum), address: MpiQueries::new(meta, c.sort_keys.address), - storage_key: RlcQueries::new(meta, c.sort_keys.storage_key), - initial_value: meta.query_advice(c.initial_value, Rotation::cur()), - initial_value_prev: meta.query_advice(c.initial_value, Rotation::prev()), + storage_key: MpiQueries::new(meta, c.sort_keys.storage_key), + initial_value: meta_query_word(meta, c.initial_value, Rotation::cur()), + initial_value_prev: meta_query_word(meta, c.initial_value, Rotation::prev()), is_non_exist: meta.query_advice(c.is_non_exist.is_zero, Rotation::cur()), mpt_proof_type: meta.query_advice(c.mpt_proof_type, Rotation::cur()), lookups: LookupsQueries::new(meta, c.lookups), @@ -1096,8 +1163,8 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) .map(|idx| meta.query_advice(first_different_limb.bits[idx], Rotation::cur())), not_first_access: meta.query_advice(c.not_first_access, Rotation::cur()), last_access: 1.expr() - meta.query_advice(c.not_first_access, Rotation::next()), - state_root: meta.query_advice(c.state_root, Rotation::cur()), - state_root_prev: meta.query_advice(c.state_root, Rotation::prev()), + state_root: meta_query_word(meta, c.state_root, Rotation::cur()), + state_root_prev: meta_query_word(meta, c.state_root, Rotation::prev()), } } diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index 8b60726bd7..8672f0d95f 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -1,11 +1,12 @@ use super::{ - lookups::Queries as LookupsQueries, multiple_precision_integer::Queries as MpiQueries, - param::*, random_linear_combination::Queries as RlcQueries, + lookups::Queries as LookupsQueries, + multiple_precision_integer::Queries as MpiQueries, + param::*, // random_linear_combination::Queries as RlcQueries, }; use crate::{ evm_circuit::{param::N_BYTES_WORD, util::not}, table::{AccountFieldTag, MPTProofType as ProofType, RwTableTag}, - util::Expr, + util::{word, Expr}, }; use eth_types::Field; use gadgets::binary_number::BinaryNumberConfig; @@ -23,23 +24,23 @@ pub struct RwTableQueries { pub address: Expression, pub prev_address: Expression, pub field_tag: Expression, - pub storage_key: Expression, - pub value: Expression, - pub value_prev: Expression, // meta.query(value, Rotation::prev()) - pub value_prev_column: Expression, /* meta.query(prev_value, Rotation::cur()) - * TODO: aux1 and aux2 */ + pub storage_key: word::Word>, + pub value: word::Word>, + pub value_prev: word::Word>, + pub value_prev_column: word::Word>, /* meta.query(prev_value, Rotation::cur()) + * * TODO: aux1 and aux2 */ } #[derive(Clone)] pub struct MptUpdateTableQueries { pub q_enable: Expression, pub address: Expression, - pub storage_key: Expression, + pub storage_key: word::Word>, pub proof_type: Expression, - pub new_root: Expression, - pub old_root: Expression, - pub new_value: Expression, - pub old_value: Expression, + pub new_root: word::Word>, + pub old_root: word::Word>, + pub new_value: word::Word>, + pub old_value: word::Word>, } #[derive(Clone)] @@ -53,9 +54,9 @@ pub struct Queries { pub id: MpiQueries, pub is_tag_and_id_unchanged: Expression, pub address: MpiQueries, - pub storage_key: RlcQueries, - pub initial_value: Expression, - pub initial_value_prev: Expression, + pub storage_key: MpiQueries, + pub initial_value: word::Word>, + pub initial_value_prev: word::Word>, pub is_non_exist: Expression, pub mpt_proof_type: Expression, pub lookups: LookupsQueries, @@ -63,8 +64,8 @@ pub struct Queries { pub first_different_limb: [Expression; 4], pub not_first_access: Expression, pub last_access: Expression, - pub state_root: Expression, - pub state_root_prev: Expression, + pub state_root: word::Word>, + pub state_root_prev: word::Word>, } type Constraint = (&'static str, Expression); @@ -76,6 +77,28 @@ pub struct ConstraintBuilder { condition: Expression, } +struct LookupBuilder(Vec<(Expression, Expression)>); +impl LookupBuilder { + fn new() -> Self { + Self(vec![]) + } + + fn add(mut self, e1: &Expression, e2: &Expression) -> Self { + self.0.push((e1.clone(), e2.clone())); + self + } + + fn add_word(mut self, e1: &word::Word>, e2: &word::Word>) -> Self { + self.0.push((e1.lo(), e2.lo())); + self.0.push((e1.hi(), e2.hi())); + self + } + + fn build(self) -> Vec<(Expression, Expression)> { + self.0 + } +} + impl ConstraintBuilder { pub fn new() -> Self { Self { @@ -151,10 +174,14 @@ impl ConstraintBuilder { // in the current row differs from the previous row. self.condition(q.first_access(), |cb| { cb.require_zero( - "first access reads don't change value", - q.is_read() * (q.rw_table.value.clone() - q.initial_value()), + "first access reads don't change value (hi)", + q.is_read() * (q.rw_table.value.hi() - q.initial_value().hi()), + ); + cb.require_zero( + "first access reads don't change value (lo)", + q.is_read() * (q.rw_table.value.lo() - q.initial_value().lo()), ); - cb.require_equal( + cb.require_word_equal( "value_prev column is initial_value for first access", q.value_prev_column(), q.initial_value.clone(), @@ -164,12 +191,20 @@ impl ConstraintBuilder { // When all the keys in the current row and previous row are equal. self.condition(q.not_first_access.clone(), |cb| { cb.require_zero( - "non-first access reads don't change value", - q.is_read() * (q.rw_table.value.clone() - q.rw_table.value_prev.clone()), + "non-first access reads don't change value (hi)", + q.is_read() * (q.rw_table.value.hi() - q.rw_table.value_prev.hi()), ); cb.require_zero( - "initial value doesn't change in an access group", - q.initial_value.clone() - q.initial_value_prev(), + "non-first access reads don't change value (lo)", + q.is_read() * (q.rw_table.value.lo() - q.rw_table.value_prev.lo()), + ); + cb.require_zero( + "initial value doesn't change in an access group (hi)", + q.initial_value.hi() - q.initial_value_prev().hi(), + ); + cb.require_zero( + "initial value doesn't change in an access group (lo)", + q.initial_value.lo() - q.initial_value_prev().lo(), ); }); } @@ -179,39 +214,41 @@ impl ConstraintBuilder { self.require_zero("field_tag is 0 for Start", q.field_tag()); self.require_zero("address is 0 for Start", q.rw_table.address.clone()); self.require_zero("id is 0 for Start", q.id()); - self.require_zero("storage_key is 0 for Start", q.rw_table.storage_key.clone()); + self.require_word_zero("storage_key is 0 for Start", q.rw_table.storage_key.clone()); // 1.1. rw_counter increases by 1 for every non-first row self.require_zero( "rw_counter increases by 1 for every non-first row", q.lexicographic_ordering_selector.clone() * (q.rw_counter_change() - 1.expr()), ); // 1.2. Start value is 0 - self.require_zero("Start value is 0", q.value()); + self.require_word_zero("Start value is 0", q.value()); // 1.3. Start initial value is 0 - self.require_zero("Start initial_value is 0", q.initial_value()); + self.require_word_zero("Start initial_value is 0", q.initial_value()); // 1.4. state_root is unchanged for every non-first row self.condition(q.lexicographic_ordering_selector.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "state_root is unchanged for Start", q.state_root(), q.state_root_prev(), ) }); - self.require_zero("value_prev column is 0 for Start", q.value_prev_column()); + self.require_word_zero("value_prev column is 0 for Start", q.value_prev_column()); } fn build_memory_constraints(&mut self, q: &Queries) { // 2.0. Unused keys are 0 self.require_zero("field_tag is 0 for Memory", q.field_tag()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for Memory", q.rw_table.storage_key.clone(), ); // 2.1. First access for a set of all keys are 0 if READ - self.require_zero( - "first access for a set of all keys are 0 if READ", - q.first_access() * q.is_read() * q.value(), - ); + self.condition(q.first_access() * q.is_read(), |cb| { + cb.require_word_zero( + "first access for a set of all keys are 0 if READ", + q.value(), + ); + }); // could do this more efficiently by just asserting address = limb0 + 2^16 * // limb1? // 2.2. mem_addr in range @@ -228,16 +265,16 @@ impl ConstraintBuilder { // 2.3. value is a word // 2.4. Start initial value is 0 - self.require_zero("initial Memory value is 0", q.initial_value()); + self.require_word_zero("initial Memory value is 0", q.initial_value()); // 2.5. state root does not change - self.require_equal( + self.require_word_equal( "state_root is unchanged for Memory", q.state_root(), q.state_root_prev(), ); // 2.6. The value on the previous row equals the value_prev column. self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -248,7 +285,7 @@ impl ConstraintBuilder { fn build_stack_constraints(&mut self, q: &Queries) { // 3.0. Unused keys are 0 self.require_zero("field_tag is 0 for Stack", q.field_tag()); - self.require_zero("storage_key is 0 for Stack", q.rw_table.storage_key.clone()); + self.require_word_zero("storage_key is 0 for Stack", q.rw_table.storage_key.clone()); // 3.1. First access for a set of all keys self.require_zero( "first access to new stack address is a write", @@ -267,14 +304,14 @@ impl ConstraintBuilder { ) }); // 3.4. Stack initial value is 0 - self.require_zero("initial Stack value is 0", q.initial_value.clone()); + self.require_word_zero("initial Stack value is 0", q.initial_value.clone()); // 3.5 state root does not change - self.require_equal( + self.require_word_equal( "state_root is unchanged for Stack", q.state_root(), q.state_root_prev(), ); - self.require_equal( + self.require_word_equal( "value_prev column equals initial_value for Stack", q.value_prev_column(), q.initial_value(), @@ -306,27 +343,21 @@ impl ConstraintBuilder { self.condition(q.last_access(), |cb| { cb.add_lookup( "mpt_update exists in mpt circuit for AccountStorage last access", - vec![ - (1.expr(), q.mpt_update_table.q_enable.clone()), - ( - q.rw_table.address.clone(), - q.mpt_update_table.address.clone(), - ), - ( - q.rw_table.storage_key.clone(), - q.mpt_update_table.storage_key.clone(), - ), - (q.mpt_proof_type(), q.mpt_update_table.proof_type.clone()), - (q.state_root(), q.mpt_update_table.new_root.clone()), - (q.state_root_prev(), q.mpt_update_table.old_root.clone()), - (q.value(), q.mpt_update_table.new_value.clone()), - (q.initial_value(), q.mpt_update_table.old_value.clone()), - ], + LookupBuilder::new() + .add(&1.expr(), &q.mpt_update_table.q_enable.clone()) + .add(&q.rw_table.address, &q.mpt_update_table.address) + .add_word(&q.rw_table.storage_key, &q.mpt_update_table.storage_key) + .add(&q.mpt_proof_type(), &q.mpt_update_table.proof_type) + .add_word(&q.state_root(), &q.mpt_update_table.new_root) + .add_word(&q.state_root_prev(), &q.mpt_update_table.old_root) + .add_word(&q.value(), &q.mpt_update_table.new_value) + .add_word(&q.initial_value(), &q.mpt_update_table.old_value) + .build(), ); }); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -336,24 +367,24 @@ impl ConstraintBuilder { fn build_tx_access_list_account_constraints(&mut self, q: &Queries) { self.require_zero("field_tag is 0 for TxAccessListAccount", q.field_tag()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for TxAccessListAccount", q.rw_table.storage_key.clone(), ); - self.require_boolean("TxAccessListAccount value is boolean", q.value()); - self.require_zero( + self.require_word_boolean("TxAccessListAccount value is boolean", q.value()); + self.require_word_zero( "initial TxAccessListAccount value is false", q.initial_value(), ); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxAccessListAccount", q.state_root(), q.state_root_prev(), ); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -366,20 +397,20 @@ impl ConstraintBuilder { "field_tag is 0 for TxAccessListAccountStorage", q.field_tag(), ); - self.require_boolean("TxAccessListAccountStorage value is boolean", q.value()); - self.require_zero( + self.require_word_boolean("TxAccessListAccountStorage value is boolean", q.value()); + self.require_word_zero( "initial TxAccessListAccountStorage value is false", q.initial_value(), ); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxAccessListAccountStorage", q.state_root(), q.state_root_prev(), ); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -391,32 +422,32 @@ impl ConstraintBuilder { // 7.0. `address`, `field_tag` and `storage_key` are 0 self.require_zero("address is 0 for TxRefund", q.rw_table.address.clone()); self.require_zero("field_tag is 0 for TxRefund", q.field_tag()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for TxRefund", q.rw_table.storage_key.clone(), ); // 7.1. `state root` is not changed - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxRefund", q.state_root(), q.state_root_prev(), ); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), ); }); // 7.2. `initial value` is 0 - self.require_zero("initial TxRefund value is 0", q.initial_value()); + self.require_word_zero("initial TxRefund value is 0", q.initial_value()); } fn build_account_constraints(&mut self, q: &Queries) { // ref. spec 6.0. Unused keys are 0 self.require_zero("id is 0 for Account", q.id()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for Account", q.rw_table.storage_key.clone(), ); @@ -439,27 +470,21 @@ impl ConstraintBuilder { self.condition(q.last_access(), |cb| { cb.add_lookup( "mpt_update exists in mpt circuit for Account last access", - vec![ - (1.expr(), q.mpt_update_table.q_enable.clone()), - ( - q.rw_table.address.clone(), - q.mpt_update_table.address.clone(), - ), - ( - q.rw_table.storage_key.clone(), - q.mpt_update_table.storage_key.clone(), - ), - (q.mpt_proof_type(), q.mpt_update_table.proof_type.clone()), - (q.state_root(), q.mpt_update_table.new_root.clone()), - (q.state_root_prev(), q.mpt_update_table.old_root.clone()), - (q.value(), q.mpt_update_table.new_value.clone()), - (q.initial_value(), q.mpt_update_table.old_value.clone()), - ], + LookupBuilder::new() + .add(&1.expr(), &q.mpt_update_table.q_enable) + .add(&q.rw_table.address, &q.mpt_update_table.address) + .add_word(&q.rw_table.storage_key, &q.mpt_update_table.storage_key) + .add(&q.mpt_proof_type(), &q.mpt_update_table.proof_type) + .add_word(&q.state_root(), &q.mpt_update_table.new_root) + .add_word(&q.state_root_prev(), &q.mpt_update_table.old_root) + .add_word(&q.value(), &q.mpt_update_table.new_value) + .add_word(&q.initial_value(), &q.mpt_update_table.old_value) + .build(), ); }); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -469,7 +494,7 @@ impl ConstraintBuilder { fn build_call_context_constraints(&mut self, q: &Queries) { self.require_zero("address is 0 for CallContext", q.rw_table.address.clone()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for CallContext", q.rw_table.storage_key.clone(), ); @@ -477,13 +502,13 @@ impl ConstraintBuilder { "field_tag in CallContextFieldTag range", vec![(q.field_tag(), q.lookups.call_context_field_tag.clone())], ); - self.require_zero("initial CallContext value is 0", q.initial_value()); - self.require_equal( + self.require_word_zero("initial CallContext value is 0", q.initial_value()); + self.require_word_equal( "state_root is unchanged for CallContext", q.state_root(), q.state_root_prev(), ); - self.require_zero( + self.require_word_zero( "value_prev column is 0 for CallContext", q.value_prev_column(), ); @@ -495,14 +520,14 @@ impl ConstraintBuilder { q.rw_table.is_write.clone(), 1.expr(), ); - self.require_zero("initial TxLog value is 0", q.initial_value()); + self.require_word_zero("initial TxLog value is 0", q.initial_value()); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxLog", q.state_root(), q.state_root_prev(), ); - self.require_equal( + self.require_word_equal( "value_prev column equals initial_value for TxLog", q.value_prev_column(), q.initial_value(), @@ -513,12 +538,12 @@ impl ConstraintBuilder { // TODO: implement TxReceipt constraints self.require_equal("TxReceipt rows not implemented", 1.expr(), 0.expr()); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxReceipt", q.state_root(), q.state_root_prev(), ); - self.require_zero( + self.require_word_zero( "value_prev_column is 0 for TxReceipt", q.value_prev_column(), ); @@ -528,14 +553,38 @@ impl ConstraintBuilder { self.constraints.push((name, self.condition.clone() * e)); } + fn require_word_zero(&mut self, name: &'static str, e: word::Word>) { + let (lo, hi) = e.into_lo_hi(); + self.constraints.push((name, self.condition.clone() * hi)); + self.constraints.push((name, self.condition.clone() * lo)); + } + fn require_equal(&mut self, name: &'static str, left: Expression, right: Expression) { self.require_zero(name, left - right) } + fn require_word_equal( + &mut self, + name: &'static str, + left: word::Word>, + right: word::Word>, + ) { + let (left_lo, left_hi) = left.into_lo_hi(); + let (right_lo, right_hi) = right.into_lo_hi(); + self.require_zero(name, left_hi - right_hi); + self.require_zero(name, left_lo - right_lo); + } + fn require_boolean(&mut self, name: &'static str, e: Expression) { self.require_zero(name, e.clone() * (1.expr() - e)) } + fn require_word_boolean(&mut self, name: &'static str, e: word::Word>) { + let (lo, hi) = e.into_lo_hi(); + self.require_zero(name, hi); + self.require_zero(name, lo.clone() * (1.expr() - lo)); + } + fn require_in_set(&mut self, name: &'static str, item: Expression, set: Vec>) { self.require_zero( name, @@ -590,19 +639,19 @@ impl Queries { self.rw_table.field_tag.clone() } - fn value(&self) -> Expression { + fn value(&self) -> word::Word> { self.rw_table.value.clone() } - fn value_prev(&self) -> Expression { + fn value_prev(&self) -> word::Word> { self.rw_table.value_prev.clone() } - fn initial_value(&self) -> Expression { + fn initial_value(&self) -> word::Word> { self.initial_value.clone() } - fn initial_value_prev(&self) -> Expression { + fn initial_value_prev(&self) -> word::Word> { self.initial_value_prev.clone() } @@ -658,15 +707,15 @@ impl Queries { self.last_access.clone() } - fn state_root(&self) -> Expression { + fn state_root(&self) -> word::Word> { self.state_root.clone() } - fn state_root_prev(&self) -> Expression { + fn state_root_prev(&self) -> word::Word> { self.state_root_prev.clone() } - fn value_prev_column(&self) -> Expression { + fn value_prev_column(&self) -> word::Word> { self.rw_table.value_prev_column.clone() } } diff --git a/zkevm-circuits/src/state_circuit/dev.rs b/zkevm-circuits/src/state_circuit/dev.rs index ee1006b1dd..1f2ca7851e 100644 --- a/zkevm-circuits/src/state_circuit/dev.rs +++ b/zkevm-circuits/src/state_circuit/dev.rs @@ -2,7 +2,7 @@ pub use super::StateCircuit; use crate::{ state_circuit::{StateCircuitConfig, StateCircuitConfigArgs}, - table::{MptTable, RwTable}, + table::{MptTable, RwTable, UXTable}, util::{Challenges, SubCircuit, SubCircuitConfig}, }; use eth_types::Field; @@ -27,6 +27,9 @@ where fn configure(meta: &mut ConstraintSystem) -> Self::Config { let rw_table = RwTable::construct(meta); let mpt_table = MptTable::construct(meta); + let u8_table = UXTable::construct(meta); + let u10_table = UXTable::construct(meta); + let u16_table = UXTable::construct(meta); let challenges = Challenges::construct(meta); let config = { @@ -36,6 +39,9 @@ where StateCircuitConfigArgs { rw_table, mpt_table, + u8_table, + u10_table, + u16_table, challenges, }, ) @@ -66,11 +72,16 @@ pub enum AdviceColumn { Address, AddressLimb0, AddressLimb1, - StorageKey, + _StorageKeyLo, + _StorageKeyHi, + StorageKeyLimb0, + _StorageKeyLimb1, StorageKeyByte0, StorageKeyByte1, - Value, - ValuePrev, + ValueLo, + ValueHi, + ValuePrevLo, + ValuePrevHi, RwCounter, RwCounterLimb0, RwCounterLimb1, @@ -84,7 +95,8 @@ pub enum AdviceColumn { LimbIndexBit2, LimbIndexBit3, LimbIndexBit4, // least significant bit - InitialValue, + InitialValueLo, + InitialValueHi, IsZero, // committed_value and value are 0 // NonEmptyWitness is the BatchedIsZero chip witness that contains the // inverse of the non-zero value if any in [committed_value, value] @@ -98,11 +110,16 @@ impl AdviceColumn { Self::Address => config.rw_table.address, Self::AddressLimb0 => config.sort_keys.address.limbs[0], Self::AddressLimb1 => config.sort_keys.address.limbs[1], - Self::StorageKey => config.rw_table.storage_key, - Self::StorageKeyByte0 => config.sort_keys.storage_key.bytes[0], - Self::StorageKeyByte1 => config.sort_keys.storage_key.bytes[1], - Self::Value => config.rw_table.value, - Self::ValuePrev => config.rw_table.value_prev, + Self::_StorageKeyLo => config.rw_table.storage_key.lo(), + Self::_StorageKeyHi => config.rw_table.storage_key.hi(), + Self::StorageKeyLimb0 => config.sort_keys.storage_key.limbs[0], + Self::_StorageKeyLimb1 => config.sort_keys.storage_key.limbs[1], + Self::StorageKeyByte0 => config.sort_keys.storage_key.limbs[0], + Self::StorageKeyByte1 => config.sort_keys.storage_key.limbs[1], + Self::ValueLo => config.rw_table.value.lo(), + Self::ValueHi => config.rw_table.value.hi(), + Self::ValuePrevLo => config.rw_table.value_prev.lo(), + Self::ValuePrevHi => config.rw_table.value_prev.hi(), Self::RwCounter => config.rw_table.rw_counter, Self::RwCounterLimb0 => config.sort_keys.rw_counter.limbs[0], Self::RwCounterLimb1 => config.sort_keys.rw_counter.limbs[1], @@ -116,7 +133,8 @@ impl AdviceColumn { Self::LimbIndexBit2 => config.lexicographic_ordering.first_different_limb.bits[2], Self::LimbIndexBit3 => config.lexicographic_ordering.first_different_limb.bits[3], Self::LimbIndexBit4 => config.lexicographic_ordering.first_different_limb.bits[4], - Self::InitialValue => config.initial_value, + Self::InitialValueLo => config.initial_value.lo(), + Self::InitialValueHi => config.initial_value.hi(), Self::IsZero => config.is_non_exist.is_zero, Self::NonEmptyWitness => config.is_non_exist.nonempty_witness, } diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index 188b7f6a33..553c310a56 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -250,7 +250,7 @@ struct Queries { field_tag: Expression, // 8 bits, so we can pack tag + field_tag into one limb. id_limbs: [Expression; N_LIMBS_ID], address_limbs: [Expression; N_LIMBS_ACCOUNT_ADDRESS], - storage_key_bytes: [Expression; N_BYTES_WORD], + storage_key_limbs: [Expression; N_LIMBS_WORD], rw_counter_limbs: [Expression; N_LIMBS_RW_COUNTER], } @@ -263,18 +263,13 @@ impl Queries { id_limbs: keys.id.limbs.map(&mut query_advice), address_limbs: keys.address.limbs.map(&mut query_advice), field_tag: query_advice(keys.field_tag), - storage_key_bytes: keys.storage_key.bytes.map(&mut query_advice), + storage_key_limbs: keys.storage_key.limbs.map(&mut query_advice), rw_counter_limbs: keys.rw_counter.limbs.map(query_advice), } } fn storage_key_be_limbs(&self) -> Vec> { - self.storage_key_bytes - .iter() - .rev() - .tuples() - .map(|(hi, lo)| (1u64 << 8).expr() * hi.clone() + lo.clone()) - .collect() + self.storage_key_limbs.iter().rev().cloned().collect() } fn be_limbs(&self) -> Vec> { diff --git a/zkevm-circuits/src/state_circuit/lookups.rs b/zkevm-circuits/src/state_circuit/lookups.rs index 24a3128763..6d0c265043 100644 --- a/zkevm-circuits/src/state_circuit/lookups.rs +++ b/zkevm-circuits/src/state_circuit/lookups.rs @@ -1,10 +1,12 @@ -use crate::table::CallContextFieldTag; +use crate::table::{CallContextFieldTag, LookupTable, UXTable}; use eth_types::Field; use halo2_proofs::{ circuit::{Layouter, Value}, plonk::{Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; + +use itertools::Itertools; use std::marker::PhantomData; use strum::IntoEnumIterator; @@ -12,35 +14,13 @@ use strum::IntoEnumIterator; pub struct Config { // Can these be TableColumn's? // https://github.com/zcash/halo2/blob/642efc1536d3ea2566b04814bd60a00c4745ae22/halo2_proofs/src/plonk/circuit.rs#L266 - u8: Column, - u10: Column, - u16: Column, + u8_table: UXTable<8>, + u10_table: UXTable<10>, + u16_table: UXTable<16>, pub call_context_field_tag: Column, } impl Config { - pub fn range_check_u8( - &self, - meta: &mut ConstraintSystem, - msg: &'static str, - exp_fn: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, - ) { - meta.lookup_any(msg, |meta| { - let exp = exp_fn(meta); - vec![(exp, meta.query_fixed(self.u8, Rotation::cur()))] - }); - } - pub fn range_check_u10( - &self, - meta: &mut ConstraintSystem, - msg: &'static str, - exp_fn: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, - ) { - meta.lookup_any(msg, |meta| { - let exp = exp_fn(meta); - vec![(exp, meta.query_fixed(self.u10, Rotation::cur()))] - }); - } pub fn range_check_u16( &self, meta: &mut ConstraintSystem, @@ -49,7 +29,10 @@ impl Config { ) { meta.lookup_any(msg, |meta| { let exp = exp_fn(meta); - vec![(exp, meta.query_fixed(self.u16, Rotation::cur()))] + vec![exp] + .into_iter() + .zip_eq(self.u16_table.table_exprs(meta)) + .collect() }); } } @@ -65,9 +48,9 @@ pub struct Queries { impl Queries { pub fn new(meta: &mut VirtualCells<'_, F>, c: Config) -> Self { Self { - u8: meta.query_fixed(c.u8, Rotation::cur()), - u10: meta.query_fixed(c.u10, Rotation::cur()), - u16: meta.query_fixed(c.u16, Rotation::cur()), + u8: c.u8_table.table_exprs(meta)[0].clone(), + u10: c.u10_table.table_exprs(meta)[0].clone(), + u16: c.u16_table.table_exprs(meta)[0].clone(), call_context_field_tag: meta.query_fixed(c.call_context_field_tag, Rotation::cur()), } } @@ -86,16 +69,18 @@ impl Chip { } } - pub fn configure(meta: &mut ConstraintSystem) -> Config { + pub fn configure( + meta: &mut ConstraintSystem, + u8_table: UXTable<8>, + u10_table: UXTable<10>, + u16_table: UXTable<16>, + ) -> Config { let config = Config { - u8: meta.fixed_column(), - u10: meta.fixed_column(), - u16: meta.fixed_column(), + u8_table, + u10_table, + u16_table, call_context_field_tag: meta.fixed_column(), }; - meta.annotate_lookup_any_column(config.u8, || "LOOKUP_u8"); - meta.annotate_lookup_any_column(config.u10, || "LOOKUP_u10"); - meta.annotate_lookup_any_column(config.u16, || "LOOKUP_u16"); meta.annotate_lookup_any_column(config.call_context_field_tag, || { "LOOKUP_call_context_field_tag" }); @@ -103,26 +88,9 @@ impl Chip { } pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - for (column, exponent) in [ - (self.config.u8, 8), - (self.config.u10, 10), - (self.config.u16, 16), - ] { - layouter.assign_region( - || format!("assign u{exponent} fixed column"), - |mut region| { - for i in 0..(1 << exponent) { - region.assign_fixed( - || format!("assign {i} in u{exponent} fixed column"), - column, - i, - || Value::known(F::from(i as u64)), - )?; - } - Ok(()) - }, - )?; - } + self.config.u8_table.load(layouter)?; + self.config.u10_table.load(layouter)?; + self.config.u16_table.load(layouter)?; layouter.assign_region( || "assign call_context_field_tags fixed column", |mut region| { @@ -137,6 +105,7 @@ impl Chip { Ok(()) }, )?; + Ok(()) } } diff --git a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs index e5c06647bf..22b3b68252 100644 --- a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs +++ b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs @@ -1,8 +1,8 @@ use super::{lookups, param::*}; use crate::util::Expr; -use eth_types::{Address, Field}; +use eth_types::{Address, Field, ToLittleEndian, Word}; use halo2_proofs::{ - circuit::{Layouter, Region, Value}, + circuit::{Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; @@ -11,6 +11,7 @@ use std::marker::PhantomData; pub trait ToLimbs { fn to_limbs(&self) -> [u16; N]; + fn annotation() -> &'static str; } impl ToLimbs for Address { @@ -21,12 +22,29 @@ impl ToLimbs for Address { let le_bytes: Vec<_> = self.0.iter().rev().cloned().collect(); le_bytes_to_limbs(&le_bytes).try_into().unwrap() } + + fn annotation() -> &'static str { + "Address" + } } impl ToLimbs for u32 { fn to_limbs(&self) -> [u16; 2] { le_bytes_to_limbs(&self.to_le_bytes()).try_into().unwrap() } + + fn annotation() -> &'static str { + "u32" + } +} + +impl ToLimbs for Word { + fn to_limbs(&self) -> [u16; N_LIMBS_WORD] { + le_bytes_to_limbs(&self.to_le_bytes()).try_into().unwrap() + } + fn annotation() -> &'static str { + "Word" + } } #[derive(Clone, Copy)] @@ -58,56 +76,32 @@ impl Queries { } } -impl Config { +impl Config +where + T: ToLimbs, +{ pub fn assign( &self, region: &mut Region<'_, F>, offset: usize, - value: Address, + value: T, ) -> Result<(), Error> { - assign_to_config(region, self, value, offset, "address") + for (i, &limb) in value.to_limbs().iter().enumerate() { + region.assign_advice( + || format!("limb[{}] in {} mpi", i, T::annotation()), + self.limbs[i], + offset, + || Value::known(F::from(limb as u64)), + )?; + } + Ok(()) } - /// Annotates columns of this gadget embedded within a circuit region. pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { annotate_columns_in_config(region, self, prefix, "address") } } -impl Config { - pub fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - value: u32, - ) -> Result<(), Error> { - assign_to_config(region, self, value, offset, "u32") - } - - /// Annotates columns of this gadget embedded within a circuit region. - pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { - annotate_columns_in_config(region, self, prefix, "u32") - } -} - -fn assign_to_config, const N: usize, F: Field>( - region: &mut Region<'_, F>, - config: &Config, - value: T, - offset: usize, - prefix: &str, -) -> Result<(), Error> { - for (i, &limb) in value.to_limbs().iter().enumerate() { - region.assign_advice( - || format!("limb[{i}] in {prefix} mpi"), - config.limbs[i], - offset, - || Value::known(F::from(limb as u64)), - )?; - } - Ok(()) -} - fn annotate_columns_in_config, const N: usize, F: Field>( region: &mut Region<'_, F>, config: &Config, @@ -125,54 +119,52 @@ fn annotate_columns_in_config, const N: usize, F: Field>( .for_each(|(col, ann)| region.name_column(|| format!("{prefix}_{ann}"), *col)); } -pub struct Chip +pub struct Chip where - T: ToLimbs, + T: ToLimbs, { - config: Config, + _config: Config, _marker: PhantomData, } -impl Chip +impl Chip where - T: ToLimbs, + T: ToLimbs, { - pub fn construct(config: Config) -> Self { - Self { - config, - _marker: PhantomData, - } - } - pub fn configure( meta: &mut ConstraintSystem, selector: Column, - value: Column, + values: [Column; N_VALUES], lookup: lookups::Config, - ) -> Config { - let limbs = [0; N].map(|_| meta.advice_column()); + ) -> Config { + assert_eq!(N_LIMBS & N_VALUES, 0); + let limbs_per_value = N_LIMBS / N_VALUES; + + let limbs = [0; N_LIMBS].map(|_| meta.advice_column()); for &limb in &limbs { lookup.range_check_u16(meta, "limb fits into u16", |meta| { meta.query_advice(limb, Rotation::cur()) }); } - meta.create_gate("mpi value matches claimed limbs", |meta| { - let selector = meta.query_fixed(selector, Rotation::cur()); - let value = meta.query_advice(value, Rotation::cur()); - let limbs = limbs.map(|limb| meta.query_advice(limb, Rotation::cur())); - vec![selector * (value - value_from_limbs(&limbs))] - }); + + for (n, value) in values.iter().enumerate() { + meta.create_gate("mpi value matches claimed limbs", |meta| { + let selector = meta.query_fixed(selector, Rotation::cur()); + let value_expr = meta.query_advice(*value, Rotation::cur()); + let value_limbs = &limbs[n * limbs_per_value..(n + 1) * limbs_per_value]; + let limbs_expr = value_limbs + .iter() + .map(|limb| meta.query_advice(*limb, Rotation::cur())); + vec![selector * (value_expr - value_from_limbs(&limbs_expr.collect::>()))] + }); + } Config { limbs, _marker: PhantomData, } } - - pub fn load(&self, _layouter: &mut impl Layouter) -> Result<(), Error> { - Ok(()) - } } fn le_bytes_to_limbs(bytes: &[u8]) -> Vec { diff --git a/zkevm-circuits/src/state_circuit/param.rs b/zkevm-circuits/src/state_circuit/param.rs index fb30860c12..e80cc0ef25 100644 --- a/zkevm-circuits/src/state_circuit/param.rs +++ b/zkevm-circuits/src/state_circuit/param.rs @@ -1,3 +1,4 @@ pub(super) const N_LIMBS_RW_COUNTER: usize = 2; pub(super) const N_LIMBS_ACCOUNT_ADDRESS: usize = 10; pub(super) const N_LIMBS_ID: usize = 2; +pub(super) const N_LIMBS_WORD: usize = 16; diff --git a/zkevm-circuits/src/state_circuit/random_linear_combination.rs b/zkevm-circuits/src/state_circuit/random_linear_combination.rs deleted file mode 100644 index 2b7f2804d5..0000000000 --- a/zkevm-circuits/src/state_circuit/random_linear_combination.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::evm_circuit::util::rlc; -use eth_types::{Field, ToLittleEndian, U256}; -use halo2_proofs::{ - circuit::{Layouter, Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, - poly::Rotation, -}; -use std::marker::PhantomData; - -use super::lookups; - -#[derive(Clone, Debug, Copy)] -pub struct Config { - // bytes are little endian - pub bytes: [Column; N], -} - -#[derive(Clone)] -pub struct Queries { - pub bytes: [Expression; N], -} - -impl Queries { - pub fn new(meta: &mut VirtualCells<'_, F>, c: Config) -> Self { - Self { - bytes: c.bytes.map(|byte| meta.query_advice(byte, Rotation::cur())), - } - } -} - -impl Config { - pub fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - _randomness: Value, // kept for future use - value: U256, - ) -> Result<(), Error> { - let bytes = value.to_le_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - region.assign_advice( - || format!("byte[{i}] in rlc"), - self.bytes[i], - offset, - || Value::known(F::from(byte as u64)), - )?; - } - Ok(()) - } - - /// Annotates columns of this gadget embedded within a circuit region. - pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { - let mut annotations = Vec::new(); - for (i, _) in self.bytes.iter().enumerate() { - annotations.push(format!("RLC_byte{i}")); - } - self.bytes - .iter() - .zip(annotations.iter()) - .for_each(|(col, ann)| region.name_column(|| format!("{prefix}_{ann}"), *col)); - } -} - -pub struct Chip { - config: Config, - _marker: PhantomData, -} - -impl Chip { - pub fn construct(config: Config) -> Self { - Self { - config, - _marker: PhantomData, - } - } - - pub fn configure( - meta: &mut ConstraintSystem, - selector: Column, - encoded: Column, - lookup: lookups::Config, - randomness: Expression, - ) -> Config { - let bytes = [0; N].map(|_| meta.advice_column()); - - for &byte in &bytes { - lookup.range_check_u8(meta, "rlc bytes fit into u8", |meta| { - meta.query_advice(byte, Rotation::cur()) - }); - } - - meta.create_gate("rlc encoded value matches bytes", |meta| { - let selector = meta.query_fixed(selector, Rotation::cur()); - let encoded = meta.query_advice(encoded, Rotation::cur()); - let bytes = bytes.map(|c| meta.query_advice(c, Rotation::cur())); - vec![selector * (encoded - rlc::expr(&bytes, randomness))] - }); - - Config { bytes } - } - - pub fn load(&self, _layouter: &mut impl Layouter) -> Result<(), Error> { - Ok(()) - } -} diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index b6849be85d..a07ef47c86 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -151,6 +151,7 @@ fn state_circuit_simple_2() { Word::zero(), ), ); + let storage_op_2 = Operation::new( RWCounter::from(19), RW::WRITE, @@ -170,7 +171,6 @@ fn state_circuit_simple_2() { vec![storage_op_0, storage_op_1, storage_op_2], ); } - #[test] fn state_circuit_simple_6() { let memory_op_0 = Operation::new( @@ -382,7 +382,7 @@ fn address_limb_mismatch() { value: U256::zero(), value_prev: U256::zero(), }]; - let overrides = HashMap::from([((AdviceColumn::AddressLimb0, 0), Fr::zero())]); + let overrides = HashMap::from([((AdviceColumn::AddressLimb0, 0), Fr::one())]); let result = verify_with_overrides(rows, overrides); @@ -421,38 +421,11 @@ fn storage_key_mismatch() { tx_id: 4, committed_value: U256::from(34), }]; - let overrides = HashMap::from([((AdviceColumn::StorageKeyByte1, 0), Fr::one())]); + let overrides = HashMap::from([((AdviceColumn::StorageKeyLimb0, 0), Fr::one())]); let result = verify_with_overrides(rows, overrides); - assert_error_matches(result, "rlc encoded value matches bytes"); -} - -#[test] -fn storage_key_byte_out_of_range() { - let rows = vec![Rw::AccountStorage { - rw_counter: 1, - is_write: false, - account_address: Address::default(), - storage_key: U256::from(256), - value: U256::from(500), - value_prev: U256::from(500), - tx_id: 4, - committed_value: U256::from(500), - }]; - let overrides = HashMap::from([ - ((AdviceColumn::StorageKeyByte0, 0), Fr::from(0xcafeu64)), - ((AdviceColumn::StorageKeyByte1, 0), Fr::zero()), - ]); - - // This will trigger two errors: an RLC encoding error and the "fit into u8", we - // remove the first one - let result = verify_with_overrides(rows, overrides).map_err(|mut err| { - err.remove(0); - err - }); - - assert_error_matches(result, "rlc bytes fit into u8"); + assert_error_matches(result, "mpi value matches claimed limbs"); } #[test] @@ -787,11 +760,14 @@ fn bad_initial_memory_value() { let v = Fr::from(200); let overrides = HashMap::from([ - ((AdviceColumn::Value, 0), v), - ((AdviceColumn::ValuePrev, 0), v), + ((AdviceColumn::ValueLo, 0), v), + ((AdviceColumn::ValueHi, 0), Fr::zero()), + ((AdviceColumn::ValuePrevLo, 0), v), + ((AdviceColumn::ValuePrevHi, 0), Fr::zero()), ((AdviceColumn::IsZero, 0), Fr::zero()), ((AdviceColumn::NonEmptyWitness, 0), v.invert().unwrap()), - ((AdviceColumn::InitialValue, 0), v), + ((AdviceColumn::InitialValueLo, 0), v), + ((AdviceColumn::InitialValueHi, 0), Fr::zero()), ]); let result = verify_with_overrides(rows, overrides); @@ -886,8 +862,10 @@ fn bad_initial_stack_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::InitialValue, 0), Fr::from(10)), - ((AdviceColumn::ValuePrev, 0), Fr::from(10)), + ((AdviceColumn::InitialValueHi, 0), Fr::zero()), + ((AdviceColumn::InitialValueLo, 0), Fr::from(10)), + ((AdviceColumn::ValuePrevHi, 0), Fr::zero()), + ((AdviceColumn::ValuePrevLo, 0), Fr::from(10)), ]); assert_error_matches( @@ -908,8 +886,10 @@ fn bad_initial_tx_access_list_account_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::InitialValue, 0), Fr::from(1)), - ((AdviceColumn::ValuePrev, 0), Fr::from(1)), + ((AdviceColumn::InitialValueHi, 0), Fr::zero()), + ((AdviceColumn::InitialValueLo, 0), Fr::from(1)), + ((AdviceColumn::ValuePrevHi, 0), Fr::zero()), + ((AdviceColumn::ValuePrevLo, 0), Fr::from(1)), ]); assert_error_matches( @@ -930,11 +910,14 @@ fn bad_initial_tx_refund_value() { let v = Fr::from(10); let overrides = HashMap::from([ ((AdviceColumn::IsWrite, 0), Fr::from(1)), - ((AdviceColumn::Value, 0), v), - ((AdviceColumn::ValuePrev, 0), v), + ((AdviceColumn::ValueHi, 0), Fr::zero()), + ((AdviceColumn::ValueLo, 0), v), + ((AdviceColumn::ValuePrevHi, 0), Fr::zero()), + ((AdviceColumn::ValuePrevLo, 0), v), ((AdviceColumn::IsZero, 0), Fr::zero()), ((AdviceColumn::NonEmptyWitness, 0), v.invert().unwrap()), - ((AdviceColumn::InitialValue, 0), v), + ((AdviceColumn::InitialValueHi, 0), Fr::zero()), + ((AdviceColumn::InitialValueLo, 0), v), ]); assert_error_matches( @@ -956,8 +939,10 @@ fn bad_initial_tx_log_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::InitialValue, 0), Fr::from(10)), - ((AdviceColumn::ValuePrev, 0), Fr::from(10)), + ((AdviceColumn::InitialValueHi, 0), Fr::zero()), + ((AdviceColumn::InitialValueLo, 0), Fr::from(10)), + ((AdviceColumn::ValuePrevHi, 0), Fr::zero()), + ((AdviceColumn::ValuePrevLo, 0), Fr::from(10)), ]); assert_error_matches( @@ -1042,8 +1027,10 @@ fn bad_initial_tx_receipt_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::Value, 0), Fr::from(1900)), - ((AdviceColumn::InitialValue, 0), Fr::from(1900)), + ((AdviceColumn::ValueHi, 0), Fr::zero()), + ((AdviceColumn::ValueLo, 0), Fr::from(1900)), + ((AdviceColumn::InitialValueHi, 0), Fr::zero()), + ((AdviceColumn::InitialValueLo, 0), Fr::from(1900)), ]); assert_error_matches( diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index e2dc6c4882..c5fcd6cfff 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -82,7 +82,7 @@ use crate::{ table::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, ModExpTable, MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, SHA256Table, - SigTable, TxTable, U16Table, U8Table, + SigTable, TxTable, UXTable, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{circuit_stats, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, @@ -113,8 +113,11 @@ pub struct SuperCircuitConfig { rlp_table: RlpTable, tx_table: TxTable, poseidon_table: PoseidonTable, - u8_table: U8Table, - u16_table: U16Table, + // u8_table: U8Table, + // u16_table: U16Table, + ux8_table: UXTable<8>, + ux10_table: UXTable<10>, + ux16_table: UXTable<16>, evm_circuit: EvmCircuitConfig, state_circuit: StateCircuitConfig, tx_circuit: TxCircuitConfig, @@ -205,9 +208,11 @@ impl SubCircuitConfig for SuperCircuitConfig { let pow_of_rand_table = PowOfRandTable::construct(meta, &challenges_expr); log_circuit_info(meta, "power of randomness table"); - let u8_table = U8Table::construct(meta); + let ux8_table = UXTable::construct(meta); log_circuit_info(meta, "u8 table"); - let u16_table = U16Table::construct(meta); + let u10_table = UXTable::construct(meta); + log_circuit_info(meta, "u10 table"); + let ux16_table = UXTable::construct(meta); log_circuit_info(meta, "u16 table"); assert!(get_num_rows_per_round() == 12); @@ -237,7 +242,8 @@ impl SubCircuitConfig for SuperCircuitConfig { meta, RlpCircuitConfigArgs { rlp_table, - u8_table, + //u8_table, + u8_table: ux8_table, challenges: challenges_expr.clone(), }, ); @@ -262,8 +268,8 @@ impl SubCircuitConfig for SuperCircuitConfig { keccak_table: keccak_table.clone(), rlp_table, sig_table, - u8_table, - u16_table, + u8_table: ux8_table, + u16_table: ux16_table, challenges: challenges_expr.clone(), }, ); @@ -312,7 +318,7 @@ impl SubCircuitConfig for SuperCircuitConfig { MptCircuitConfigArgs { poseidon_table, mpt_table, - challenges, + // challenges, }, ); #[cfg(feature = "zktrie")] @@ -325,6 +331,9 @@ impl SubCircuitConfig for SuperCircuitConfig { StateCircuitConfigArgs { rw_table, mpt_table, + u8_table: ux8_table, + u10_table, + u16_table: ux16_table, challenges: challenges_expr.clone(), }, ); @@ -334,7 +343,7 @@ impl SubCircuitConfig for SuperCircuitConfig { meta, ExpCircuitArgs { exp_table, - u16_table, + u16_table: ux16_table, }, ); log_circuit_info(meta, "exp circuit"); @@ -392,8 +401,9 @@ impl SubCircuitConfig for SuperCircuitConfig { tx_table, rlp_table, poseidon_table, - u8_table, - u16_table, + ux8_table, + ux10_table: u10_table, + ux16_table, evm_circuit, state_circuit, copy_circuit, @@ -767,8 +777,9 @@ impl< ) -> Result<(), Error> { let challenges = challenges.values(&layouter); - config.u8_table.load(&mut layouter)?; - config.u16_table.load(&mut layouter)?; + config.ux8_table.load(&mut layouter)?; + config.ux10_table.load(&mut layouter)?; + config.ux16_table.load(&mut layouter)?; self.synthesize_sub(&config, &challenges, &mut layouter) } diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 11150e8049..ff92630688 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -1,14 +1,14 @@ //! Table definitions used cross-circuits use crate::{ - copy_circuit::util::number_or_hash_to_field, + copy_circuit::util::number_or_hash_to_word, evm_circuit::util::{ constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc, }, exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, - util::{build_tx_log_address, Challenges}, + util::{build_tx_log_address, word, Challenges}, witness::{ Block, BlockContexts, Bytecode, MptUpdateRow, MptUpdates, RlpFsmWitnessGen, Rw, RwMap, RwRow, Transaction, @@ -214,7 +214,8 @@ pub struct TxTable { /// Index for Tag = CallData pub index: Column, /// Value - pub value: Column, + //pub value: Column, + pub value: word::Word>, /// Access list address pub access_list_address: Column, } @@ -229,7 +230,8 @@ impl TxTable { tx_id: meta.advice_column(), tag, index: meta.advice_column(), - value: meta.advice_column_in(SecondPhase), + //value: meta.advice_column_in(SecondPhase), + value: word::Word::new([meta.advice_column(), meta.advice_column()]), access_list_address: meta.advice_column(), } } @@ -244,7 +246,7 @@ impl TxTable { max_calldata: usize, chain_id: u64, challenges: &Challenges>, - ) -> Result>, Error> { + ) -> Result>>, Error> { assert!( txs.len() <= max_txs, "txs.len() <= max_txs: txs.len()={}, max_txs={}", @@ -269,8 +271,9 @@ impl TxTable { tag: &Column, row: &[Value; 5], msg: &str, - ) -> Result, Error> { - let mut value_cell = None; + ) -> Result>, Error> { + let mut value_cell_lo: Option> = None; + let mut value_cell_hi: Option> = None; for (index, column) in advice_columns.iter().enumerate() { let cell = region.assign_advice( || format!("tx table {msg} row {offset}"), @@ -278,11 +281,15 @@ impl TxTable { offset, || row[if index > 0 { index + 1 } else { index }], )?; - // tx_id, index, value + // tx_id, index, value_lo, value_hi if index == 2 { - value_cell = Some(cell); + value_cell_lo = Some(cell.clone()); + } + if index == 3 { + value_cell_hi = Some(cell); } } + region.assign_fixed( || format!("tx table q_enable row {offset}"), q_enable, @@ -295,7 +302,10 @@ impl TxTable { offset, || row[1], )?; - Ok(value_cell.unwrap()) + Ok(word::Word::new([ + value_cell_lo.unwrap(), + value_cell_hi.unwrap(), + ])) } layouter.assign_region( @@ -303,7 +313,7 @@ impl TxTable { |mut region| { let mut offset = 0; let mut tx_value_cells = vec![]; - let advice_columns = [self.tx_id, self.index, self.value]; + let advice_columns = [self.tx_id, self.index, self.value.lo(), self.value.hi()]; assign_row( &mut region, offset, @@ -392,7 +402,8 @@ impl LookupTable for TxTable { self.tx_id.into(), self.tag.into(), self.index.into(), - self.value.into(), + self.value.lo().into(), + self.value.hi().into(), self.access_list_address.into(), ] } @@ -403,7 +414,8 @@ impl LookupTable for TxTable { String::from("tx_id"), String::from("tag"), String::from("index"), - String::from("value"), + String::from("value lo"), + String::from("value hi"), String::from("access_list_address"), ] } @@ -414,7 +426,8 @@ impl LookupTable for TxTable { meta.query_advice(self.tx_id, Rotation::cur()), meta.query_fixed(self.tag, Rotation::cur()), meta.query_advice(self.index, Rotation::cur()), - meta.query_advice(self.value, Rotation::cur()), + meta.query_advice(self.value.lo(), Rotation::cur()), + meta.query_advice(self.value.hi(), Rotation::cur()), meta.query_advice(self.access_list_address, Rotation::cur()), ] } @@ -590,15 +603,15 @@ pub struct RwTable { /// Key3 (FieldTag) pub field_tag: Column, /// Key3 (StorageKey) - pub storage_key: Column, + pub storage_key: word::Word>, /// Value - pub value: Column, + pub value: word::Word>, /// Value Previous - pub value_prev: Column, + pub value_prev: word::Word>, /// Aux1 - pub aux1: Column, + pub aux1: word::Word>, /// Aux2 (Committed Value) - pub aux2: Column, + pub aux2: word::Word>, } impl LookupTable for RwTable { @@ -611,11 +624,16 @@ impl LookupTable for RwTable { self.id.into(), self.address.into(), self.field_tag.into(), - self.storage_key.into(), - self.value.into(), - self.value_prev.into(), - self.aux1.into(), - self.aux2.into(), + self.storage_key.lo().into(), + self.storage_key.hi().into(), + self.value.lo().into(), + self.value.hi().into(), + self.value_prev.lo().into(), + self.value_prev.hi().into(), + self.aux1.lo().into(), + self.aux1.hi().into(), + self.aux2.lo().into(), + self.aux2.hi().into(), ] } @@ -628,11 +646,16 @@ impl LookupTable for RwTable { String::from("id"), String::from("address"), String::from("field_tag"), - String::from("storage_key"), - String::from("value"), - String::from("value_prev"), - String::from("aux1"), - String::from("aux2"), + String::from("storage_key_lo"), + String::from("storage_key_hi"), + String::from("value_lo"), + String::from("value_hi"), + String::from("value_prev_lo"), + String::from("value_prev_hi"), + String::from("aux1_lo"), + String::from("aux1_hi"), + String::from("aux2_lo"), + String::from("aux2_hi"), ] } } @@ -647,13 +670,11 @@ impl RwTable { id: meta.advice_column(), address: meta.advice_column(), field_tag: meta.advice_column(), - storage_key: meta.advice_column_in(SecondPhase), - value: meta.advice_column_in(SecondPhase), - value_prev: meta.advice_column_in(SecondPhase), - // It seems that aux1 for the moment is not using randomness - // TODO check in a future review - aux1: meta.advice_column_in(SecondPhase), - aux2: meta.advice_column_in(SecondPhase), + storage_key: word::Word::new([meta.advice_column(), meta.advice_column()]), + value: word::Word::new([meta.advice_column(), meta.advice_column()]), + value_prev: word::Word::new([meta.advice_column(), meta.advice_column()]), + aux1: word::Word::new([meta.advice_column(), meta.advice_column()]), + aux2: word::Word::new([meta.advice_column(), meta.advice_column()]), } } fn assign( @@ -675,14 +696,25 @@ impl RwTable { (self.id, row.id), (self.address, row.address), (self.field_tag, row.field_tag), + // (self.storage_key, row.storage_key), + // (self.value, row.value), + // (self.value_prev, row.value_prev), + // (self.aux1, row.aux1), + // (self.aux2, row.aux2), + ] { + region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; + } + + for (column, value) in [ (self.storage_key, row.storage_key), (self.value, row.value), (self.value_prev, row.value_prev), (self.aux1, row.aux1), (self.aux2, row.aux2), ] { - region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; + value.assign_advice(region, || "assign rw row on rw table", column, offset)?; } + Ok(()) } @@ -706,11 +738,11 @@ impl RwTable { region: &mut Region<'_, F>, rws: &[Rw], n_rows: usize, - challenges: Value, + _challenges: Value, ) -> Result<(), Error> { let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); for (offset, row) in rows.iter().enumerate() { - self.assign(region, offset, &row.table_assignment(challenges))?; + self.assign(region, offset, &row.table_assignment())?; } Ok(()) } @@ -719,10 +751,10 @@ impl RwTable { &self, region: &mut Region<'_, F>, rws: &[Rw], - challenges: Value, + _challenges: Value, ) -> Result<(), Error> { for (offset, row) in rws.iter().enumerate() { - self.assign(region, offset, &row.table_assignment(challenges))?; + self.assign(region, offset, &row.table_assignment())?; } Ok(()) } @@ -751,43 +783,59 @@ pub struct MptTable { /// Address pub address: Column, /// Storage key - pub storage_key: Column, + pub storage_key: word::Word>, /// Proof type pub proof_type: Column, /// New root - pub new_root: Column, + pub new_root: word::Word>, /// Old root - pub old_root: Column, + pub old_root: word::Word>, /// New value - pub new_value: Column, + pub new_value: word::Word>, /// Old value - pub old_value: Column, + pub old_value: word::Word>, } impl LookupTable for MptTable { fn columns(&self) -> Vec> { - vec![ - self.q_enable.into(), - self.address.into(), - self.storage_key.into(), - self.proof_type.into(), - self.new_root.into(), - self.old_root.into(), - self.new_value.into(), - self.old_value.into(), - ] + let mut columns: Vec> = vec![self.q_enable.into()]; + columns.extend( + vec![ + self.address, + self.storage_key.lo(), + self.storage_key.hi(), + self.proof_type, + self.new_root.lo(), + self.new_root.hi(), + self.old_root.lo(), + self.old_root.hi(), + self.new_value.lo(), + self.new_value.hi(), + self.old_value.lo(), + self.old_value.hi(), + ] + .into_iter() + .map(|col| col.into()) + .collect::>>(), + ); + columns } fn annotations(&self) -> Vec { vec![ String::from("q_enable"), String::from("address"), - String::from("storage_key"), + String::from("storage_key_lo"), + String::from("storage_key_hi"), String::from("proof_type"), - String::from("new_root"), - String::from("old_root"), - String::from("new_value"), - String::from("old_value"), + String::from("new_root_lo"), + String::from("new_root_hi"), + String::from("old_root_lo"), + String::from("old_root_hi"), + String::from("new_value_lo"), + String::from("new_value_hi"), + String::from("old_value_lo"), + String::from("old_value_hi"), ] } } @@ -798,12 +846,12 @@ impl MptTable { Self { q_enable: meta.fixed_column(), address: meta.advice_column(), - storage_key: meta.advice_column_in(SecondPhase), + storage_key: word::Word::new([meta.advice_column(), meta.advice_column()]), proof_type: meta.advice_column(), - new_root: meta.advice_column_in(SecondPhase), - old_root: meta.advice_column_in(SecondPhase), - new_value: meta.advice_column_in(SecondPhase), - old_value: meta.advice_column_in(SecondPhase), + new_root: word::Word::new([meta.advice_column(), meta.advice_column()]), + old_root: word::Word::new([meta.advice_column(), meta.advice_column()]), + new_value: word::Word::new([meta.advice_column(), meta.advice_column()]), + old_value: word::Word::new([meta.advice_column(), meta.advice_column()]), } } @@ -821,7 +869,7 @@ impl MptTable { )?; let mpt_table_columns = >::advice_columns(self); for (column, value) in mpt_table_columns.iter().zip_eq(row.values()) { - region.assign_advice(|| "assign mpt table row value", *column, offset, || *value)?; + region.assign_advice(|| "assign mpt table row value", *column, offset, || value)?; } Ok(()) } @@ -831,11 +879,11 @@ impl MptTable { layouter: &mut impl Layouter, updates: &MptUpdates, max_mpt_rows: usize, - randomness: Value, + _randomness: Value, ) -> Result<(), Error> { layouter.assign_region( || "mpt table zkevm", - |mut region| self.load_with_region(&mut region, updates, max_mpt_rows, randomness), + |mut region| self.load_with_region(&mut region, updates, max_mpt_rows), ) } @@ -844,12 +892,11 @@ impl MptTable { layouter: &mut impl Layouter, updates: &MptUpdates, max_mpt_rows: usize, - randomness: Value, ) -> Result<(), Error> { let num_threads = std::thread::available_parallelism().unwrap().get(); let chunk_size = (max_mpt_rows + num_threads - 1) / num_threads; let mpt_update_rows = updates - .table_assignments(randomness) + .table_assignments() .into_iter() .chain(repeat(MptUpdateRow::padding())) .take(max_mpt_rows) @@ -884,9 +931,9 @@ impl MptTable { region: &mut Region<'_, F>, updates: &MptUpdates, max_mpt_rows: usize, - randomness: Value, + //randomness: Value, ) -> Result<(), Error> { - let mpt_update_rows = updates.table_assignments(randomness); + let mpt_update_rows = updates.table_assignments(); for (offset, row) in mpt_update_rows .into_iter() .chain(repeat(MptUpdateRow::padding())) @@ -1147,7 +1194,7 @@ pub struct BytecodeTable { /// Is Enabled pub q_enable: Column, /// Code Hash - pub code_hash: Column, + pub code_hash: word::Word>, /// Tag pub tag: Column, /// Index @@ -1165,7 +1212,7 @@ impl BytecodeTable { /// Construct a new BytecodeTable pub fn construct(meta: &mut ConstraintSystem) -> Self { let [tag, index, is_code, value] = array::from_fn(|_| meta.advice_column()); - let code_hash = meta.advice_column_in(SecondPhase); + let code_hash = word::Word::new([meta.advice_column(), meta.advice_column()]); let push_rlc = meta.advice_column_in(SecondPhase); Self { q_enable: meta.fixed_column(), @@ -1220,7 +1267,8 @@ impl BytecodeTable { fn columns_mini(&self) -> Vec> { vec![ self.q_enable.into(), - self.code_hash.into(), + self.code_hash.lo().into(), + self.code_hash.hi().into(), self.tag.into(), self.index.into(), self.value.into(), @@ -1240,7 +1288,8 @@ impl LookupTable for BytecodeTable { fn columns(&self) -> Vec> { vec![ self.q_enable.into(), - self.code_hash.into(), + self.code_hash.lo().into(), + self.code_hash.hi().into(), self.tag.into(), self.index.into(), self.is_code.into(), @@ -1252,7 +1301,8 @@ impl LookupTable for BytecodeTable { fn annotations(&self) -> Vec { vec![ String::from("q_enable"), - String::from("code_hash"), + String::from("code_hash_lo"), + String::from("code_hash_hi"), String::from("tag"), String::from("index"), String::from("is_code"), @@ -1313,6 +1363,7 @@ pub struct BlockTable { pub index: Column, /// Value pub value: Column, + //pub value: word::Word>, } impl BlockTable { @@ -1322,6 +1373,7 @@ impl BlockTable { tag: meta.fixed_column(), index: meta.advice_column(), value: meta.advice_column_in(SecondPhase), + //value: word::Word::new([meta.advice_column(), meta.advice_column()]), } } @@ -1407,6 +1459,8 @@ pub struct KeccakTable { pub input_len: Column, /// RLC of the hash result pub output_rlc: Column, // RLC of hash of input bytes + /// TODO: finally remove output_rlc and use Word hi lo + pub output: word::Word>, } impl LookupTable for KeccakTable { @@ -1417,6 +1471,8 @@ impl LookupTable for KeccakTable { self.input_rlc.into(), self.input_len.into(), self.output_rlc.into(), + self.output.lo().into(), + self.output.hi().into(), ] } @@ -1427,6 +1483,8 @@ impl LookupTable for KeccakTable { String::from("input_rlc"), String::from("input_len"), String::from("output_rlc"), + String::from("output_lo"), + String::from("output_hi"), ] } } @@ -1440,6 +1498,7 @@ impl KeccakTable { input_rlc: meta.advice_column_in(SecondPhase), input_len: meta.advice_column(), output_rlc: meta.advice_column_in(SecondPhase), + output: word::Word::new([meta.advice_column(), meta.advice_column()]), } } @@ -1448,7 +1507,7 @@ impl KeccakTable { pub fn assignments( input: &[u8], challenges: &Challenges>, - ) -> Vec<[Value; 4]> { + ) -> Vec<[Value; 6]> { let input_rlc = challenges .keccak_input() .map(|challenge| rlc::value(input.iter().rev(), challenge)); @@ -1456,18 +1515,20 @@ impl KeccakTable { let mut keccak = Keccak::default(); keccak.update(input); let output = keccak.digest(); - let output_rlc = challenges.evm_word().map(|challenge| { - rlc::value( - &Word::from_big_endian(output.as_slice()).to_le_bytes(), - challenge, - ) - }); + let output_word = Word::from_big_endian(output.as_slice()); + let output_bytes = output_word.to_le_bytes(); + let output_rlc = challenges + .evm_word() + .map(|challenge| rlc::value(&output_bytes, challenge)); + let output_lo_hi = word::Word::from(output_word); vec![[ Value::known(F::one()), input_rlc, Value::known(input_len), output_rlc, + Value::known(output_lo_hi.lo()), + Value::known(output_lo_hi.hi()), ]] } @@ -1478,7 +1539,7 @@ impl KeccakTable { &self, region: &mut Region, offset: usize, - values: [Value; 4], + values: [Value; 6], ) -> Result>, Error> { let mut res = vec![]; for (&column, value) in >::advice_columns(self) @@ -1553,12 +1614,15 @@ impl KeccakTable { &self, value_rlc: Column, length: Column, - code_hash: Column, + //code_hash: Column, + code_hash_word: word::Word>, ) -> Vec<(Column, Column)> { vec![ (value_rlc, self.input_rlc), (length, self.input_len), - (code_hash, self.output_rlc), + //(code_hash, self.output), + (code_hash_word.lo(), self.output.lo()), + (code_hash_word.hi(), self.output.hi()), ] } } @@ -1695,9 +1759,9 @@ pub struct CopyTable { /// The relevant ID for the read-write row, represented as a random linear /// combination. The ID may be one of the below: /// 1. Call ID/Caller ID for CopyDataType::Memory - /// 2. RLC encoding of bytecode hash for CopyDataType::Bytecode + /// 2. The hi/lo limbs of bytecode hash for CopyDataType::Bytecode /// 3. Transaction ID for CopyDataType::TxCalldata, CopyDataType::TxLog - pub id: Column, + pub id: word::Word>, /// The source/destination address for this copy step. Can be memory /// address, byte index in the bytecode, tx call data, and tx log data. pub addr: Column, @@ -1725,21 +1789,23 @@ pub struct CopyTable { pub tag: BinaryNumberConfig, } -type CopyTableRow = [(Value, &'static str); 8]; -type CopyCircuitRow = [(Value, &'static str); 10]; +type CopyTableRow = [(Value, &'static str); 9]; +type CopyCircuitRow = [(Value, &'static str); 12]; /// CopyThread is the state used while generating rows of the copy table. struct CopyThread { tag: CopyDataType, is_rw: bool, - id: Value, + id: word::Word>, front_mask: bool, addr: u64, addr_end: u64, bytes_left: u64, value_acc: Value, - word_rlc: Value, - word_rlc_prev: Value, + //word_rlc: Value, + value_word: word::Word>, + //word_rlc_prev: Value, + value_word_prev: word::Word>, } impl CopyTable { @@ -1748,7 +1814,7 @@ impl CopyTable { Self { q_enable, is_first: meta.advice_column(), - id: meta.advice_column_in(SecondPhase), + id: word::Word::new([meta.advice_column(), meta.advice_column()]), tag: BinaryNumberChip::configure(meta, q_enable, None), addr: meta.advice_column(), src_addr_end: meta.advice_column(), @@ -1805,31 +1871,35 @@ impl CopyTable { let mut rw_counter = copy_event.rw_counter_start(); let mut rwc_inc_left = copy_event.rw_counter_delta(); + let mut value_word_read_bytes: [u8; 32] = [0; 32]; + let mut value_word_write_bytes: [u8; 32] = [0; 32]; + let mut value_word_read_prev_bytes: [u8; 32] = [0; 32]; + let mut value_word_write_prev_bytes: [u8; 32] = [0; 32]; let mut reader = CopyThread { tag: copy_event.src_type, is_rw: copy_event.is_source_rw(), - id: number_or_hash_to_field(©_event.src_id, challenges.evm_word()), + id: number_or_hash_to_word(©_event.src_id), front_mask: true, addr: copy_event.src_addr, addr_end: copy_event.src_addr_end, bytes_left: copy_event.copy_length(), value_acc: Value::known(F::zero()), - word_rlc: Value::known(F::zero()), - word_rlc_prev: Value::known(F::zero()), + value_word: word::Word::new([Value::known(F::zero()), Value::known(F::zero())]), + value_word_prev: word::Word::new([Value::known(F::zero()), Value::known(F::zero())]), }; let mut writer = CopyThread { tag: copy_event.dst_type, is_rw: copy_event.is_destination_rw(), - id: number_or_hash_to_field(©_event.dst_id, challenges.evm_word()), + id: number_or_hash_to_word(©_event.dst_id), front_mask: true, addr: copy_event.dst_addr, addr_end: copy_event.dst_addr + copy_event.full_length(), bytes_left: reader.bytes_left, value_acc: Value::known(F::zero()), - word_rlc: Value::known(F::zero()), - word_rlc_prev: Value::known(F::zero()), + value_word: word::Word::new([Value::known(F::zero()), Value::known(F::zero())]), + value_word_prev: word::Word::new([Value::known(F::zero()), Value::known(F::zero())]), }; let is_access_list = copy_event.src_type == CopyDataType::AccessListAddresses @@ -1857,10 +1927,18 @@ impl CopyTable { } let copy_step = copy_step; - let thread = if is_read_step { - &mut reader + let (thread, value_word_bytes, value_word_prev_bytes) = if is_read_step { + ( + &mut reader, + &mut value_word_read_bytes, + &mut value_word_read_prev_bytes, + ) } else { - &mut writer + ( + &mut writer, + &mut value_word_write_bytes, + &mut value_word_write_prev_bytes, + ) }; let is_first = step_idx == 0; @@ -1898,26 +1976,48 @@ impl CopyTable { } if (step_idx / 2) % 32 == 0 { // reset - thread.word_rlc = Value::known(F::zero()); - thread.word_rlc_prev = Value::known(F::zero()); + thread.value_word = + word::Word::new([Value::known(F::zero()), Value::known(F::zero())]); + thread.value_word_prev = + word::Word::new([Value::known(F::zero()), Value::known(F::zero())]); + *value_word_bytes = [0; 32]; + *value_word_prev_bytes = [0; 32]; } - thread.word_rlc = thread.word_rlc * challenges.evm_word() + value; - thread.word_rlc_prev = if is_read_step { - thread.word_rlc // Reader does not change the word. + + let word_index = (step_idx as u64 / 2) % 32; + value_word_bytes[word_index as usize] = copy_step.value; + + let u256_word = U256::from_big_endian(value_word_bytes); + thread.value_word = word::Word::from(u256_word).into_value(); + + thread.value_word_prev = if is_read_step { + thread.value_word // Reader does not change the word. } else { - thread.word_rlc_prev * challenges.evm_word() + value_prev + value_word_prev_bytes[word_index as usize] = copy_step.prev_value; + let u256_word_prev = U256::from_big_endian(value_word_prev_bytes); + word::Word::from(u256_word_prev).into_value() }; if is_access_list { + // let address_pair = copy_event.access_list[step_idx / 2]; + // thread.value_word = word::Word::new(access_list.address.to_scalar().unwrap()); + // thread.value_word_prev = word::Word::from(address_pair.1).into_value(); + // Save address, storage_key, storage_key_index and is_warm_prev // to column value_word_rlc, value_word_rlc_prev, value and // value_prev in copy circuit. let access_list = ©_event.access_list[step_idx / 2]; - - thread.word_rlc = Value::known(access_list.address.to_scalar().unwrap()); - thread.word_rlc_prev = challenges - .evm_word() - .map(|challenge| rlc::value(&access_list.storage_key.to_le_bytes(), challenge)); + thread.value_word = + word::Word::new([access_list.address.to_scalar().unwrap(), F::zero()]) + .into_value(); + // TODO: check if take use of storage_key rlc value ? + thread.value_word_prev = word::Word::from(access_list.storage_key).into_value(); + + // thread.word_rlc = Value::known(access_list.address.to_scalar().unwrap()); + // thread.word_rlc_prev = challenges + // .evm_word() + // .map(|challenge| rlc::value(&access_list.storage_key.to_le_bytes(), + // challenge)); } let word_index = (step_idx as u64 / 2) % 32; @@ -1935,7 +2035,8 @@ impl CopyTable { thread.tag, [ (Value::known(F::from(is_first)), "is_first"), - (thread.id, "id"), + (thread.id.lo(), "id_lo"), + (thread.id.hi(), "id_hi"), (Value::known(addr), "addr"), (Value::known(F::from(thread.addr_end)), "src_addr_end"), (Value::known(F::from(thread.bytes_left)), "real_bytes_left"), @@ -1947,8 +2048,10 @@ impl CopyTable { (Value::known(F::from(is_last)), "is_last"), (value, "value"), (value_prev, "value_prev"), - (thread.word_rlc, "value_word_rlc"), - (thread.word_rlc_prev, "value_word_rlc_prev"), + (thread.value_word.lo(), "value_word lo"), + (thread.value_word.hi(), "value_word hi"), + (thread.value_word_prev.lo(), "value_word_prev lo"), + (thread.value_word_prev.hi(), "value_word_prev hi"), (thread.value_acc, "value_acc"), (Value::known(F::from(is_pad)), "is_pad"), (Value::known(F::from(copy_step.mask)), "mask"), @@ -2037,7 +2140,9 @@ impl LookupTable for CopyTable { vec![ self.q_enable.into(), self.is_first.into(), - self.id.into(), + //self.id.into(), + self.id.lo().into(), + self.id.hi().into(), self.addr.into(), self.src_addr_end.into(), self.real_bytes_left.into(), @@ -2051,7 +2156,9 @@ impl LookupTable for CopyTable { vec![ String::from("q_enable"), String::from("is_first"), - String::from("id"), + //String::from("id"), + String::from("id_lo"), + String::from("id_hi"), String::from("addr"), String::from("src_addr_end"), String::from("real_bytes_left"), @@ -2065,13 +2172,15 @@ impl LookupTable for CopyTable { vec![ meta.query_fixed(self.q_enable, Rotation::cur()), meta.query_advice(self.is_first, Rotation::cur()), - meta.query_advice(self.id, Rotation::cur()), // src_id - self.tag.value(Rotation::cur())(meta), // src_tag - meta.query_advice(self.id, Rotation::next()), // dst_id - self.tag.value(Rotation::next())(meta), // dst_tag - meta.query_advice(self.addr, Rotation::cur()), // src_addr + meta.query_advice(self.id.lo(), Rotation::cur()), // src_id + meta.query_advice(self.id.hi(), Rotation::cur()), // src_id + self.tag.value(Rotation::cur())(meta), // src_tag + meta.query_advice(self.id.lo(), Rotation::next()), // dst_id + meta.query_advice(self.id.hi(), Rotation::next()), // dst_id + self.tag.value(Rotation::next())(meta), // dst_tag + meta.query_advice(self.addr, Rotation::cur()), // src_addr meta.query_advice(self.src_addr_end, Rotation::cur()), // src_addr_end - meta.query_advice(self.addr, Rotation::next()), // dst_addr + meta.query_advice(self.addr, Rotation::next()), // dst_addr meta.query_advice(self.real_bytes_left, Rotation::cur()), // real_length meta.query_advice(self.rlc_acc, Rotation::cur()), // rlc_acc meta.query_advice(self.rw_counter, Rotation::cur()), // rw_counter @@ -2456,7 +2565,10 @@ pub struct SigTable { /// Indicates whether or not the gates are enabled on the current row. pub q_enable: Column, /// Random-linear combination of the Keccak256 hash of the message that's signed. + /// currently do not change to word hi-lo type (msg_hash_word) becuase it is related to ecc + /// chip. ecc chip requires rlc value to check. pub msg_hash_rlc: Column, + // TODO: sig_r_rlc, sig_s_rlc to word in the future. /// should be in range [0, 1] pub sig_v: Column, /// Random-linear combination of the signature's `r` component. @@ -2475,6 +2587,7 @@ impl SigTable { Self { q_enable: meta.fixed_column(), msg_hash_rlc: meta.advice_column_in(SecondPhase), + // msg_hash_word: word::Word::new([meta.advice_column(), meta.advice_column()]), sig_v: meta.advice_column(), sig_s_rlc: meta.advice_column_in(SecondPhase), sig_r_rlc: meta.advice_column_in(SecondPhase), @@ -2503,6 +2616,11 @@ impl SigTable { challenge, ) }); + + // let msg_hash_word = + // word::Word::from(Word::from_big_endian(&sign_data.msg_hash.to_bytes())) + // .map(Value::::known); + let sig_r_rlc = evm_word.map(|challenge| { rlc::value( sign_data.signature.0.to_bytes().iter().collect_vec(), @@ -2542,6 +2660,13 @@ impl SigTable { || value, )?; } + + // msg_hash_word.assign_advice( + // &mut region, + // || format!("sig table msg_hash_word {offset}"), + // self.msg_hash_word, + // offset, + // )?; } Ok(()) @@ -2557,6 +2682,8 @@ impl LookupTable for SigTable { vec![ self.q_enable.into(), self.msg_hash_rlc.into(), + // self.msg_hash_word.lo().into(), + // self.msg_hash_word.hi().into(), self.sig_v.into(), self.sig_r_rlc.into(), self.sig_s_rlc.into(), @@ -2569,6 +2696,8 @@ impl LookupTable for SigTable { vec![ String::from("q_enable"), String::from("msg_hash_rlc"), + String::from("msg_hash_word lo"), + String::from("msg_hash_word hi"), String::from("sig_v"), String::from("sig_r_rlc"), String::from("sig_s_rlc"), @@ -3151,3 +3280,72 @@ impl From> for TableColumn { table.0 } } + +/// Lookup table for max n bits range check +#[derive(Clone, Copy, Debug)] +pub struct UXTable { + /// content col in the table + pub col: Column, + // col: TableColumn, +} + +impl UXTable { + /// Construct the UXTable. + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + col: meta.fixed_column(), + // col: meta.lookup_table_column(), + } + } + + /// Load the `UXTable` for range check + pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + //layouter.assign_table( + layouter.assign_region( + || format!("assign u{} fixed column", 8), + |mut region| { + // |mut table| { + for i in 0..(1 << N_BITS) { + region.assign_fixed( + || format!("assign {i} in u{N_BITS} fixed column"), + self.col, + i, + || Value::known(F::from(i as u64)), + )?; + // table.assign_cell( + // || format!("range at offset = {i}"), + // self.col, + // i, + // || Value::known(F::from(i as u64)), + // )?; + } + Ok(()) + }, + )?; + Ok(()) + } +} + +impl LookupTable for UXTable { + fn columns(&self) -> Vec> { + // vec![self.col.into()] + vec![self.col.into()] + } + + fn annotations(&self) -> Vec { + vec![format!("u{N_BITS}_col")] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![meta.query_fixed(self.col, Rotation::cur())] + } +} + +// impl From> for TableColumn { +// fn from(table: UXTable) -> TableColumn { +// //table.col +// TableColumn { +// inner: table.col, +// } +// } +// } diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index cefee440a0..78d46696ce 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -24,11 +24,11 @@ use crate::{ Gas, GasPrice, IsCreate, MaxFeePerGas, MaxPriorityFeePerGas, Nonce, SigR, SigS, SigV, TxDataGasCost, TxHashLength, TxHashRLC, TxSignHash, TxSignLength, TxSignRLC, }, - TxTable, U16Table, U8Table, + TxTable, UXTable, }, util::{ is_zero::{IsZeroChip, IsZeroConfig}, - keccak, rlc_be_bytes, SubCircuit, SubCircuitConfig, + keccak, rlc_be_bytes, word, SubCircuit, SubCircuitConfig, }, witness, witness::{ @@ -50,7 +50,7 @@ use eth_types::{ TxType::{Eip155, Eip1559, Eip2930, L1Msg, PreEip155}, }, sign_types::SignData, - AccessList, Address, Field, ToAddress, ToBigEndian, ToScalar, + AccessList, Address, Field, ToAddress, ToBigEndian, ToScalar, ToWord, U256, }; use ethers_core::utils::keccak256; use gadgets::{ @@ -82,6 +82,8 @@ use halo2_proofs::plonk::Fixed; use halo2_proofs::plonk::SecondPhase; use itertools::Itertools; +use halo2_proofs::dev::unwrap_value; + /// Number of rows of one tx occupies in the fixed part of tx table pub const TX_LEN: usize = 28; /// Offset of TxHash tag in the tx table @@ -131,21 +133,29 @@ pub struct TxCircuitConfig { // Whether tag's RLP-encoded value is 0x80 = rlp([]) is_none: Column, tx_value_length: Column, + // keccak rlc tx_value_rlc: Column, + // evm word rlc, use for rlp table lookup + tx_value_evm_rlc: Column, + // keccak lookup needs additional output word field, this word field targets for it. + tx_hash_word: word::Word>, - u8_table: U8Table, - u16_table: U16Table, + //u8_table: U8Table, + u8_table: UXTable<8>, + u16_table: UXTable<16>, /// Verify if the tx_id is zero or not. tx_id_is_zero: IsZeroConfig, /// Primarily used to verify if the `CallDataLength` is zero or non-zero /// and `CallData` byte is zero or non-zero. - value_is_zero: IsZeroConfig, + value_limb_is_zero: [IsZeroConfig; 2], + /// We use an equality gadget to know whether the tx id changes between /// subsequent rows or not. tx_id_unchanged: IsEqualConfig, /// Columns used to reduce degree + is_value_zero: Column, is_tag_block_num: Column, is_calldata: Column, is_caller_address: Column, @@ -228,9 +238,10 @@ pub struct TxCircuitConfigArgs { /// SigTable pub sig_table: SigTable, /// Reusable u8 lookup table, - pub u8_table: U8Table, + // pub u8_table: U8Table, + pub u8_table: UXTable<8>, /// Reusable u16 lookup table, - pub u16_table: U16Table, + pub u16_table: UXTable<16>, /// Challenges pub challenges: crate::util::Challenges>, } @@ -283,7 +294,9 @@ impl SubCircuitConfig for TxCircuitConfig { let tx_type = meta.advice_column(); let rlp_tag = meta.advice_column(); let tx_value_rlc = meta.advice_column_in(SecondPhase); + let tx_value_evm_rlc = meta.advice_column_in(SecondPhase); let tx_value_length = meta.advice_column(); + let tx_hash_word = word::Word::new([meta.advice_column(), meta.advice_column()]); let is_none = meta.advice_column(); let tag_bits = BinaryNumberChip::configure(meta, q_enable, Some(tx_table.tag.into())); let tx_type_bits = BinaryNumberChip::configure(meta, q_enable, Some(tx_type.into())); @@ -308,6 +321,7 @@ impl SubCircuitConfig for TxCircuitConfig { let is_tx_id_zero = meta.advice_column(); let is_caller_address = meta.advice_column(); let is_chain_id = meta.advice_column(); + let is_value_zero = meta.advice_column(); let is_tag_block_num = meta.advice_column(); let lookup_conditions = [ LookupCondition::TxCalldata, @@ -332,7 +346,8 @@ impl SubCircuitConfig for TxCircuitConfig { // TODO: add lookup to SignVerify table for sv_address let sv_address = meta.advice_column(); - meta.enable_equality(tx_table.value); + meta.enable_equality(tx_table.value.lo()); + meta.enable_equality(tx_table.value.hi()); let log_deg = |s: &'static str, meta: &mut ConstraintSystem| { debug_assert!(meta.degree() <= 9); @@ -398,7 +413,7 @@ impl SubCircuitConfig for TxCircuitConfig { ); // testing if value is zero for tags - let value_is_zero = IsZeroChip::configure( + let value_is_zero_lo = IsZeroChip::configure( meta, |meta| { and::expr(vec![ @@ -413,7 +428,25 @@ impl SubCircuitConfig for TxCircuitConfig { ]), ]) }, - tx_table.value, + tx_table.value.lo(), + |meta| meta.advice_column_in(SecondPhase), // value is at 2nd phase + ); + let value_is_zero_hi = IsZeroChip::configure( + meta, + |meta| { + and::expr(vec![ + meta.query_fixed(q_enable, Rotation::cur()), + sum::expr(vec![ + // if caller_address is zero, then skip the sig verify. + is_caller_addr(meta), + // if call_data_length is zero, then skip lookup to tx table for call data + is_data_length(meta), + // if call data byte is zero, then gas_cost = 4 (16 otherwise) + is_data(meta), + ]), + ]) + }, + tx_table.value.hi(), |meta| meta.advice_column_in(SecondPhase), // value is at 2nd phase ); @@ -555,7 +588,8 @@ impl SubCircuitConfig for TxCircuitConfig { cb.require_equal( "associated tx type to tag", meta.query_advice(tx_type, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + // tx type value only uses lo column + meta.query_advice(tx_table.value.lo(), Rotation::cur()), ); }); @@ -571,7 +605,7 @@ impl SubCircuitConfig for TxCircuitConfig { cb.require_equal( "is_create == is_none", // we rely on the assumption that IsCreate is next to CalleeAddress - meta.query_advice(tx_table.value, Rotation::next()), + meta.query_advice(tx_table.value.lo(), Rotation::next()), meta.query_advice(is_none, Rotation::cur()), ); }); @@ -580,10 +614,10 @@ impl SubCircuitConfig for TxCircuitConfig { // is_none == true cb.condition(is_none_expr.expr(), |cb| { // value == 0 - cb.require_equal( - "is_none is true => value == 0", - meta.query_advice(tx_table.value, Rotation::cur()), - 0.expr(), + cb.require_equal_word( + "is_none is true => value == 0 (value_hi = value_lo = 0)", + tx_table.value.query_advice(meta, Rotation::cur()), + word::Word::zero(), ); }); @@ -593,15 +627,15 @@ impl SubCircuitConfig for TxCircuitConfig { cb.condition(and::expr([is_data_rlc(meta), is_none_expr.expr()]), |cb| { // we rely on the assumption that CallDataLength and CallDataGasCost are after // CallDataRLC - cb.require_equal( + cb.require_equal_word( "CallDataLength.value == 0", - meta.query_advice(tx_table.value, Rotation::next()), - 0.expr(), + tx_table.value.query_advice(meta, Rotation::next()), + word::Word::zero(), ); - cb.require_equal( + cb.require_equal_word( "CallDataGasCost.value == 0", - meta.query_advice(tx_table.value, Rotation(2)), - 0.expr(), + tx_table.value.query_advice(meta, Rotation(2)), + word::Word::zero(), ); }); @@ -611,7 +645,7 @@ impl SubCircuitConfig for TxCircuitConfig { |cb| { cb.require_zero( "CallDataLength != 0", - value_is_zero.expr(Rotation::next())(meta), + meta.query_advice(is_value_zero, Rotation::next()), ); }, ); @@ -718,6 +752,23 @@ impl SubCircuitConfig for TxCircuitConfig { cb.gate(meta.query_fixed(q_enable, Rotation::cur())) }); + meta.create_gate("is_value_zero", |meta| { + let mut cb = BaseConstraintBuilder::default(); + + cb.require_boolean( + "is_value_zero is bool", + meta.query_advice(is_value_zero, Rotation::cur()), + ); + cb.require_equal( + "is_value_zero = value_is_zero_lo && value_is_zero_hi", + meta.query_advice(is_value_zero, Rotation::cur()), + value_is_zero_lo.expr(Rotation::cur())(meta) + * value_is_zero_hi.expr(Rotation::cur())(meta), + ); + + cb.gate(meta.query_fixed(q_enable, Rotation::cur())) + }); + meta.create_gate( "distinguish tx type: is_l1_msg, is_eip2930, is_eip1559", |meta| { @@ -752,7 +803,7 @@ impl SubCircuitConfig for TxCircuitConfig { "condition", and::expr([ is_data_length(meta), - not::expr(value_is_zero.expr(Rotation::cur())(meta)), + not::expr(meta.query_advice(is_value_zero, Rotation::cur())), ]), meta.query_advice( lookup_conditions[&LookupCondition::TxCalldata], @@ -770,7 +821,8 @@ impl SubCircuitConfig for TxCircuitConfig { "condition", and::expr([ is_access_list_addresses_len(meta), - not::expr(value_is_zero.expr(Rotation::cur())(meta)), + //not::expr(value_is_zero.expr(Rotation::cur())(meta)), + not::expr(meta.query_advice(is_value_zero, Rotation::cur())), ]), meta.query_advice( lookup_conditions[&LookupCondition::TxAccessList], @@ -926,7 +978,9 @@ impl SubCircuitConfig for TxCircuitConfig { q_enable, rlp_tag, tx_value_rlc, + tx_value_evm_rlc, tx_value_length, + tx_hash_word, tx_type_bits, tx_id_is_zero.clone(), is_none, @@ -957,9 +1011,9 @@ impl SubCircuitConfig for TxCircuitConfig { let mut cb = BaseConstraintBuilder::default(); cb.condition(is_tx_gas_cost(meta), |cb| { - cb.require_zero( + cb.require_zero_word( "tx_gas_cost == 0", - meta.query_advice(tx_table.value, Rotation::cur()), + tx_table.value.query_advice(meta, Rotation::cur()), ); }); @@ -978,7 +1032,8 @@ impl SubCircuitConfig for TxCircuitConfig { cb.condition(is_nonce(meta), |cb| { cb.require_equal( "tx_nonce = tx_table.value if tag == Nonce", - meta.query_advice(tx_table.value, Rotation::cur()), + // value.lo() should cover tx_nonce value. + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(tx_nonce, Rotation::cur()), ); }); @@ -992,7 +1047,7 @@ impl SubCircuitConfig for TxCircuitConfig { cb.condition(meta.query_advice(is_tag_block_num, Rotation::cur()), |cb| { cb.require_equal( "block_num = tx_table.value if tag == BlockNum", - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(block_num, Rotation::cur()), ); }); @@ -1016,7 +1071,7 @@ impl SubCircuitConfig for TxCircuitConfig { |meta| meta.query_advice(block_num, Rotation::cur()), ); - meta.lookup("block_num is non-decreasing till padding txs", |meta| { + meta.lookup_any("block_num is non-decreasing till padding txs", |meta| { // Block nums like this [1, 3, 5, 4, 0] is rejected by this. But [1, 2, 3, 5, 0] is // acceptable. let lookup_condition = and::expr([ @@ -1030,7 +1085,10 @@ impl SubCircuitConfig for TxCircuitConfig { let block_num_diff = meta.query_advice(block_num, Rotation::next()) - meta.query_advice(block_num, Rotation::cur()); - vec![(lookup_condition * block_num_diff, u16_table.into())] + vec![( + lookup_condition * block_num_diff, + meta.query_fixed(u16_table.col, Rotation::cur()), + )] }); meta.create_gate("num_all_txs in a block", |meta| { @@ -1143,7 +1201,8 @@ impl SubCircuitConfig for TxCircuitConfig { meta.lookup_any("num_all_txs in block table", |meta| { let is_tag_block_num = meta.query_advice(is_tag_block_num, Rotation::cur()); - let block_num = meta.query_advice(tx_table.value, Rotation::cur()); + // tx_table.value.lo can cover block_num + let block_num = meta.query_advice(tx_table.value.lo(), Rotation::cur()); let num_all_txs_acc = meta.query_advice(num_all_txs_acc, Rotation::cur()); let input_expr = vec![NumAllTxs.expr(), block_num, num_all_txs_acc]; @@ -1173,7 +1232,7 @@ impl SubCircuitConfig for TxCircuitConfig { cb.require_equal( "is_padding_tx = true if caller_address = 0", meta.query_advice(is_padding_tx, Rotation::cur()), - value_is_zero.expr(Rotation::cur())(meta), + meta.query_advice(is_value_zero, Rotation::cur()), ); }); cb.gate(meta.query_fixed(q_enable, Rotation::cur())) @@ -1195,7 +1254,7 @@ impl SubCircuitConfig for TxCircuitConfig { cum_num_txs - num_txs }, |meta| meta.query_advice(tx_table.tx_id, Rotation::cur()), - u8_table.into(), + u8_table.col, ); // last non-padding tx must have tx_id == cum_num_txs @@ -1233,7 +1292,7 @@ impl SubCircuitConfig for TxCircuitConfig { }, |meta| meta.query_advice(tx_table.tx_id, Rotation::cur()), |meta| meta.query_advice(cum_num_txs, Rotation::cur()), - u8_table.into(), + u8_table.col, ); meta.create_gate("tx_id <= cum_num_txs", |meta| { @@ -1252,7 +1311,8 @@ impl SubCircuitConfig for TxCircuitConfig { meta.lookup_any("num_txs in block table", |meta| { let is_tag_block_num = meta.query_advice(is_tag_block_num, Rotation::cur()); - let block_num = meta.query_advice(tx_table.value, Rotation::cur()); + // TODO: check why block_num come from tx table ? + let block_num = meta.query_advice(tx_table.value.lo(), Rotation::cur()); let num_txs = meta.query_advice(num_txs, Rotation::cur()); let input_expr = vec![NumTxs.expr(), block_num, num_txs]; @@ -1272,7 +1332,7 @@ impl SubCircuitConfig for TxCircuitConfig { meta.lookup_any("cum_num_txs in block table", |meta| { let is_tag_block_num = meta.query_advice(is_tag_block_num, Rotation::cur()); - let block_num = meta.query_advice(tx_table.value, Rotation::cur()); + let block_num = meta.query_advice(tx_table.value.lo(), Rotation::cur()); let cum_num_txs = meta.query_advice(cum_num_txs, Rotation::cur()); let input_expr = vec![CumNumTxs.expr(), block_num, cum_num_txs]; @@ -1293,7 +1353,7 @@ impl SubCircuitConfig for TxCircuitConfig { //////////////////////////////////////////////////////////////////////// /////////// CallData length and gas_cost calculation ///////////////// //////////////////////////////////////////////////////////////////////// - meta.lookup("tx_id_diff must in u16", |meta| { + meta.lookup_any("tx_id_diff must in u16", |meta| { let q_enable = meta.query_fixed(q_enable, Rotation::next()); let is_calldata = meta.query_advice(is_calldata, Rotation::cur()); let tx_id = meta.query_advice(tx_table.tx_id, Rotation::cur()); @@ -1302,8 +1362,8 @@ impl SubCircuitConfig for TxCircuitConfig { let lookup_condition = and::expr([q_enable, is_calldata, not::expr(tx_id_next_is_zero)]); - - vec![(lookup_condition * (tx_id_next - tx_id), u16_table.into())] + let u16_expr = meta.query_fixed(u16_table.col, Rotation::cur()); + vec![(lookup_condition * (tx_id_next - tx_id), u16_expr)] }); meta.create_gate("last row of call data", |meta| { @@ -1320,7 +1380,8 @@ impl SubCircuitConfig for TxCircuitConfig { cb.require_equal( "calldata_byte == tx_table.value", meta.query_advice(calldata_byte, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + // byte only use value.lo + meta.query_advice(tx_table.value.lo(), Rotation::cur()), ); }); @@ -1329,8 +1390,8 @@ impl SubCircuitConfig for TxCircuitConfig { meta.create_gate("tx call data init", |meta| { let mut cb = BaseConstraintBuilder::default(); + let value_is_zero = meta.query_advice(is_value_zero, Rotation::cur()); - let value_is_zero = value_is_zero.expr(Rotation::cur())(meta); let gas_cost = select::expr(value_is_zero, 4.expr(), 16.expr()); cb.require_equal( @@ -1346,7 +1407,7 @@ impl SubCircuitConfig for TxCircuitConfig { cb.require_equal( "section_rlc == byte", meta.query_advice(section_rlc, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), ); cb.gate(and::expr([ @@ -1374,7 +1435,8 @@ impl SubCircuitConfig for TxCircuitConfig { 1.expr(), ); - let value_next_is_zero = value_is_zero.expr(Rotation::next())(meta); + let value_next_is_zero = meta.query_advice(is_value_zero, Rotation::next()); + let gas_cost_next = select::expr(value_next_is_zero, 4.expr(), 16.expr()); // call data gas cost accumulator check. cb.require_equal( @@ -1386,7 +1448,7 @@ impl SubCircuitConfig for TxCircuitConfig { "section_rlc' = section_rlc * r + byte'", meta.query_advice(section_rlc, Rotation::next()), meta.query_advice(section_rlc, Rotation::cur()) * challenges.keccak_input() - + meta.query_advice(tx_table.value, Rotation::next()), + + meta.query_advice(tx_table.value.lo(), Rotation::next()), ); }); @@ -1415,7 +1477,9 @@ impl SubCircuitConfig for TxCircuitConfig { meta.query_advice(is_calldata, Rotation::next()), ]), |cb| { - let value_next_is_zero = value_is_zero.expr(Rotation::next())(meta); + //let value_next_is_zero = value_is_zero.expr(Rotation::next())(meta); + let value_next_is_zero = meta.query_advice(is_value_zero, Rotation::next()); + let gas_cost_next = select::expr(value_next_is_zero, 4.expr(), 16.expr()); cb.require_equal( @@ -1431,7 +1495,7 @@ impl SubCircuitConfig for TxCircuitConfig { cb.require_equal( "section_rlc' == byte'", meta.query_advice(section_rlc, Rotation::next()), - meta.query_advice(tx_table.value, Rotation::next()), + meta.query_advice(tx_table.value.lo(), Rotation::next()), ); }, ); @@ -1501,7 +1565,7 @@ impl SubCircuitConfig for TxCircuitConfig { ); cb.require_equal( "access_list_address = value", - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(tx_table.access_list_address, Rotation::cur()), ); cb.require_zero( @@ -1628,8 +1692,9 @@ impl SubCircuitConfig for TxCircuitConfig { ]), |cb| { // we rely on the assumption that SigV is on the next of ChainID - let v = meta.query_advice(tx_table.value, Rotation::next()); - let chain_id = meta.query_advice(tx_table.value, Rotation::cur()); + let v = meta.query_advice(tx_table.value.lo(), Rotation::next()); + // tx_table.value.lo() can cover chain_id range. + let chain_id = meta.query_advice(tx_table.value.lo(), Rotation::cur()); cb.require_boolean( "V - (chain_id * 2 + 35) Є {0, 1}", @@ -1645,7 +1710,8 @@ impl SubCircuitConfig for TxCircuitConfig { tx_type_bits.value_equals(PreEip155, Rotation::cur())(meta), ]), |cb| { - let v = meta.query_advice(tx_table.value, Rotation::next()); + // use value.lo() for v + let v = meta.query_advice(tx_table.value.lo(), Rotation::next()); cb.require_boolean("V - 27 Є {0, 1}", v - 27.expr()); }, ); @@ -1657,8 +1723,8 @@ impl SubCircuitConfig for TxCircuitConfig { tx_type_bits.value_equals(L1Msg, Rotation::cur())(meta), ]), |cb| { - let v = meta.query_advice(tx_table.value, Rotation::next()); - cb.require_zero("V == 0", v); + let v = tx_table.value.query_advice(meta, Rotation::next()); + cb.require_zero_word("V == 0", v); }, ); @@ -1673,11 +1739,12 @@ impl SubCircuitConfig for TxCircuitConfig { "caller address == sv_address if it's not zero and tx_type != L1Msg", |meta| { let mut cb = BaseConstraintBuilder::default(); + let value_is_zero = meta.query_advice(is_value_zero, Rotation::cur()); - cb.condition(not::expr(value_is_zero.expr(Rotation::cur())(meta)), |cb| { + cb.condition(not::expr(value_is_zero), |cb| { cb.require_equal( "caller address == sv_address", - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(sv_address, Rotation::cur()), ); }); @@ -1703,12 +1770,15 @@ impl SubCircuitConfig for TxCircuitConfig { rlp_tag, is_none, tx_value_rlc, + tx_value_evm_rlc, tx_value_length, + tx_hash_word, u8_table, u16_table, tx_id_is_zero, - value_is_zero, + value_limb_is_zero: [value_is_zero_lo, value_is_zero_hi], tx_id_unchanged, + is_value_zero, is_calldata, is_tx_id_zero, is_caller_address, @@ -1757,7 +1827,9 @@ impl TxCircuitConfig { q_enable: Column, rlp_tag: Column, tx_value_rlc: Column, + tx_value_evm_rlc: Column, tx_value_length: Column, + tx_hash_word: word::Word>, tx_type_bits: BinaryNumberConfig, tx_id_is_zero: IsZeroConfig, is_none: Column, @@ -1812,8 +1884,8 @@ impl TxCircuitConfig { vec![ meta.query_advice(tx_table.tx_id, Rotation::cur()), CallData.expr(), - meta.query_advice(tx_table.value, Rotation::next()), // calldata_gas_cost - 1.expr(), // is_final = 1 + meta.query_advice(tx_table.value.lo(), Rotation::next()), // calldata_gas_cost + 1.expr(), // is_final = 1 ] .into_iter() .zip(vec![ @@ -1840,8 +1912,8 @@ impl TxCircuitConfig { vec![ meta.query_advice(tx_table.tx_id, Rotation::cur()), CallData.expr(), - meta.query_advice(tx_table.value, Rotation::cur()) - 1.expr(), /* index starts - * from 0 */ + meta.query_advice(tx_table.value.lo(), Rotation::cur()) - 1.expr(), /* index starts + * from 0 */ 1.expr(), // is_final = true ] .into_iter() @@ -1872,7 +1944,7 @@ impl TxCircuitConfig { let table_exprs = vec![ meta.query_advice(tx_table.tx_id, Rotation::cur()), meta.query_fixed(tx_table.tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), ]; input_exprs @@ -1896,7 +1968,8 @@ impl TxCircuitConfig { let table_exprs = vec![ meta.query_advice(tx_table.tx_id, Rotation::cur()), meta.query_fixed(tx_table.tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + // value.lo represents rlc of access list. + meta.query_advice(tx_table.value.lo(), Rotation::cur()), ]; input_exprs @@ -1920,7 +1993,7 @@ impl TxCircuitConfig { let table_exprs = vec![ meta.query_advice(tx_table.tx_id, Rotation::cur()), meta.query_fixed(tx_table.tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), ]; input_exprs @@ -1944,7 +2017,7 @@ impl TxCircuitConfig { let table_exprs = vec![ meta.query_advice(tx_table.tx_id, Rotation::cur()), meta.query_fixed(tx_table.tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), ]; input_exprs @@ -1965,9 +2038,9 @@ impl TxCircuitConfig { meta.query_advice(tx_table.tx_id, Rotation::cur()), 1.expr(), 1.expr(), - meta.query_advice(tx_table.value, Rotation(0)), // al_idx - meta.query_advice(tx_table.value, Rotation(1)), // sks_acc - meta.query_advice(tx_table.value, Rotation(2)), // section_rlc for access list + meta.query_advice(tx_table.value.lo(), Rotation(0)), // al_idx + meta.query_advice(tx_table.value.lo(), Rotation(1)), // sks_acc + meta.query_advice(tx_table.value.lo(), Rotation(2)), // section_rlc for access list ]; let table_exprs = vec![ meta.query_advice(tx_table.tx_id, Rotation::cur()), @@ -2044,7 +2117,8 @@ impl TxCircuitConfig { meta.query_advice(tx_table.tx_id, Rotation::cur()), sign_format, rlp_tag, - meta.query_advice(tx_table.value, Rotation::cur()), + //meta.query_advice(tx_table.value.lo(), Rotation::cur()), // lo covers tag + meta.query_advice(tx_value_evm_rlc, Rotation::cur()), meta.query_advice(tx_value_rlc, Rotation::cur()), meta.query_advice(tx_value_length, Rotation::cur()), 1.expr(), // is_output = true @@ -2086,7 +2160,8 @@ impl TxCircuitConfig { meta.query_advice(tx_table.tx_id, Rotation::cur()), hash_format, rlp_tag, - meta.query_advice(tx_table.value, Rotation::cur()), + //meta.query_advice(tx_table.value.lo(), Rotation::cur()), + meta.query_advice(tx_value_evm_rlc, Rotation::cur()), meta.query_advice(tx_value_rlc, Rotation::cur()), meta.query_advice(tx_value_length, Rotation::cur()), 1.expr(), // is_output = true @@ -2121,7 +2196,8 @@ impl TxCircuitConfig { meta.query_advice(tx_table.tx_id, Rotation::cur()), sign_format, meta.query_advice(rlp_tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + // value.lo represents rlc value used in RLP table. + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(field_rlc, Rotation::cur()), 20.expr(), // 20 bytes for address 1.expr(), // is_output = true @@ -2154,7 +2230,8 @@ impl TxCircuitConfig { meta.query_advice(tx_table.tx_id, Rotation::cur()), hash_format, meta.query_advice(rlp_tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + // check if lo is correct ? + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(field_rlc, Rotation::cur()), 20.expr(), // 20 bytes for address 1.expr(), // is_output = true @@ -2190,7 +2267,7 @@ impl TxCircuitConfig { meta.query_advice(tx_table.tx_id, Rotation::cur()), sign_format, meta.query_advice(rlp_tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(field_rlc, Rotation::cur()), 32.expr(), // 32 bytes for storage keys 1.expr(), // is_output = true @@ -2223,7 +2300,7 @@ impl TxCircuitConfig { meta.query_advice(tx_table.tx_id, Rotation::cur()), hash_format, meta.query_advice(rlp_tag, Rotation::cur()), - meta.query_advice(tx_table.value, Rotation::cur()), + meta.query_advice(tx_table.value.lo(), Rotation::cur()), meta.query_advice(field_rlc, Rotation::cur()), 32.expr(), // 32 bytes for storage keys 1.expr(), // is_output = true @@ -2251,22 +2328,26 @@ impl TxCircuitConfig { meta.query_advice(is_chain_id, Rotation::cur()), ]); - let msg_hash_rlc = meta.query_advice(tx_table.value, Rotation(6)); - let chain_id = meta.query_advice(tx_table.value, Rotation::cur()); - let sig_v = meta.query_advice(tx_table.value, Rotation(1)); - let sig_r = meta.query_advice(tx_table.value, Rotation(2)); - let sig_s = meta.query_advice(tx_table.value, Rotation(3)); + // value.lo can cover msg_hash_rlc, sig(v,r,s) as they are in rlc format. + let msg_hash_lo = meta.query_advice(tx_table.value.lo(), Rotation(6)); + // let msg_hash_hi = meta.query_advice(tx_table.value.hi(), Rotation(6)); + let chain_id = meta.query_advice(tx_table.value.lo(), Rotation::cur()); + let sig_v = meta.query_advice(tx_table.value.lo(), Rotation(1)); + let sig_r = meta.query_advice(tx_table.value.lo(), Rotation(2)); + let sig_s = meta.query_advice(tx_table.value.lo(), Rotation(3)); let sv_address = meta.query_advice(sv_address, Rotation::cur()); let v = is_eip155(meta) * (sig_v.expr() - 2.expr() * chain_id - 35.expr()) + is_pre_eip155(meta) * (sig_v.expr() - 27.expr()); let input_exprs = vec![ - 1.expr(), // q_enable = true - msg_hash_rlc, // msg_hash_rlc - v, // sig_v - sig_r, // sig_r - sig_s, // sig_s + 1.expr(), // q_enable = true + // msg_hash_rlc, // msg_hash_rlc + msg_hash_lo, + // msg_hash_hi, + v, // sig_v + sig_r, // sig_r + sig_s, // sig_s sv_address, 1.expr(), // is_valid ]; @@ -2276,6 +2357,8 @@ impl TxCircuitConfig { meta.query_fixed(sig_table.q_enable, Rotation::cur()), // msg_hash_rlc not needed to be looked up for tx circuit? meta.query_advice(sig_table.msg_hash_rlc, Rotation::cur()), + // meta.query_advice(sig_table.msg_hash_word.lo(), Rotation::cur()), + // meta.query_advice(sig_table.msg_hash_word.hi(), Rotation::cur()), meta.query_advice(sig_table.sig_v, Rotation::cur()), meta.query_advice(sig_table.sig_r_rlc, Rotation::cur()), meta.query_advice(sig_table.sig_s_rlc, Rotation::cur()), @@ -2303,11 +2386,13 @@ impl TxCircuitConfig { ]); vec![ - 1.expr(), // q_enable - 1.expr(), // is_final - meta.query_advice(tx_table.value, Rotation::next()), // input_rlc - meta.query_advice(tx_table.value, Rotation::cur()), // input_len - meta.query_advice(tx_table.value, Rotation(2)), // output_rlc + 1.expr(), // q_enable + 1.expr(), // is_final + meta.query_advice(tx_table.value.lo(), Rotation::next()), // input_rlc + meta.query_advice(tx_table.value.lo(), Rotation::cur()), // input_len + meta.query_advice(tx_table.value.lo(), Rotation(2)), // output_rlc + meta.query_advice(tx_hash_word.lo(), Rotation::cur()), // output_word lo + meta.query_advice(tx_hash_word.hi(), Rotation::cur()), // output_word hi ] .into_iter() .zip(keccak_table.table_exprs(meta)) @@ -2325,8 +2410,9 @@ impl TxCircuitConfig { 1, TxFieldTag::Null, 0, + word::Word::new([Value::known(F::zero()), Value::known(F::zero())]), Value::known(F::zero()), - Value::known(F::zero()), + // Value::known(F::zero()), )?; let (col_anno, col, col_val) = ("rlp_tag", self.rlp_tag, F::from(usize::from(Null) as u64)); region.assign_advice(|| col_anno, col, *offset, || Value::known(col_val))?; @@ -2349,7 +2435,7 @@ impl TxCircuitConfig { num_txs: u64, cum_num_txs: u64, challenges: &Challenges>, - ) -> Result>, Error> { + ) -> Result>>, Error> { let keccak_input = challenges.keccak_input(); let evm_word = challenges.evm_word(); let zero_rlc = keccak_input.map(|_| F::zero()); @@ -2378,7 +2464,7 @@ impl TxCircuitConfig { be_bytes_len: tx.nonce.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.nonce.to_be_bytes(), keccak_input), }), - Value::known(F::from(tx.nonce)), + word::Word::new([Value::known(F::from(tx.nonce)), Value::known(F::zero())]), ), ( GasPrice, @@ -2388,7 +2474,12 @@ impl TxCircuitConfig { be_bytes_len: tx.gas_price.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.gas_price.to_be_bytes(), keccak_input), }), - rlc_be_bytes(&tx.gas_price.to_be_bytes(), evm_word), + // use rlc format for `gas_price` since rlp/sig circuit requires. + // consider to change to word type in the future. + word::Word::new([ + rlc_be_bytes(&tx.gas_price.to_be_bytes(), evm_word), + Value::known(F::zero()), + ]), ), ( Gas, @@ -2398,7 +2489,8 @@ impl TxCircuitConfig { be_bytes_len: tx.gas.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.gas.to_be_bytes(), keccak_input), }), - Value::known(F::from(tx.gas)), + //Value::known(F::from(tx.gas)), + word::Word::new([Value::known(F::from(tx.gas)), Value::known(F::zero())]), ), ( CallerAddress, @@ -2408,7 +2500,10 @@ impl TxCircuitConfig { be_bytes_len: tx.caller_address.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.caller_address.to_fixed_bytes(), keccak_input), }), - Value::known(tx.caller_address.to_scalar().expect("tx.from too big")), + word::Word::new([ + Value::known(tx.caller_address.to_scalar().expect("fx from")), + Value::known(F::zero()), + ]), ), ( CalleeAddress, @@ -2423,14 +2518,24 @@ impl TxCircuitConfig { keccak_input, ), }), - Value::known( - tx.callee_address - .unwrap_or(Address::zero()) - .to_scalar() - .expect("tx.to too big"), - ), + word::Word::new([ + Value::known( + tx.callee_address + .unwrap_or(Address::zero()) + .to_scalar() + .expect("fx to"), + ), + Value::known(F::zero()), + ]), + ), + ( + IsCreate, + None, + word::Word::new([ + Value::known(F::from(tx.is_create as u64)), + Value::known(F::zero()), + ]), ), - (IsCreate, None, Value::known(F::from(tx.is_create as u64))), ( TxFieldTag::Value, Some(RlpTableInputValue { @@ -2439,7 +2544,7 @@ impl TxCircuitConfig { be_bytes_len: tx.value.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.value.to_be_bytes(), keccak_input), }), - rlc_be_bytes(&tx.value.to_be_bytes(), evm_word), + word::Word::from(tx.value.to_word()).map(Value::known), ), ( CallDataRLC, @@ -2449,17 +2554,29 @@ impl TxCircuitConfig { be_bytes_len: tx.call_data.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.call_data, keccak_input), }), - rlc_be_bytes(&tx.call_data, keccak_input), + //rlc_be_bytes(&tx.call_data, keccak_input) + word::Word::new([ + rlc_be_bytes(&tx.call_data, keccak_input), + Value::known(F::zero()), + ]), ), ( CallDataLength, None, - Value::known(F::from(tx.call_data.len() as u64)), + //Value::known(F::from(tx.call_data.len() as u64)), + word::Word::new([ + Value::known(F::from(tx.call_data.len() as u64)), + Value::known(F::zero()), + ]), ), ( CallDataGasCost, None, - Value::known(F::from(tx.call_data_gas_cost)), + //Value::known(F::from(tx.call_data_gas_cost)), + word::Word::new([ + Value::known(F::from(tx.call_data_gas_cost)), + Value::known(F::zero()), + ]), ), ( TxDataGasCost, @@ -2469,7 +2586,11 @@ impl TxCircuitConfig { be_bytes_len: 0, be_bytes_rlc: zero_rlc, }), - Value::known(F::from(tx.tx_data_gas_cost)), + //Value::known(F::from(tx.tx_data_gas_cost)), + word::Word::new([ + Value::known(F::from(tx.tx_data_gas_cost)), + Value::known(F::zero()), + ]), ), ( ChainID, @@ -2479,7 +2600,8 @@ impl TxCircuitConfig { be_bytes_len: tx.chain_id.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.chain_id.to_be_bytes(), keccak_input), }), - Value::known(F::from(tx.chain_id)), + //Value::known(F::from(tx.chain_id)), + word::Word::new([Value::known(F::from(tx.chain_id)), Value::known(F::zero())]), ), ( SigV, @@ -2489,7 +2611,7 @@ impl TxCircuitConfig { be_bytes_len: tx.v.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.v.to_be_bytes(), keccak_input), }), - Value::known(F::from(tx.v)), + word::Word::new([Value::known(F::from(tx.v)), Value::known(F::zero())]), ), ( SigR, @@ -2499,7 +2621,12 @@ impl TxCircuitConfig { be_bytes_len: tx.r.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.r.to_be_bytes(), keccak_input), }), - rlc_be_bytes(&tx.r.to_be_bytes(), evm_word), + // use rlc format for `SigR` since rlp/sig circuit requires. + // consider to change to word type in the future. + word::Word::new([ + Value::known(unwrap_value(rlc_be_bytes(&tx.r.to_be_bytes(), evm_word))), + Value::known(F::zero()), + ]), ), ( SigS, @@ -2509,7 +2636,12 @@ impl TxCircuitConfig { be_bytes_len: tx.s.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.s.to_be_bytes(), keccak_input), }), - rlc_be_bytes(&tx.s.to_be_bytes(), evm_word), + // use rlc format for `SigS` since rlp/sig circuit requires. + // consider to change to word type in the future. + word::Word::new([ + Value::known(unwrap_value(rlc_be_bytes(&tx.s.to_be_bytes(), evm_word))), + Value::known(F::zero()), + ]), ), ( TxSignLength, @@ -2519,7 +2651,10 @@ impl TxCircuitConfig { be_bytes_len: rlp_sign_tag_length, be_bytes_rlc: zero_rlc, }), - Value::known(F::from(tx.rlp_unsigned.len() as u64)), + word::Word::new([ + Value::known(F::from(tx.rlp_unsigned.len() as u64)), + Value::known(F::zero()), + ]), ), ( TxSignRLC, @@ -2529,9 +2664,17 @@ impl TxCircuitConfig { be_bytes_len: 0, be_bytes_rlc: zero_rlc, }), - rlc_be_bytes(&tx.rlp_unsigned, keccak_input), + //rlc_be_bytes(&tx.rlp_unsigned, keccak_input), + word::Word::new([ + rlc_be_bytes(&tx.rlp_unsigned, keccak_input), + Value::known(F::zero()), + ]), + ), + ( + TxSignHash, + None, + word::Word::new([sign_hash_rlc, Value::known(F::zero())]), ), - (TxSignHash, None, sign_hash_rlc), ( TxHashLength, Some(RlpTableInputValue { @@ -2540,7 +2683,10 @@ impl TxCircuitConfig { be_bytes_len: get_rlp_len_tag_length(&tx.rlp_signed), be_bytes_rlc: zero_rlc, }), - Value::known(F::from(tx.rlp_signed.len() as u64)), + word::Word::new([ + Value::known(F::from(tx.rlp_signed.len() as u64)), + Value::known(F::zero()), + ]), ), ( TxHashRLC, @@ -2550,23 +2696,44 @@ impl TxCircuitConfig { be_bytes_len: 0, be_bytes_rlc: zero_rlc, }), - rlc_be_bytes(&tx.rlp_signed, keccak_input), + //rlc_be_bytes(&tx.rlp_signed, keccak_input), + word::Word::new([ + rlc_be_bytes(&tx.rlp_signed, keccak_input), + Value::known(F::zero()), + ]), + ), + ( + TxFieldTag::TxHash, + None, + // use rlc format for `SigS` since keccak lookup requires. + // consider to change to word type in the future. + word::Word::new([hash_rlc, Value::known(F::zero())]), ), - (TxFieldTag::TxHash, None, hash_rlc), ( TxFieldTag::TxType, None, - Value::known(F::from(tx.tx_type as u64)), + word::Word::new([ + Value::known(F::from(tx.tx_type as u64)), + Value::known(F::zero()), + ]), ), ( AccessListAddressesLen, None, - Value::known(F::from(access_list_address_size)), + //Value::known(F::from(access_list_address_size)), + word::Word::new([ + Value::known(F::from(access_list_address_size)), + Value::known(F::zero()), + ]), ), ( AccessListStorageKeysLen, None, - Value::known(F::from(access_list_storage_key_size)), + //Value::known(F::from(access_list_storage_key_size)), + word::Word::new([ + Value::known(F::from(access_list_storage_key_size)), + Value::known(F::zero()), + ]), ), ( AccessListRLC, @@ -2576,7 +2743,11 @@ impl TxCircuitConfig { be_bytes_len: 0, be_bytes_rlc: zero_rlc, }), - access_list_rlc(&tx.access_list, challenges), + // access_list_rlc(&tx.access_list, challenges), + word::Word::new([ + access_list_rlc(&tx.access_list, challenges), + Value::known(F::zero()), + ]), ), ( MaxFeePerGas, @@ -2586,7 +2757,11 @@ impl TxCircuitConfig { be_bytes_len: tx.max_fee_per_gas.tag_length(), be_bytes_rlc: rlc_be_bytes(&tx.max_fee_per_gas.to_be_bytes(), keccak_input), }), - rlc_be_bytes(&tx.max_fee_per_gas.to_be_bytes(), evm_word), + // rlc_be_bytes(&tx.max_fee_per_gas.to_be_bytes(), evm_word), + word::Word::new([ + rlc_be_bytes(&tx.max_fee_per_gas.to_be_bytes(), evm_word), + Value::known(F::zero()), + ]), ), ( MaxPriorityFeePerGas, @@ -2599,10 +2774,27 @@ impl TxCircuitConfig { keccak_input, ), }), - rlc_be_bytes(&tx.max_priority_fee_per_gas.to_be_bytes(), evm_word), + //rlc_be_bytes(&tx.max_priority_fee_per_gas.to_be_bytes(), evm_word), + word::Word::new([ + rlc_be_bytes(&tx.max_priority_fee_per_gas.to_be_bytes(), evm_word), + Value::known(F::zero()), + ]), + ), + ( + BlockNumber, + None, + word::Word::new([ + Value::known(F::from(tx.block_number)), + Value::known(F::zero()), + ]), ), - (BlockNumber, None, Value::known(F::from(tx.block_number))), ]; + + // constructs two hashes' words + let sign_hash_word = word::Word::from(U256::from_big_endian(&sign_hash)).map(Value::known); + let tx_hash_word = word::Word::from(U256::from_big_endian(&hash)).map(Value::known); + let tx_value_evm_rlc = rlc_be_bytes(&tx.value.to_be_bytes(), evm_word); + for (tx_tag, rlp_input, tx_value) in fixed_rows { let rlp_tag = rlp_input.clone().map_or(Null, |input| input.tag); let rlp_is_none = rlp_input.clone().map_or(false, |input| input.is_none); @@ -2666,7 +2858,7 @@ impl TxCircuitConfig { ), ( "sv_address", - self.sv_address, + self.sv_address, // not change to word sign_data.get_addr().to_scalar().unwrap(), ), ( @@ -2699,6 +2891,17 @@ impl TxCircuitConfig { let (col_anno, col, col_val) = ("tx_value_rlc", self.tx_value_rlc, rlp_be_bytes_rlc); region.assign_advice(|| col_anno, col, *offset, || col_val)?; + let value_evm_rlc = if tx_tag == TxFieldTag::Value { + tx_value_evm_rlc + } else { + tx_value.lo() + }; + region.assign_advice( + || "tx_value_evm_rlc", + self.tx_value_evm_rlc, + *offset, + || value_evm_rlc, + )?; } // lookup conditions @@ -2822,6 +3025,24 @@ impl TxCircuitConfig { F::from(tx.id as u64), )?; + let hash_word_assign = if tx_tag == TxFieldTag::TxSignLength { + sign_hash_word + } else if tx_tag == TxFieldTag::TxHashLength { + tx_hash_word + } else { + // for non TxSignLength & non TxHashLength tag, this word value won't lookup, hence + // all set zero limbs. word::Word::default() will panic as default + // limbs are none. + word::Word::new([F::zero(), F::zero()]).map(Value::known) + }; + + hash_word_assign.assign_advice( + region, + || "assign sign_hash_word", + self.tx_hash_word, + *offset, + )?; + *offset += 1; } Ok(tx_value_cells) @@ -2859,7 +3080,8 @@ impl TxCircuitConfig { tx_id_next, CallData, idx as u64, - Value::known(F::from(*byte as u64)), + word::Word::new([Value::known(F::from(*byte as u64)), Value::known(F::zero())]), + //Value::known(F::from(*byte as u64)), Value::known(F::zero()), )?; @@ -2940,7 +3162,10 @@ impl TxCircuitConfig { tx_id_next, TxFieldTag::AccessListAddress, (al_idx + 1) as u64, - Value::known(al.address.to_scalar().unwrap()), + word::Word::new([ + Value::known(al.address.to_scalar().unwrap()), + Value::known(F::zero()), + ]), Value::known(al.address.to_scalar().unwrap()), )?; @@ -2998,7 +3223,11 @@ impl TxCircuitConfig { tx_id_next, TxFieldTag::AccessListStorageKey, sks_acc as u64, - rlc_be_bytes(&sk.to_fixed_bytes(), challenges.evm_word()), + //rlc_be_bytes(&sk.to_fixed_bytes(), challenges.evm_word()), + word::Word::new([ + rlc_be_bytes(&sk.to_fixed_bytes(), challenges.evm_word()), + Value::known(F::zero()), + ]), Value::known(al.address.to_scalar().unwrap()), )?; @@ -3056,9 +3285,9 @@ impl TxCircuitConfig { tx_id_next: usize, tag: TxFieldTag, index: u64, - value: Value, + value: word::Word>, access_list_address: Value, - ) -> Result, Error> { + ) -> Result>, Error> { let (tx_type, tx_id) = if let Some(tx) = tx { (tx.tx_type, tx.id) } else { @@ -3075,9 +3304,19 @@ impl TxCircuitConfig { let tx_id_is_zero_chip = IsZeroChip::construct(self.tx_id_is_zero.clone()); tx_id_is_zero_chip.assign(region, offset, Value::known(F::from(tx_id as u64)))?; - let value_is_zero_chip = IsZeroChip::construct(self.value_is_zero.clone()); - value_is_zero_chip.assign(region, offset, value)?; + let value_word_zero = + unwrap_value(value.lo()) == F::zero() && unwrap_value(value.hi()) == F::zero(); + let value_is_zero_lo_chip = IsZeroChip::construct(self.value_limb_is_zero[0].clone()); + value_is_zero_lo_chip.assign(region, offset, value.lo())?; + let value_is_zero_hi_chip = IsZeroChip::construct(self.value_limb_is_zero[1].clone()); + value_is_zero_hi_chip.assign(region, offset, value.hi())?; + region.assign_advice( + || "tx circuit: is_value_zero", + self.is_value_zero, + offset, + || Value::known(F::from(value_word_zero as u64)), + )?; let tx_id_unchanged_chip = IsEqualChip::construct(self.tx_id_unchanged.clone()); tx_id_unchanged_chip.assign( region, @@ -3132,10 +3371,12 @@ impl TxCircuitConfig { )?; // 2nd phase columns - let tx_value_cell = - region.assign_advice(|| "tx_value", self.tx_table.value, offset, || value)?; + // let tx_value_cell = + // region.assign_advice(|| "tx_value", self.tx_table.value, offset, || value)?; - Ok(tx_value_cell) + let tx_value_cells = + value.assign_advice(region, || "tx_value", self.tx_table.value, offset)?; + Ok(tx_value_cells) } fn assign_calldata_zeros( @@ -3144,10 +3385,11 @@ impl TxCircuitConfig { start: usize, end: usize, ) -> Result<(), Error> { - // let rlp_data = F::from( as u64); let tag = F::from(CallData as u64); let tx_id_is_zero_chip = IsZeroChip::construct(self.tx_id_is_zero.clone()); - let value_is_zero_chip = IsZeroChip::construct(self.value_is_zero.clone()); + let value_is_zero_lo_chip = IsZeroChip::construct(self.value_limb_is_zero[0].clone()); + let value_is_zero_hi_chip = IsZeroChip::construct(self.value_limb_is_zero[1].clone()); + let tx_id_unchanged = IsEqualChip::construct(self.tx_id_unchanged.clone()); let tag_chip = BinaryNumberChip::construct(self.tx_tag_bits); @@ -3169,7 +3411,9 @@ impl TxCircuitConfig { // no need to assign tx_id_is_zero_chip for real prover as tx_id = 0 tx_id_is_zero_chip.assign(region, offset, Value::known(F::zero()))?; // no need to assign value_is_zero_chip for real prover as value = 0 - value_is_zero_chip.assign(region, offset, Value::known(F::zero()))?; + value_is_zero_lo_chip.assign(region, offset, Value::known(F::zero()))?; + value_is_zero_hi_chip.assign(region, offset, Value::known(F::zero()))?; + tx_id_unchanged.assign( region, offset, @@ -3180,7 +3424,10 @@ impl TxCircuitConfig { for (col, value) in [ (self.tx_table.tx_id, F::zero()), (self.tx_table.index, F::zero()), - (self.tx_table.value, F::zero()), + (self.tx_table.value.lo(), F::zero()), + (self.tx_table.value.hi(), F::zero()), + // when value is zero, is_value_zero is true + (self.is_value_zero, F::one()), (self.is_final, F::one()), (self.is_calldata, F::one()), (self.calldata_gas_cost_acc, F::zero()), @@ -3239,7 +3486,8 @@ pub struct TxCircuit { /// Size pub size: usize, /// Tx value cells (exported for PI circuit) - pub value_cells: RefCell>>>, + #[allow(clippy::type_complexity)] + pub value_cells: RefCell>>>>, _marker: PhantomData, } @@ -3415,12 +3663,11 @@ impl TxCircuit { start_l1_queue_index: u64, sign_datas: Vec, padding_txs: &[Transaction], - ) -> Result>, Error> { + ) -> Result>>, Error> { layouter.assign_region( || "tx table aux", |mut region| { let mut offset = 0; - let sigs = &sign_datas; debug_assert_eq!(padding_txs.len() + self.txs.len(), sigs.len()); @@ -3620,7 +3867,6 @@ impl SubCircuit for TxCircuit { layouter: &mut impl Layouter, ) -> Result<(), Error> { assert!(self.txs.len() <= self.max_txs); - let padding_txs = (self.txs.len()..self.max_txs) .map(|i| { let mut tx = Transaction::dummy(self.chain_id); diff --git a/zkevm-circuits/src/tx_circuit/dev.rs b/zkevm-circuits/src/tx_circuit/dev.rs index e17711710c..adb6abf556 100644 --- a/zkevm-circuits/src/tx_circuit/dev.rs +++ b/zkevm-circuits/src/tx_circuit/dev.rs @@ -6,9 +6,7 @@ pub use super::TxCircuit; use crate::{ sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, - table::{ - BlockTable, KeccakTable, RlpFsmRlpTable as RlpTable, SigTable, TxTable, U16Table, U8Table, - }, + table::{BlockTable, KeccakTable, RlpFsmRlpTable as RlpTable, SigTable, TxTable, UXTable}, tx_circuit::{TxCircuitConfig, TxCircuitConfigArgs}, util::{Challenges, SubCircuit, SubCircuitConfig}, witness::Transaction, @@ -32,9 +30,9 @@ pub struct TxCircuitTesterConfigArgs { /// SigTable pub sig_table: SigTable, /// u8 lookup table, - pub u8_table: U8Table, + pub u8_table: UXTable<8>, /// u16 lookup table, - pub u16_table: U16Table, + pub u16_table: UXTable<16>, /// Challenges pub challenges: Challenges>, } @@ -46,9 +44,9 @@ pub struct TxCircuitTesterConfig { // SigTable is assigned inside SigCircuit sig_config: SigCircuitConfig, /// u16 lookup table, - pub u8_table: U8Table, + pub u8_table: UXTable<8>, /// u16 lookup table, - pub u16_table: U16Table, + pub u16_table: UXTable<16>, } impl SubCircuitConfig for TxCircuitTesterConfig { @@ -168,8 +166,8 @@ impl Circuit for TxCircuitTester { let keccak_table = KeccakTable::construct(meta); let rlp_table = RlpTable::construct(meta); let sig_table = SigTable::construct(meta); - let u8_table = U8Table::construct(meta); - let u16_table = U16Table::construct(meta); + let u8_table = UXTable::construct(meta); + let u16_table = UXTable::construct(meta); let challenges = Challenges::construct(meta); let config = { diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index a0e10fac08..98c89232c3 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -18,8 +18,10 @@ use eth_types::{Field, ToAddress, Word}; pub use ethers_core::types::{Address, U256}; pub use gadgets::util::Expr; +pub mod int_decomposition; /// A wrapper of is_zero in gadgets which gives is_zero at any rotation pub mod is_zero; +pub mod word; pub(crate) fn query_expression( meta: &mut ConstraintSystem, diff --git a/zkevm-circuits/src/util/int_decomposition.rs b/zkevm-circuits/src/util/int_decomposition.rs new file mode 100644 index 0000000000..dd2ae65b41 --- /dev/null +++ b/zkevm-circuits/src/util/int_decomposition.rs @@ -0,0 +1,108 @@ +//! Define IntDecomposition to decompose int into byte limbs +use eth_types::{Field, ToLittleEndian, H160, U256}; +use gadgets::util::{sum, Expr}; +use halo2_proofs::{ + circuit::{AssignedCell, Value}, + plonk::{Error, Expression}, +}; +use itertools::Itertools; + +use crate::evm_circuit::{ + param::{MAX_N_BYTES_INTEGER, N_BYTES_HALF_WORD}, + util::{rlc, CachedRegion, Cell}, +}; + +use super::word::{Word, WordExpr}; + +#[derive(Clone, Debug)] +/// IntDecomposition decompose integer into byte limbs +pub struct IntDecomposition { + /// inner cells in little-endian for synthesis + pub limbs: [Cell; N_LIMBS], +} + +impl IntDecomposition { + /// new by cell limbs + pub fn new(limbs: [Cell; N_LIMBS]) -> Self { + Self { limbs } + } + + /// assign bytes to cells + pub fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes: Option<[u8; N_BYTES]>, + ) -> Result>>, Error> { + assert!(N_BYTES >= N_LIMBS); + if let Some(bytes) = bytes { + if N_BYTES > N_LIMBS { + for byte in &bytes[N_LIMBS..] { + assert_eq!(*byte, 0); + } + } + } + bytes.map_or(Err(Error::Synthesis), |bytes| { + self.limbs + .iter() + .zip(bytes.iter()) + .map(|(cell, byte)| { + cell.assign(region, offset, Value::known(F::from(*byte as u64))) + }) + .collect() + }) + } + + /// assign h160 to cells + pub fn assign_h160( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + h160: H160, + ) -> Result>>, Error> { + let mut bytes = *h160.as_fixed_bytes(); + bytes.reverse(); + self.assign(region, offset, Some(bytes)) + } + + /// assign u256 to cells + pub fn assign_u256( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + u256: U256, + ) -> Result>>, Error> { + self.assign(region, offset, Some(u256.to_le_bytes())) + } + + /// assign all limbs into one expression + pub fn sum_expr(&self) -> Expression { + sum::expr(self.limbs.clone()) + } +} + +impl Expr for IntDecomposition { + fn expr(&self) -> Expression { + assert!(N_LIMBS <= MAX_N_BYTES_INTEGER); + rlc::expr(&self.limbs.clone().map(|limb| limb.expr()), 256.expr()) + } +} + +impl WordExpr for IntDecomposition { + fn to_word(&self) -> Word> { + let exprs = self + .limbs + .clone() + .map(|x| x.expr()) + .chunks(N_BYTES_HALF_WORD) + .map(|chunk| rlc::expr(chunk, 256.expr())) + .collect::>>(); + Word::new( + (0..2) + .map(|id| exprs.get(id).unwrap_or(&0.expr()).clone()) + .collect_vec() + .try_into() + .unwrap(), + ) + } +} diff --git a/zkevm-circuits/src/util/word.rs b/zkevm-circuits/src/util/word.rs new file mode 100644 index 0000000000..396896fa1f --- /dev/null +++ b/zkevm-circuits/src/util/word.rs @@ -0,0 +1,461 @@ +//! Define generic Word type with utility functions +// Naming Convesion +// - 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, VirtualCells}, + poly::Rotation, +}; +use itertools::Itertools; + +use crate::evm_circuit::util::{from_bytes, CachedRegion, Cell}; + +/// evm word 32 bytes, half word 16 bytes +const N_BYTES_HALF_WORD: usize = 16; + +/// The EVM word for witness +#[derive(Clone, Debug, Copy)] +pub struct WordLimbs { + /// The limbs of this word. + pub limbs: [T; N], +} + +pub(crate) type Word2 = WordLimbs; + +pub(crate) type Word4 = WordLimbs; + +pub(crate) type Word32 = WordLimbs; + +pub(crate) type WordCell = Word>; + +pub(crate) type Word32Cell = Word32>; + +impl WordLimbs { + /// Constructor + pub fn new(limbs: [T; N]) -> Self { + Self { limbs } + } + /// The number of limbs + pub fn n() -> usize { + N + } +} + +impl WordLimbs, N> { + /// Query advice of WordLibs of columns advice + pub fn query_advice( + &self, + meta: &mut VirtualCells, + at: Rotation, + ) -> WordLimbs, N> { + WordLimbs::new(self.limbs.map(|column| meta.query_advice(column, at))) + } +} + +impl WordLimbs { + /// Convert WordLimbs of u8 to WordLimbs of expressions + pub fn to_expr(&self) -> WordLimbs, N> { + WordLimbs::new(self.limbs.map(|v| Expression::Constant(F::from(v as u64)))) + } +} + +impl Default for WordLimbs { + fn default() -> Self { + Self { + limbs: [(); N].map(|_| T::default()), + } + } +} + +/// Get the word expression +pub trait WordExpr { + /// Get the word expression + fn to_word(&self) -> Word>; +} + +impl WordLimbs, N> { + /// assign bytes to wordlimbs first half/second half respectively + // N_LO, N_HI are number of bytes to assign to first half and second half of size N limbs, + // respectively N_LO and N_HI can be different size, the only requirement is N_LO % (N/2) + // and N_HI % (N/2) [N/2] limbs will be assigned separately. + // E.g. N_LO = 4 => [nl1, nl2, nl3, nl4] + // N_HI = 2 => [nh1, nh2] + // N = 2 => [l1, l2] + // it equivalent l1.assign(nl1.expr() + nl2.expr() * 256 + nl3.expr() * 256^2 + nl3.expr() * + // 256^3) and l2.assign(nh1.expr() + nh2.expr() * 256) + fn assign_lo_hi( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes_lo_le: [u8; N_LO], + bytes_hi_le: Option<[u8; N_HI]>, + ) { + assert_eq!(N % 2, 0); // TODO use static_assertion instead + assert_eq!(N_LO % (N / 2), 0); + assert_eq!(N_HI % (N / 2), 0); + let half_limb_size = N / 2; + + // assign lo + for (value, cell) in bytes_lo_le + .chunks(N_LO / half_limb_size) // chunk in little endian + .map(|chunk| from_bytes::value(chunk)) + .zip_eq(self.limbs[0..half_limb_size].iter()) + { + let _res = cell.assign(region, offset, Value::known(value)); + // disable clippy complaint: err should be handled + match _res { + Ok(_value) => {} + Err(error) => println!("Error: {error}"), + } + } + // assign hi + + if let Some(bytes) = bytes_hi_le { + for (value, cell) in bytes + .chunks(N_HI / half_limb_size) // chunk in little endian + .map(|chunk| from_bytes::value(chunk)) + .zip_eq(self.limbs[half_limb_size..].iter()) + { + let _res = cell.assign(region, offset, Value::known(value)); + // disable clippy complaint: err should be handled + match _res { + Ok(_value) => {} + Err(error) => println!("Error: {error}"), + } + } + } + } + + /// assign u256 to wordlimbs + pub fn assign_u256( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + word: eth_types::Word, + ) -> Result { + self.assign_lo_hi::( + region, + offset, + word.to_le_bytes()[0..N_BYTES_HALF_WORD].try_into().unwrap(), + word.to_le_bytes()[N_BYTES_HALF_WORD..].try_into().ok(), + ); + + Ok(true) + } + + /// assign h160 to wordlimbs + pub fn assign_h160( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + h160: H160, + ) -> Result { + let mut bytes = *h160.as_fixed_bytes(); + bytes.reverse(); + self.assign_lo_hi::( + region, + offset, + bytes[0..N_BYTES_HALF_WORD].try_into().unwrap(), + bytes[N_BYTES_HALF_WORD..].try_into().ok(), + ); + + Ok(true) + } + + /// assign u64 to wordlimbs + pub fn assign_u64( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: u64, + ) -> Result { + self.assign_lo_hi(region, offset, value.to_le_bytes(), Option::<[u8; 0]>::None); + Ok(true) + } + + /// word expr + fn word_expr(&self) -> WordLimbs, N> { + WordLimbs::new(self.limbs.clone().map(|cell| cell.expr())) + } + + /// convert from N cells to N2 expressions limbs + pub fn to_word_n(&self) -> WordLimbs, N2> { + self.word_expr().to_word_n() + } +} + +impl WordExpr for WordLimbs, N> { + fn to_word(&self) -> Word> { + Word(self.word_expr().to_word_n()) + } +} + +impl WordLimbs { + /// Check if zero + pub fn is_zero_vartime(&self) -> bool { + self.limbs.iter().all(|limb| limb.is_zero_vartime()) + } +} + +/// `Word`, special alias for Word2. +#[derive(Clone, Debug, Copy, Default)] +pub struct Word(Word2); + +impl Word { + /// Construct the word from 2 limbs + pub fn new(limbs: [T; 2]) -> Self { + Self(WordLimbs::::new(limbs)) + } + /// The high 128 bits limb + pub fn hi(&self) -> T { + self.0.limbs[1].clone() + } + /// the low 128 bits limb + pub fn lo(&self) -> T { + self.0.limbs[0].clone() + } + /// number of limbs + pub fn n() -> usize { + 2 + } + /// word to low and high 128 bits + pub fn to_lo_hi(&self) -> (T, T) { + (self.0.limbs[0].clone(), self.0.limbs[1].clone()) + } + + /// Extract (move) lo and hi values + pub fn into_lo_hi(self) -> (T, T) { + let [lo, hi] = self.0.limbs; + (lo, hi) + } + + /// Wrap `Word` into `Word` + pub fn into_value(self) -> Word> { + let [lo, hi] = self.0.limbs; + Word::new([Value::known(lo), Value::known(hi)]) + } + + /// Map the word to other types + pub fn map(&self, mut func: impl FnMut(T) -> T2) -> Word { + Word(WordLimbs::::new([func(self.lo()), func(self.hi())])) + } +} + +impl std::ops::Deref for Word { + type Target = WordLimbs; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl PartialEq for Word { + fn eq(&self, other: &Self) -> bool { + self.lo() == other.lo() && self.hi() == other.hi() + } +} + +impl From for Word { + /// Construct the word from u256 + fn from(value: eth_types::Word) -> Self { + let bytes = value.to_le_bytes(); + Word::new([ + from_bytes::value(&bytes[..N_BYTES_HALF_WORD]), + from_bytes::value(&bytes[N_BYTES_HALF_WORD..]), + ]) + } +} + +impl From for Word { + /// Construct the word from H256 + fn from(h: H256) -> Self { + let le_bytes = { + let mut b = h.to_fixed_bytes(); + b.reverse(); + b + }; + Word::new([ + from_bytes::value(&le_bytes[..N_BYTES_HALF_WORD]), + from_bytes::value(&le_bytes[N_BYTES_HALF_WORD..]), + ]) + } +} + +impl From for Word { + /// Construct the word from u64 + fn from(value: u64) -> Self { + let bytes = value.to_le_bytes(); + Word::new([from_bytes::value(&bytes), F::from(0)]) + } +} + +impl From for Word { + /// Construct the word from u8 + fn from(value: u8) -> Self { + Word::new([F::from(value as u64), F::from(0)]) + } +} + +impl From for Word { + fn from(value: bool) -> Self { + Word::new([F::from(value as u64), F::from(0)]) + } +} + +impl From for Word { + /// Construct the word from h160 + fn from(value: H160) -> Self { + let mut bytes = *value.as_fixed_bytes(); + bytes.reverse(); + Word::new([ + from_bytes::value(&bytes[..N_BYTES_HALF_WORD]), + from_bytes::value(&bytes[N_BYTES_HALF_WORD..]), + ]) + } +} + +impl Word> { + /// Assign advice + pub fn assign_advice( + &self, + region: &mut Region<'_, F>, + annotation: A, + column: Word>, + offset: usize, + ) -> Result>, Error> + where + A: Fn() -> AR, + AR: Into, + { + let annotation: String = annotation().into(); + let lo = region.assign_advice(|| &annotation, column.lo(), offset, || self.lo())?; + let hi = region.assign_advice(|| &annotation, column.hi(), offset, || self.hi())?; + + Ok(Word::new([lo, hi])) + } +} + +impl Word> { + /// Query advice of Word of columns advice + pub fn query_advice( + &self, + meta: &mut VirtualCells, + at: Rotation, + ) -> Word> { + self.0.query_advice(meta, at).to_word() + } +} + +impl WordExpr for Word> { + fn to_word(&self) -> Word> { + self.word_expr().to_word() + } +} + +impl Word> { + /// create word from lo limb with hi limb as 0. caller need to guaranteed to be 128 bits. + pub fn from_lo_unchecked(lo: Expression) -> Self { + Self(WordLimbs::, 2>::new([lo, 0.expr()])) + } + /// zero word + pub fn zero() -> Self { + Self(WordLimbs::, 2>::new([0.expr(), 0.expr()])) + } + + /// one word + pub fn one() -> Self { + Self(WordLimbs::, 2>::new([1.expr(), 0.expr()])) + } + + /// select based on selector. Here assume selector is 1/0 therefore no overflow check + pub fn select + Clone>( + selector: T, + when_true: Word, + when_false: Word, + ) -> Word> { + let (true_lo, true_hi) = when_true.to_lo_hi(); + + let (false_lo, false_hi) = when_false.to_lo_hi(); + Word::new([ + selector.expr() * true_lo.expr() + (1.expr() - selector.expr()) * false_lo.expr(), + selector.expr() * true_hi.expr() + (1.expr() - selector.expr()) * false_hi.expr(), + ]) + } + + /// Assume selector is 1/0 therefore no overflow check + pub fn mul_selector(&self, selector: Expression) -> Self { + Word::new([self.lo() * selector.clone(), self.hi() * selector]) + } + + /// No overflow check on lo/hi limbs + pub fn add_unchecked(self, rhs: Self) -> Self { + Word::new([self.lo() + rhs.lo(), self.hi() + rhs.hi()]) + } + + /// No underflow check on lo/hi limbs + pub fn sub_unchecked(self, rhs: Self) -> Self { + Word::new([self.lo() - rhs.lo(), self.hi() - rhs.hi()]) + } + + /// No overflow check on lo/hi limbs + pub fn mul_unchecked(self, rhs: Self) -> Self { + Word::new([self.lo() * rhs.lo(), self.hi() * rhs.hi()]) + } +} + +impl WordExpr for Word> { + fn to_word(&self) -> Word> { + self.clone() + } +} + +impl WordLimbs, 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 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 + pub fn to_word_n(&self) -> WordLimbs, N2> { + assert_eq!(N1 % N2, 0); + let limbs = self + .limbs + .chunks(N1 / N2) + .map(|chunk| from_bytes::expr(chunk)) + .collect_vec() + .try_into() + .unwrap(); + WordLimbs::, N2>::new(limbs) + } + + /// Equality expression + // TODO static assertion. wordaround https://github.com/nvzqz/static-assertions-rs/issues/40 + pub fn eq(&self, others: &WordLimbs, N2>) -> Expression { + assert_eq!(N1 % N2, 0); + not::expr(or::expr( + self.limbs + .chunks(N1 / N2) + .map(|chunk| from_bytes::expr(chunk)) + .zip(others.limbs.clone()) + .map(|(expr1, expr2)| expr1 - expr2) + .collect_vec(), + )) + } +} + +impl WordExpr for WordLimbs, N1> { + fn to_word(&self) -> Word> { + Word(self.to_word_n()) + } +} + +/// Return the hash of the empty code as a Word> in little-endian. +pub fn empty_code_hash_word_value() -> Word> { + Word::from(CodeDB::empty_code_hash()).into_value() +} + +// TODO unittest diff --git a/zkevm-circuits/src/witness/bytecode.rs b/zkevm-circuits/src/witness/bytecode.rs index bdf5a2daab..0082e8c498 100644 --- a/zkevm-circuits/src/witness/bytecode.rs +++ b/zkevm-circuits/src/witness/bytecode.rs @@ -1,8 +1,9 @@ +use crate::util::word; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToLittleEndian, Word}; +use eth_types::{Field, Word}; use halo2_proofs::circuit::Value; -use crate::{evm_circuit::util::rlc, table::BytecodeFieldTag, util::Challenges}; +use crate::{table::BytecodeFieldTag, util::Challenges}; /// Bytecode #[derive(Clone, Debug)] @@ -18,22 +19,17 @@ impl Bytecode { pub fn table_assignments( &self, challenges: &Challenges>, - ) -> Vec<[Value; 6]> { + ) -> Vec<[Value; 7]> { let n = 1 + self.bytes.len(); let mut rows = Vec::with_capacity(n); - let hash = if cfg!(feature = "poseidon-codehash") { - challenges - .evm_word() - .map(|_challenge| rlc::value(&self.hash.to_le_bytes(), F::from(256u64))) - //Value::known(rlc::value(&self.hash.to_le_bytes(), F::from(256u64))) - } else { - challenges - .evm_word() - .map(|challenge| rlc::value(&self.hash.to_le_bytes(), challenge)) - }; + + // no need rlc for hash now + let hash_word: word::Word> = word::Word::::from(self.hash).map(Value::known); rows.push([ - hash, + hash_word.lo(), + hash_word.hi(), + // hash Value::known(F::from(BytecodeFieldTag::Header as u64)), Value::known(F::zero()), Value::known(F::zero()), @@ -61,7 +57,9 @@ impl Bytecode { } rows.push([ - hash, + hash_word.lo(), + hash_word.hi(), + //hash, Value::known(F::from(BytecodeFieldTag::Byte as u64)), Value::known(F::from(idx as u64)), Value::known(F::from(is_code as u64)), diff --git a/zkevm-circuits/src/witness/mpt.rs b/zkevm-circuits/src/witness/mpt.rs index ee517c34ce..eae4f576ce 100644 --- a/zkevm-circuits/src/witness/mpt.rs +++ b/zkevm-circuits/src/witness/mpt.rs @@ -1,8 +1,5 @@ -use crate::{ - evm_circuit::{util::rlc, witness::Rw}, - table::AccountFieldTag, -}; -use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word, U256}; +use crate::{evm_circuit::witness::Rw, table::AccountFieldTag, util::word}; +use eth_types::{Address, Field, ToScalar, Word, U256}; use halo2_proofs::circuit::Value; use itertools::Itertools; use mpt_zktrie::{ @@ -103,8 +100,16 @@ pub struct MptUpdates { } /// The field element encoding of an MPT update, which is used by the MptTable -#[derive(Debug, Clone, Copy)] -pub struct MptUpdateRow(pub(crate) [F; 7]); +#[derive(Clone, Copy, Debug)] +pub struct MptUpdateRow { + pub(crate) address: F, + pub(crate) storage_key: word::Word, + pub(crate) proof_type: F, + pub(crate) new_root: word::Word, + pub(crate) old_root: word::Word, + pub(crate) new_value: word::Word, + pub(crate) old_value: word::Word, +} impl MptUpdates { pub(crate) fn len(&self) -> usize { @@ -326,13 +331,22 @@ impl MptUpdates { } } - pub(crate) fn table_assignments( - &self, - randomness: Value, - ) -> Vec>> { + pub(crate) fn table_assignments(&self) -> Vec>> { self.updates .values() - .map(|update| update.table_assignments(randomness)) + .map(|update| { + let (new_root, old_root) = update.root_assignments(); + let (new_value, old_value) = update.value_assignments(); + MptUpdateRow { + address: Value::known(update.key.address().to_scalar().unwrap()), + storage_key: word::Word::::from(update.key.storage_key()).into_value(), + proof_type: Value::known(F::from(update.proof_type() as u64)), + new_root: word::Word::::from(new_root).into_value(), + old_root: word::Word::::from(old_root).into_value(), + new_value: word::Word::::from(new_value).into_value(), + old_value: word::Word::::from(old_value).into_value(), + } + }) .collect() } @@ -351,57 +365,15 @@ impl MptUpdate { pub(crate) fn values(&self) -> (Word, Word) { (self.new_value, self.old_value) } - - pub(crate) fn value_assignments(&self, word_randomness: F) -> (F, F) { - let assign = |x: Word| match self.key { - Key::Account { - field_tag: AccountFieldTag::CodeHash, - .. - } => { - if cfg!(feature = "poseidon-codehash") { - x.to_scalar().unwrap() - } else { - rlc::value(&x.to_le_bytes(), word_randomness) - } - } - Key::Account { - field_tag: - AccountFieldTag::Nonce | AccountFieldTag::NonExisting | AccountFieldTag::CodeSize, - .. - } => x.to_scalar().unwrap(), - _ => rlc::value(&x.to_le_bytes(), word_randomness), - }; - - (assign(self.new_value), assign(self.old_value)) + pub(crate) fn value_assignments(&self) -> (Word, Word) { + (self.new_value, self.old_value) } - pub(crate) fn root_assignments(&self, word_randomness: F) -> (F, F) { - ( - rlc::value(&self.new_root.to_le_bytes(), word_randomness), - rlc::value(&self.old_root.to_le_bytes(), word_randomness), - ) + pub(crate) fn root_assignments(&self) -> (Word, Word) { + (self.new_root, self.old_root) } - pub(crate) fn table_assignments( - &self, - randomness: Value, - ) -> MptUpdateRow> { - let (new_root, old_root) = randomness - .map(|randomness| self.root_assignments(randomness)) - .unzip(); - let (new_value, old_value) = randomness - .map(|randomness| self.value_assignments(randomness)) - .unzip(); - MptUpdateRow([ - Value::known(self.key.address()), - randomness.map(|randomness| self.key.storage_key(randomness)), - Value::known(F::from(self.proof_type() as u64)), - new_root, - old_root, - new_value, - old_value, - ]) - } + // mpt proof type fn proof_type(&self) -> MPTProofType { match self.key { Key::AccountStorage { .. } => { @@ -472,19 +444,16 @@ impl Key { self } } - fn address(&self) -> F { + fn address(&self) -> Address { match self { - Self::Account { address, .. } | Self::AccountStorage { address, .. } => { - address.to_scalar().unwrap() - } + Self::Account { address, .. } | Self::AccountStorage { address, .. } => *address, } } - fn storage_key(&self, randomness: F) -> F { + + fn storage_key(&self) -> Word { match self { - Self::Account { .. } => F::zero(), - Self::AccountStorage { storage_key, .. } => { - rlc::value(&storage_key.to_le_bytes(), randomness) - } + Self::Account { .. } => Word::zero(), + Self::AccountStorage { storage_key, .. } => *storage_key, } } } @@ -492,15 +461,36 @@ impl Key { impl MptUpdateRow> { /// Corresponds to the padding row the mpt circuit uses to fill its columns. pub fn padding() -> Self { - let mut values = [F::zero(); 7]; - values[2] = F::from(MPTProofType::AccountDoesNotExist as u64); - Self(values.map(Value::known)) + let zero_word = word::Word::new([Value::known(F::zero()); 2]); + let proof_type = F::from(MPTProofType::AccountDoesNotExist as u64); + Self { + address: Value::known(F::zero()), + storage_key: zero_word, + proof_type: Value::known(proof_type), + new_root: zero_word, + old_root: zero_word, + new_value: zero_word, + old_value: zero_word, + } } /// The individual values of the row, in the column order used by the /// MptTable - pub fn values(&self) -> impl Iterator> { - self.0.iter() + pub fn values(&self) -> [Value; 12] { + [ + self.address, + self.storage_key.lo(), + self.storage_key.hi(), + self.proof_type, + self.new_root.lo(), + self.new_root.hi(), + self.old_root.lo(), + self.old_root.hi(), + self.new_value.lo(), + self.new_value.hi(), + self.old_value.lo(), + self.old_value.hi(), + ] } } diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index a0b6f5ed80..97a2699715 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -5,16 +5,16 @@ use bus_mapping::{ operation::{self, AccountField, CallContextField, TxLogField, TxReceiptField}, Error, }; -use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word, U256}; +use eth_types::{Address, Field, ToScalar, Word, U256}; -use halo2_proofs::{circuit::Value, halo2curves::bn256::Fr}; +use halo2_proofs::circuit::Value; use itertools::Itertools; use rayon::prelude::{ParallelBridge, ParallelIterator}; use crate::{ - evm_circuit::util::rlc, + // evm_circuit::util::rlc, table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag, TxReceiptFieldTag}, - util::build_tx_log_address, + util::{build_tx_log_address, word}, }; use super::MptUpdates; @@ -98,7 +98,6 @@ impl RwMap { /// Check value in the same way like StateCircuit pub fn check_value_strict(&self) { - let mock_rand = Fr::from(0x1000u64); let rows = self.table_assignments(); let updates = MptUpdates::from_rws_with_mock_state_roots( &rows, @@ -122,12 +121,12 @@ impl RwMap { key(prev_row) != key(row) }; if !row.is_write() { - let value = row.value_assignment::(mock_rand); + let value = row.value_assignment(); if is_first { // value == init_value let init_value = updates .get(row) - .map(|u| u.value_assignments(mock_rand).1) + .map(|u| u.value_assignments().1) .unwrap_or_default(); if value != init_value { // EIP2930 @@ -137,7 +136,7 @@ impl RwMap { } } else { // value == prev_value - let prev_value = prev_row.value_assignment::(mock_rand); + let prev_value = prev_row.value_assignment(); if value != prev_value { errs.push((idx, ERR_MSG_NON_FIRST, *row, Some(*prev_row))); } @@ -330,15 +329,16 @@ pub struct RwRow { pub(crate) id: F, pub(crate) address: F, pub(crate) field_tag: F, - pub(crate) storage_key: F, - pub(crate) value: F, - pub(crate) value_prev: F, - pub(crate) aux1: F, - pub(crate) aux2: F, + pub(crate) storage_key: word::Word, + pub(crate) value: word::Word, + pub(crate) value_prev: word::Word, + //pub(crate) init_val: word::Word, + pub(crate) aux1: word::Word, + pub(crate) aux2: word::Word, } impl RwRow { - pub(crate) fn values(&self) -> [F; 11] { + pub(crate) fn values(&self) -> [F; 16] { [ self.rw_counter, self.is_write, @@ -346,11 +346,16 @@ impl RwRow { self.id, self.address, self.field_tag, - self.storage_key, - self.value, - self.value_prev, - self.aux1, - self.aux2, + self.storage_key.lo(), + self.storage_key.hi(), + self.value.lo(), + self.value.hi(), + self.value_prev.lo(), + self.value_prev.hi(), + self.aux1.lo(), + self.aux1.hi(), + self.aux2.lo(), + self.aux2.hi(), ] } pub(crate) fn rlc(&self, randomness: F) -> F { @@ -366,6 +371,36 @@ impl RwRow { } } +impl RwRow> { + pub(crate) fn unwrap(self) -> RwRow { + let unwrap_f = |f: Value| { + let mut inner = None; + _ = f.map(|v| { + inner = Some(v); + }); + inner.unwrap_or_default() + }; + let unwrap_w = |f: word::Word>| { + let (lo, hi) = f.into_lo_hi(); + word::Word::new([unwrap_f(lo), unwrap_f(hi)]) + }; + + RwRow { + rw_counter: unwrap_f(self.rw_counter), + is_write: unwrap_f(self.is_write), + tag: unwrap_f(self.tag), + id: unwrap_f(self.id), + address: unwrap_f(self.address), + field_tag: unwrap_f(self.field_tag), + storage_key: unwrap_w(self.storage_key), + value: unwrap_w(self.value), + value_prev: unwrap_w(self.value_prev), + aux1: unwrap_w(self.aux1), + aux2: unwrap_w(self.aux2), + } + } +} + impl Rw { pub fn tx_access_list_value_pair(&self) -> (bool, bool) { match self { @@ -496,28 +531,25 @@ impl Rw { // At this moment is a helper for the EVM circuit until EVM challange API is // applied - pub(crate) fn table_assignment_aux(&self, randomness: F) -> RwRow { + pub(crate) fn table_assignment_aux(&self) -> RwRow> { RwRow { - rw_counter: F::from(self.rw_counter() as u64), - is_write: F::from(self.is_write() as u64), - tag: F::from(self.tag() as u64), - id: F::from(self.id().unwrap_or_default() as u64), - address: self.address().unwrap_or_default().to_scalar().unwrap(), - field_tag: F::from(self.field_tag().unwrap_or_default()), - storage_key: rlc::value( - &self.storage_key().unwrap_or_default().to_le_bytes(), - randomness, - ), - value: self.value_assignment(randomness), - value_prev: self.value_prev_assignment(randomness).unwrap_or_default(), - aux1: F::zero(), // only used for AccountStorage::tx_id, which moved to key1. - aux2: self - .committed_value_assignment(randomness) - .unwrap_or_default(), + rw_counter: Value::known(F::from(self.rw_counter() as u64)), + is_write: Value::known(F::from(self.is_write() as u64)), + tag: Value::known(F::from(self.tag() as u64)), + id: Value::known(F::from(self.id().unwrap_or_default() as u64)), + address: Value::known(self.address().unwrap_or_default().to_scalar().unwrap()), + field_tag: Value::known(F::from(self.field_tag().unwrap_or_default())), + storage_key: word::Word::from(self.storage_key().unwrap_or_default()).into_value(), + value: word::Word::from(self.value_assignment()).into_value(), + value_prev: word::Word::from(self.value_prev_assignment().unwrap_or_default()) + .into_value(), + aux1: word::Word::from(U256::zero()).into_value(), + aux2: word::Word::from(self.committed_value_assignment().unwrap_or_default()) + .into_value(), } } - pub(crate) fn table_assignment(&self, randomness: Value) -> RwRow> { + pub(crate) fn table_assignment(&self) -> RwRow> { RwRow { rw_counter: Value::known(F::from(self.rw_counter() as u64)), is_write: Value::known(F::from(self.is_write() as u64)), @@ -525,21 +557,13 @@ impl Rw { id: Value::known(F::from(self.id().unwrap_or_default() as u64)), address: Value::known(self.address().unwrap_or_default().to_scalar().unwrap()), field_tag: Value::known(F::from(self.field_tag().unwrap_or_default())), - storage_key: randomness.map(|randomness| { - rlc::value( - &self.storage_key().unwrap_or_default().to_le_bytes(), - randomness, - ) - }), - value: randomness.map(|randomness| self.value_assignment(randomness)), - value_prev: randomness - .map(|randomness| self.value_prev_assignment(randomness).unwrap_or_default()), - aux1: Value::known(F::zero()), /* only used for AccountStorage::tx_id, which moved to - * key1. */ - aux2: randomness.map(|randomness| { - self.committed_value_assignment(randomness) - .unwrap_or_default() - }), + storage_key: word::Word::from(self.storage_key().unwrap_or_default()).into_value(), + value: word::Word::from(self.value_assignment()).into_value(), + value_prev: word::Word::from(self.value_prev_assignment().unwrap_or_default()) + .into_value(), + aux1: word::Word::from(U256::zero()).into_value(), + aux2: word::Word::from(self.committed_value_assignment().unwrap_or_default()) + .into_value(), } } @@ -678,59 +702,18 @@ impl Rw { } } - pub(crate) fn value_assignment(&self, randomness: F) -> F { + pub(crate) fn value_assignment(&self) -> Word { match self { - Self::Start { .. } => F::zero(), - Self::CallContext { - field_tag, value, .. - } => { - match field_tag { - // Only these two tags have values that may not fit into a scalar, so we need to - // RLC. (for poseidon hash feature, CodeHash not need rlc) - CallContextFieldTag::CodeHash => { - if cfg!(feature = "poseidon-codehash") { - value.to_scalar().unwrap() - } else { - rlc::value(&value.to_le_bytes(), randomness) - } - } - CallContextFieldTag::Value => rlc::value(&value.to_le_bytes(), randomness), - _ => value.to_scalar().unwrap(), - } - } - Self::Account { - value, field_tag, .. - } => match field_tag { - AccountFieldTag::KeccakCodeHash | AccountFieldTag::Balance => { - rlc::value(&value.to_le_bytes(), randomness) - } - AccountFieldTag::CodeHash => { - if cfg!(feature = "poseidon-codehash") { - value.to_scalar().unwrap() - } else { - rlc::value(&value.to_le_bytes(), randomness) - } - } - AccountFieldTag::Nonce - | AccountFieldTag::NonExisting - | AccountFieldTag::CodeSize => value.to_scalar().unwrap(), - }, - Self::AccountStorage { value, .. } | Self::Stack { value, .. } => { - rlc::value(&value.to_le_bytes(), randomness) - } - - Self::TxLog { - field_tag, value, .. - } => match field_tag { - TxLogFieldTag::Topic => rlc::value(&value.to_le_bytes(), randomness), - TxLogFieldTag::Data => rlc::value(&value.to_le_bytes(), randomness), - _ => value.to_scalar().unwrap(), - }, - + Self::Start { .. } => U256::zero(), + Self::CallContext { value, .. } + | Self::Account { value, .. } + | Self::AccountStorage { value, .. } + | Self::Stack { value, .. } + | Self::Memory { value, .. } + | Self::TxLog { value, .. } => *value, Self::TxAccessListAccount { is_warm, .. } - | Self::TxAccessListAccountStorage { is_warm, .. } => F::from(*is_warm as u64), - Self::Memory { value, .. } => rlc::value(&value.to_le_bytes(), randomness), - Self::TxRefund { value, .. } | Self::TxReceipt { value, .. } => F::from(*value), + | Self::TxAccessListAccountStorage { is_warm, .. } => U256::from(*is_warm as u64), + Self::TxRefund { value, .. } | Self::TxReceipt { value, .. } => U256::from(*value), } } @@ -749,38 +732,18 @@ impl Rw { } } - pub(crate) fn value_prev_assignment(&self, randomness: F) -> Option { + pub(crate) fn value_prev_assignment(&self) -> Option { match self { - Self::Account { - value_prev, - field_tag, - .. - } => Some(match field_tag { - AccountFieldTag::KeccakCodeHash | AccountFieldTag::Balance => { - rlc::value(&value_prev.to_le_bytes(), randomness) - } - AccountFieldTag::CodeHash => { - if cfg!(feature = "poseidon-codehash") { - value_prev.to_scalar().unwrap() - } else { - rlc::value(&value_prev.to_le_bytes(), randomness) - } - } - AccountFieldTag::Nonce - | AccountFieldTag::NonExisting - | AccountFieldTag::CodeSize => value_prev.to_scalar().unwrap(), - }), - Self::AccountStorage { value_prev, .. } => { - Some(rlc::value(&value_prev.to_le_bytes(), randomness)) - } - Self::Memory { value_prev, .. } => { - Some(rlc::value(&value_prev.to_le_bytes(), randomness)) + Self::Account { value_prev, .. } | Self::AccountStorage { value_prev, .. } => { + Some(*value_prev) } Self::TxAccessListAccount { is_warm_prev, .. } | Self::TxAccessListAccountStorage { is_warm_prev, .. } => { - Some(F::from(*is_warm_prev as u64)) + Some(U256::from(*is_warm_prev as u64)) } - Self::TxRefund { value_prev, .. } => Some(F::from(*value_prev)), + Self::TxRefund { value_prev, .. } => Some(U256::from(*value_prev)), + Self::Memory { value_prev, .. } => Some(*value_prev), + Self::Start { .. } | Self::Stack { .. } | Self::CallContext { .. } @@ -789,11 +752,11 @@ impl Rw { } } - fn committed_value_assignment(&self, randomness: F) -> Option { + fn committed_value_assignment(&self) -> Option { match self { Self::AccountStorage { committed_value, .. - } => Some(rlc::value(&committed_value.to_le_bytes(), randomness)), + } => Some(*committed_value), _ => None, } } diff --git a/zkevm-circuits/src/witness/tx.rs b/zkevm-circuits/src/witness/tx.rs index 0ace2b752b..df4bcd45f1 100644 --- a/zkevm-circuits/src/witness/tx.rs +++ b/zkevm-circuits/src/witness/tx.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{step::ExecutionState, util::rlc}, table::TxContextFieldTag, - util::{rlc_be_bytes, Challenges}, + util::{rlc_be_bytes, word, Challenges}, witness::{ rlp_fsm::{RlpStackOp, SmState}, DataTable, Format, @@ -169,6 +169,14 @@ impl Transaction { let tx_sign_hash_be_bytes = keccak256(&self.rlp_unsigned); let (access_list_address_size, access_list_storage_key_size) = access_list_size(&self.access_list); + let value_word = word::Word::from(self.value.to_word()).map(Value::known); + // currently fields like gas_price, tx_hash, tx_sign_hash still use rlc format for keccak + // lookup usage, can be changed to word type later. + // let gas_price_word = word::Word::from(self.gas_price.to_word()).map(Value::known); + // let tx_hash_word = + // word::Word::from(Word::from_big_endian(&tx_hash_be_bytes)).map(Value::known); + // let tx_sign_hash_word = + // word::Word::from(Word::from_big_endian(&tx_sign_hash_be_bytes)).map(Value::known); let ret = vec![ [ @@ -186,6 +194,9 @@ impl Transaction { .evm_word() .map(|challenge| rlc::value(&self.gas_price.to_le_bytes(), challenge)), Value::known(F::zero()), + // can not use word type as tx circuit use rlc value to lookup keccak table + // gas_price_word.lo(), + // gas_price_word.hi(), ], [ Value::known(F::from(self.id as u64)), @@ -224,10 +235,11 @@ impl Transaction { Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::Value as u64)), Value::known(F::zero()), - challenges - .evm_word() - .map(|challenge| rlc::value(&self.value.to_le_bytes(), challenge)), - Value::known(F::zero()), + // challenges + // .evm_word() + // .map(|challenge| rlc::value(&self.value.to_le_bytes(), challenge)), + value_word.lo(), + value_word.hi(), ], [ Value::known(F::from(self.id as u64)), @@ -275,6 +287,8 @@ impl Transaction { Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::SigR as u64)), Value::known(F::zero()), + // still use rlc format for `SigR` since rlp/sig circuit requires. + // consider to change to word type in the future. rlc_be_bytes(&self.r.to_be_bytes(), challenges.evm_word()), Value::known(F::zero()), ], @@ -282,6 +296,8 @@ impl Transaction { Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::SigS as u64)), Value::known(F::zero()), + // still use rlc format for `SigS` since rlp/sig circuit requires. + // consider to change to word type in the future. rlc_be_bytes(&self.s.to_be_bytes(), challenges.evm_word()), Value::known(F::zero()), ], @@ -303,6 +319,8 @@ impl Transaction { Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::TxSignHash as u64)), Value::known(F::zero()), + // still use rlc format for TxSignHash since keccak lookup requires. + // consider to change to word type in the future. rlc_be_bytes(&tx_sign_hash_be_bytes, challenges.evm_word()), Value::known(F::zero()), ], @@ -324,6 +342,8 @@ impl Transaction { Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::TxHash as u64)), Value::known(F::zero()), + // still use rlc format for TxHash since keccak lookup requires. + // consider to change to word type in the future. rlc_be_bytes(&tx_hash_be_bytes, challenges.evm_word()), Value::known(F::zero()), ], diff --git a/zktrie/Cargo.toml b/zktrie/Cargo.toml index 7e5b4ff5e0..138ac829b4 100644 --- a/zktrie/Cargo.toml +++ b/zktrie/Cargo.toml @@ -8,8 +8,8 @@ license.workspace = true [dependencies] halo2_proofs.workspace = true -mpt-circuits = { package = "halo2-mpt-circuits", git = "https://github.com/scroll-tech/mpt-circuit.git", branch = "v0.7" } -zktrie = { git = "https://github.com/scroll-tech/zktrie.git", tag = "v0.7.1" } +mpt-circuits = { package = "halo2-mpt-circuits", git = "https://github.com/scroll-tech/mpt-circuit.git", branch = "v0.8" } +zktrie = { git = "https://github.com/scroll-tech/zktrie.git", tag = "v0.7.1"} hash-circuit.workspace = true eth-types = { path = "../eth-types" } num-bigint.workspace = true