Skip to content

Commit

Permalink
[fix] create coinbase account in EndTx (privacy-scaling-explorations#…
Browse files Browse the repository at this point in the history
…1583)

### Description

This PR adds additional logic to the EVM circuit to handle empty
coinbase accounts, including reading and writing of their code hash and
checking if the previous coinbase code hash is zero.

### Issue Link


privacy-scaling-explorations#1554

### Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update

### Contents

- Added reading and writing of coinbase code hash in
`bus-mapping/src/evm/opcodes/begin_end_tx.rs`
- Modified the logic of EVM circuit `end_tx.rs` to handle empty coinbase
account
- Update the `TransferToGadget` struct with additional fields and
methods

### Rationale

The additional logic ensures that the EVM circuit accounts for empty
coinbase accounts and properly handles updates to their state.

### How Has This Been Tested?

---------

Co-authored-by: DreamWuGit <[email protected]>
  • Loading branch information
lightsing and DreamWuGit authored Sep 6, 2023
1 parent cee187b commit 242d38b
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 86 deletions.
24 changes: 22 additions & 2 deletions bus-mapping/src/evm/opcodes/begin_end_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,29 @@ fn gen_end_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Error>
if !found {
return Err(Error::AccountNotFound(state.block.coinbase));
}
let coinbase_account = coinbase_account.clone();
let coinbase_balance_prev = coinbase_account.balance;
let coinbase_balance =
coinbase_balance_prev + effective_tip * (state.tx.gas() - exec_step.gas_left);
let coinbase_transfer_value = effective_tip * (state.tx.gas() - exec_step.gas_left);
let coinbase_balance = coinbase_balance_prev + coinbase_transfer_value;
state.account_read(
&mut exec_step,
state.block.coinbase,
AccountField::CodeHash,
if coinbase_account.is_empty() {
Word::zero()
} else {
coinbase_account.code_hash.to_word()
},
);
if coinbase_account.is_empty() {
state.account_write(
&mut exec_step,
state.block.coinbase,
AccountField::CodeHash,
CodeDB::empty_code_hash().to_word(),
Word::zero(),
)?;
}
state.account_write(
&mut exec_step,
state.block.coinbase,
Expand Down
62 changes: 48 additions & 14 deletions zkevm-circuits/src/evm_circuit/execution/end_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@ use crate::{
param::N_BYTES_GAS,
step::ExecutionState,
util::{
common_gadget::UpdateBalanceGadget,
common_gadget::{TransferToGadget, UpdateBalanceGadget},
constraint_builder::{
ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition,
Transition::{Delta, Same},
},
math_gadget::{
AddWordsGadget, ConstantDivisionGadget, IsEqualGadget, MinMaxGadget,
MulWordByU64Gadget,
AddWordsGadget, ConstantDivisionGadget, IsEqualGadget, IsZeroWordGadget,
MinMaxGadget, MulWordByU64Gadget,
},
CachedRegion, Cell,
},
witness::{Block, Call, ExecStep, Transaction},
},
table::{BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag, TxReceiptFieldTag},
table::{
AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag,
TxReceiptFieldTag,
},
util::{
word::{Word, WordCell, WordExpr},
Expr,
Expand All @@ -41,7 +44,9 @@ pub(crate) struct EndTxGadget<F> {
sub_gas_price_by_base_fee: AddWordsGadget<F, 2, true>,
mul_effective_tip_by_gas_used: MulWordByU64Gadget<F>,
coinbase: WordCell<F>,
coinbase_reward: UpdateBalanceGadget<F, 2, true>,
coinbase_code_hash: WordCell<F>,
coinbase_code_hash_is_zero: IsZeroWordGadget<F, WordCell<F>>,
coinbase_reward: TransferToGadget<F>,
current_cumulative_gas_used: Cell<F>,
is_first_tx: IsEqualGadget<F>,
is_persistent: Cell<F>,
Expand Down Expand Up @@ -87,6 +92,13 @@ impl<F: Field> ExecutionGadget<F> for EndTxGadget<F> {

// Add gas_used * effective_tip to coinbase's balance
let coinbase = cb.query_word_unchecked();
let coinbase_code_hash = cb.query_word_unchecked();
let coinbase_code_hash_is_zero = IsZeroWordGadget::construct(cb, &coinbase_code_hash);
cb.account_read(
coinbase.to_word(),
AccountFieldTag::CodeHash,
coinbase_code_hash.to_word(),
);
let base_fee = cb.query_word32();
// lookup && range check
for (tag, value) in [
Expand All @@ -100,11 +112,14 @@ impl<F: Field> ExecutionGadget<F> for EndTxGadget<F> {
AddWordsGadget::construct(cb, [effective_tip.clone(), base_fee], tx_gas_price);
let mul_effective_tip_by_gas_used =
MulWordByU64Gadget::construct(cb, effective_tip, gas_used.clone());
let coinbase_reward = UpdateBalanceGadget::construct(
let coinbase_reward = TransferToGadget::construct(
cb,
coinbase.to_word(),
vec![mul_effective_tip_by_gas_used.product().clone()],
1.expr() - coinbase_code_hash_is_zero.expr(),
false.expr(),
mul_effective_tip_by_gas_used.product().clone(),
None,
true,
);

// constrain tx receipt fields
Expand Down Expand Up @@ -158,7 +173,9 @@ impl<F: Field> ExecutionGadget<F> for EndTxGadget<F> {
);

cb.require_step_state_transition(StepStateTransition {
rw_counter: Delta(10.expr() - is_first_tx.expr()),
rw_counter: Delta(
11.expr() - is_first_tx.expr() + coinbase_code_hash_is_zero.expr(),
),
..StepStateTransition::any()
});
},
Expand All @@ -168,7 +185,9 @@ impl<F: Field> ExecutionGadget<F> for EndTxGadget<F> {
cb.next.execution_state_selector([ExecutionState::EndBlock]),
|cb| {
cb.require_step_state_transition(StepStateTransition {
rw_counter: Delta(9.expr() - is_first_tx.expr()),
rw_counter: Delta(
10.expr() - is_first_tx.expr() + coinbase_code_hash_is_zero.expr(),
),
// We propagate call_id so that EndBlock can get the last tx_id
// in order to count processed txs.
call_id: Same,
Expand All @@ -189,6 +208,8 @@ impl<F: Field> ExecutionGadget<F> for EndTxGadget<F> {
sub_gas_price_by_base_fee,
mul_effective_tip_by_gas_used,
coinbase,
coinbase_code_hash,
coinbase_code_hash_is_zero,
coinbase_reward,
current_cumulative_gas_used,
is_first_tx,
Expand All @@ -207,8 +228,18 @@ impl<F: Field> ExecutionGadget<F> for EndTxGadget<F> {
) -> 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), (coinbase_balance, coinbase_balance_prev)] =
[3, 4].map(|index| block.get_rws(step, index).account_value_pair());
let (caller_balance, caller_balance_prev) = block.get_rws(step, 3).account_value_pair();
let (coinbase_code_hash_prev, _) = block.get_rws(step, 4).account_value_pair();
let (coinbase_balance, coinbase_balance_prev) = block
.get_rws(
step,
if coinbase_code_hash_prev.is_zero() {
6
} else {
5
},
)
.account_value_pair();

self.tx_id
.assign(region, offset, Value::known(F::from(tx.id)))?;
Expand Down Expand Up @@ -257,12 +288,15 @@ impl<F: Field> ExecutionGadget<F> for EndTxGadget<F> {
)?;
self.coinbase
.assign_h160(region, offset, block.context.coinbase)?;
self.coinbase_code_hash
.assign_u256(region, offset, coinbase_code_hash_prev)?;
self.coinbase_code_hash_is_zero
.assign_u256(region, offset, coinbase_code_hash_prev)?;
self.coinbase_reward.assign(
region,
offset,
coinbase_balance_prev,
vec![effective_tip * gas_used],
coinbase_balance,
(coinbase_balance, coinbase_balance_prev),
effective_tip * gas_used,
)?;

let current_cumulative_gas_used: u64 = if tx.id == 1 {
Expand Down
Loading

0 comments on commit 242d38b

Please sign in to comment.