diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index a4e26ba414..58eef4ed68 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -221,6 +221,12 @@ pub enum CopyDataType { /// scenario where we wish to accumulate the value (RLC) over all rows. /// This is used for Copy Lookup from SHA3 opcode verification. RlcAcc, + /// When copy event is access-list addresses (EIP-2930), source is tx-table + /// and destination is rw-table. + AccessListAddresses, + /// When copy event is access-list storage keys (EIP-2930), source is + /// tx-table and destination is rw-table. + AccessListStorageKeys, } impl CopyDataType { /// How many bits are necessary to represent a copy data type. @@ -307,6 +313,8 @@ impl From for usize { CopyDataType::TxCalldata => 3, CopyDataType::TxLog => 4, CopyDataType::RlcAcc => 5, + CopyDataType::AccessListAddresses => 6, + CopyDataType::AccessListStorageKeys => 7, } } } @@ -320,6 +328,8 @@ impl From<&CopyDataType> for u64 { CopyDataType::TxCalldata => 3, CopyDataType::TxLog => 4, CopyDataType::RlcAcc => 5, + CopyDataType::AccessListAddresses => 6, + CopyDataType::AccessListStorageKeys => 7, } } } diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs index 85033db03d..0689b03411 100644 --- a/bus-mapping/src/evm/opcodes/begin_end_tx.rs +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -16,6 +16,7 @@ use eth_types::{ gas_utils::{tx_access_list_gas_cost, tx_data_gas_cost}, GasCost, MAX_REFUND_QUOTIENT_OF_GAS_USED, }, + geth_types::access_list_size, Bytecode, ToWord, Word, }; use ethers_core::utils::get_contract_address; @@ -42,10 +43,12 @@ impl TxExecSteps for BeginEndTx { pub fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result { let mut exec_step = state.new_begin_tx_step(); - let call = state.call()?.clone(); - let caller_address = call.caller_address; + // Add two copy-events for tx access-list addresses and storage keys if EIP-2930. + gen_tx_eip2930_ops(state, &mut exec_step)?; + let call = state.call()?.clone(); + let caller_address = call.caller_address; if state.tx.tx_type.is_l1_msg() { // for l1 message, no need to add rw op, but we must check // caller for its existent status @@ -615,3 +618,56 @@ fn gen_tx_l1_fee_ops( )?; Ok(()) } + +// Add two copy-events for tx access-list addresses and storage keys if EIP-2930. +fn gen_tx_eip2930_ops( + state: &mut CircuitInputStateRef, + exec_step: &mut ExecStep, +) -> Result<(), Error> { + if !state.tx.tx_type.is_eip2930_tx() { + return Ok(()); + } + + let tx_id = NumberOrHash::Number(state.tx_ctx.id()); + let (address_size, storage_key_size) = access_list_size(&state.tx.access_list); + + // Add copy event for access-list addresses. + let rw_counter_start = state.block_ctx.rwc; + state.push_copy( + exec_step, + CopyEvent { + src_addr: 0, + src_addr_end: address_size, + src_type: CopyDataType::AccessListAddresses, + src_id: tx_id.clone(), + dst_addr: 0, + dst_type: CopyDataType::AccessListAddresses, + dst_id: tx_id.clone(), + log_id: None, + rw_counter_start, + // TODO + copy_bytes: CopyBytes::new(vec![], None, None), + }, + ); + + // Add copy event for access-list storage keys. + let rw_counter_start = state.block_ctx.rwc; + state.push_copy( + exec_step, + CopyEvent { + src_addr: 0, + src_addr_end: storage_key_size, + src_type: CopyDataType::AccessListStorageKeys, + src_id: tx_id.clone(), + dst_addr: 0, + dst_type: CopyDataType::AccessListStorageKeys, + dst_id: tx_id, + log_id: None, + rw_counter_start, + // TODO + copy_bytes: CopyBytes::new(vec![], None, None), + }, + ); + + Ok(()) +} diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index e0ee50ead5..9a6bc09272 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -49,12 +49,17 @@ impl From for u64 { impl TxType { /// If this type is L1Msg or not pub fn is_l1_msg(&self) -> bool { - matches!(*self, TxType::L1Msg) + matches!(*self, Self::L1Msg) } - /// If this type is Eip155 or not + /// If this type is EIP-155 or not pub fn is_eip155_tx(&self) -> bool { - matches!(*self, TxType::Eip155) + matches!(*self, Self::Eip155) + } + + /// If this type is EIP-2930 or not + pub fn is_eip2930_tx(&self) -> bool { + matches!(*self, Self::Eip2930) } /// Get the type of transaction @@ -402,3 +407,19 @@ impl GethData { } } */ + +/// Returns the number of addresses and the cumulative number of storage keys in +/// the entire access list. +pub fn access_list_size(access_list: &Option) -> (u64, u64) { + access_list.as_ref().map_or_else( + || (0, 0), + |list| { + ( + list.0.len() as u64, + list.0 + .iter() + .fold(0, |acc, item| acc + item.storage_keys.len()) as u64, + ) + }, + ) +} diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index b7f9883577..ee5e004a02 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -6,7 +6,8 @@ use crate::{ util::{ and, common_gadget::{ - TransferGadgetInfo, TransferWithGasFeeGadget, TxL1FeeGadget, TxL1MsgGadget, + TransferGadgetInfo, TransferWithGasFeeGadget, TxEip2930Gadget, TxL1FeeGadget, + TxL1MsgGadget, }, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, @@ -43,6 +44,7 @@ const PRECOMPILE_COUNT: usize = 9; #[derive(Clone, Debug)] pub(crate) struct BeginTxGadget { tx_id: Cell, + tx_type: Cell, sender_nonce: Cell, tx_nonce: Cell, tx_gas: Cell, @@ -60,8 +62,6 @@ pub(crate) struct BeginTxGadget { is_call_data_empty: IsZeroGadget, tx_call_data_word_length: ConstantDivisionGadget, tx_call_data_gas_cost: Cell, - // The gas cost for access list (EIP 2930) - access_list_gas_cost: Cell, // The gas cost for rlp-encoded bytes of unsigned tx tx_data_gas_cost: Cell, reversion_info: ReversionInfo, @@ -92,6 +92,7 @@ pub(crate) struct BeginTxGadget { is_coinbase_warm: Cell, tx_l1_fee: TxL1FeeGadget, tx_l1_msg: TxL1MsgGadget, + tx_eip2930: TxEip2930Gadget, } impl ExecutionGadget for BeginTxGadget { @@ -104,10 +105,11 @@ impl ExecutionGadget for BeginTxGadget { let call_id = cb.curr.state.rw_counter.clone(); let tx_id = cb.query_cell(); - let sender_nonce = cb.query_cell(); - let [tx_nonce, tx_gas, tx_caller_address, tx_callee_address, tx_is_create, tx_call_data_length, tx_call_data_gas_cost, access_list_gas_cost, tx_data_gas_cost] = + + let [tx_type, tx_nonce, tx_gas, tx_caller_address, tx_callee_address, tx_is_create, tx_call_data_length, tx_call_data_gas_cost, tx_data_gas_cost] = [ + TxContextFieldTag::TxType, TxContextFieldTag::Nonce, TxContextFieldTag::Gas, TxContextFieldTag::CallerAddress, @@ -115,14 +117,14 @@ impl ExecutionGadget for BeginTxGadget { TxContextFieldTag::IsCreate, TxContextFieldTag::CallDataLength, TxContextFieldTag::CallDataGasCost, - TxContextFieldTag::AccessListGasCost, TxContextFieldTag::TxDataGasCost, ] .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); + let tx_eip2930 = TxEip2930Gadget::construct(cb, tx_id.expr(), tx_type.expr()); let is_call_data_empty = IsZeroGadget::construct(cb, tx_call_data_length.expr()); - let tx_l1_msg = TxL1MsgGadget::construct(cb, tx_id.expr(), tx_caller_address.expr()); + let tx_l1_msg = TxL1MsgGadget::construct(cb, tx_type.expr(), tx_caller_address.expr()); let tx_l1_fee = cb.condition(not::expr(tx_l1_msg.is_l1_msg()), |cb| { cb.require_equal( "tx.nonce == sender.nonce", @@ -258,7 +260,7 @@ impl ExecutionGadget for BeginTxGadget { eth_types::evm_types::GasCost::CREATION_TX.expr(), eth_types::evm_types::GasCost::TX.expr(), ) + tx_call_data_gas_cost.expr() - + access_list_gas_cost.expr() + + tx_eip2930.gas_cost() + init_code_gas_cost, ) }); @@ -676,6 +678,7 @@ impl ExecutionGadget for BeginTxGadget { Self { tx_id, + tx_type, tx_nonce, sender_nonce, tx_gas, @@ -693,7 +696,6 @@ impl ExecutionGadget for BeginTxGadget { is_call_data_empty, tx_call_data_word_length, tx_call_data_gas_cost, - access_list_gas_cost, tx_data_gas_cost, reversion_info, sufficient_gas_left, @@ -717,6 +719,7 @@ impl ExecutionGadget for BeginTxGadget { is_coinbase_warm, tx_l1_fee, tx_l1_msg, + tx_eip2930, } } @@ -737,7 +740,8 @@ impl ExecutionGadget for BeginTxGadget { let mut rws = StepRws::new(block, step); - let caller_code_hash = if tx.tx_type.is_l1_msg() { + let tx_type = tx.tx_type; + let caller_code_hash = if tx_type.is_l1_msg() { let caller_code_hash_pair = rws.next().account_codehash_pair(); assert_eq!( caller_code_hash_pair.0, caller_code_hash_pair.1, @@ -748,7 +752,7 @@ impl ExecutionGadget for BeginTxGadget { U256::zero() }; self.tx_l1_msg - .assign(region, offset, tx.tx_type, caller_code_hash)?; + .assign(region, offset, tx_type, caller_code_hash)?; ////////////// RWS //////////////// // if L1: @@ -768,7 +772,7 @@ impl ExecutionGadget for BeginTxGadget { // caller addr // callee addr // coinbase - rws.offset_add(if tx.tx_type.is_l1_msg() { + rws.offset_add(if tx_type.is_l1_msg() { if caller_code_hash.is_zero() { assert_eq!( tx.nonce, 0, @@ -851,6 +855,8 @@ impl ExecutionGadget for BeginTxGadget { self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; + self.tx_type + .assign(region, offset, Value::known(F::from(tx_type as u64)))?; self.tx_nonce .assign(region, offset, Value::known(F::from(tx.nonce)))?; self.sender_nonce @@ -921,11 +927,6 @@ impl ExecutionGadget for BeginTxGadget { offset, Value::known(F::from(tx.call_data_gas_cost)), )?; - self.access_list_gas_cost.assign( - region, - offset, - Value::known(F::from(tx.access_list_gas_cost)), - )?; self.tx_data_gas_cost .assign(region, offset, Value::known(F::from(tx.tx_data_gas_cost)))?; self.reversion_info.assign( @@ -1054,7 +1055,9 @@ impl ExecutionGadget for BeginTxGadget { tx.l1_fee, tx.l1_fee_committed, tx.tx_data_gas_cost, - ) + )?; + + self.tx_eip2930.assign(region, offset, tx) } } diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index aad855e111..9214f7c501 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -31,9 +31,11 @@ use halo2_proofs::{ plonk::{Error, Expression}, }; +mod tx_eip2930; mod tx_l1_fee; mod tx_l1_msg; +pub(crate) use tx_eip2930::TxEip2930Gadget; pub(crate) use tx_l1_fee::TxL1FeeGadget; pub(crate) use tx_l1_msg::TxL1MsgGadget; diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs new file mode 100644 index 0000000000..0230a0e472 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs @@ -0,0 +1,129 @@ +use super::{CachedRegion, Cell}; +use crate::{ + evm_circuit::util::{ + constraint_builder::EVMConstraintBuilder, math_gadget::IsEqualGadget, select, + }, + table::TxFieldTag, + util::Expr, + witness::Transaction, +}; +use bus_mapping::circuit_input_builder::CopyDataType; +use eth_types::{ + evm_types::GasCost, + geth_types::{access_list_size, TxType}, + Field, +}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; + +/// Transaction EIP-2930 gadget to handle optional access-list +#[derive(Clone, Debug)] +pub(crate) struct TxEip2930Gadget { + is_eip2930_tx: IsEqualGadget, + access_list_address_len: Cell, + access_list_storage_key_len: Cell, +} + +impl TxEip2930Gadget { + pub(crate) fn construct( + cb: &mut EVMConstraintBuilder, + tx_id: Expression, + tx_type: Expression, + ) -> Self { + let is_eip2930_tx = IsEqualGadget::construct(cb, tx_type, (TxType::Eip2930 as u64).expr()); + + let [access_list_address_len, access_list_storage_key_len] = + cb.condition(is_eip2930_tx.expr(), |cb| { + let [address_len, storage_key_len] = [ + TxFieldTag::AccessListAddressesLen, + TxFieldTag::AccessListStorageKeysLen, + ] + .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); + + // Let copy-circuit to write the tx-table's access list addresses into rw-table. + cb.copy_table_lookup( + tx_id.expr(), + CopyDataType::AccessListAddresses.expr(), + tx_id.expr(), + CopyDataType::AccessListAddresses.expr(), + 0.expr(), + address_len.expr(), + 0.expr(), + address_len.expr(), + 0.expr(), + // TODO + 0.expr(), + ); + + // Let copy-circuit to write the tx-table's access list storage keys into rw-table. + cb.copy_table_lookup( + tx_id.expr(), + CopyDataType::AccessListStorageKeys.expr(), + tx_id.expr(), + CopyDataType::AccessListStorageKeys.expr(), + 0.expr(), + storage_key_len.expr(), + 0.expr(), + storage_key_len.expr(), + 0.expr(), + // TODO + 0.expr(), + ); + + [address_len, storage_key_len] + }); + + Self { + is_eip2930_tx, + access_list_address_len, + access_list_storage_key_len, + } + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + tx: &Transaction, + ) -> Result<(), Error> { + self.is_eip2930_tx.assign( + region, + offset, + F::from(tx.tx_type as u64), + F::from(TxType::Eip2930 as u64), + )?; + + let (access_list_address_len, access_list_storage_key_len) = + access_list_size(&tx.access_list); + + self.access_list_address_len.assign( + region, + offset, + Value::known(F::from(access_list_address_len)), + )?; + self.access_list_storage_key_len.assign( + region, + offset, + Value::known(F::from(access_list_storage_key_len)), + )?; + + Ok(()) + } + + pub(crate) fn gas_cost(&self) -> Expression { + select::expr( + self.is_eip2930_tx.expr(), + self.access_list_address_len.expr() * GasCost::ACCESS_LIST_PER_ADDRESS.expr() + + self.access_list_storage_key_len.expr() + * GasCost::ACCESS_LIST_PER_STORAGE_KEY.expr(), + 0.expr(), + ) + } + + pub(crate) fn rw_delta(&self) -> Expression { + // TODO + 0.expr() + } +} diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs index d9d19caf7f..f85387023c 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs @@ -6,22 +6,17 @@ use crate::{ math_gadget::{IsEqualGadget, IsZeroGadget}, select, }, - table::{AccountFieldTag, TxFieldTag::TxType as TxTypeField}, + table::AccountFieldTag, util::Expr, }; use eth_types::{geth_types::TxType, Field, U256}; -use halo2_proofs::{ - circuit::Value, - plonk::{Error, Expression}, -}; +use halo2_proofs::plonk::{Error, Expression}; /// L1 Msg Transaction gadget for some extra handling #[derive(Clone, Debug)] pub(crate) struct TxL1MsgGadget { /// tx is l1 msg tx tx_is_l1msg: IsEqualGadget, - /// tx type - tx_type: Cell, /// caller is empty is_caller_empty: IsZeroGadget, caller_codehash: Cell, @@ -30,10 +25,9 @@ pub(crate) struct TxL1MsgGadget { impl TxL1MsgGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - tx_id: Expression, + tx_type: Expression, caller_address: Expression, ) -> Self { - let tx_type = cb.tx_context(tx_id.expr(), TxTypeField, None); let tx_is_l1msg = IsEqualGadget::construct(cb, tx_type.expr(), (TxType::L1Msg as u64).expr()); let caller_codehash = cb.query_cell_phase2(); @@ -71,7 +65,6 @@ impl TxL1MsgGadget { ); Self { - tx_type, tx_is_l1msg, caller_codehash, is_caller_empty, @@ -85,8 +78,6 @@ impl TxL1MsgGadget { tx_type: TxType, code_hash: U256, ) -> Result<(), Error> { - self.tx_type - .assign(region, offset, Value::known(F::from(tx_type as u64)))?; self.tx_is_l1msg.assign( region, offset, diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index acce7d0711..1750fccd12 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -146,8 +146,6 @@ pub enum TxFieldTag { CallDataLength, /// Gas cost for transaction call data (4 for byte == 0, 16 otherwise) CallDataGasCost, - /// Gas cost for access list (EIP 2930) - AccessListGasCost, /// Gas cost of the transaction data charged in L1 TxDataGasCost, /// Chain ID @@ -177,6 +175,12 @@ pub enum TxFieldTag { TxHash, /// TxType: Type of the transaction TxType, + /// Access list address count (EIP-2930) + AccessListAddressesLen, + /// Access list all storage key count (EIP-2930) + AccessListStorageKeysLen, + /// RLC of access list (EIP-2930) + AccessListRLC, /// The block number in which this tx is included. BlockNumber, } diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 8d6f1d4875..e007dc3ae6 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -19,9 +19,10 @@ use crate::{ BlockContextFieldTag::{CumNumTxs, NumAllTxs, NumTxs}, BlockTable, KeccakTable, LookupTable, RlpFsmRlpTable as RlpTable, SigTable, TxFieldTag, TxFieldTag::{ - AccessListGasCost, BlockNumber, CallData, CallDataGasCost, CallDataLength, CallDataRLC, - CalleeAddress, CallerAddress, ChainID, Gas, GasPrice, IsCreate, Nonce, SigR, SigS, - SigV, TxDataGasCost, TxHashLength, TxHashRLC, TxSignHash, TxSignLength, TxSignRLC, + AccessListAddressesLen, AccessListRLC, AccessListStorageKeysLen, BlockNumber, CallData, + CallDataGasCost, CallDataLength, CallDataRLC, CalleeAddress, CallerAddress, ChainID, + Gas, GasPrice, IsCreate, Nonce, SigR, SigS, SigV, TxDataGasCost, TxHashLength, + TxHashRLC, TxSignHash, TxSignLength, TxSignRLC, }, TxTable, U16Table, U8Table, }, @@ -42,13 +43,13 @@ use crate::{ use bus_mapping::circuit_input_builder::keccak_inputs_sign_verify; use eth_types::{ geth_types::{ - TxType, + access_list_size, TxType, TxType::{Eip155, L1Msg, PreEip155}, }, sign_types::SignData, Address, Field, ToAddress, ToBigEndian, ToScalar, }; -use ethers_core::utils::keccak256; +use ethers_core::utils::{keccak256, rlp::Encodable}; use gadgets::{ binary_number::{BinaryNumberChip, BinaryNumberConfig}, comparator::{ComparatorChip, ComparatorConfig, ComparatorInstruction}, @@ -79,11 +80,11 @@ use halo2_proofs::plonk::SecondPhase; use itertools::Itertools; /// Number of rows of one tx occupies in the fixed part of tx table -pub const TX_LEN: usize = 24; +pub const TX_LEN: usize = 26; /// Offset of TxHash tag in the tx table -pub const TX_HASH_OFFSET: usize = 22; +pub const TX_HASH_OFFSET: usize = 21; /// Offset of ChainID tag in the tx table -pub const CHAIN_ID_OFFSET: usize = 13; +pub const CHAIN_ID_OFFSET: usize = 12; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum LookupCondition { @@ -331,7 +332,6 @@ impl SubCircuitConfig for TxCircuitConfig { is_tx_tag!(is_data, CallData); is_tx_tag!(is_data_length, CallDataLength); is_tx_tag!(is_data_gas_cost, CallDataGasCost); - is_tx_tag!(is_access_list_gas_cost, AccessListGasCost); is_tx_tag!(is_tx_gas_cost, TxDataGasCost); is_tx_tag!(is_data_rlc, CallDataRLC); is_tx_tag!(is_chain_id_expr, ChainID); @@ -346,6 +346,9 @@ impl SubCircuitConfig for TxCircuitConfig { is_tx_tag!(is_hash, TxHash); is_tx_tag!(is_block_num, BlockNumber); is_tx_tag!(is_tx_type, TxType); + is_tx_tag!(is_access_list_addresses_len, AccessListAddressesLen); + is_tx_tag!(is_access_list_storage_keys_len, AccessListStorageKeysLen); + is_tx_tag!(is_access_list_rlc, AccessListRLC); let tx_id_unchanged = IsEqualChip::configure( meta, @@ -467,13 +470,15 @@ impl SubCircuitConfig for TxCircuitConfig { (is_create(meta), Null), (is_data_length(meta), Null), (is_data_gas_cost(meta), Null), - (is_access_list_gas_cost(meta), Null), (is_sign_hash(meta), Null), (is_hash(meta), Null), (is_data(meta), Null), (is_block_num(meta), Null), (is_chain_id_expr(meta), Tag::ChainId.into()), (is_tx_type(meta), Null), + (is_access_list_addresses_len(meta), Null), + (is_access_list_storage_keys_len(meta), Null), + (is_access_list_rlc(meta), RLC), ]; cb.require_boolean( @@ -559,7 +564,8 @@ impl SubCircuitConfig for TxCircuitConfig { cb.gate(meta.query_fixed(q_enable, Rotation::cur())) }); - // TODO: add constraints for AccessListGasCost. + // TODO: add constraints for AccessListAddressesLen, AccessListStorageKeysLen + // and AccessListRLC. ////////////////////////////////////////////////////////// ///// Constraints for booleans that reducing degree ///// @@ -1744,6 +1750,8 @@ impl TxCircuitConfig { } else { get_rlp_len_tag_length(&tx.rlp_unsigned) }; + let (access_list_address_size, access_list_storage_key_size) = + access_list_size(&tx.access_list); // fixed_rows of a tx let fixed_rows = vec![ @@ -1839,11 +1847,6 @@ impl TxCircuitConfig { None, Value::known(F::from(tx.call_data_gas_cost)), ), - ( - AccessListGasCost, - None, - Value::known(F::from(tx.access_list_gas_cost)), - ), ( TxDataGasCost, Some(RlpTableInputValue { @@ -1941,6 +1944,33 @@ impl TxCircuitConfig { None, Value::known(F::from(tx.tx_type as u64)), ), + ( + AccessListAddressesLen, + None, + Value::known(F::from(access_list_address_size)), + ), + ( + AccessListStorageKeysLen, + None, + Value::known(F::from(access_list_storage_key_size)), + ), + ( + AccessListRLC, + Some(RlpTableInputValue { + tag: RLC, + is_none: false, + be_bytes_len: 0, + be_bytes_rlc: zero_rlc, + }), + // TODO: need to check if it's correct with RLP. + rlc_be_bytes( + &tx.access_list + .as_ref() + .map(|access_list| access_list.rlp_bytes()) + .unwrap_or_default(), + keccak_input, + ), + ), (BlockNumber, None, Value::known(F::from(tx.block_number))), ]; diff --git a/zkevm-circuits/src/witness/tx.rs b/zkevm-circuits/src/witness/tx.rs index 74506c31f6..fc226a0d5a 100644 --- a/zkevm-circuits/src/witness/tx.rs +++ b/zkevm-circuits/src/witness/tx.rs @@ -17,14 +17,18 @@ use crate::{ }; use bus_mapping::circuit_input_builder::{self, get_dummy_tx_hash, TxL1Fee}; use eth_types::{ - evm_types::gas_utils::{tx_access_list_gas_cost, tx_data_gas_cost}, - geth_types::{TxType, TxType::PreEip155}, + evm_types::gas_utils::tx_data_gas_cost, + geth_types::{access_list_size, TxType, TxType::PreEip155}, sign_types::{ biguint_to_32bytes_le, ct_option_ok_or, get_dummy_tx, recover_pk2, SignData, SECP256K1_Q, }, - Address, Error, Field, Signature, ToBigEndian, ToLittleEndian, ToScalar, ToWord, Word, H256, + AccessList, Address, Error, Field, Signature, ToBigEndian, ToLittleEndian, ToScalar, ToWord, + Word, H256, +}; +use ethers_core::{ + types::TransactionRequest, + utils::{keccak256, rlp::Encodable}, }; -use ethers_core::{types::TransactionRequest, utils::keccak256}; use halo2_proofs::{ circuit::Value, halo2curves::{group::ff::PrimeField, secp256k1}, @@ -67,8 +71,6 @@ pub struct Transaction { pub call_data_length: usize, /// The gas cost for transaction call data pub call_data_gas_cost: u64, - /// The gas cost for access list (EIP 2930) - pub access_list_gas_cost: u64, /// The gas cost for rlp-encoded bytes of unsigned tx pub tx_data_gas_cost: u64, /// Chain ID as per EIP-155. @@ -87,6 +89,8 @@ pub struct Transaction { pub l1_fee: TxL1Fee, /// Committed values of L1 fee pub l1_fee_committed: TxL1Fee, + /// Optional access list for EIP-2930 + pub access_list: Option, /// The calls made in the transaction pub calls: Vec, /// The steps executioned in the transaction @@ -157,6 +161,8 @@ impl Transaction { ) -> Vec<[Value; 4]> { let tx_hash_be_bytes = keccak256(&self.rlp_signed); let tx_sign_hash_be_bytes = keccak256(&self.rlp_unsigned); + let (access_list_address_size, access_list_storage_key_size) = + access_list_size(&self.access_list); let ret = vec![ [ @@ -228,12 +234,6 @@ impl Transaction { Value::known(F::zero()), Value::known(F::from(self.call_data_gas_cost)), ], - [ - Value::known(F::from(self.id as u64)), - Value::known(F::from(TxContextFieldTag::AccessListGasCost as u64)), - Value::known(F::zero()), - Value::known(F::from(self.access_list_gas_cost)), - ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::TxDataGasCost as u64)), @@ -306,6 +306,32 @@ impl Transaction { Value::known(F::zero()), Value::known(F::from(self.tx_type as u64)), ], + [ + Value::known(F::from(self.id as u64)), + Value::known(F::from(TxContextFieldTag::AccessListAddressesLen as u64)), + Value::known(F::zero()), + Value::known(F::from(access_list_address_size)), + ], + [ + Value::known(F::from(self.id as u64)), + Value::known(F::from(TxContextFieldTag::AccessListStorageKeysLen as u64)), + Value::known(F::zero()), + Value::known(F::from(access_list_storage_key_size)), + ], + [ + Value::known(F::from(self.id as u64)), + Value::known(F::from(TxContextFieldTag::AccessListRLC as u64)), + Value::known(F::zero()), + // TODO: need to check if it's correct with RLP. + rlc_be_bytes( + &self + .access_list + .as_ref() + .map(|access_list| access_list.rlp_bytes()) + .unwrap_or_default(), + challenges.keccak_input(), + ), + ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::BlockNumber as u64)), @@ -851,6 +877,7 @@ impl From for Transaction { (unsigned, signed) }; + let access_list = Some(mock_tx.access_list); Self { block_number: 1, id: mock_tx.transaction_index.as_usize(), @@ -866,7 +893,6 @@ impl From for Transaction { call_data: mock_tx.input.to_vec(), call_data_length: mock_tx.input.len(), call_data_gas_cost: tx_data_gas_cost(&mock_tx.input), - access_list_gas_cost: tx_access_list_gas_cost(&Some(mock_tx.access_list)), tx_data_gas_cost: tx_data_gas_cost(&rlp_signed), chain_id: mock_tx.chain_id, rlp_unsigned, @@ -876,6 +902,7 @@ impl From for Transaction { s: sig.s, l1_fee: Default::default(), l1_fee_committed: Default::default(), + access_list, calls: vec![], steps: vec![], } @@ -918,7 +945,6 @@ pub(super) fn tx_convert( call_data: tx.input.clone(), call_data_length: tx.input.len(), call_data_gas_cost: tx_data_gas_cost(&tx.input), - access_list_gas_cost: tx_access_list_gas_cost(&tx.access_list), tx_data_gas_cost: tx_gas_cost, chain_id, rlp_unsigned: tx.rlp_unsigned_bytes.clone(), @@ -928,6 +954,7 @@ pub(super) fn tx_convert( s: tx.signature.s, l1_fee: tx.l1_fee, l1_fee_committed: tx.l1_fee_committed, + access_list: tx.access_list.clone(), calls: tx .calls() .iter()