diff --git a/bus-mapping/src/evm.rs b/bus-mapping/src/evm.rs index 510ede7b53..b1ad5235fd 100644 --- a/bus-mapping/src/evm.rs +++ b/bus-mapping/src/evm.rs @@ -164,6 +164,10 @@ impl GasCost { pub const TX: Self = Self(21000); /// Constant cost for creation transaction pub const CREATION_TX: Self = Self(53000); + /// Denominator of quadratic part of memory expansion gas cost + pub const MEMORY_EXPANSION_QUAD_DENOMINATOR: Self = Self(512); + /// Coefficient of linear part of memory expansion gas cost + pub const MEMORY_EXPANSION_LINEAR_COEFF: Self = Self(3); } impl GasCost { diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index ca899a503f..26604710d9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{MAX_GAS_SIZE_IN_BYTES, STACK_CAPACITY}, + param::{N_BYTES_GAS, STACK_CAPACITY}, step::ExecutionState, table::{AccountFieldTag, CallContextFieldTag, TxContextFieldTag}, util::{ @@ -38,7 +38,7 @@ pub(crate) struct BeginTxGadget { tx_call_data_gas_cost: Cell, rw_counter_end_of_reversion: Cell, is_persistent: Cell, - sufficient_gas_left: RangeCheckGadget, + sufficient_gas_left: RangeCheckGadget, transfer_with_gas_fee: TransferWithGasFeeGadget, code_hash: Cell, } @@ -328,6 +328,7 @@ mod test { eth_types::{self, Address, ToLittleEndian, ToWord, Word}, evm::{GasCost, OpcodeId}, }; + use std::convert::TryInto; fn test_ok(tx: eth_types::Transaction, result: bool) { let rw_counter_end_of_reversion = if result { 0 } else { 20 }; @@ -355,8 +356,8 @@ mod test { randomness, txs: vec![Transaction { id: 1, - nonce: tx.nonce.low_u64(), - gas: tx.gas.low_u64(), + nonce: tx.nonce.try_into().unwrap(), + gas: tx.gas.try_into().unwrap(), gas_price: tx.gas_price.unwrap_or_else(Word::zero), caller_address: tx.from, callee_address: tx.to.unwrap_or_else(Address::zero), diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_pure_memory.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_pure_memory.rs index c0e397658e..dc404a1689 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_pure_memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_pure_memory.rs @@ -1,11 +1,11 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::MAX_MEMORY_SIZE_IN_BYTES, + param::{N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE}, step::ExecutionState, util::{ constraint_builder::ConstraintBuilder, - math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, + math_gadget::{IsEqualGadget, IsZeroGadget, RangeCheckGadget}, memory_gadget::{address_high, address_low, MemoryExpansionGadget}, Cell, Word, }, @@ -21,9 +21,18 @@ pub(crate) struct ErrorOOGPureMemoryGadget { opcode: Cell, address: Word, address_in_range: IsZeroGadget, + // Allow memory size to expand to 5 bytes, because memory address could be + // at most 2^40 - 1, after constant division by 32, the memory word size + // then could be at most 2^35 - 1. + // So generic N_BYTES_MEMORY_WORD_SIZE for MemoryExpansionGadget needs to + // be larger by 1 than normal usage (to be 5), to be able to contain + // number up to 2^35 - 1. memory_expansion: - MemoryExpansionGadget, - insufficient_gas: LtGadget, + MemoryExpansionGadget, + // Even memory size at most could be 2^35 - 1, the qudratic part of memory + // expansion gas cost could be at most 2^61 - 2^27, due to the constant + // division by 512, which still fits in 8 bytes. + insufficient_gas: RangeCheckGadget, is_mstore8: IsEqualGadget, } @@ -52,9 +61,9 @@ impl ExecutionGadget for ErrorOOGPureMemoryGadget { let memory_expansion = MemoryExpansionGadget::construct( cb, cb.curr.state.memory_word_size.expr(), - address_low::expr(&address) + [address_low::expr(&address) + 1.expr() - + (is_not_mstore8 * 31.expr()), + + (is_not_mstore8 * 31.expr())], ); // Check if the memory address is too large @@ -62,20 +71,14 @@ impl ExecutionGadget for ErrorOOGPureMemoryGadget { IsZeroGadget::construct(cb, address_high::expr(&address)); // Check if the amount of gas available is less than the amount of gas // required - let insufficient_gas = LtGadget::construct( - cb, - cb.curr.state.gas_left.expr(), - OpcodeId::MLOAD.constant_gas_cost().expr() - + memory_expansion.gas_cost(), - ); - - // Make sure we are out of gas - // Out of gas when either the address is too big and/or the amount of - // gas available is lower than what is required. - cb.require_zero( - "Either the address is too big or insufficient gas", - address_in_range.expr() * (1.expr() - insufficient_gas.expr()), - ); + let insufficient_gas = cb.condition(address_in_range.expr(), |cb| { + RangeCheckGadget::construct( + cb, + OpcodeId::MLOAD.constant_gas_cost().expr() + + memory_expansion.gas_cost() + - cb.curr.state.gas_left.expr(), + ) + }); // Pop the address from the stack // We still have to do this to verify the correctness of `address` @@ -130,9 +133,8 @@ impl ExecutionGadget for ErrorOOGPureMemoryGadget { region, offset, step.memory_word_size(), - address_low::value::(address.to_le_bytes()) - + 1 - + if is_mstore8 == F::one() { 0 } else { 31 }, + [address_low::value(address.to_le_bytes()) + + if is_mstore8 == F::one() { 1 } else { 32 }], )?; // Gas insufficient check @@ -140,8 +142,7 @@ impl ExecutionGadget for ErrorOOGPureMemoryGadget { self.insufficient_gas.assign( region, offset, - F::from(step.gas_left), - F::from(step.gas_cost), + F::from(step.gas_cost - step.gas_left), )?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/jump.rs b/zkevm-circuits/src/evm_circuit/execution/jump.rs index 70121ae273..b26ebf837b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jump.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::NUM_BYTES_PROGRAM_COUNTER, + param::N_BYTES_PROGRAM_COUNTER, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -22,7 +22,7 @@ use std::convert::TryInto; #[derive(Clone, Debug)] pub(crate) struct JumpGadget { same_context: SameContextGadget, - destination: RandomLinearCombination, + destination: RandomLinearCombination, } impl ExecutionGadget for JumpGadget { @@ -31,10 +31,7 @@ impl ExecutionGadget for JumpGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::JUMP; fn configure(cb: &mut ConstraintBuilder) -> Self { - let destination = RandomLinearCombination::new( - cb.query_bytes(), - cb.power_of_randomness(), - ); + let destination = cb.query_rlc(); // Pop the value from the stack cb.stack_pop(destination.expr()); @@ -83,7 +80,7 @@ impl ExecutionGadget for JumpGadget { region, offset, Some( - destination.to_le_bytes()[..NUM_BYTES_PROGRAM_COUNTER] + destination.to_le_bytes()[..N_BYTES_PROGRAM_COUNTER] .try_into() .unwrap(), ), diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs index 083f9824c6..fe4d413465 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::NUM_BYTES_PROGRAM_COUNTER, + param::N_BYTES_PROGRAM_COUNTER, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -24,7 +24,7 @@ use std::convert::TryInto; #[derive(Clone, Debug)] pub(crate) struct JumpiGadget { same_context: SameContextGadget, - destination: RandomLinearCombination, + destination: RandomLinearCombination, condition: Cell, is_condition_zero: IsZeroGadget, } @@ -35,10 +35,7 @@ impl ExecutionGadget for JumpiGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::JUMPI; fn configure(cb: &mut ConstraintBuilder) -> Self { - let destination = RandomLinearCombination::new( - cb.query_bytes(), - cb.power_of_randomness(), - ); + let destination = cb.query_rlc(); let condition = cb.query_cell(); // Pop the value from the stack @@ -111,7 +108,7 @@ impl ExecutionGadget for JumpiGadget { region, offset, Some( - destination.to_le_bytes()[..NUM_BYTES_PROGRAM_COUNTER] + destination.to_le_bytes()[..N_BYTES_PROGRAM_COUNTER] .try_into() .unwrap(), ), diff --git a/zkevm-circuits/src/evm_circuit/execution/memory.rs b/zkevm-circuits/src/evm_circuit/execution/memory.rs index 26aa269fba..34d62e68a4 100644 --- a/zkevm-circuits/src/evm_circuit/execution/memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/memory.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{MAX_GAS_SIZE_IN_BYTES, NUM_ADDRESS_BYTES_USED}, + param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE}, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -27,7 +27,7 @@ pub(crate) struct MemoryGadget { same_context: SameContextGadget, address: MemoryAddress, value: Word, - memory_expansion: MemoryExpansionGadget, + memory_expansion: MemoryExpansionGadget, is_mload: IsEqualGadget, is_mstore8: IsEqualGadget, } @@ -41,8 +41,7 @@ impl ExecutionGadget for MemoryGadget { let opcode = cb.query_cell(); // In successful case the address must be in 5 bytes - let address = - MemoryAddress::new(cb.query_bytes(), cb.power_of_randomness()); + let address = cb.query_rlc(); let value = cb.query_word(); // Check if this is an MLOAD @@ -64,9 +63,9 @@ impl ExecutionGadget for MemoryGadget { let memory_expansion = MemoryExpansionGadget::construct( cb, cb.curr.state.memory_word_size.expr(), - from_bytes::expr(&address.cells) + [from_bytes::expr(&address.cells) + 1.expr() - + (is_not_mstore8.clone() * 31.expr()), + + (is_not_mstore8.clone() * 31.expr())], ); /* Stack operations */ @@ -166,7 +165,7 @@ impl ExecutionGadget for MemoryGadget { region, offset, Some( - address.to_le_bytes()[..NUM_ADDRESS_BYTES_USED] + address.to_le_bytes()[..N_BYTES_MEMORY_ADDRESS] .try_into() .unwrap(), ), @@ -194,7 +193,7 @@ impl ExecutionGadget for MemoryGadget { region, offset, step.memory_word_size(), - address.as_u64() + 1 + if is_mstore8 == F::one() { 0 } else { 31 }, + [address.as_u64() + if is_mstore8 == F::one() { 1 } else { 32 }], )?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/msize.rs b/zkevm-circuits/src/evm_circuit/execution/msize.rs index b204a16c6e..54f328a237 100644 --- a/zkevm-circuits/src/evm_circuit/execution/msize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/msize.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::NUM_BYTES_WORD, + param::N_BYTES_WORD, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -14,7 +14,6 @@ use crate::{ }, util::Expr, }; -use array_init::array_init; use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error}; #[derive(Clone, Debug)] @@ -29,17 +28,16 @@ impl ExecutionGadget for MsizeGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::MSIZE; fn configure(cb: &mut ConstraintBuilder) -> Self { + let value = cb.query_rlc(); + // memory_size is limited to 64 bits so we only consider 8 bytes - let bytes = array_init(|_| cb.query_cell()); cb.require_equal( "Constrain memory_size equal to stack value", - from_bytes::expr(&bytes), - cb.curr.state.memory_word_size.expr() * NUM_BYTES_WORD.expr(), + from_bytes::expr(&value.cells), + cb.curr.state.memory_word_size.expr() * N_BYTES_WORD.expr(), ); // Push the value on the stack - let value = - RandomLinearCombination::new(bytes, cb.power_of_randomness()); cb.stack_push(value.expr()); // State transition @@ -76,7 +74,7 @@ impl ExecutionGadget for MsizeGadget { self.value.assign( region, offset, - Some(step.memory_size.to_le_bytes()), + Some((step.memory_size as u64).to_le_bytes()), )?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/pc.rs b/zkevm-circuits/src/evm_circuit/execution/pc.rs index ff3a47d801..f4b840b06e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pc.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pc.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::NUM_BYTES_PROGRAM_COUNTER, + param::N_BYTES_PROGRAM_COUNTER, step::ExecutionState, util::{ common_gadget::SameContextGadget, @@ -14,13 +14,12 @@ use crate::{ }, util::Expr, }; -use array_init::array_init; use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct PcGadget { same_context: SameContextGadget, - value: RandomLinearCombination, + value: RandomLinearCombination, } impl ExecutionGadget for PcGadget { @@ -29,17 +28,16 @@ impl ExecutionGadget for PcGadget { const EXECUTION_STATE: ExecutionState = ExecutionState::PC; fn configure(cb: &mut ConstraintBuilder) -> Self { + let value = cb.query_rlc(); + // program_counter is limited to 64 bits so we only consider 8 bytes - let bytes = array_init(|_| cb.query_cell()); cb.require_equal( "Constrain program_counter equal to stack value", - from_bytes::expr(&bytes), + from_bytes::expr(&value.cells), cb.curr.state.program_counter.expr(), ); // Push the value on the stack - let value = - RandomLinearCombination::new(bytes, cb.power_of_randomness()); cb.stack_push(value.expr()); // State transition diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 1b863f44fc..9c576a40c2 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -2,28 +2,33 @@ pub(crate) const STEP_WIDTH: usize = 32; /// Step height pub const STEP_HEIGHT: usize = 14; -pub(crate) const NUM_CELLS_STEP_STATE: usize = 10; +pub(crate) const N_CELLS_STEP_STATE: usize = 10; -// The number of bytes an u64 has. -pub(crate) const NUM_BYTES_U64: usize = 8; -// The number of bytes in EVM word. -pub(crate) const NUM_BYTES_WORD: usize = 32; +/// Maximum number of bytes that an integer can fit in field without wrapping +/// around. +pub(crate) const MAX_N_BYTES_INTEGER: usize = 31; -/// The maximum number of bytes that a field element -/// can be broken down into without causing the value it -/// represents to overflow a single field element. -pub(crate) const MAX_BYTES_FIELD: usize = 31; +// Number of bytes an EVM word has. +pub(crate) const N_BYTES_WORD: usize = 32; + +// Number of bytes an u64 has. +pub(crate) const N_BYTES_U64: usize = 8; + +pub(crate) const N_BYTES_GAS: usize = N_BYTES_U64; + +pub(crate) const N_BYTES_ACCOUNT_ADDRESS: usize = 20; + +// Number of bytes that will be used of the memory address and size. +// If any of the other more signficant bytes are used it will always result in +// an out-of-gas error. +pub(crate) const N_BYTES_MEMORY_ADDRESS: usize = 5; +pub(crate) const N_BYTES_MEMORY_WORD_SIZE: usize = 4; pub(crate) const STACK_CAPACITY: usize = 1024; -pub(crate) const MAX_GAS_SIZE_IN_BYTES: usize = 8; -// Number of bytes that will be used of the address word. -// If any of the other more signficant bytes are used it will -// always result in an out-of-gas error. -pub(crate) const NUM_ADDRESS_BYTES_USED: usize = 5; -pub(crate) const MAX_MEMORY_SIZE_IN_BYTES: usize = 5; + // Number of bytes that will be used of prorgam counter. Although the maximum // size of execution bytecode could be at most 128kB due to the size limit of a // transaction, which could be covered by 3 bytes, we still support program // counter to u64 as go-ethereum in case transaction size is allowed larger in // the future. -pub(crate) const NUM_BYTES_PROGRAM_COUNTER: usize = NUM_BYTES_U64; +pub(crate) const N_BYTES_PROGRAM_COUNTER: usize = N_BYTES_U64; diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index 87c1896e1a..fc74a079bb 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -1,6 +1,6 @@ use crate::{ evm_circuit::{ - param::{NUM_CELLS_STEP_STATE, STEP_HEIGHT, STEP_WIDTH}, + param::{N_CELLS_STEP_STATE, STEP_HEIGHT, STEP_WIDTH}, util::Cell, witness::{Call, ExecStep}, }, @@ -438,7 +438,7 @@ impl Step { advices: [Column; STEP_WIDTH], is_next_step: bool, ) -> Self { - let num_state_cells = ExecutionState::amount() + NUM_CELLS_STEP_STATE; + let num_state_cells = ExecutionState::amount() + N_CELLS_STEP_STATE; let state = { let mut cells = VecDeque::with_capacity(num_state_cells); diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index 6b9931fbd9..babe9dbce5 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -1,4 +1,4 @@ -use crate::{evm_circuit::param::MAX_MEMORY_SIZE_IN_BYTES, util::Expr}; +use crate::{evm_circuit::param::N_BYTES_MEMORY_ADDRESS, util::Expr}; use bus_mapping::eth_types::U256; use halo2::{ arithmetic::FieldExt, @@ -140,7 +140,7 @@ impl Expr for RandomLinearCombination { pub(crate) type Word = RandomLinearCombination; pub(crate) type MemoryAddress = - RandomLinearCombination; + RandomLinearCombination; /// Returns the sum of the passed in cells pub(crate) mod sum { @@ -218,11 +218,14 @@ pub(crate) mod select { /// Decodes a field element from its byte representation pub(crate) mod from_bytes { - use crate::{evm_circuit::param::MAX_BYTES_FIELD, util::Expr}; + use crate::{evm_circuit::param::MAX_N_BYTES_INTEGER, util::Expr}; use halo2::{arithmetic::FieldExt, plonk::Expression}; pub(crate) fn expr>(bytes: &[E]) -> Expression { - assert!(bytes.len() <= MAX_BYTES_FIELD, "number of bytes too large"); + assert!( + bytes.len() <= MAX_N_BYTES_INTEGER, + "Too many bytes to compose an integer in field" + ); let mut value = 0.expr(); let mut multiplier = F::one(); for byte in bytes.iter() { @@ -233,7 +236,10 @@ pub(crate) mod from_bytes { } pub(crate) fn value(bytes: &[u8]) -> F { - assert!(bytes.len() <= MAX_BYTES_FIELD, "number of bytes too large"); + assert!( + bytes.len() <= MAX_N_BYTES_INTEGER, + "Too many bytes to compose an integer in field" + ); let mut value = F::zero(); let mut multiplier = F::one(); for byte in bytes.iter() { diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 2df5706f10..ed5ef240d8 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -1,6 +1,6 @@ use crate::{ evm_circuit::{ - param::MAX_GAS_SIZE_IN_BYTES, + param::N_BYTES_GAS, table::{AccountFieldTag, FixedTableTag, Lookup}, util::{ constraint_builder::{ @@ -26,7 +26,7 @@ use halo2::{ #[derive(Clone, Debug)] pub(crate) struct SameContextGadget { opcode: Cell, - sufficient_gas_left: RangeCheckGadget, + sufficient_gas_left: RangeCheckGadget, } impl SameContextGadget { diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index aa190bf639..6f1d2baf32 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -5,7 +5,7 @@ use crate::{ AccountFieldTag, CallContextFieldTag, FixedTableTag, Lookup, RwTableTag, TxContextFieldTag, }, - util::{Cell, Word}, + util::{Cell, RandomLinearCombination, Word}, }, util::Expr, }; @@ -189,7 +189,16 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { } pub(crate) fn query_word(&mut self) -> Word { - Word::new(self.query_bytes(), self.power_of_randomness) + self.query_rlc() + } + + pub(crate) fn query_rlc( + &mut self, + ) -> RandomLinearCombination { + RandomLinearCombination::::new( + self.query_bytes(), + self.power_of_randomness, + ) } pub(crate) fn query_bytes(&mut self) -> [Cell; N] { diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget.rs index 31da4b37aa..784604ccaf 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget.rs @@ -1,16 +1,14 @@ use crate::{ - evm_circuit::{ - param::MAX_BYTES_FIELD, - util::{ - self, constraint_builder::ConstraintBuilder, from_bytes, - pow_of_two, pow_of_two_expr, select, split_u256, sum, Cell, - }, + evm_circuit::util::{ + self, constraint_builder::ConstraintBuilder, from_bytes, pow_of_two, + pow_of_two_expr, select, split_u256, sum, Cell, }, util::Expr, }; -use bus_mapping::eth_types::{ToLittleEndian, Word}; +use bus_mapping::eth_types::{ToLittleEndian, ToScalar, Word}; use halo2::plonk::Error; use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Expression}; +use std::convert::TryFrom; /// Returns `1` when `value == 0`, and returns `0` otherwise. #[derive(Clone, Debug)] @@ -177,16 +175,8 @@ impl AddWordsGadget { let carry_lo = (sum_of_addends_lo - sum_lo) >> 128; let carry_hi = (sum_of_addends_hi + carry_lo - sum_hi) >> 128; - self.carry_lo.assign( - region, - offset, - Some(F::from(carry_lo.low_u64())), - )?; - self.carry_hi.assign( - region, - offset, - Some(F::from(carry_hi.low_u64())), - )?; + self.carry_lo.assign(region, offset, carry_lo.to_scalar())?; + self.carry_hi.assign(region, offset, carry_hi.to_scalar())?; Ok(()) } @@ -466,9 +456,12 @@ impl MulWordByU64Gadget { let mut assign_quotient = |cells: &[Cell], value: Word| -> Result<(), Error> { - for (cell, byte) in - cells.iter().zip(value.low_u64().to_le_bytes().iter()) - { + for (cell, byte) in cells.iter().zip( + u64::try_from(value) + .map_err(|_| Error::Synthesis)? + .to_le_bytes() + .iter(), + ) { cell.assign(region, offset, Some(F::from(*byte as u64)))?; } Ok(()) @@ -493,20 +486,19 @@ impl MulWordByU64Gadget { } /// Requires that the passed in value is within the specified range. -/// `NUM_BYTES` is required to be `<= 31`. +/// `N_BYTES` is required to be `<= MAX_N_BYTES_INTEGER`. #[derive(Clone, Debug)] -pub struct RangeCheckGadget { - parts: [Cell; NUM_BYTES], +pub struct RangeCheckGadget { + parts: [Cell; N_BYTES], } -impl RangeCheckGadget { +impl RangeCheckGadget { pub(crate) fn construct( cb: &mut ConstraintBuilder, value: Expression, ) -> Self { - assert!(NUM_BYTES <= 31, "number of bytes too large"); - let parts = cb.query_bytes(); + // Require that the reconstructed value from the parts equals the // original value cb.require_equal( @@ -533,33 +525,31 @@ impl RangeCheckGadget { } /// Returns `1` when `lhs < rhs`, and returns `0` otherwise. -/// lhs and rhs `< 256**NUM_BYTES` -/// `NUM_BYTES` is required to be `<= MAX_BYTES_FIELD` to prevent overflow: -/// values are stored in a single field element and two of these -/// are added together. +/// lhs and rhs `< 256**N_BYTES` +/// `N_BYTES` is required to be `<= MAX_N_BYTES_INTEGER` to prevent overflow: +/// values are stored in a single field element and two of these are added +/// together. /// The equation that is enforced is `lhs - rhs == diff - (lt * range)`. -/// Because all values are `<= 256**NUM_BYTES` and `lt` is boolean, -/// `lt` can only be `1` when `lhs < rhs`. +/// Because all values are `<= 256**N_BYTES` and `lt` is boolean, `lt` can only +/// be `1` when `lhs < rhs`. #[derive(Clone, Debug)] -pub struct LtGadget { +pub struct LtGadget { lt: Cell, // `1` when `lhs < rhs`, `0` otherwise. - diff: [Cell; NUM_BYTES], /* The byte values of `diff`. + diff: [Cell; N_BYTES], /* The byte values of `diff`. * `diff` equals `lhs - rhs` if `lhs >= rhs`, * `lhs - rhs + range` otherwise. */ - range: F, // The range of the inputs, `256**NUM_BYTES` + range: F, // The range of the inputs, `256**N_BYTES` } -impl LtGadget { +impl LtGadget { pub(crate) fn construct( cb: &mut ConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { - assert!(NUM_BYTES <= MAX_BYTES_FIELD, "unsupported number of bytes"); - let lt = cb.query_bool(); let diff = cb.query_bytes(); - let range = pow_of_two(NUM_BYTES * 8); + let range = pow_of_two(N_BYTES * 8); // The equation we require to hold: `lhs - rhs == diff - (lt * range)`. cb.require_equal( @@ -587,7 +577,7 @@ impl LtGadget { self.lt.assign( region, offset, - Some(F::from(if lt { 1 } else { 0 })), + Some(if lt { F::one() } else { F::zero() }), )?; // Set the bytes of diff @@ -608,21 +598,21 @@ impl LtGadget { /// Returns (lt, eq): /// - `lt` is `1` when `lhs < rhs`, `0` otherwise. /// - `eq` is `1` when `lhs == rhs`, `0` otherwise. -/// lhs and rhs `< 256**NUM_BYTES` -/// `NUM_BYTES` is required to be `<= MAX_BYTES_FIELD`. +/// lhs and rhs `< 256**N_BYTES` +/// `N_BYTES` is required to be `<= MAX_N_BYTES_INTEGER`. #[derive(Clone, Debug)] -pub struct ComparisonGadget { - lt: LtGadget, +pub struct ComparisonGadget { + lt: LtGadget, eq: IsZeroGadget, } -impl ComparisonGadget { +impl ComparisonGadget { pub(crate) fn construct( cb: &mut ConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { - let lt = LtGadget::::construct(cb, lhs, rhs); + let lt = LtGadget::::construct(cb, lhs, rhs); let eq = IsZeroGadget::::construct(cb, sum::expr(<.diff_bytes())); Self { lt, eq } @@ -705,55 +695,58 @@ impl PairSelectGadget { } } -/// Returns (quotient: numerator/divisor, remainder: numerator%divisor), -/// with `numerator` an expression and `divisor` a constant. +/// Returns (quotient: numerator/denominator, remainder: numerator%denominator), +/// with `numerator` an expression and `denominator` a constant. /// Input requirements: -/// - `quotient < 256**NUM_BYTES` -/// - `quotient * divisor < field size` -/// - `remainder < divisor` currently requires a lookup table for `divisor` +/// - `quotient < 256**N_BYTES` +/// - `quotient * denominator < field size` +/// - `remainder < denominator` requires a range lookup table for `denominator` #[derive(Clone, Debug)] -pub struct ConstantDivisionGadget { +pub struct ConstantDivisionGadget { quotient: Cell, remainder: Cell, - divisor: u64, - quotient_range_check: RangeCheckGadget, + denominator: u64, + quotient_range_check: RangeCheckGadget, } -impl ConstantDivisionGadget { +impl ConstantDivisionGadget { pub(crate) fn construct( cb: &mut ConstraintBuilder, numerator: Expression, - divisor: u64, + denominator: u64, ) -> Self { let quotient = cb.query_cell(); let remainder = cb.query_cell(); - // Require that remainder < divisor - cb.range_lookup(remainder.expr(), divisor); + // Require that remainder < denominator + cb.range_lookup(remainder.expr(), denominator); - // Require that quotient < 2**NUM_BYTES - // so we can't have any overflow when doing `quotient * divisor`. + // Require that quotient < 2**N_BYTES + // so we can't have any overflow when doing `quotient * denominator`. let quotient_range_check = RangeCheckGadget::construct(cb, quotient.expr()); // Check if the division was done correctly cb.require_equal( - "lhnumerator - remainder == quotient ⋅ divisor", + "numerator - remainder == quotient ⋅ denominator", numerator - remainder.expr(), - quotient.expr() * divisor.expr(), + quotient.expr() * denominator.expr(), ); Self { quotient, remainder, - divisor, + denominator, quotient_range_check, } } - pub(crate) fn expr(&self) -> (Expression, Expression) { - // Return the quotient and the remainder - (self.quotient.expr(), self.remainder.expr()) + pub(crate) fn quotient(&self) -> Expression { + self.quotient.expr() + } + + pub(crate) fn remainder(&self) -> Expression { + self.remainder.expr() } pub(crate) fn assign( @@ -762,9 +755,10 @@ impl ConstantDivisionGadget { offset: usize, numerator: u128, ) -> Result<(u128, u128), Error> { - let divisor = self.divisor as u128; - let quotient = numerator / divisor; - let remainder = numerator % divisor; + let denominator = self.denominator as u128; + let quotient = numerator / denominator; + let remainder = numerator % denominator; + self.quotient .assign(region, offset, Some(F::from_u128(quotient)))?; self.remainder @@ -781,27 +775,33 @@ impl ConstantDivisionGadget { } /// Returns `rhs` when `lhs < rhs`, and returns `lhs` otherwise. -/// lhs and rhs `< 256**NUM_BYTES` -/// `NUM_BYTES` is required to be `<= 31`. +/// lhs and rhs `< 256**N_BYTES` +/// `N_BYTES` is required to be `<= MAX_N_BYTES_INTEGER`. #[derive(Clone, Debug)] -pub struct MaxGadget { - lt: LtGadget, +pub struct MinMaxGadget { + lt: LtGadget, + min: Expression, max: Expression, } -impl MaxGadget { +impl MinMaxGadget { pub(crate) fn construct( cb: &mut ConstraintBuilder, lhs: Expression, rhs: Expression, ) -> Self { let lt = LtGadget::construct(cb, lhs.clone(), rhs.clone()); - let max = select::expr(lt.expr(), rhs, lhs); + let max = select::expr(lt.expr(), rhs.clone(), lhs.clone()); + let min = select::expr(lt.expr(), lhs, rhs); - Self { lt, max } + Self { lt, min, max } } - pub(crate) fn expr(&self) -> Expression { + pub(crate) fn min(&self) -> Expression { + self.min.clone() + } + + pub(crate) fn max(&self) -> Expression { self.max.clone() } @@ -811,8 +811,12 @@ impl MaxGadget { offset: usize, lhs: F, rhs: F, - ) -> Result { + ) -> Result<(F, F), Error> { let (lt, _) = self.lt.assign(region, offset, lhs, rhs)?; - Ok(select::value(lt, rhs, lhs)) + Ok(if lt.is_zero_vartime() { + (rhs, lhs) + } else { + (lhs, rhs) + }) } } diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index d580615a1f..cc961e6607 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -1,31 +1,43 @@ -use crate::evm_circuit::{ - param::MAX_MEMORY_SIZE_IN_BYTES, - util::{ - constraint_builder::ConstraintBuilder, - math_gadget::{ConstantDivisionGadget, MaxGadget}, - Address, MemorySize, +use crate::{ + evm_circuit::{ + param::{ + N_BYTES_GAS, N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE, + }, + util::{ + constraint_builder::ConstraintBuilder, + from_bytes, + math_gadget::IsZeroGadget, + math_gadget::{ConstantDivisionGadget, MinMaxGadget}, + sum, Cell, MemoryAddress, Word, + }, }, + util::Expr, +}; +use array_init::array_init; +use bus_mapping::{ + eth_types::{ToLittleEndian, U256}, + evm::GasCost, }; -use crate::util::Expr; -use bus_mapping::evm::GasCost; use halo2::plonk::Error; use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Expression}; +use std::convert::TryInto; /// Decodes the usable part of an address stored in a Word pub(crate) mod address_low { use crate::evm_circuit::{ - param::NUM_ADDRESS_BYTES_USED, - util::{from_bytes, Address, Word}, + param::N_BYTES_MEMORY_ADDRESS, + util::{from_bytes, Word}, }; use halo2::{arithmetic::FieldExt, plonk::Expression}; pub(crate) fn expr(address: &Word) -> Expression { - from_bytes::expr(&address.cells[0..NUM_ADDRESS_BYTES_USED]) + from_bytes::expr(&address.cells[..N_BYTES_MEMORY_ADDRESS]) } - pub(crate) fn value(address: [u8; 32]) -> Address { - from_bytes::value::(&address[0..NUM_ADDRESS_BYTES_USED]) - .get_lower_128() as Address + pub(crate) fn value(address: [u8; 32]) -> u64 { + let mut bytes = [0; 8]; + bytes.copy_from_slice(&address[..N_BYTES_MEMORY_ADDRESS]); + u64::from_le_bytes(bytes) } } @@ -33,17 +45,117 @@ pub(crate) mod address_low { /// address pub(crate) mod address_high { use crate::evm_circuit::{ - param::NUM_ADDRESS_BYTES_USED, + param::N_BYTES_MEMORY_ADDRESS, util::{sum, Word}, }; use halo2::{arithmetic::FieldExt, plonk::Expression}; pub(crate) fn expr(address: &Word) -> Expression { - sum::expr(&address.cells[NUM_ADDRESS_BYTES_USED..32]) + sum::expr(&address.cells[N_BYTES_MEMORY_ADDRESS..]) } pub(crate) fn value(address: [u8; 32]) -> F { - sum::value::(&address[NUM_ADDRESS_BYTES_USED..32]) + sum::value::(&address[N_BYTES_MEMORY_ADDRESS..]) + } +} + +/// Convert the dynamic memory offset and length from random linear combiation +/// to integer. It handles the "no expansion" feature when length is zero. +#[derive(Clone, Debug)] +pub(crate) struct MemoryAddressGadget { + memory_offset: Cell, + memory_offset_bytes: MemoryAddress, + memory_length: MemoryAddress, + memory_length_is_zero: IsZeroGadget, +} + +impl MemoryAddressGadget { + pub(crate) fn construct( + cb: &mut ConstraintBuilder, + memory_offset: Cell, + memory_length: MemoryAddress, + ) -> Self { + let memory_length_is_zero = + IsZeroGadget::construct(cb, sum::expr(&memory_length.cells)); + let memory_offset_bytes = cb.query_rlc(); + + let has_length = 1.expr() - memory_length_is_zero.expr(); + cb.condition(has_length, |cb| { + cb.require_equal( + "Offset decomposition into 5 bytes", + memory_offset_bytes.expr(), + memory_offset.expr(), + ); + }); + + Self { + memory_offset, + memory_offset_bytes, + memory_length, + memory_length_is_zero, + } + } + + pub(crate) fn assign( + &self, + region: &mut Region<'_, F>, + offset: usize, + memory_offset: U256, + memory_length: U256, + randomness: F, + ) -> Result { + let memory_offset_bytes = memory_offset.to_le_bytes(); + let memory_length_bytes = memory_length.to_le_bytes(); + let memory_length_is_zero = memory_length.is_zero(); + self.memory_offset.assign( + region, + offset, + Some(Word::random_linear_combine(memory_offset_bytes, randomness)), + )?; + self.memory_offset_bytes.assign( + region, + offset, + Some(if memory_length_is_zero { + [0; 5] + } else { + memory_offset_bytes[..N_BYTES_MEMORY_ADDRESS] + .try_into() + .unwrap() + }), + )?; + self.memory_length.assign( + region, + offset, + Some( + memory_length_bytes[..N_BYTES_MEMORY_ADDRESS] + .try_into() + .unwrap(), + ), + )?; + self.memory_length_is_zero.assign( + region, + offset, + sum::value(&memory_length_bytes), + )?; + Ok(if memory_length_is_zero { + 0 + } else { + address_low::value(memory_offset_bytes) + + address_low::value(memory_length_bytes) + }) + } + + pub(crate) fn offset(&self) -> Expression { + (1.expr() - self.memory_length_is_zero.expr()) + * from_bytes::expr(&self.memory_offset_bytes.cells) + } + + pub(crate) fn length(&self) -> Expression { + from_bytes::expr(&self.memory_length.cells) + } + + pub(crate) fn address(&self) -> Expression { + self.offset() + self.length() } } @@ -52,7 +164,7 @@ pub(crate) mod address_high { /// `memory_word_size = ceil(address/32) = floor((address + 31) / 32)` #[derive(Clone, Debug)] pub(crate) struct MemoryWordSizeGadget { - memory_word_size: ConstantDivisionGadget, + memory_word_size: ConstantDivisionGadget, } impl MemoryWordSizeGadget { @@ -67,22 +179,21 @@ impl MemoryWordSizeGadget { } pub(crate) fn expr(&self) -> Expression { - let (quotient, _) = self.memory_word_size.expr(); - quotient + self.memory_word_size.quotient() } pub(crate) fn assign( &self, region: &mut Region<'_, F>, offset: usize, - address: Address, - ) -> Result { + address: u64, + ) -> Result { let (quotient, _) = self.memory_word_size.assign( region, offset, (address as u128) + 31, )?; - Ok(quotient as MemorySize) + Ok(quotient as u64) } } @@ -92,21 +203,22 @@ impl MemoryWordSizeGadget { /// `memory_cost = Gmem * memory_word_size + floor(memory_word_size * /// memory_word_size / 512)` #[derive(Clone, Debug)] -pub(crate) struct MemoryExpansionGadget -{ - addr_memory_word_size: MemoryWordSizeGadget, - next_memory_word_size: MaxGadget, - curr_quad_memory_cost: ConstantDivisionGadget, - next_quad_memory_cost: ConstantDivisionGadget, +pub(crate) struct MemoryExpansionGadget< + F, + const N: usize, + const N_BYTES_MEMORY_WORD_SIZE: usize, +> { + memory_word_sizes: [MemoryWordSizeGadget; N], + max_memory_word_sizes: [MinMaxGadget; N], + curr_quad_memory_cost: ConstantDivisionGadget, + next_quad_memory_cost: ConstantDivisionGadget, + next_memory_word_size: Expression, gas_cost: Expression, } -impl - MemoryExpansionGadget +impl + MemoryExpansionGadget { - pub const GAS_MEM: GasCost = GasCost::MEMORY; - pub const QUAD_COEFF_DIV: u64 = 512u64; - /// Input requirements: /// - `curr_memory_word_size < 256**MAX_MEMORY_SIZE_IN_BYTES` /// - `address < 32 * 256**MAX_MEMORY_SIZE_IN_BYTES` @@ -117,21 +229,26 @@ impl pub(crate) fn construct( cb: &mut ConstraintBuilder, curr_memory_word_size: Expression, - address: Expression, + addresses: [Expression; N], ) -> Self { // Calculate the memory size of the memory access - // `addr_memory_word_size < 256**MAX_MEMORY_SIZE_IN_BYTES` - let addr_memory_word_size = - MemoryWordSizeGadget::construct(cb, address); + // `address_memory_word_size < 256**MAX_MEMORY_SIZE_IN_BYTES` + let memory_word_sizes = addresses + .map(|address| MemoryWordSizeGadget::construct(cb, address)); // The memory size needs to be updated if this memory access // requires expanding the memory. // `next_memory_word_size < 256**MAX_MEMORY_SIZE_IN_BYTES` - let next_memory_word_size = MaxGadget::construct( - cb, - addr_memory_word_size.expr(), - curr_memory_word_size.clone(), - ); + let mut next_memory_word_size = curr_memory_word_size.clone(); + let max_memory_word_sizes = array_init(|idx| { + let max_memory_word_size = MinMaxGadget::construct( + cb, + next_memory_word_size.clone(), + memory_word_sizes[idx].expr(), + ); + next_memory_word_size = max_memory_word_size.max(); + max_memory_word_size + }); // Calculate the quad memory cost for the current and next memory size. // These quad costs will also be range limited to `< @@ -139,34 +256,36 @@ impl let curr_quad_memory_cost = ConstantDivisionGadget::construct( cb, curr_memory_word_size.clone() * curr_memory_word_size.clone(), - Self::QUAD_COEFF_DIV, + GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.as_u64(), ); let next_quad_memory_cost = ConstantDivisionGadget::construct( cb, - next_memory_word_size.expr() * next_memory_word_size.expr(), - Self::QUAD_COEFF_DIV, + next_memory_word_size.clone() * next_memory_word_size.clone(), + GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.as_u64(), ); // Calculate the gas cost for the memory expansion. // This gas cost is the difference between the next and current memory // costs. `gas_cost <= // GAS_MEM*256**MAX_MEMORY_SIZE_IN_BYTES + 256**MAX_QUAD_COST_IN_BYTES` - let gas_cost = (next_memory_word_size.expr() - curr_memory_word_size) - * Self::GAS_MEM.expr() - + (next_quad_memory_cost.expr().0 - curr_quad_memory_cost.expr().0); + let gas_cost = GasCost::MEMORY_EXPANSION_LINEAR_COEFF.expr() + * (next_memory_word_size.clone() - curr_memory_word_size) + + (next_quad_memory_cost.quotient() + - curr_quad_memory_cost.quotient()); Self { - addr_memory_word_size, - next_memory_word_size, + memory_word_sizes, + max_memory_word_sizes, curr_quad_memory_cost, next_quad_memory_cost, + next_memory_word_size, gas_cost, } } pub(crate) fn next_memory_word_size(&self) -> Expression { - // Return the new memory word size - self.next_memory_word_size.expr() + // Return the new memory size + self.next_memory_word_size.clone() } pub(crate) fn gas_cost(&self) -> Expression { @@ -178,23 +297,34 @@ impl &self, region: &mut Region<'_, F>, offset: usize, - curr_memory_word_size: MemorySize, - address: Address, - ) -> Result<(MemorySize, u128), Error> { - // Calculate the active memory word size - let addr_memory_word_size = - self.addr_memory_word_size.assign(region, offset, address)?; - - // Calculate the next memory word size - let next_memory_word_size = self - .next_memory_word_size - .assign( + curr_memory_word_size: u64, + addresses: [u64; N], + ) -> Result<(u64, u64), Error> { + // Calculate the active memory size + let memory_word_sizes = self + .memory_word_sizes + .iter() + .zip(addresses.iter()) + .map(|(memory_word_size, address)| { + memory_word_size.assign(region, offset, *address) + }) + .collect::, _>>()?; + + // Calculate the next memory size + let mut next_memory_word_size = curr_memory_word_size as u64; + for (max_memory_word_sizes, memory_word_size) in self + .max_memory_word_sizes + .iter() + .zip(memory_word_sizes.iter()) + { + let (_, max) = max_memory_word_sizes.assign( region, offset, - F::from(addr_memory_word_size), - F::from(curr_memory_word_size), - )? - .get_lower_128() as MemorySize; + F::from(next_memory_word_size as u64), + F::from(*memory_word_size), + )?; + next_memory_word_size = max.get_lower_128() as u64; + } // Calculate the quad gas cost for the memory size let (curr_quad_memory_cost, _) = self.curr_quad_memory_cost.assign( @@ -209,10 +339,9 @@ impl )?; // Calculate the gas cost for the expansian - let memory_cost = (next_memory_word_size - curr_memory_word_size) - as u128 - * (Self::GAS_MEM.as_u64() as u128) - + (next_quad_memory_cost - curr_quad_memory_cost); + let memory_cost = GasCost::MEMORY_EXPANSION_LINEAR_COEFF.as_u64() + * (next_memory_word_size - curr_memory_word_size as u64) + + (next_quad_memory_cost - curr_quad_memory_cost) as u64; // Return the new memory size and the memory expansion gas cost Ok((next_memory_word_size, memory_cost)) diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index 3409c9ede3..d26302d8d5 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -1,6 +1,6 @@ #![allow(missing_docs)] use crate::evm_circuit::{ - param::{NUM_BYTES_WORD, STACK_CAPACITY}, + param::{N_BYTES_WORD, STACK_CAPACITY}, step::ExecutionState, table::{ AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, RwTableTag, @@ -295,8 +295,8 @@ impl ExecStep { // EVM always pads the memory size to word size // https://github.com/ethereum/go-ethereum/blob/master/core/vm/interpreter.go#L212-L216 // Thus, the memory size must be a multiple of 32 bytes. - assert_eq!(self.memory_size % NUM_BYTES_WORD as u64, 0); - self.memory_size / NUM_BYTES_WORD as u64 + assert_eq!(self.memory_size % N_BYTES_WORD as u64, 0); + self.memory_size / N_BYTES_WORD as u64 } } @@ -472,7 +472,7 @@ impl Rw { value_prev, } => { let to_scalar = |value: &Word| match field_tag { - AccountFieldTag::Nonce => F::from(value.low_u64()), + AccountFieldTag::Nonce => value.to_scalar().unwrap(), _ => RandomLinearCombination::random_linear_combine( value.to_le_bytes(), randomness, @@ -512,7 +512,7 @@ impl Rw { CallContextFieldTag::CallerAddress | CallContextFieldTag::CalleeAddress | CallContextFieldTag::Result => value.to_scalar().unwrap(), - _ => F::from(value.low_u64()), + _ => value.to_scalar().unwrap(), }, F::zero(), F::zero(),