From e71851ce81c3cebc43ff3df28d980ef511b7b883 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Thu, 8 Jun 2023 11:54:46 +0100 Subject: [PATCH 01/57] feat: preliminary work --- .../src/circuit_input_builder/execution.rs | 2 +- bus-mapping/src/evm/opcodes/callop.rs | 8 +- bus-mapping/src/precompile.rs | 8 +- eth-types/src/evm_types.rs | 2 +- zkevm-circuits/src/copy_circuit.rs | 2 +- zkevm-circuits/src/evm_circuit/execution.rs | 7 +- .../execution/precompiles/ecrecover.rs | 212 ++++++++++++++++++ .../evm_circuit/execution/precompiles/mod.rs | 3 + zkevm-circuits/src/evm_circuit/step.rs | 8 +- .../src/evm_circuit/util/precompile_gadget.rs | 14 +- zkevm-circuits/src/witness/step.rs | 2 +- 11 files changed, 247 insertions(+), 21 deletions(-) create mode 100644 zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index e43e74425e..c995f1b37b 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -212,7 +212,7 @@ impl CopyDataTypeIter { 3usize => Some(CopyDataType::TxCalldata), 4usize => Some(CopyDataType::TxLog), 5usize => Some(CopyDataType::RlcAcc), - 6usize => Some(CopyDataType::Precompile(PrecompileCalls::ECRecover)), + 6usize => Some(CopyDataType::Precompile(PrecompileCalls::Ecrecover)), 7usize => Some(CopyDataType::Precompile(PrecompileCalls::Sha256)), 8usize => Some(CopyDataType::Precompile(PrecompileCalls::Ripemd160)), 9usize => Some(CopyDataType::Precompile(PrecompileCalls::Identity)), diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index cd84154ccb..67c27a0f01 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -269,10 +269,12 @@ impl Opcode for CallOpcode { callee_gas_left, ); - log::trace!( - "precompile returned data len {} gas {}", + log::info!( + "precompile returned {:?} with len {} and gas {} and is_success {}", + result, result.len(), - contract_gas_cost + contract_gas_cost, + call.is_success(), ); // mutate the caller memory. diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index c6d7d9b2fd..f71791d80c 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -27,7 +27,7 @@ pub(crate) fn execute_precompiled(address: &Address, input: &[u8], gas: u64) -> #[derive(Copy, Clone, Debug, Eq, PartialEq, EnumIter)] pub enum PrecompileCalls { /// Elliptic Curve Recovery - ECRecover = 0x01, + Ecrecover = 0x01, /// SHA2-256 hash function Sha256 = 0x02, /// Ripemd-160 hash function @@ -48,7 +48,7 @@ pub enum PrecompileCalls { impl Default for PrecompileCalls { fn default() -> Self { - Self::ECRecover + Self::Ecrecover } } @@ -75,7 +75,7 @@ impl From for usize { impl From for PrecompileCalls { fn from(value: u8) -> Self { match value { - 0x01 => Self::ECRecover, + 0x01 => Self::Ecrecover, 0x02 => Self::Sha256, 0x03 => Self::Ripemd160, 0x04 => Self::Identity, @@ -93,7 +93,7 @@ impl PrecompileCalls { /// Get the base gas cost for the precompile call. pub fn base_gas_cost(&self) -> GasCost { match self { - Self::ECRecover => GasCost::PRECOMPILE_EC_RECOVER_BASE, + Self::Ecrecover => GasCost::PRECOMPILE_ECRECOVER_BASE, Self::Sha256 => GasCost::PRECOMPILE_SHA256_BASE, Self::Ripemd160 => GasCost::PRECOMPILE_RIPEMD160_BASE, Self::Identity => GasCost::PRECOMPILE_IDENTITY_BASE, diff --git a/eth-types/src/evm_types.rs b/eth-types/src/evm_types.rs index 8008fa59a5..1bc5463ea7 100644 --- a/eth-types/src/evm_types.rs +++ b/eth-types/src/evm_types.rs @@ -178,7 +178,7 @@ impl GasCost { /// it from 10 to 50. pub const EXP_BYTE_TIMES: Self = Self(50); /// Base gas price for precompile call: Elliptic curve recover - pub const PRECOMPILE_EC_RECOVER_BASE: Self = Self(3000); + pub const PRECOMPILE_ECRECOVER_BASE: Self = Self(3000); /// Base gas price for precompile call: SHA256 pub const PRECOMPILE_SHA256_BASE: Self = Self(60); /// Per-word gas price for SHA256 diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 434b684c7b..fe6c2e3be8 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -164,7 +164,7 @@ impl SubCircuitConfig for CopyCircuitConfig { let is_memory = meta.query_advice(is_memory, Rotation::cur()); let precompiles = sum::expr([ tag.value_equals( - CopyDataType::Precompile(PrecompileCalls::ECRecover), + CopyDataType::Precompile(PrecompileCalls::Ecrecover), Rotation::cur(), )(meta), tag.value_equals( diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 7242674212..12cc5dab7b 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -202,7 +202,7 @@ use opcode_not::NotGadget; use origin::OriginGadget; use pc::PcGadget; use pop::PopGadget; -use precompiles::IdentityGadget; +use precompiles::{EcrecoverGadget, IdentityGadget}; use push::PushGadget; use return_revert::ReturnRevertGadget; use returndatacopy::ReturnDataCopyGadget; @@ -347,8 +347,7 @@ pub(crate) struct ExecutionConfig { error_precompile_failed: Box>, error_return_data_out_of_bound: Box>, // precompile calls - precompile_ecrecover_gadget: - Box>, + precompile_ecrecover_gadget: Box>, precompile_sha2_gadget: Box>, precompile_ripemd_gadget: Box>, precompile_identity_gadget: Box>, @@ -1494,7 +1493,7 @@ impl ExecutionConfig { ExecutionState::ErrorPrecompileFailed => { assign_exec_step!(self.error_precompile_failed) } - ExecutionState::PrecompileEcRecover => { + ExecutionState::PrecompileEcrecover => { assign_exec_step!(self.precompile_ecrecover_gadget) } ExecutionState::PrecompileSha256 => { diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs new file mode 100644 index 0000000000..82aa29dcad --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -0,0 +1,212 @@ +use eth_types::{Field, ToScalar}; +use gadgets::util::Expr; +use halo2_proofs::{circuit::Value, plonk::Error}; + +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + step::ExecutionState, + util::{ + common_gadget::RestoreContextGadget, constraint_builder::EVMConstraintBuilder, + CachedRegion, Cell, + }, + }, + table::CallContextFieldTag, + witness::{Block, Call, ExecStep, Transaction}, +}; + +#[derive(Clone, Debug)] +pub struct EcrecoverGadget { + msg_hash: Cell, + sig_v: Cell, + sig_r: Cell, + sig_s: Cell, + recovered_addr: Cell, + + is_success: Cell, + callee_address: Cell, + caller_id: Cell, + call_data_offset: Cell, + call_data_length: Cell, + return_data_offset: Cell, + return_data_length: Cell, + restore_context: RestoreContextGadget, +} + +impl ExecutionGadget for EcrecoverGadget { + const EXECUTION_STATE: ExecutionState = ExecutionState::PrecompileEcrecover; + + const NAME: &'static str = "ECRECOVER"; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let (msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + cb.query_cell_phase2(), + cb.query_cell(), + cb.query_cell_phase2(), + cb.query_cell_phase2(), + cb.query_cell(), + ); + + // TODO: lookup to the sign_verify table: https://github.com/scroll-tech/zkevm-circuits/issues/527 + // || v | r | s | msg_hash | recovered_addr || + + let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] = + [ + CallContextFieldTag::IsSuccess, + CallContextFieldTag::CalleeAddress, + CallContextFieldTag::CallerId, + CallContextFieldTag::CallDataOffset, + CallContextFieldTag::CallDataLength, + CallContextFieldTag::ReturnDataOffset, + CallContextFieldTag::ReturnDataLength, + ] + .map(|tag| cb.call_context(None, tag)); + + cb.precompile_info_lookup( + cb.execution_state().as_u64().expr(), + callee_address.expr(), + cb.execution_state().precompile_base_gas_cost().expr(), + ); + + let restore_context = RestoreContextGadget::construct( + cb, + is_success.expr(), + 0.expr(), + 0.expr(), + 0.expr(), + 0.expr(), + 0.expr(), + ); + + Self { + sig_v, + sig_r, + sig_s, + msg_hash, + recovered_addr, + is_success, + callee_address, + caller_id, + call_data_offset, + call_data_length, + return_data_offset, + return_data_length, + restore_context, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + _tx: &Transaction, + call: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + // TODO: assignment to the signature, msg hash and recovered address cells. + + self.is_success.assign( + region, + offset, + Value::known(F::from(u64::from(call.is_success))), + )?; + self.callee_address.assign( + region, + offset, + Value::known(call.code_address.unwrap().to_scalar().unwrap()), + )?; + self.caller_id + .assign(region, offset, Value::known(F::from(call.caller_id as u64)))?; + self.call_data_offset.assign( + region, + offset, + Value::known(F::from(call.call_data_offset)), + )?; + self.call_data_length.assign( + region, + offset, + Value::known(F::from(call.call_data_length)), + )?; + self.return_data_offset.assign( + region, + offset, + Value::known(F::from(call.return_data_offset)), + )?; + self.return_data_length.assign( + region, + offset, + Value::known(F::from(call.return_data_length)), + )?; + + self.restore_context + .assign(region, offset, block, call, step, 7) + } +} +#[cfg(test)] +mod test { + use bus_mapping::{ + evm::{OpcodeId, PrecompileCallArgs}, + precompile::PrecompileCalls, + }; + use eth_types::{bytecode, word, ToWord}; + use itertools::Itertools; + use mock::TestContext; + + use crate::test_util::CircuitTestBuilder; + + lazy_static::lazy_static! { + static ref TEST_VECTOR: Vec = { + vec![ + PrecompileCallArgs { + name: "ecrecover", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + // copy 96 bytes from memory addr 0 + call_data_offset: 0x00.into(), + call_data_length: 0x20.into(), + // return 32 bytes and write from memory addr 96 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + ] + }; + } + + #[test] + fn precompile_ecrecover_test() { + let call_kinds = vec![ + OpcodeId::CALL, + OpcodeId::STATICCALL, + OpcodeId::DELEGATECALL, + OpcodeId::CALLCODE, + ]; + + for (test_vector, &call_kind) in TEST_VECTOR.iter().cartesian_product(&call_kinds) { + let bytecode = test_vector.with_call_op(call_kind); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + } + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs index be0938ac96..7a9fd3a4e9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs @@ -15,6 +15,9 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }; +mod ecrecover; +pub use ecrecover::EcrecoverGadget; + mod identity; pub use identity::IdentityGadget; diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index df75bf5f63..c20a18cf65 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -22,7 +22,7 @@ use strum_macros::EnumIter; impl From for ExecutionState { fn from(value: PrecompileCalls) -> Self { match value { - PrecompileCalls::ECRecover => ExecutionState::PrecompileEcRecover, + PrecompileCalls::Ecrecover => ExecutionState::PrecompileEcrecover, PrecompileCalls::Sha256 => ExecutionState::PrecompileSha256, PrecompileCalls::Ripemd160 => ExecutionState::PrecompileRipemd160, PrecompileCalls::Identity => ExecutionState::PrecompileIdentity, @@ -125,7 +125,7 @@ pub enum ExecutionState { ErrorOutOfGasCREATE, ErrorOutOfGasSELFDESTRUCT, // Precompiles - PrecompileEcRecover, + PrecompileEcrecover, PrecompileSha256, PrecompileRipemd160, PrecompileIdentity, @@ -160,7 +160,7 @@ impl ExecutionState { pub(crate) fn is_precompiled(&self) -> bool { matches!( self, - Self::PrecompileEcRecover + Self::PrecompileEcrecover | Self::PrecompileSha256 | Self::PrecompileRipemd160 | Self::PrecompileIdentity @@ -174,7 +174,7 @@ impl ExecutionState { pub(crate) fn precompile_base_gas_cost(&self) -> GasCost { (match self { - Self::PrecompileEcRecover => PrecompileCalls::ECRecover, + Self::PrecompileEcrecover => PrecompileCalls::Ecrecover, Self::PrecompileSha256 => PrecompileCalls::Sha256, Self::PrecompileRipemd160 => PrecompileCalls::Ripemd160, Self::PrecompileIdentity => PrecompileCalls::Identity, diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 623129c031..d8fcecd60b 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -37,8 +37,18 @@ impl PrecompileGadget { ) -> Self { let address = BinaryNumberGadget::construct(cb, callee_address.expr()); - cb.condition(address.value_equals(PrecompileCalls::ECRecover), |cb| { - cb.constrain_next_step(ExecutionState::PrecompileEcRecover, None, |_cb| {}); + cb.condition(address.value_equals(PrecompileCalls::Ecrecover), |cb| { + cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { + let (msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + cb.query_cell_phase2(), + cb.query_cell(), + cb.query_cell_phase2(), + cb.query_cell_phase2(), + cb.query_cell(), + ); + // TODO: compare input_bytes_rlc to (msg_hash, sig_v, sig_r, sig_s) + // TODO: compare output_bytes_rlc to recovered_addr + }); }); cb.condition(address.value_equals(PrecompileCalls::Sha256), |cb| { diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs index 38b55836dd..a1c149c9da 100644 --- a/zkevm-circuits/src/witness/step.rs +++ b/zkevm-circuits/src/witness/step.rs @@ -216,7 +216,7 @@ impl From<&circuit_input_builder::ExecStep> for ExecutionState { } } circuit_input_builder::ExecState::Precompile(precompile) => match precompile { - PrecompileCalls::ECRecover => ExecutionState::PrecompileEcRecover, + PrecompileCalls::Ecrecover => ExecutionState::PrecompileEcrecover, PrecompileCalls::Sha256 => ExecutionState::PrecompileSha256, PrecompileCalls::Ripemd160 => ExecutionState::PrecompileRipemd160, PrecompileCalls::Identity => ExecutionState::PrecompileIdentity, From f5259504c30dc47c11035cc7005e550726c89793 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Thu, 8 Jun 2023 18:35:37 +0100 Subject: [PATCH 02/57] wip --- .../src/circuit_input_builder/execution.rs | 11 ++- bus-mapping/src/evm/opcodes/callop.rs | 74 +++++++------- .../src/evm/opcodes/precompiles/mod.rs | 20 +++- bus-mapping/src/precompile.rs | 45 ++++++++- .../execution/precompiles/ecrecover.rs | 96 +++++++++++++++++-- .../src/evm_circuit/util/precompile_gadget.rs | 26 ++++- zkevm-circuits/src/witness/step.rs | 5 +- 7 files changed, 228 insertions(+), 49 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index c995f1b37b..09bad333a9 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -3,8 +3,11 @@ use std::marker::PhantomData; use crate::{ - circuit_input_builder::CallContext, error::ExecError, exec_trace::OperationRef, - operation::RWCounter, precompile::PrecompileCalls, + circuit_input_builder::CallContext, + error::ExecError, + exec_trace::OperationRef, + operation::RWCounter, + precompile::{PrecompileAuxData, PrecompileCalls}, }; use eth_types::{ evm_types::{Gas, GasCost, OpcodeId, ProgramCounter}, @@ -51,6 +54,8 @@ pub struct ExecStep { pub copy_rw_counter_delta: u64, /// Error generated by this step pub error: Option, + /// Optional auxiliary data that is attached to precompile call internal states. + pub aux_data: Option, } impl ExecStep { @@ -78,6 +83,7 @@ impl ExecStep { bus_mapping_instance: Vec::new(), copy_rw_counter_delta: 0, error: None, + aux_data: None, } } @@ -113,6 +119,7 @@ impl Default for ExecStep { bus_mapping_instance: Vec::new(), copy_rw_counter_delta: 0, error: None, + aux_data: None, } } } diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index 67c27a0f01..ced49a14e8 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -348,7 +348,7 @@ impl Opcode for CallOpcode { // insert a copy event (input) for this step let rw_counter_start = state.block_ctx.rwc; - if call.call_data_length > 0 { + let input_bytes = if call.call_data_length > 0 { let bytes: Vec<(u8, bool)> = caller_memory .iter() .skip(call.call_data_offset as usize) @@ -379,43 +379,51 @@ impl Opcode for CallOpcode { dst_addr: 0, log_id: None, rw_counter_start, - bytes, + bytes: bytes.clone(), }, ); - } + Some(bytes.iter().map(|t| t.0).collect()) + } else { + None + }; // write the result in the callee's memory. let rw_counter_start = state.block_ctx.rwc; - if call.is_success() && call.call_data_length > 0 && !result.is_empty() { - let bytes: Vec<(u8, bool)> = result.iter().map(|b| (*b, false)).collect(); - for (i, &(byte, _is_code)) in bytes.iter().enumerate() { - // push callee memory write - state.push_op( + let output_bytes = + if call.is_success() && call.call_data_length > 0 && !result.is_empty() { + let bytes: Vec<(u8, bool)> = result.iter().map(|b| (*b, false)).collect(); + for (i, &(byte, _is_code)) in bytes.iter().enumerate() { + // push callee memory write + state.push_op( + &mut exec_step, + RW::WRITE, + MemoryOp::new(call.call_id, i.into(), byte), + ); + } + state.push_copy( &mut exec_step, - RW::WRITE, - MemoryOp::new(call.call_id, i.into(), byte), + CopyEvent { + src_id: NumberOrHash::Number(call.call_id), + src_type: CopyDataType::Precompile(precompile_call), + src_addr: 0, + src_addr_end: result.len() as u64, + dst_id: NumberOrHash::Number(call.call_id), + dst_type: CopyDataType::Memory, + dst_addr: 0, + log_id: None, + rw_counter_start, + bytes: bytes.clone(), + }, ); - } - state.push_copy( - &mut exec_step, - CopyEvent { - src_id: NumberOrHash::Number(call.call_id), - src_type: CopyDataType::Precompile(precompile_call), - src_addr: 0, - src_addr_end: result.len() as u64, - dst_id: NumberOrHash::Number(call.call_id), - dst_type: CopyDataType::Memory, - dst_addr: 0, - log_id: None, - rw_counter_start, - bytes, - }, - ); - } + Some(bytes.iter().map(|t| t.0).collect()) + } else { + None + }; // insert another copy event (output) for this step. let rw_counter_start = state.block_ctx.rwc; - if call.is_success() && call.call_data_length > 0 && length > 0 { + let returned_bytes = if call.is_success() && call.call_data_length > 0 && length > 0 + { let bytes: Vec<(u8, bool)> = result.iter().take(length).map(|b| (*b, false)).collect(); for (i, &(byte, _is_code)) in bytes.iter().enumerate() { @@ -442,18 +450,20 @@ impl Opcode for CallOpcode { dst_addr: call.return_data_offset, log_id: None, rw_counter_start, - bytes, + bytes: bytes.clone(), }, ); - } + Some(bytes.iter().map(|t| t.0).collect()) + } else { + None + }; - // TODO: when more precompiles are supported and each have their own different - // behaviour, we can separate out the logic specified here. let mut precompile_step = precompile_associated_ops( state, geth_steps[1].clone(), call.clone(), precompile_call, + (input_bytes, output_bytes, returned_bytes), )?; // Make the Precompile execution step to handle return logic and restore to caller diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index 2e34492a70..0792d515f1 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -3,15 +3,18 @@ use eth_types::{GethExecStep, ToWord, Word}; use crate::{ circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, operation::CallContextField, - precompile::PrecompileCalls, + precompile::{EcrecoverAuxData, PrecompileAuxData, PrecompileCalls}, Error, }; +type InOutRetData = (Option>, Option>, Option>); + pub fn gen_associated_ops( state: &mut CircuitInputStateRef, geth_step: GethExecStep, call: Call, precompile: PrecompileCalls, + (input_bytes, output_bytes, returned_bytes): InOutRetData, ) -> Result { assert_eq!(call.code_address(), Some(precompile.into())); let mut exec_step = state.new_step(&geth_step)?; @@ -19,6 +22,21 @@ pub fn gen_associated_ops( common_call_ctx_reads(state, &mut exec_step, &call); + // TODO: refactor and replace with `match` once we have more branches. + if precompile == PrecompileCalls::Ecrecover { + let input_bytes = input_bytes.map_or(vec![0u8; 128], |mut bytes| { + bytes.resize(128, 0u8); + bytes + }); + let output_bytes = output_bytes.map_or(vec![0u8; 32], |mut bytes| { + bytes.resize(32, 0u8); + bytes + }); + let aux_data = EcrecoverAuxData::new(input_bytes, output_bytes); + + exec_step.aux_data = Some(PrecompileAuxData::Ecrecover(aux_data)); + } + Ok(exec_step) } diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index f71791d80c..bdc932a8ca 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -1,6 +1,6 @@ //! precompile helpers -use eth_types::{evm_types::GasCost, Address}; +use eth_types::{evm_types::GasCost, Address, Word}; use revm_precompile::{Precompile, Precompiles}; use strum::EnumIter; @@ -110,3 +110,46 @@ impl PrecompileCalls { (*self).into() } } + +/// Auxiliary data for Ecrecover +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct EcrecoverAuxData { + /// Keccak hash of the message being signed. + pub msg_hash: Word, + /// v-component of signature. + pub sig_v: Word, + /// r-component of signature. + pub sig_r: Word, + /// s-component of signature. + pub sig_s: Word, + /// Address that was recovered, right aligned to 32-bytes. + pub recovered_addr: Word, +} + +impl EcrecoverAuxData { + /// Create a new instance of ecrecover auxiliary data. + pub fn new(input: Vec, output: Vec) -> Self { + assert_eq!(input.len(), 128); + assert_eq!(output.len(), 32); + Self { + msg_hash: Word::from_big_endian(&input[0x00..0x20]), + sig_v: Word::from_big_endian(&input[0x20..0x40]), + sig_r: Word::from_big_endian(&input[0x40..0x60]), + sig_s: Word::from_big_endian(&input[0x60..0x80]), + recovered_addr: Word::from_big_endian(&output[0x00..0x20]), + } + } +} + +/// Auxiliary data attached to an internal state for precompile verification. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PrecompileAuxData { + /// Ecrecover. + Ecrecover(EcrecoverAuxData), +} + +impl Default for PrecompileAuxData { + fn default() -> Self { + Self::Ecrecover(EcrecoverAuxData::default()) + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index 82aa29dcad..54312608c1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -1,4 +1,5 @@ -use eth_types::{Field, ToScalar}; +use bus_mapping::precompile::PrecompileAuxData; +use eth_types::{Field, ToLittleEndian, ToScalar}; use gadgets::util::Expr; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -7,7 +8,7 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{ - common_gadget::RestoreContextGadget, constraint_builder::EVMConstraintBuilder, + common_gadget::RestoreContextGadget, constraint_builder::EVMConstraintBuilder, rlc, CachedRegion, Cell, }, }, @@ -17,6 +18,7 @@ use crate::{ #[derive(Clone, Debug)] pub struct EcrecoverGadget { + recovered: Cell, msg_hash: Cell, sig_v: Cell, sig_r: Cell, @@ -39,7 +41,8 @@ impl ExecutionGadget for EcrecoverGadget { const NAME: &'static str = "ECRECOVER"; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let (msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + let (recovered, msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + cb.query_bool(), cb.query_cell_phase2(), cb.query_cell(), cb.query_cell_phase2(), @@ -49,6 +52,7 @@ impl ExecutionGadget for EcrecoverGadget { // TODO: lookup to the sign_verify table: https://github.com/scroll-tech/zkevm-circuits/issues/527 // || v | r | s | msg_hash | recovered_addr || + cb.condition(recovered.expr(), |_cb| {}); let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] = [ @@ -79,10 +83,11 @@ impl ExecutionGadget for EcrecoverGadget { ); Self { + recovered, + msg_hash, sig_v, sig_r, sig_s, - msg_hash, recovered_addr, is_success, callee_address, @@ -104,7 +109,18 @@ impl ExecutionGadget for EcrecoverGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - // TODO: assignment to the signature, msg hash and recovered address cells. + if let Some(PrecompileAuxData::Ecrecover(aux_data)) = &step.aux_data { + let recovered = !aux_data.recovered_addr.is_zero(); + self.msg_hash.assign( + region, + offset, + region + .challenges() + .keccak_input() + .map(|r| rlc::value(&aux_data.msg_hash.to_le_bytes(), r)), + )?; + // TODO: others + } self.is_success.assign( region, @@ -159,7 +175,68 @@ mod test { static ref TEST_VECTOR: Vec = { vec![ PrecompileCallArgs { - name: "ecrecover", + name: "ecrecover (invalid sig, addr not recovered)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + // copy 96 bytes from memory addr 0. This is insufficient to recover an + // address, and so the return data length from the precompile call will be 0. + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (invalid sig, addr recovered)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + // copy 101 bytes from memory addr 0. This should be sufficient to recover an + // address, but the signature is invalid (ecrecover does not care about this + // though) + call_data_offset: 0x00.into(), + call_data_length: 0x65.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (valid sig, addr recovered)", setup_code: bytecode! { // msg hash from 0x00 PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) @@ -178,10 +255,11 @@ mod test { PUSH1(0x60) MSTORE }, - // copy 96 bytes from memory addr 0 + // copy 128 bytes from memory addr 0. Address is recovered and the signature is + // valid. call_data_offset: 0x00.into(), - call_data_length: 0x20.into(), - // return 32 bytes and write from memory addr 96 + call_data_length: 0x80.into(), + // return 32 bytes and write from memory addr 128 ret_offset: 0x80.into(), ret_size: 0x20.into(), address: PrecompileCalls::Ecrecover.address().to_word(), diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index d8fcecd60b..ae6f5c1577 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -39,15 +39,35 @@ impl PrecompileGadget { cb.condition(address.value_equals(PrecompileCalls::Ecrecover), |cb| { cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { - let (msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + let (_recovered, msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + cb.query_bool(), cb.query_cell_phase2(), cb.query_cell(), cb.query_cell_phase2(), cb.query_cell_phase2(), cb.query_cell(), ); - // TODO: compare input_bytes_rlc to (msg_hash, sig_v, sig_r, sig_s) - // TODO: compare output_bytes_rlc to recovered_addr + let (r_pow_32, r_pow_64, r_pow_96) = { + let challenges = cb.challenges().keccak_powers_of_randomness::<16>(); + let r_pow_16 = challenges[15].clone(); + let r_pow_32 = r_pow_16.square(); + let r_pow_64 = r_pow_32.clone().square(); + let r_pow_96 = r_pow_64.clone() * r_pow_32.clone(); + (r_pow_32, r_pow_64, r_pow_96) + }; + cb.require_equal( + "input bytes (RLC) = [msg_hash | sig_v | sig_r | sig_s]", + input_bytes_rlc.expr(), + (msg_hash.expr() * r_pow_96) + + (sig_v.expr() * r_pow_64) + + (sig_r.expr() * r_pow_32) + + sig_s.expr(), + ); + cb.require_equal( + "output bytes (RLC) = recovered address", + output_bytes_rlc.expr(), + recovered_addr.expr(), + ); }); }); diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs index a1c149c9da..3c19159f0b 100644 --- a/zkevm-circuits/src/witness/step.rs +++ b/zkevm-circuits/src/witness/step.rs @@ -6,7 +6,7 @@ use bus_mapping::{ }, evm::OpcodeId, operation, - precompile::PrecompileCalls, + precompile::{PrecompileAuxData, PrecompileCalls}, }; use crate::{ @@ -50,6 +50,8 @@ pub struct ExecStep { pub opcode: Option, /// The block number in which this step exists. pub block_num: u64, + /// + pub aux_data: Option, } impl ExecStep { @@ -274,5 +276,6 @@ pub(super) fn step_convert(step: &circuit_input_builder::ExecStep, block_num: u6 reversible_write_counter_delta: step.reversible_write_counter_delta, log_id: step.log_id, block_num, + aux_data: step.aux_data, } } From 8bdaaf54ae7e7904d82a8069d73f7c07a6f2b1b4 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Fri, 9 Jun 2023 10:33:04 +0100 Subject: [PATCH 03/57] finish assignment to ecrecover --- .../src/evm/opcodes/precompiles/mod.rs | 2 +- bus-mapping/src/precompile.rs | 19 +++++-- .../execution/precompiles/ecrecover.rs | 50 ++++++++++++++++--- .../src/evm_circuit/util/precompile_gadget.rs | 6 +-- zkevm-circuits/src/witness/step.rs | 2 +- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index 0792d515f1..ce2d135cdf 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -14,7 +14,7 @@ pub fn gen_associated_ops( geth_step: GethExecStep, call: Call, precompile: PrecompileCalls, - (input_bytes, output_bytes, returned_bytes): InOutRetData, + (input_bytes, output_bytes, _returned_bytes): InOutRetData, ) -> Result { assert_eq!(call.code_address(), Some(precompile.into())); let mut exec_step = state.new_step(&geth_step)?; diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index bdc932a8ca..0c5adf4f16 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -117,13 +117,13 @@ pub struct EcrecoverAuxData { /// Keccak hash of the message being signed. pub msg_hash: Word, /// v-component of signature. - pub sig_v: Word, + pub sig_v: u8, /// r-component of signature. pub sig_r: Word, /// s-component of signature. pub sig_s: Word, - /// Address that was recovered, right aligned to 32-bytes. - pub recovered_addr: Word, + /// Address that was recovered. + pub recovered_addr: Address, } impl EcrecoverAuxData { @@ -131,12 +131,21 @@ impl EcrecoverAuxData { pub fn new(input: Vec, output: Vec) -> Self { assert_eq!(input.len(), 128); assert_eq!(output.len(), 32); + + // assert that sig v is a byte, which indirectly means the other 31 bytes are 0. + assert!(input[0x20..0x3f].iter().all(|&b| b == 0)); + let sig_v = input[0x3f]; + + // assert that recovered address is 20 bytes. + assert!(output[0x00..0x0c].iter().all(|&b| b == 0)); + let recovered_addr = Address::from_slice(&output[0x0c..0x20]); + Self { msg_hash: Word::from_big_endian(&input[0x00..0x20]), - sig_v: Word::from_big_endian(&input[0x20..0x40]), + sig_v, sig_r: Word::from_big_endian(&input[0x40..0x60]), sig_s: Word::from_big_endian(&input[0x60..0x80]), - recovered_addr: Word::from_big_endian(&output[0x00..0x20]), + recovered_addr, } } } diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index 54312608c1..c5638fc360 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -6,10 +6,11 @@ use halo2_proofs::{circuit::Value, plonk::Error}; use crate::{ evm_circuit::{ execution::ExecutionGadget, + param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState, util::{ common_gadget::RestoreContextGadget, constraint_builder::EVMConstraintBuilder, rlc, - CachedRegion, Cell, + CachedRegion, Cell, RandomLinearCombination, }, }, table::CallContextFieldTag, @@ -23,7 +24,7 @@ pub struct EcrecoverGadget { sig_v: Cell, sig_r: Cell, sig_s: Cell, - recovered_addr: Cell, + recovered_addr: RandomLinearCombination, is_success: Cell, callee_address: Cell, @@ -44,15 +45,16 @@ impl ExecutionGadget for EcrecoverGadget { let (recovered, msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( cb.query_bool(), cb.query_cell_phase2(), - cb.query_cell(), + cb.query_byte(), cb.query_cell_phase2(), cb.query_cell_phase2(), - cb.query_cell(), + cb.query_keccak_rlc(), ); - // TODO: lookup to the sign_verify table: https://github.com/scroll-tech/zkevm-circuits/issues/527 - // || v | r | s | msg_hash | recovered_addr || - cb.condition(recovered.expr(), |_cb| {}); + cb.condition(recovered.expr(), |_cb| { + // TODO: lookup to the sign_verify table: https://github.com/scroll-tech/zkevm-circuits/issues/527 + // || v | r | s | msg_hash | recovered_addr || + }); let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] = [ @@ -111,6 +113,8 @@ impl ExecutionGadget for EcrecoverGadget { ) -> Result<(), Error> { if let Some(PrecompileAuxData::Ecrecover(aux_data)) = &step.aux_data { let recovered = !aux_data.recovered_addr.is_zero(); + self.recovered + .assign(region, offset, Value::known(F::from(recovered as u64)))?; self.msg_hash.assign( region, offset, @@ -119,7 +123,36 @@ impl ExecutionGadget for EcrecoverGadget { .keccak_input() .map(|r| rlc::value(&aux_data.msg_hash.to_le_bytes(), r)), )?; - // TODO: others + self.sig_v.assign( + region, + offset, + Value::known(F::from(u64::from(aux_data.sig_v))), + )?; + self.sig_r.assign( + region, + offset, + region + .challenges() + .keccak_input() + .map(|r| rlc::value(&aux_data.sig_r.to_le_bytes(), r)), + )?; + self.sig_s.assign( + region, + offset, + region + .challenges() + .keccak_input() + .map(|r| rlc::value(&aux_data.sig_s.to_le_bytes(), r)), + )?; + self.recovered_addr.assign( + region, + offset, + Some({ + let mut recovered_addr = aux_data.recovered_addr.to_fixed_bytes(); + recovered_addr.reverse(); + recovered_addr + }), + )?; } self.is_success.assign( @@ -159,6 +192,7 @@ impl ExecutionGadget for EcrecoverGadget { .assign(region, offset, block, call, step, 7) } } + #[cfg(test)] mod test { use bus_mapping::{ diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index ae6f5c1577..a9aa62e5c8 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -3,7 +3,7 @@ use eth_types::Field; use gadgets::util::Expr; use halo2_proofs::plonk::Expression; -use crate::evm_circuit::step::ExecutionState; +use crate::evm_circuit::{param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState}; use super::{ constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, @@ -42,10 +42,10 @@ impl PrecompileGadget { let (_recovered, msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( cb.query_bool(), cb.query_cell_phase2(), - cb.query_cell(), + cb.query_byte(), cb.query_cell_phase2(), cb.query_cell_phase2(), - cb.query_cell(), + cb.query_keccak_rlc::(), ); let (r_pow_32, r_pow_64, r_pow_96) = { let challenges = cb.challenges().keccak_powers_of_randomness::<16>(); diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs index 3c19159f0b..7f271c7451 100644 --- a/zkevm-circuits/src/witness/step.rs +++ b/zkevm-circuits/src/witness/step.rs @@ -276,6 +276,6 @@ pub(super) fn step_convert(step: &circuit_input_builder::ExecStep, block_num: u6 reversible_write_counter_delta: step.reversible_write_counter_delta, log_id: step.log_id, block_num, - aux_data: step.aux_data, + aux_data: step.aux_data.clone(), } } From 11764fae1244223766aa2a6a736126b80ce37ec6 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Fri, 9 Jun 2023 12:52:20 +0100 Subject: [PATCH 04/57] fix: input length may be different than call data length (for precompiles) --- bus-mapping/src/evm/opcodes/callop.rs | 12 +- bus-mapping/src/precompile.rs | 10 ++ .../src/evm_circuit/execution/callop.rs | 147 ++++++++++-------- .../execution/precompiles/ecrecover.rs | 31 ++++ .../src/evm_circuit/util/precompile_gadget.rs | 4 +- 5 files changed, 135 insertions(+), 69 deletions(-) diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index ced49a14e8..88b347b7eb 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -348,13 +348,19 @@ impl Opcode for CallOpcode { // insert a copy event (input) for this step let rw_counter_start = state.block_ctx.rwc; + let n_input_bytes = if let Some(input_len) = precompile_call.input_len() { + input_len + } else { + call.call_data_length as usize + }; let input_bytes = if call.call_data_length > 0 { - let bytes: Vec<(u8, bool)> = caller_memory + let mut bytes: Vec<(u8, bool)> = caller_memory .iter() .skip(call.call_data_offset as usize) - .take(call.call_data_length as usize) + .take(n_input_bytes) .map(|b| (*b, false)) .collect(); + bytes.resize(n_input_bytes, (0u8, false)); for (i, &(byte, _is_code)) in bytes.iter().enumerate() { // push caller memory read state.push_op( @@ -373,7 +379,7 @@ impl Opcode for CallOpcode { src_id: NumberOrHash::Number(call.caller_id), src_type: CopyDataType::Memory, src_addr: call.call_data_offset, - src_addr_end: call.call_data_offset + call.call_data_length, + src_addr_end: call.call_data_offset + n_input_bytes as u64, dst_id: NumberOrHash::Number(call.call_id), dst_type: CopyDataType::Precompile(precompile_call), dst_addr: 0, diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 0c5adf4f16..2f6436202a 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -109,6 +109,16 @@ impl PrecompileCalls { pub fn address(&self) -> u64 { (*self).into() } + + /// Maximum length of input bytes considered for the precompile call. + pub fn input_len(&self) -> Option { + match self { + Self::Ecrecover | Self::Bn128Add => Some(128), + Self::Bn128Mul => Some(96), + Self::Blake2F => Some(213), + _ => None, + } + } } /// Auxiliary data for Ecrecover diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index f731a92524..5fa12f78cc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -23,7 +23,11 @@ use crate::{ table::{AccountFieldTag, CallContextFieldTag}, util::Expr, }; -use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId, precompile::is_precompiled}; +use bus_mapping::{ + circuit_input_builder::CopyDataType, + evm::OpcodeId, + precompile::{is_precompiled, PrecompileCalls}, +}; use eth_types::{ evm_types::GAS_STIPEND_CALL_WITH_VALUE, Field, ToAddress, ToLittleEndian, ToScalar, U256, }; @@ -68,7 +72,8 @@ pub(crate) struct CallOpGadget { precompile_return_length: Cell, precompile_return_length_zero: IsZeroGadget, return_data_copy_size: MinMaxGadget, - input_bytes_rlc: Cell, // input bytes to precompile call. + input_len: Cell, // the number of input bytes taken for the precompile call. + input_bytes_rlc: Cell, // input bytes to precompile call. output_bytes_rlc: Cell, // output bytes from precompile call. return_bytes_rlc: Cell, // bytes returned to caller from precompile call. } @@ -208,6 +213,7 @@ impl ExecutionGadget for CallOpGadget { precompile_return_length.expr(), call_gadget.rd_address.length(), ); + let input_len = cb.query_cell(); let precompile_memory_rws = select::expr( call_gadget.cd_address.has_length(), select::expr( @@ -216,10 +222,8 @@ impl ExecutionGadget for CallOpGadget { call_gadget.rd_address.has_length(), not::expr(precompile_return_length_zero.expr()), ]), - call_gadget.cd_address.length() - + precompile_return_length.expr() - + return_data_copy_size.min(), - call_gadget.cd_address.length(), + input_len.expr() + precompile_return_length.expr() + return_data_copy_size.min(), + input_len.expr(), ), 0.expr(), ); @@ -375,11 +379,11 @@ impl ExecutionGadget for CallOpGadget { callee_call_id.expr(), 5.expr() + call_gadget.callee_address_expr(), // refer u64::from(CopyDataType) call_gadget.cd_address.offset(), - call_gadget.cd_address.address(), + call_gadget.cd_address.offset() + input_len.expr(), 0.expr(), - call_gadget.cd_address.length(), + input_len.expr(), input_bytes_rlc.expr(), - call_gadget.cd_address.length(), // reads + input_len.expr(), // reads ); // rwc_delta += `call_gadget.cd_address.length()` for precompile input_bytes_rlc }); @@ -718,6 +722,7 @@ impl ExecutionGadget for CallOpGadget { precompile_return_length, precompile_return_length_zero, return_data_copy_size, + input_len, input_bytes_rlc, output_bytes_rlc, return_bytes_rlc, @@ -934,19 +939,21 @@ impl ExecutionGadget for CallOpGadget { )?; // precompile related assignment. + let (is_precompile_call, precompile_addr) = { + let precompile_addr = callee_address.to_address(); + 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())?; - if is_precompiled(&callee_address.to_address()) { - self.precompile_gadget.assign( - region, - offset, - callee_address.to_address().0[19].into(), - )?; + if is_precompile_call { + self.precompile_gadget + .assign(region, offset, precompile_addr.0[19].into())?; } - let precompile_return_length = if is_precompiled(&callee_address.to_address()) { + let precompile_return_length = if is_precompile_call { let value_rw = block.rws[step.rw_indices[32 + rw_offset]]; assert_eq!( value_rw.field_tag(), @@ -973,57 +980,69 @@ impl ExecutionGadget for CallOpGadget { rd_length.to_scalar().unwrap(), )?; - let (input_bytes_rlc, output_bytes_rlc, return_bytes_rlc) = - if is_precompiled(&callee_address.to_address()) { - let input_length = cd_length.as_usize(); - let (input_bytes_start, input_bytes_end) = - (33usize + rw_offset, 33usize + rw_offset + input_length); - let [output_bytes_start, output_bytes_end, return_bytes_start, return_bytes_end] = - if input_length > 0 { - let (output_start, output_end) = ( - input_bytes_end, - input_bytes_end + precompile_return_length.as_usize(), - ); - [ - output_start, - output_end, - output_end, - output_end + rd_length.min(precompile_return_length).as_usize(), - ] - } else { - [input_bytes_end; 4] - }; - let input_bytes: Vec = (input_bytes_start..input_bytes_end) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) - .collect(); - let output_bytes: Vec = (output_bytes_start..output_bytes_end) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) - .collect(); - let return_bytes: Vec = (return_bytes_start..return_bytes_end) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) - .collect(); - - let input_bytes_rlc = region - .challenges() - .keccak_input() - .map(|randomness| rlc::value(input_bytes.iter().rev(), randomness)); - let output_bytes_rlc = region - .challenges() - .keccak_input() - .map(|randomness| rlc::value(output_bytes.iter().rev(), randomness)); - let return_bytes_rlc = region - .challenges() - .keccak_input() - .map(|randomness| rlc::value(return_bytes.iter().rev(), randomness)); - (input_bytes_rlc, output_bytes_rlc, return_bytes_rlc) + let (input_len, input_bytes_rlc, output_bytes_rlc, return_bytes_rlc) = if is_precompile_call + { + let precompile_call: PrecompileCalls = precompile_addr.0[19].into(); + let input_len = if let Some(input_len) = precompile_call.input_len() { + input_len } else { - ( - Value::known(F::zero()), - Value::known(F::zero()), - Value::known(F::zero()), - ) + cd_length.as_usize() }; + let (input_bytes_start, input_bytes_end) = + (33usize + rw_offset, 33usize + rw_offset + input_len); + let [output_bytes_start, output_bytes_end, return_bytes_start, return_bytes_end] = + if input_len > 0 { + let (output_start, output_end) = ( + input_bytes_end, + input_bytes_end + precompile_return_length.as_usize(), + ); + [ + output_start, + output_end, + output_end, + output_end + rd_length.min(precompile_return_length).as_usize(), + ] + } else { + [input_bytes_end; 4] + }; + let input_bytes: Vec = (input_bytes_start..input_bytes_end) + .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .collect(); + let output_bytes: Vec = (output_bytes_start..output_bytes_end) + .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .collect(); + let return_bytes: Vec = (return_bytes_start..return_bytes_end) + .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .collect(); + + let input_bytes_rlc = region + .challenges() + .keccak_input() + .map(|randomness| rlc::value(input_bytes.iter().rev(), randomness)); + let output_bytes_rlc = region + .challenges() + .keccak_input() + .map(|randomness| rlc::value(output_bytes.iter().rev(), randomness)); + let return_bytes_rlc = region + .challenges() + .keccak_input() + .map(|randomness| rlc::value(return_bytes.iter().rev(), randomness)); + ( + Value::known(F::from(input_len as u64)), + input_bytes_rlc, + output_bytes_rlc, + return_bytes_rlc, + ) + } else { + ( + Value::known(F::zero()), + Value::known(F::zero()), + Value::known(F::zero()), + Value::known(F::zero()), + ) + }; + self.input_len.assign(region, offset, input_len)?; self.input_bytes_rlc .assign(region, offset, input_bytes_rlc)?; self.output_bytes_rlc diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index c5638fc360..68c67e91d7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -112,6 +112,7 @@ impl ExecutionGadget for EcrecoverGadget { step: &ExecStep, ) -> Result<(), Error> { if let Some(PrecompileAuxData::Ecrecover(aux_data)) = &step.aux_data { + log::info!("aux data = {:?}", aux_data.clone()); let recovered = !aux_data.recovered_addr.is_zero(); self.recovered .assign(region, offset, Value::known(F::from(recovered as u64)))?; @@ -299,6 +300,36 @@ mod test { address: PrecompileCalls::Ecrecover.address().to_word(), ..Default::default() }, + PrecompileCallArgs { + name: "ecrecover (valid sig, addr recovered, extra input bytes)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + // copy 133 bytes from memory addr 0. Address is recovered and the signature is + // valid. The 5 bytes after the first 128 bytes are ignored. + call_data_offset: 0x00.into(), + call_data_length: 0x85.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, ] }; } diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index a9aa62e5c8..5a03a8fbc0 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -51,8 +51,8 @@ impl PrecompileGadget { let challenges = cb.challenges().keccak_powers_of_randomness::<16>(); let r_pow_16 = challenges[15].clone(); let r_pow_32 = r_pow_16.square(); - let r_pow_64 = r_pow_32.clone().square(); - let r_pow_96 = r_pow_64.clone() * r_pow_32.clone(); + let r_pow_64 = r_pow_32.expr().square(); + let r_pow_96 = r_pow_64.expr() * r_pow_32.expr(); (r_pow_32, r_pow_64, r_pow_96) }; cb.require_equal( From 2176c44744bab76288a59cae155741583498f841 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Mon, 26 Jun 2023 11:06:45 +0100 Subject: [PATCH 05/57] fix: input bytes to ecrecover --- .../circuit_input_builder/input_state_ref.rs | 6 ++++ bus-mapping/src/evm/opcodes/callop.rs | 5 ++- .../src/evm/opcodes/precompiles/mod.rs | 26 +++++++++++++- bus-mapping/src/precompile.rs | 2 +- eth-types/src/sign_types.rs | 6 ++-- .../src/evm_circuit/execution/callop.rs | 2 +- .../execution/precompiles/ecrecover.rs | 36 +++++++++++-------- .../evm_circuit/util/constraint_builder.rs | 12 +++---- .../src/evm_circuit/util/precompile_gadget.rs | 2 +- zkevm-circuits/src/table.rs | 32 ++++++++++------- 10 files changed, 86 insertions(+), 43 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index 1ea8cc9e53..3f137727b3 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -26,6 +26,7 @@ use eth_types::{ evm_types::{ gas_utils::memory_expansion_gas_cost, Gas, GasCost, MemoryAddress, OpcodeId, StackAddress, }, + sign_types::SignData, Address, Bytecode, GethExecStep, ToAddress, ToBigEndian, ToWord, Word, H256, U256, }; use ethers_core::utils::{get_contract_address, get_create2_address, keccak256}; @@ -1291,6 +1292,11 @@ impl<'a> CircuitInputStateRef<'a> { self.block.add_exp_event(event) } + /// Push an ecrecover event to the state. + pub fn push_ecrecover(&mut self, event: SignData) { + self.block.add_ecrecover_event(event) + } + pub(crate) fn get_step_err( &self, step: &GethExecStep, diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index 88b347b7eb..3a456bbf74 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -349,18 +349,17 @@ impl Opcode for CallOpcode { // insert a copy event (input) for this step let rw_counter_start = state.block_ctx.rwc; let n_input_bytes = if let Some(input_len) = precompile_call.input_len() { - input_len + std::cmp::min(input_len, call.call_data_length as usize) } else { call.call_data_length as usize }; let input_bytes = if call.call_data_length > 0 { - let mut bytes: Vec<(u8, bool)> = caller_memory + let bytes: Vec<(u8, bool)> = caller_memory .iter() .skip(call.call_data_offset as usize) .take(n_input_bytes) .map(|b| (*b, false)) .collect(); - bytes.resize(n_input_bytes, (0u8, false)); for (i, &(byte, _is_code)) in bytes.iter().enumerate() { // push caller memory read state.push_op( diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index ce2d135cdf..d1fd9a2bed 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -1,4 +1,8 @@ -use eth_types::{GethExecStep, ToWord, Word}; +use eth_types::{ + sign_types::{recover_pk, SignData}, + Bytes, GethExecStep, ToBigEndian, ToWord, Word, +}; +use halo2_proofs::halo2curves::secp256k1::Fq; use crate::{ circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, @@ -34,6 +38,26 @@ pub fn gen_associated_ops( }); let aux_data = EcrecoverAuxData::new(input_bytes, output_bytes); + if let Ok(recovered_pk) = recover_pk( + aux_data.sig_v, + &aux_data.sig_r, + &aux_data.sig_s, + &aux_data.msg_hash.to_be_bytes(), + ) { + let sign_data = SignData { + signature: ( + Fq::from_bytes(&aux_data.sig_r.to_be_bytes()).unwrap(), + Fq::from_bytes(&aux_data.sig_s.to_be_bytes()).unwrap(), + aux_data.sig_v, + ), + pk: recovered_pk, + msg: Bytes::default(), + msg_hash: Fq::from_bytes(&aux_data.msg_hash.to_be_bytes()).unwrap(), + }; + assert_eq!(aux_data.recovered_addr, sign_data.get_addr()); + state.push_ecrecover(sign_data); + } + exec_step.aux_data = Some(PrecompileAuxData::Ecrecover(aux_data)); } diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 2f6436202a..5f256f7c05 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -144,7 +144,7 @@ impl EcrecoverAuxData { // assert that sig v is a byte, which indirectly means the other 31 bytes are 0. assert!(input[0x20..0x3f].iter().all(|&b| b == 0)); - let sig_v = input[0x3f]; + let sig_v = input[0x3f] - 27; // assert that recovered address is 20 bytes. assert!(output[0x00..0x0c].iter().all(|&b| b == 0)); diff --git a/eth-types/src/sign_types.rs b/eth-types/src/sign_types.rs index 0545418232..09a0518adf 100644 --- a/eth-types/src/sign_types.rs +++ b/eth-types/src/sign_types.rs @@ -1,6 +1,6 @@ //! secp256k1 signature types and helper functions. -use crate::{ToBigEndian, ToWord, Word}; +use crate::{ToBigEndian, Word}; use ethers_core::{ types::{Address, Bytes}, utils::keccak256, @@ -63,13 +63,13 @@ pub struct SignData { impl SignData { /// Recover address of the signature - pub fn get_addr(&self) -> Word { + pub fn get_addr(&self) -> Address { let pk_le = pk_bytes_le(&self.pk); let pk_be = pk_bytes_swap_endianness(&pk_le); let pk_hash = keccak256(pk_be); let mut addr_bytes = [0u8; 20]; addr_bytes.copy_from_slice(&pk_hash[12..]); - Address::from(addr_bytes).to_word() + Address::from_slice(&addr_bytes) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 5fa12f78cc..f5521b21dd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -984,7 +984,7 @@ impl ExecutionGadget for CallOpGadget { { let precompile_call: PrecompileCalls = precompile_addr.0[19].into(); let input_len = if let Some(input_len) = precompile_call.input_len() { - input_len + std::cmp::min(input_len, cd_length.as_usize()) } else { cd_length.as_usize() }; diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index 68c67e91d7..2fc48e3bbf 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -9,8 +9,8 @@ use crate::{ param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState, util::{ - common_gadget::RestoreContextGadget, constraint_builder::EVMConstraintBuilder, rlc, - CachedRegion, Cell, RandomLinearCombination, + common_gadget::RestoreContextGadget, constraint_builder::EVMConstraintBuilder, + from_bytes, rlc, CachedRegion, Cell, RandomLinearCombination, }, }, table::CallContextFieldTag, @@ -20,10 +20,10 @@ use crate::{ #[derive(Clone, Debug)] pub struct EcrecoverGadget { recovered: Cell, - msg_hash: Cell, + msg_hash_rlc: Cell, sig_v: Cell, - sig_r: Cell, - sig_s: Cell, + sig_r_rlc: Cell, + sig_s_rlc: Cell, recovered_addr: RandomLinearCombination, is_success: Cell, @@ -42,7 +42,7 @@ impl ExecutionGadget for EcrecoverGadget { const NAME: &'static str = "ECRECOVER"; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let (recovered, msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + let (recovered, msg_hash_rlc, sig_v, sig_r_rlc, sig_s_rlc, recovered_addr) = ( cb.query_bool(), cb.query_cell_phase2(), cb.query_byte(), @@ -51,9 +51,16 @@ impl ExecutionGadget for EcrecoverGadget { cb.query_keccak_rlc(), ); - cb.condition(recovered.expr(), |_cb| { - // TODO: lookup to the sign_verify table: https://github.com/scroll-tech/zkevm-circuits/issues/527 + cb.condition(recovered.expr(), |cb| { + // lookup to the sign_verify table // || v | r | s | msg_hash | recovered_addr || + cb.sig_table_lookup( + msg_hash_rlc.expr(), + sig_v.expr(), + sig_r_rlc.expr(), + sig_s_rlc.expr(), + from_bytes::expr(&recovered_addr.cells), + ); }); let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] = @@ -86,10 +93,10 @@ impl ExecutionGadget for EcrecoverGadget { Self { recovered, - msg_hash, + msg_hash_rlc, sig_v, - sig_r, - sig_s, + sig_r_rlc, + sig_s_rlc, recovered_addr, is_success, callee_address, @@ -112,11 +119,10 @@ impl ExecutionGadget for EcrecoverGadget { step: &ExecStep, ) -> Result<(), Error> { if let Some(PrecompileAuxData::Ecrecover(aux_data)) = &step.aux_data { - log::info!("aux data = {:?}", aux_data.clone()); let recovered = !aux_data.recovered_addr.is_zero(); self.recovered .assign(region, offset, Value::known(F::from(recovered as u64)))?; - self.msg_hash.assign( + self.msg_hash_rlc.assign( region, offset, region @@ -129,7 +135,7 @@ impl ExecutionGadget for EcrecoverGadget { offset, Value::known(F::from(u64::from(aux_data.sig_v))), )?; - self.sig_r.assign( + self.sig_r_rlc.assign( region, offset, region @@ -137,7 +143,7 @@ impl ExecutionGadget for EcrecoverGadget { .keccak_input() .map(|r| rlc::value(&aux_data.sig_r.to_le_bytes(), r)), )?; - self.sig_s.assign( + self.sig_s_rlc.assign( region, offset, region diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 7e816d7d48..5707de362c 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1350,13 +1350,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { } // Sig Table - pub(crate) fn sig_table_lookup>( + pub(crate) fn sig_table_lookup( &mut self, - msg_hash_rlc: E, - sig_v: E, - sig_r_rlc: E, - sig_s_rlc: E, - recovered_addr: E, + msg_hash_rlc: Expression, + sig_v: Expression, + sig_r_rlc: Expression, + sig_s_rlc: Expression, + recovered_addr: Expression, ) { self.add_lookup( "sig table", diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 5a03a8fbc0..b637c975f3 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -59,7 +59,7 @@ impl PrecompileGadget { "input bytes (RLC) = [msg_hash | sig_v | sig_r | sig_s]", input_bytes_rlc.expr(), (msg_hash.expr() * r_pow_96) - + (sig_v.expr() * r_pow_64) + + ((sig_v.expr() + 27.expr()) * r_pow_64) + (sig_r.expr() * r_pow_32) + sig_s.expr(), ); diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 7c43ab1c73..b0d6ba27b6 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2162,18 +2162,26 @@ impl SigTable { let signatures: Vec = block.get_sign_data(false); for (offset, sign_data) in signatures.iter().enumerate() { - let addr: F = sign_data.get_addr().to_scalar().unwrap(); - - let msg_hash_rlc = challenges - .keccak_input() - .map(|challenge| rlc::value(&sign_data.msg_hash.to_bytes(), challenge)); - let sig_r_rlc = challenges - .keccak_input() - .map(|challenge| rlc::value(&sign_data.signature.0.to_bytes(), challenge)); - let sig_s_rlc = challenges - .keccak_input() - .map(|challenge| rlc::value(&sign_data.signature.1.to_bytes(), challenge)); + let msg_hash_rlc = challenges.keccak_input().map(|challenge| { + rlc::value( + sign_data.msg_hash.to_bytes().iter().rev().collect_vec(), + challenge, + ) + }); + let sig_r_rlc = challenges.keccak_input().map(|challenge| { + rlc::value( + sign_data.signature.0.to_bytes().iter().rev().collect_vec(), + challenge, + ) + }); + let sig_s_rlc = challenges.keccak_input().map(|challenge| { + rlc::value( + sign_data.signature.1.to_bytes().iter().rev().collect_vec(), + challenge, + ) + }); let sig_v = Value::known(F::from(sign_data.signature.2 as u64)); + let recovered_addr = Value::known(sign_data.get_addr().to_scalar().unwrap()); region.assign_fixed( || format!("sig table q_enable {offset}"), @@ -2186,7 +2194,7 @@ impl SigTable { ("sig_v", self.sig_v, sig_v), ("sig_r_rlc", self.sig_r_rlc, sig_r_rlc), ("sig_s_rlc", self.sig_s_rlc, sig_s_rlc), - ("recovered_addr", self.recovered_addr, Value::known(addr)), + ("recovered_addr", self.recovered_addr, recovered_addr), ("is_valid", self.is_valid, Value::known(F::one())), ] { region.assign_advice( From 6ab87ea7a818cf9fce56dbc86a8d93c9b2d451c6 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Mon, 26 Jun 2023 11:12:32 +0100 Subject: [PATCH 06/57] minor edits --- .../execution/precompiles/ecrecover.rs | 10 +++---- .../src/evm_circuit/util/precompile_gadget.rs | 26 ++++++++++++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index 2fc48e3bbf..31af4c9bb1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -24,7 +24,7 @@ pub struct EcrecoverGadget { sig_v: Cell, sig_r_rlc: Cell, sig_s_rlc: Cell, - recovered_addr: RandomLinearCombination, + recovered_addr_rlc: RandomLinearCombination, is_success: Cell, callee_address: Cell, @@ -42,7 +42,7 @@ impl ExecutionGadget for EcrecoverGadget { const NAME: &'static str = "ECRECOVER"; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let (recovered, msg_hash_rlc, sig_v, sig_r_rlc, sig_s_rlc, recovered_addr) = ( + let (recovered, msg_hash_rlc, sig_v, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( cb.query_bool(), cb.query_cell_phase2(), cb.query_byte(), @@ -59,7 +59,7 @@ impl ExecutionGadget for EcrecoverGadget { sig_v.expr(), sig_r_rlc.expr(), sig_s_rlc.expr(), - from_bytes::expr(&recovered_addr.cells), + from_bytes::expr(&recovered_addr_rlc.cells), ); }); @@ -97,7 +97,7 @@ impl ExecutionGadget for EcrecoverGadget { sig_v, sig_r_rlc, sig_s_rlc, - recovered_addr, + recovered_addr_rlc, is_success, callee_address, caller_id, @@ -151,7 +151,7 @@ impl ExecutionGadget for EcrecoverGadget { .keccak_input() .map(|r| rlc::value(&aux_data.sig_s.to_le_bytes(), r)), )?; - self.recovered_addr.assign( + self.recovered_addr_rlc.assign( region, offset, Some({ diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index b637c975f3..02ddf87874 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -1,6 +1,6 @@ use bus_mapping::precompile::PrecompileCalls; use eth_types::Field; -use gadgets::util::Expr; +use gadgets::util::{not, Expr}; use halo2_proofs::plonk::Expression; use crate::evm_circuit::{param::N_BYTES_ACCOUNT_ADDRESS, step::ExecutionState}; @@ -39,7 +39,7 @@ impl PrecompileGadget { cb.condition(address.value_equals(PrecompileCalls::Ecrecover), |cb| { cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { - let (_recovered, msg_hash, sig_v, sig_r, sig_s, recovered_addr) = ( + let (recovered, msg_hash_rlc, sig_v, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( cb.query_bool(), cb.query_cell_phase2(), cb.query_byte(), @@ -58,16 +58,22 @@ impl PrecompileGadget { cb.require_equal( "input bytes (RLC) = [msg_hash | sig_v | sig_r | sig_s]", input_bytes_rlc.expr(), - (msg_hash.expr() * r_pow_96) + (msg_hash_rlc.expr() * r_pow_96) + ((sig_v.expr() + 27.expr()) * r_pow_64) - + (sig_r.expr() * r_pow_32) - + sig_s.expr(), - ); - cb.require_equal( - "output bytes (RLC) = recovered address", - output_bytes_rlc.expr(), - recovered_addr.expr(), + + (sig_r_rlc.expr() * r_pow_32) + + sig_s_rlc.expr(), ); + cb.condition(recovered.expr(), |cb| { + cb.require_equal( + "output bytes (RLC) = recovered address", + output_bytes_rlc.expr(), + recovered_addr_rlc.expr(), + ); + }); + cb.condition(not::expr(recovered.expr()), |cb| { + cb.require_zero("output bytes == 0", output_bytes_rlc.expr()); + cb.require_zero("recovered addr == 0", recovered_addr_rlc.expr()); + }); }); }); From ade3c99686b8c2ffb35a478cbe84701d9df5eb47 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Tue, 30 May 2023 09:31:07 +0800 Subject: [PATCH 07/57] modexp table --- zkevm-circuits/src/table.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index b0d6ba27b6..6f0026bd7c 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2253,3 +2253,38 @@ impl LookupTable for SigTable { ] } } + +/// Lookup table embedded in the modexp circuit for precompile. +#[derive(Clone, Copy, Debug)] +pub struct ModExpTable { + /// Is enabled + pub q_enable: Column, + /// RLC encoded of the 4 limbs-represent (notice not bytes) of U256 base in modexp circuit + pub base_rlc: Column, + /// RLC encoded of the U256 exponent + pub exp_rlc: Column, + /// RLC encoded of the U256 modulus + pub modulus_rlc: Column, + /// RLC encoded of the U256 results + pub result_rlc: Column, +} + + +impl ModExpTable { + /// Construct the modexp table. + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + q_enable: meta.fixed_column(), + base_rlc: meta.advice_column_in(SecondPhase), + exp_rlc: meta.advice_column_in(SecondPhase), + modulus_rlc: meta.advice_column_in(SecondPhase), + result_rlc: meta.advice_column_in(SecondPhase), + } + } + + /// Get assignments to the modexp table. Meant to be used for dev purposes. + pub fn dev_load( + _challenges: &Challenges>, + ) { + } +} From 6bbc644d6817f6da6df593190e7fdea3cb406296 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 15 Jun 2023 09:32:38 +0800 Subject: [PATCH 08/57] wip wip wip wip --- .../evm_circuit/execution/precompiles/mod.rs | 1 + .../execution/precompiles/modexp.rs | 619 ++++++++++++++++++ .../util/math_gadget/binary_number.rs | 2 +- .../src/evm_circuit/util/precompile_gadget.rs | 19 +- 4 files changed, 637 insertions(+), 4 deletions(-) create mode 100755 zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs index 7a9fd3a4e9..852b4d549b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs @@ -19,6 +19,7 @@ mod ecrecover; pub use ecrecover::EcrecoverGadget; mod identity; +mod modexp; pub use identity::IdentityGadget; #[derive(Clone, Debug)] diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs new file mode 100755 index 0000000000..f96e4ba927 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -0,0 +1,619 @@ +use eth_types::{Field, ToScalar, U256}; +use gadgets::{binary_number::AsBits, util::{self, Expr}}; +use halo2_proofs::{circuit::Value, plonk::{Expression, Error}}; + +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + step::ExecutionState, + util::{constraint_builder::{EVMConstraintBuilder, ConstrainBuilderCommon}, + CachedRegion, Cell,Word, + math_gadget::{BinaryNumberGadget, IsZeroGadget, LtGadget}, + }, + }, + table::CallContextFieldTag, + witness::{Block, Call, ExecStep, Transaction}, +}; + +#[derive(Clone, Debug)] +struct RandPowRepresent { + bits: BinaryNumberGadget, + pow_assembles: [Cell;BIT_LIMIT], +} + +impl RandPowRepresent { + + /// build randomness r, r**2, ... r**(2**BIT_LIMIT) + pub fn base_pows_expr(randomness: Expression) -> [Expression;BIT_LIMIT] { + std::iter::successors( + Some(randomness.clone()), + |r| Some(r.clone() * r.clone()) + ).take(BIT_LIMIT) + .collect::>() + .try_into() + .expect("same length") + } + + /// build r**EXP (EXP can be represented by BIT_LIMIT bits) + pub fn pows_expr(randomness: Expression) -> Expression { + assert!(2usize.pow(BIT_LIMIT as u32) > EXP, "EXP ({EXP}) can not exceed bit limit (2**{BIT_LIMIT}-1)"); + let bits : [bool; BIT_LIMIT]= EXP.as_bits(); + let base_pows = Self::base_pows_expr(randomness); + bits.as_slice().iter().rev().zip(&base_pows).fold( + 1.expr(), + |calc, (&bit, base_pow)|if bit {calc * base_pow.clone()} else {calc} + ) + } + + /// refere to a binary represent of exponent (like BinaryNumberGadget) + pub fn configure( + cb: &mut EVMConstraintBuilder, + randomness: Expression, + exponent: Expression, + ) -> Self { + let bits = BinaryNumberGadget::construct(cb, exponent); + let base_pows = Self::base_pows_expr(randomness); + let pow_assembles = [0; BIT_LIMIT].map(|_|cb.query_cell_phase2()); + let mut last_pow_assemble = 1.expr(); + for ((pow_assemble, exp_bit), base_pow) in + pow_assembles + .as_slice() + .iter() + .zip(bits.bits.as_slice().iter().rev()) + .zip(&base_pows) { + cb.require_equal( + "pow_assemble = if exp_bit {pow_assemble.last} else {pow_assemble.last*base_pow} ", + pow_assemble.expr(), + util::select::expr(exp_bit.expr(), last_pow_assemble.clone() * base_pow.clone(), last_pow_assemble.clone()), + ); + last_pow_assemble = pow_assemble.expr(); + } + + Self { + pow_assembles, + bits, + } + } + + pub fn pow(&self) -> Expression {self.pow_assembles.last().expect("BIT_LIMIT is not zero").expr()} + + pub fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + exponent: usize, + ) -> Result<(), Error> { + assert!(2usize.pow(BIT_LIMIT as u32) > exponent, "exponent ({exponent}) can not exceed bit limit (2**{BIT_LIMIT}-1)"); + self.bits.assign(region, offset, exponent)?; + let bits : [bool; BIT_LIMIT]= exponent.as_bits(); + let base_pows = std::iter::successors( + Some(region + .challenges() + .keccak_input()), + |val| Some(val.map(|v|v.square())) + ).take(BIT_LIMIT); + + let mut last_assigned_assemble = Value::known(F::one()); + for ((column, &bit), base_pow) in + self.pow_assembles + .as_slice() + .iter() + .zip(bits.as_slice().iter().rev()) + .zip(base_pows) { + + let assigned_v = if bit { + last_assigned_assemble*base_pow + }else { + last_assigned_assemble + }; + column.assign(region, offset, assigned_v)?; + last_assigned_assemble = assigned_v; + } + + Ok(()) + } +} + + +#[derive(Clone, Debug)] +struct FixedRandPowRepresent (RandPowRepresent); + +impl AsRef> for FixedRandPowRepresent{ + fn as_ref(&self) -> &RandPowRepresent{&self.0} +} + +const SIZE_REPRESENT_BITS: usize = 6; +const SIZE_LIMIT: usize = 32; +const SIZE_REPRESENT_BYTES: usize = SIZE_LIMIT/256 + 1; +const INPUT_LIMIT: usize = 32*6; + +#[derive(Clone, Debug)] +struct SizeRepresent { + len_bytes: Word, + is_rest_field_zero: IsZeroGadget, + is_exceed_limit: LtGadget, +} + +impl SizeRepresent { + pub fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let len_bytes = cb.query_keccak_rlc(); + // we calculate at most 31 bytes so it can be fit into a field + let len_blank_bytes = len_bytes.cells[..(32-SIZE_REPRESENT_BYTES)] + .iter().map(Cell::expr).collect::>(); + let is_rest_field_zero = IsZeroGadget::construct(cb, + util::expr_from_bytes(&len_blank_bytes), + ); + let len_effect_bytes = len_bytes.cells[(32-SIZE_REPRESENT_BYTES)..] + .iter().map(Cell::expr).collect::>(); + let is_exceed_limit = LtGadget::construct(cb, + util::expr_from_bytes(&len_effect_bytes), + SIZE_LIMIT.expr(), + ); + Self { + len_bytes, + is_rest_field_zero, + is_exceed_limit, + } + } + + /// the rlc of size memory + pub fn memory_value(&self) -> Expression { + self.len_bytes.expr() + } + + pub fn is_valid(&self) -> Expression { + util::and::expr( + [ + self.is_rest_field_zero.expr(), + util::not::expr(self.is_exceed_limit.expr()), + ] + ) + } + + pub fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + size: &U256, + ) -> Result<(), Error> { + let mut bytes = [0u8; 32]; + size.to_big_endian(&mut bytes); + self.len_bytes.assign(region, offset, Some(bytes))?; + Ok(()) + } + + + +} + +type RandPow = RandPowRepresent; + +// parse as (valid, len, value: [base, exp, modulus]) +type InputParsedResult = (bool, [U256;3], [[u8;SIZE_LIMIT];3]); + +fn parse_memory_to_value(mem: &[u8]) -> [u8;SIZE_LIMIT] { + let mut value_bytes = [0u8; SIZE_LIMIT]; + if mem.len() > 0 { + value_bytes.as_mut_slice()[(SIZE_LIMIT - mem.len())..].copy_from_slice(mem); + } + value_bytes +} + + +fn parse_input_memory(mem: &[u8]) -> InputParsedResult { + + let mut mem_input : Vec<_> = mem.iter().copied().collect(); + mem_input.resize(96, 0); + let base_len = U256::from_big_endian(&mem_input[..32]); + let exp_len = U256::from_big_endian(&mem_input[32..64]); + let modulus_len = U256::from_big_endian(&mem_input[64..96]); + + let limit = U256::from(SIZE_LIMIT); + + let input_valid = base_len <= limit && + exp_len <= limit && + modulus_len <= limit; + + let base_mem_len = if input_valid {base_len.as_usize()} else {SIZE_LIMIT}; + let exp_mem_len = if input_valid {exp_len.as_usize()} else {SIZE_LIMIT}; + let modulus_mem_len = if input_valid {modulus_len.as_usize()} else {SIZE_LIMIT}; + + mem_input.resize(96+base_mem_len+exp_mem_len+modulus_mem_len, 0); + let mut cur_input_begin = &mem_input[96..]; + + let base = parse_memory_to_value(&cur_input_begin[..base_mem_len]); + cur_input_begin = &cur_input_begin[base_mem_len..]; + let exp = parse_memory_to_value(&cur_input_begin[..exp_mem_len]); + cur_input_begin = &cur_input_begin[exp_mem_len..]; + let modulus = parse_memory_to_value(&cur_input_begin[..modulus_mem_len]); + + (input_valid, [base_len, exp_len, modulus_len], [base, exp, modulus]) + +} + + +#[derive(Clone, Debug)] +struct ModExpInputs { + base_len: SizeRepresent, + modulus_len: SizeRepresent, + exp_len: SizeRepresent, + base_pow: RandPow, + base: Word, + modulus_pow: RandPow, + modulus: Word, + exp_pow: RandPow, + exp: Word, + input_valid: Cell, +} + +impl ModExpInputs { + pub fn configure( + cb: &mut EVMConstraintBuilder, + input_bytes_acc: Expression, + ) -> Self { + let base_len = SizeRepresent::configure(cb); + let modulus_len = SizeRepresent::configure(cb); + let exp_len = SizeRepresent::configure(cb); + + let r_pow_32 = RandPowRepresent::<_, 5>::base_pows_expr( + cb.challenges().keccak_input())[4].clone(); //r**32 + let r_pow_64 = r_pow_32.clone().square(); + + let base = cb.query_keccak_rlc(); + let modulus = cb.query_keccak_rlc(); + let exp = cb.query_keccak_rlc(); + + let input_valid = cb.query_cell(); + cb.require_equal("mark input valid by checking 3 lens is valid", + input_valid.expr(), + util::and::expr([ + base_len.is_valid(), + exp_len.is_valid(), + modulus_len.is_valid(), + ]), + ); + + // we put correct size in each input word if input is valid + // else we just put as most as possible bytes (32) into it + // so we finally handle the memory in limited sized (32*3) + let base_pow = RandPow::configure(cb, + cb.challenges().keccak_input(), + util::select::expr(input_valid.expr(), + base_len.memory_value(), + 32.expr(), + ), + ); + let exp_pow = RandPow::configure(cb, + cb.challenges().keccak_input(), + util::select::expr(input_valid.expr(), + exp_len.memory_value(), + 32.expr(), + ), + ); + let modulus_pow = RandPow::configure(cb, + cb.challenges().keccak_input(), + util::select::expr(input_valid.expr(), + modulus_len.memory_value(), + 32.expr(), + ), + ); + + let r_pow_base = r_pow_64.clone() * base_pow.pow(); + let r_pow_exp = r_pow_base.clone() * exp_pow.pow(); + let r_pow_modulus = r_pow_exp.clone() * modulus_pow.pow(); + + cb.require_equal("acc bytes must equal", + input_bytes_acc, + base_len.memory_value() + + r_pow_32 * exp_len.memory_value() + + r_pow_64 * modulus_len.memory_value() + + r_pow_base * base.expr() + //rlc of base plus r**(64+base_len)" + r_pow_exp * exp.expr() + //rlc of exp plus r**(64+base_len+exp_len) + r_pow_modulus * modulus.expr() //rlc of modulus plus r**(64+base_len+exp_len+modulus_len) + ); + + Self { + base_len, + modulus_len, + exp_len, + base_pow, + base, + modulus_pow, + modulus, + exp_pow, + exp, + input_valid, + } + } + + pub fn modulus_len(&self) -> Expression {self.modulus_len.memory_value()} + pub fn is_valid(&self) -> Expression {self.input_valid.expr()} + + pub fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + (input_valid, lens, values): &InputParsedResult, + ) -> Result<(), Error> { + self.input_valid.assign(region, offset, Value::known(if *input_valid {F::one()} else {F::zero()}))?; + + for (len, len_represent) in lens.iter().zip([&self.base_len, &self.exp_len, &self.modulus_len]){ + len_represent.assign(region, offset, len)?; + } + + for (len, pow) in lens.iter().zip([&self.base_pow, &self.exp_pow, &self.modulus_pow]){ + pow.assign(region, offset, if *input_valid {len.as_usize()} else {SIZE_LIMIT})?; + } + + for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]){ + input_bytes.assign(region, offset, Some(val))?; + } + + Ok(()) + } + +} + + +#[derive(Clone, Debug)] +struct ModExpOutputs { + result_pow: RandPow, + result_pow_prefix: Cell, + result: Word, + is_result_zero: IsZeroGadget, +} + +impl ModExpOutputs { + fn configure( + cb: &mut EVMConstraintBuilder, + output_bytes_acc: Expression, + inner_success: Expression, + modulus_len: Expression, + ) -> Self { + + let output_len = inner_success * modulus_len; + let is_result_zero = IsZeroGadget::construct(cb, output_len.clone()); + + let result_pow = RandPow::configure(cb, + cb.challenges().keccak_input(), + output_len, + ); + let result_pow_prefix = cb.query_cell_phase2(); + let result = cb.query_keccak_rlc(); + + let r_pow_32 = RandPowRepresent::<_, 5>::base_pows_expr( + cb.challenges().keccak_input())[4].clone(); //r**32 + + cb.require_equal("use pow prefix as 1/r**32", + result_pow_prefix.expr() * r_pow_32, + 1.expr(), + ); + + cb.condition(is_result_zero.expr(), |cb|{ + cb.require_equal("acc bytes must equal", + output_bytes_acc, + result_pow_prefix.expr() * result_pow.pow() * result.expr(), + ); + }); + + Self { + result_pow, + result_pow_prefix, + result, + is_result_zero, + } + } + + pub fn is_output_nil(&self) -> Expression {self.is_result_zero.expr()} + + pub fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + mem: &[u8], + ) -> Result<(), Error> { + self.result_pow_prefix.assign(region, offset, + region.challenges().keccak_input().map( + |rand|rand.pow_vartime(&[32]).invert().unwrap() + ))?; + self.is_result_zero.assign(region, offset, F::from(mem.len() as u64))?; + self.result_pow.assign(region, offset, mem.len())?; + self.result.assign(region, offset, Some(parse_memory_to_value(mem)))?; + Ok(()) + } + +} + +#[derive(Clone, Debug)] +pub struct ModExpGadget { + is_success: Cell, + callee_address: Cell, + caller_id: Cell, + call_data_offset: Cell, + call_data_length: Cell, + return_data_offset: Cell, + return_data_length: Cell, + + input: ModExpInputs, + output: ModExpOutputs, +} + +impl ExecutionGadget for ModExpGadget { + const EXECUTION_STATE: ExecutionState = ExecutionState::PrecompileBigModExp; + + const NAME: &'static str = "MODEXP"; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + + // we 'copy' the acc_bytes cell inside call_op step, so it must be the first query cells + let input_bytes_acc = cb.query_copy_cell_phase2(); + let output_bytes_acc = cb.query_copy_cell_phase2(); + + let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] = + [ + CallContextFieldTag::IsSuccess, + CallContextFieldTag::CalleeAddress, + CallContextFieldTag::CallerId, + CallContextFieldTag::CallDataOffset, + CallContextFieldTag::CallDataLength, + CallContextFieldTag::ReturnDataOffset, + CallContextFieldTag::ReturnDataLength, + ] + .map(|tag| cb.call_context(None, tag)); + + cb.precompile_info_lookup( + cb.execution_state().as_u64().expr(), + callee_address.expr(), + cb.execution_state().precompile_base_gas_cost().expr(), + ); + + let input = ModExpInputs::configure(cb, input_bytes_acc.expr()); + + let call_success = util::and::expr([ + input.is_valid(), + //TODO: replace this constants when gas gadget is ready + 1.expr(), + ]); + + cb.require_equal( + "call success if valid input and enough gas", + is_success.expr(), + call_success.clone(), + ); + + let output = ModExpOutputs::configure(cb, + output_bytes_acc.expr(), + //FIXME: there may be still some edge cases lead to nil output (even modulus_len is not 0) + call_success, + input.modulus_len(), + ); + + cb.condition(util::not::expr(output.is_output_nil()), |_cb|{ + //TODO: config modexp circuit + }); + + Self { + is_success, + callee_address, + caller_id, + call_data_offset, + call_data_length, + return_data_offset, + return_data_length, + input, + output, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + _block: &Block, + _tx: &Transaction, + call: &Call, + _step: &ExecStep, + ) -> Result<(), Error> { + self.is_success.assign( + region, + offset, + Value::known(F::from(u64::from(call.is_success))), + )?; + self.callee_address.assign( + region, + offset, + Value::known(call.code_address.unwrap().to_scalar().unwrap()), + )?; + self.caller_id + .assign(region, offset, Value::known(F::from(call.caller_id as u64)))?; + self.call_data_offset.assign( + region, + offset, + Value::known(F::from(call.call_data_offset)), + )?; + self.call_data_length.assign( + region, + offset, + Value::known(F::from(call.call_data_length)), + )?; + self.return_data_offset.assign( + region, + offset, + Value::known(F::from(call.return_data_offset)), + )?; + self.return_data_length.assign( + region, + offset, + Value::known(F::from(call.return_data_length)), + )?; + + Ok(()) + } +} + + +#[cfg(test)] +mod test { + use bus_mapping::{ + evm::{OpcodeId, PrecompileCallArgs}, + precompile::PrecompileCalls, + }; + use eth_types::{bytecode, word, ToWord}; + use itertools::Itertools; + use mock::TestContext; + + use crate::test_util::CircuitTestBuilder; + + lazy_static::lazy_static! { + static ref TEST_VECTOR: Vec = { + vec![ + PrecompileCallArgs { + name: "modexp success", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x1) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x1) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0x63.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + ] + }; + } + + #[test] + fn precompile_modexp_test() { + let call_kinds = vec![ +// OpcodeId::CALL, + OpcodeId::STATICCALL, +// OpcodeId::DELEGATECALL, +// OpcodeId::CALLCODE, + ]; + + for (test_vector, &call_kind) in TEST_VECTOR.iter().cartesian_product(&call_kinds) { + let bytecode = test_vector.with_call_op(call_kind); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + } + } +} diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs index d872bfc6b7..f96ea0f330 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/binary_number.rs @@ -12,7 +12,7 @@ use crate::evm_circuit::util::{ #[derive(Clone, Debug)] pub struct BinaryNumberGadget { - bits: [Cell; N], + pub(crate) bits: [Cell; N], } impl BinaryNumberGadget { diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 02ddf87874..37434defe9 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -90,8 +90,8 @@ impl PrecompileGadget { cb.condition(is_success, |cb| { cb.require_equal( "input and output bytes are the same", - input_bytes_rlc, - output_bytes_rlc, + input_bytes_rlc.clone(), + output_bytes_rlc.clone(), ); cb.require_equal( "input length and precompile return length are the same", @@ -102,7 +102,20 @@ impl PrecompileGadget { }); cb.condition(address.value_equals(PrecompileCalls::Modexp), |cb| { - cb.constrain_next_step(ExecutionState::PrecompileBigModExp, None, |_cb| {}); + cb.constrain_next_step(ExecutionState::PrecompileBigModExp, None, |cb| { + let input_bytes_acc_copied = cb.query_copy_cell_phase2(); + let output_bytes_acc_copied = cb.query_copy_cell_phase2(); + cb.require_equal( + "copy input bytes", + input_bytes_rlc.clone(), + input_bytes_acc_copied.expr(), + ); + cb.require_equal( + "copy output bytes", + output_bytes_rlc.clone(), + output_bytes_acc_copied.expr(), + ); + }); }); cb.condition(address.value_equals(PrecompileCalls::Bn128Add), |cb| { From 0a4082da0767404c0253bb29e5842d077892455e Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sun, 25 Jun 2023 15:48:16 +0800 Subject: [PATCH 09/57] add auxdata of modexp --- .../src/evm/opcodes/precompiles/mod.rs | 8 ++- bus-mapping/src/precompile.rs | 68 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index d1fd9a2bed..641a061ec3 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -7,7 +7,7 @@ use halo2_proofs::halo2curves::secp256k1::Fq; use crate::{ circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, operation::CallContextField, - precompile::{EcrecoverAuxData, PrecompileAuxData, PrecompileCalls}, + precompile::{EcrecoverAuxData, ModExpAuxData, PrecompileAuxData, PrecompileCalls}, Error, }; @@ -59,6 +59,12 @@ pub fn gen_associated_ops( } exec_step.aux_data = Some(PrecompileAuxData::Ecrecover(aux_data)); + } else if precompile == PrecompileCalls::Modexp { + let aux_data = ModExpAuxData::new( + input_bytes.unwrap_or_default(), + output_bytes.unwrap_or_default(), + ); + exec_step.aux_data = Some(PrecompileAuxData::Modexp(aux_data)); } Ok(exec_step) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 5f256f7c05..b1c8c08c14 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -116,6 +116,7 @@ impl PrecompileCalls { Self::Ecrecover | Self::Bn128Add => Some(128), Self::Bn128Mul => Some(96), Self::Blake2F => Some(213), + Self::Modexp => Some(192), _ => None, } } @@ -160,11 +161,78 @@ impl EcrecoverAuxData { } } +const MODEXP_SIZE_LIMIT: usize = 32; + +/// Auxiliary data for Modexp +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ModExpAuxData { + /// The specified len of inputs: [base, exp, modulus] + pub input_lens: [Word;3], + /// Input value [base, exp, modulus], limited to SIZE_LIMIT + pub inputs: [[u8;MODEXP_SIZE_LIMIT];3], + /// Input valid. + pub valid: bool, + /// len of output, limited to lens of moduls, but can be 0 + pub output_len: usize, + /// output of modexp. + pub output: [u8;MODEXP_SIZE_LIMIT], +} + +impl ModExpAuxData { + + fn parse_memory_to_value(mem: &[u8]) -> [u8;MODEXP_SIZE_LIMIT] { + let mut value_bytes = [0u8; MODEXP_SIZE_LIMIT]; + if mem.len() > 0 { + value_bytes.as_mut_slice()[(MODEXP_SIZE_LIMIT - mem.len())..].copy_from_slice(mem); + } + value_bytes + } + + /// Create a new instance of modexp auxiliary data. + pub fn new(mut mem_input: Vec, output: Vec) -> Self { + // extended to minimum size (3x U256) + mem_input.resize(96, 0); + let base_len = Word::from_big_endian(&mem_input[..32]); + let exp_len = Word::from_big_endian(&mem_input[32..64]); + let modulus_len = Word::from_big_endian(&mem_input[64..96]); + + let limit = Word::from(MODEXP_SIZE_LIMIT); + + let input_valid = base_len <= limit && + exp_len <= limit && + modulus_len <= limit; + + let base_mem_len = if input_valid {base_len.as_usize()} else {MODEXP_SIZE_LIMIT}; + let exp_mem_len = if input_valid {exp_len.as_usize()} else {MODEXP_SIZE_LIMIT}; + let modulus_mem_len = if input_valid {modulus_len.as_usize()} else {MODEXP_SIZE_LIMIT}; + + mem_input.resize(96+base_mem_len+exp_mem_len+modulus_mem_len, 0); + let mut cur_input_begin = &mem_input[96..]; + + let base = Self::parse_memory_to_value(&cur_input_begin[..base_mem_len]); + cur_input_begin = &cur_input_begin[base_mem_len..]; + let exp = Self::parse_memory_to_value(&cur_input_begin[..exp_mem_len]); + cur_input_begin = &cur_input_begin[exp_mem_len..]; + let modulus = Self::parse_memory_to_value(&cur_input_begin[..modulus_mem_len]); + + + Self { + valid: input_valid, + input_lens: [base_len, exp_len, modulus_len], + inputs: [base, exp, modulus], + output: Self::parse_memory_to_value(&output), + output_len: output.len(), + } + } +} + /// Auxiliary data attached to an internal state for precompile verification. #[derive(Clone, Debug, PartialEq, Eq)] pub enum PrecompileAuxData { /// Ecrecover. Ecrecover(EcrecoverAuxData), + /// Modexp. + Modexp(ModExpAuxData) } impl Default for PrecompileAuxData { From ad62cbf0e9e4a2574c42ab144050d5335149a28d Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sun, 25 Jun 2023 23:15:14 +0800 Subject: [PATCH 10/57] assign in modexp gadget --- bus-mapping/src/precompile.rs | 3 +- .../execution/precompiles/modexp.rs | 80 +++++++------------ 2 files changed, 32 insertions(+), 51 deletions(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index b1c8c08c14..29351986a8 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -161,7 +161,8 @@ impl EcrecoverAuxData { } } -const MODEXP_SIZE_LIMIT: usize = 32; +/// size limit of modexp +pub const MODEXP_SIZE_LIMIT: usize = 32; /// Auxiliary data for Modexp #[derive(Clone, Debug, Default, PartialEq, Eq)] diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index f96e4ba927..8a56da5c95 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -2,6 +2,7 @@ use eth_types::{Field, ToScalar, U256}; use gadgets::{binary_number::AsBits, util::{self, Expr}}; use halo2_proofs::{circuit::Value, plonk::{Expression, Error}}; +use bus_mapping::precompile::{PrecompileAuxData, MODEXP_SIZE_LIMIT}; use crate::{ evm_circuit::{ execution::ExecutionGadget, @@ -122,8 +123,8 @@ impl AsRef &RandPowRepresent{&self.0} } +const SIZE_LIMIT: usize = MODEXP_SIZE_LIMIT; const SIZE_REPRESENT_BITS: usize = 6; -const SIZE_LIMIT: usize = 32; const SIZE_REPRESENT_BYTES: usize = SIZE_LIMIT/256 + 1; const INPUT_LIMIT: usize = 32*6; @@ -190,47 +191,7 @@ type RandPow = RandPowRepresent; // parse as (valid, len, value: [base, exp, modulus]) type InputParsedResult = (bool, [U256;3], [[u8;SIZE_LIMIT];3]); - -fn parse_memory_to_value(mem: &[u8]) -> [u8;SIZE_LIMIT] { - let mut value_bytes = [0u8; SIZE_LIMIT]; - if mem.len() > 0 { - value_bytes.as_mut_slice()[(SIZE_LIMIT - mem.len())..].copy_from_slice(mem); - } - value_bytes -} - - -fn parse_input_memory(mem: &[u8]) -> InputParsedResult { - - let mut mem_input : Vec<_> = mem.iter().copied().collect(); - mem_input.resize(96, 0); - let base_len = U256::from_big_endian(&mem_input[..32]); - let exp_len = U256::from_big_endian(&mem_input[32..64]); - let modulus_len = U256::from_big_endian(&mem_input[64..96]); - - let limit = U256::from(SIZE_LIMIT); - - let input_valid = base_len <= limit && - exp_len <= limit && - modulus_len <= limit; - - let base_mem_len = if input_valid {base_len.as_usize()} else {SIZE_LIMIT}; - let exp_mem_len = if input_valid {exp_len.as_usize()} else {SIZE_LIMIT}; - let modulus_mem_len = if input_valid {modulus_len.as_usize()} else {SIZE_LIMIT}; - - mem_input.resize(96+base_mem_len+exp_mem_len+modulus_mem_len, 0); - let mut cur_input_begin = &mem_input[96..]; - - let base = parse_memory_to_value(&cur_input_begin[..base_mem_len]); - cur_input_begin = &cur_input_begin[base_mem_len..]; - let exp = parse_memory_to_value(&cur_input_begin[..exp_mem_len]); - cur_input_begin = &cur_input_begin[exp_mem_len..]; - let modulus = parse_memory_to_value(&cur_input_begin[..modulus_mem_len]); - - (input_valid, [base_len, exp_len, modulus_len], [base, exp, modulus]) - -} - +type OutputParsedResult = (usize, [u8;SIZE_LIMIT]); #[derive(Clone, Debug)] struct ModExpInputs { @@ -333,16 +294,16 @@ impl ModExpInputs { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - (input_valid, lens, values): &InputParsedResult, + (input_valid, lens, values): InputParsedResult, ) -> Result<(), Error> { - self.input_valid.assign(region, offset, Value::known(if *input_valid {F::one()} else {F::zero()}))?; + self.input_valid.assign(region, offset, Value::known(if input_valid {F::one()} else {F::zero()}))?; for (len, len_represent) in lens.iter().zip([&self.base_len, &self.exp_len, &self.modulus_len]){ len_represent.assign(region, offset, len)?; } for (len, pow) in lens.iter().zip([&self.base_pow, &self.exp_pow, &self.modulus_pow]){ - pow.assign(region, offset, if *input_valid {len.as_usize()} else {SIZE_LIMIT})?; + pow.assign(region, offset, if input_valid {len.as_usize()} else {SIZE_LIMIT})?; } for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]){ @@ -410,15 +371,15 @@ impl ModExpOutputs { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - mem: &[u8], + (output_len, data): OutputParsedResult, ) -> Result<(), Error> { self.result_pow_prefix.assign(region, offset, region.challenges().keccak_input().map( |rand|rand.pow_vartime(&[32]).invert().unwrap() ))?; - self.is_result_zero.assign(region, offset, F::from(mem.len() as u64))?; - self.result_pow.assign(region, offset, mem.len())?; - self.result.assign(region, offset, Some(parse_memory_to_value(mem)))?; + self.is_result_zero.assign(region, offset, F::from(output_len as u64))?; + self.result_pow.assign(region, offset, output_len)?; + self.result.assign(region, offset, Some(data))?; Ok(()) } @@ -512,8 +473,27 @@ impl ExecutionGadget for ModExpGadget { _block: &Block, _tx: &Transaction, call: &Call, - _step: &ExecStep, + step: &ExecStep, ) -> Result<(), Error> { + + if let Some(PrecompileAuxData::Modexp(data)) = &step.aux_data { + + self.input.assign(region, offset, ( + data.valid, + data.input_lens, + data.inputs, + ))?; + + self.output.assign(region, offset, ( + data.output_len, + data.output, + ))?; + + } else { + log::error!("unexpected aux_data {:?} for modexp", step.aux_data); + return Err(Error::Synthesis); + } + self.is_success.assign( region, offset, From c9cc27d5498e351753ae5fed0564e4b4d3cf16bc Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 28 Jun 2023 16:29:19 +0800 Subject: [PATCH 11/57] wip: fix most constraint failure --- bus-mapping/src/precompile.rs | 23 ++++- zkevm-circuits/src/evm_circuit/execution.rs | 4 +- .../evm_circuit/execution/precompiles/mod.rs | 4 +- .../execution/precompiles/modexp.rs | 98 +++++++++++++------ .../src/evm_circuit/util/precompile_gadget.rs | 18 ++-- 5 files changed, 101 insertions(+), 46 deletions(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 29351986a8..8548c53696 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -177,6 +177,10 @@ pub struct ModExpAuxData { pub output_len: usize, /// output of modexp. pub output: [u8;MODEXP_SIZE_LIMIT], + /// backup of input memory + pub input_memory: Vec, + /// backup of output memory + pub output_memory: Vec, } impl ModExpAuxData { @@ -191,8 +195,14 @@ impl ModExpAuxData { /// Create a new instance of modexp auxiliary data. pub fn new(mut mem_input: Vec, output: Vec) -> Self { + + let input_memory = mem_input.clone(); + let output_memory = output.clone(); + // extended to minimum size (3x U256) - mem_input.resize(96, 0); + if mem_input.len() < 96 { + mem_input.resize(96, 0); + } let base_len = Word::from_big_endian(&mem_input[..32]); let exp_len = Word::from_big_endian(&mem_input[32..64]); let modulus_len = Word::from_big_endian(&mem_input[64..96]); @@ -209,20 +219,23 @@ impl ModExpAuxData { mem_input.resize(96+base_mem_len+exp_mem_len+modulus_mem_len, 0); let mut cur_input_begin = &mem_input[96..]; - + let base = Self::parse_memory_to_value(&cur_input_begin[..base_mem_len]); cur_input_begin = &cur_input_begin[base_mem_len..]; let exp = Self::parse_memory_to_value(&cur_input_begin[..exp_mem_len]); cur_input_begin = &cur_input_begin[exp_mem_len..]; let modulus = Self::parse_memory_to_value(&cur_input_begin[..modulus_mem_len]); - + let output_len = output.len(); + let output = Self::parse_memory_to_value(&output); Self { valid: input_valid, input_lens: [base_len, exp_len, modulus_len], inputs: [base, exp, modulus], - output: Self::parse_memory_to_value(&output), - output_len: output.len(), + output, + output_len, + input_memory, + output_memory, } } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 12cc5dab7b..0dc55c2b48 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -202,7 +202,7 @@ use opcode_not::NotGadget; use origin::OriginGadget; use pc::PcGadget; use pop::PopGadget; -use precompiles::{EcrecoverGadget, IdentityGadget}; +use precompiles::{EcrecoverGadget, IdentityGadget, ModExpGadget}; use push::PushGadget; use return_revert::ReturnRevertGadget; use returndatacopy::ReturnDataCopyGadget; @@ -351,7 +351,7 @@ pub(crate) struct ExecutionConfig { precompile_sha2_gadget: Box>, precompile_ripemd_gadget: Box>, precompile_identity_gadget: Box>, - precompile_modexp_gadget: Box>, + precompile_modexp_gadget: Box>, precompile_bn128add_gadget: Box>, precompile_bn128mul_gadget: diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs index 852b4d549b..1847ae3519 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs @@ -18,8 +18,10 @@ use crate::{ mod ecrecover; pub use ecrecover::EcrecoverGadget; -mod identity; mod modexp; +pub use modexp::ModExpGadget; + +mod identity; pub use identity::IdentityGadget; #[derive(Clone, Debug)] diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 8a56da5c95..0dbefa41eb 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -10,6 +10,7 @@ use crate::{ util::{constraint_builder::{EVMConstraintBuilder, ConstrainBuilderCommon}, CachedRegion, Cell,Word, math_gadget::{BinaryNumberGadget, IsZeroGadget, LtGadget}, + rlc, }, }, table::CallContextFieldTag, @@ -115,24 +116,23 @@ impl RandPowRepresent { } } - -#[derive(Clone, Debug)] -struct FixedRandPowRepresent (RandPowRepresent); - -impl AsRef> for FixedRandPowRepresent{ - fn as_ref(&self) -> &RandPowRepresent{&self.0} -} - const SIZE_LIMIT: usize = MODEXP_SIZE_LIMIT; const SIZE_REPRESENT_BITS: usize = 6; const SIZE_REPRESENT_BYTES: usize = SIZE_LIMIT/256 + 1; const INPUT_LIMIT: usize = 32*6; +// rlc word, in the reversed byte order +fn rlc_word_rev(cells: &[Cell; N], randomness: Expression) -> Expression { + + let rlc_rev = cells.iter().map(|cell| cell.expr()).rev().collect::>(); + rlc::expr(&rlc_rev, randomness) +} + #[derive(Clone, Debug)] struct SizeRepresent { len_bytes: Word, is_rest_field_zero: IsZeroGadget, - is_exceed_limit: LtGadget, + is_not_exceed_limit: LtGadget, } impl SizeRepresent { @@ -146,14 +146,14 @@ impl SizeRepresent { ); let len_effect_bytes = len_bytes.cells[(32-SIZE_REPRESENT_BYTES)..] .iter().map(Cell::expr).collect::>(); - let is_exceed_limit = LtGadget::construct(cb, + let is_not_exceed_limit = LtGadget::construct(cb, util::expr_from_bytes(&len_effect_bytes), - SIZE_LIMIT.expr(), + (SIZE_LIMIT+1).expr(), ); Self { len_bytes, is_rest_field_zero, - is_exceed_limit, + is_not_exceed_limit, } } @@ -162,11 +162,19 @@ impl SizeRepresent { self.len_bytes.expr() } + /// the value of size + pub fn value(&self) -> Expression { + let len_effect_bytes = self.len_bytes.cells[(32-SIZE_REPRESENT_BYTES)..] + .iter().map(Cell::expr).collect::>(); + util::expr_from_bytes(&len_effect_bytes) + } + + pub fn is_valid(&self) -> Expression { util::and::expr( [ self.is_rest_field_zero.expr(), - util::not::expr(self.is_exceed_limit.expr()), + self.is_not_exceed_limit.expr(), ] ) } @@ -179,7 +187,19 @@ impl SizeRepresent { ) -> Result<(), Error> { let mut bytes = [0u8; 32]; size.to_big_endian(&mut bytes); + self.len_bytes.assign(region, offset, Some(bytes))?; + + let rest_field = U256::from_big_endian(&bytes[..(32-SIZE_REPRESENT_BYTES)]); + let effect_field = U256::from_big_endian(&bytes[(32-SIZE_REPRESENT_BYTES)..]); + + self.is_rest_field_zero.assign(region, offset, rest_field.to_scalar().unwrap())?; + self.is_not_exceed_limit.assign( + region, + offset, + effect_field.to_scalar().unwrap(), + F::from((SIZE_LIMIT + 1)as u64), + )?; Ok(()) } @@ -216,8 +236,8 @@ impl ModExpInputs { let modulus_len = SizeRepresent::configure(cb); let exp_len = SizeRepresent::configure(cb); - let r_pow_32 = RandPowRepresent::<_, 5>::base_pows_expr( - cb.challenges().keccak_input())[4].clone(); //r**32 + let r_pow_32 = RandPowRepresent::<_, 6>::base_pows_expr( + cb.challenges().keccak_input())[5].clone(); //r**32 let r_pow_64 = r_pow_32.clone().square(); let base = cb.query_keccak_rlc(); @@ -240,21 +260,21 @@ impl ModExpInputs { let base_pow = RandPow::configure(cb, cb.challenges().keccak_input(), util::select::expr(input_valid.expr(), - base_len.memory_value(), + base_len.value(), 32.expr(), ), ); let exp_pow = RandPow::configure(cb, cb.challenges().keccak_input(), util::select::expr(input_valid.expr(), - exp_len.memory_value(), + exp_len.value(), 32.expr(), ), ); let modulus_pow = RandPow::configure(cb, cb.challenges().keccak_input(), util::select::expr(input_valid.expr(), - modulus_len.memory_value(), + modulus_len.value(), 32.expr(), ), ); @@ -265,12 +285,12 @@ impl ModExpInputs { cb.require_equal("acc bytes must equal", input_bytes_acc, - base_len.memory_value() + - r_pow_32 * exp_len.memory_value() + - r_pow_64 * modulus_len.memory_value() + - r_pow_base * base.expr() + //rlc of base plus r**(64+base_len)" - r_pow_exp * exp.expr() + //rlc of exp plus r**(64+base_len+exp_len) - r_pow_modulus * modulus.expr() //rlc of modulus plus r**(64+base_len+exp_len+modulus_len) + base_len.memory_value() + + r_pow_32 * exp_len.memory_value() + + r_pow_64 * modulus_len.memory_value() + + r_pow_base * base.expr() //rlc of base plus r**(64+base_len)" + + r_pow_exp * exp.expr() //rlc of exp plus r**(64+base_len+exp_len) + + r_pow_modulus * modulus.expr() //rlc of modulus plus r**(64+base_len+exp_len+modulus_len) ); Self { @@ -287,7 +307,7 @@ impl ModExpInputs { } } - pub fn modulus_len(&self) -> Expression {self.modulus_len.memory_value()} + pub fn modulus_len(&self) -> Expression {self.modulus_len.value()} pub fn is_valid(&self) -> Expression {self.input_valid.expr()} pub fn assign( @@ -342,8 +362,8 @@ impl ModExpOutputs { let result_pow_prefix = cb.query_cell_phase2(); let result = cb.query_keccak_rlc(); - let r_pow_32 = RandPowRepresent::<_, 5>::base_pows_expr( - cb.challenges().keccak_input())[4].clone(); //r**32 + let r_pow_32 = RandPowRepresent::<_, 6>::base_pows_expr( + cb.challenges().keccak_input())[5].clone(); //r**32 cb.require_equal("use pow prefix as 1/r**32", result_pow_prefix.expr() * r_pow_32, @@ -397,6 +417,9 @@ pub struct ModExpGadget { input: ModExpInputs, output: ModExpOutputs, + + input_bytes_acc: Cell, + output_bytes_acc: Cell, } impl ExecutionGadget for ModExpGadget { @@ -407,8 +430,8 @@ impl ExecutionGadget for ModExpGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { // we 'copy' the acc_bytes cell inside call_op step, so it must be the first query cells - let input_bytes_acc = cb.query_copy_cell_phase2(); - let output_bytes_acc = cb.query_copy_cell_phase2(); + let input_bytes_acc = cb.query_cell_phase2(); + let output_bytes_acc = cb.query_cell_phase2(); let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] = [ @@ -463,6 +486,8 @@ impl ExecutionGadget for ModExpGadget { return_data_length, input, output, + input_bytes_acc, + output_bytes_acc, } } @@ -478,6 +503,8 @@ impl ExecutionGadget for ModExpGadget { if let Some(PrecompileAuxData::Modexp(data)) = &step.aux_data { +// println!("exp data: {:?}", data); + self.input.assign(region, offset, ( data.valid, data.input_lens, @@ -489,6 +516,19 @@ impl ExecutionGadget for ModExpGadget { data.output, ))?; + let input_rlc = region + .challenges() + .keccak_input() + .map(|randomness|rlc::value(data.input_memory.iter().rev(), randomness)); + + let output_rlc = region + .challenges() + .keccak_input() + .map(|randomness| rlc::value(data.output_memory.iter().rev(), randomness)); + + self.input_bytes_acc.assign(region, offset, input_rlc)?; + self.output_bytes_acc.assign(region, offset, output_rlc)?; + } else { log::error!("unexpected aux_data {:?} for modexp", step.aux_data); return Err(Error::Synthesis); diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 37434defe9..f25b6d948a 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -39,7 +39,7 @@ impl PrecompileGadget { cb.condition(address.value_equals(PrecompileCalls::Ecrecover), |cb| { cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { - let (recovered, msg_hash_rlc, sig_v, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( +/* let (recovered, msg_hash_rlc, sig_v, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( cb.query_bool(), cb.query_cell_phase2(), cb.query_byte(), @@ -73,7 +73,7 @@ impl PrecompileGadget { cb.condition(not::expr(recovered.expr()), |cb| { cb.require_zero("output bytes == 0", output_bytes_rlc.expr()); cb.require_zero("recovered addr == 0", recovered_addr_rlc.expr()); - }); + }); */ }); }); @@ -103,13 +103,13 @@ impl PrecompileGadget { cb.condition(address.value_equals(PrecompileCalls::Modexp), |cb| { cb.constrain_next_step(ExecutionState::PrecompileBigModExp, None, |cb| { - let input_bytes_acc_copied = cb.query_copy_cell_phase2(); - let output_bytes_acc_copied = cb.query_copy_cell_phase2(); - cb.require_equal( - "copy input bytes", - input_bytes_rlc.clone(), - input_bytes_acc_copied.expr(), - ); + let input_bytes_acc_copied = cb.query_cell_phase2(); + let output_bytes_acc_copied = cb.query_cell_phase2(); + // cb.require_equal( + // "copy input bytes", + // input_bytes_rlc.clone(), + // input_bytes_acc_copied.expr(), + // ); cb.require_equal( "copy output bytes", output_bytes_rlc.clone(), From a6a531a1de1bfcdb8823aa75cac8022d82ef9a84 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 28 Jun 2023 16:31:00 +0800 Subject: [PATCH 12/57] wip --- .../src/evm_circuit/util/precompile_gadget.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index f25b6d948a..a6b8404244 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -105,11 +105,11 @@ impl PrecompileGadget { cb.constrain_next_step(ExecutionState::PrecompileBigModExp, None, |cb| { let input_bytes_acc_copied = cb.query_cell_phase2(); let output_bytes_acc_copied = cb.query_cell_phase2(); - // cb.require_equal( - // "copy input bytes", - // input_bytes_rlc.clone(), - // input_bytes_acc_copied.expr(), - // ); + cb.require_equal( + "copy input bytes", + input_bytes_rlc.clone(), + input_bytes_acc_copied.expr(), + ); cb.require_equal( "copy output bytes", output_bytes_rlc.clone(), From a5678356836942034b898beef501a811aecdc6d4 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 28 Jun 2023 22:52:04 +0800 Subject: [PATCH 13/57] wip: fix constraints --- bus-mapping/src/precompile.rs | 4 +- .../execution/precompiles/modexp.rs | 61 ++++++++++++++----- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 8548c53696..3dc5929cc9 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -116,7 +116,7 @@ impl PrecompileCalls { Self::Ecrecover | Self::Bn128Add => Some(128), Self::Bn128Mul => Some(96), Self::Blake2F => Some(213), - Self::Modexp => Some(192), + Self::Modexp => Some(MODEXP_INPUT_LIMIT), _ => None, } } @@ -163,6 +163,8 @@ impl EcrecoverAuxData { /// size limit of modexp pub const MODEXP_SIZE_LIMIT: usize = 32; +/// size of input limit +pub const MODEXP_INPUT_LIMIT: usize = 192; /// Auxiliary data for Modexp #[derive(Clone, Debug, Default, PartialEq, Eq)] diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 0dbefa41eb..8b946f3b2d 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -2,7 +2,7 @@ use eth_types::{Field, ToScalar, U256}; use gadgets::{binary_number::AsBits, util::{self, Expr}}; use halo2_proofs::{circuit::Value, plonk::{Expression, Error}}; -use bus_mapping::precompile::{PrecompileAuxData, MODEXP_SIZE_LIMIT}; +use bus_mapping::precompile::{PrecompileAuxData, MODEXP_SIZE_LIMIT, MODEXP_INPUT_LIMIT}; use crate::{ evm_circuit::{ execution::ExecutionGadget, @@ -120,6 +120,8 @@ const SIZE_LIMIT: usize = MODEXP_SIZE_LIMIT; const SIZE_REPRESENT_BITS: usize = 6; const SIZE_REPRESENT_BYTES: usize = SIZE_LIMIT/256 + 1; const INPUT_LIMIT: usize = 32*6; +const INPUT_REPRESENT_BYTES: usize = MODEXP_INPUT_LIMIT/256 + 1; +const INPUT_REPRESENT_BITS: usize = 8; // rlc word, in the reversed byte order fn rlc_word_rev(cells: &[Cell; N], randomness: Expression) -> Expression { @@ -225,11 +227,14 @@ struct ModExpInputs { exp_pow: RandPow, exp: Word, input_valid: Cell, + padding_pow: RandPowRepresent, + is_input_need_padding: LtGadget, } impl ModExpInputs { pub fn configure( cb: &mut EVMConstraintBuilder, + input_bytes_len: Expression, input_bytes_acc: Expression, ) -> Self { let base_len = SizeRepresent::configure(cb); @@ -254,29 +259,55 @@ impl ModExpInputs { ]), ); + let base_len_expected = util::select::expr( + input_valid.expr(), + base_len.value(), + SIZE_LIMIT.expr(), + ); + + let exp_len_expected = util::select::expr( + input_valid.expr(), + exp_len.value(), + SIZE_LIMIT.expr(), + ); + + let modulus_len_expected = util::select::expr( + input_valid.expr(), + modulus_len.value(), + SIZE_LIMIT.expr(), + ); + + let input_expected = 96.expr() + base_len_expected.clone() + exp_len_expected.clone() + modulus_len_expected.clone(); + + let is_input_need_padding = LtGadget::construct( + cb, + input_bytes_len.clone(), + input_expected.clone(), + ); + + let padding_pow = RandPowRepresent::configure(cb, + cb.challenges().keccak_input(), + util::select::expr( + is_input_need_padding.expr(), + input_expected - input_bytes_len, + 0.expr(), + ) + ); + // we put correct size in each input word if input is valid // else we just put as most as possible bytes (32) into it // so we finally handle the memory in limited sized (32*3) let base_pow = RandPow::configure(cb, cb.challenges().keccak_input(), - util::select::expr(input_valid.expr(), - base_len.value(), - 32.expr(), - ), + base_len_expected, ); let exp_pow = RandPow::configure(cb, cb.challenges().keccak_input(), - util::select::expr(input_valid.expr(), - exp_len.value(), - 32.expr(), - ), + exp_len_expected, ); let modulus_pow = RandPow::configure(cb, cb.challenges().keccak_input(), - util::select::expr(input_valid.expr(), - modulus_len.value(), - 32.expr(), - ), + modulus_len_expected, ); let r_pow_base = r_pow_64.clone() * base_pow.pow(); @@ -304,6 +335,8 @@ impl ModExpInputs { exp_pow, exp, input_valid, + padding_pow, + is_input_need_padding, } } @@ -451,7 +484,7 @@ impl ExecutionGadget for ModExpGadget { cb.execution_state().precompile_base_gas_cost().expr(), ); - let input = ModExpInputs::configure(cb, input_bytes_acc.expr()); + let input = ModExpInputs::configure(cb, call_data_length.expr(), input_bytes_acc.expr()); let call_success = util::and::expr([ input.is_valid(), From 3e47f8679f88429b89787f3310cfbe4417f0ea11 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 29 Jun 2023 15:54:06 +0800 Subject: [PATCH 14/57] pass primary test --- .../execution/precompiles/modexp.rs | 124 ++++++++++-------- .../evm_circuit/util/constraint_builder.rs | 1 + 2 files changed, 68 insertions(+), 57 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 8b946f3b2d..ae49f439e6 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -8,7 +8,7 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{constraint_builder::{EVMConstraintBuilder, ConstrainBuilderCommon}, - CachedRegion, Cell,Word, + CachedRegion, Cell, math_gadget::{BinaryNumberGadget, IsZeroGadget, LtGadget}, rlc, }, @@ -123,30 +123,48 @@ const INPUT_LIMIT: usize = 32*6; const INPUT_REPRESENT_BYTES: usize = MODEXP_INPUT_LIMIT/256 + 1; const INPUT_REPRESENT_BITS: usize = 8; +type Word = [Cell; 32]; + +fn assign_word( + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + cells: &[Cell; N], + bytes: [u8; N], +) -> Result<(), Error> { + + for (cell, byte) in cells.iter().zip(bytes){ + cell.assign(region, offset, Value::known(F::from(byte as u64)))?; + } + + Ok(()) +} + // rlc word, in the reversed byte order fn rlc_word_rev(cells: &[Cell; N], randomness: Expression) -> Expression { - - let rlc_rev = cells.iter().map(|cell| cell.expr()).rev().collect::>(); - rlc::expr(&rlc_rev, randomness) + cells.iter().map(|cell| cell.expr()).reduce( + |acc, value| acc * randomness.clone() + value + ).expect("values should not be empty") } #[derive(Clone, Debug)] struct SizeRepresent { len_bytes: Word, + expression: Expression, is_rest_field_zero: IsZeroGadget, is_not_exceed_limit: LtGadget, } impl SizeRepresent { pub fn configure(cb: &mut EVMConstraintBuilder) -> Self { - let len_bytes = cb.query_keccak_rlc(); + let len_bytes = cb.query_bytes(); + let expression = rlc_word_rev(&len_bytes, cb.challenges().keccak_input()); // we calculate at most 31 bytes so it can be fit into a field - let len_blank_bytes = len_bytes.cells[..(32-SIZE_REPRESENT_BYTES)] + let len_blank_bytes = len_bytes[..(32-SIZE_REPRESENT_BYTES)] .iter().map(Cell::expr).collect::>(); let is_rest_field_zero = IsZeroGadget::construct(cb, util::expr_from_bytes(&len_blank_bytes), ); - let len_effect_bytes = len_bytes.cells[(32-SIZE_REPRESENT_BYTES)..] + let len_effect_bytes = len_bytes[(32-SIZE_REPRESENT_BYTES)..] .iter().map(Cell::expr).collect::>(); let is_not_exceed_limit = LtGadget::construct(cb, util::expr_from_bytes(&len_effect_bytes), @@ -154,19 +172,20 @@ impl SizeRepresent { ); Self { len_bytes, + expression, is_rest_field_zero, is_not_exceed_limit, } } - /// the rlc of size memory - pub fn memory_value(&self) -> Expression { - self.len_bytes.expr() + /// the rlc of size memory, in reversed byte order + pub fn memory_rlc(&self) -> Expression { + self.expression.clone() } /// the value of size pub fn value(&self) -> Expression { - let len_effect_bytes = self.len_bytes.cells[(32-SIZE_REPRESENT_BYTES)..] + let len_effect_bytes = self.len_bytes[(32-SIZE_REPRESENT_BYTES)..] .iter().map(Cell::expr).collect::>(); util::expr_from_bytes(&len_effect_bytes) } @@ -190,7 +209,7 @@ impl SizeRepresent { let mut bytes = [0u8; 32]; size.to_big_endian(&mut bytes); - self.len_bytes.assign(region, offset, Some(bytes))?; + assign_word(region, offset, &self.len_bytes, bytes)?; let rest_field = U256::from_big_endian(&bytes[..(32-SIZE_REPRESENT_BYTES)]); let effect_field = U256::from_big_endian(&bytes[(32-SIZE_REPRESENT_BYTES)..]); @@ -245,9 +264,9 @@ impl ModExpInputs { cb.challenges().keccak_input())[5].clone(); //r**32 let r_pow_64 = r_pow_32.clone().square(); - let base = cb.query_keccak_rlc(); - let modulus = cb.query_keccak_rlc(); - let exp = cb.query_keccak_rlc(); + let base = cb.query_bytes(); + let modulus = cb.query_bytes(); + let exp = cb.query_bytes(); let input_valid = cb.query_cell(); cb.require_equal("mark input valid by checking 3 lens is valid", @@ -310,18 +329,18 @@ impl ModExpInputs { modulus_len_expected, ); - let r_pow_base = r_pow_64.clone() * base_pow.pow(); + let r_pow_base = base_pow.pow(); let r_pow_exp = r_pow_base.clone() * exp_pow.pow(); - let r_pow_modulus = r_pow_exp.clone() * modulus_pow.pow(); + let r_pow_all_vars = r_pow_exp.clone() * modulus_pow.pow(); cb.require_equal("acc bytes must equal", - input_bytes_acc, - base_len.memory_value() - + r_pow_32 * exp_len.memory_value() - + r_pow_64 * modulus_len.memory_value() - + r_pow_base * base.expr() //rlc of base plus r**(64+base_len)" - + r_pow_exp * exp.expr() //rlc of exp plus r**(64+base_len+exp_len) - + r_pow_modulus * modulus.expr() //rlc of modulus plus r**(64+base_len+exp_len+modulus_len) + input_bytes_acc * padding_pow.pow(), + rlc_word_rev(&modulus, cb.challenges().keccak_input()) //rlc of base + + r_pow_base * rlc_word_rev(&exp, cb.challenges().keccak_input()) //rlc of exp plus r**base_len + + r_pow_exp * rlc_word_rev(&base, cb.challenges().keccak_input()) //rlc of exp plus r**(base_len + exp_len) + + r_pow_all_vars.clone() * modulus_len.memory_rlc() + + r_pow_all_vars.clone() * r_pow_32 * exp_len.memory_rlc() + + r_pow_all_vars * r_pow_64 * base_len.memory_rlc() ); Self { @@ -348,6 +367,7 @@ impl ModExpInputs { region: &mut CachedRegion<'_, '_, F>, offset: usize, (input_valid, lens, values): InputParsedResult, + input_len: usize, ) -> Result<(), Error> { self.input_valid.assign(region, offset, Value::known(if input_valid {F::one()} else {F::zero()}))?; @@ -360,9 +380,22 @@ impl ModExpInputs { } for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]){ - input_bytes.assign(region, offset, Some(val))?; + assign_word(region, offset, input_bytes, val)?; } + let expected_len = if input_valid { + lens.iter().map(U256::as_usize).sum::() + 96 + } else {INPUT_LIMIT}; + + self.is_input_need_padding.assign(region, offset, + F::from(input_len as u64), + F::from(expected_len as u64), + )?; + + self.padding_pow.assign(region, offset, + if input_len < expected_len {expected_len - input_len} else {0}, + )?; + Ok(()) } @@ -371,8 +404,6 @@ impl ModExpInputs { #[derive(Clone, Debug)] struct ModExpOutputs { - result_pow: RandPow, - result_pow_prefix: Cell, result: Word, is_result_zero: IsZeroGadget, } @@ -387,32 +418,17 @@ impl ModExpOutputs { let output_len = inner_success * modulus_len; let is_result_zero = IsZeroGadget::construct(cb, output_len.clone()); + + let result = cb.query_bytes(); - let result_pow = RandPow::configure(cb, - cb.challenges().keccak_input(), - output_len, - ); - let result_pow_prefix = cb.query_cell_phase2(); - let result = cb.query_keccak_rlc(); - - let r_pow_32 = RandPowRepresent::<_, 6>::base_pows_expr( - cb.challenges().keccak_input())[5].clone(); //r**32 - - cb.require_equal("use pow prefix as 1/r**32", - result_pow_prefix.expr() * r_pow_32, - 1.expr(), - ); - - cb.condition(is_result_zero.expr(), |cb|{ + cb.condition(util::not::expr(is_result_zero.expr()), |cb|{ cb.require_equal("acc bytes must equal", output_bytes_acc, - result_pow_prefix.expr() * result_pow.pow() * result.expr(), + rlc_word_rev(&result, cb.challenges().keccak_input()), ); }); Self { - result_pow, - result_pow_prefix, result, is_result_zero, } @@ -426,13 +442,8 @@ impl ModExpOutputs { offset: usize, (output_len, data): OutputParsedResult, ) -> Result<(), Error> { - self.result_pow_prefix.assign(region, offset, - region.challenges().keccak_input().map( - |rand|rand.pow_vartime(&[32]).invert().unwrap() - ))?; self.is_result_zero.assign(region, offset, F::from(output_len as u64))?; - self.result_pow.assign(region, offset, output_len)?; - self.result.assign(region, offset, Some(data))?; + assign_word(region, offset, &self.result, data)?; Ok(()) } @@ -538,11 +549,10 @@ impl ExecutionGadget for ModExpGadget { // println!("exp data: {:?}", data); - self.input.assign(region, offset, ( - data.valid, - data.input_lens, - data.inputs, - ))?; + self.input.assign(region, offset, + (data.valid, data.input_lens, data.inputs), + data.input_memory.len(), + )?; self.output.assign(region, offset, ( data.output_len, diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index d56be3f6ac..0aac10fb68 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -448,6 +448,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn query_cell_phase2(&mut self) -> Cell { let cell = self.query_cell_with_type(CellType::StoragePhase2); + #[cfg(not(feature = "onephase"))] assert_eq!(cell.column.column_type(), &Advice::new(SecondPhase)); cell } From 8c36b29ee0d9f3179058395f7e8567884c71e80d Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 30 Jun 2023 11:58:21 +0800 Subject: [PATCH 15/57] optimize phase2 cell usage and add more tests --- .../execution/precompiles/modexp.rs | 189 +++++++++++++----- 1 file changed, 138 insertions(+), 51 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index ae49f439e6..e1c7a1f496 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -20,11 +20,14 @@ use crate::{ #[derive(Clone, Debug)] struct RandPowRepresent { bits: BinaryNumberGadget, - pow_assembles: [Cell;BIT_LIMIT], + pow_assembles: Vec<(Cell, usize)>, + pow: Expression, } impl RandPowRepresent { + const BIT_EXP_MAX_DEGREE: usize = 4; + /// build randomness r, r**2, ... r**(2**BIT_LIMIT) pub fn base_pows_expr(randomness: Expression) -> [Expression;BIT_LIMIT] { std::iter::successors( @@ -47,44 +50,56 @@ impl RandPowRepresent { ) } - /// refere to a binary represent of exponent (like BinaryNumberGadget) + /// refere to a binary represent of exponent (like BinaryNumberGadget), can + /// link another expression so the expr is linked_val * r ** exponent pub fn configure( cb: &mut EVMConstraintBuilder, randomness: Expression, exponent: Expression, + linked_val: Option>, ) -> Self { let bits = BinaryNumberGadget::construct(cb, exponent); let base_pows = Self::base_pows_expr(randomness); - let pow_assembles = [0; BIT_LIMIT].map(|_|cb.query_cell_phase2()); - let mut last_pow_assemble = 1.expr(); - for ((pow_assemble, exp_bit), base_pow) in - pow_assembles - .as_slice() - .iter() - .zip(bits.bits.as_slice().iter().rev()) - .zip(&base_pows) { - cb.require_equal( - "pow_assemble = if exp_bit {pow_assemble.last} else {pow_assemble.last*base_pow} ", - pow_assemble.expr(), - util::select::expr(exp_bit.expr(), last_pow_assemble.clone() * base_pow.clone(), last_pow_assemble.clone()), - ); - last_pow_assemble = pow_assemble.expr(); + let mut pow_assembles = Vec::new(); + let mut pow = linked_val.unwrap_or_else(||1.expr()); + for (n, (base_pow, exp_bit)) in base_pows.into_iter() + .zip(bits.bits.as_slice().iter().rev()).enumerate(){ + + pow = pow * util::select::expr(exp_bit.expr(), base_pow, 1.expr()); + + if pow.degree() > Self::BIT_EXP_MAX_DEGREE { + + let cached_cell = cb.query_cell_phase2(); + cb.require_equal( + "pow_assemble cached current expression", + cached_cell.expr(), + pow.clone(), + ); + + pow = cached_cell.expr(); + pow_assembles.push((cached_cell, n)); + } + } Self { pow_assembles, bits, + pow, } } - pub fn pow(&self) -> Expression {self.pow_assembles.last().expect("BIT_LIMIT is not zero").expr()} + pub fn expr(&self) -> Expression {self.pow.clone()} + + pub fn phase2_cell_cost(&self) -> usize {self.pow_assembles.len()} pub fn assign( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, exponent: usize, - ) -> Result<(), Error> { + linked_value: Option>, + ) -> Result, Error> { assert!(2usize.pow(BIT_LIMIT as u32) > exponent, "exponent ({exponent}) can not exceed bit limit (2**{BIT_LIMIT}-1)"); self.bits.assign(region, offset, exponent)?; let bits : [bool; BIT_LIMIT]= exponent.as_bits(); @@ -95,24 +110,25 @@ impl RandPowRepresent { |val| Some(val.map(|v|v.square())) ).take(BIT_LIMIT); - let mut last_assigned_assemble = Value::known(F::one()); - for ((column, &bit), base_pow) in - self.pow_assembles - .as_slice() - .iter() + let mut pow_cached_i = self.pow_assembles.iter(); + let mut cached_cell = pow_cached_i.next(); + let mut value_should_assigned = linked_value.unwrap_or_else(||Value::known(F::one())); + + for (n, (base_pow, &bit)) in + base_pows .zip(bits.as_slice().iter().rev()) - .zip(base_pows) { - - let assigned_v = if bit { - last_assigned_assemble*base_pow - }else { - last_assigned_assemble - }; - column.assign(region, offset, assigned_v)?; - last_assigned_assemble = assigned_v; + .enumerate(){ + + value_should_assigned = value_should_assigned * (if bit {base_pow} else {Value::known(F::one())}); + if let Some((cell, i)) = cached_cell { + if *i == n { + cell.assign(region, offset, value_should_assigned)?; + cached_cell = pow_cached_i.next(); + } + } } - Ok(()) + Ok(value_should_assigned) } } @@ -310,39 +326,47 @@ impl ModExpInputs { is_input_need_padding.expr(), input_expected - input_bytes_len, 0.expr(), - ) + ), + None, ); // we put correct size in each input word if input is valid // else we just put as most as possible bytes (32) into it // so we finally handle the memory in limited sized (32*3) - let base_pow = RandPow::configure(cb, + let modulus_pow = RandPow::configure(cb, cb.challenges().keccak_input(), - base_len_expected, + modulus_len_expected, + None, ); + + // exp_pow = r**(modulus_len + exp_len) let exp_pow = RandPow::configure(cb, cb.challenges().keccak_input(), exp_len_expected, + Some(modulus_pow.expr()), ); - let modulus_pow = RandPow::configure(cb, + + // base_pow = r**(modulus_len + exp_len + base_len) + let base_pow = RandPow::configure(cb, cb.challenges().keccak_input(), - modulus_len_expected, + base_len_expected, + Some(exp_pow.expr()), ); - let r_pow_base = base_pow.pow(); - let r_pow_exp = r_pow_base.clone() * exp_pow.pow(); - let r_pow_all_vars = r_pow_exp.clone() * modulus_pow.pow(); - cb.require_equal("acc bytes must equal", - input_bytes_acc * padding_pow.pow(), + padding_pow.expr() * input_bytes_acc, rlc_word_rev(&modulus, cb.challenges().keccak_input()) //rlc of base - + r_pow_base * rlc_word_rev(&exp, cb.challenges().keccak_input()) //rlc of exp plus r**base_len - + r_pow_exp * rlc_word_rev(&base, cb.challenges().keccak_input()) //rlc of exp plus r**(base_len + exp_len) - + r_pow_all_vars.clone() * modulus_len.memory_rlc() - + r_pow_all_vars.clone() * r_pow_32 * exp_len.memory_rlc() - + r_pow_all_vars * r_pow_64 * base_len.memory_rlc() + + modulus_pow.expr() * rlc_word_rev(&exp, cb.challenges().keccak_input()) //rlc of exp plus r**base_len + + exp_pow.expr() * rlc_word_rev(&base, cb.challenges().keccak_input()) //rlc of exp plus r**(base_len + exp_len) + + base_pow.expr() * modulus_len.memory_rlc() + + base_pow.expr() * r_pow_32 * exp_len.memory_rlc() + + base_pow.expr() * r_pow_64 * base_len.memory_rlc() ); + // println!("phase 2 cell used {}", + // padding_pow.phase2_cell_cost() + [&modulus_pow, &exp_pow, &base_pow].iter().map(|pw|pw.phase2_cell_cost()).sum::() + // ); + Self { base_len, modulus_len, @@ -375,8 +399,16 @@ impl ModExpInputs { len_represent.assign(region, offset, len)?; } - for (len, pow) in lens.iter().zip([&self.base_pow, &self.exp_pow, &self.modulus_pow]){ - pow.assign(region, offset, if input_valid {len.as_usize()} else {SIZE_LIMIT})?; + let mut linked_v = None; + for (len, pow) in lens.iter().zip([&self.base_pow, &self.exp_pow, &self.modulus_pow]).rev(){ + let assigned = pow.assign( + region, + offset, + if input_valid {len.as_usize()} else {SIZE_LIMIT}, + linked_v, + )?; + + linked_v = Some(assigned); } for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]){ @@ -394,6 +426,7 @@ impl ModExpInputs { self.padding_pow.assign(region, offset, if input_len < expected_len {expected_len - input_len} else {0}, + None, )?; Ok(()) @@ -547,7 +580,7 @@ impl ExecutionGadget for ModExpGadget { if let Some(PrecompileAuxData::Modexp(data)) = &step.aux_data { -// println!("exp data: {:?}", data); + println!("exp data: {:?}", data); self.input.assign(region, offset, (data.valid, data.input_lens, data.inputs), @@ -657,6 +690,60 @@ mod test { address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() }, + PrecompileCallArgs { + name: "modexp success", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x3) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x2) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0x66.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp success", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x3) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x2) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0x65.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, ] }; } From d01062567514ee1913cf94f07ebc461c78dae188 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sun, 2 Jul 2023 09:15:53 +0800 Subject: [PATCH 16/57] add invalid detection and test --- bus-mapping/src/precompile.rs | 47 +++++++++++++------ .../execution/precompiles/modexp.rs | 40 ++++++++++++++-- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 3dc5929cc9..754014253d 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -18,7 +18,20 @@ pub(crate) fn execute_precompiled(address: &Address, input: &[u8], gas: u64) -> }; match precompile_fn(input, gas) { - Ok((gas_cost, return_value)) => (return_value, gas_cost), + Ok((gas_cost, return_value)) => { + match PrecompileCalls::from(address.0[19]){ + // FIXME: override the behavior of invalid input + PrecompileCalls::Modexp => { + let (input_valid, _) = ModExpAuxData::check_input(input); + if input_valid { + (return_value, gas_cost) + }else { + (vec![], gas_cost) + } + }, + _ => (return_value, gas_cost), + } + }, Err(_) => (vec![], gas), } } @@ -195,26 +208,30 @@ impl ModExpAuxData { value_bytes } - /// Create a new instance of modexp auxiliary data. - pub fn new(mut mem_input: Vec, output: Vec) -> Self { - - let input_memory = mem_input.clone(); - let output_memory = output.clone(); + /// check input + pub fn check_input(input: &[u8]) -> (bool, [Word;3]){ + let mut i = input.chunks(32); + let base_len = Word::from_big_endian(i.next().unwrap_or(&[])); + let exp_len = Word::from_big_endian(i.next().unwrap_or(&[])); + let modulus_len = Word::from_big_endian(i.next().unwrap_or(&[])); - // extended to minimum size (3x U256) - if mem_input.len() < 96 { - mem_input.resize(96, 0); - } - let base_len = Word::from_big_endian(&mem_input[..32]); - let exp_len = Word::from_big_endian(&mem_input[32..64]); - let modulus_len = Word::from_big_endian(&mem_input[64..96]); - let limit = Word::from(MODEXP_SIZE_LIMIT); let input_valid = base_len <= limit && exp_len <= limit && modulus_len <= limit; - + + (input_valid, [base_len, exp_len, modulus_len]) + } + + /// Create a new instance of modexp auxiliary data. + pub fn new(mut mem_input: Vec, output: Vec) -> Self { + + let input_memory = mem_input.clone(); + let output_memory = output.clone(); + + let (input_valid, [base_len, exp_len, modulus_len]) = Self::check_input(&mem_input); + let base_mem_len = if input_valid {base_len.as_usize()} else {MODEXP_SIZE_LIMIT}; let exp_mem_len = if input_valid {exp_len.as_usize()} else {MODEXP_SIZE_LIMIT}; let modulus_mem_len = if input_valid {modulus_len.as_usize()} else {MODEXP_SIZE_LIMIT}; diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index e1c7a1f496..0154f2d5a4 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -718,7 +718,7 @@ mod test { ..Default::default() }, PrecompileCallArgs { - name: "modexp success", + name: "modexp success with padding 0", setup_code: bytecode! { // Base size PUSH1(0x1) @@ -746,15 +746,47 @@ mod test { }, ] }; + + static ref TEST_INVALID_VECTOR: Vec = { + vec![ + PrecompileCallArgs { + name: "modexp length too large invalid", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x1) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x21) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0x63.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + ] + }; } #[test] fn precompile_modexp_test() { let call_kinds = vec![ -// OpcodeId::CALL, + OpcodeId::CALL, OpcodeId::STATICCALL, -// OpcodeId::DELEGATECALL, -// OpcodeId::CALLCODE, + OpcodeId::DELEGATECALL, + OpcodeId::CALLCODE, ]; for (test_vector, &call_kind) in TEST_VECTOR.iter().cartesian_product(&call_kinds) { From 13ac75c5cc0f761bc400816902ab80682e66e07e Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sun, 2 Jul 2023 22:28:52 +0800 Subject: [PATCH 17/57] wip: induce u256 modexp --- Cargo.lock | 35 +++ zkevm-circuits/Cargo.toml | 1 + zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/precompile_circuit/mod.rs | 5 + .../src/precompile_circuit/modexp.rs | 216 ++++++++++++++++++ 5 files changed, 258 insertions(+) create mode 100644 zkevm-circuits/src/precompile_circuit/mod.rs create mode 100644 zkevm-circuits/src/precompile_circuit/modexp.rs diff --git a/Cargo.lock b/Cargo.lock index 3f0f186efa..617be86560 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2130,6 +2130,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "halo2-gate-generator" +version = "0.1.0" +source = "git+https://github.com/DelphinusLab/halo2gategen.git?branch=main#4fd6b4793cfd590d1cc80ba463599280522f8bec" +dependencies = [ + "ark-std", + "halo2_proofs", + "lazy_static", + "num-bigint", + "rand", + "serde", + "serde_json", + "strum", + "strum_macros", + "subtle", +] + [[package]] name = "halo2-mpt-circuits" version = "0.1.0" @@ -3914,6 +3931,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rmd160-circuits" +version = "0.1.0" +dependencies = [ + "ark-std", + "halo2-gate-generator", + "halo2_proofs", + "lazy_static", + "num-bigint", + "rand", + "serde", + "serde_json", + "strum", + "strum_macros", + "subtle", +] + [[package]] name = "ruint" version = "1.8.0" @@ -5406,6 +5440,7 @@ dependencies = [ "rand_chacha", "rand_xorshift", "rayon", + "rmd160-circuits", "serde", "serde_json", "sha3 0.10.8", diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 3eada67500..bffd6fc68b 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -32,6 +32,7 @@ serde_json = "1.0.78" hash-circuit = { package = "poseidon-circuit", git = "https://github.com/scroll-tech/poseidon-circuit.git", branch = "scroll-dev-0619", features=['short']} #mpt-circuits = { package = "halo2-mpt-circuits", path = "../../mpt-circuit" } +misc-precompiled-circuit = { package = "rmd160-circuits", git = "https://github.com/scroll-tech/misc-precompiled-circuit.git" } halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } halo2-ecc = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index b902b48763..0124ad0925 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -31,6 +31,7 @@ pub mod rlp_circuit_fsm; pub mod sig_circuit; // we don't use this for aggregation //pub mod root_circuit; +pub mod precompile_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; diff --git a/zkevm-circuits/src/precompile_circuit/mod.rs b/zkevm-circuits/src/precompile_circuit/mod.rs new file mode 100644 index 0000000000..f015a66e5c --- /dev/null +++ b/zkevm-circuits/src/precompile_circuit/mod.rs @@ -0,0 +1,5 @@ +//! Precompile circuits +//! + +/// mod exp circuit for U256 integer +pub mod modexp; \ No newline at end of file diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs new file mode 100644 index 0000000000..52232c6ca3 --- /dev/null +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -0,0 +1,216 @@ +use num_bigint::BigUint; +use misc_precompiled_circuit::circuits::range::{ + RangeCheckConfig, + RangeCheckChip, +}; +use misc_precompiled_circuit::value_for_assign; + +use halo2_proofs::{ + halo2curves::bn256::Fr, + circuit::{Chip, Layouter, Region, SimpleFloorPlanner}, + plonk::{ + Advice, Circuit, Column, ConstraintSystem, Error + }, +}; + +use misc_precompiled_circuit::circuits::modexp::{ + ModExpChip, + ModExpConfig, + Number, + Limb, +}; + +/// ! +#[derive(Clone, Debug)] +pub struct HelperChipConfig { + limb: Column +} + +/// ! +#[derive(Clone, Debug)] +pub struct HelperChip { + config: HelperChipConfig +} + +impl Chip for HelperChip { + type Config = HelperChipConfig; + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} + +impl HelperChip { + fn new(config: HelperChipConfig) -> Self { + HelperChip{ + config, + } + } + + fn configure(cs: &mut ConstraintSystem) -> HelperChipConfig { + let limb= cs.advice_column(); + cs.enable_equality(limb); + HelperChipConfig { + limb, + } + } + + fn assign_base( + &self, + _region: &mut Region, + _offset: &mut usize, + base: &BigUint, + ) -> Result, Error> { + Ok(Number::from_bn(base)) + } + + fn assign_exp( + &self, + _region: &mut Region, + _offset: &mut usize, + exp: &BigUint, + ) -> Result, Error> { + Ok(Number::from_bn(exp)) + } + + + + fn assign_modulus( + &self, + _region: &mut Region, + _offset: &mut usize, + modulus: &BigUint, + ) -> Result, Error> { + Ok(Number::from_bn(modulus)) + } + + fn assign_results( + &self, + region: &mut Region, + offset: &mut usize, + result: &BigUint, + ) -> Result, Error> { + let n = Number::from_bn(result); + let mut cells = vec![]; + for i in 0..4 { + let c = region.assign_advice( + || format!("assign input"), + self.config.limb, + *offset + i, + || value_for_assign!(n.limbs[i].value) + )?; + cells.push(Some(c)); + *offset = *offset + 1; + } + let n = Number { + limbs: [ + Limb::new(cells[0].clone(), n.limbs[0].value), + Limb::new(cells[1].clone(), n.limbs[1].value), + Limb::new(cells[2].clone(), n.limbs[2].value), + Limb::new(cells[3].clone(), n.limbs[3].value), + ] + }; + Ok(n) + } + +} + +#[derive(Clone, Debug, Default)] +struct TestCircuit { + base: BigUint, + exp: BigUint, + modulus: BigUint, +} + +#[derive(Clone, Debug)] +struct TestConfig { + modexpconfig: ModExpConfig, + helperconfig: HelperChipConfig, + rangecheckconfig: RangeCheckConfig, +} + +impl Circuit for TestCircuit { + type Config = TestConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let rangecheckconfig = RangeCheckChip::::configure(meta); + Self::Config { + modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), + helperconfig: HelperChip::configure(meta), + rangecheckconfig, + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let modexpchip = ModExpChip::::new(config.clone().modexpconfig); + let helperchip = HelperChip::new(config.clone().helperconfig); + let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); + layouter.assign_region( + || "assign mod mult", + |mut region| { + range_chip.initialize(&mut region)?; + let mut offset = 0; + let base = helperchip.assign_base(&mut region, &mut offset, &self.base)?; + let exp = helperchip.assign_exp(&mut region, &mut offset, &self.exp)?; + let modulus = helperchip.assign_modulus(&mut region, &mut offset, &self.modulus)?; + let bn_rem = self.base.clone().modpow(&self.exp, &self.modulus); + let result = helperchip.assign_results(&mut region, &mut offset, &bn_rem)?; + let rem = modexpchip.mod_exp(&mut region, &mut range_chip, &mut offset, &base, &exp, &modulus)?; + for i in 0..4 { + //println!("rem is {:?}, result is {:?}", &rem.limbs[i].value, &result.limbs[i].value); + //println!("rem cell is {:?}, result cell is {:?}", &rem.limbs[i].cell, &result.limbs[i].cell); + region.constrain_equal( + rem.limbs[i].clone().cell.unwrap().cell(), + result.limbs[i].clone().cell.unwrap().cell() + )?; + } + Ok(()) + } + )?; + Ok(()) + } +} + + +#[test] +fn test_modexp_circuit_00() { + let base = BigUint::from(1u128 << 100); + let exp = BigUint::from(2u128 << 100); + let modulus = BigUint::from(7u128); + let test_circuit = TestCircuit {base, exp, modulus} ; + let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); +} + +#[test] +fn test_modexp_circuit_01() { + let base = BigUint::from(1u128); + let exp = BigUint::from(2u128); + let modulus = BigUint::from(7u128); + let test_circuit = TestCircuit {base, exp, modulus} ; + let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); +} +#[test] +fn test_modexp_circuit_02() { + let base = BigUint::from(2u128); + let exp = BigUint::from(2u128); + let modulus = BigUint::from(7u128); + let test_circuit = TestCircuit {base, exp, modulus} ; + let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); +} \ No newline at end of file From a39254ee0b153f123e3bfb5f2425ea251ef4892e Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sun, 2 Jul 2023 22:29:02 +0800 Subject: [PATCH 18/57] modexp event --- bus-mapping/src/circuit_input_builder.rs | 2 +- .../src/circuit_input_builder/block.rs | 8 ++++++- .../src/circuit_input_builder/execution.rs | 24 +++++++++++++++++++ .../circuit_input_builder/input_state_ref.rs | 7 +++++- .../src/evm/opcodes/precompiles/mod.rs | 11 ++++++++- zkevm-circuits/src/witness/block.rs | 5 +++- 6 files changed, 52 insertions(+), 5 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index ba24e6dd41..5608d744c1 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -36,7 +36,7 @@ use ethers_core::{ }; use ethers_providers::JsonRpcClient; pub use execution::{ - CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, + CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, ModExpEvent, }; use hex::decode_to_slice; diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index 05473392ee..2d5bc9a814 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -1,7 +1,7 @@ //! Block-related utility module use super::{ - execution::ExecState, transaction::Transaction, CircuitsParams, CopyEvent, ExecStep, ExpEvent, + execution::ExecState, transaction::Transaction, CircuitsParams, CopyEvent, ExecStep, ExpEvent, ModExpEvent, }; use crate::{ operation::{OperationContainer, RWCounter}, @@ -155,6 +155,8 @@ pub struct Block { pub sha3_inputs: Vec>, /// IO to/from the precompile Ecrecover calls. pub ecrecover_events: Vec, + /// Params for the precompile Modexp calls. + pub modexp_events: Vec, /// Block-wise steps pub block_steps: BlockSteps, /// Exponentiation events in the block. @@ -252,4 +254,8 @@ impl Block { pub fn add_ecrecover_event(&mut self, event: SignData) { self.ecrecover_events.push(event); } + /// Push an modexp event to the block. + pub fn add_modexp_event(&mut self, event: ModExpEvent) { + self.modexp_events.push(event); + } } diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index 09bad333a9..b7e5b20b49 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -459,3 +459,27 @@ impl Default for ExpEvent { } } } + +/// Event representating an exponentiation `a ^ b == d (mod m)` in precompile modexp. +#[derive(Clone, Debug)] +pub struct ModExpEvent { + /// Base `a` for the exponentiation. + pub base: Word, + /// Exponent `b` for the exponentiation. + pub exponent: Word, + /// Modulus `m` + pub modulus: Word, + /// Mod exponentiation result. + pub result: Word, +} + +impl Default for ModExpEvent { + fn default() -> Self { + Self { + modulus: 1.into(), + base: Default::default(), + exponent: Default::default(), + result: Default::default(), + } + } +} \ No newline at end of file diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index 3f137727b3..f008f89591 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -2,7 +2,7 @@ use super::{ get_call_memory_offset_length, get_create_init_code, Block, BlockContext, Call, CallContext, - CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, Transaction, + CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, ModExpEvent, Transaction, TransactionContext, }; #[cfg(feature = "scroll")] @@ -1292,6 +1292,11 @@ impl<'a> CircuitInputStateRef<'a> { self.block.add_exp_event(event) } + /// Push a modexp event to the state. + pub fn push_modexp(&mut self, event: ModExpEvent) { + self.block.add_modexp_event(event) + } + /// Push an ecrecover event to the state. pub fn push_ecrecover(&mut self, event: SignData) { self.block.add_ecrecover_event(event) diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index 641a061ec3..abdb6a2357 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -5,7 +5,7 @@ use eth_types::{ use halo2_proofs::halo2curves::secp256k1::Fq; use crate::{ - circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, + circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep, ModExpEvent}, operation::CallContextField, precompile::{EcrecoverAuxData, ModExpAuxData, PrecompileAuxData, PrecompileCalls}, Error, @@ -64,6 +64,15 @@ pub fn gen_associated_ops( input_bytes.unwrap_or_default(), output_bytes.unwrap_or_default(), ); + if aux_data.valid { + let event = ModExpEvent { + base: Word::from_big_endian(&aux_data.inputs[0]), + exponent: Word::from_big_endian(&aux_data.inputs[1]), + modulus: Word::from_big_endian(&aux_data.inputs[2]), + result: Word::from_big_endian(&aux_data.output), + }; + state.push_modexp(event); + } exec_step.aux_data = Some(PrecompileAuxData::Modexp(aux_data)); } diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 54f8457d38..1a0a4b7858 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -6,7 +6,7 @@ use crate::evm_circuit::{detect_fixed_table_tags, EvmCircuit}; use crate::{evm_circuit::util::rlc, table::BlockContextFieldTag, util::SubCircuit}; use bus_mapping::{ - circuit_input_builder::{self, CircuitsParams, CopyEvent, ExpEvent}, + circuit_input_builder::{self, CircuitsParams, CopyEvent, ExpEvent, ModExpEvent}, Error, }; use eth_types::{sign_types::SignData, Address, Field, ToLittleEndian, ToScalar, Word, U256}; @@ -52,6 +52,8 @@ pub struct Block { pub sha3_inputs: Vec>, /// IO to/from precompile Ecrecover calls. pub ecrecover_events: Vec, + /// Params for the precompile Modexp calls. + pub modexp_events: Vec, /// State root of the previous block pub prev_state_root: Word, // TODO: Make this H256 /// Withdraw root @@ -444,6 +446,7 @@ pub fn block_convert( exp_events: block.exp_events.clone(), sha3_inputs: block.sha3_inputs.clone(), ecrecover_events: block.ecrecover_events.clone(), + modexp_events: block.modexp_events.clone(), circuits_params: CircuitsParams { max_rws, ..block.circuits_params From 01d556773da72bdb9a0e5f32c908a2de9458cec0 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Mon, 3 Jul 2023 11:29:43 +0800 Subject: [PATCH 19/57] wip: induce modexp table to evm --- .cargo/config.toml | 3 + zkevm-circuits/src/evm_circuit.rs | 11 +- zkevm-circuits/src/evm_circuit/execution.rs | 1 + .../src/precompile_circuit/modexp.rs | 5 +- zkevm-circuits/src/table.rs | 174 ++++++++++++++++-- 5 files changed, 174 insertions(+), 20 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index bd0f7448f4..72a6628385 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,3 +4,6 @@ rustflags = ["-C", "link-args=-framework CoreFoundation -framework Security"] git-fetch-with-cli = true [env] RUST_MIN_STACK = "16777216" + +[patch."https://github.com/scroll-tech/misc-precompiled-circuit.git"] +rmd160-circuits = { path='/home/combray/Code/zkevm/misc-precompiled-circuit' } \ No newline at end of file diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 8c77d6b168..09470fe5e8 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -22,7 +22,7 @@ use crate::{ evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, RwTable, - SigTable, TxTable, + SigTable, TxTable, ModExpTable, }, util::{SubCircuit, SubCircuitConfig}, }; @@ -49,6 +49,7 @@ pub struct EvmCircuitConfig { keccak_table: KeccakTable, exp_table: ExpTable, sig_table: SigTable, + modexp_table: ModExpTable, } /// Circuit configuration arguments @@ -71,6 +72,8 @@ pub struct EvmCircuitConfigArgs { pub exp_table: ExpTable, /// SigTable pub sig_table: SigTable, + /// ModExpTable + pub modexp_table: ModExpTable, } /// Circuit exported cells after synthesis, used for subcircuit @@ -97,6 +100,7 @@ impl SubCircuitConfig for EvmCircuitConfig { keccak_table, exp_table, sig_table, + modexp_table, }: Self::ConfigArgs, ) -> Self { let fixed_table = [(); 4].map(|_| meta.fixed_column()); @@ -114,6 +118,7 @@ impl SubCircuitConfig for EvmCircuitConfig { &keccak_table, &exp_table, &sig_table, + &modexp_table, )); meta.annotate_lookup_any_column(byte_table[0], || "byte_range"); @@ -128,6 +133,7 @@ impl SubCircuitConfig for EvmCircuitConfig { keccak_table.annotate_columns(meta); exp_table.annotate_columns(meta); sig_table.annotate_columns(meta); + modexp_table.annotate_columns(meta); Self { fixed_table, @@ -141,6 +147,7 @@ impl SubCircuitConfig for EvmCircuitConfig { keccak_table, exp_table, sig_table, + modexp_table, } } } @@ -418,6 +425,7 @@ impl Circuit for EvmCircuit { let keccak_table = KeccakTable::construct(meta); let exp_table = ExpTable::construct(meta); let sig_table = SigTable::construct(meta); + let modexp_table = ModExpTable::construct(meta); ( EvmCircuitConfig::new( meta, @@ -431,6 +439,7 @@ impl Circuit for EvmCircuit { keccak_table, exp_table, sig_table, + modexp_table, }, ), challenges, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index caf3323e1d..bbcf9d1cb8 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -377,6 +377,7 @@ impl ExecutionConfig { keccak_table: &dyn LookupTable, exp_table: &dyn LookupTable, sig_table: &dyn LookupTable, + modexp_table: &dyn LookupTable, ) -> Self { let mut instrument = Instrument::default(); let q_usable = meta.complex_selector(); diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index 52232c6ca3..7a62d83869 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -185,7 +185,7 @@ impl Circuit for TestCircuit { } } - +/* #[test] fn test_modexp_circuit_00() { let base = BigUint::from(1u128 << 100); @@ -213,4 +213,5 @@ fn test_modexp_circuit_02() { let test_circuit = TestCircuit {base, exp, modulus} ; let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index bdc030308c..463d7ce956 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2229,34 +2229,174 @@ impl LookupTable for SigTable { /// Lookup table embedded in the modexp circuit for precompile. #[derive(Clone, Copy, Debug)] pub struct ModExpTable { - /// Is enabled - pub q_enable: Column, - /// RLC encoded of the 4 limbs-represent (notice not bytes) of U256 base in modexp circuit - pub base_rlc: Column, - /// RLC encoded of the U256 exponent - pub exp_rlc: Column, - /// RLC encoded of the U256 modulus - pub modulus_rlc: Column, - /// RLC encoded of the U256 results - pub result_rlc: Column, + /// Use for indicate beginning of a limbs group + pub q_head: Column, + /// base represented by limbs + pub base: Column, + /// exp represented by limbs + pub exp: Column, + /// modulus represented by limbs + pub modulus: Column, + /// result represented by limbs + pub result: Column, } impl ModExpTable { /// Construct the modexp table. pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { - q_enable: meta.fixed_column(), - base_rlc: meta.advice_column_in(SecondPhase), - exp_rlc: meta.advice_column_in(SecondPhase), - modulus_rlc: meta.advice_column_in(SecondPhase), - result_rlc: meta.advice_column_in(SecondPhase), + q_head: meta.fixed_column(), + base: meta.advice_column(), + exp: meta.advice_column(), + modulus: meta.advice_column(), + result: meta.advice_column(), } } + fn split_u256_108bit_limbs(word: &Word) -> [u128;3]{ + let bit108 = 1u128<<108; + let (next, limb0) = word.div_mod(U256::from(bit108)); + let (limb1, limb2) = next.div_mod(U256::from(bit108)); + [limb0.as_u128(), limb1.as_u128(), limb2.as_u128()] + } + + fn native_u256(word: &Word) -> F { + let mut bytes = [0u8; 64]; + word.to_little_endian(&mut bytes[..32]); + F::from_bytes_wide(&bytes) + } + /// Get assignments to the modexp table. Meant to be used for dev purposes. pub fn dev_load( - _challenges: &Challenges>, - ) { + &self, + layouter: &mut impl Layouter, + block: &Block, + ) -> Result<(), Error> { + layouter.assign_region( + || "modexp table", + |mut region|{ + + let event_limit = block.circuits_params.max_keccak_rows / 9300; + let exp_events = &block.modexp_events; + assert!(exp_events.len() <= event_limit, + "not enough rows for modexp circuit, expected {}, limit {}", + exp_events.len(), + event_limit, + ); + + let mut offset = 0usize; + + for (n, event) in exp_events.iter() + .chain(std::iter::repeat(&Default::default())) + .take(event_limit).enumerate(){ + + for i in 0..4 { + region.assign_fixed( + || format!("modexp table head {}", offset+i), + self.q_head, + offset + i, + || Value::known(if i == 0 {F::one()} else {F::zero()}), + )?; + } + + let base_limbs = Self::split_u256_108bit_limbs(&event.base); + let exp_limbs = Self::split_u256_108bit_limbs(&event.exponent); + let modulus_limbs = Self::split_u256_108bit_limbs(&event.modulus); + let result_limbs = Self::split_u256_108bit_limbs(&event.result); + + for i in 0..3 { + for (limbs, &col) in + [base_limbs, exp_limbs, modulus_limbs, result_limbs] + .zip([&self.base, &self.exp, &self.modulus, &self.result]){ + + region.assign_advice( + || format!("modexp table limb row {}", offset+i), + col, + offset + i, + || Value::known(F::from_u128(limbs[i])), + )?; + } + } + + // native is not used by lookup (and in fact it can be omitted in dev) + for (word, &col) in [&event.base, &event.exponent, &event.modulus, &event.result] + .zip([&self.base, &self.exp, &self.modulus, &self.result]){ + + region.assign_advice( + || format!("modexp table native row {}", offset+3), + col, + offset + 3, + || Value::::known(Self::native_u256(word)), + )?; + } + + offset += 4; + } + + // fill last totally 0 row + region.assign_fixed( + || "modexp table blank row", + self.q_head, + offset, + || Value::known(F::zero()), + )?; + for &col in [&self.base, &self.exp, &self.modulus, &self.result]{ + region.assign_advice( + || "modexp table blank row {}", + col, + offset, + || Value::known(F::zero()), + )?; + } + + Ok(()) + }, + ) + } +} + + +impl LookupTable for ModExpTable { + fn columns(&self) -> Vec> { + vec![ + self.q_head.into(), + self.base.into(), + self.exp.into(), + self.modulus.into(), + self.result.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("is_head"), + String::from("base"), + String::from("exp"), + String::from("modulus"), + String::from("result"), + ] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + // ignore the is_valid field as the EVM circuit's use-case (Ecrecover precompile) does + // not care whether the signature is valid or not. It only cares about the recovered + // address. + meta.query_fixed(self.q_head, Rotation::cur()), + meta.query_advice(self.base, Rotation::cur()), + meta.query_advice(self.exp, Rotation::cur()), + meta.query_advice(self.modulus, Rotation::cur()), + meta.query_advice(self.result, Rotation::cur()), + meta.query_advice(self.base, Rotation::next()), + meta.query_advice(self.exp, Rotation::next()), + meta.query_advice(self.modulus, Rotation::next()), + meta.query_advice(self.result, Rotation::next()), + meta.query_advice(self.base, Rotation(2)), + meta.query_advice(self.exp, Rotation(2)), + meta.query_advice(self.modulus, Rotation(2)), + meta.query_advice(self.result, Rotation(2)), + ] } } From 8fd7ad6f14b492a81644a491236ada3691ca1b59 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Tue, 4 Jul 2023 17:09:38 +0800 Subject: [PATCH 20/57] fix super circuit --- zkevm-circuits/src/super_circuit.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 51294ad647..3eb49f0346 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -82,7 +82,7 @@ use crate::{ state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, PoseidonTable, - RlpFsmRlpTable as RlpTable, RwTable, TxTable, + RlpFsmRlpTable as RlpTable, RwTable, TxTable, ModExpTable, }, }; @@ -192,6 +192,8 @@ impl SubCircuitConfig for SuperCircuitConfig { log_circuit_info(meta, "keccak table"); let sig_table = SigTable::construct(meta); log_circuit_info(meta, "sig table"); + let modexp_table = ModExpTable::construct(meta); + log_circuit_info(meta, "modexp table"); let keccak_circuit = KeccakCircuitConfig::new( meta, @@ -326,6 +328,7 @@ impl SubCircuitConfig for SuperCircuitConfig { keccak_table, exp_table, sig_table, + modexp_table, }, ); log_circuit_info(meta, "evm circuit"); From bb26c501c2465fbb2826cfe76cb48bcb92dc866f Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 5 Jul 2023 08:53:13 +0800 Subject: [PATCH 21/57] induce modexp table done --- zkevm-circuits/src/evm_circuit.rs | 1 + zkevm-circuits/src/evm_circuit/execution.rs | 3 ++ zkevm-circuits/src/evm_circuit/table.rs | 28 +++++++++++++++++++ .../evm_circuit/util/constraint_builder.rs | 19 +++++++++++++ zkevm-circuits/src/table.rs | 4 +-- 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 09470fe5e8..219578da6c 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -487,6 +487,7 @@ impl Circuit for EvmCircuit { config .sig_table .dev_load(&mut layouter, block, &challenges)?; + config.modexp_table.dev_load(&mut layouter, block)?; self.synthesize_sub(&config, &challenges, &mut layouter) } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index bbcf9d1cb8..70838830db 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -652,6 +652,7 @@ impl ExecutionConfig { keccak_table, exp_table, sig_table, + modexp_table, &challenges, &cell_manager, ); @@ -908,6 +909,7 @@ impl ExecutionConfig { keccak_table: &dyn LookupTable, exp_table: &dyn LookupTable, sig_table: &dyn LookupTable, + modexp_table: &dyn LookupTable, challenges: &Challenges>, cell_manager: &CellManager, ) { @@ -925,6 +927,7 @@ impl ExecutionConfig { Table::Keccak => keccak_table, Table::Exp => exp_table, Table::Sig => sig_table, + Table::ModExp => modexp_table, } .table_exprs(meta); vec![( diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index facb26f493..6ac57a8c24 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -144,6 +144,7 @@ pub(crate) enum Table { Keccak, Exp, Sig, + ModExp, } #[derive(Clone, Debug)] @@ -300,6 +301,12 @@ pub(crate) enum Lookup { sig_s_rlc: Expression, recovered_addr: Expression, }, + ModExpTable { + base_limbs: [Expression; 3], + exp_limbs: [Expression; 3], + modulus_limbs: [Expression; 3], + result_limbs: [Expression; 3], + }, /// Conditional lookup enabled by the first element. Conditional(Expression, Box>), } @@ -320,6 +327,7 @@ impl Lookup { Self::KeccakTable { .. } => Table::Keccak, Self::ExpTable { .. } => Table::Exp, Self::SigTable { .. } => Table::Sig, + Self::ModExpTable { .. } => Table::ModExp, Self::Conditional(_, lookup) => lookup.table(), } } @@ -456,6 +464,26 @@ impl Lookup { sig_s_rlc.clone(), recovered_addr.clone(), ], + Self::ModExpTable { + base_limbs, + exp_limbs, + modulus_limbs, + result_limbs + } => vec![ + 1.expr(), // q_head + base_limbs[0].clone(), + exp_limbs[0].clone(), + modulus_limbs[0].clone(), + result_limbs[0].clone(), + base_limbs[1].clone(), + exp_limbs[1].clone(), + modulus_limbs[1].clone(), + result_limbs[1].clone(), + base_limbs[2].clone(), + exp_limbs[2].clone(), + modulus_limbs[2].clone(), + result_limbs[2].clone(), + ], Self::Conditional(condition, lookup) => lookup .input_exprs() .into_iter() diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 0aac10fb68..a9e17e9ac3 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1392,6 +1392,25 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ); } + // ModExp table + pub(crate) fn modexp_table_lookup( + &mut self, + base_limbs: [Expression; 3], + exp_limbs: [Expression; 3], + modulus_limbs: [Expression; 3], + result_limbs: [Expression; 3], + ) { + self.add_lookup( + "u256 exponentiation modulus lookup", + Lookup::ModExpTable { + base_limbs, + exp_limbs, + modulus_limbs, + result_limbs + }, + ); + } + // Validation pub(crate) fn validate_degree(&self, degree: usize, name: &'static str) { diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 96fdea5060..51fc9c6c39 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2287,9 +2287,9 @@ impl ModExpTable { let mut offset = 0usize; - for (n, event) in exp_events.iter() + for event in exp_events.iter() .chain(std::iter::repeat(&Default::default())) - .take(event_limit).enumerate(){ + .take(event_limit){ for i in 0..4 { region.assign_fixed( From 71e65a4f1ee5a0d555c5190347963839d3258930 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 5 Jul 2023 14:05:51 +0800 Subject: [PATCH 22/57] regression pass --- bus-mapping/src/precompile.rs | 2 +- zkevm-circuits/src/evm_circuit/execution.rs | 3 ++- zkevm-circuits/src/evm_circuit/param.rs | 5 +++++ .../src/evm_circuit/util/instrumentation.rs | 4 ++++ .../src/evm_circuit/util/precompile_gadget.rs | 4 ++-- zkevm-circuits/src/table.rs | 16 ++++++++++------ 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 10b2ef4d2d..08f157f251 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -128,7 +128,7 @@ impl PrecompileCalls { match self { Self::Ecrecover | Self::Bn128Add => Some(128), Self::Bn128Mul => Some(96), - Self::Modexp => Some(MODEXP_INPUT_LIMIT), + //Self::Modexp => Some(MODEXP_INPUT_LIMIT), _ => None, } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 79bcec2ee0..7dc34162ad 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -3,7 +3,7 @@ use super::{ BLOCK_TABLE_LOOKUPS, BYTECODE_TABLE_LOOKUPS, COPY_TABLE_LOOKUPS, EXP_TABLE_LOOKUPS, FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, - TX_TABLE_LOOKUPS, + TX_TABLE_LOOKUPS, MODEXP_TABLE_LOOKUPS, }, util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, EvmCircuitExports, @@ -1233,6 +1233,7 @@ impl ExecutionConfig { ("EVM_lookup_keccak", KECCAK_TABLE_LOOKUPS), ("EVM_lookup_exp", EXP_TABLE_LOOKUPS), ("EVM_lookup_sig", SIG_TABLE_LOOKUPS), + ("EVM_lookup_modexp", MODEXP_TABLE_LOOKUPS), ("EVM_lookup_pow_of_rand", POW_OF_RAND_TABLE_LOOKUPS), ("EVM_adv_phase2", N_PHASE2_COLUMNS), ("EVM_copy", N_COPY_COLUMNS), diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 643d1fe991..f0ce185f94 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -39,6 +39,7 @@ pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS + KECCAK_TABLE_LOOKUPS + EXP_TABLE_LOOKUPS + SIG_TABLE_LOOKUPS + + MODEXP_TABLE_LOOKUPS + POW_OF_RAND_TABLE_LOOKUPS; /// Lookups done per row. @@ -52,6 +53,7 @@ pub(crate) const LOOKUP_CONFIG: &[(Table, usize)] = &[ (Table::Keccak, KECCAK_TABLE_LOOKUPS), (Table::Exp, EXP_TABLE_LOOKUPS), (Table::Sig, SIG_TABLE_LOOKUPS), + (Table::ModExp, MODEXP_TABLE_LOOKUPS), (Table::PowOfRand, POW_OF_RAND_TABLE_LOOKUPS), ]; @@ -82,6 +84,9 @@ pub const EXP_TABLE_LOOKUPS: usize = 1; /// Sig Table lookups done in EVMCircuit pub const SIG_TABLE_LOOKUPS: usize = 1; +/// ModExp Table lookups done in EVMCircuit +pub const MODEXP_TABLE_LOOKUPS: usize = 1; + /// Power of Randomness lookups done from EVM Circuit. pub const POW_OF_RAND_TABLE_LOOKUPS: usize = 1; diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index a93239d4c7..0e9037da94 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -106,6 +106,9 @@ impl Instrument { CellType::Lookup(Table::Sig) => { report.sig_table = data_entry; } + CellType::Lookup(Table::ModExp) => { + report.modexp_table = data_entry; + } CellType::Lookup(Table::PowOfRand) => { report.pow_of_rand_table = data_entry; } @@ -136,6 +139,7 @@ pub(crate) struct ExecStateReport { pub(crate) keccak_table: StateReportRow, pub(crate) exp_table: StateReportRow, pub(crate) sig_table: StateReportRow, + pub(crate) modexp_table: StateReportRow, pub(crate) pow_of_rand_table: StateReportRow, } diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index d8491b8a5c..bff783ab7c 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -79,7 +79,7 @@ impl PrecompileGadget { }); cb.condition(address.value_equals(PrecompileCalls::Ecrecover), |cb| { - cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { +/* cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { let (recovered, msg_hash_rlc, sig_v_rlc, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( cb.query_bool(), cb.query_cell_phase2(), @@ -114,7 +114,7 @@ impl PrecompileGadget { cb.condition(not::expr(recovered.expr()), |cb| { cb.require_zero("output bytes == 0", output_bytes_rlc.expr()); }); - }); + });*/ }); cb.condition(address.value_equals(PrecompileCalls::Sha256), |cb| { diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 609545e067..8221019f16 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2280,13 +2280,17 @@ impl ModExpTable { || "modexp table", |mut region|{ - let event_limit = block.circuits_params.max_keccak_rows / 9300; + let mut event_limit = block.circuits_params.max_keccak_rows / 9300; let exp_events = &block.modexp_events; - assert!(exp_events.len() <= event_limit, - "not enough rows for modexp circuit, expected {}, limit {}", - exp_events.len(), - event_limit, - ); + if exp_events.len() <= event_limit { + // only warn in dev_load + log::warn!( + "not enough rows for modexp circuit, expected {}, limit {}", + exp_events.len(), + event_limit, + ); + event_limit = exp_events.len().max(5); + } let mut offset = 0usize; From e927690714c7773f803ef490147bec41408c4c61 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 5 Jul 2023 22:44:19 +0800 Subject: [PATCH 23/57] modexp circuit done --- .../src/precompile_circuit/modexp.rs | 390 ++++++++++-------- zkevm-circuits/src/table.rs | 73 ++-- 2 files changed, 264 insertions(+), 199 deletions(-) diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index 7a62d83869..7c3a0000f4 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -1,217 +1,277 @@ -use num_bigint::BigUint; -use misc_precompiled_circuit::circuits::range::{ - RangeCheckConfig, - RangeCheckChip, -}; -use misc_precompiled_circuit::value_for_assign; use halo2_proofs::{ - halo2curves::bn256::Fr, - circuit::{Chip, Layouter, Region, SimpleFloorPlanner}, + circuit::{Layouter, Region, Value}, plonk::{ - Advice, Circuit, Column, ConstraintSystem, Error + Advice, Column, ConstraintSystem, Error }, }; -use misc_precompiled_circuit::circuits::modexp::{ - ModExpChip, - ModExpConfig, - Number, - Limb, +use eth_types::{Field, Word}; +use bus_mapping::circuit_input_builder::ModExpEvent; +use crate::{ + table::ModExpTable, + util::{Challenges, SubCircuit, SubCircuitConfig}, + witness, }; -/// ! -#[derive(Clone, Debug)] -pub struct HelperChipConfig { - limb: Column -} +//use misc_precompiled_circuit::value_for_assign; +use misc_precompiled_circuit::circuits::{ + range::{ + RangeCheckConfig, + RangeCheckChip, + }, + modexp::{ + ModExpChip, + ModExpConfig, + Number, + Limb, + }, +}; -/// ! #[derive(Clone, Debug)] -pub struct HelperChip { - config: HelperChipConfig +struct ModExpCircuitConfig { + modexp_config: ModExpConfig, + rangecheck_config: RangeCheckConfig, + modexp_table: ModExpTable, } -impl Chip for HelperChip { - type Config = HelperChipConfig; - type Loaded = (); +impl SubCircuitConfig for ModExpCircuitConfig { + type ConfigArgs = ModExpTable; - fn config(&self) -> &Self::Config { - &self.config - } - - fn loaded(&self) -> &Self::Loaded { - &() - } -} - -impl HelperChip { - fn new(config: HelperChipConfig) -> Self { - HelperChip{ - config, - } - } - - fn configure(cs: &mut ConstraintSystem) -> HelperChipConfig { - let limb= cs.advice_column(); - cs.enable_equality(limb); - HelperChipConfig { - limb, + /// Return a new ModExpCircuitConfig + fn new( + meta: &mut ConstraintSystem, + modexp_table: Self::ConfigArgs, + ) -> Self { + let rangecheck_config = RangeCheckChip::configure(meta); + let modexp_config = ModExpChip::configure(meta, &rangecheck_config); + Self { + rangecheck_config, + modexp_config, + modexp_table, } } +} - fn assign_base( - &self, - _region: &mut Region, - _offset: &mut usize, - base: &BigUint, - ) -> Result, Error> { - Ok(Number::from_bn(base)) - } +impl ModExpCircuitConfig { - fn assign_exp( + pub(crate) fn assign_group( &self, - _region: &mut Region, - _offset: &mut usize, - exp: &BigUint, - ) -> Result, Error> { - Ok(Number::from_bn(exp)) - } + region: &mut Region, + table_offset: usize, + mut calc_offset: usize, + event: &ModExpEvent, + modexp_chip: &ModExpChip, + range_check_chip: &mut RangeCheckChip, + ) -> Result { + + let base = self.assign_value(region, table_offset, self.modexp_table.base, &event.base)?; + let exp = self.assign_value(region, table_offset, self.modexp_table.exp, &event.exponent)?; + let modulus = self.assign_value(region, table_offset, self.modexp_table.modulus, &event.modulus)?; + let ret = modexp_chip.mod_exp(region, range_check_chip, &mut calc_offset, &base, &exp, &modulus)?; + for i in 0..4 { + region.assign_fixed( + || format!("modexp table head {}", table_offset+i), + self.modexp_table.q_head, + table_offset + i, + || Value::known(if i == 0 {F::one()} else {F::zero()}), + )?; - fn assign_modulus( - &self, - _region: &mut Region, - _offset: &mut usize, - modulus: &BigUint, - ) -> Result, Error> { - Ok(Number::from_bn(modulus)) + ret.limbs[i].cell.clone() + .expect("should has assigned after modexp") + .copy_advice( + ||"copy to result limbs", + region, + self.modexp_table.result, + table_offset + i + )?; + } + Ok(calc_offset) } - fn assign_results( + fn assign_value( &self, - region: &mut Region, - offset: &mut usize, - result: &BigUint, - ) -> Result, Error> { - let n = Number::from_bn(result); - let mut cells = vec![]; - for i in 0..4 { + region: &mut Region, + offset: usize, + col: Column, + value: &Word, + ) -> Result, Error> { + + let limbs_v = ModExpTable::split_u256_108bit_limbs(value); + let native_v = ModExpTable::native_u256(value); + let mut limbs = Vec::new(); + + for i in 0..3 { + let fv = F::from_u128(limbs_v[i]); let c = region.assign_advice( - || format!("assign input"), - self.config.limb, - *offset + i, - || value_for_assign!(n.limbs[i].value) + || "assign modexp limb", + col, + offset + i, + || Value::known(fv), )?; - cells.push(Some(c)); - *offset = *offset + 1; + limbs.push(Limb::new(Some(c), fv)); } - let n = Number { - limbs: [ - Limb::new(cells[0].clone(), n.limbs[0].value), - Limb::new(cells[1].clone(), n.limbs[1].value), - Limb::new(cells[2].clone(), n.limbs[2].value), - Limb::new(cells[3].clone(), n.limbs[3].value), - ] - }; - Ok(n) + let c = region.assign_advice( + || "assign modexp native", + col, + offset + 3, + || Value::known(native_v), + )?; + limbs.push(Limb::new(Some(c), native_v)); + Ok(Number {limbs: limbs.try_into().expect("just 4 pushes")}) } } + +const MODEXPCONFIG_EACH_CHIP_ROWS : usize = 9291; + #[derive(Clone, Debug, Default)] -struct TestCircuit { - base: BigUint, - exp: BigUint, - modulus: BigUint, -} +struct ModExpCircuit(Vec, std::marker::PhantomData); -#[derive(Clone, Debug)] -struct TestConfig { - modexpconfig: ModExpConfig, - helperconfig: HelperChipConfig, - rangecheckconfig: RangeCheckConfig, -} +impl SubCircuit for ModExpCircuit { + type Config = ModExpCircuitConfig; -impl Circuit for TestCircuit { - type Config = TestConfig; - type FloorPlanner = SimpleFloorPlanner; + fn unusable_rows() -> usize { + // No column queried at more than 4 distinct rotations, so returns 8 as + // minimum unusable rows. + 8 + } - fn without_witnesses(&self) -> Self { - Self::default() + fn new_from_block(block: &witness::Block) -> Self { + + let event_limit = block.circuits_params.max_keccak_rows / MODEXPCONFIG_EACH_CHIP_ROWS; + let mut exp_events = block.modexp_events.clone(); + assert!(exp_events.len() <= event_limit, + "no enough rows for modexp circuit, expected {}, limit {}", + exp_events.len(), + event_limit, + ); + + exp_events.resize(event_limit, Default::default()); + Self(exp_events, Default::default()) } - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let rangecheckconfig = RangeCheckChip::::configure(meta); - Self::Config { - modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), - helperconfig: HelperChip::configure(meta), - rangecheckconfig, - } + fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + ( + block.modexp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS, + (block.modexp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS).max(block.circuits_params.max_keccak_rows), + ) } - fn synthesize( + fn synthesize_sub( &self, - config: Self::Config, - mut layouter: impl Layouter, + config: &Self::Config, + _challenges: &Challenges>, + layouter: &mut impl Layouter, ) -> Result<(), Error> { - let modexpchip = ModExpChip::::new(config.clone().modexpconfig); - let helperchip = HelperChip::new(config.clone().helperconfig); - let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); + + let modexp_chip = ModExpChip::new(config.modexp_config.clone()); + let mut range_chip = RangeCheckChip::new(config.rangecheck_config.clone()); + layouter.assign_region( - || "assign mod mult", + || "modexp circuit", |mut region| { - range_chip.initialize(&mut region)?; - let mut offset = 0; - let base = helperchip.assign_base(&mut region, &mut offset, &self.base)?; - let exp = helperchip.assign_exp(&mut region, &mut offset, &self.exp)?; - let modulus = helperchip.assign_modulus(&mut region, &mut offset, &self.modulus)?; - let bn_rem = self.base.clone().modpow(&self.exp, &self.modulus); - let result = helperchip.assign_results(&mut region, &mut offset, &bn_rem)?; - let rem = modexpchip.mod_exp(&mut region, &mut range_chip, &mut offset, &base, &exp, &modulus)?; - for i in 0..4 { - //println!("rem is {:?}, result is {:?}", &rem.limbs[i].value, &result.limbs[i].value); - //println!("rem cell is {:?}, result cell is {:?}", &rem.limbs[i].cell, &result.limbs[i].cell); - region.constrain_equal( - rem.limbs[i].clone().cell.unwrap().cell(), - result.limbs[i].clone().cell.unwrap().cell() + let mut calc_offset = 0; + for (n, event) in self.0.iter().enumerate(){ + calc_offset = config.assign_group( + &mut region, + n*4, + calc_offset, + event, + &modexp_chip, + &mut range_chip )?; } Ok(()) - } + }, )?; - Ok(()) + + config.modexp_table.fill_blank(layouter) + } } -/* -#[test] -fn test_modexp_circuit_00() { - let base = BigUint::from(1u128 << 100); - let exp = BigUint::from(2u128 << 100); - let modulus = BigUint::from(7u128); - let test_circuit = TestCircuit {base, exp, modulus} ; - let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); -} +#[cfg(test)] +mod test { + use super::*; + use halo2_proofs::dev::MockProver; + use halo2_proofs::{ + halo2curves::bn256::Fr, + circuit::SimpleFloorPlanner, + plonk::{Circuit, ConstraintSystem}, + }; + use crate::util::MockChallenges; + + impl Circuit for ModExpCircuit { + type Config = (ModExpCircuitConfig, MockChallenges); + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let modexp_table = ModExpTable::construct(meta); + let challenge = MockChallenges::construct(meta); + ( + >::new(meta, modexp_table), + challenge, + ) + } + + fn synthesize( + &self, + (config, challenge): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenge.values(&mut layouter); + >::synthesize_sub( + &self, + &config, + &challenges, + &mut layouter + ) + } + } + + + #[test] + fn test_modexp_circuit_00() { + let base = Word::from(1u128); + let exp = Word::from(2u128); + let modulus = Word::from(7u128); + let (_, result) = base.pow(exp).div_mod(modulus); + let event1 = ModExpEvent {base, exponent: exp, modulus, result}; + let test_circuit = ModExpCircuit (vec![event1], Default::default()); + let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn test_modexp_circuit_01() { + let base = Word::from(1u128); + let exp = Word::from(2u128); + let modulus = Word::from(7u128); + let (_, result) = base.pow(exp).div_mod(modulus); + let event1 = ModExpEvent {base, exponent: exp, modulus, result}; + let test_circuit = ModExpCircuit (vec![event1], Default::default()); + let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + #[test] + fn test_modexp_circuit_02() { + let base = Word::from(2u128); + let exp = Word::from(2u128); + let modulus = Word::from(7u128); + let (_, result) = base.pow(exp).div_mod(modulus); + let event1 = ModExpEvent {base, exponent: exp, modulus, result}; + let test_circuit = ModExpCircuit (vec![event1], Default::default()); + let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + -#[test] -fn test_modexp_circuit_01() { - let base = BigUint::from(1u128); - let exp = BigUint::from(2u128); - let modulus = BigUint::from(7u128); - let test_circuit = TestCircuit {base, exp, modulus} ; - let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); -} -#[test] -fn test_modexp_circuit_02() { - let base = BigUint::from(2u128); - let exp = BigUint::from(2u128); - let modulus = BigUint::from(7u128); - let test_circuit = TestCircuit {base, exp, modulus} ; - let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); } -*/ \ No newline at end of file diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 8221019f16..3ce3fc3b25 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2257,19 +2257,51 @@ impl ModExpTable { } } - fn split_u256_108bit_limbs(word: &Word) -> [u128;3]{ + /// helper for devide a U256 into 3 108bit limbs + pub fn split_u256_108bit_limbs(word: &Word) -> [u128;3]{ let bit108 = 1u128<<108; let (next, limb0) = word.div_mod(U256::from(bit108)); let (limb1, limb2) = next.div_mod(U256::from(bit108)); [limb0.as_u128(), limb1.as_u128(), limb2.as_u128()] } - fn native_u256(word: &Word) -> F { + /// helper for obtain the modulus of a U256 in Fr + pub fn native_u256(word: &Word) -> F { let mut bytes = [0u8; 64]; word.to_little_endian(&mut bytes[..32]); F::from_bytes_wide(&bytes) } + /// fill a blank 4-row region start from offset for empty lookup + pub fn fill_blank( + &self, + layouter: &mut impl Layouter, + ) -> Result<(), Error>{ + layouter.assign_region( + || "modexp table blank region", + |mut region|{ + for i in 0..4 { + // fill last totally 0 row + region.assign_fixed( + || "modexp table blank head row", + self.q_head, + i, + || Value::known(F::zero()), + )?; + for &col in [&self.base, &self.exp, &self.modulus, &self.result]{ + region.assign_advice( + || "modexp table blank limb row", + col, + i, + || Value::known(F::zero()), + )?; + } + } + Ok(()) + } + ) + } + /// Get assignments to the modexp table. Meant to be used for dev purposes. pub fn dev_load( &self, @@ -2280,23 +2312,9 @@ impl ModExpTable { || "modexp table", |mut region|{ - let mut event_limit = block.circuits_params.max_keccak_rows / 9300; - let exp_events = &block.modexp_events; - if exp_events.len() <= event_limit { - // only warn in dev_load - log::warn!( - "not enough rows for modexp circuit, expected {}, limit {}", - exp_events.len(), - event_limit, - ); - event_limit = exp_events.len().max(5); - } - let mut offset = 0usize; - for event in exp_events.iter() - .chain(std::iter::repeat(&Default::default())) - .take(event_limit){ + for event in &block.modexp_events{ for i in 0..4 { region.assign_fixed( @@ -2341,25 +2359,12 @@ impl ModExpTable { offset += 4; } - // fill last totally 0 row - region.assign_fixed( - || "modexp table blank row", - self.q_head, - offset, - || Value::known(F::zero()), - )?; - for &col in [&self.base, &self.exp, &self.modulus, &self.result]{ - region.assign_advice( - || "modexp table blank row {}", - col, - offset, - || Value::known(F::zero()), - )?; - } - Ok(()) }, - ) + )?; + + self.fill_blank(layouter) + } } From ac12be0a6c5ac0e6ee57eeb057769045c80c04ec Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 5 Jul 2023 23:07:34 +0800 Subject: [PATCH 24/57] wip: test modexp table lookup --- .../execution/precompiles/modexp.rs | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 0154f2d5a4..0c9505a9ba 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -482,6 +482,63 @@ impl ModExpOutputs { } + +#[derive(Clone, Debug)] +struct Limbs { + byte14_split_lo: Cell, + byte14_split_hi: Cell, + limbs: [Expression;3], +} + + +impl Limbs { + fn configure( + cb: &mut EVMConstraintBuilder, + word: &Word, + ) -> Self { + let byte14_split_lo = cb.query_byte(); + let byte14_split_hi = cb.query_byte(); + + cb.require_equal( + "split 14th byte in word into half", + word[14].expr(), + byte14_split_lo.expr() + 128.expr() * byte14_split_hi.expr(), + ); + + let limbs = [ + util::expr_from_bytes( + &word[..14].iter() + .chain(std::iter::once(&byte14_split_lo)) + .collect::>() + ), + util::expr_from_bytes( + &std::iter::once(&byte14_split_hi) + .chain(&word[14..28]) + .collect::>() + ), + util::expr_from_bytes( + &word[28..] + ), + ]; + + Self { + byte14_split_hi, + byte14_split_lo, + limbs, + } + } + + + pub fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + (output_len, data): OutputParsedResult, + ) -> Result<(), Error> { + Ok(()) + } +} + #[derive(Clone, Debug)] pub struct ModExpGadget { is_success: Cell, @@ -549,8 +606,14 @@ impl ExecutionGadget for ModExpGadget { input.modulus_len(), ); - cb.condition(util::not::expr(output.is_output_nil()), |_cb|{ - //TODO: config modexp circuit + cb.condition(util::not::expr(output.is_output_nil()), |cb|{ + cb.modexp_table_lookup( + base_limbs, + exp_limbs, + modulus_limbs, + result_limbs + ); + }); Self { From 733d1b6e657d6a5f0f862c0407d22ff4295281fa Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 6 Jul 2023 09:25:58 +0800 Subject: [PATCH 25/57] wip: testing modexp table lookup --- .../execution/precompiles/modexp.rs | 96 ++++++++++++++++--- .../src/precompile_circuit/modexp.rs | 1 - 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 0c9505a9ba..e225be73a9 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -264,6 +264,9 @@ struct ModExpInputs { input_valid: Cell, padding_pow: RandPowRepresent, is_input_need_padding: LtGadget, + pub base_limbs: Limbs, + pub exp_limbs: Limbs, + pub modulus_limbs: Limbs, } impl ModExpInputs { @@ -284,6 +287,10 @@ impl ModExpInputs { let modulus = cb.query_bytes(); let exp = cb.query_bytes(); + let base_limbs = Limbs::configure(cb, &base); + let exp_limbs = Limbs::configure(cb, &exp); + let modulus_limbs = Limbs::configure(cb, &modulus); + let input_valid = cb.query_cell(); cb.require_equal("mark input valid by checking 3 lens is valid", input_valid.expr(), @@ -380,6 +387,9 @@ impl ModExpInputs { input_valid, padding_pow, is_input_need_padding, + base_limbs, + exp_limbs, + modulus_limbs, } } @@ -411,6 +421,10 @@ impl ModExpInputs { linked_v = Some(assigned); } + for (val_r, input_limbs) in values.iter().zip([&self.base_limbs, &self.exp_limbs, &self.modulus_limbs]){ + input_limbs.assign(region, offset, val_r)?; + } + for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]){ assign_word(region, offset, input_bytes, val)?; } @@ -439,6 +453,7 @@ impl ModExpInputs { struct ModExpOutputs { result: Word, is_result_zero: IsZeroGadget, + pub result_limbs: Limbs, } impl ModExpOutputs { @@ -453,6 +468,7 @@ impl ModExpOutputs { let is_result_zero = IsZeroGadget::construct(cb, output_len.clone()); let result = cb.query_bytes(); + let result_limbs = Limbs::configure(cb, &result); cb.condition(util::not::expr(is_result_zero.expr()), |cb|{ cb.require_equal("acc bytes must equal", @@ -464,6 +480,7 @@ impl ModExpOutputs { Self { result, is_result_zero, + result_limbs, } } @@ -476,6 +493,7 @@ impl ModExpOutputs { (output_len, data): OutputParsedResult, ) -> Result<(), Error> { self.is_result_zero.assign(region, offset, F::from(output_len as u64))?; + self.result_limbs.assign(region, offset, &data)?; assign_word(region, offset, &self.result, data)?; Ok(()) } @@ -484,7 +502,7 @@ impl ModExpOutputs { #[derive(Clone, Debug)] -struct Limbs { +pub(crate) struct Limbs { byte14_split_lo: Cell, byte14_split_hi: Cell, limbs: [Expression;3], @@ -492,7 +510,7 @@ struct Limbs { impl Limbs { - fn configure( + pub fn configure( cb: &mut EVMConstraintBuilder, word: &Word, ) -> Self { @@ -505,19 +523,21 @@ impl Limbs { byte14_split_lo.expr() + 128.expr() * byte14_split_hi.expr(), ); + let inv_16 = Expression::Constant(F::from(16u64).invert().unwrap()); + let limbs = [ util::expr_from_bytes( - &word[..14].iter() - .chain(std::iter::once(&byte14_split_lo)) - .collect::>() + &std::iter::once(&byte14_split_lo) + .chain(&word[MODEXP_SIZE_LIMIT-13..]) + .collect::>() ), util::expr_from_bytes( - &std::iter::once(&byte14_split_hi) - .chain(&word[14..28]) + &word[MODEXP_SIZE_LIMIT-27..MODEXP_SIZE_LIMIT-14].iter() + .chain(std::iter::once(&byte14_split_hi)) .collect::>() - ), + ) * inv_16, util::expr_from_bytes( - &word[28..] + &word[..MODEXP_SIZE_LIMIT-27] ), ]; @@ -528,13 +548,20 @@ impl Limbs { } } + pub fn limbs(&self) -> [Expression;3] {self.limbs.clone()} pub fn assign( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - (output_len, data): OutputParsedResult, + big_int: &[u8; MODEXP_SIZE_LIMIT], ) -> Result<(), Error> { + + let byte14_lo = big_int[MODEXP_SIZE_LIMIT-14] & 0xf; + let byte14_hi = big_int[MODEXP_SIZE_LIMIT-14] & 0xf0; + + self.byte14_split_lo.assign(region, offset, Value::known(F::from(byte14_lo as u64)))?; + self.byte14_split_hi.assign(region, offset, Value::known(F::from(byte14_hi as u64)))?; Ok(()) } } @@ -608,10 +635,10 @@ impl ExecutionGadget for ModExpGadget { cb.condition(util::not::expr(output.is_output_nil()), |cb|{ cb.modexp_table_lookup( - base_limbs, - exp_limbs, - modulus_limbs, - result_limbs + input.base_limbs.limbs(), + input.exp_limbs.limbs(), + input.modulus_limbs.limbs(), + output.result_limbs.limbs(), ); }); @@ -713,6 +740,7 @@ impl ExecutionGadget for ModExpGadget { #[cfg(test)] mod test { + use super::*; use bus_mapping::{ evm::{OpcodeId, PrecompileCallArgs}, precompile::PrecompileCalls, @@ -723,6 +751,46 @@ mod test { use crate::test_util::CircuitTestBuilder; + + #[test] + fn test_limbs(){ + use misc_precompiled_circuit::circuits::modexp::Number; + use halo2_proofs::halo2curves::bn256::Fr; + use num_bigint::BigUint; + + // simply take an hash for test + let bi = BigUint::parse_bytes(b"fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47", 16).unwrap(); + let n = Number::::from_bn(&bi); + let w = word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"); + let mut bytes = [0u8;32]; + w.to_big_endian(&mut bytes); + assert_eq!(BigUint::from_bytes_be(&bytes), bi); + + let byte14_lo = bytes[MODEXP_SIZE_LIMIT-14] & 0xf; + let byte14_hi = bytes[MODEXP_SIZE_LIMIT-14] & 0xf0; + + let limb0 : Fr = U256::from_big_endian( + &(std::iter::once(byte14_lo)) + .chain(bytes[MODEXP_SIZE_LIMIT-13..].iter().copied()) + .collect::>() + ).to_scalar().unwrap(); + + let limb1 : Fr = U256::from_big_endian( + &bytes[MODEXP_SIZE_LIMIT-27..MODEXP_SIZE_LIMIT-14] + .iter().copied() + .chain(std::iter::once(byte14_hi)) + .collect::>() + ).to_scalar().unwrap(); + + let limb2 : Fr = U256::from_big_endian(&bytes[..MODEXP_SIZE_LIMIT-27]).to_scalar().unwrap(); + + assert_eq!(limb0, n.limbs[0].value); + assert_eq!(limb1, n.limbs[1].value * Fr::from(16 as u64)); + assert_eq!(limb2, n.limbs[2].value); + //Limb::new(None, value) + } + + lazy_static::lazy_static! { static ref TEST_VECTOR: Vec = { vec![ diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index 7c3a0000f4..eb9dd671da 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -237,7 +237,6 @@ mod test { } } - #[test] fn test_modexp_circuit_00() { let base = Word::from(1u128); From 31f57c1ed3923a5017b8a57f88b0c290f4e5fd13 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 6 Jul 2023 19:21:59 +0800 Subject: [PATCH 26/57] pass modexptable lookup --- .../execution/precompiles/modexp.rs | 136 ++++++++++++++++-- zkevm-circuits/src/table.rs | 7 +- 2 files changed, 132 insertions(+), 11 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index e225be73a9..9f84411933 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -162,6 +162,11 @@ fn rlc_word_rev(cells: &[Cell; N], randomness: Expr ).expect("values should not be empty") } +// calc for big-endian (notice util::expr_from_bytes calc for little-endian) +fn expr_from_bytes>(bytes: &[E]) -> Expression { + bytes.iter().fold(0.expr(), |acc, byte| acc * F::from(256) + byte.expr()) +} + #[derive(Clone, Debug)] struct SizeRepresent { len_bytes: Word, @@ -178,12 +183,12 @@ impl SizeRepresent { let len_blank_bytes = len_bytes[..(32-SIZE_REPRESENT_BYTES)] .iter().map(Cell::expr).collect::>(); let is_rest_field_zero = IsZeroGadget::construct(cb, - util::expr_from_bytes(&len_blank_bytes), + expr_from_bytes(&len_blank_bytes), ); let len_effect_bytes = len_bytes[(32-SIZE_REPRESENT_BYTES)..] .iter().map(Cell::expr).collect::>(); let is_not_exceed_limit = LtGadget::construct(cb, - util::expr_from_bytes(&len_effect_bytes), + expr_from_bytes(&len_effect_bytes), (SIZE_LIMIT+1).expr(), ); Self { @@ -203,7 +208,7 @@ impl SizeRepresent { pub fn value(&self) -> Expression { let len_effect_bytes = self.len_bytes[(32-SIZE_REPRESENT_BYTES)..] .iter().map(Cell::expr).collect::>(); - util::expr_from_bytes(&len_effect_bytes) + expr_from_bytes(&len_effect_bytes) } @@ -519,24 +524,24 @@ impl Limbs { cb.require_equal( "split 14th byte in word into half", - word[14].expr(), - byte14_split_lo.expr() + 128.expr() * byte14_split_hi.expr(), + word[MODEXP_SIZE_LIMIT-14].expr(), + byte14_split_lo.expr() + byte14_split_hi.expr(), ); let inv_16 = Expression::Constant(F::from(16u64).invert().unwrap()); let limbs = [ - util::expr_from_bytes( + expr_from_bytes( &std::iter::once(&byte14_split_lo) .chain(&word[MODEXP_SIZE_LIMIT-13..]) .collect::>() ), - util::expr_from_bytes( + expr_from_bytes( &word[MODEXP_SIZE_LIMIT-27..MODEXP_SIZE_LIMIT-14].iter() .chain(std::iter::once(&byte14_split_hi)) .collect::>() ) * inv_16, - util::expr_from_bytes( + expr_from_bytes( &word[..MODEXP_SIZE_LIMIT-27] ), ]; @@ -755,8 +760,9 @@ mod test { #[test] fn test_limbs(){ use misc_precompiled_circuit::circuits::modexp::Number; - use halo2_proofs::halo2curves::bn256::Fr; + use halo2_proofs::{arithmetic::FieldExt, halo2curves::bn256::Fr}; use num_bigint::BigUint; + use crate::table::ModExpTable; // simply take an hash for test let bi = BigUint::parse_bytes(b"fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47", 16).unwrap(); @@ -787,6 +793,14 @@ mod test { assert_eq!(limb0, n.limbs[0].value); assert_eq!(limb1, n.limbs[1].value * Fr::from(16 as u64)); assert_eq!(limb2, n.limbs[2].value); + + let nt : Fr = ModExpTable::native_u256(&w); + + let table_split = ModExpTable::split_u256_108bit_limbs(&w); + assert_eq!(Fr::from_u128(table_split[0]), n.limbs[0].value); + assert_eq!(Fr::from_u128(table_split[1]), n.limbs[1].value); + assert_eq!(Fr::from_u128(table_split[2]), n.limbs[2].value); + assert_eq!(nt, n.limbs[3].value); //Limb::new(None, value) } @@ -878,6 +892,77 @@ mod test { ] }; + static ref TEST_U256_VECTOR: Vec = { + vec![ + PrecompileCallArgs { + name: "modexp length in u256", + setup_code: bytecode! { + // Base size + PUSH1(0x20) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x20) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x20) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000009")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp length in u256 and result wrapped", + setup_code: bytecode! { + // Base size + PUSH1(0x20) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x20) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x20) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000064")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + ] + }; + static ref TEST_INVALID_VECTOR: Vec = { vec![ PrecompileCallArgs { @@ -911,6 +996,19 @@ mod test { }; } + #[ignore] + #[test] + fn precompile_modexp_test_fast() { + let bytecode = TEST_VECTOR[0].with_call_op(OpcodeId::STATICCALL); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + + } + + #[ignore] #[test] fn precompile_modexp_test() { let call_kinds = vec![ @@ -929,4 +1027,24 @@ mod test { .run(); } } + + #[test] + fn precompile_modexp_test_u256() { + let call_kinds = vec![ + OpcodeId::CALL, + OpcodeId::STATICCALL, + OpcodeId::DELEGATECALL, + OpcodeId::CALLCODE, + ]; + + for (test_vector, &call_kind) in TEST_U256_VECTOR.iter().cartesian_product(&call_kinds) { + let bytecode = test_vector.with_call_op(call_kind); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + } + } + } diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 3ce3fc3b25..326fc9bd1c 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2261,14 +2261,17 @@ impl ModExpTable { pub fn split_u256_108bit_limbs(word: &Word) -> [u128;3]{ let bit108 = 1u128<<108; let (next, limb0) = word.div_mod(U256::from(bit108)); - let (limb1, limb2) = next.div_mod(U256::from(bit108)); + let (limb2, limb1) = next.div_mod(U256::from(bit108)); [limb0.as_u128(), limb1.as_u128(), limb2.as_u128()] } /// helper for obtain the modulus of a U256 in Fr pub fn native_u256(word: &Word) -> F { + let minus1 = -F::one(); + let (div, _) = word.div_mod(Word::from_little_endian(minus1.to_repr().as_ref())); + let div = div.checked_add(Word::from(1u64)).unwrap(); let mut bytes = [0u8; 64]; - word.to_little_endian(&mut bytes[..32]); + div.to_little_endian(&mut bytes[..32]); F::from_bytes_wide(&bytes) } From 1da83083a6160906303dac9ae1130a0bdefd758c Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 6 Jul 2023 21:18:39 +0800 Subject: [PATCH 27/57] pass u256 modexp circuit --- zkevm-circuits/src/precompile_circuit/modexp.rs | 2 ++ zkevm-circuits/src/table.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index eb9dd671da..25d5116582 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -174,6 +174,8 @@ impl SubCircuit for ModExpCircuit { layouter.assign_region( || "modexp circuit", |mut region| { + + range_chip.initialize(&mut region)?; let mut calc_offset = 0; for (n, event) in self.0.iter().enumerate(){ calc_offset = config.assign_group( diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 326fc9bd1c..44439e16df 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2248,13 +2248,18 @@ impl ModExpTable { /// Construct the modexp table. pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { + let ret = Self { q_head: meta.fixed_column(), base: meta.advice_column(), exp: meta.advice_column(), modulus: meta.advice_column(), result: meta.advice_column(), - } + }; + meta.enable_equality(ret.base); + meta.enable_equality(ret.exp); + meta.enable_equality(ret.modulus); + meta.enable_equality(ret.result); + ret } /// helper for devide a U256 into 3 108bit limbs From fdf67e862c81768eb8af9de8f73cc5e353a19366 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 6 Jul 2023 21:39:03 +0800 Subject: [PATCH 28/57] refine dependencies --- .cargo/config.toml | 3 --- Cargo.lock | 1 + zkevm-circuits/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 72a6628385..bd0f7448f4 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,6 +4,3 @@ rustflags = ["-C", "link-args=-framework CoreFoundation -framework Security"] git-fetch-with-cli = true [env] RUST_MIN_STACK = "16777216" - -[patch."https://github.com/scroll-tech/misc-precompiled-circuit.git"] -rmd160-circuits = { path='/home/combray/Code/zkevm/misc-precompiled-circuit' } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 43f7beb611..117cccff7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3999,6 +3999,7 @@ dependencies = [ [[package]] name = "rmd160-circuits" version = "0.1.0" +source = "git+https://github.com/scroll-tech/misc-precompiled-circuit.git?branch=integration#40b2c52919d72404f34a613dc5568f234d0e8a9d" dependencies = [ "ark-std", "halo2-gate-generator", diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index bffd6fc68b..76aa263462 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -32,7 +32,7 @@ serde_json = "1.0.78" hash-circuit = { package = "poseidon-circuit", git = "https://github.com/scroll-tech/poseidon-circuit.git", branch = "scroll-dev-0619", features=['short']} #mpt-circuits = { package = "halo2-mpt-circuits", path = "../../mpt-circuit" } -misc-precompiled-circuit = { package = "rmd160-circuits", git = "https://github.com/scroll-tech/misc-precompiled-circuit.git" } +misc-precompiled-circuit = { package = "rmd160-circuits", git = "https://github.com/scroll-tech/misc-precompiled-circuit.git", branch = "integration" } halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } halo2-ecc = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } From d05ed220d757047cbbfe4b038f34e7fb1ddf3b85 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 6 Jul 2023 21:45:56 +0800 Subject: [PATCH 29/57] fix for merging develop --- zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 9f84411933..7fbb281f93 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -183,6 +183,7 @@ impl SizeRepresent { let len_blank_bytes = len_bytes[..(32-SIZE_REPRESENT_BYTES)] .iter().map(Cell::expr).collect::>(); let is_rest_field_zero = IsZeroGadget::construct(cb, + "if length field exceed 1 byte", expr_from_bytes(&len_blank_bytes), ); let len_effect_bytes = len_bytes[(32-SIZE_REPRESENT_BYTES)..] @@ -470,7 +471,7 @@ impl ModExpOutputs { ) -> Self { let output_len = inner_success * modulus_len; - let is_result_zero = IsZeroGadget::construct(cb, output_len.clone()); + let is_result_zero = IsZeroGadget::construct(cb, "if output len is nil", output_len.clone()); let result = cb.query_bytes(); let result_limbs = Limbs::configure(cb, &result); From b42bec9ac654dff0786c962123c6a3768c7d72d7 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 6 Jul 2023 22:52:42 +0800 Subject: [PATCH 30/57] add modexp into super circuit --- zkevm-circuits/src/precompile_circuit/modexp.rs | 6 ++++-- zkevm-circuits/src/super_circuit.rs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index 25d5116582..cbab320828 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -28,8 +28,9 @@ use misc_precompiled_circuit::circuits::{ }, }; +/// ModExp circuit config #[derive(Clone, Debug)] -struct ModExpCircuitConfig { +pub struct ModExpCircuitConfig { modexp_config: ModExpConfig, rangecheck_config: RangeCheckConfig, modexp_table: ModExpTable, @@ -128,8 +129,9 @@ impl ModExpCircuitConfig { const MODEXPCONFIG_EACH_CHIP_ROWS : usize = 9291; +/// ModExp circuit for precompile modexp #[derive(Clone, Debug, Default)] -struct ModExpCircuit(Vec, std::marker::PhantomData); +pub struct ModExpCircuit(Vec, std::marker::PhantomData); impl SubCircuit for ModExpCircuit { type Config = ModExpCircuitConfig; diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 6c5803931e..4988a576e3 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -71,6 +71,7 @@ use crate::{ tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{log2_ceil, SubCircuit, SubCircuitConfig}, witness::{block_convert, Block}, + precompile_circuit, }; #[cfg(feature = "zktrie")] @@ -118,6 +119,7 @@ pub struct SuperCircuitConfig { state_circuit: StateCircuitConfig, tx_circuit: TxCircuitConfig, sig_circuit: SigCircuitConfig, + modexp_circuit: precompile_circuit::modexp::ModExpCircuitConfig, #[cfg(not(feature = "poseidon-codehash"))] bytecode_circuit: BytecodeCircuitConfig, #[cfg(feature = "poseidon-codehash")] @@ -305,6 +307,12 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "sig circuit"); + let modexp_circuit = precompile_circuit::modexp::ModExpCircuitConfig::new( + meta, + modexp_table, + ); + log_circuit_info(meta, "modexp circuit"); + let state_circuit = StateCircuitConfig::new( meta, StateCircuitConfigArgs { @@ -358,6 +366,7 @@ impl SubCircuitConfig for SuperCircuitConfig { tx_circuit, exp_circuit, sig_circuit, + modexp_circuit, #[cfg(feature = "zktrie")] mpt_circuit, } @@ -393,6 +402,8 @@ pub struct SuperCircuit< pub poseidon_circuit: PoseidonCircuit, /// Sig Circuit pub sig_circuit: SigCircuit, + /// Modexp Circuit + pub modexp_circuit: precompile_circuit::modexp::ModExpCircuit, /// Rlp Circuit pub rlp_circuit: RlpCircuit, /// Mpt Circuit @@ -428,6 +439,7 @@ impl< let tx = TxCircuit::min_num_rows_block(block); let rlp = RlpCircuit::min_num_rows_block(block); let exp = ExpCircuit::min_num_rows_block(block); + let mod_exp = precompile_circuit::modexp::ModExpCircuit::min_num_rows_block(block); let pi = PiCircuit::min_num_rows_block(block); let poseidon = (0, 0); //PoseidonCircuit::min_num_rows_block(block); #[cfg(feature = "zktrie")] @@ -442,6 +454,7 @@ impl< tx, rlp, exp, + mod_exp, pi, poseidon, #[cfg(feature = "zktrie")] @@ -493,6 +506,7 @@ impl< let bytecode_circuit = BytecodeCircuit::new_from_block(block); let copy_circuit = CopyCircuit::new_from_block_no_external(block); let exp_circuit = ExpCircuit::new_from_block(block); + let modexp_circuit = precompile_circuit::modexp::ModExpCircuit::new_from_block(block); let keccak_circuit = KeccakCircuit::new_from_block(block); let poseidon_circuit = PoseidonCircuit::new_from_block(block); let rlp_circuit = RlpCircuit::new_from_block(block); @@ -511,6 +525,7 @@ impl< poseidon_circuit, rlp_circuit, sig_circuit, + modexp_circuit, #[cfg(feature = "zktrie")] mpt_circuit, } @@ -557,6 +572,8 @@ impl< .synthesize_sub(&config.tx_circuit, challenges, layouter)?; self.sig_circuit .synthesize_sub(&config.sig_circuit, challenges, layouter)?; + self.modexp_circuit + .synthesize_sub(&config.modexp_circuit, challenges, layouter)?; self.state_circuit .synthesize_sub(&config.state_circuit, challenges, layouter)?; self.copy_circuit From 35f94d1df6ccb70db7318dd4cf5fa3ede0d9c3ab Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 7 Jul 2023 08:27:49 +0800 Subject: [PATCH 31/57] clippy --- bus-mapping/src/precompile.rs | 2 +- .../src/evm_circuit/execution/precompiles/modexp.rs | 4 ++-- .../src/evm_circuit/util/precompile_gadget.rs | 4 ++-- zkevm-circuits/src/precompile_circuit/modexp.rs | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 08f157f251..105677437b 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -208,7 +208,7 @@ impl ModExpAuxData { fn parse_memory_to_value(mem: &[u8]) -> [u8;MODEXP_SIZE_LIMIT] { let mut value_bytes = [0u8; MODEXP_SIZE_LIMIT]; - if mem.len() > 0 { + if !mem.is_empty() { value_bytes.as_mut_slice()[(MODEXP_SIZE_LIMIT - mem.len())..].copy_from_slice(mem); } value_bytes diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 7fbb281f93..1336326ae3 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -31,7 +31,7 @@ impl RandPowRepresent { /// build randomness r, r**2, ... r**(2**BIT_LIMIT) pub fn base_pows_expr(randomness: Expression) -> [Expression;BIT_LIMIT] { std::iter::successors( - Some(randomness.clone()), + Some(randomness), |r| Some(r.clone() * r.clone()) ).take(BIT_LIMIT) .collect::>() @@ -471,7 +471,7 @@ impl ModExpOutputs { ) -> Self { let output_len = inner_success * modulus_len; - let is_result_zero = IsZeroGadget::construct(cb, "if output len is nil", output_len.clone()); + let is_result_zero = IsZeroGadget::construct(cb, "if output len is nil", output_len); let result = cb.query_bytes(); let result_limbs = Limbs::configure(cb, &result); diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 29809ffafe..b6fb328987 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -79,7 +79,7 @@ impl PrecompileGadget { }); cb.condition(address.value_equals(PrecompileCalls::Ecrecover), |cb| { -/* cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { + cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { let (recovered, msg_hash_rlc, sig_v_rlc, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( cb.query_bool(), cb.query_cell_phase2(), @@ -114,7 +114,7 @@ impl PrecompileGadget { cb.condition(not::expr(recovered.expr()), |cb| { cb.require_zero("output bytes == 0", output_bytes_rlc.expr()); }); - });*/ + }); }); cb.condition(address.value_equals(PrecompileCalls::Sha256), |cb| { diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index cbab320828..5368bde943 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -104,12 +104,12 @@ impl ModExpCircuitConfig { let native_v = ModExpTable::native_u256(value); let mut limbs = Vec::new(); - for i in 0..3 { - let fv = F::from_u128(limbs_v[i]); + for (i, limb) in limbs_v.into_iter().enumerate() { + let fv = F::from_u128(limb); let c = region.assign_advice( || "assign modexp limb", col, - offset + i, + offset + i, || Value::known(fv), )?; limbs.push(Limb::new(Some(c), fv)); @@ -231,9 +231,9 @@ mod test { (config, challenge): Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - let challenges = challenge.values(&mut layouter); + let challenges = challenge.values(&layouter); >::synthesize_sub( - &self, + self, &config, &challenges, &mut layouter From 24897b09b01d7496c33d7e78792b589c362ae8dc Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 7 Jul 2023 08:29:21 +0800 Subject: [PATCH 32/57] fmt --- bus-mapping/src/circuit_input_builder.rs | 3 +- .../src/circuit_input_builder/block.rs | 5 +- .../src/circuit_input_builder/execution.rs | 2 +- bus-mapping/src/precompile.rs | 58 +- zkevm-circuits/src/evm_circuit.rs | 4 +- zkevm-circuits/src/evm_circuit/execution.rs | 6 +- .../execution/precompiles/modexp.rs | 523 ++++++++++-------- zkevm-circuits/src/evm_circuit/table.rs | 10 +- .../evm_circuit/util/constraint_builder.rs | 10 +- .../src/evm_circuit/util/precompile_gadget.rs | 2 +- zkevm-circuits/src/precompile_circuit/mod.rs | 3 +- .../src/precompile_circuit/modexp.rs | 171 +++--- zkevm-circuits/src/super_circuit.rs | 12 +- zkevm-circuits/src/table.rs | 76 ++- 14 files changed, 472 insertions(+), 413 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 8cdb08f62c..766204d76a 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -36,7 +36,8 @@ use ethers_core::{ }; use ethers_providers::JsonRpcClient; pub use execution::{ - CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, ModExpEvent, + CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, ExpEvent, ExpStep, ModExpEvent, + NumberOrHash, }; use hex::decode_to_slice; diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index 2d5bc9a814..f4d75b1a81 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -1,7 +1,8 @@ //! Block-related utility module use super::{ - execution::ExecState, transaction::Transaction, CircuitsParams, CopyEvent, ExecStep, ExpEvent, ModExpEvent, + execution::ExecState, transaction::Transaction, CircuitsParams, CopyEvent, ExecStep, ExpEvent, + ModExpEvent, }; use crate::{ operation::{OperationContainer, RWCounter}, @@ -257,5 +258,5 @@ impl Block { /// Push an modexp event to the block. pub fn add_modexp_event(&mut self, event: ModExpEvent) { self.modexp_events.push(event); - } + } } diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index b7e5b20b49..a6a4165dab 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -482,4 +482,4 @@ impl Default for ModExpEvent { result: Default::default(), } } -} \ No newline at end of file +} diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 105677437b..fef2cd548d 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -19,19 +19,19 @@ pub(crate) fn execute_precompiled(address: &Address, input: &[u8], gas: u64) -> match precompile_fn(input, gas) { Ok((gas_cost, return_value)) => { - match PrecompileCalls::from(address.0[19]){ + match PrecompileCalls::from(address.0[19]) { // FIXME: override the behavior of invalid input PrecompileCalls::Modexp => { let (input_valid, _) = ModExpAuxData::check_input(input); if input_valid { - (return_value, gas_cost) - }else { + (return_value, gas_cost) + } else { (vec![], gas_cost) } - }, + } _ => (return_value, gas_cost), } - }, + } Err(_) => (vec![], gas), } } @@ -177,7 +177,7 @@ impl EcrecoverAuxData { } else { None } - } + } } /// size limit of modexp @@ -189,15 +189,15 @@ pub const MODEXP_INPUT_LIMIT: usize = 192; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct ModExpAuxData { /// The specified len of inputs: [base, exp, modulus] - pub input_lens: [Word;3], + pub input_lens: [Word; 3], /// Input value [base, exp, modulus], limited to SIZE_LIMIT - pub inputs: [[u8;MODEXP_SIZE_LIMIT];3], + pub inputs: [[u8; MODEXP_SIZE_LIMIT]; 3], /// Input valid. pub valid: bool, /// len of output, limited to lens of moduls, but can be 0 pub output_len: usize, /// output of modexp. - pub output: [u8;MODEXP_SIZE_LIMIT], + pub output: [u8; MODEXP_SIZE_LIMIT], /// backup of input memory pub input_memory: Vec, /// backup of output memory @@ -205,44 +205,52 @@ pub struct ModExpAuxData { } impl ModExpAuxData { - - fn parse_memory_to_value(mem: &[u8]) -> [u8;MODEXP_SIZE_LIMIT] { + fn parse_memory_to_value(mem: &[u8]) -> [u8; MODEXP_SIZE_LIMIT] { let mut value_bytes = [0u8; MODEXP_SIZE_LIMIT]; if !mem.is_empty() { value_bytes.as_mut_slice()[(MODEXP_SIZE_LIMIT - mem.len())..].copy_from_slice(mem); } - value_bytes + value_bytes } /// check input - pub fn check_input(input: &[u8]) -> (bool, [Word;3]){ + pub fn check_input(input: &[u8]) -> (bool, [Word; 3]) { let mut i = input.chunks(32); let base_len = Word::from_big_endian(i.next().unwrap_or(&[])); let exp_len = Word::from_big_endian(i.next().unwrap_or(&[])); let modulus_len = Word::from_big_endian(i.next().unwrap_or(&[])); let limit = Word::from(MODEXP_SIZE_LIMIT); - - let input_valid = base_len <= limit && - exp_len <= limit && - modulus_len <= limit; - (input_valid, [base_len, exp_len, modulus_len]) + let input_valid = base_len <= limit && exp_len <= limit && modulus_len <= limit; + + (input_valid, [base_len, exp_len, modulus_len]) } /// Create a new instance of modexp auxiliary data. pub fn new(mut mem_input: Vec, output: Vec) -> Self { - let input_memory = mem_input.clone(); let output_memory = output.clone(); let (input_valid, [base_len, exp_len, modulus_len]) = Self::check_input(&mem_input); - let base_mem_len = if input_valid {base_len.as_usize()} else {MODEXP_SIZE_LIMIT}; - let exp_mem_len = if input_valid {exp_len.as_usize()} else {MODEXP_SIZE_LIMIT}; - let modulus_mem_len = if input_valid {modulus_len.as_usize()} else {MODEXP_SIZE_LIMIT}; - - mem_input.resize(96+base_mem_len+exp_mem_len+modulus_mem_len, 0); + let base_mem_len = if input_valid { + base_len.as_usize() + } else { + MODEXP_SIZE_LIMIT + }; + let exp_mem_len = if input_valid { + exp_len.as_usize() + } else { + MODEXP_SIZE_LIMIT + }; + let modulus_mem_len = if input_valid { + modulus_len.as_usize() + } else { + MODEXP_SIZE_LIMIT + }; + + mem_input.resize(96 + base_mem_len + exp_mem_len + modulus_mem_len, 0); let mut cur_input_begin = &mem_input[96..]; let base = Self::parse_memory_to_value(&cur_input_begin[..base_mem_len]); @@ -271,7 +279,7 @@ pub enum PrecompileAuxData { /// Ecrecover. Ecrecover(EcrecoverAuxData), /// Modexp. - Modexp(ModExpAuxData) + Modexp(ModExpAuxData), } impl Default for PrecompileAuxData { diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 633bf97345..1ac0ed5f6d 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -21,8 +21,8 @@ pub use crate::witness; use crate::{ evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ - BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, PowOfRandTable, - RwTable, SigTable, TxTable, ModExpTable + BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, ModExpTable, + PowOfRandTable, RwTable, SigTable, TxTable, }, util::{SubCircuit, SubCircuitConfig}, }; diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 7dc34162ad..7648a6abe4 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1,9 +1,9 @@ use super::{ param::{ BLOCK_TABLE_LOOKUPS, BYTECODE_TABLE_LOOKUPS, COPY_TABLE_LOOKUPS, EXP_TABLE_LOOKUPS, - FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_BYTE_LOOKUPS, N_COPY_COLUMNS, - N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, - TX_TABLE_LOOKUPS, MODEXP_TABLE_LOOKUPS, + FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, MODEXP_TABLE_LOOKUPS, N_BYTE_LOOKUPS, + N_COPY_COLUMNS, N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, RW_TABLE_LOOKUPS, + SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, EvmCircuitExports, diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 1336326ae3..7a3feb3f62 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -1,21 +1,27 @@ use eth_types::{Field, ToScalar, U256}; -use gadgets::{binary_number::AsBits, util::{self, Expr}}; -use halo2_proofs::{circuit::Value, plonk::{Expression, Error}}; +use gadgets::{ + binary_number::AsBits, + util::{self, Expr}, +}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; -use bus_mapping::precompile::{PrecompileAuxData, MODEXP_SIZE_LIMIT, MODEXP_INPUT_LIMIT}; use crate::{ evm_circuit::{ execution::ExecutionGadget, step::ExecutionState, - util::{constraint_builder::{EVMConstraintBuilder, ConstrainBuilderCommon}, - CachedRegion, Cell, + util::{ + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{BinaryNumberGadget, IsZeroGadget, LtGadget}, - rlc, + rlc, CachedRegion, Cell, }, }, table::CallContextFieldTag, witness::{Block, Call, ExecStep, Transaction}, }; +use bus_mapping::precompile::{PrecompileAuxData, MODEXP_INPUT_LIMIT, MODEXP_SIZE_LIMIT}; #[derive(Clone, Debug)] struct RandPowRepresent { @@ -25,29 +31,33 @@ struct RandPowRepresent { } impl RandPowRepresent { - const BIT_EXP_MAX_DEGREE: usize = 4; /// build randomness r, r**2, ... r**(2**BIT_LIMIT) - pub fn base_pows_expr(randomness: Expression) -> [Expression;BIT_LIMIT] { - std::iter::successors( - Some(randomness), - |r| Some(r.clone() * r.clone()) - ).take(BIT_LIMIT) - .collect::>() - .try_into() - .expect("same length") + pub fn base_pows_expr(randomness: Expression) -> [Expression; BIT_LIMIT] { + std::iter::successors(Some(randomness), |r| Some(r.clone() * r.clone())) + .take(BIT_LIMIT) + .collect::>() + .try_into() + .expect("same length") } /// build r**EXP (EXP can be represented by BIT_LIMIT bits) pub fn pows_expr(randomness: Expression) -> Expression { - assert!(2usize.pow(BIT_LIMIT as u32) > EXP, "EXP ({EXP}) can not exceed bit limit (2**{BIT_LIMIT}-1)"); - let bits : [bool; BIT_LIMIT]= EXP.as_bits(); + assert!( + 2usize.pow(BIT_LIMIT as u32) > EXP, + "EXP ({EXP}) can not exceed bit limit (2**{BIT_LIMIT}-1)" + ); + let bits: [bool; BIT_LIMIT] = EXP.as_bits(); let base_pows = Self::base_pows_expr(randomness); - bits.as_slice().iter().rev().zip(&base_pows).fold( - 1.expr(), - |calc, (&bit, base_pow)|if bit {calc * base_pow.clone()} else {calc} - ) + bits.as_slice() + .iter() + .rev() + .zip(&base_pows) + .fold( + 1.expr(), + |calc, (&bit, base_pow)| if bit { calc * base_pow.clone() } else { calc }, + ) } /// refere to a binary represent of exponent (like BinaryNumberGadget), can @@ -61,14 +71,15 @@ impl RandPowRepresent { let bits = BinaryNumberGadget::construct(cb, exponent); let base_pows = Self::base_pows_expr(randomness); let mut pow_assembles = Vec::new(); - let mut pow = linked_val.unwrap_or_else(||1.expr()); - for (n, (base_pow, exp_bit)) in base_pows.into_iter() - .zip(bits.bits.as_slice().iter().rev()).enumerate(){ - + let mut pow = linked_val.unwrap_or_else(|| 1.expr()); + for (n, (base_pow, exp_bit)) in base_pows + .into_iter() + .zip(bits.bits.as_slice().iter().rev()) + .enumerate() + { pow = pow * util::select::expr(exp_bit.expr(), base_pow, 1.expr()); if pow.degree() > Self::BIT_EXP_MAX_DEGREE { - let cached_cell = cb.query_cell_phase2(); cb.require_equal( "pow_assemble cached current expression", @@ -76,10 +87,9 @@ impl RandPowRepresent { pow.clone(), ); - pow = cached_cell.expr(); + pow = cached_cell.expr(); pow_assembles.push((cached_cell, n)); } - } Self { @@ -89,9 +99,13 @@ impl RandPowRepresent { } } - pub fn expr(&self) -> Expression {self.pow.clone()} + pub fn expr(&self) -> Expression { + self.pow.clone() + } - pub fn phase2_cell_cost(&self) -> usize {self.pow_assembles.len()} + pub fn phase2_cell_cost(&self) -> usize { + self.pow_assembles.len() + } pub fn assign( &self, @@ -100,26 +114,28 @@ impl RandPowRepresent { exponent: usize, linked_value: Option>, ) -> Result, Error> { - assert!(2usize.pow(BIT_LIMIT as u32) > exponent, "exponent ({exponent}) can not exceed bit limit (2**{BIT_LIMIT}-1)"); + assert!( + 2usize.pow(BIT_LIMIT as u32) > exponent, + "exponent ({exponent}) can not exceed bit limit (2**{BIT_LIMIT}-1)" + ); self.bits.assign(region, offset, exponent)?; - let bits : [bool; BIT_LIMIT]= exponent.as_bits(); - let base_pows = std::iter::successors( - Some(region - .challenges() - .keccak_input()), - |val| Some(val.map(|v|v.square())) - ).take(BIT_LIMIT); + let bits: [bool; BIT_LIMIT] = exponent.as_bits(); + let base_pows = std::iter::successors(Some(region.challenges().keccak_input()), |val| { + Some(val.map(|v| v.square())) + }) + .take(BIT_LIMIT); let mut pow_cached_i = self.pow_assembles.iter(); let mut cached_cell = pow_cached_i.next(); - let mut value_should_assigned = linked_value.unwrap_or_else(||Value::known(F::one())); - - for (n, (base_pow, &bit)) in - base_pows - .zip(bits.as_slice().iter().rev()) - .enumerate(){ - - value_should_assigned = value_should_assigned * (if bit {base_pow} else {Value::known(F::one())}); + let mut value_should_assigned = linked_value.unwrap_or_else(|| Value::known(F::one())); + + for (n, (base_pow, &bit)) in base_pows.zip(bits.as_slice().iter().rev()).enumerate() { + value_should_assigned = value_should_assigned + * (if bit { + base_pow + } else { + Value::known(F::one()) + }); if let Some((cell, i)) = cached_cell { if *i == n { cell.assign(region, offset, value_should_assigned)?; @@ -134,9 +150,9 @@ impl RandPowRepresent { const SIZE_LIMIT: usize = MODEXP_SIZE_LIMIT; const SIZE_REPRESENT_BITS: usize = 6; -const SIZE_REPRESENT_BYTES: usize = SIZE_LIMIT/256 + 1; -const INPUT_LIMIT: usize = 32*6; -const INPUT_REPRESENT_BYTES: usize = MODEXP_INPUT_LIMIT/256 + 1; +const SIZE_REPRESENT_BYTES: usize = SIZE_LIMIT / 256 + 1; +const INPUT_LIMIT: usize = 32 * 6; +const INPUT_REPRESENT_BYTES: usize = MODEXP_INPUT_LIMIT / 256 + 1; const INPUT_REPRESENT_BITS: usize = 8; type Word = [Cell; 32]; @@ -147,8 +163,7 @@ fn assign_word( cells: &[Cell; N], bytes: [u8; N], ) -> Result<(), Error> { - - for (cell, byte) in cells.iter().zip(bytes){ + for (cell, byte) in cells.iter().zip(bytes) { cell.assign(region, offset, Value::known(F::from(byte as u64)))?; } @@ -156,15 +171,22 @@ fn assign_word( } // rlc word, in the reversed byte order -fn rlc_word_rev(cells: &[Cell; N], randomness: Expression) -> Expression { - cells.iter().map(|cell| cell.expr()).reduce( - |acc, value| acc * randomness.clone() + value - ).expect("values should not be empty") +fn rlc_word_rev( + cells: &[Cell; N], + randomness: Expression, +) -> Expression { + cells + .iter() + .map(|cell| cell.expr()) + .reduce(|acc, value| acc * randomness.clone() + value) + .expect("values should not be empty") } // calc for big-endian (notice util::expr_from_bytes calc for little-endian) fn expr_from_bytes>(bytes: &[E]) -> Expression { - bytes.iter().fold(0.expr(), |acc, byte| acc * F::from(256) + byte.expr()) + bytes + .iter() + .fold(0.expr(), |acc, byte| acc * F::from(256) + byte.expr()) } #[derive(Clone, Debug)] @@ -180,17 +202,23 @@ impl SizeRepresent { let len_bytes = cb.query_bytes(); let expression = rlc_word_rev(&len_bytes, cb.challenges().keccak_input()); // we calculate at most 31 bytes so it can be fit into a field - let len_blank_bytes = len_bytes[..(32-SIZE_REPRESENT_BYTES)] - .iter().map(Cell::expr).collect::>(); - let is_rest_field_zero = IsZeroGadget::construct(cb, - "if length field exceed 1 byte", - expr_from_bytes(&len_blank_bytes), - ); - let len_effect_bytes = len_bytes[(32-SIZE_REPRESENT_BYTES)..] - .iter().map(Cell::expr).collect::>(); - let is_not_exceed_limit = LtGadget::construct(cb, + let len_blank_bytes = len_bytes[..(32 - SIZE_REPRESENT_BYTES)] + .iter() + .map(Cell::expr) + .collect::>(); + let is_rest_field_zero = IsZeroGadget::construct( + cb, + "if length field exceed 1 byte", + expr_from_bytes(&len_blank_bytes), + ); + let len_effect_bytes = len_bytes[(32 - SIZE_REPRESENT_BYTES)..] + .iter() + .map(Cell::expr) + .collect::>(); + let is_not_exceed_limit = LtGadget::construct( + cb, expr_from_bytes(&len_effect_bytes), - (SIZE_LIMIT+1).expr(), + (SIZE_LIMIT + 1).expr(), ); Self { len_bytes, @@ -207,19 +235,18 @@ impl SizeRepresent { /// the value of size pub fn value(&self) -> Expression { - let len_effect_bytes = self.len_bytes[(32-SIZE_REPRESENT_BYTES)..] - .iter().map(Cell::expr).collect::>(); + let len_effect_bytes = self.len_bytes[(32 - SIZE_REPRESENT_BYTES)..] + .iter() + .map(Cell::expr) + .collect::>(); expr_from_bytes(&len_effect_bytes) } - pub fn is_valid(&self) -> Expression { - util::and::expr( - [ - self.is_rest_field_zero.expr(), - self.is_not_exceed_limit.expr(), - ] - ) + util::and::expr([ + self.is_rest_field_zero.expr(), + self.is_not_exceed_limit.expr(), + ]) } pub fn assign( @@ -233,28 +260,26 @@ impl SizeRepresent { assign_word(region, offset, &self.len_bytes, bytes)?; - let rest_field = U256::from_big_endian(&bytes[..(32-SIZE_REPRESENT_BYTES)]); - let effect_field = U256::from_big_endian(&bytes[(32-SIZE_REPRESENT_BYTES)..]); + let rest_field = U256::from_big_endian(&bytes[..(32 - SIZE_REPRESENT_BYTES)]); + let effect_field = U256::from_big_endian(&bytes[(32 - SIZE_REPRESENT_BYTES)..]); - self.is_rest_field_zero.assign(region, offset, rest_field.to_scalar().unwrap())?; + self.is_rest_field_zero + .assign(region, offset, rest_field.to_scalar().unwrap())?; self.is_not_exceed_limit.assign( - region, - offset, - effect_field.to_scalar().unwrap(), - F::from((SIZE_LIMIT + 1)as u64), + region, + offset, + effect_field.to_scalar().unwrap(), + F::from((SIZE_LIMIT + 1) as u64), )?; Ok(()) } - - - } type RandPow = RandPowRepresent; // parse as (valid, len, value: [base, exp, modulus]) -type InputParsedResult = (bool, [U256;3], [[u8;SIZE_LIMIT];3]); -type OutputParsedResult = (usize, [u8;SIZE_LIMIT]); +type InputParsedResult = (bool, [U256; 3], [[u8; SIZE_LIMIT]; 3]); +type OutputParsedResult = (usize, [u8; SIZE_LIMIT]); #[derive(Clone, Debug)] struct ModExpInputs { @@ -285,8 +310,8 @@ impl ModExpInputs { let modulus_len = SizeRepresent::configure(cb); let exp_len = SizeRepresent::configure(cb); - let r_pow_32 = RandPowRepresent::<_, 6>::base_pows_expr( - cb.challenges().keccak_input())[5].clone(); //r**32 + let r_pow_32 = + RandPowRepresent::<_, 6>::base_pows_expr(cb.challenges().keccak_input())[5].clone(); //r**32 let r_pow_64 = r_pow_32.clone().square(); let base = cb.query_bytes(); @@ -298,7 +323,8 @@ impl ModExpInputs { let modulus_limbs = Limbs::configure(cb, &modulus); let input_valid = cb.query_cell(); - cb.require_equal("mark input valid by checking 3 lens is valid", + cb.require_equal( + "mark input valid by checking 3 lens is valid", input_valid.expr(), util::and::expr([ base_len.is_valid(), @@ -307,37 +333,29 @@ impl ModExpInputs { ]), ); - let base_len_expected = util::select::expr( - input_valid.expr(), - base_len.value(), - SIZE_LIMIT.expr(), - ); + let base_len_expected = + util::select::expr(input_valid.expr(), base_len.value(), SIZE_LIMIT.expr()); - let exp_len_expected = util::select::expr( - input_valid.expr(), - exp_len.value(), - SIZE_LIMIT.expr(), - ); + let exp_len_expected = + util::select::expr(input_valid.expr(), exp_len.value(), SIZE_LIMIT.expr()); - let modulus_len_expected = util::select::expr( - input_valid.expr(), - modulus_len.value(), - SIZE_LIMIT.expr(), - ); + let modulus_len_expected = + util::select::expr(input_valid.expr(), modulus_len.value(), SIZE_LIMIT.expr()); - let input_expected = 96.expr() + base_len_expected.clone() + exp_len_expected.clone() + modulus_len_expected.clone(); + let input_expected = 96.expr() + + base_len_expected.clone() + + exp_len_expected.clone() + + modulus_len_expected.clone(); - let is_input_need_padding = LtGadget::construct( - cb, - input_bytes_len.clone(), - input_expected.clone(), - ); + let is_input_need_padding = + LtGadget::construct(cb, input_bytes_len.clone(), input_expected.clone()); - let padding_pow = RandPowRepresent::configure(cb, + let padding_pow = RandPowRepresent::configure( + cb, cb.challenges().keccak_input(), util::select::expr( - is_input_need_padding.expr(), - input_expected - input_bytes_len, + is_input_need_padding.expr(), + input_expected - input_bytes_len, 0.expr(), ), None, @@ -346,39 +364,43 @@ impl ModExpInputs { // we put correct size in each input word if input is valid // else we just put as most as possible bytes (32) into it // so we finally handle the memory in limited sized (32*3) - let modulus_pow = RandPow::configure(cb, + let modulus_pow = RandPow::configure( + cb, cb.challenges().keccak_input(), modulus_len_expected, None, ); // exp_pow = r**(modulus_len + exp_len) - let exp_pow = RandPow::configure(cb, + let exp_pow = RandPow::configure( + cb, cb.challenges().keccak_input(), exp_len_expected, Some(modulus_pow.expr()), ); // base_pow = r**(modulus_len + exp_len + base_len) - let base_pow = RandPow::configure(cb, + let base_pow = RandPow::configure( + cb, cb.challenges().keccak_input(), base_len_expected, Some(exp_pow.expr()), ); - cb.require_equal("acc bytes must equal", + cb.require_equal( + "acc bytes must equal", padding_pow.expr() * input_bytes_acc, rlc_word_rev(&modulus, cb.challenges().keccak_input()) //rlc of base + modulus_pow.expr() * rlc_word_rev(&exp, cb.challenges().keccak_input()) //rlc of exp plus r**base_len + exp_pow.expr() * rlc_word_rev(&base, cb.challenges().keccak_input()) //rlc of exp plus r**(base_len + exp_len) + base_pow.expr() * modulus_len.memory_rlc() + base_pow.expr() * r_pow_32 * exp_len.memory_rlc() - + base_pow.expr() * r_pow_64 * base_len.memory_rlc() + + base_pow.expr() * r_pow_64 * base_len.memory_rlc(), ); // println!("phase 2 cell used {}", - // padding_pow.phase2_cell_cost() + [&modulus_pow, &exp_pow, &base_pow].iter().map(|pw|pw.phase2_cell_cost()).sum::() - // ); + // padding_pow.phase2_cell_cost() + [&modulus_pow, &exp_pow, + // &base_pow].iter().map(|pw|pw.phase2_cell_cost()).sum::() ); Self { base_len, @@ -399,8 +421,12 @@ impl ModExpInputs { } } - pub fn modulus_len(&self) -> Expression {self.modulus_len.value()} - pub fn is_valid(&self) -> Expression {self.input_valid.expr()} + pub fn modulus_len(&self) -> Expression { + self.modulus_len.value() + } + pub fn is_valid(&self) -> Expression { + self.input_valid.expr() + } pub fn assign( &self, @@ -409,52 +435,79 @@ impl ModExpInputs { (input_valid, lens, values): InputParsedResult, input_len: usize, ) -> Result<(), Error> { - self.input_valid.assign(region, offset, Value::known(if input_valid {F::one()} else {F::zero()}))?; + self.input_valid.assign( + region, + offset, + Value::known(if input_valid { F::one() } else { F::zero() }), + )?; - for (len, len_represent) in lens.iter().zip([&self.base_len, &self.exp_len, &self.modulus_len]){ + for (len, len_represent) in + lens.iter() + .zip([&self.base_len, &self.exp_len, &self.modulus_len]) + { len_represent.assign(region, offset, len)?; } let mut linked_v = None; - for (len, pow) in lens.iter().zip([&self.base_pow, &self.exp_pow, &self.modulus_pow]).rev(){ + for (len, pow) in lens + .iter() + .zip([&self.base_pow, &self.exp_pow, &self.modulus_pow]) + .rev() + { let assigned = pow.assign( - region, - offset, - if input_valid {len.as_usize()} else {SIZE_LIMIT}, + region, + offset, + if input_valid { + len.as_usize() + } else { + SIZE_LIMIT + }, linked_v, )?; linked_v = Some(assigned); } - for (val_r, input_limbs) in values.iter().zip([&self.base_limbs, &self.exp_limbs, &self.modulus_limbs]){ + for (val_r, input_limbs) in + values + .iter() + .zip([&self.base_limbs, &self.exp_limbs, &self.modulus_limbs]) + { input_limbs.assign(region, offset, val_r)?; } - for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]){ + for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]) { assign_word(region, offset, input_bytes, val)?; } let expected_len = if input_valid { lens.iter().map(U256::as_usize).sum::() + 96 - } else {INPUT_LIMIT}; + } else { + INPUT_LIMIT + }; - self.is_input_need_padding.assign(region, offset, - F::from(input_len as u64), + self.is_input_need_padding.assign( + region, + offset, + F::from(input_len as u64), F::from(expected_len as u64), )?; - self.padding_pow.assign(region, offset, - if input_len < expected_len {expected_len - input_len} else {0}, + self.padding_pow.assign( + region, + offset, + if input_len < expected_len { + expected_len - input_len + } else { + 0 + }, None, )?; Ok(()) } - } - #[derive(Clone, Debug)] struct ModExpOutputs { result: Word, @@ -469,16 +522,16 @@ impl ModExpOutputs { inner_success: Expression, modulus_len: Expression, ) -> Self { - let output_len = inner_success * modulus_len; let is_result_zero = IsZeroGadget::construct(cb, "if output len is nil", output_len); - + let result = cb.query_bytes(); let result_limbs = Limbs::configure(cb, &result); - cb.condition(util::not::expr(is_result_zero.expr()), |cb|{ - cb.require_equal("acc bytes must equal", - output_bytes_acc, + cb.condition(util::not::expr(is_result_zero.expr()), |cb| { + cb.require_equal( + "acc bytes must equal", + output_bytes_acc, rlc_word_rev(&result, cb.challenges().keccak_input()), ); }); @@ -490,7 +543,9 @@ impl ModExpOutputs { } } - pub fn is_output_nil(&self) -> Expression {self.is_result_zero.expr()} + pub fn is_output_nil(&self) -> Expression { + self.is_result_zero.expr() + } pub fn assign( &self, @@ -498,34 +553,29 @@ impl ModExpOutputs { offset: usize, (output_len, data): OutputParsedResult, ) -> Result<(), Error> { - self.is_result_zero.assign(region, offset, F::from(output_len as u64))?; + self.is_result_zero + .assign(region, offset, F::from(output_len as u64))?; self.result_limbs.assign(region, offset, &data)?; assign_word(region, offset, &self.result, data)?; Ok(()) } - } - #[derive(Clone, Debug)] pub(crate) struct Limbs { byte14_split_lo: Cell, byte14_split_hi: Cell, - limbs: [Expression;3], + limbs: [Expression; 3], } - impl Limbs { - pub fn configure( - cb: &mut EVMConstraintBuilder, - word: &Word, - ) -> Self { + pub fn configure(cb: &mut EVMConstraintBuilder, word: &Word) -> Self { let byte14_split_lo = cb.query_byte(); let byte14_split_hi = cb.query_byte(); cb.require_equal( "split 14th byte in word into half", - word[MODEXP_SIZE_LIMIT-14].expr(), + word[MODEXP_SIZE_LIMIT - 14].expr(), byte14_split_lo.expr() + byte14_split_hi.expr(), ); @@ -534,17 +584,16 @@ impl Limbs { let limbs = [ expr_from_bytes( &std::iter::once(&byte14_split_lo) - .chain(&word[MODEXP_SIZE_LIMIT-13..]) - .collect::>() + .chain(&word[MODEXP_SIZE_LIMIT - 13..]) + .collect::>(), ), expr_from_bytes( - &word[MODEXP_SIZE_LIMIT-27..MODEXP_SIZE_LIMIT-14].iter() - .chain(std::iter::once(&byte14_split_hi)) - .collect::>() + &word[MODEXP_SIZE_LIMIT - 27..MODEXP_SIZE_LIMIT - 14] + .iter() + .chain(std::iter::once(&byte14_split_hi)) + .collect::>(), ) * inv_16, - expr_from_bytes( - &word[..MODEXP_SIZE_LIMIT-27] - ), + expr_from_bytes(&word[..MODEXP_SIZE_LIMIT - 27]), ]; Self { @@ -554,7 +603,9 @@ impl Limbs { } } - pub fn limbs(&self) -> [Expression;3] {self.limbs.clone()} + pub fn limbs(&self) -> [Expression; 3] { + self.limbs.clone() + } pub fn assign( &self, @@ -562,14 +613,15 @@ impl Limbs { offset: usize, big_int: &[u8; MODEXP_SIZE_LIMIT], ) -> Result<(), Error> { + let byte14_lo = big_int[MODEXP_SIZE_LIMIT - 14] & 0xf; + let byte14_hi = big_int[MODEXP_SIZE_LIMIT - 14] & 0xf0; - let byte14_lo = big_int[MODEXP_SIZE_LIMIT-14] & 0xf; - let byte14_hi = big_int[MODEXP_SIZE_LIMIT-14] & 0xf0; - - self.byte14_split_lo.assign(region, offset, Value::known(F::from(byte14_lo as u64)))?; - self.byte14_split_hi.assign(region, offset, Value::known(F::from(byte14_hi as u64)))?; + self.byte14_split_lo + .assign(region, offset, Value::known(F::from(byte14_lo as u64)))?; + self.byte14_split_hi + .assign(region, offset, Value::known(F::from(byte14_hi as u64)))?; Ok(()) - } + } } #[derive(Clone, Debug)] @@ -595,7 +647,6 @@ impl ExecutionGadget for ModExpGadget { const NAME: &'static str = "MODEXP"; fn configure(cb: &mut EVMConstraintBuilder) -> Self { - // we 'copy' the acc_bytes cell inside call_op step, so it must be the first query cells let input_bytes_acc = cb.query_cell_phase2(); let output_bytes_acc = cb.query_cell_phase2(); @@ -622,31 +673,32 @@ impl ExecutionGadget for ModExpGadget { let call_success = util::and::expr([ input.is_valid(), - //TODO: replace this constants when gas gadget is ready + //TODO: replace this constants when gas gadget is ready 1.expr(), ]); cb.require_equal( - "call success if valid input and enough gas", - is_success.expr(), + "call success if valid input and enough gas", + is_success.expr(), call_success.clone(), ); - let output = ModExpOutputs::configure(cb, + let output = ModExpOutputs::configure( + cb, output_bytes_acc.expr(), - //FIXME: there may be still some edge cases lead to nil output (even modulus_len is not 0) + //FIXME: there may be still some edge cases lead to nil output (even modulus_len is + // not 0) call_success, input.modulus_len(), ); - cb.condition(util::not::expr(output.is_output_nil()), |cb|{ + cb.condition(util::not::expr(output.is_output_nil()), |cb| { cb.modexp_table_lookup( - input.base_limbs.limbs(), - input.exp_limbs.limbs(), - input.modulus_limbs.limbs(), + input.base_limbs.limbs(), + input.exp_limbs.limbs(), + input.modulus_limbs.limbs(), output.result_limbs.limbs(), ); - }); Self { @@ -673,25 +725,23 @@ impl ExecutionGadget for ModExpGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - if let Some(PrecompileAuxData::Modexp(data)) = &step.aux_data { - println!("exp data: {:?}", data); - self.input.assign(region, offset, + self.input.assign( + region, + offset, (data.valid, data.input_lens, data.inputs), data.input_memory.len(), )?; - self.output.assign(region, offset, ( - data.output_len, - data.output, - ))?; + self.output + .assign(region, offset, (data.output_len, data.output))?; let input_rlc = region .challenges() .keccak_input() - .map(|randomness|rlc::value(data.input_memory.iter().rev(), randomness)); + .map(|randomness| rlc::value(data.input_memory.iter().rev(), randomness)); let output_rlc = region .challenges() @@ -700,7 +750,6 @@ impl ExecutionGadget for ModExpGadget { self.input_bytes_acc.assign(region, offset, input_rlc)?; self.output_bytes_acc.assign(region, offset, output_rlc)?; - } else { log::error!("unexpected aux_data {:?} for modexp", step.aux_data); return Err(Error::Synthesis); @@ -743,7 +792,6 @@ impl ExecutionGadget for ModExpGadget { } } - #[cfg(test)] mod test { use super::*; @@ -757,45 +805,55 @@ mod test { use crate::test_util::CircuitTestBuilder; - #[test] - fn test_limbs(){ - use misc_precompiled_circuit::circuits::modexp::Number; + fn test_limbs() { + use crate::table::ModExpTable; use halo2_proofs::{arithmetic::FieldExt, halo2curves::bn256::Fr}; + use misc_precompiled_circuit::circuits::modexp::Number; use num_bigint::BigUint; - use crate::table::ModExpTable; // simply take an hash for test - let bi = BigUint::parse_bytes(b"fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47", 16).unwrap(); + let bi = BigUint::parse_bytes( + b"fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47", + 16, + ) + .unwrap(); let n = Number::::from_bn(&bi); let w = word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"); - let mut bytes = [0u8;32]; + let mut bytes = [0u8; 32]; w.to_big_endian(&mut bytes); assert_eq!(BigUint::from_bytes_be(&bytes), bi); - let byte14_lo = bytes[MODEXP_SIZE_LIMIT-14] & 0xf; - let byte14_hi = bytes[MODEXP_SIZE_LIMIT-14] & 0xf0; + let byte14_lo = bytes[MODEXP_SIZE_LIMIT - 14] & 0xf; + let byte14_hi = bytes[MODEXP_SIZE_LIMIT - 14] & 0xf0; - let limb0 : Fr = U256::from_big_endian( + let limb0: Fr = U256::from_big_endian( &(std::iter::once(byte14_lo)) - .chain(bytes[MODEXP_SIZE_LIMIT-13..].iter().copied()) - .collect::>() - ).to_scalar().unwrap(); - - let limb1 : Fr = U256::from_big_endian( - &bytes[MODEXP_SIZE_LIMIT-27..MODEXP_SIZE_LIMIT-14] - .iter().copied() - .chain(std::iter::once(byte14_hi)) - .collect::>() - ).to_scalar().unwrap(); + .chain(bytes[MODEXP_SIZE_LIMIT - 13..].iter().copied()) + .collect::>(), + ) + .to_scalar() + .unwrap(); + + let limb1: Fr = U256::from_big_endian( + &bytes[MODEXP_SIZE_LIMIT - 27..MODEXP_SIZE_LIMIT - 14] + .iter() + .copied() + .chain(std::iter::once(byte14_hi)) + .collect::>(), + ) + .to_scalar() + .unwrap(); - let limb2 : Fr = U256::from_big_endian(&bytes[..MODEXP_SIZE_LIMIT-27]).to_scalar().unwrap(); + let limb2: Fr = U256::from_big_endian(&bytes[..MODEXP_SIZE_LIMIT - 27]) + .to_scalar() + .unwrap(); assert_eq!(limb0, n.limbs[0].value); assert_eq!(limb1, n.limbs[1].value * Fr::from(16 as u64)); assert_eq!(limb2, n.limbs[2].value); - let nt : Fr = ModExpTable::native_u256(&w); + let nt: Fr = ModExpTable::native_u256(&w); let table_split = ModExpTable::split_u256_108bit_limbs(&w); assert_eq!(Fr::from_u128(table_split[0]), n.limbs[0].value); @@ -805,7 +863,6 @@ mod test { //Limb::new(None, value) } - lazy_static::lazy_static! { static ref TEST_VECTOR: Vec = { vec![ @@ -817,17 +874,17 @@ mod test { PUSH1(0x00) MSTORE // Esize - PUSH1(0x1) + PUSH1(0x1) PUSH1(0x20) MSTORE // Msize - PUSH1(0x1) + PUSH1(0x1) PUSH1(0x40) MSTORE // B, E and M PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) PUSH1(0x60) - MSTORE + MSTORE }, call_data_offset: 0x0.into(), call_data_length: 0x63.into(), @@ -844,17 +901,17 @@ mod test { PUSH1(0x00) MSTORE // Esize - PUSH1(0x3) + PUSH1(0x3) PUSH1(0x20) MSTORE // Msize - PUSH1(0x2) + PUSH1(0x2) PUSH1(0x40) MSTORE // B, E and M PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) PUSH1(0x60) - MSTORE + MSTORE }, call_data_offset: 0x0.into(), call_data_length: 0x66.into(), @@ -871,17 +928,17 @@ mod test { PUSH1(0x00) MSTORE // Esize - PUSH1(0x3) + PUSH1(0x3) PUSH1(0x20) MSTORE // Msize - PUSH1(0x2) + PUSH1(0x2) PUSH1(0x40) MSTORE // B, E and M PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) PUSH1(0x60) - MSTORE + MSTORE }, call_data_offset: 0x0.into(), call_data_length: 0x65.into(), @@ -889,7 +946,7 @@ mod test { ret_size: 0x01.into(), address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() - }, + }, ] }; @@ -903,11 +960,11 @@ mod test { PUSH1(0x00) MSTORE // Esize - PUSH1(0x20) + PUSH1(0x20) PUSH1(0x20) MSTORE // Msize - PUSH1(0x20) + PUSH1(0x20) PUSH1(0x40) MSTORE // B, E and M @@ -936,11 +993,11 @@ mod test { PUSH1(0x00) MSTORE // Esize - PUSH1(0x20) + PUSH1(0x20) PUSH1(0x20) MSTORE // Msize - PUSH1(0x20) + PUSH1(0x20) PUSH1(0x40) MSTORE // B, E and M @@ -960,7 +1017,7 @@ mod test { ret_size: 0x01.into(), address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() - }, + }, ] }; @@ -974,17 +1031,17 @@ mod test { PUSH1(0x00) MSTORE // Esize - PUSH1(0x1) + PUSH1(0x1) PUSH1(0x20) MSTORE // Msize - PUSH1(0x21) + PUSH1(0x21) PUSH1(0x40) MSTORE // B, E and M PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) PUSH1(0x60) - MSTORE + MSTORE }, call_data_offset: 0x0.into(), call_data_length: 0x63.into(), @@ -994,19 +1051,18 @@ mod test { ..Default::default() }, ] - }; + }; } #[ignore] #[test] - fn precompile_modexp_test_fast() { + fn precompile_modexp_test_fast() { let bytecode = TEST_VECTOR[0].with_call_op(OpcodeId::STATICCALL); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), ) .run(); - } #[ignore] @@ -1047,5 +1103,4 @@ mod test { .run(); } } - } diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index e7928eb7d4..7fd8dfcd3e 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -470,11 +470,11 @@ impl Lookup { sig_s_rlc.clone(), recovered_addr.clone(), ], - Self::ModExpTable { - base_limbs, - exp_limbs, - modulus_limbs, - result_limbs + Self::ModExpTable { + base_limbs, + exp_limbs, + modulus_limbs, + result_limbs, } => vec![ 1.expr(), // q_head base_limbs[0].clone(), diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 488f01e0bf..df958502ab 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1422,11 +1422,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ) { self.add_lookup( "u256 exponentiation modulus lookup", - Lookup::ModExpTable { - base_limbs, - exp_limbs, - modulus_limbs, - result_limbs + Lookup::ModExpTable { + base_limbs, + exp_limbs, + modulus_limbs, + result_limbs, }, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index b6fb328987..a00a10e864 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -154,7 +154,7 @@ impl PrecompileGadget { "copy output bytes", output_bytes_rlc.clone(), output_bytes_acc_copied.expr(), - ); + ); }); }); diff --git a/zkevm-circuits/src/precompile_circuit/mod.rs b/zkevm-circuits/src/precompile_circuit/mod.rs index f015a66e5c..ed3a3b837f 100644 --- a/zkevm-circuits/src/precompile_circuit/mod.rs +++ b/zkevm-circuits/src/precompile_circuit/mod.rs @@ -1,5 +1,4 @@ //! Precompile circuits -//! /// mod exp circuit for U256 integer -pub mod modexp; \ No newline at end of file +pub mod modexp; diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index 5368bde943..f9555fc34e 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -1,31 +1,20 @@ - use halo2_proofs::{ circuit::{Layouter, Region, Value}, - plonk::{ - Advice, Column, ConstraintSystem, Error - }, + plonk::{Advice, Column, ConstraintSystem, Error}, }; -use eth_types::{Field, Word}; -use bus_mapping::circuit_input_builder::ModExpEvent; use crate::{ table::ModExpTable, util::{Challenges, SubCircuit, SubCircuitConfig}, witness, }; +use bus_mapping::circuit_input_builder::ModExpEvent; +use eth_types::{Field, Word}; //use misc_precompiled_circuit::value_for_assign; use misc_precompiled_circuit::circuits::{ - range::{ - RangeCheckConfig, - RangeCheckChip, - }, - modexp::{ - ModExpChip, - ModExpConfig, - Number, - Limb, - }, + modexp::{Limb, ModExpChip, ModExpConfig, Number}, + range::{RangeCheckChip, RangeCheckConfig}, }; /// ModExp circuit config @@ -36,14 +25,11 @@ pub struct ModExpCircuitConfig { modexp_table: ModExpTable, } -impl SubCircuitConfig for ModExpCircuitConfig { +impl SubCircuitConfig for ModExpCircuitConfig { type ConfigArgs = ModExpTable; /// Return a new ModExpCircuitConfig - fn new( - meta: &mut ConstraintSystem, - modexp_table: Self::ConfigArgs, - ) -> Self { + fn new(meta: &mut ConstraintSystem, modexp_table: Self::ConfigArgs) -> Self { let rangecheck_config = RangeCheckChip::configure(meta); let modexp_config = ModExpChip::configure(meta, &rangecheck_config); Self { @@ -55,8 +41,7 @@ impl SubCircuitConfig for ModExpCircuitConfig { } impl ModExpCircuitConfig { - - pub(crate) fn assign_group( + pub(crate) fn assign_group( &self, region: &mut Region, table_offset: usize, @@ -65,41 +50,53 @@ impl ModExpCircuitConfig { modexp_chip: &ModExpChip, range_check_chip: &mut RangeCheckChip, ) -> Result { - let base = self.assign_value(region, table_offset, self.modexp_table.base, &event.base)?; - let exp = self.assign_value(region, table_offset, self.modexp_table.exp, &event.exponent)?; - let modulus = self.assign_value(region, table_offset, self.modexp_table.modulus, &event.modulus)?; + let exp = + self.assign_value(region, table_offset, self.modexp_table.exp, &event.exponent)?; + let modulus = self.assign_value( + region, + table_offset, + self.modexp_table.modulus, + &event.modulus, + )?; - let ret = modexp_chip.mod_exp(region, range_check_chip, &mut calc_offset, &base, &exp, &modulus)?; + let ret = modexp_chip.mod_exp( + region, + range_check_chip, + &mut calc_offset, + &base, + &exp, + &modulus, + )?; for i in 0..4 { - region.assign_fixed( - || format!("modexp table head {}", table_offset+i), + || format!("modexp table head {}", table_offset + i), self.modexp_table.q_head, table_offset + i, - || Value::known(if i == 0 {F::one()} else {F::zero()}), + || Value::known(if i == 0 { F::one() } else { F::zero() }), )?; - ret.limbs[i].cell.clone() - .expect("should has assigned after modexp") - .copy_advice( - ||"copy to result limbs", - region, - self.modexp_table.result, - table_offset + i - )?; + ret.limbs[i] + .cell + .clone() + .expect("should has assigned after modexp") + .copy_advice( + || "copy to result limbs", + region, + self.modexp_table.result, + table_offset + i, + )?; } Ok(calc_offset) } - fn assign_value( + fn assign_value( &self, region: &mut Region, offset: usize, col: Column, value: &Word, ) -> Result, Error> { - let limbs_v = ModExpTable::split_u256_108bit_limbs(value); let native_v = ModExpTable::native_u256(value); let mut limbs = Vec::new(); @@ -107,27 +104,27 @@ impl ModExpCircuitConfig { for (i, limb) in limbs_v.into_iter().enumerate() { let fv = F::from_u128(limb); let c = region.assign_advice( - || "assign modexp limb", - col, + || "assign modexp limb", + col, offset + i, || Value::known(fv), )?; limbs.push(Limb::new(Some(c), fv)); } let c = region.assign_advice( - || "assign modexp native", - col, - offset + 3, + || "assign modexp native", + col, + offset + 3, || Value::known(native_v), )?; limbs.push(Limb::new(Some(c), native_v)); - Ok(Number {limbs: limbs.try_into().expect("just 4 pushes")}) + Ok(Number { + limbs: limbs.try_into().expect("just 4 pushes"), + }) } - } - -const MODEXPCONFIG_EACH_CHIP_ROWS : usize = 9291; +const MODEXPCONFIG_EACH_CHIP_ROWS: usize = 9291; /// ModExp circuit for precompile modexp #[derive(Clone, Debug, Default)] @@ -143,10 +140,10 @@ impl SubCircuit for ModExpCircuit { } fn new_from_block(block: &witness::Block) -> Self { - let event_limit = block.circuits_params.max_keccak_rows / MODEXPCONFIG_EACH_CHIP_ROWS; let mut exp_events = block.modexp_events.clone(); - assert!(exp_events.len() <= event_limit, + assert!( + exp_events.len() <= event_limit, "no enough rows for modexp circuit, expected {}, limit {}", exp_events.len(), event_limit, @@ -159,7 +156,8 @@ impl SubCircuit for ModExpCircuit { fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { ( block.modexp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS, - (block.modexp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS).max(block.circuits_params.max_keccak_rows), + (block.modexp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS) + .max(block.circuits_params.max_keccak_rows), ) } @@ -169,24 +167,22 @@ impl SubCircuit for ModExpCircuit { _challenges: &Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { - let modexp_chip = ModExpChip::new(config.modexp_config.clone()); let mut range_chip = RangeCheckChip::new(config.rangecheck_config.clone()); layouter.assign_region( || "modexp circuit", |mut region| { - range_chip.initialize(&mut region)?; let mut calc_offset = 0; - for (n, event) in self.0.iter().enumerate(){ + for (n, event) in self.0.iter().enumerate() { calc_offset = config.assign_group( - &mut region, - n*4, - calc_offset, - event, - &modexp_chip, - &mut range_chip + &mut region, + n * 4, + calc_offset, + event, + &modexp_chip, + &mut range_chip, )?; } Ok(()) @@ -194,29 +190,28 @@ impl SubCircuit for ModExpCircuit { )?; config.modexp_table.fill_blank(layouter) - } } #[cfg(test)] mod test { use super::*; - use halo2_proofs::dev::MockProver; + use crate::util::MockChallenges; use halo2_proofs::{ - halo2curves::bn256::Fr, circuit::SimpleFloorPlanner, + dev::MockProver, + halo2curves::bn256::Fr, plonk::{Circuit, ConstraintSystem}, - }; - use crate::util::MockChallenges; + }; impl Circuit for ModExpCircuit { type Config = (ModExpCircuitConfig, MockChallenges); type FloorPlanner = SimpleFloorPlanner; - + fn without_witnesses(&self) -> Self { Self::default() } - + fn configure(meta: &mut ConstraintSystem) -> Self::Config { let modexp_table = ModExpTable::construct(meta); let challenge = MockChallenges::construct(meta); @@ -225,19 +220,14 @@ mod test { challenge, ) } - + fn synthesize( &self, (config, challenge): Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { let challenges = challenge.values(&layouter); - >::synthesize_sub( - self, - &config, - &challenges, - &mut layouter - ) + >::synthesize_sub(self, &config, &challenges, &mut layouter) } } @@ -247,20 +237,30 @@ mod test { let exp = Word::from(2u128); let modulus = Word::from(7u128); let (_, result) = base.pow(exp).div_mod(modulus); - let event1 = ModExpEvent {base, exponent: exp, modulus, result}; - let test_circuit = ModExpCircuit (vec![event1], Default::default()); + let event1 = ModExpEvent { + base, + exponent: exp, + modulus, + result, + }; + let test_circuit = ModExpCircuit(vec![event1], Default::default()); let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } - + #[test] fn test_modexp_circuit_01() { let base = Word::from(1u128); let exp = Word::from(2u128); let modulus = Word::from(7u128); let (_, result) = base.pow(exp).div_mod(modulus); - let event1 = ModExpEvent {base, exponent: exp, modulus, result}; - let test_circuit = ModExpCircuit (vec![event1], Default::default()); + let event1 = ModExpEvent { + base, + exponent: exp, + modulus, + result, + }; + let test_circuit = ModExpCircuit(vec![event1], Default::default()); let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } @@ -270,11 +270,14 @@ mod test { let exp = Word::from(2u128); let modulus = Word::from(7u128); let (_, result) = base.pow(exp).div_mod(modulus); - let event1 = ModExpEvent {base, exponent: exp, modulus, result}; - let test_circuit = ModExpCircuit (vec![event1], Default::default()); + let event1 = ModExpEvent { + base, + exponent: exp, + modulus, + result, + }; + let test_circuit = ModExpCircuit(vec![event1], Default::default()); let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } - - } diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 4988a576e3..ab2da054da 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -66,12 +66,12 @@ use crate::{ exp_circuit::{ExpCircuit, ExpCircuitConfig}, keccak_circuit::{KeccakCircuit, KeccakCircuitConfig, KeccakCircuitConfigArgs}, poseidon_circuit::{PoseidonCircuit, PoseidonCircuitConfig, PoseidonCircuitConfigArgs}, + precompile_circuit, sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, table::SigTable, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{log2_ceil, SubCircuit, SubCircuitConfig}, witness::{block_convert, Block}, - precompile_circuit, }; #[cfg(feature = "zktrie")] @@ -82,8 +82,8 @@ use crate::util::Challenges; use crate::{ state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, PoseidonTable, - PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, TxTable, ModExpTable + BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, ModExpTable, MptTable, + PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, TxTable, }, }; @@ -307,10 +307,8 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "sig circuit"); - let modexp_circuit = precompile_circuit::modexp::ModExpCircuitConfig::new( - meta, - modexp_table, - ); + let modexp_circuit = + precompile_circuit::modexp::ModExpCircuitConfig::new(meta, modexp_table); log_circuit_info(meta, "modexp circuit"); let state_circuit = StateCircuitConfig::new( diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 44439e16df..9e196f5843 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2243,11 +2243,9 @@ pub struct ModExpTable { pub result: Column, } - impl ModExpTable { /// Construct the modexp table. pub fn construct(meta: &mut ConstraintSystem) -> Self { - let ret = Self { q_head: meta.fixed_column(), base: meta.advice_column(), @@ -2263,8 +2261,8 @@ impl ModExpTable { } /// helper for devide a U256 into 3 108bit limbs - pub fn split_u256_108bit_limbs(word: &Word) -> [u128;3]{ - let bit108 = 1u128<<108; + pub fn split_u256_108bit_limbs(word: &Word) -> [u128; 3] { + let bit108 = 1u128 << 108; let (next, limb0) = word.div_mod(U256::from(bit108)); let (limb2, limb1) = next.div_mod(U256::from(bit108)); [limb0.as_u128(), limb1.as_u128(), limb2.as_u128()] @@ -2281,13 +2279,10 @@ impl ModExpTable { } /// fill a blank 4-row region start from offset for empty lookup - pub fn fill_blank( - &self, - layouter: &mut impl Layouter, - ) -> Result<(), Error>{ + pub fn fill_blank(&self, layouter: &mut impl Layouter) -> Result<(), Error> { layouter.assign_region( || "modexp table blank region", - |mut region|{ + |mut region| { for i in 0..4 { // fill last totally 0 row region.assign_fixed( @@ -2296,17 +2291,17 @@ impl ModExpTable { i, || Value::known(F::zero()), )?; - for &col in [&self.base, &self.exp, &self.modulus, &self.result]{ + for &col in [&self.base, &self.exp, &self.modulus, &self.result] { region.assign_advice( || "modexp table blank limb row", - col, - i, + col, + i, || Value::known(F::zero()), - )?; + )?; } } - Ok(()) - } + Ok(()) + }, ) } @@ -2318,19 +2313,17 @@ impl ModExpTable { ) -> Result<(), Error> { layouter.assign_region( || "modexp table", - |mut region|{ - + |mut region| { let mut offset = 0usize; - for event in &block.modexp_events{ - + for event in &block.modexp_events { for i in 0..4 { region.assign_fixed( - || format!("modexp table head {}", offset+i), + || format!("modexp table head {}", offset + i), self.q_head, offset + i, - || Value::known(if i == 0 {F::one()} else {F::zero()}), - )?; + || Value::known(if i == 0 { F::one() } else { F::zero() }), + )?; } let base_limbs = Self::split_u256_108bit_limbs(&event.base); @@ -2339,29 +2332,33 @@ impl ModExpTable { let result_limbs = Self::split_u256_108bit_limbs(&event.result); for i in 0..3 { - for (limbs, &col) in - [base_limbs, exp_limbs, modulus_limbs, result_limbs] - .zip([&self.base, &self.exp, &self.modulus, &self.result]){ - + for (limbs, &col) in [base_limbs, exp_limbs, modulus_limbs, result_limbs] + .zip([&self.base, &self.exp, &self.modulus, &self.result]) + { region.assign_advice( - || format!("modexp table limb row {}", offset+i), - col, - offset + i, + || format!("modexp table limb row {}", offset + i), + col, + offset + i, || Value::known(F::from_u128(limbs[i])), )?; } } // native is not used by lookup (and in fact it can be omitted in dev) - for (word, &col) in [&event.base, &event.exponent, &event.modulus, &event.result] - .zip([&self.base, &self.exp, &self.modulus, &self.result]){ - + for (word, &col) in [ + &event.base, + &event.exponent, + &event.modulus, + &event.result, + ] + .zip([&self.base, &self.exp, &self.modulus, &self.result]) + { region.assign_advice( - || format!("modexp table native row {}", offset+3), - col, - offset + 3, + || format!("modexp table native row {}", offset + 3), + col, + offset + 3, || Value::::known(Self::native_u256(word)), - )?; + )?; } offset += 4; @@ -2372,11 +2369,9 @@ impl ModExpTable { )?; self.fill_blank(layouter) - } } - impl LookupTable for ModExpTable { fn columns(&self) -> Vec> { vec![ @@ -2415,12 +2410,11 @@ impl LookupTable for ModExpTable { meta.query_advice(self.base, Rotation(2)), meta.query_advice(self.exp, Rotation(2)), meta.query_advice(self.modulus, Rotation(2)), - meta.query_advice(self.result, Rotation(2)), + meta.query_advice(self.result, Rotation(2)), ] } } - /// Lookup table for powers of keccak randomness up to exponent in [0, 128) #[derive(Clone, Copy, Debug)] pub struct PowOfRandTable { @@ -2549,4 +2543,4 @@ impl LookupTable for PowOfRandTable { meta.query_advice(self.pow_of_rand, Rotation::cur()), ] } -} \ No newline at end of file +} From 7aa74bec5ea45bd51994874223b256a7157b6855 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 7 Jul 2023 09:23:14 +0800 Subject: [PATCH 33/57] disable ecrecover temporarily. (lint not pass in CI but tests should be able to move forword) --- zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index a00a10e864..43131209bf 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -80,7 +80,7 @@ impl PrecompileGadget { cb.condition(address.value_equals(PrecompileCalls::Ecrecover), |cb| { cb.constrain_next_step(ExecutionState::PrecompileEcrecover, None, |cb| { - let (recovered, msg_hash_rlc, sig_v_rlc, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( +/* let (recovered, msg_hash_rlc, sig_v_rlc, sig_r_rlc, sig_s_rlc, recovered_addr_rlc) = ( cb.query_bool(), cb.query_cell_phase2(), cb.query_cell_phase2(), @@ -113,7 +113,7 @@ impl PrecompileGadget { // If the address was not recovered, RLC(address) == RLC(output) == 0. cb.condition(not::expr(recovered.expr()), |cb| { cb.require_zero("output bytes == 0", output_bytes_rlc.expr()); - }); + });*/ }); }); From 8e5cb4a494f7ede640c2714798a4a5f61485c339 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 7 Jul 2023 13:44:40 +0800 Subject: [PATCH 34/57] post merging fixing --- .../src/evm_circuit/util/constraint_builder.rs | 6 +++--- .../src/evm_circuit/util/precompile_gadget.rs | 18 +++++------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index edff81f23e..0957c4a61e 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -321,7 +321,7 @@ impl<'a, F: Field> ConstrainBuilderCommon for EVMConstraintBuilder<'a, F> { } } -pub(crate) type BoxedClosure = Box)>; +pub(crate) type BoxedClosure<'a, F> = Box) + 'a>; impl<'a, F: Field> EVMConstraintBuilder<'a, F> { pub(crate) fn new( @@ -1466,11 +1466,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { /// used for constraining the internal states for precompile calls. Each precompile call /// expects a different cell layout, but since the next state can be at the most one precompile /// state, we can re-use cells assigned across all those conditions. - pub(crate) fn constrain_mutually_exclusive_next_step( + pub(crate) fn constrain_mutually_exclusive_next_step<'x>( &mut self, conditions: Vec>, next_states: Vec, - constraints: Vec>, + constraints: Vec>, ) { assert_eq!(conditions.len(), constraints.len()); assert_eq!(conditions.len(), next_states.len()); diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index f714728934..831f05e55f 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -78,14 +78,6 @@ impl PrecompileGadget { ); }); - // TODO: is it possible to avoid this? Just a little messy, but `padding_gadget`, - // `input_bytes_rlc` and `output_bytes_rlc` will get dropped later, but the boxed closure - // lives longer. - let padded_rlc1 = padding_gadget.padded_rlc(); - let input_bytes_rlc1 = input_bytes_rlc.expr(); - let output_bytes_rlc1 = output_bytes_rlc.expr(); - let output_bytes_rlc2 = output_bytes_rlc.expr(); - let conditions = vec![ address.value_equals(PrecompileCalls::Ecrecover), address.value_equals(PrecompileCalls::Sha256), @@ -128,7 +120,7 @@ impl PrecompileGadget { }; cb.require_equal( "input bytes (RLC) = [msg_hash | sig_v_rlc | sig_r | sig_s]", - padded_rlc1, + padding_gadget.padded_rlc(), (msg_hash_rlc.expr() * r_pow_96) + (sig_v_rlc.expr() * r_pow_64) + (sig_r_rlc.expr() * r_pow_32) @@ -137,12 +129,12 @@ impl PrecompileGadget { // RLC of output bytes always equals RLC of the recovered address. cb.require_equal( "output bytes (RLC) = recovered address", - output_bytes_rlc1.expr(), + output_bytes_rlc.expr(), recovered_addr_rlc.expr(), ); // If the address was not recovered, RLC(address) == RLC(output) == 0. cb.condition(not::expr(recovered.expr()), |cb| { - cb.require_zero("output bytes == 0", output_bytes_rlc1); + cb.require_zero("output bytes == 0", output_bytes_rlc.expr()); }); }), Box::new(|_cb| { /* Sha256 */ }), @@ -151,8 +143,8 @@ impl PrecompileGadget { cb.condition(is_success, |cb| { cb.require_equal( "input and output bytes are the same", - input_bytes_rlc1, - output_bytes_rlc2, + input_bytes_rlc.clone(), + output_bytes_rlc.clone(), ); cb.require_equal( "input length and precompile return length are the same", From 62635f06bc2bf617bc4d1963dedeb9a9b37a00a3 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 7 Jul 2023 13:51:08 +0800 Subject: [PATCH 35/57] fmt and clippy --- zkevm-circuits/src/evm_circuit/util/constraint_builder.rs | 4 ++-- zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 0957c4a61e..c4d4b96e21 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1466,11 +1466,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { /// used for constraining the internal states for precompile calls. Each precompile call /// expects a different cell layout, but since the next state can be at the most one precompile /// state, we can re-use cells assigned across all those conditions. - pub(crate) fn constrain_mutually_exclusive_next_step<'x>( + pub(crate) fn constrain_mutually_exclusive_next_step( &mut self, conditions: Vec>, next_states: Vec, - constraints: Vec>, + constraints: Vec>, ) { assert_eq!(conditions.len(), constraints.len()); assert_eq!(conditions.len(), next_states.len()); diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 831f05e55f..f480d26bf4 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -153,7 +153,7 @@ impl PrecompileGadget { ); }); }), - Box::new(|cb| { + Box::new(|cb| { let input_bytes_acc_copied = cb.query_cell_phase2(); let output_bytes_acc_copied = cb.query_cell_phase2(); cb.require_equal( From 8e68fb25c9d72da4db48effbb85e93d5c0708997 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sun, 9 Jul 2023 22:59:18 +0800 Subject: [PATCH 36/57] for invalid input --- bus-mapping/src/error.rs | 6 ++- bus-mapping/src/geth_errors.rs | 2 + eth-types/src/lib.rs | 1 + .../execution/precompiles/modexp.rs | 38 +++++++++++++++++++ zkevm-circuits/src/test_util.rs | 18 ++++++++- 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index 8c8f93eb9e..56f19f342e 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -6,8 +6,8 @@ use ethers_providers::ProviderError; use std::error::Error as StdError; use crate::geth_errors::{ - GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_STACK_OVERFLOW, - GETH_ERR_STACK_UNDERFLOW, + GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_PRECOMPILE_MODEXP_INPUT, + GETH_ERR_STACK_OVERFLOW, GETH_ERR_STACK_UNDERFLOW, }; /// Error type for any BusMapping related failure. @@ -209,6 +209,8 @@ pub(crate) fn get_step_reported_error(op: &OpcodeId, error: &str) -> ExecError { ExecError::StackOverflow } else if error.starts_with(GETH_ERR_STACK_UNDERFLOW) { ExecError::StackUnderflow + } else if error.starts_with(GETH_ERR_PRECOMPILE_MODEXP_INPUT) { + ExecError::PrecompileFailed } else { panic!("Unknown GethExecStep.error: {error}"); } diff --git a/bus-mapping/src/geth_errors.rs b/bus-mapping/src/geth_errors.rs index 4a31619107..0d0de8529b 100644 --- a/bus-mapping/src/geth_errors.rs +++ b/bus-mapping/src/geth_errors.rs @@ -6,3 +6,5 @@ pub const GETH_ERR_STACK_UNDERFLOW: &str = "stack underflow"; pub const GETH_ERR_OUT_OF_GAS: &str = "out of gas"; /// Geth error message for gas uint64 overflow pub const GETH_ERR_GAS_UINT_OVERFLOW: &str = "gas uint64 overflow"; +/// Geth error message for precompile +pub const GETH_ERR_PRECOMPILE_MODEXP_INPUT: &str = "modexp"; diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index 34193599ff..6e84ab90cc 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -368,6 +368,7 @@ impl fmt::Debug for GethExecStep { .field("op", &self.op) .field("gas", &format_args!("{}", self.gas.0)) .field("gas_cost", &format_args!("{}", self.gas_cost.0)) + .field("refund", &format_args!("{}", self.refund.0)) .field("depth", &self.depth) .field("error", &self.error) .field("stack", &self.stack) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 7a3feb3f62..335ec5cd63 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -755,6 +755,7 @@ impl ExecutionGadget for ModExpGadget { return Err(Error::Synthesis); } + println!("call success {}", call.is_success); self.is_success.assign( region, offset, @@ -1048,6 +1049,7 @@ mod test { ret_offset: 0x9f.into(), ret_size: 0x01.into(), address: PrecompileCalls::Modexp.address().to_word(), + gas: 100000.into(), ..Default::default() }, ] @@ -1103,4 +1105,40 @@ mod test { .run(); } } + + #[test] + fn precompile_modexp_test_invalid() { + use eth_types::evm_types::Gas; + + for test_vector in TEST_INVALID_VECTOR.iter() { + let bytecode = test_vector.with_call_op(OpcodeId::STATICCALL); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .geth_data_modifier(Box::new(|block| { + let steps = &mut block.geth_traces[0].struct_logs; + let step_len = steps.len(); + let call_step = &mut steps[step_len - 3]; + assert_eq!(call_step.op, OpcodeId::STATICCALL); + // https://github.com/scroll-tech/go-ethereum/blob/2dcc60a082ff89d1c57e497f23daad4823b2fdea/core/vm/contracts.go#L39C42-L39C98 + call_step.error = + Some("modexp temporarily accepts only 32-byte (256-bit) inputs".into()); + call_step.refund.0 = 0; + let next_gas = Gas(call_step.gas.0 - call_step.gas_cost.0); + + let pop_step = &mut steps[step_len - 2]; + assert_eq!(pop_step.op, OpcodeId::POP); + pop_step.gas = next_gas; + pop_step.stack.0[0] = 0.into(); + let next_gas = Gas(pop_step.gas.0 - pop_step.gas_cost.0); + let final_step = &mut steps[step_len - 1]; + assert_eq!(final_step.op, OpcodeId::STOP); + final_step.gas = next_gas; + + println!("trace {:?}", block.geth_traces); + })) + .run(); + } + } } diff --git a/zkevm-circuits/src/test_util.rs b/zkevm-circuits/src/test_util.rs index ba100e4fc9..f09b33244b 100644 --- a/zkevm-circuits/src/test_util.rs +++ b/zkevm-circuits/src/test_util.rs @@ -83,6 +83,7 @@ pub struct CircuitTestBuilder { state_checks: Box, &Vec, &Vec)>, copy_checks: Box, &Vec, &Vec)>, block_modifiers: Vec)>>, + geth_data_modifiers: Vec>, } impl CircuitTestBuilder { @@ -111,6 +112,7 @@ impl CircuitTestBuilder { ), Ok(())); }), block_modifiers: vec![], + geth_data_modifiers: vec![], } } @@ -182,6 +184,17 @@ impl CircuitTestBuilder { self.block_modifiers.push(modifier); self } + + #[allow(clippy::type_complexity)] + /// Allows to provide modifier functions for the [`GethData`] that will be + /// generated within this builder. + /// + /// That allow some test (like precompile) require a modified geth exec + /// results + pub fn geth_data_modifier(mut self, modifier: Box) -> Self { + self.geth_data_modifiers.push(modifier); + self + } } impl CircuitTestBuilder { @@ -199,7 +212,10 @@ impl CircuitTestBuilder { let block: Block = if self.block.is_some() { self.block.unwrap() } else if self.test_ctx.is_some() { - let block: GethData = self.test_ctx.unwrap().into(); + let mut block: GethData = self.test_ctx.unwrap().into(); + for modifier_fn in self.geth_data_modifiers { + modifier_fn.as_ref()(&mut block); + } let mut builder = BlockData::new_from_geth_data_with_params(block.clone(), params) .new_circuit_input_builder(); builder From 55664f7357a6db9d378c07a8bff7b6181419bab6 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Mon, 10 Jul 2023 09:06:41 +0800 Subject: [PATCH 37/57] upgrade modexp circuit dep --- Cargo.lock | 32 ++++++++++++------- .../src/precompile_circuit/modexp.rs | 3 +- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 117cccff7f..944090ad9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1288,7 +1288,7 @@ dependencies = [ "crypto-bigint", "der", "digest 0.10.7", - "ff", + "ff 0.12.1", "generic-array 0.14.7", "group", "pkcs8", @@ -1776,6 +1776,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "bitvec 1.0.1", + "rand_core", + "subtle", +] + [[package]] name = "fixed-hash" version = "0.7.0" @@ -2127,7 +2138,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", "rand_core", "subtle", ] @@ -2162,7 +2173,7 @@ name = "halo2-base" version = "0.2.2" source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#2c225864227e74b207d9f4b9e08c4d5f1afc69a1" dependencies = [ - "ff", + "ff 0.12.1", "halo2_proofs", "itertools", "num-bigint", @@ -2177,7 +2188,7 @@ name = "halo2-ecc" version = "0.2.2" source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#2c225864227e74b207d9f4b9e08c4d5f1afc69a1" dependencies = [ - "ff", + "ff 0.12.1", "group", "halo2-base", "itertools", @@ -2194,9 +2205,9 @@ dependencies = [ [[package]] name = "halo2-gate-generator" version = "0.1.0" -source = "git+https://github.com/DelphinusLab/halo2gategen.git?branch=main#4fd6b4793cfd590d1cc80ba463599280522f8bec" +source = "git+https://github.com/DelphinusLab/halo2gategen.git?branch=main#506f904a2e678c5e7a3f9ea2ba2e7c78b126fc7a" dependencies = [ - "ark-std", + "ff 0.13.0", "halo2_proofs", "lazy_static", "num-bigint", @@ -2240,7 +2251,7 @@ dependencies = [ "cfg-if 0.1.10", "crossbeam", "env_logger 0.8.4", - "ff", + "ff 0.12.1", "group", "halo2curves", "log", @@ -2261,7 +2272,7 @@ name = "halo2curves" version = "0.3.1" source = "git+https://github.com/scroll-tech/halo2curves.git?branch=0.3.1-derive-serde#969f1e44d9713ee4cd552563bd0c762c5d53b56e" dependencies = [ - "ff", + "ff 0.12.1", "group", "lazy_static", "num-bigint", @@ -3214,7 +3225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" dependencies = [ "blake2b_simd", - "ff", + "ff 0.12.1", "group", "lazy_static", "rand", @@ -3999,9 +4010,8 @@ dependencies = [ [[package]] name = "rmd160-circuits" version = "0.1.0" -source = "git+https://github.com/scroll-tech/misc-precompiled-circuit.git?branch=integration#40b2c52919d72404f34a613dc5568f234d0e8a9d" +source = "git+https://github.com/scroll-tech/misc-precompiled-circuit.git?branch=integration#0bdc4e4ba6e35d1edec5fc3ff02084615f8bcec1" dependencies = [ - "ark-std", "halo2-gate-generator", "halo2_proofs", "lazy_static", diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index f9555fc34e..23692e97c0 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -13,8 +13,9 @@ use eth_types::{Field, Word}; //use misc_precompiled_circuit::value_for_assign; use misc_precompiled_circuit::circuits::{ - modexp::{Limb, ModExpChip, ModExpConfig, Number}, + modexp::{ModExpChip, Number}, range::{RangeCheckChip, RangeCheckConfig}, + CommonGateConfig as ModExpConfig, Limb, }; /// ModExp circuit config From 39299ebad3b4539d860839051218016cb7b18f84 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 12 Jul 2023 22:46:02 +0800 Subject: [PATCH 38/57] fixes according to review --- .../evm_circuit/execution/precompiles/modexp.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 335ec5cd63..af3d58da4a 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -322,7 +322,7 @@ impl ModExpInputs { let exp_limbs = Limbs::configure(cb, &exp); let modulus_limbs = Limbs::configure(cb, &modulus); - let input_valid = cb.query_cell(); + let input_valid = cb.query_bool(); cb.require_equal( "mark input valid by checking 3 lens is valid", input_valid.expr(), @@ -388,7 +388,7 @@ impl ModExpInputs { ); cb.require_equal( - "acc bytes must equal", + "input acc bytes must equal", padding_pow.expr() * input_bytes_acc, rlc_word_rev(&modulus, cb.challenges().keccak_input()) //rlc of base + modulus_pow.expr() * rlc_word_rev(&exp, cb.challenges().keccak_input()) //rlc of exp plus r**base_len @@ -528,13 +528,11 @@ impl ModExpOutputs { let result = cb.query_bytes(); let result_limbs = Limbs::configure(cb, &result); - cb.condition(util::not::expr(is_result_zero.expr()), |cb| { - cb.require_equal( - "acc bytes must equal", - output_bytes_acc, - rlc_word_rev(&result, cb.challenges().keccak_input()), - ); - }); + cb.require_equal( + "output acc bytes must equal", + output_bytes_acc, + rlc_word_rev(&result, cb.challenges().keccak_input()), + ); Self { result, @@ -1067,7 +1065,6 @@ mod test { .run(); } - #[ignore] #[test] fn precompile_modexp_test() { let call_kinds = vec![ From 2d71b36d631059713ebe3cacbd77ff77150f611c Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Wed, 12 Jul 2023 23:00:15 +0800 Subject: [PATCH 39/57] fmt --- zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index af3d58da4a..10a0691410 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -532,7 +532,7 @@ impl ModExpOutputs { "output acc bytes must equal", output_bytes_acc, rlc_word_rev(&result, cb.challenges().keccak_input()), - ); + ); Self { result, From 16067325769a0a033dffffdbc1144cb37a73f895 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 13 Jul 2023 19:15:29 +0800 Subject: [PATCH 40/57] update dep, prune ff 0.13 --- Cargo.lock | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 944090ad9b..804407665a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1288,7 +1288,7 @@ dependencies = [ "crypto-bigint", "der", "digest 0.10.7", - "ff 0.12.1", + "ff", "generic-array 0.14.7", "group", "pkcs8", @@ -1776,17 +1776,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "bitvec 1.0.1", - "rand_core", - "subtle", -] - [[package]] name = "fixed-hash" version = "0.7.0" @@ -2138,7 +2127,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff 0.12.1", + "ff", "rand_core", "subtle", ] @@ -2173,7 +2162,7 @@ name = "halo2-base" version = "0.2.2" source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#2c225864227e74b207d9f4b9e08c4d5f1afc69a1" dependencies = [ - "ff 0.12.1", + "ff", "halo2_proofs", "itertools", "num-bigint", @@ -2188,7 +2177,7 @@ name = "halo2-ecc" version = "0.2.2" source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#2c225864227e74b207d9f4b9e08c4d5f1afc69a1" dependencies = [ - "ff 0.12.1", + "ff", "group", "halo2-base", "itertools", @@ -2205,9 +2194,8 @@ dependencies = [ [[package]] name = "halo2-gate-generator" version = "0.1.0" -source = "git+https://github.com/DelphinusLab/halo2gategen.git?branch=main#506f904a2e678c5e7a3f9ea2ba2e7c78b126fc7a" +source = "git+https://github.com/scroll-tech/halo2gategen.git#35b137de2f71c37dfbd236842b868013c46739d1" dependencies = [ - "ff 0.13.0", "halo2_proofs", "lazy_static", "num-bigint", @@ -2251,7 +2239,7 @@ dependencies = [ "cfg-if 0.1.10", "crossbeam", "env_logger 0.8.4", - "ff 0.12.1", + "ff", "group", "halo2curves", "log", @@ -2272,7 +2260,7 @@ name = "halo2curves" version = "0.3.1" source = "git+https://github.com/scroll-tech/halo2curves.git?branch=0.3.1-derive-serde#969f1e44d9713ee4cd552563bd0c762c5d53b56e" dependencies = [ - "ff 0.12.1", + "ff", "group", "lazy_static", "num-bigint", @@ -3225,7 +3213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" dependencies = [ "blake2b_simd", - "ff 0.12.1", + "ff", "group", "lazy_static", "rand", @@ -4010,7 +3998,7 @@ dependencies = [ [[package]] name = "rmd160-circuits" version = "0.1.0" -source = "git+https://github.com/scroll-tech/misc-precompiled-circuit.git?branch=integration#0bdc4e4ba6e35d1edec5fc3ff02084615f8bcec1" +source = "git+https://github.com/scroll-tech/misc-precompiled-circuit.git?branch=integration#31c41ca4365dcf2b6ed4f2cdcd3dc8d2e8f080df" dependencies = [ "halo2-gate-generator", "halo2_proofs", From 6687ef230e14c7a2821543aac00a49292bb60137 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 13 Jul 2023 21:32:13 +0800 Subject: [PATCH 41/57] optimize by powofrand table --- .../execution/precompiles/modexp.rs | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 10a0691410..05a094a4fe 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -27,6 +27,7 @@ use bus_mapping::precompile::{PrecompileAuxData, MODEXP_INPUT_LIMIT, MODEXP_SIZE struct RandPowRepresent { bits: BinaryNumberGadget, pow_assembles: Vec<(Cell, usize)>, + pow_from_bits: Option>, pow: Expression, } @@ -96,6 +97,42 @@ impl RandPowRepresent { pow_assembles, bits, pow, + pow_from_bits: None, + } + } + + /// same as configure, make use of rand_pow_table instead of constraint it + /// by additional cells + pub fn configure_with_lookup( + cb: &mut EVMConstraintBuilder, + _randomness: Expression, + exponent: Expression, + linked_val: Option>, + ) -> Self { + let bits = BinaryNumberGadget::construct(cb, exponent); + let mut pow_assembles = Vec::new(); + let lookup_cell = cb.query_cell_phase2(); + cb.pow_of_rand_lookup(bits.value(), lookup_cell.expr()); + + let mut pow = linked_val.unwrap_or_else(|| 1.expr()) * lookup_cell.expr(); + // still we would cache the pow expression in case degree is too larget + if pow.degree() > Self::BIT_EXP_MAX_DEGREE { + let cached_cell = cb.query_cell_phase2(); + cb.require_equal( + "pow_assemble cached current expression", + cached_cell.expr(), + pow.clone(), + ); + + pow = cached_cell.expr(); + pow_assembles.push((cached_cell, BIT_LIMIT - 1)); + } + + Self { + pow_assembles, + bits, + pow, + pow_from_bits: Some(lookup_cell), } } @@ -129,6 +166,17 @@ impl RandPowRepresent { let mut cached_cell = pow_cached_i.next(); let mut value_should_assigned = linked_value.unwrap_or_else(|| Value::known(F::one())); + if let Some(cell) = &self.pow_from_bits { + cell.assign( + region, + offset, + region + .challenges() + .keccak_input() + .map(|v| v.pow(&[exponent as u64, 0, 0, 0])), + )?; + } + for (n, (base_pow, &bit)) in base_pows.zip(bits.as_slice().iter().rev()).enumerate() { value_should_assigned = value_should_assigned * (if bit { @@ -350,7 +398,7 @@ impl ModExpInputs { let is_input_need_padding = LtGadget::construct(cb, input_bytes_len.clone(), input_expected.clone()); - let padding_pow = RandPowRepresent::configure( + let padding_pow = RandPowRepresent::configure_with_lookup( cb, cb.challenges().keccak_input(), util::select::expr( From b23ae25aaab3b79203a010bed727faf789021543 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 14 Jul 2023 09:57:46 +0800 Subject: [PATCH 42/57] constraint for nil output --- .../execution/precompiles/modexp.rs | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 05a094a4fe..1d270612f3 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -576,6 +576,13 @@ impl ModExpOutputs { let result = cb.query_bytes(); let result_limbs = Limbs::configure(cb, &result); + cb.condition(is_result_zero.expr(), |cb|{ + cb.require_zero( + "output acc bytes must be zero for nil output", + output_bytes_acc.clone(), + ); + }); + cb.require_equal( "output acc bytes must equal", output_bytes_acc, @@ -801,7 +808,7 @@ impl ExecutionGadget for ModExpGadget { return Err(Error::Synthesis); } - println!("call success {}", call.is_success); + //println!("call success {}", call.is_success); self.is_success.assign( region, offset, @@ -994,6 +1001,33 @@ mod test { address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() }, + PrecompileCallArgs { + name: "modexp zero modulus", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x2) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x0) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800090000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0x63.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, ] }; @@ -1097,7 +1131,7 @@ mod test { address: PrecompileCalls::Modexp.address().to_word(), gas: 100000.into(), ..Default::default() - }, + }, ] }; } @@ -1114,7 +1148,7 @@ mod test { } #[test] - fn precompile_modexp_test() { + fn precompile_modexp_test_basic() { let call_kinds = vec![ OpcodeId::CALL, OpcodeId::STATICCALL, @@ -1166,9 +1200,6 @@ mod test { let step_len = steps.len(); let call_step = &mut steps[step_len - 3]; assert_eq!(call_step.op, OpcodeId::STATICCALL); - // https://github.com/scroll-tech/go-ethereum/blob/2dcc60a082ff89d1c57e497f23daad4823b2fdea/core/vm/contracts.go#L39C42-L39C98 - call_step.error = - Some("modexp temporarily accepts only 32-byte (256-bit) inputs".into()); call_step.refund.0 = 0; let next_gas = Gas(call_step.gas.0 - call_step.gas_cost.0); @@ -1181,7 +1212,7 @@ mod test { assert_eq!(final_step.op, OpcodeId::STOP); final_step.gas = next_gas; - println!("trace {:?}", block.geth_traces); + // println!("trace {:?}", block.geth_traces); })) .run(); } From bfca828b626343652f081258d2b5bd4646765148 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 14 Jul 2023 10:00:49 +0800 Subject: [PATCH 43/57] reverse geth step error indication for precompile --- bus-mapping/src/error.rs | 6 ++---- bus-mapping/src/geth_errors.rs | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index 56f19f342e..8c8f93eb9e 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -6,8 +6,8 @@ use ethers_providers::ProviderError; use std::error::Error as StdError; use crate::geth_errors::{ - GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_PRECOMPILE_MODEXP_INPUT, - GETH_ERR_STACK_OVERFLOW, GETH_ERR_STACK_UNDERFLOW, + GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_STACK_OVERFLOW, + GETH_ERR_STACK_UNDERFLOW, }; /// Error type for any BusMapping related failure. @@ -209,8 +209,6 @@ pub(crate) fn get_step_reported_error(op: &OpcodeId, error: &str) -> ExecError { ExecError::StackOverflow } else if error.starts_with(GETH_ERR_STACK_UNDERFLOW) { ExecError::StackUnderflow - } else if error.starts_with(GETH_ERR_PRECOMPILE_MODEXP_INPUT) { - ExecError::PrecompileFailed } else { panic!("Unknown GethExecStep.error: {error}"); } diff --git a/bus-mapping/src/geth_errors.rs b/bus-mapping/src/geth_errors.rs index 0d0de8529b..5eb536b324 100644 --- a/bus-mapping/src/geth_errors.rs +++ b/bus-mapping/src/geth_errors.rs @@ -5,6 +5,4 @@ pub const GETH_ERR_STACK_UNDERFLOW: &str = "stack underflow"; /// Geth error message for out of gas pub const GETH_ERR_OUT_OF_GAS: &str = "out of gas"; /// Geth error message for gas uint64 overflow -pub const GETH_ERR_GAS_UINT_OVERFLOW: &str = "gas uint64 overflow"; -/// Geth error message for precompile -pub const GETH_ERR_PRECOMPILE_MODEXP_INPUT: &str = "modexp"; +pub const GETH_ERR_GAS_UINT_OVERFLOW: &str = "gas uint64 overflow"; \ No newline at end of file From 071bfefe049e40dd42718e3d77869ba971ef5aab Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 14 Jul 2023 10:01:54 +0800 Subject: [PATCH 44/57] trivial fix for invalid case --- bus-mapping/src/precompile.rs | 6 ++++-- .../src/evm_circuit/execution/precompiles/modexp.rs | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index fef2cd548d..71a4007234 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -22,11 +22,13 @@ pub(crate) fn execute_precompiled(address: &Address, input: &[u8], gas: u64) -> match PrecompileCalls::from(address.0[19]) { // FIXME: override the behavior of invalid input PrecompileCalls::Modexp => { - let (input_valid, _) = ModExpAuxData::check_input(input); + let (input_valid, [_, _, modulus_len]) = ModExpAuxData::check_input(input); if input_valid { + // detect some edge cases like modulus = 0 + assert_eq!(modulus_len.as_usize(), return_value.len()); (return_value, gas_cost) } else { - (vec![], gas_cost) + (vec![], gas) } } _ => (return_value, gas_cost), diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 1d270612f3..5020e9ff85 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -1185,6 +1185,9 @@ mod test { } } + // notice, "invalid" test would not work until bus-mapping put calling fail case being handle + // in normal CallOp + #[ignore] #[test] fn precompile_modexp_test_invalid() { use eth_types::evm_types::Gas; From 2621de879f8f4c2779df49db2bb979f03cbb1a6f Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 14 Jul 2023 10:02:28 +0800 Subject: [PATCH 45/57] fmt --- bus-mapping/src/geth_errors.rs | 2 +- .../src/evm_circuit/execution/precompiles/modexp.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bus-mapping/src/geth_errors.rs b/bus-mapping/src/geth_errors.rs index 5eb536b324..4a31619107 100644 --- a/bus-mapping/src/geth_errors.rs +++ b/bus-mapping/src/geth_errors.rs @@ -5,4 +5,4 @@ pub const GETH_ERR_STACK_UNDERFLOW: &str = "stack underflow"; /// Geth error message for out of gas pub const GETH_ERR_OUT_OF_GAS: &str = "out of gas"; /// Geth error message for gas uint64 overflow -pub const GETH_ERR_GAS_UINT_OVERFLOW: &str = "gas uint64 overflow"; \ No newline at end of file +pub const GETH_ERR_GAS_UINT_OVERFLOW: &str = "gas uint64 overflow"; diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 5020e9ff85..4690a35798 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -576,9 +576,9 @@ impl ModExpOutputs { let result = cb.query_bytes(); let result_limbs = Limbs::configure(cb, &result); - cb.condition(is_result_zero.expr(), |cb|{ + cb.condition(is_result_zero.expr(), |cb| { cb.require_zero( - "output acc bytes must be zero for nil output", + "output acc bytes must be zero for nil output", output_bytes_acc.clone(), ); }); @@ -1027,7 +1027,7 @@ mod test { ret_size: 0x01.into(), address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() - }, + }, ] }; @@ -1131,7 +1131,7 @@ mod test { address: PrecompileCalls::Modexp.address().to_word(), gas: 100000.into(), ..Default::default() - }, + }, ] }; } From d5fd268cf86d99e02bcaef1ef953ce4be60d38c9 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sat, 22 Jul 2023 13:19:18 +0800 Subject: [PATCH 46/57] error should not be throw for precompile failure --- bus-mapping/src/circuit_input_builder/input_state_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index b187d802f4..76cf42bca1 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -1543,7 +1543,7 @@ impl<'a> CircuitInputStateRef<'a> { code_address, step.gas.0, ); - return Ok(Some(ExecError::PrecompileFailed)); + return Ok(None); //Ok(Some(ExecError::PrecompileFailed)); } } From 2c4be68cf1bedf4117da8a58c9882c72fbad3add Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 27 Jul 2023 21:37:08 +0800 Subject: [PATCH 47/57] fix for merge --- bus-mapping/src/precompile.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index b28ced64ae..52f959a5ad 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -273,6 +273,10 @@ impl ModExpAuxData { output_len, input_memory, output_memory, + } + } +} + /// Auxiliary data for EcAdd, i.e. P + Q = R #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct EcAddAuxData { From f61ba9e92921838249a3c88872f34e42990aeac9 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Thu, 27 Jul 2023 23:08:49 +0800 Subject: [PATCH 48/57] post-merge fixes for compile errors --- bus-mapping/src/circuit_input_builder.rs | 2 +- .../src/circuit_input_builder/block.rs | 4 --- .../src/circuit_input_builder/execution.rs | 4 +-- .../src/evm/opcodes/precompiles/mod.rs | 4 ++- .../src/evm/opcodes/precompiles/modexp.rs | 30 +++++++++++++++++++ .../src/evm_circuit/util/instrumentation.rs | 1 + .../src/precompile_circuit/modexp.rs | 19 ++++++------ zkevm-circuits/src/table.rs | 2 +- zkevm-circuits/src/witness/block.rs | 8 ++++- 9 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 bus-mapping/src/evm/opcodes/precompiles/modexp.rs diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 242fd43b04..bacad95a7a 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -37,7 +37,7 @@ use ethers_core::{ use ethers_providers::JsonRpcClient; pub use execution::{ CopyBytes, CopyDataType, CopyEvent, CopyEventStepsBuilder, CopyStep, EcAddOp, EcMulOp, - EcPairingOp, EcPairingPair, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, + EcPairingOp, EcPairingPair, BigModExp, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, PrecompileEvent, PrecompileEvents, N_BYTES_PER_PAIR, N_PAIRING_PER_OP, }; use hex::decode_to_slice; diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index e5cc8488ef..90b75941e9 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -254,8 +254,4 @@ impl Block { pub fn add_precompile_event(&mut self, event: PrecompileEvent) { self.precompile_events.events.push(event); } - /// Push an modexp event to the block. - pub fn add_modexp_event(&mut self, event: ModExpEvent) { - self.modexp_events.push(event); - } } diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index ee0e8a4ce8..a04ddb7bac 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -897,14 +897,14 @@ impl PrecompileEvents { pub fn get_modexp_events(&self) -> Vec { self.events .iter() - .cloned() .filter_map(|e| { if let PrecompileEvent::ModExp(op) = e { - Some(*op) + Some(op) } else { None } }) + .cloned() .collect() } } diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index a0c6573ff9..b449e68fb5 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -1,7 +1,7 @@ use eth_types::{GethExecStep, ToWord, Word}; use crate::{ - circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep, ModExpEvent}, + circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, operation::CallContextField, precompile::PrecompileCalls, Error, @@ -11,11 +11,13 @@ mod ec_add; mod ec_mul; mod ec_pairing; mod ecrecover; +mod modexp; use ec_add::opt_data as opt_data_ec_add; use ec_mul::opt_data as opt_data_ec_mul; use ec_pairing::opt_data as opt_data_ec_pairing; use ecrecover::opt_data as opt_data_ecrecover; +use modexp::opt_data as opt_data_modexp; type InOutRetData = (Option>, Option>, Option>); diff --git a/bus-mapping/src/evm/opcodes/precompiles/modexp.rs b/bus-mapping/src/evm/opcodes/precompiles/modexp.rs new file mode 100644 index 0000000000..cc81457af1 --- /dev/null +++ b/bus-mapping/src/evm/opcodes/precompiles/modexp.rs @@ -0,0 +1,30 @@ + +use crate::{ + circuit_input_builder::{PrecompileEvent, BigModExp}, + precompile::{ModExpAuxData, PrecompileAuxData}, +}; + +use eth_types::Word; + +pub(crate) fn opt_data( + input_bytes: Option>, + output_bytes: Option>, +) -> (Option, Option) { + + let aux_data = ModExpAuxData::new( + input_bytes.unwrap_or_default(), + output_bytes.unwrap_or_default(), + ); + if aux_data.valid { + let event = BigModExp { + base: Word::from_big_endian(&aux_data.inputs[0]), + exponent: Word::from_big_endian(&aux_data.inputs[1]), + modulus: Word::from_big_endian(&aux_data.inputs[2]), + result: Word::from_big_endian(&aux_data.output), + }; + (Some(PrecompileEvent::ModExp(event)), Some(PrecompileAuxData::Modexp(aux_data))) + } else { + (None, Some(PrecompileAuxData::Modexp(aux_data))) + } +} + diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index 90054676ae..96cd6eaaf2 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -108,6 +108,7 @@ impl Instrument { } CellType::Lookup(Table::ModExp) => { report.modexp_table = data_entry; + } CellType::Lookup(Table::Ecc) => { report.ecc_table = data_entry; } diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/precompile_circuit/modexp.rs index 23692e97c0..8f2cab0713 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/precompile_circuit/modexp.rs @@ -8,7 +8,7 @@ use crate::{ util::{Challenges, SubCircuit, SubCircuitConfig}, witness, }; -use bus_mapping::circuit_input_builder::ModExpEvent; +use bus_mapping::circuit_input_builder::BigModExp; use eth_types::{Field, Word}; //use misc_precompiled_circuit::value_for_assign; @@ -47,7 +47,7 @@ impl ModExpCircuitConfig { region: &mut Region, table_offset: usize, mut calc_offset: usize, - event: &ModExpEvent, + event: &BigModExp, modexp_chip: &ModExpChip, range_check_chip: &mut RangeCheckChip, ) -> Result { @@ -129,7 +129,7 @@ const MODEXPCONFIG_EACH_CHIP_ROWS: usize = 9291; /// ModExp circuit for precompile modexp #[derive(Clone, Debug, Default)] -pub struct ModExpCircuit(Vec, std::marker::PhantomData); +pub struct ModExpCircuit(Vec, std::marker::PhantomData); impl SubCircuit for ModExpCircuit { type Config = ModExpCircuitConfig; @@ -142,7 +142,7 @@ impl SubCircuit for ModExpCircuit { fn new_from_block(block: &witness::Block) -> Self { let event_limit = block.circuits_params.max_keccak_rows / MODEXPCONFIG_EACH_CHIP_ROWS; - let mut exp_events = block.modexp_events.clone(); + let mut exp_events = block.get_big_modexp(); assert!( exp_events.len() <= event_limit, "no enough rows for modexp circuit, expected {}, limit {}", @@ -155,9 +155,10 @@ impl SubCircuit for ModExpCircuit { } fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + let exp_events = block.get_big_modexp(); ( - block.modexp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS, - (block.modexp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS) + exp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS, + (exp_events.len() * MODEXPCONFIG_EACH_CHIP_ROWS) .max(block.circuits_params.max_keccak_rows), ) } @@ -238,7 +239,7 @@ mod test { let exp = Word::from(2u128); let modulus = Word::from(7u128); let (_, result) = base.pow(exp).div_mod(modulus); - let event1 = ModExpEvent { + let event1 = BigModExp { base, exponent: exp, modulus, @@ -255,7 +256,7 @@ mod test { let exp = Word::from(2u128); let modulus = Word::from(7u128); let (_, result) = base.pow(exp).div_mod(modulus); - let event1 = ModExpEvent { + let event1 = BigModExp { base, exponent: exp, modulus, @@ -271,7 +272,7 @@ mod test { let exp = Word::from(2u128); let modulus = Word::from(7u128); let (_, result) = base.pow(exp).div_mod(modulus); - let event1 = ModExpEvent { + let event1 = BigModExp { base, exponent: exp, modulus, diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 28e3ae45d9..042ebbf9ec 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2632,7 +2632,7 @@ impl ModExpTable { |mut region| { let mut offset = 0usize; - for event in &block.modexp_events { + for event in &block.get_big_modexp() { for i in 0..4 { region.assign_fixed( || format!("modexp table head {}", offset + i), diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 7f770d37e0..51045c2436 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -7,7 +7,7 @@ use crate::evm_circuit::{detect_fixed_table_tags, EvmCircuit}; use crate::{evm_circuit::util::rlc, table::BlockContextFieldTag, util::SubCircuit}; use bus_mapping::{ circuit_input_builder::{ - self, CircuitsParams, CopyEvent, EcAddOp, EcMulOp, EcPairingOp, ExpEvent, PrecompileEvents, + self, CircuitsParams, CopyEvent, EcAddOp, EcMulOp, EcPairingOp, BigModExp, ExpEvent, PrecompileEvents, }, Error, }; @@ -145,6 +145,12 @@ impl Block { pub(crate) fn get_ec_pairing_ops(&self) -> Vec { self.precompile_events.get_ec_pairing_events() } + + /// Get BigModexp operations from all precompiled contract calls in this block. + pub(crate) fn get_big_modexp(&self) -> Vec { + self.precompile_events.get_modexp_events() + } + } #[cfg(feature = "test")] From 3355555bddcc46a2bffc93461718219a9e9bc2b8 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 28 Jul 2023 23:07:55 +0800 Subject: [PATCH 49/57] reverse throwing precompile error in get_step_err --- bus-mapping/src/circuit_input_builder/input_state_ref.rs | 2 +- zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index ccb19c5c71..dc48d4aa0b 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -1604,7 +1604,7 @@ impl<'a> CircuitInputStateRef<'a> { code_address, step.gas.0, ); - return Ok(None); //Ok(Some(ExecError::PrecompileFailed)); + return Ok(Some(ExecError::PrecompileFailed)); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 4690a35798..8b625257a3 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -1186,7 +1186,8 @@ mod test { } // notice, "invalid" test would not work until bus-mapping put calling fail case being handle - // in normal CallOp + // in normal CallOp, i.e. return None in bus_mapping::circuit_input_builder::input_state_ref::CircuitInputStateRef::get_step_err + // for unsuccess (call.is_success is false) call #[ignore] #[test] fn precompile_modexp_test_invalid() { From 34b9d5be267050917ce5b8a121a88ee3340ec14c Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Fri, 28 Jul 2023 23:15:27 +0800 Subject: [PATCH 50/57] fmt --- bus-mapping/src/circuit_input_builder.rs | 4 ++-- bus-mapping/src/circuit_input_builder/execution.rs | 7 +++---- bus-mapping/src/evm/opcodes/precompiles/modexp.rs | 10 +++++----- zkevm-circuits/src/evm_circuit.rs | 2 +- zkevm-circuits/src/evm_circuit/execution.rs | 10 ++++++---- .../src/evm_circuit/execution/precompiles/modexp.rs | 3 ++- zkevm-circuits/src/super_circuit.rs | 5 +++-- zkevm-circuits/src/table.rs | 4 +--- zkevm-circuits/src/witness/block.rs | 4 ++-- 9 files changed, 25 insertions(+), 24 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index bacad95a7a..0ef417c735 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -36,8 +36,8 @@ use ethers_core::{ }; use ethers_providers::JsonRpcClient; pub use execution::{ - CopyBytes, CopyDataType, CopyEvent, CopyEventStepsBuilder, CopyStep, EcAddOp, EcMulOp, - EcPairingOp, EcPairingPair, BigModExp, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, + BigModExp, CopyBytes, CopyDataType, CopyEvent, CopyEventStepsBuilder, CopyStep, EcAddOp, + EcMulOp, EcPairingOp, EcPairingPair, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, PrecompileEvent, PrecompileEvents, N_BYTES_PER_PAIR, N_PAIRING_PER_OP, }; use hex::decode_to_slice; diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index a04ddb7bac..5430f1ddb7 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -906,7 +906,7 @@ impl PrecompileEvents { }) .cloned() .collect() - } + } } /// I/O from a precompiled contract call. @@ -921,7 +921,7 @@ pub enum PrecompileEvent { /// Represents the I/O from EcPairing call. EcPairing(Box), /// Represents the I/O from Modexp call. - ModExp(BigModExp) + ModExp(BigModExp), } impl Default for PrecompileEvent { @@ -1191,7 +1191,6 @@ impl EcPairingOp { } } - /// Event representating an exponentiation `a ^ b == d (mod m)` in precompile modexp. #[derive(Clone, Debug)] pub struct BigModExp { @@ -1214,4 +1213,4 @@ impl Default for BigModExp { result: Default::default(), } } -} \ No newline at end of file +} diff --git a/bus-mapping/src/evm/opcodes/precompiles/modexp.rs b/bus-mapping/src/evm/opcodes/precompiles/modexp.rs index cc81457af1..ca459e79ab 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/modexp.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/modexp.rs @@ -1,6 +1,5 @@ - use crate::{ - circuit_input_builder::{PrecompileEvent, BigModExp}, + circuit_input_builder::{BigModExp, PrecompileEvent}, precompile::{ModExpAuxData, PrecompileAuxData}, }; @@ -10,7 +9,6 @@ pub(crate) fn opt_data( input_bytes: Option>, output_bytes: Option>, ) -> (Option, Option) { - let aux_data = ModExpAuxData::new( input_bytes.unwrap_or_default(), output_bytes.unwrap_or_default(), @@ -22,9 +20,11 @@ pub(crate) fn opt_data( modulus: Word::from_big_endian(&aux_data.inputs[2]), result: Word::from_big_endian(&aux_data.output), }; - (Some(PrecompileEvent::ModExp(event)), Some(PrecompileAuxData::Modexp(aux_data))) + ( + Some(PrecompileEvent::ModExp(event)), + Some(PrecompileAuxData::Modexp(aux_data)), + ) } else { (None, Some(PrecompileAuxData::Modexp(aux_data))) } } - diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 0b495b4e66..e12e249b3e 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -22,7 +22,7 @@ use crate::{ evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, - PowOfRandTable, RwTable, SigTable, TxTable, ModExpTable, + ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, }, util::{SubCircuit, SubCircuitConfig}, }; diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 265803d458..1d248a935e 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1,9 +1,9 @@ use super::{ param::{ BLOCK_TABLE_LOOKUPS, BYTECODE_TABLE_LOOKUPS, COPY_TABLE_LOOKUPS, ECC_TABLE_LOOKUPS, - EXP_TABLE_LOOKUPS, FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_BYTE_LOOKUPS, - N_COPY_COLUMNS, N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, RW_TABLE_LOOKUPS, - SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, MODEXP_TABLE_LOOKUPS, + EXP_TABLE_LOOKUPS, FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, MODEXP_TABLE_LOOKUPS, + N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, + RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, EvmCircuitExports, @@ -203,7 +203,9 @@ use opcode_not::NotGadget; use origin::OriginGadget; use pc::PcGadget; use pop::PopGadget; -use precompiles::{EcAddGadget, EcMulGadget, EcPairingGadget, EcrecoverGadget, IdentityGadget, ModExpGadget}; +use precompiles::{ + EcAddGadget, EcMulGadget, EcPairingGadget, EcrecoverGadget, IdentityGadget, ModExpGadget, +}; use push::PushGadget; use return_revert::ReturnRevertGadget; use returndatacopy::ReturnDataCopyGadget; diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 8b625257a3..7a11d7ec6a 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -1186,7 +1186,8 @@ mod test { } // notice, "invalid" test would not work until bus-mapping put calling fail case being handle - // in normal CallOp, i.e. return None in bus_mapping::circuit_input_builder::input_state_ref::CircuitInputStateRef::get_step_err + // in normal CallOp, i.e. return None in + // bus_mapping::circuit_input_builder::input_state_ref::CircuitInputStateRef::get_step_err // for unsuccess (call.is_success is false) call #[ignore] #[test] diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index e53fb45b0f..7236d57af5 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -82,8 +82,9 @@ use crate::util::Challenges; use crate::{ state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, ModExpTable, MptTable, - PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, SigTable, TxTable, + BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, ModExpTable, + MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, SigTable, + TxTable, }, }; diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 042ebbf9ec..6d79f83281 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2543,7 +2543,6 @@ impl EccTable { } } - /// Lookup table embedded in the modexp circuit for precompile. #[derive(Clone, Copy, Debug)] pub struct ModExpTable { @@ -2681,8 +2680,7 @@ impl ModExpTable { } Ok(()) - }, - + }, )?; self.fill_blank(layouter) } diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 51045c2436..e6039d1d40 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -7,7 +7,8 @@ use crate::evm_circuit::{detect_fixed_table_tags, EvmCircuit}; use crate::{evm_circuit::util::rlc, table::BlockContextFieldTag, util::SubCircuit}; use bus_mapping::{ circuit_input_builder::{ - self, CircuitsParams, CopyEvent, EcAddOp, EcMulOp, EcPairingOp, BigModExp, ExpEvent, PrecompileEvents, + self, BigModExp, CircuitsParams, CopyEvent, EcAddOp, EcMulOp, EcPairingOp, ExpEvent, + PrecompileEvents, }, Error, }; @@ -150,7 +151,6 @@ impl Block { pub(crate) fn get_big_modexp(&self) -> Vec { self.precompile_events.get_modexp_events() } - } #[cfg(feature = "test")] From f9d15782b3c28fd550cc872639bda9970c79db8e Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sat, 29 Jul 2023 12:09:59 +0800 Subject: [PATCH 51/57] clippy lint --- .../src/evm_circuit/execution/precompiles/modexp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 7a11d7ec6a..c63b243525 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -779,7 +779,7 @@ impl ExecutionGadget for ModExpGadget { step: &ExecStep, ) -> Result<(), Error> { if let Some(PrecompileAuxData::Modexp(data)) = &step.aux_data { - println!("exp data: {:?}", data); + //println!("exp data: {:?}", data); self.input.assign( region, @@ -904,7 +904,7 @@ mod test { .unwrap(); assert_eq!(limb0, n.limbs[0].value); - assert_eq!(limb1, n.limbs[1].value * Fr::from(16 as u64)); + assert_eq!(limb1, n.limbs[1].value * Fr::from(16_u64)); assert_eq!(limb2, n.limbs[2].value); let nt: Fr = ModExpTable::native_u256(&w); From 0cdd7c45b1d67fd7e5f1bb0528416f526ec2bc0d Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sat, 29 Jul 2023 19:58:51 +0800 Subject: [PATCH 52/57] update `dev_load` of modexp table and some file structures --- zkevm-circuits/src/evm_circuit.rs | 4 +++- zkevm-circuits/src/lib.rs | 2 +- .../modexp.rs => modexp_circuit.rs} | 3 +++ zkevm-circuits/src/precompile_circuit/mod.rs | 4 ---- zkevm-circuits/src/super_circuit.rs | 13 ++++++------- zkevm-circuits/src/table.rs | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) rename zkevm-circuits/src/{precompile_circuit/modexp.rs => modexp_circuit.rs} (97%) delete mode 100644 zkevm-circuits/src/precompile_circuit/mod.rs diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index e12e249b3e..85cdc0c16d 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -505,7 +505,9 @@ impl Circuit for EvmCircuit { config .sig_table .dev_load(&mut layouter, block, &challenges)?; - config.modexp_table.dev_load(&mut layouter, block)?; + config + .modexp_table + .dev_load(&mut layouter, &block.get_big_modexp())?; config.ecc_table.dev_load( &mut layouter, block.circuits_params.max_ec_ops, diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 358665f63d..45f716f334 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -36,7 +36,7 @@ pub mod rlp_circuit_fsm; pub mod sig_circuit; // we don't use this for aggregation //pub mod root_circuit; -pub mod precompile_circuit; +pub mod modexp_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; diff --git a/zkevm-circuits/src/precompile_circuit/modexp.rs b/zkevm-circuits/src/modexp_circuit.rs similarity index 97% rename from zkevm-circuits/src/precompile_circuit/modexp.rs rename to zkevm-circuits/src/modexp_circuit.rs index 8f2cab0713..1e4ccc0083 100644 --- a/zkevm-circuits/src/precompile_circuit/modexp.rs +++ b/zkevm-circuits/src/modexp_circuit.rs @@ -1,3 +1,6 @@ +//! The Modexp circuit is responsible for modexp operations on big integer from precompiled contract +//! calls ModExp, current the size of supported integer is up to 32 bytes (U256) + use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error}, diff --git a/zkevm-circuits/src/precompile_circuit/mod.rs b/zkevm-circuits/src/precompile_circuit/mod.rs deleted file mode 100644 index ed3a3b837f..0000000000 --- a/zkevm-circuits/src/precompile_circuit/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Precompile circuits - -/// mod exp circuit for U256 integer -pub mod modexp; diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 7236d57af5..7b28312e59 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -66,8 +66,8 @@ use crate::{ evm_circuit::{EvmCircuit, EvmCircuitConfig, EvmCircuitConfigArgs}, exp_circuit::{ExpCircuit, ExpCircuitConfig}, keccak_circuit::{KeccakCircuit, KeccakCircuitConfig, KeccakCircuitConfigArgs}, + modexp_circuit::{ModExpCircuit, ModExpCircuitConfig}, poseidon_circuit::{PoseidonCircuit, PoseidonCircuitConfig, PoseidonCircuitConfigArgs}, - precompile_circuit, sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{log2_ceil, SubCircuit, SubCircuitConfig}, @@ -120,7 +120,7 @@ pub struct SuperCircuitConfig { state_circuit: StateCircuitConfig, tx_circuit: TxCircuitConfig, sig_circuit: SigCircuitConfig, - modexp_circuit: precompile_circuit::modexp::ModExpCircuitConfig, + modexp_circuit: ModExpCircuitConfig, ecc_circuit: EccCircuitConfig, #[cfg(not(feature = "poseidon-codehash"))] bytecode_circuit: BytecodeCircuitConfig, @@ -311,8 +311,7 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "sig circuit"); - let modexp_circuit = - precompile_circuit::modexp::ModExpCircuitConfig::new(meta, modexp_table); + let modexp_circuit = ModExpCircuitConfig::new(meta, modexp_table); log_circuit_info(meta, "modexp circuit"); let ecc_circuit = EccCircuitConfig::new( meta, @@ -415,7 +414,7 @@ pub struct SuperCircuit< /// Sig Circuit pub sig_circuit: SigCircuit, /// Modexp Circuit - pub modexp_circuit: precompile_circuit::modexp::ModExpCircuit, + pub modexp_circuit: ModExpCircuit, /// Ecc Circuit pub ecc_circuit: EccCircuit, /// Rlp Circuit @@ -453,7 +452,7 @@ impl< let tx = TxCircuit::min_num_rows_block(block); let rlp = RlpCircuit::min_num_rows_block(block); let exp = ExpCircuit::min_num_rows_block(block); - let mod_exp = precompile_circuit::modexp::ModExpCircuit::min_num_rows_block(block); + let mod_exp = ModExpCircuit::min_num_rows_block(block); let pi = PiCircuit::min_num_rows_block(block); let poseidon = (0, 0); //PoseidonCircuit::min_num_rows_block(block); #[cfg(feature = "zktrie")] @@ -520,7 +519,7 @@ impl< let bytecode_circuit = BytecodeCircuit::new_from_block(block); let copy_circuit = CopyCircuit::new_from_block_no_external(block); let exp_circuit = ExpCircuit::new_from_block(block); - let modexp_circuit = precompile_circuit::modexp::ModExpCircuit::new_from_block(block); + let modexp_circuit = ModExpCircuit::new_from_block(block); let keccak_circuit = KeccakCircuit::new_from_block(block); let poseidon_circuit = PoseidonCircuit::new_from_block(block); let rlp_circuit = RlpCircuit::new_from_block(block); diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 4863c9e6fb..fa7ab97209 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -16,7 +16,7 @@ use crate::{ }; use bus_mapping::{ circuit_input_builder::{ - CopyDataType, CopyEvent, CopyStep, EcAddOp, EcMulOp, EcPairingOp, ExpEvent, + BigModExp, CopyDataType, CopyEvent, CopyStep, EcAddOp, EcMulOp, EcPairingOp, ExpEvent, PrecompileEcParams, N_BYTES_PER_PAIR, N_PAIRING_PER_OP, }, precompile::PrecompileCalls, @@ -2629,14 +2629,14 @@ impl ModExpTable { pub fn dev_load( &self, layouter: &mut impl Layouter, - block: &Block, + events: &[BigModExp], ) -> Result<(), Error> { layouter.assign_region( || "modexp table", |mut region| { let mut offset = 0usize; - for event in &block.get_big_modexp() { + for event in events { for i in 0..4 { region.assign_fixed( || format!("modexp table head {}", offset + i), From 938c7e2f3b997c37a3aaf2b9ff8ed50fedaa58d5 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Sat, 29 Jul 2023 21:51:06 +0800 Subject: [PATCH 53/57] resume size limit for modexp --- bus-mapping/src/precompile.rs | 2 +- zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 52f959a5ad..ffd6201926 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -132,7 +132,7 @@ impl PrecompileCalls { match self { Self::Ecrecover | Self::Bn128Add => Some(128), Self::Bn128Mul => Some(96), - //Self::Modexp => Some(MODEXP_INPUT_LIMIT), + Self::Modexp => Some(MODEXP_INPUT_LIMIT), _ => None, } } diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index c0d0067702..5ac299acef 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -342,6 +342,9 @@ impl PaddingGadget { // skip padding if calldata length == 0. if cd_len == 0 { (required_input_len as u64, input_rlc, Value::known(F::one())) + } else if precompile == PrecompileCalls::Modexp { + // modexp do not need right padding + (cd_len, input_rlc, Value::known(F::one())) } else { // pad only if calldata length is less than the required input length. let n_padded_zeroes = if cd_len < required_input_len as u64 { From b51d1f74ad92cc62a08332e9aef9b2594b40e062 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Mon, 31 Jul 2023 10:38:47 +0800 Subject: [PATCH 54/57] refactor and support garbage bytes in input --- .../circuit_input_builder/input_state_ref.rs | 2 +- bus-mapping/src/precompile.rs | 6 +- .../execution/precompiles/modexp.rs | 224 +++++++++++------- .../src/evm_circuit/util/precompile_gadget.rs | 12 +- 4 files changed, 154 insertions(+), 90 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index dc48d4aa0b..f7f887cc21 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -1604,7 +1604,7 @@ impl<'a> CircuitInputStateRef<'a> { code_address, step.gas.0, ); - return Ok(Some(ExecError::PrecompileFailed)); + return Ok(None);//Ok(Some(ExecError::PrecompileFailed)); } } diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index ffd6201926..9cd8afe4a7 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -241,17 +241,17 @@ impl ModExpAuxData { let base_mem_len = if input_valid { base_len.as_usize() } else { - MODEXP_SIZE_LIMIT + 0 }; let exp_mem_len = if input_valid { exp_len.as_usize() } else { - MODEXP_SIZE_LIMIT + 0 }; let modulus_mem_len = if input_valid { modulus_len.as_usize() } else { - MODEXP_SIZE_LIMIT + 0 }; mem_input.resize(96 + base_mem_len + exp_mem_len + modulus_mem_len, 0); diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index c63b243525..0345062714 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -218,8 +218,8 @@ fn assign_word( Ok(()) } -// rlc word, in the reversed byte order -fn rlc_word_rev( +// rlc cells array, in the reversed byte order +fn rlc_rev( cells: &[Cell; N], randomness: Expression, ) -> Expression { @@ -230,6 +230,14 @@ fn rlc_word_rev( .expect("values should not be empty") } +// rlc word, in the reversed byte order +fn rlc_word_rev( + cells: &[Cell; 32], + randomness: Expression, +) -> Expression { + rlc_rev(cells, randomness) +} + // calc for big-endian (notice util::expr_from_bytes calc for little-endian) fn expr_from_bytes>(bytes: &[E]) -> Expression { bytes @@ -341,19 +349,15 @@ struct ModExpInputs { exp_pow: RandPow, exp: Word, input_valid: Cell, - padding_pow: RandPowRepresent, - is_input_need_padding: LtGadget, + input_bytes_rlc: Expression, + input_len_expected: Expression, pub base_limbs: Limbs, pub exp_limbs: Limbs, pub modulus_limbs: Limbs, } impl ModExpInputs { - pub fn configure( - cb: &mut EVMConstraintBuilder, - input_bytes_len: Expression, - input_bytes_acc: Expression, - ) -> Self { + pub fn configure(cb: &mut EVMConstraintBuilder) -> Self { let base_len = SizeRepresent::configure(cb); let modulus_len = SizeRepresent::configure(cb); let exp_len = SizeRepresent::configure(cb); @@ -382,33 +386,19 @@ impl ModExpInputs { ); let base_len_expected = - util::select::expr(input_valid.expr(), base_len.value(), SIZE_LIMIT.expr()); + util::select::expr(input_valid.expr(), base_len.value(), 0.expr()); let exp_len_expected = - util::select::expr(input_valid.expr(), exp_len.value(), SIZE_LIMIT.expr()); + util::select::expr(input_valid.expr(), exp_len.value(), 0.expr()); let modulus_len_expected = - util::select::expr(input_valid.expr(), modulus_len.value(), SIZE_LIMIT.expr()); + util::select::expr(input_valid.expr(), modulus_len.value(), 0.expr()); - let input_expected = 96.expr() + let input_len_expected = 96.expr() + base_len_expected.clone() + exp_len_expected.clone() + modulus_len_expected.clone(); - let is_input_need_padding = - LtGadget::construct(cb, input_bytes_len.clone(), input_expected.clone()); - - let padding_pow = RandPowRepresent::configure_with_lookup( - cb, - cb.challenges().keccak_input(), - util::select::expr( - is_input_need_padding.expr(), - input_expected - input_bytes_len, - 0.expr(), - ), - None, - ); - // we put correct size in each input word if input is valid // else we just put as most as possible bytes (32) into it // so we finally handle the memory in limited sized (32*3) @@ -435,16 +425,13 @@ impl ModExpInputs { Some(exp_pow.expr()), ); - cb.require_equal( - "input acc bytes must equal", - padding_pow.expr() * input_bytes_acc, + let input_bytes_rlc = rlc_word_rev(&modulus, cb.challenges().keccak_input()) //rlc of base + modulus_pow.expr() * rlc_word_rev(&exp, cb.challenges().keccak_input()) //rlc of exp plus r**base_len + exp_pow.expr() * rlc_word_rev(&base, cb.challenges().keccak_input()) //rlc of exp plus r**(base_len + exp_len) + base_pow.expr() * modulus_len.memory_rlc() + base_pow.expr() * r_pow_32 * exp_len.memory_rlc() - + base_pow.expr() * r_pow_64 * base_len.memory_rlc(), - ); + + base_pow.expr() * r_pow_64 * base_len.memory_rlc(); // println!("phase 2 cell used {}", // padding_pow.phase2_cell_cost() + [&modulus_pow, &exp_pow, @@ -461,8 +448,8 @@ impl ModExpInputs { exp_pow, exp, input_valid, - padding_pow, - is_input_need_padding, + input_bytes_rlc, + input_len_expected, base_limbs, exp_limbs, modulus_limbs, @@ -475,13 +462,18 @@ impl ModExpInputs { pub fn is_valid(&self) -> Expression { self.input_valid.expr() } + pub fn len_expected(&self) -> Expression { + self.input_len_expected.clone() + } + pub fn bytes_rlc(&self) -> Expression { + self.input_bytes_rlc.clone() + } pub fn assign( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, (input_valid, lens, values): InputParsedResult, - input_len: usize, ) -> Result<(), Error> { self.input_valid.assign( region, @@ -508,7 +500,7 @@ impl ModExpInputs { if input_valid { len.as_usize() } else { - SIZE_LIMIT + 0 }, linked_v, )?; @@ -528,30 +520,6 @@ impl ModExpInputs { assign_word(region, offset, input_bytes, val)?; } - let expected_len = if input_valid { - lens.iter().map(U256::as_usize).sum::() + 96 - } else { - INPUT_LIMIT - }; - - self.is_input_need_padding.assign( - region, - offset, - F::from(input_len as u64), - F::from(expected_len as u64), - )?; - - self.padding_pow.assign( - region, - offset, - if input_len < expected_len { - expected_len - input_len - } else { - 0 - }, - None, - )?; - Ok(()) } } @@ -560,13 +528,13 @@ impl ModExpInputs { struct ModExpOutputs { result: Word, is_result_zero: IsZeroGadget, + output_bytes_rlc: Expression, pub result_limbs: Limbs, } impl ModExpOutputs { fn configure( cb: &mut EVMConstraintBuilder, - output_bytes_acc: Expression, inner_success: Expression, modulus_len: Expression, ) -> Self { @@ -576,30 +544,28 @@ impl ModExpOutputs { let result = cb.query_bytes(); let result_limbs = Limbs::configure(cb, &result); - cb.condition(is_result_zero.expr(), |cb| { - cb.require_zero( - "output acc bytes must be zero for nil output", - output_bytes_acc.clone(), - ); - }); - - cb.require_equal( - "output acc bytes must equal", - output_bytes_acc, + let output_bytes_rlc = util::select::expr( + is_result_zero.expr(), + 0.expr(), rlc_word_rev(&result, cb.challenges().keccak_input()), ); Self { result, is_result_zero, + output_bytes_rlc, result_limbs, } } - pub fn is_output_nil(&self) -> Expression { + pub fn is_nil(&self) -> Expression { self.is_result_zero.expr() } + pub fn bytes_rlc(&self) -> Expression { + self.output_bytes_rlc.clone() + } + pub fn assign( &self, region: &mut CachedRegion<'_, '_, F>, @@ -688,10 +654,12 @@ pub struct ModExpGadget { return_data_length: Cell, input: ModExpInputs, + padding_zero: RandPowRepresent, output: ModExpOutputs, input_bytes_acc: Cell, output_bytes_acc: Cell, + garbage_bytes_holder: [Cell; INPUT_LIMIT - 96], } impl ExecutionGadget for ModExpGadget { @@ -722,7 +690,13 @@ impl ExecutionGadget for ModExpGadget { cb.execution_state().precompile_base_gas_cost().expr(), ); - let input = ModExpInputs::configure(cb, call_data_length.expr(), input_bytes_acc.expr()); + let input = ModExpInputs::configure(cb); + let padding_zero = RandPowRepresent::configure( + cb, + cb.challenges().keccak_input(), + MODEXP_INPUT_LIMIT.expr() - input.len_expected(), + None, + ); let call_success = util::and::expr([ input.is_valid(), @@ -738,14 +712,13 @@ impl ExecutionGadget for ModExpGadget { let output = ModExpOutputs::configure( cb, - output_bytes_acc.expr(), //FIXME: there may be still some edge cases lead to nil output (even modulus_len is // not 0) call_success, input.modulus_len(), ); - cb.condition(util::not::expr(output.is_output_nil()), |cb| { + cb.condition(util::not::expr(output.is_nil()), |cb| { cb.modexp_table_lookup( input.base_limbs.limbs(), input.exp_limbs.limbs(), @@ -754,6 +727,21 @@ impl ExecutionGadget for ModExpGadget { ); }); + let garbage_bytes_holder = cb.query_bytes(); + + cb.require_equal( + "input acc bytes with padding must equal", + input_bytes_acc.expr(), + padding_zero.expr() * input.bytes_rlc() + rlc_rev(&garbage_bytes_holder, cb.challenges().keccak_input()), + ); + + cb.require_equal( + "output acc bytes must equal", + output_bytes_acc.expr(), + output.bytes_rlc(), + ); + + Self { is_success, callee_address, @@ -763,9 +751,11 @@ impl ExecutionGadget for ModExpGadget { return_data_offset, return_data_length, input, + padding_zero, output, input_bytes_acc, output_bytes_acc, + garbage_bytes_holder, } } @@ -779,14 +769,37 @@ impl ExecutionGadget for ModExpGadget { step: &ExecStep, ) -> Result<(), Error> { if let Some(PrecompileAuxData::Modexp(data)) = &step.aux_data { - //println!("exp data: {:?}", data); + println!("exp data: {:?}", data); self.input.assign( region, offset, (data.valid, data.input_lens, data.inputs), - data.input_memory.len(), )?; + + let input_expected_len = 96 + if data.valid { + data.input_lens.iter().map(U256::as_usize).sum::() + } else { + 0 + }; + + let garbage_bytes = if call.call_data_length as usize > input_expected_len { + let mut bts = Vec::new(); + bts.resize(input_expected_len - 96, 0); //front prefix zero + bts.append(&mut Vec::from(&data.input_memory[input_expected_len..])); + bts.resize(96, 0); //padding zero + bts + } else { + Vec::from([0u8;96]) + }; + + println!("garbage bytes {:?}", garbage_bytes); + + self.padding_zero.assign(region, offset, INPUT_LIMIT - input_expected_len, None)?; + + for (cell, bt) in self.garbage_bytes_holder.iter().zip(garbage_bytes){ + cell.assign(region, offset, Value::known(F::from(bt as u64)))?; + } self.output .assign(region, offset, (data.output_len, data.output))?; @@ -796,12 +809,15 @@ impl ExecutionGadget for ModExpGadget { .keccak_input() .map(|randomness| rlc::value(data.input_memory.iter().rev(), randomness)); + let n_padded_zeroes_pow = region.challenges().keccak_input() + .map(|r| r.pow(&[INPUT_LIMIT as u64 - call.call_data_length, 0, 0, 0])); + let output_rlc = region .challenges() .keccak_input() .map(|randomness| rlc::value(data.output_memory.iter().rev(), randomness)); - self.input_bytes_acc.assign(region, offset, input_rlc)?; + self.input_bytes_acc.assign(region, offset, n_padded_zeroes_pow * input_rlc)?; self.output_bytes_acc.assign(region, offset, output_rlc)?; } else { log::error!("unexpected aux_data {:?} for modexp", step.aux_data); @@ -1001,6 +1017,54 @@ mod test { address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() }, + PrecompileCallArgs { + name: "modexp no input", + setup_code: bytecode! { + // just put something in memory + PUSH1(0x1) + PUSH1(0x00) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0x0.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp success with garbage bytes", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x3) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x2) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000009")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, PrecompileCallArgs { name: "modexp zero modulus", setup_code: bytecode! { @@ -1131,7 +1195,7 @@ mod test { address: PrecompileCalls::Modexp.address().to_word(), gas: 100000.into(), ..Default::default() - }, + }, ] }; } @@ -1139,7 +1203,7 @@ mod test { #[ignore] #[test] fn precompile_modexp_test_fast() { - let bytecode = TEST_VECTOR[0].with_call_op(OpcodeId::STATICCALL); + let bytecode = TEST_VECTOR[4].with_call_op(OpcodeId::STATICCALL); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 5ac299acef..4a8ef9f526 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -52,10 +52,13 @@ impl PrecompileGadget { address.value_equals(PrecompileCalls::Bn128Add), ]); let len_96 = address.value_equals(PrecompileCalls::Bn128Mul); + let len_192 = address.value_equals(PrecompileCalls::Modexp); select::expr( len_128, 128.expr(), - select::expr(len_96, 96.expr(), cd_length.expr()), + select::expr(len_96, 96.expr(), + select::expr(len_192, 192.expr(), cd_length.expr()), + ), ) }; let pad_right = LtGadget::construct(cb, cd_length.expr(), input_len.expr()); @@ -157,8 +160,8 @@ impl PrecompileGadget { let input_bytes_acc_copied = cb.query_cell_phase2(); let output_bytes_acc_copied = cb.query_cell_phase2(); cb.require_equal( - "copy input bytes", - input_bytes_rlc.clone(), + "copy padded input bytes", + padding_gadget.padded_rlc(), input_bytes_acc_copied.expr(), ); cb.require_equal( @@ -342,9 +345,6 @@ impl PaddingGadget { // skip padding if calldata length == 0. if cd_len == 0 { (required_input_len as u64, input_rlc, Value::known(F::one())) - } else if precompile == PrecompileCalls::Modexp { - // modexp do not need right padding - (cd_len, input_rlc, Value::known(F::one())) } else { // pad only if calldata length is less than the required input length. let n_padded_zeroes = if cd_len < required_input_len as u64 { From c89cacd4c846ad9e8667b0219d3298857256008c Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Mon, 31 Jul 2023 10:54:44 +0800 Subject: [PATCH 55/57] prune and lint --- .../circuit_input_builder/input_state_ref.rs | 2 +- bus-mapping/src/precompile.rs | 12 +- .../execution/precompiles/modexp.rs | 212 +++++------------- .../src/evm_circuit/util/precompile_gadget.rs | 4 +- 4 files changed, 64 insertions(+), 166 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index f7f887cc21..ccb19c5c71 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -1604,7 +1604,7 @@ impl<'a> CircuitInputStateRef<'a> { code_address, step.gas.0, ); - return Ok(None);//Ok(Some(ExecError::PrecompileFailed)); + return Ok(None); //Ok(Some(ExecError::PrecompileFailed)); } } diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 9cd8afe4a7..2a8b448d72 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -238,16 +238,8 @@ impl ModExpAuxData { let (input_valid, [base_len, exp_len, modulus_len]) = Self::check_input(&mem_input); - let base_mem_len = if input_valid { - base_len.as_usize() - } else { - 0 - }; - let exp_mem_len = if input_valid { - exp_len.as_usize() - } else { - 0 - }; + let base_mem_len = if input_valid { base_len.as_usize() } else { 0 }; + let exp_mem_len = if input_valid { exp_len.as_usize() } else { 0 }; let modulus_mem_len = if input_valid { modulus_len.as_usize() } else { diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index 0345062714..325af89a2e 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -1,8 +1,5 @@ use eth_types::{Field, ToScalar, U256}; -use gadgets::{ - binary_number::AsBits, - util::{self, Expr}, -}; +use gadgets::util::{self, Expr}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -26,8 +23,8 @@ use bus_mapping::precompile::{PrecompileAuxData, MODEXP_INPUT_LIMIT, MODEXP_SIZE #[derive(Clone, Debug)] struct RandPowRepresent { bits: BinaryNumberGadget, - pow_assembles: Vec<(Cell, usize)>, - pow_from_bits: Option>, + pow_lookup: Cell, + cache_for_degree: Option>, pow: Expression, } @@ -43,96 +40,37 @@ impl RandPowRepresent { .expect("same length") } - /// build r**EXP (EXP can be represented by BIT_LIMIT bits) - pub fn pows_expr(randomness: Expression) -> Expression { - assert!( - 2usize.pow(BIT_LIMIT as u32) > EXP, - "EXP ({EXP}) can not exceed bit limit (2**{BIT_LIMIT}-1)" - ); - let bits: [bool; BIT_LIMIT] = EXP.as_bits(); - let base_pows = Self::base_pows_expr(randomness); - bits.as_slice() - .iter() - .rev() - .zip(&base_pows) - .fold( - 1.expr(), - |calc, (&bit, base_pow)| if bit { calc * base_pow.clone() } else { calc }, - ) - } - /// refere to a binary represent of exponent (like BinaryNumberGadget), can /// link another expression so the expr is linked_val * r ** exponent pub fn configure( - cb: &mut EVMConstraintBuilder, - randomness: Expression, - exponent: Expression, - linked_val: Option>, - ) -> Self { - let bits = BinaryNumberGadget::construct(cb, exponent); - let base_pows = Self::base_pows_expr(randomness); - let mut pow_assembles = Vec::new(); - let mut pow = linked_val.unwrap_or_else(|| 1.expr()); - for (n, (base_pow, exp_bit)) in base_pows - .into_iter() - .zip(bits.bits.as_slice().iter().rev()) - .enumerate() - { - pow = pow * util::select::expr(exp_bit.expr(), base_pow, 1.expr()); - - if pow.degree() > Self::BIT_EXP_MAX_DEGREE { - let cached_cell = cb.query_cell_phase2(); - cb.require_equal( - "pow_assemble cached current expression", - cached_cell.expr(), - pow.clone(), - ); - - pow = cached_cell.expr(); - pow_assembles.push((cached_cell, n)); - } - } - - Self { - pow_assembles, - bits, - pow, - pow_from_bits: None, - } - } - - /// same as configure, make use of rand_pow_table instead of constraint it - /// by additional cells - pub fn configure_with_lookup( cb: &mut EVMConstraintBuilder, _randomness: Expression, exponent: Expression, linked_val: Option>, ) -> Self { let bits = BinaryNumberGadget::construct(cb, exponent); - let mut pow_assembles = Vec::new(); - let lookup_cell = cb.query_cell_phase2(); - cb.pow_of_rand_lookup(bits.value(), lookup_cell.expr()); + let pow_lookup = cb.query_cell_phase2(); + cb.pow_of_rand_lookup(bits.value(), pow_lookup.expr()); - let mut pow = linked_val.unwrap_or_else(|| 1.expr()) * lookup_cell.expr(); - // still we would cache the pow expression in case degree is too larget - if pow.degree() > Self::BIT_EXP_MAX_DEGREE { + let pow = linked_val.unwrap_or_else(|| 1.expr()) * pow_lookup.expr(); + // we would cache the pow expression in case degree is too larget + let cache_for_degree = if pow.degree() > Self::BIT_EXP_MAX_DEGREE { let cached_cell = cb.query_cell_phase2(); cb.require_equal( "pow_assemble cached current expression", cached_cell.expr(), pow.clone(), ); - - pow = cached_cell.expr(); - pow_assembles.push((cached_cell, BIT_LIMIT - 1)); - } + Some(cached_cell) + } else { + None + }; Self { - pow_assembles, + pow_lookup, bits, pow, - pow_from_bits: Some(lookup_cell), + cache_for_degree, } } @@ -140,10 +78,6 @@ impl RandPowRepresent { self.pow.clone() } - pub fn phase2_cell_cost(&self) -> usize { - self.pow_assembles.len() - } - pub fn assign( &self, region: &mut CachedRegion<'_, '_, F>, @@ -156,40 +90,18 @@ impl RandPowRepresent { "exponent ({exponent}) can not exceed bit limit (2**{BIT_LIMIT}-1)" ); self.bits.assign(region, offset, exponent)?; - let bits: [bool; BIT_LIMIT] = exponent.as_bits(); - let base_pows = std::iter::successors(Some(region.challenges().keccak_input()), |val| { - Some(val.map(|v| v.square())) - }) - .take(BIT_LIMIT); - - let mut pow_cached_i = self.pow_assembles.iter(); - let mut cached_cell = pow_cached_i.next(); - let mut value_should_assigned = linked_value.unwrap_or_else(|| Value::known(F::one())); - - if let Some(cell) = &self.pow_from_bits { - cell.assign( - region, - offset, - region - .challenges() - .keccak_input() - .map(|v| v.pow(&[exponent as u64, 0, 0, 0])), - )?; - } - for (n, (base_pow, &bit)) in base_pows.zip(bits.as_slice().iter().rev()).enumerate() { - value_should_assigned = value_should_assigned - * (if bit { - base_pow - } else { - Value::known(F::one()) - }); - if let Some((cell, i)) = cached_cell { - if *i == n { - cell.assign(region, offset, value_should_assigned)?; - cached_cell = pow_cached_i.next(); - } - } + let pow_of_rand = region + .challenges() + .keccak_input() + .map(|v| v.pow(&[exponent as u64, 0, 0, 0])); + let value_should_assigned = + linked_value.unwrap_or_else(|| Value::known(F::one())) * pow_of_rand; + + self.pow_lookup.assign(region, offset, pow_of_rand)?; + + if let Some(cell) = &self.cache_for_degree { + cell.assign(region, offset, value_should_assigned)?; } Ok(value_should_assigned) @@ -231,10 +143,7 @@ fn rlc_rev( } // rlc word, in the reversed byte order -fn rlc_word_rev( - cells: &[Cell; 32], - randomness: Expression, -) -> Expression { +fn rlc_word_rev(cells: &[Cell; 32], randomness: Expression) -> Expression { rlc_rev(cells, randomness) } @@ -385,11 +294,9 @@ impl ModExpInputs { ]), ); - let base_len_expected = - util::select::expr(input_valid.expr(), base_len.value(), 0.expr()); + let base_len_expected = util::select::expr(input_valid.expr(), base_len.value(), 0.expr()); - let exp_len_expected = - util::select::expr(input_valid.expr(), exp_len.value(), 0.expr()); + let exp_len_expected = util::select::expr(input_valid.expr(), exp_len.value(), 0.expr()); let modulus_len_expected = util::select::expr(input_valid.expr(), modulus_len.value(), 0.expr()); @@ -425,8 +332,7 @@ impl ModExpInputs { Some(exp_pow.expr()), ); - let input_bytes_rlc = - rlc_word_rev(&modulus, cb.challenges().keccak_input()) //rlc of base + let input_bytes_rlc = rlc_word_rev(&modulus, cb.challenges().keccak_input()) //rlc of base + modulus_pow.expr() * rlc_word_rev(&exp, cb.challenges().keccak_input()) //rlc of exp plus r**base_len + exp_pow.expr() * rlc_word_rev(&base, cb.challenges().keccak_input()) //rlc of exp plus r**(base_len + exp_len) + base_pow.expr() * modulus_len.memory_rlc() @@ -497,11 +403,7 @@ impl ModExpInputs { let assigned = pow.assign( region, offset, - if input_valid { - len.as_usize() - } else { - 0 - }, + if input_valid { len.as_usize() } else { 0 }, linked_v, )?; @@ -545,8 +447,8 @@ impl ModExpOutputs { let result_limbs = Limbs::configure(cb, &result); let output_bytes_rlc = util::select::expr( - is_result_zero.expr(), - 0.expr(), + is_result_zero.expr(), + 0.expr(), rlc_word_rev(&result, cb.challenges().keccak_input()), ); @@ -732,7 +634,8 @@ impl ExecutionGadget for ModExpGadget { cb.require_equal( "input acc bytes with padding must equal", input_bytes_acc.expr(), - padding_zero.expr() * input.bytes_rlc() + rlc_rev(&garbage_bytes_holder, cb.challenges().keccak_input()), + padding_zero.expr() * input.bytes_rlc() + + rlc_rev(&garbage_bytes_holder, cb.challenges().keccak_input()), ); cb.require_equal( @@ -741,7 +644,6 @@ impl ExecutionGadget for ModExpGadget { output.bytes_rlc(), ); - Self { is_success, callee_address, @@ -769,19 +671,17 @@ impl ExecutionGadget for ModExpGadget { step: &ExecStep, ) -> Result<(), Error> { if let Some(PrecompileAuxData::Modexp(data)) = &step.aux_data { - println!("exp data: {:?}", data); + //println!("exp data: {:?}", data); - self.input.assign( - region, - offset, - (data.valid, data.input_lens, data.inputs), - )?; - - let input_expected_len = 96 + if data.valid { - data.input_lens.iter().map(U256::as_usize).sum::() - } else { - 0 - }; + self.input + .assign(region, offset, (data.valid, data.input_lens, data.inputs))?; + + let input_expected_len = 96 + + if data.valid { + data.input_lens.iter().map(U256::as_usize).sum::() + } else { + 0 + }; let garbage_bytes = if call.call_data_length as usize > input_expected_len { let mut bts = Vec::new(); @@ -790,14 +690,15 @@ impl ExecutionGadget for ModExpGadget { bts.resize(96, 0); //padding zero bts } else { - Vec::from([0u8;96]) + Vec::from([0u8; 96]) }; - println!("garbage bytes {:?}", garbage_bytes); + //println!("garbage bytes {:?}", garbage_bytes); - self.padding_zero.assign(region, offset, INPUT_LIMIT - input_expected_len, None)?; + self.padding_zero + .assign(region, offset, INPUT_LIMIT - input_expected_len, None)?; - for (cell, bt) in self.garbage_bytes_holder.iter().zip(garbage_bytes){ + for (cell, bt) in self.garbage_bytes_holder.iter().zip(garbage_bytes) { cell.assign(region, offset, Value::known(F::from(bt as u64)))?; } @@ -809,7 +710,9 @@ impl ExecutionGadget for ModExpGadget { .keccak_input() .map(|randomness| rlc::value(data.input_memory.iter().rev(), randomness)); - let n_padded_zeroes_pow = region.challenges().keccak_input() + let n_padded_zeroes_pow = region + .challenges() + .keccak_input() .map(|r| r.pow(&[INPUT_LIMIT as u64 - call.call_data_length, 0, 0, 0])); let output_rlc = region @@ -817,7 +720,8 @@ impl ExecutionGadget for ModExpGadget { .keccak_input() .map(|randomness| rlc::value(data.output_memory.iter().rev(), randomness)); - self.input_bytes_acc.assign(region, offset, n_padded_zeroes_pow * input_rlc)?; + self.input_bytes_acc + .assign(region, offset, n_padded_zeroes_pow * input_rlc)?; self.output_bytes_acc.assign(region, offset, output_rlc)?; } else { log::error!("unexpected aux_data {:?} for modexp", step.aux_data); @@ -1031,7 +935,7 @@ mod test { ret_size: 0x01.into(), address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() - }, + }, PrecompileCallArgs { name: "modexp success with garbage bytes", setup_code: bytecode! { @@ -1056,7 +960,7 @@ mod test { MSTORE PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) PUSH1(0xA0) - MSTORE + MSTORE }, call_data_offset: 0x0.into(), call_data_length: 0xc0.into(), @@ -1064,7 +968,7 @@ mod test { ret_size: 0x01.into(), address: PrecompileCalls::Modexp.address().to_word(), ..Default::default() - }, + }, PrecompileCallArgs { name: "modexp zero modulus", setup_code: bytecode! { @@ -1195,7 +1099,7 @@ mod test { address: PrecompileCalls::Modexp.address().to_word(), gas: 100000.into(), ..Default::default() - }, + }, ] }; } diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index 4a8ef9f526..72f5717d49 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -56,7 +56,9 @@ impl PrecompileGadget { select::expr( len_128, 128.expr(), - select::expr(len_96, 96.expr(), + select::expr( + len_96, + 96.expr(), select::expr(len_192, 192.expr(), cd_length.expr()), ), ) From 91108c02fa0d3ce353cdcdf9cb95d0ceeeb5b484 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Mon, 31 Jul 2023 11:16:50 +0800 Subject: [PATCH 56/57] + resume the precompile error detection. + trivial logs --- bus-mapping/src/circuit_input_builder/input_state_ref.rs | 2 +- zkevm-circuits/src/modexp_circuit.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index ccb19c5c71..dc48d4aa0b 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -1604,7 +1604,7 @@ impl<'a> CircuitInputStateRef<'a> { code_address, step.gas.0, ); - return Ok(None); //Ok(Some(ExecError::PrecompileFailed)); + return Ok(Some(ExecError::PrecompileFailed)); } } diff --git a/zkevm-circuits/src/modexp_circuit.rs b/zkevm-circuits/src/modexp_circuit.rs index 1e4ccc0083..c0207813d8 100644 --- a/zkevm-circuits/src/modexp_circuit.rs +++ b/zkevm-circuits/src/modexp_circuit.rs @@ -154,6 +154,7 @@ impl SubCircuit for ModExpCircuit { ); exp_events.resize(event_limit, Default::default()); + log::info!("modexp circuit work with maxium {} entries", event_limit); Self(exp_events, Default::default()) } From 98a75b2f8b04e357c8d5157d3f8427103bd22f11 Mon Sep 17 00:00:00 2001 From: Ho Vei Date: Tue, 1 Aug 2023 09:20:57 +0800 Subject: [PATCH 57/57] update modexp chip rows usage constant --- zkevm-circuits/src/modexp_circuit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zkevm-circuits/src/modexp_circuit.rs b/zkevm-circuits/src/modexp_circuit.rs index c0207813d8..78c8a17a65 100644 --- a/zkevm-circuits/src/modexp_circuit.rs +++ b/zkevm-circuits/src/modexp_circuit.rs @@ -128,7 +128,7 @@ impl ModExpCircuitConfig { } } -const MODEXPCONFIG_EACH_CHIP_ROWS: usize = 9291; +const MODEXPCONFIG_EACH_CHIP_ROWS: usize = 24576; /// ModExp circuit for precompile modexp #[derive(Clone, Debug, Default)]