diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index 57fd76d1db..07c250ead4 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -559,13 +559,13 @@ impl<'a> CircuitInputStateRef<'a> { /// balance by `value`. If `fee` is existing (not None), also need to push 1 /// non-reversible [`AccountOp`] to update `sender` balance by `fee`. #[allow(clippy::too_many_arguments)] - pub fn transfer_with_fee( + pub fn transfer( &mut self, step: &mut ExecStep, sender: Address, receiver: Address, receiver_exists: bool, - must_create: bool, + is_create: bool, value: Word, fee: Option, ) -> Result<(), Error> { @@ -608,82 +608,35 @@ impl<'a> CircuitInputStateRef<'a> { sender_balance_prev, sender_balance ); - // If receiver doesn't exist, create it - if !receiver_exists && (!value.is_zero() || must_create) { + + if !value.is_zero() { self.push_op_reversible( step, AccountOp { - address: receiver, - field: AccountField::CodeHash, - value: CodeDB::empty_code_hash().to_word(), - value_prev: Word::zero(), + address: sender, + field: AccountField::Balance, + value: sender_balance, + value_prev: sender_balance_prev, }, )?; } - if value.is_zero() { - // Skip transfer if value == 0 - return Ok(()); - } - self.push_op_reversible( - step, - AccountOp { - address: sender, - field: AccountField::Balance, - value: sender_balance, - value_prev: sender_balance_prev, - }, - )?; - - let (_found, receiver_account) = self.sdb.get_account(&receiver); - let receiver_balance_prev = receiver_account.balance; - let receiver_balance = receiver_account.balance + value; - self.push_op_reversible( - step, - AccountOp { - address: receiver, - field: AccountField::Balance, - value: receiver_balance, - value_prev: receiver_balance_prev, - }, - )?; + self.transfer_to(step, receiver, receiver_exists, is_create, value, true)?; Ok(()) } - /// Same functionality with `transfer_with_fee` but with `fee` set zero. - pub fn transfer( - &mut self, - step: &mut ExecStep, - sender: Address, - receiver: Address, - receiver_exists: bool, - must_create: bool, - value: Word, - ) -> Result<(), Error> { - self.transfer_with_fee( - step, - sender, - receiver, - receiver_exists, - must_create, - value, - None, - ) - } - /// Transfer to an address. Create an account if it is not existed before. pub fn transfer_to( &mut self, step: &mut ExecStep, receiver: Address, receiver_exists: bool, - must_create: bool, + is_create: bool, value: Word, reversible: bool, ) -> Result<(), Error> { - // If receiver doesn't exist, create it - if (!receiver_exists && !value.is_zero()) || must_create { + if !receiver_exists && (!value.is_zero() || is_create) { self.account_write( step, receiver, diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs index 1b6c79a5c8..f9ee29d714 100644 --- a/bus-mapping/src/evm/opcodes/begin_end_tx.rs +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -122,7 +122,7 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result Opcode for CallOpcode { callee_exists, false, call.value, + None, )?; } diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index 51b399b099..c1a0bbf940 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -217,8 +217,9 @@ impl Opcode for Create { caller.address, callee.address, callee_exists, - !callee_exists, + true, callee.value, + None, )?; // EIP 161, increase callee's nonce diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index 80de4c1973..817b7dbff7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ and, - common_gadget::TransferWithGasFeeGadget, + common_gadget::TransferGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::{Delta, To}, @@ -14,7 +14,7 @@ use crate::{ math_gadget::{ ContractCreateGadget, IsEqualWordGadget, IsZeroWordGadget, RangeCheckGadget, }, - not, or, + not, tx::{BeginTxHelperGadget, TxDataGadget}, AccountAddress, CachedRegion, Cell, StepRws, }, @@ -41,7 +41,7 @@ pub(crate) struct BeginTxGadget { call_callee_address: AccountAddress, reversion_info: ReversionInfo, sufficient_gas_left: RangeCheckGadget, - transfer_with_gas_fee: TransferWithGasFeeGadget, + transfer_with_gas_fee: TransferGadget, code_hash: WordLoHiCell, is_empty_code_hash: IsEqualWordGadget>, WordLoHi>>, caller_nonce_hash_bytes: Word32Cell, @@ -170,17 +170,16 @@ impl ExecutionGadget for BeginTxGadget { AccountFieldTag::CodeHash, code_hash.to_word(), ); - // Transfer value from caller to callee, creating account if necessary. - let transfer_with_gas_fee = TransferWithGasFeeGadget::construct( + let transfer_with_gas_fee = TransferGadget::construct( cb, tx.caller_address.to_word(), tx.callee_address.to_word(), not::expr(callee_not_exists.expr()), - or::expr([tx.is_create.expr(), callee_not_exists.expr()]), + tx.is_create.expr(), tx.value.clone(), - tx.mul_gas_fee_by_gas.product().clone(), &mut reversion_info, + Some(tx.mul_gas_fee_by_gas.product().clone()), ); let caller_nonce_hash_bytes = cb.query_word32(); @@ -467,18 +466,15 @@ impl ExecutionGadget for BeginTxGadget { } let callee_exists = is_precompiled(&tx.to_or_contract_addr()) || !callee_code_hash.is_zero(); - let caller_balance_sub_fee_pair = rws.next().account_balance_pair(); - let must_create = tx.is_create(); - if !callee_exists && (!tx.value.is_zero() || must_create) { - callee_code_hash = rws.next().account_codehash_pair().1; - } - let mut caller_balance_sub_value_pair = (zero, zero); - let mut callee_balance_pair = (zero, zero); - if !tx.value.is_zero() { - caller_balance_sub_value_pair = rws.next().account_balance_pair(); - callee_balance_pair = rws.next().account_balance_pair(); - }; - + self.transfer_with_gas_fee.assign( + region, + offset, + &mut rws, + callee_exists, + tx.value, + tx.is_create(), + Some(gas_fee), + )?; self.begin_tx.assign(region, offset, tx)?; self.tx.assign(region, offset, tx)?; @@ -502,15 +498,6 @@ impl ExecutionGadget for BeginTxGadget { )?; self.sufficient_gas_left .assign(region, offset, F::from(tx.gas() - step.gas_cost))?; - self.transfer_with_gas_fee.assign( - region, - offset, - caller_balance_sub_fee_pair, - caller_balance_sub_value_pair, - callee_balance_pair, - tx.value, - gas_fee, - )?; self.code_hash .assign_u256(region, offset, callee_code_hash)?; self.is_empty_code_hash.assign_u256( diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 761e54d50f..571a33dc5b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -60,7 +60,7 @@ pub(crate) struct CallOpGadget { is_warm: Cell, is_warm_prev: Cell, callee_reversion_info: ReversionInfo, - transfer: TransferGadget, + transfer: TransferGadget, // current handling Call* opcode's caller balance caller_balance: WordLoHi>, // check if insufficient balance case @@ -242,9 +242,10 @@ impl ExecutionGadget for CallOpGadget { caller_address.to_word(), callee_address.to_word(), not::expr(call_gadget.callee_not_exists.expr()), - 0.expr(), + false.expr(), call_gadget.value.clone(), &mut callee_reversion_info, + None, ) }); // rwc_delta = 8 + is_delegatecall * 2 + call_gadget.rw_delta() + @@ -866,20 +867,9 @@ impl ExecutionGadget for CallOpGadget { depth.low_u64() < 1025 && (!(is_call || is_callcode) || caller_balance >= value); // conditionally assign - if is_call && is_precheck_ok && !value.is_zero() { - if !callee_exists { - rws.next().account_codehash_pair(); // callee hash - } - - let caller_balance_pair = rws.next().account_balance_pair(); - let callee_balance_pair = rws.next().account_balance_pair(); - self.transfer.assign( - region, - offset, - caller_balance_pair, - callee_balance_pair, - value, - )?; + if is_call && is_precheck_ok { + self.transfer + .assign(region, offset, &mut rws, callee_exists, value, false, None)?; } self.opcode diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index 9b560a16ce..c999a73e40 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -63,7 +63,7 @@ pub(crate) struct CreateGadget, prev_code_hash: WordLoHiCell, prev_code_hash_is_zero: IsZeroWordGadget>>, - transfer: TransferGadget, + transfer: TransferGadget, create: ContractCreateGadget, init_code: MemoryAddressGadget, @@ -333,10 +333,11 @@ impl ExecutionGadget< cb, create.caller_address(), contract_addr.to_word(), - 0.expr(), - 1.expr(), + false.expr(), + true.expr(), value.clone(), &mut callee_reversion_info, + None, ); // EIP 161, the nonce of a newly created contract is 1 @@ -638,21 +639,14 @@ impl ExecutionGadget< let code_hash = if is_precheck_ok { if !is_address_collision { - // transfer - if callee_prev_code_hash.is_zero() { - rws.next(); // codehash update - } - let [caller_balance_pair, callee_balance_pair] = if !value.is_zero() { - [(); 2].map(|_| rws.next().account_balance_pair()) - } else { - [(0.into(), 0.into()), (0.into(), 0.into())] - }; self.transfer.assign( region, offset, - caller_balance_pair, - callee_balance_pair, + &mut rws, + !callee_prev_code_hash.is_zero(), value, + true, + None, )?; } diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index 5f650f693a..27ca5f119e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -11,7 +11,7 @@ use crate::{ MulWordByU64Gadget, }, tx::EndTxHelperGadget, - CachedRegion, Cell, + CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -75,10 +75,9 @@ impl ExecutionGadget for EndTxGadget { tx_gas_price.clone(), effective_refund.min() + cb.curr.state.gas_left.expr(), ); - let gas_fee_refund = UpdateBalanceGadget::construct( - cb, + let gas_fee_refund = cb.increase_balance( tx_caller_address.to_word(), - vec![mul_gas_price_by_refund.product().clone()], + mul_gas_price_by_refund.product().clone(), None, ); @@ -111,7 +110,6 @@ impl ExecutionGadget for EndTxGadget { false.expr(), mul_effective_tip_by_gas_used.product().clone(), None, - true, ); let end_tx = EndTxHelperGadget::construct( @@ -152,9 +150,11 @@ impl ExecutionGadget for EndTxGadget { step: &ExecStep, ) -> Result<(), Error> { let gas_used = tx.gas() - step.gas_left; - let (refund, _) = block.get_rws(step, 2).tx_refund_value_pair(); - let (caller_balance, caller_balance_prev) = block.get_rws(step, 3).account_balance_pair(); - let (coinbase_code_hash_prev, _) = block.get_rws(step, 4).account_codehash_pair(); + let mut rws = StepRws::new(block, step); + rws.offset_add(2); + let (refund, _) = rws.next().tx_refund_value_pair(); + let (caller_balance, caller_balance_prev) = rws.next().account_balance_pair(); + let (coinbase_code_hash_prev, _) = rws.next().account_codehash_pair(); self.tx_id .assign(region, offset, Value::known(F::from(tx.id)))?; @@ -208,24 +208,14 @@ impl ExecutionGadget for EndTxGadget { .assign_u256(region, offset, coinbase_code_hash_prev)?; self.coinbase_code_hash_is_zero .assign_u256(region, offset, coinbase_code_hash_prev)?; - if !coinbase_reward.is_zero() { - let coinbase_balance_pair = block - .get_rws( - step, - if coinbase_code_hash_prev.is_zero() { - 6 - } else { - 5 - }, - ) - .account_balance_pair(); - self.coinbase_reward.assign( - region, - offset, - coinbase_balance_pair, - effective_tip * gas_used, - )?; - } + self.coinbase_reward.assign( + region, + offset, + &mut rws, + !coinbase_code_hash_prev.is_zero(), + coinbase_reward, + false, + )?; self.is_persistent.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 04ee168d49..ebf88f135f 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -3,7 +3,7 @@ use super::{ from_bytes, math_gadget::{IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget, LtGadget}, memory_gadget::{CommonMemoryAddressGadget, MemoryExpansionGadget}, - AccountAddress, CachedRegion, + AccountAddress, CachedRegion, StepRws, }; use crate::{ evm_circuit::{ @@ -17,7 +17,7 @@ use crate::{ Transition::{Delta, Same, To}, }, math_gadget::{AddWordsGadget, RangeCheckGadget}, - not, or, Cell, + not, Cell, }, }, table::{AccountFieldTag, CallContextFieldTag}, @@ -31,7 +31,7 @@ use bus_mapping::state_db::CodeDB; use eth_types::{ evm_types::GasCost, Field, OpsIdentity, ToAddress, ToLittleEndian, ToScalar, ToWord, U256, }; -use gadgets::util::{select, sum}; +use gadgets::util::{or, select, sum}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -309,7 +309,7 @@ impl pub(crate) fn construct( cb: &mut EVMConstraintBuilder, address: WordLoHi>, - updates: Vec>, + updates: &[Word32Cell], reversion_info: Option<&mut ReversionInfo>, ) -> Self { debug_assert!(updates.len() == N_ADDENDS - 1); @@ -368,10 +368,10 @@ impl #[derive(Clone, Debug)] pub(crate) struct TransferToGadget { - receiver: UpdateBalanceGadget, + receiver_balance: UpdateBalanceGadget, receiver_exists: Expression, - must_create: Expression, - pub(crate) value_is_zero: IsZeroWordGadget>, + is_create: Expression, + value_is_zero: IsZeroWordGadget>, } impl TransferToGadget { @@ -380,51 +380,18 @@ impl TransferToGadget { cb: &mut EVMConstraintBuilder, receiver_address: WordLoHi>, receiver_exists: Expression, - must_create: Expression, + is_create: Expression, value: Word32Cell, mut reversion_info: Option<&mut ReversionInfo>, - account_write: bool, ) -> Self { let value_is_zero = cb.is_zero_word(&value); - if account_write { - Self::create_account( - cb, - receiver_address.clone(), - receiver_exists.clone(), - must_create.clone(), - value_is_zero.expr(), - reversion_info.as_deref_mut(), - ); - } - let receiver = cb.condition(not::expr(value_is_zero.expr()), |cb| { - UpdateBalanceGadget::construct( - cb, - receiver_address, - vec![value.clone()], - reversion_info, - ) - }); - Self { - receiver, - receiver_exists, - must_create, - value_is_zero, - } - } - - pub(crate) fn create_account( - cb: &mut EVMConstraintBuilder, - receiver_address: WordLoHi>, - receiver_exists: Expression, - must_create: Expression, - value_is_zero: Expression, - reversion_info: Option<&mut ReversionInfo>, - ) { + // Create account + // See https://github.com/ethereum/go-ethereum/pull/28666 for the context of this check. cb.condition( and::expr([ not::expr(receiver_exists.expr()), - or::expr([not::expr(value_is_zero.expr()), must_create]), + or::expr([not::expr(value_is_zero.expr()), is_create.expr()]), ]), |cb| { cb.account_write( @@ -432,23 +399,46 @@ impl TransferToGadget { AccountFieldTag::CodeHash, cb.empty_code_hash(), WordLoHi::zero(), - reversion_info, + reversion_info.as_deref_mut(), ); }, ); + + let receiver_balance = cb.condition(not::expr(value_is_zero.expr()), |cb| { + cb.increase_balance(receiver_address, value.clone(), reversion_info) + }); + + Self { + receiver_balance, + receiver_exists, + is_create, + value_is_zero, + } } pub(crate) fn assign( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - (receiver_balance, prev_receiver_balance): (U256, U256), + rws: &mut StepRws, + receiver_exists: bool, value: U256, + is_create: bool, ) -> Result<(), Error> { - self.receiver.assign( + if !receiver_exists && (!value.is_zero() || is_create) { + // receiver's code_hash + rws.next().account_codehash_pair(); + } + + let (receiver_balance, receiver_balance_prev) = if !value.is_zero() { + rws.next().account_balance_pair() + } else { + (0.into(), 0.into()) + }; + self.receiver_balance.assign( region, offset, - prev_receiver_balance, + receiver_balance_prev, vec![value], receiver_balance, )?; @@ -460,70 +450,58 @@ impl TransferToGadget { pub(crate) fn rw_delta(&self) -> Expression { // +1 Write Account (receiver) CodeHash (account creation via code_hash update) and::expr([ - not::expr(self.receiver_exists.expr()), - or::expr([not::expr(self.value_is_zero.expr()), self.must_create.clone()]), - ]) + + not::expr(self.receiver_exists.expr()), + or::expr([not::expr(self.value_is_zero.expr()), self.is_create.expr()]) + ])+ // +1 Write Account (receiver) Balance not::expr(self.value_is_zero.expr()) } } -// TODO: Merge with TransferGadget -/// The TransferWithGasFeeGadget handles an irreversible gas fee subtraction to -/// the sender and a transfer of value from sender to receiver. The value -/// transfer is only performed if the value is not zero. If the transfer is -/// performed and the receiver account doesn't exist, it will be created by -/// setting it's code_hash = EMPTY_HASH. The receiver account is also created -/// unconditionally if must_create is true. This gadget is used in BeginTx. +/// The [`TransferGadget`] handles +/// - (optional) an irreversible gas fee subtraction to the sender, and +/// - a transfer of value from sender to receiver. +/// +/// The value transfer is only performed if the value is not zero. +/// It also create the receiver account when the conditions in [`TransferToGadget`] is met. +/// This gadget is used in BeginTx, Call ops, and Create. #[derive(Clone, Debug)] -pub(crate) struct TransferWithGasFeeGadget { - sender_sub_fee: UpdateBalanceGadget, +pub(crate) struct TransferGadget { + sender_sub_fee: Option>, sender_sub_value: UpdateBalanceGadget, receiver: TransferToGadget, pub(crate) value_is_zero: IsZeroWordGadget>, } -impl TransferWithGasFeeGadget { +impl TransferGadget { #[allow(clippy::too_many_arguments)] pub(crate) fn construct( cb: &mut EVMConstraintBuilder, sender_address: WordLoHi>, receiver_address: WordLoHi>, receiver_exists: Expression, - must_create: Expression, + is_create: Expression, value: Word32Cell, - gas_fee: Word32Cell, reversion_info: &mut ReversionInfo, + gas_fee: Option>, ) -> Self { - let sender_sub_fee = - UpdateBalanceGadget::construct(cb, sender_address.to_word(), vec![gas_fee], None); + let sender_sub_fee = if WITH_FEE { + Some(cb.decrease_balance(sender_address.to_word(), gas_fee.expect("fee exists"), None)) + } else { + None + }; let value_is_zero = cb.is_zero_word(&value); - // If receiver doesn't exist, create it - TransferToGadget::create_account( - cb, - receiver_address.clone(), - receiver_exists.clone(), - must_create.clone(), - value_is_zero.expr(), - Some(reversion_info), - ); // Skip transfer if value == 0 let sender_sub_value = cb.condition(not::expr(value_is_zero.expr()), |cb| { - UpdateBalanceGadget::construct( - cb, - sender_address, - vec![value.clone()], - Some(reversion_info), - ) + cb.decrease_balance(sender_address, value.clone(), Some(reversion_info)) }); let receiver = TransferToGadget::construct( cb, receiver_address, receiver_exists, - must_create, + is_create, value, Some(reversion_info), - false, ); Self { @@ -536,27 +514,19 @@ impl TransferWithGasFeeGadget { pub(crate) fn rw_delta(&self) -> Expression { // +1 Write Account (sender) Balance (Not Reversible tx fee) - 1.expr() + - // +1 Write Account (receiver) CodeHash (account creation via code_hash update) - and::expr([not::expr(self.receiver.receiver_exists.expr()), or::expr([ - not::expr(self.value_is_zero.expr()), - self.receiver.must_create.clone()] - )]) * 1.expr() + + WITH_FEE.expr() + // +1 Write Account (sender) Balance - // +1 Write Account (receiver) Balance - not::expr(self.value_is_zero.expr()) * 2.expr() + not::expr(self.value_is_zero.expr()) + + // +1 Write Account (receiver) CodeHash (account creation via code_hash update) + self.receiver.rw_delta() } pub(crate) fn reversible_w_delta(&self) -> Expression { // NOTE: Write Account (sender) Balance (Not Reversible tx fee) - // +1 Write Account (receiver) CodeHash (account creation via code_hash update) - and::expr([not::expr(self.receiver.receiver_exists.expr()), or::expr([ - not::expr(self.value_is_zero.expr()), - self.receiver.must_create.clone()] - )]) + // +1 Write Account (sender) Balance - // +1 Write Account (receiver) Balance - not::expr(self.value_is_zero.expr()) * 2.expr() + not::expr(self.value_is_zero.expr()) + + // +1 Write Account (receiver) CodeHash (account creation via code_hash update) + self.receiver.rw_delta() } #[allow(clippy::too_many_arguments)] @@ -564,137 +534,40 @@ impl TransferWithGasFeeGadget { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - (sender_balance_sub_fee, prev_sender_balance_sub_fee): (U256, U256), - (sender_balance_sub_value, prev_sender_balance_sub_value): (U256, U256), - (receiver_balance, prev_receiver_balance): (U256, U256), + rws: &mut StepRws, + receiver_exists: bool, value: U256, - gas_fee: U256, + is_create: bool, + gas_fee: Option, ) -> Result<(), Error> { - self.sender_sub_fee.assign( - region, - offset, - prev_sender_balance_sub_fee, - vec![gas_fee], - sender_balance_sub_fee, - )?; - self.sender_sub_value.assign( - region, - offset, - prev_sender_balance_sub_value, - vec![value], - sender_balance_sub_value, - )?; - self.receiver.assign( - region, - offset, - (receiver_balance, prev_receiver_balance), - value, - )?; - self.value_is_zero - .assign_value(region, offset, Value::known(WordLoHi::from(value)))?; - Ok(()) - } -} - -/// The TransferGadget handles a transfer of value from sender to receiver. The -/// transfer is only performed if the value is not zero. If the transfer is -/// performed and the receiver account doesn't exist, it will be created by -/// setting it's code_hash = EMPTY_HASH. This gadget is used in callop. -#[derive(Clone, Debug)] -pub(crate) struct TransferGadget { - sender: UpdateBalanceGadget, - receiver: TransferToGadget, - pub(crate) value_is_zero: IsZeroWordGadget>, -} - -impl TransferGadget { - pub(crate) fn construct( - cb: &mut EVMConstraintBuilder, - sender_address: WordLoHi>, - receiver_address: WordLoHi>, - receiver_exists: Expression, - must_create: Expression, - // _prev_code_hash: WordLoHi>, - value: Word32Cell, - reversion_info: &mut ReversionInfo, - ) -> Self { - let value_is_zero = cb.is_zero_word(&value); - // If receiver doesn't exist, create it - TransferToGadget::create_account( - cb, - receiver_address.clone(), - receiver_exists.clone(), - must_create.clone(), - value_is_zero.expr(), - Some(reversion_info), - ); - // Skip transfer if value == 0 - let sender = cb.condition(not::expr(value_is_zero.expr()), |cb| { - UpdateBalanceGadget::construct( - cb, - sender_address, - vec![value.clone()], - Some(reversion_info), - ) - }); - let receiver = TransferToGadget::construct( - cb, - receiver_address, - receiver_exists, - must_create, - value, - Some(reversion_info), - false, - ); - - Self { - sender, - receiver, - value_is_zero, + if WITH_FEE { + let sender_balance_sub_fee = rws.next().account_balance_pair(); + self.sender_sub_fee.as_ref().expect("Exists").assign( + region, + offset, + sender_balance_sub_fee.1, + vec![gas_fee.expect("exists")], + sender_balance_sub_fee.0, + )?; } - } - - pub(crate) fn reversible_w_delta(&self) -> Expression { - // +1 Write Account (receiver) CodeHash (account creation via code_hash update) - or::expr([ - not::expr(self.value_is_zero.expr()) * not::expr(self.receiver.receiver_exists.clone()), - self.receiver.must_create.clone()] - ) * 1.expr() + - // +1 Write Account (sender) Balance - // +1 Write Account (receiver) Balance - not::expr(self.value_is_zero.expr()) * 2.expr() - } - - pub(crate) fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - (sender_balance, sender_balance_prev): (U256, U256), - (receiver_balance, receiver_balance_prev): (U256, U256), - value: U256, - ) -> Result<(), Error> { - self.sender.assign( + let sender_balance_sub_value = if !value.is_zero() { + rws.next().account_balance_pair() + } else { + (0.into(), 0.into()) + }; + self.sender_sub_value.assign( region, offset, - sender_balance_prev, + sender_balance_sub_value.1, vec![value], - sender_balance, - )?; - self.receiver.assign( - region, - offset, - (receiver_balance, receiver_balance_prev), - value, + sender_balance_sub_value.0, )?; + self.receiver + .assign(region, offset, rws, receiver_exists, value, is_create)?; self.value_is_zero .assign_value(region, offset, Value::known(WordLoHi::from(value)))?; Ok(()) } - - pub(crate) fn rw_delta(&self) -> Expression { - // +1 Write Account (sender) Balance - not::expr(self.value_is_zero.expr()) + self.receiver.rw_delta() - } } #[derive(Clone, Debug)] diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index c7fce109ab..5e20bff23f 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1,4 +1,5 @@ use super::{ + common_gadget::UpdateBalanceGadget, math_gadget::{ ConstantDivisionGadget, IsEqualGadget, IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget, LtGadget, LtWordGadget, MinMaxGadget, @@ -639,6 +640,26 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ConstantDivisionGadget::construct(self, numerator, denominator) } + // Common Gadget + + pub(crate) fn increase_balance( + &mut self, + address: WordLoHi>, + value: Word32Cell, + reversion_info: Option<&mut ReversionInfo>, + ) -> UpdateBalanceGadget { + UpdateBalanceGadget::construct(self, address, &[value], reversion_info) + } + + pub(crate) fn decrease_balance( + &mut self, + address: WordLoHi>, + value: Word32Cell, + reversion_info: Option<&mut ReversionInfo>, + ) -> UpdateBalanceGadget { + UpdateBalanceGadget::construct(self, address, &[value], reversion_info) + } + // Fixed pub(crate) fn range_lookup(&mut self, value: Expression, range: u64) {