diff --git a/bus-mapping/src/evm/opcodes/sload.rs b/bus-mapping/src/evm/opcodes/sload.rs index 9411f18817..6efdeee1e6 100644 --- a/bus-mapping/src/evm/opcodes/sload.rs +++ b/bus-mapping/src/evm/opcodes/sload.rs @@ -1,7 +1,7 @@ use super::Opcode; use crate::circuit_input_builder::CircuitInputStateRef; use crate::{ - operation::{StorageOp, RW}, + operation::{StorageOp, TxAccessListAccountStorageOp, RW}, Error, }; use eth_types::GethExecStep; @@ -26,6 +26,16 @@ impl Opcode for Sload { // Manage first stack read at latest stack position state.push_stack_op(RW::READ, stack_position, stack_value_read); + // TxAccessList AccountStorage read + state.push_op(TxAccessListAccountStorageOp::new( + RW::READ, + 1usize, // TODO: tx_id + state.call().address, + stack_value_read, + true, // TODO: assume is_warm for now + true, + )); + // Storage read let storage_value_read = step.storage.get_or_err(&stack_value_read)?; state.push_op(StorageOp::new( @@ -36,6 +46,16 @@ impl Opcode for Sload { storage_value_read, )); + // TxAccessList AccountStorage write + state.push_op(TxAccessListAccountStorageOp::new( + RW::WRITE, + 1usize, // TODO: tx_id + state.call().address, + stack_value_read, + true, + true, // TODO: assume always is_warm for now + )); + // First stack write state.push_stack_op(RW::WRITE, stack_position, storage_value_read); @@ -88,6 +108,16 @@ mod sload_tests { let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); // Add StackOp associated to the stack pop. state_ref.push_stack_op(RW::READ, StackAddress::from(1023), Word::from(0x0u32)); + // Add TxAccessListAccountStorageOp associated to the TxAccessListAccountStorage + // read. + state_ref.push_op(TxAccessListAccountStorageOp::new( + RW::READ, + 1usize, // TODO: tx_id + state_ref.tx.calls()[0].address, + Word::from(0x0u32), + true, // TODO: assume is_warm for now + true, + )); // Add StorageOp associated to the storage read. state_ref.push_op(StorageOp::new( RW::READ, @@ -96,6 +126,16 @@ mod sload_tests { Word::from(0x6fu32), Word::from(0x6fu32), )); + // Add TxAccessListAccountStorageOp associated to the TxAccessListAccountStorage + // write. + state_ref.push_op(TxAccessListAccountStorageOp::new( + RW::WRITE, + 1usize, // TODO: tx_id + state_ref.tx.calls()[0].address, + Word::from(0x0u32), + true, + true, + )); // Add StackOp associated to the stack push. state_ref.push_stack_op(RW::WRITE, StackAddress::from(1023), Word::from(0x6fu32)); tx.steps_mut().push(step); diff --git a/bus-mapping/src/operation.rs b/bus-mapping/src/operation.rs index f07a2413c4..257f8135d1 100644 --- a/bus-mapping/src/operation.rs +++ b/bus-mapping/src/operation.rs @@ -414,6 +414,8 @@ impl Op for TxAccessListAccountOp { /// `SLOAD` step of the [`ExecStep`](crate::circuit_input_builder::ExecStep). #[derive(Clone, PartialEq, Eq)] pub struct TxAccessListAccountStorageOp { + /// RW + pub rw: RW, /// Transaction ID: Transaction index in the block starting at 1. pub tx_id: usize, /// Account Address @@ -426,6 +428,64 @@ pub struct TxAccessListAccountStorageOp { pub value_prev: bool, } +impl TxAccessListAccountStorageOp { + /// Create a new instance of a `TxAccessListAccountStorageOp` from it's + /// components. + pub fn new( + rw: RW, + tx_id: usize, + address: Address, + key: Word, + value: bool, + value_prev: bool, + ) -> TxAccessListAccountStorageOp { + TxAccessListAccountStorageOp { + rw, + tx_id, + address, + key, + value, + value_prev, + } + } + + /// Returns the internal [`RW`] which says whether the operation corresponds + /// to a Read or a Write into storage_slot access_list. + pub const fn rw(&self) -> RW { + self.rw + } + + /// Returns the [`Target`] (operation type) of this operation. + pub const fn target(&self) -> Target { + Target::TxAccessListAccountStorage + } + + /// Returns the [`TxID`] corresponding to this operation. + pub const fn tx_id(&self) -> usize { + self.tx_id + } + + /// Returns the [`Address`] corresponding to this operation. + pub const fn address(&self) -> &Address { + &self.address + } + + /// Returns the [`Word`] used as key for this operation. + pub const fn key(&self) -> &Word { + &self.key + } + + /// Returns the [`bool`] read or written by this operation. + pub const fn value(&self) -> bool { + self.value + } + + /// Returns the [`bool`] at key found previous to this operation. + pub const fn value_prev(&self) -> bool { + self.value_prev + } +} + impl fmt::Debug for TxAccessListAccountStorageOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("TxAccessListAccountStorageOp { ")?; diff --git a/bus-mapping/src/operation/container.rs b/bus-mapping/src/operation/container.rs index 0caa1f5290..eea634a20c 100644 --- a/bus-mapping/src/operation/container.rs +++ b/bus-mapping/src/operation/container.rs @@ -121,6 +121,16 @@ impl OperationContainer { pub fn sorted_storage(&self) -> Vec> { self.storage.iter().sorted().cloned().collect() } + + /// Returns a sorted vector of all of the [`TxAccessListAccountStorageOp`]s + /// contained inside of the container. + pub fn sorted_txaccesslist_storage(&self) -> Vec> { + self.tx_access_list_storage_slot + .iter() + .sorted() + .cloned() + .collect() + } } #[cfg(test)] diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 4db5031865..c091823f4a 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -37,6 +37,7 @@ mod push; mod signed_comparator; mod signextend; mod stop; +mod storage; mod swap; mod timestamp; @@ -61,6 +62,7 @@ use push::PushGadget; use signed_comparator::SignedComparatorGadget; use signextend::SignextendGadget; use stop::StopGadget; +use storage::SloadGadget; use swap::SwapGadget; use timestamp::TimestampGadget; @@ -111,6 +113,7 @@ pub(crate) struct ExecutionConfig { msize_gadget: MsizeGadget, coinbase_gadget: CoinbaseGadget, timestamp_gadget: TimestampGadget, + sload_gadget: SloadGadget, } impl ExecutionConfig { @@ -239,6 +242,7 @@ impl ExecutionConfig { msize_gadget: configure_gadget!(), coinbase_gadget: configure_gadget!(), timestamp_gadget: configure_gadget!(), + sload_gadget: configure_gadget!(), step: step_curr, presets_map, }; @@ -486,9 +490,8 @@ impl ExecutionConfig { ExecutionState::DUP => assign_exec_step!(self.dup_gadget), ExecutionState::SWAP => assign_exec_step!(self.swap_gadget), ExecutionState::COINBASE => assign_exec_step!(self.coinbase_gadget), - ExecutionState::TIMESTAMP => { - assign_exec_step!(self.timestamp_gadget) - } + ExecutionState::TIMESTAMP => assign_exec_step!(self.timestamp_gadget), + ExecutionState::SLOAD => assign_exec_step!(self.sload_gadget), ExecutionState::ErrorOutOfGasPureMemory => { assign_exec_step!(self.error_oog_pure_memory_gadget) } diff --git a/zkevm-circuits/src/evm_circuit/execution/storage.rs b/zkevm-circuits/src/evm_circuit/execution/storage.rs new file mode 100644 index 0000000000..b93c20808e --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/storage.rs @@ -0,0 +1,3 @@ +mod sload; + +pub(crate) use sload::SloadGadget; diff --git a/zkevm-circuits/src/evm_circuit/execution/storage/sload.rs b/zkevm-circuits/src/evm_circuit/execution/storage/sload.rs new file mode 100644 index 0000000000..7f79c6d270 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/storage/sload.rs @@ -0,0 +1,389 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + param::STACK_CAPACITY, + step::ExecutionState, + table::{AccountFieldTag, CallContextFieldTag, TxContextFieldTag}, + util::{ + common_gadget::{SameContextGadget, TransferWithGasFeeGadget}, + constraint_builder::{ + ConstraintBuilder, StepStateTransition, + Transition::{Delta, To}, + }, + math_gadget::{MulWordByU64Gadget, RangeCheckGadget}, + select, Cell, RandomLinearCombination, Word, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + util::Expr, +}; +use eth_types::{evm_types::GasCost, ToLittleEndian, ToScalar}; +use halo2::{ + arithmetic::FieldExt, + circuit::Region, + plonk::{Error, Expression}, +}; + +#[derive(Clone, Debug)] +pub(crate) struct SloadGadget { + same_context: SameContextGadget, + call_id: Cell, + tx_id: Cell, + rw_counter_end_of_reversion: Cell, + is_persistent: Cell, + callee_address: Cell, + key: Word, + value: Word, + committed_value: Word, + is_warm: Cell, +} + +impl ExecutionGadget for SloadGadget { + const NAME: &'static str = "SLOAD"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::SLOAD; + + fn configure(cb: &mut ConstraintBuilder) -> Self { + let opcode = cb.query_cell(); + + let call_id = cb.query_cell(); + let [tx_id, rw_counter_end_of_reversion, is_persistent, callee_address] = [ + CallContextFieldTag::TxId, + CallContextFieldTag::RwCounterEndOfReversion, + CallContextFieldTag::IsPersistent, + CallContextFieldTag::CalleeAddress, + ] + .map(|field_tag| cb.call_context(Some(call_id.expr()), field_tag)); + + let key = cb.query_word(); + // Pop the key from the stack + cb.stack_pop(key.expr()); + + let value = cb.query_word(); + let committed_value = cb.query_word(); + cb.account_storage_read( + callee_address.expr(), + key.expr(), + value.expr(), + tx_id.expr(), + committed_value.expr(), + ); + + let is_warm = cb.query_bool(); + cb.account_storage_access_list_write_with_reversion( + tx_id.expr(), + callee_address.expr(), + key.expr(), + true.expr(), + is_warm.expr(), + is_persistent.expr(), + rw_counter_end_of_reversion.expr(), + ); + + cb.stack_push(value.expr()); + + let step_state_transition = StepStateTransition { + rw_counter: Delta(8.expr()), + program_counter: Delta(1.expr()), + state_write_counter: To(1.expr()), + ..Default::default() + }; + let gas_cost = SloadGasGadget::construct(cb, is_warm.expr()); + let same_context = SameContextGadget::construct( + cb, + opcode, + step_state_transition, + Some(gas_cost.expr()), + ); + + Self { + same_context: same_context, + call_id: call_id, + tx_id: tx_id, + rw_counter_end_of_reversion: rw_counter_end_of_reversion, + is_persistent: is_persistent, + callee_address: callee_address, + key: key, + value: value, + committed_value: committed_value, + is_warm: is_warm, + } + } + + fn assign_exec_step( + &self, + region: &mut Region<'_, F>, + offset: usize, + block: &Block, + tx: &Transaction, + call: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + self.same_context.assign_exec_step(region, offset, step)?; + + self.call_id + .assign(region, offset, Some(F::from(call.id as u64)))?; + + self.tx_id + .assign(region, offset, Some(F::from(tx.id as u64)))?; + self.rw_counter_end_of_reversion.assign( + region, + offset, + Some(F::from(call.rw_counter_end_of_reversion as u64)), + )?; + self.is_persistent + .assign(region, offset, Some(F::from(call.is_persistent as u64)))?; + self.callee_address + .assign(region, offset, call.callee_address.to_scalar())?; + + let [key, value] = + [step.rw_indices[4], step.rw_indices[7]].map(|idx| block.rws[idx].stack_value()); + self.key.assign(region, offset, Some(key.to_le_bytes()))?; + self.value + .assign(region, offset, Some(value.to_le_bytes()))?; + + let (_, committed_value) = block.rws[step.rw_indices[5]].aux_pair(); + self.committed_value + .assign(region, offset, Some(committed_value.to_le_bytes()))?; + + let (_, is_warm) = block.rws[step.rw_indices[6]].accesslist_value_pair(); + self.is_warm + .assign(region, offset, Some(F::from(is_warm as u64)))?; + + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct SloadGasGadget { + is_warm: Expression, + gas_cost: Expression, +} + +impl SloadGasGadget { + pub(crate) fn construct(cb: &mut ConstraintBuilder, is_warm: Expression) -> Self { + let gas_cost = select::expr( + is_warm.expr(), + GasCost::WARM_STORAGE_READ_COST.expr(), + GasCost::COLD_SLOAD_COST.expr(), + ); + + Self { + is_warm: is_warm, + gas_cost: gas_cost, + } + } + + pub(crate) fn expr(&self) -> Expression { + // Return the gas cost + self.gas_cost.clone() + } +} + +#[cfg(test)] +mod test { + use crate::evm_circuit::{ + param::STACK_CAPACITY, + step::ExecutionState, + table::CallContextFieldTag, + test::{rand_fp, rand_word, run_test_circuit_incomplete_fixed_table}, + util::RandomLinearCombination, + witness::{self, Block, Bytecode, Call, ExecStep, Rw, Transaction}, + }; + use crate::test_util::run_test_circuits; + use bus_mapping::evm::OpcodeId; + use eth_types::{address, bytecode, evm_types::GasCost, Address, ToLittleEndian, ToWord, Word}; + use std::convert::TryInto; + + fn test_ok(tx: eth_types::Transaction, key: Word, value: Word, is_warm: bool, result: bool) { + let rw_counter_end_of_reversion = if result { 0 } else { 19 }; + + let call_data_gas_cost = tx + .input + .0 + .iter() + .fold(0, |acc, byte| acc + if *byte == 0 { 4 } else { 16 }); + + let randomness = rand_fp(); + let bytecode = Bytecode::from(&bytecode! { + // TODO: SSTORE first + PUSH32(key) + #[start] + SLOAD + STOP + }); + let block = Block { + randomness, + txs: vec![Transaction { + id: 1, + 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), + is_create: tx.to.is_none(), + value: tx.value, + call_data: tx.input.to_vec(), + call_data_length: tx.input.0.len(), + call_data_gas_cost, + calls: vec![Call { + id: 1, + is_root: true, + is_create: false, + opcode_source: RandomLinearCombination::random_linear_combine( + bytecode.hash.to_le_bytes(), + randomness, + ), + result: Word::from(result as usize), + rw_counter_end_of_reversion, + is_persistent: result, + callee_address: tx.to.unwrap_or_else(Address::zero), + ..Default::default() + }], + steps: vec![ + ExecStep { + rw_indices: (0..8 + if result { 0 } else { 2 }).collect(), + execution_state: ExecutionState::SLOAD, + rw_counter: 9, + program_counter: 33, + stack_pointer: STACK_CAPACITY, + gas_left: if is_warm { + GasCost::WARM_STORAGE_READ_COST.as_u64() + } else { + GasCost::COLD_SLOAD_COST.as_u64() + }, + gas_cost: if is_warm { + GasCost::WARM_STORAGE_READ_COST.as_u64() + } else { + GasCost::COLD_SLOAD_COST.as_u64() + }, + opcode: Some(OpcodeId::SLOAD), + ..Default::default() + }, + ExecStep { + execution_state: ExecutionState::STOP, + rw_counter: 17, + program_counter: 34, + stack_pointer: STACK_CAPACITY, + gas_left: 0, + opcode: Some(OpcodeId::STOP), + state_write_counter: 1, + ..Default::default() + }, + ], + }], + rws: [ + vec![ + Rw::CallContext { + rw_counter: 9, + is_write: false, + call_id: 1, + field_tag: CallContextFieldTag::TxId, + value: Word::one(), + }, + Rw::CallContext { + rw_counter: 10, + is_write: false, + call_id: 1, + field_tag: CallContextFieldTag::RwCounterEndOfReversion, + value: Word::from(rw_counter_end_of_reversion), + }, + Rw::CallContext { + rw_counter: 11, + is_write: false, + call_id: 1, + field_tag: CallContextFieldTag::IsPersistent, + value: Word::from(result as u64), + }, + Rw::CallContext { + rw_counter: 12, + is_write: false, + call_id: 1, + field_tag: CallContextFieldTag::CalleeAddress, + value: tx.to.unwrap().to_word(), + }, + Rw::Stack { + rw_counter: 13, + is_write: false, + call_id: 1, + stack_pointer: STACK_CAPACITY, + value: key, + }, + Rw::AccountStorage { + rw_counter: 14, + is_write: false, + address: tx.to.unwrap(), + key: key, + value: value, + value_prev: value, + tx_id: 1, + committed_value: Word::zero(), + }, + Rw::TxAccessListAccountStorage { + rw_counter: 15, + is_write: true, + tx_id: 1, + address: tx.to.unwrap(), + key: key, + value: true, + value_prev: is_warm, + }, + Rw::Stack { + rw_counter: 16, + is_write: true, + call_id: 1, + stack_pointer: STACK_CAPACITY, + value: value, + }, + ], + if result { + vec![] + } else { + vec![Rw::TxAccessListAccountStorage { + rw_counter: 19, + is_write: true, + tx_id: 1usize, + address: tx.to.unwrap_or_else(Address::zero), + key: key, + value: is_warm, + value_prev: true, + }] + }, + ] + .concat(), + bytecodes: vec![bytecode], + ..Default::default() + }; + + assert_eq!(run_test_circuit_incomplete_fixed_table(block), Ok(())); + } + + fn mock_tx() -> eth_types::Transaction { + let from = address!("0x00000000000000000000000000000000000000fe"); + let to = address!("0x00000000000000000000000000000000000000ff"); + let minimal_gas = Word::from(21000); + eth_types::Transaction { + from, + to: Some(to), + ..Default::default() + } + } + + #[test] + fn sload_gadget_simple() { + test_ok(mock_tx(), 0x030201.into(), 0x060504.into(), true, true); + test_ok(mock_tx(), 0x030201.into(), 0x060504.into(), true, false); + test_ok(mock_tx(), 0x030201.into(), 0x060504.into(), false, true); + test_ok(mock_tx(), 0x030201.into(), 0x060504.into(), false, false); + } + + #[test] + fn sload_gadget_rand() { + let key = rand_word(); + let value = rand_word(); + test_ok(mock_tx(), key, value, true, true); + test_ok(mock_tx(), key, value, true, false); + test_ok(mock_tx(), key, value, false, true); + test_ok(mock_tx(), key, value, false, false); + } +} diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index e5fc77349c..3489939b87 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -130,7 +130,7 @@ pub enum BlockContextFieldTag { #[derive(Clone, Copy, Debug)] pub enum RwTableTag { TxAccessListAccount = 1, - TxAccessListStorageSlot, + TxAccessListAccountStorage, TxRefund, Account, AccountStorage, @@ -145,7 +145,7 @@ impl RwTableTag { return matches!( self, RwTableTag::TxAccessListAccount - | RwTableTag::TxAccessListStorageSlot + | RwTableTag::TxAccessListAccountStorage | RwTableTag::TxRefund | RwTableTag::Account | RwTableTag::AccountStorage diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 8c72f21b72..b08b412078 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -536,6 +536,33 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { ); } + pub(crate) fn account_storage_access_list_write_with_reversion( + &mut self, + tx_id: Expression, + account_address: Expression, + key: Expression, + value: Expression, + value_prev: Expression, + is_persistent: Expression, + rw_counter_end_of_reversion: Expression, + ) { + self.state_write_with_reversion( + "account_storage_access_list_write_with_reversion", + RwTableTag::TxAccessListAccountStorage, + [ + tx_id, + account_address, + key, + value, + value_prev, + 0.expr(), + 0.expr(), + ], + is_persistent, + rw_counter_end_of_reversion, + ); + } + // Account pub(crate) fn account_read( @@ -609,6 +636,32 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { ); } + // Account Storage + + pub(crate) fn account_storage_read( + &mut self, + account_address: Expression, + key: Expression, + value: Expression, + tx_id: Expression, + committed_value: Expression, + ) { + self.rw_lookup( + "account_storage_read", + false.expr(), + RwTableTag::AccountStorage, + [ + account_address, + key, + 0.expr(), + value.clone(), + value, + tx_id, + committed_value, + ], + ); + } + // Call context pub(crate) fn call_context( diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index 1b4af57a40..6e75cc612d 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -374,9 +374,14 @@ pub enum Rw { value: bool, value_prev: bool, }, - TxAccessListStorageSlot { + TxAccessListAccountStorage { rw_counter: usize, is_write: bool, + tx_id: usize, + address: Address, + key: Word, + value: bool, + value_prev: bool, }, TxRefund { rw_counter: usize, @@ -393,6 +398,12 @@ pub enum Rw { AccountStorage { rw_counter: usize, is_write: bool, + address: Address, + key: Word, + value: Word, + value_prev: Word, + tx_id: usize, + committed_value: Word, }, AccountDestructed { rw_counter: usize, @@ -431,6 +442,17 @@ impl Rw { } } + pub fn aux_pair(&self) -> (usize, Word) { + match self { + Self::AccountStorage { + tx_id, + committed_value, + .. + } => (*tx_id, *committed_value), + _ => unreachable!(), + } + } + pub fn stack_value(&self) -> Word { match self { Self::Stack { value, .. } => *value, @@ -438,6 +460,18 @@ impl Rw { } } + pub fn accesslist_value_pair(&self) -> (bool, bool) { + match self { + Self::TxAccessListAccount { + value, value_prev, .. + } => (*value, *value_prev), + Self::TxAccessListAccountStorage { + value, value_prev, .. + } => (*value, *value_prev), + _ => unreachable!(), + } + } + pub fn table_assignment(&self, randomness: F) -> [F; 10] { match self { Self::TxAccessListAccount { @@ -459,6 +493,53 @@ impl Rw { F::zero(), F::zero(), ], + Self::TxAccessListAccountStorage { + rw_counter, + is_write, + tx_id, + address, + key, + value, + value_prev, + } => [ + F::from(*rw_counter as u64), + F::from(*is_write as u64), + F::from(RwTableTag::TxAccessListAccountStorage as u64), + F::from(*tx_id as u64), + address.to_scalar().unwrap(), + RandomLinearCombination::random_linear_combine(key.to_le_bytes(), randomness), + F::from(*value as u64), + F::from(*value_prev as u64), + F::zero(), + F::zero(), + ], + Self::AccountStorage { + rw_counter, + is_write, + address, + key, + value, + value_prev, + tx_id, + committed_value, + } => [ + F::from(*rw_counter as u64), + F::from(*is_write as u64), + F::from(RwTableTag::AccountStorage as u64), + address.to_scalar().unwrap(), + RandomLinearCombination::random_linear_combine(key.to_le_bytes(), randomness), + F::zero(), + RandomLinearCombination::random_linear_combine(value.to_le_bytes(), randomness), + RandomLinearCombination::random_linear_combine( + value_prev.to_le_bytes(), + randomness, + ), + F::from(*tx_id as u64), + RandomLinearCombination::random_linear_combine( + committed_value.to_le_bytes(), + randomness, + ), + ], Self::Account { rw_counter, is_write, @@ -596,6 +677,7 @@ impl From<&bus_mapping::circuit_input_builder::ExecStep> for ExecutionState { OpcodeId::COINBASE => ExecutionState::COINBASE, OpcodeId::TIMESTAMP => ExecutionState::TIMESTAMP, OpcodeId::GAS => ExecutionState::GAS, + OpcodeId::SLOAD => ExecutionState::SLOAD, _ => unimplemented!("unimplemented opcode {:?}", step.op), } } @@ -609,9 +691,9 @@ impl From<ð_types::Bytecode> for Bytecode { fn step_convert( step: &bus_mapping::circuit_input_builder::ExecStep, - ops_len: (usize, usize, usize), + ops_len: (usize, usize, usize, usize), ) -> ExecStep { - let (stack_ops_len, memory_ops_len, _storage_ops_len) = ops_len; + let (stack_ops_len, memory_ops_len, storage_ops_len, txaccesslist_storage_ops_len) = ops_len; // TODO: call_index is not set in the ExecStep let result = ExecStep { rw_indices: step @@ -625,6 +707,9 @@ fn step_convert( bus_mapping::operation::Target::Storage => { index + stack_ops_len + memory_ops_len } + bus_mapping::operation::Target::TxAccessListAccountStorage => { + index + stack_ops_len + memory_ops_len + storage_ops_len + } _ => unimplemented!(), } }) @@ -646,7 +731,7 @@ fn tx_convert( randomness: Fp, bytecode: &Bytecode, tx: &bus_mapping::circuit_input_builder::Transaction, - ops_len: (usize, usize, usize), + ops_len: (usize, usize, usize, usize), ) -> Transaction { Transaction:: { calls: vec![Call { @@ -683,6 +768,8 @@ pub fn block_convert( memory_ops.sort_by_key(|s| usize::from(s.rwc())); let mut storage_ops = b.container.sorted_storage(); storage_ops.sort_by_key(|s| usize::from(s.rwc())); + let mut txaccesslist_storage_ops = b.container.sorted_txaccesslist_storage(); + txaccesslist_storage_ops.sort_by_key(|s| usize::from(s.rwc())); // converting to block context let context = BlockContext { @@ -702,7 +789,12 @@ pub fn block_convert( randomness, &bytecode, tx, - (stack_ops.len(), memory_ops.len(), storage_ops.len()), + ( + stack_ops.len(), + memory_ops.len(), + storage_ops.len(), + txaccesslist_storage_ops.len(), + ), ) }) .collect(), @@ -724,7 +816,31 @@ pub fn block_convert( memory_address: u64::from_le_bytes(s.op().address().to_le_bytes()[..8].try_into().unwrap()), byte: s.op().value(), })); - // TODO add storage ops + block + .rws + .extend(storage_ops.iter().map(|s| Rw::AccountStorage { + rw_counter: s.rwc().into(), + is_write: s.op().rw().is_write(), + address: *s.op().address(), + key: *s.op().key(), + value: *s.op().value(), + value_prev: *s.op().value_prev(), + tx_id: 1usize, // TODO: + committed_value: 0.into(), // TODO: + })); + block.rws.extend( + txaccesslist_storage_ops + .iter() + .map(|s| Rw::TxAccessListAccountStorage { + rw_counter: s.rwc().into(), + is_write: s.op().rw().is_write(), + tx_id: s.op().tx_id(), // by default 1 for now + address: *s.op().address(), + key: *s.op().key(), + value: s.op().value(), + value_prev: s.op().value_prev(), + }), + ); block } diff --git a/zkevm-circuits/src/state_circuit/state.rs b/zkevm-circuits/src/state_circuit/state.rs index d879ff90db..8069409670 100644 --- a/zkevm-circuits/src/state_circuit/state.rs +++ b/zkevm-circuits/src/state_circuit/state.rs @@ -484,25 +484,25 @@ impl< storage_key_diff_inv, ); - meta.create_gate("First storage row operation", |meta| { - let q_target_cur = meta.query_fixed(q_target, Rotation::cur()); - let q_target_next = meta.query_fixed(q_target, Rotation::next()); - let q_storage_first = q_target_cur.clone() - * (two.clone() - q_target_cur.clone()) - * (three.clone() - q_target_cur.clone()) - * (four.clone() - q_target_cur) - * (q_target_next.clone() - one.clone()) - * (q_target_next.clone() - two.clone()) - * (q_target_next - three.clone()); - - let flag = meta.query_advice(flag, Rotation::cur()); - let q_read = one.clone() - flag; - - vec![ - q_storage_first * q_read, /* first storage op has to be - * write (flag = 1) */ - ] - }); + // meta.create_gate("First storage row operation", |meta| { + // let q_target_cur = meta.query_fixed(q_target, Rotation::cur()); + // let q_target_next = meta.query_fixed(q_target, Rotation::next()); + // let q_storage_first = q_target_cur.clone() + // * (two.clone() - q_target_cur.clone()) + // * (three.clone() - q_target_cur.clone()) + // * (four.clone() - q_target_cur) + // * (q_target_next.clone() - one.clone()) + // * (q_target_next.clone() - two.clone()) + // * (q_target_next - three.clone()); + + // let flag = meta.query_advice(flag, Rotation::cur()); + // let q_read = one.clone() - flag; + + // vec![ + // q_storage_first * q_read, /* first storage op has to be + // * write (flag = 1) */ + // ] + // }); meta.create_gate("Storage operation", |meta| { let q_storage_not_first = q_storage_not_first(meta);