Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Commit b373a61

Browse files
silathdiirVelaciela
authored andcommitted
feat: add sender balance check to begin-tx (EIP-1559) (#1066)
* Add balance check for gas fee (EIP-1559). * Fix CI with scroll feature. * Add more comments. * Add test case. * Delete duplicate fixes in tx circuit. * Replace `or::expr` with `sum::expr` to simplify for boolean expressions. * Update and add test cases. * Fix lint. * Disable tracing error cases for scroll feature.
1 parent 38c346e commit b373a61

File tree

10 files changed

+324
-31
lines changed

10 files changed

+324
-31
lines changed

bus-mapping/src/circuit_input_builder/transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ impl From<&Transaction> for geth_types::Transaction {
253253
nonce: Word::from(tx.nonce),
254254
gas_limit: Word::from(tx.gas),
255255
value: tx.value,
256-
gas_price: tx.gas_price,
256+
gas_price: Some(tx.gas_price),
257257
call_data: tx.input.clone().into(),
258258
v: tx.signature.v,
259259
r: tx.signature.r,

eth-types/src/geth_types.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ pub struct Transaction {
264264
/// Transfered value
265265
pub value: Word,
266266
/// Gas Price
267-
pub gas_price: Word,
267+
pub gas_price: Option<Word>,
268268
/// Gas fee cap
269269
pub gas_fee_cap: Word,
270270
/// Gas tip cap
@@ -300,9 +300,9 @@ impl From<&Transaction> for crate::Transaction {
300300
nonce: tx.nonce,
301301
gas: tx.gas_limit,
302302
value: tx.value,
303-
gas_price: Some(tx.gas_price),
304-
max_priority_fee_per_gas: Some(tx.gas_fee_cap),
305-
max_fee_per_gas: Some(tx.gas_tip_cap),
303+
gas_price: tx.gas_price,
304+
max_priority_fee_per_gas: Some(tx.gas_tip_cap),
305+
max_fee_per_gas: Some(tx.gas_fee_cap),
306306
input: tx.call_data.clone(),
307307
access_list: tx.access_list.clone(),
308308
v: tx.v.into(),
@@ -323,9 +323,9 @@ impl From<&crate::Transaction> for Transaction {
323323
nonce: tx.nonce,
324324
gas_limit: tx.gas,
325325
value: tx.value,
326-
gas_price: tx.gas_price.unwrap_or_default(),
327-
gas_fee_cap: tx.max_priority_fee_per_gas.unwrap_or_default(),
328-
gas_tip_cap: tx.max_fee_per_gas.unwrap_or_default(),
326+
gas_price: tx.gas_price,
327+
gas_tip_cap: tx.max_priority_fee_per_gas.unwrap_or_default(),
328+
gas_fee_cap: tx.max_fee_per_gas.unwrap_or_default(),
329329
call_data: tx.input.clone(),
330330
access_list: tx.access_list.clone(),
331331
v: tx.v.as_u64(),
@@ -344,7 +344,7 @@ impl From<&Transaction> for TransactionRequest {
344344
from: Some(tx.from),
345345
to: tx.to.map(NameOrAddress::Address),
346346
gas: Some(tx.gas_limit),
347-
gas_price: Some(tx.gas_price),
347+
gas_price: tx.gas_price,
348348
value: Some(tx.value),
349349
data: Some(tx.call_data.clone()),
350350
nonce: Some(tx.nonce),

mock/src/transaction.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Mock Transaction definition and builder related methods.
22
3-
use super::{MOCK_ACCOUNTS, MOCK_CHAIN_ID, MOCK_GASPRICE};
3+
use super::{MOCK_ACCOUNTS, MOCK_CHAIN_ID};
44
use eth_types::{
55
geth_types::Transaction as GethTransaction, word, AccessList, Address, Bytes, Hash,
66
Transaction, Word, U64,
@@ -149,7 +149,7 @@ pub struct MockTransaction {
149149
pub from: AddrOrWallet,
150150
pub to: Option<AddrOrWallet>,
151151
pub value: Word,
152-
pub gas_price: Word,
152+
pub gas_price: Option<Word>,
153153
pub gas: Word,
154154
pub input: Bytes,
155155
pub v: Option<U64>,
@@ -174,7 +174,7 @@ impl Default for MockTransaction {
174174
from: AddrOrWallet::random(&mut OsRng),
175175
to: None,
176176
value: Word::zero(),
177-
gas_price: *MOCK_GASPRICE,
177+
gas_price: None,
178178
gas: Word::from(1_000_000u64),
179179
input: Bytes::default(),
180180
v: None,
@@ -200,7 +200,7 @@ impl From<MockTransaction> for Transaction {
200200
from: mock.from.address(),
201201
to: mock.to.map(|addr| addr.address()),
202202
value: mock.value,
203-
gas_price: Some(mock.gas_price),
203+
gas_price: mock.gas_price,
204204
gas: mock.gas,
205205
input: mock.input,
206206
v: mock.v.unwrap_or_default(),
@@ -274,7 +274,7 @@ impl MockTransaction {
274274

275275
/// Set gas_price field for the MockTransaction.
276276
pub fn gas_price(&mut self, gas_price: Word) -> &mut Self {
277-
self.gas_price = gas_price;
277+
self.gas_price = Some(gas_price);
278278
self
279279
}
280280

@@ -337,9 +337,13 @@ impl MockTransaction {
337337
.value(self.value)
338338
.data(self.input.clone())
339339
.gas(self.gas)
340-
.gas_price(self.gas_price)
341340
.chain_id(self.chain_id);
342341

342+
let tx = if let Some(gas_price) = self.gas_price {
343+
tx.gas_price(gas_price)
344+
} else {
345+
tx
346+
};
343347
let tx = if let Some(to_addr) = self.to.clone() {
344348
tx.to(to_addr.address())
345349
} else {

testool/src/statetest/executor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ fn into_traceconfig(st: StateTest) -> (String, TraceConfig, StateTestResult) {
189189
nonce: st.nonce,
190190
value: st.value,
191191
gas_limit: U256::from(st.gas_limit),
192-
gas_price: st.gas_price,
192+
gas_price: Some(st.gas_price),
193193
gas_fee_cap: U256::zero(),
194194
gas_tip_cap: U256::zero(),
195195
call_data: st.data,
@@ -369,7 +369,7 @@ fn trace_config_to_witness_block_l1(
369369
to: tx.to,
370370
value: tx.value,
371371
input: tx.call_data,
372-
gas_price: Some(tx.gas_price),
372+
gas_price: tx.gas_price,
373373
access_list: tx.access_list,
374374
nonce: tx.nonce,
375375
gas: tx.gas_limit,

zkevm-circuits/src/evm_circuit/execution/begin_tx.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::{
66
util::{
77
and,
88
common_gadget::{
9-
TransferGadgetInfo, TransferWithGasFeeGadget, TxEip2930Gadget, TxL1FeeGadget,
10-
TxL1MsgGadget,
9+
TransferGadgetInfo, TransferWithGasFeeGadget, TxEip1559Gadget, TxEip2930Gadget,
10+
TxL1FeeGadget, TxL1MsgGadget,
1111
},
1212
constraint_builder::{
1313
ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition,
@@ -99,6 +99,7 @@ pub(crate) struct BeginTxGadget<F> {
9999
is_coinbase_warm: Cell<F>,
100100
tx_l1_fee: TxL1FeeGadget<F>,
101101
tx_l1_msg: TxL1MsgGadget<F>,
102+
tx_eip1559: TxEip1559Gadget<F>,
102103
tx_eip2930: TxEip2930Gadget<F>,
103104
}
104105

@@ -358,6 +359,17 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
358359
&mut reversion_info,
359360
);
360361

362+
// Construct EIP-1559 gadget to check sender balance.
363+
let tx_eip1559 = TxEip1559Gadget::construct(
364+
cb,
365+
tx_id.expr(),
366+
tx_type.expr(),
367+
tx_gas.expr(),
368+
tx_l1_fee.tx_l1_fee_word(),
369+
&tx_value,
370+
transfer_with_gas_fee.sender_balance_prev(),
371+
);
372+
361373
let caller_nonce_hash_bytes = array_init::array_init(|_| cb.query_byte());
362374
let create = ContractCreateGadget::construct(cb);
363375
cb.require_equal(
@@ -803,6 +815,7 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
803815
is_coinbase_warm,
804816
tx_l1_fee,
805817
tx_l1_msg,
818+
tx_eip1559,
806819
tx_eip2930,
807820
}
808821
}
@@ -1180,6 +1193,17 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
11801193
tx.tx_data_gas_cost,
11811194
)?;
11821195

1196+
self.tx_eip1559.assign(
1197+
region,
1198+
offset,
1199+
tx,
1200+
tx_l1_fee,
1201+
transfer_assign_result
1202+
.sender_balance_sub_fee_pair
1203+
.unwrap()
1204+
.1,
1205+
)?;
1206+
11831207
self.tx_eip2930.assign(region, offset, tx)
11841208
}
11851209
}

zkevm-circuits/src/evm_circuit/param.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use halo2_proofs::{
77
use std::{collections::HashMap, sync::LazyLock};
88

99
// Step dimension
10-
pub(crate) const STEP_WIDTH: usize = 140;
10+
pub(crate) const STEP_WIDTH: usize = 141;
1111
/// Step height
1212
pub const MAX_STEP_HEIGHT: usize = 21;
1313
/// The height of the state of a step, used by gates that connect two
@@ -27,7 +27,7 @@ pub(crate) const N_COPY_COLUMNS: usize = 2;
2727
// Number of copy columns for phase2
2828
pub(crate) const N_PHASE2_COPY_COLUMNS: usize = 1;
2929

30-
pub(crate) const N_BYTE_LOOKUPS: usize = 26;
30+
pub(crate) const N_BYTE_LOOKUPS: usize = 36;
3131

3232
/// Amount of lookup columns in the EVM circuit dedicated to lookups.
3333
pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS

zkevm-circuits/src/evm_circuit/util/common_gadget.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ use halo2_proofs::{
3131
plonk::{Error, Expression},
3232
};
3333

34+
mod tx_eip1559;
3435
mod tx_eip2930;
3536
mod tx_l1_fee;
3637
mod tx_l1_msg;
3738

39+
pub(crate) use tx_eip1559::TxEip1559Gadget;
3840
pub(crate) use tx_eip2930::TxEip2930Gadget;
3941
pub(crate) use tx_l1_fee::TxL1FeeGadget;
4042
pub(crate) use tx_l1_msg::TxL1MsgGadget;
@@ -591,6 +593,11 @@ impl<F: Field> TransferFromWithGasFeeGadget<F> {
591593
value: U256,
592594
gas_fee: U256,
593595
) -> Result<(), Error> {
596+
assert!(
597+
sender_balance_sub_fee >= prev_sender_balance_sub_value,
598+
"fee must be subtracted before value"
599+
);
600+
594601
if let Either::Left(value_is_zero) = &self.value_is_zero {
595602
value_is_zero.assign_value(region, offset, region.word_rlc(value))?;
596603
}
@@ -609,6 +616,12 @@ impl<F: Field> TransferFromWithGasFeeGadget<F> {
609616
sender_balance_sub_value,
610617
)
611618
}
619+
620+
/// Return sender balance before subtracting fee and value.
621+
pub(crate) fn sender_balance_prev(&self) -> &Word<F> {
622+
// Fee is subtracted before value.
623+
self.sender_sub_fee.balance_prev()
624+
}
612625
}
613626

614627
impl<F: Field> TransferFromAssign<F> for TransferFromWithGasFeeGadget<F> {
@@ -626,6 +639,11 @@ impl<F: Field> TransferFromAssign<F> for TransferFromWithGasFeeGadget<F> {
626639
} else {
627640
(0.into(), 0.into())
628641
};
642+
assert!(
643+
sender_balance_sub_fee_pair.0 >= sender_balance_sub_value_pair.1,
644+
"fee must be subtracted before value"
645+
);
646+
629647
self.assign(
630648
region,
631649
offset,
@@ -923,6 +941,11 @@ impl<F: Field> TransferWithGasFeeGadget<F> {
923941
to,
924942
}
925943
}
944+
945+
/// Return sender balance before subtracting fee and value.
946+
pub(crate) fn sender_balance_prev(&self) -> &Word<F> {
947+
self.from.sender_balance_prev()
948+
}
926949
}
927950

928951
impl<F: Field> TransferGadget<F> {

0 commit comments

Comments
 (0)