diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index 0af3a7425d..dbc157e1d9 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -54,4 +54,5 @@ rpc-legacy-tracer = [] # For the trace obtained from erigon node, refund field is missed # and must be rebuild fix-refund = ["rpc-legacy-tracer"] -retrace-tx = [] \ No newline at end of file +retrace-tx = [] +l1_fee_curie = [] \ No newline at end of file diff --git a/bus-mapping/src/circuit_input_builder/transaction.rs b/bus-mapping/src/circuit_input_builder/transaction.rs index 0eecacf4b5..cf049d5166 100644 --- a/bus-mapping/src/circuit_input_builder/transaction.rs +++ b/bus-mapping/src/circuit_input_builder/transaction.rs @@ -204,6 +204,8 @@ pub struct Transaction { pub rlp_bytes: Vec, /// RLP bytes for signing pub rlp_unsigned_bytes: Vec, + /// RLP bytes for signed tx + pub rlp_signed_bytes: Vec, /// Current values of L1 fee pub l1_fee: TxL1Fee, /// Committed values of L1 fee @@ -233,6 +235,7 @@ impl From<&Transaction> for geth_types::Transaction { gas_fee_cap: Some(tx.gas_fee_cap), gas_tip_cap: Some(tx.gas_tip_cap), rlp_unsigned_bytes: tx.rlp_unsigned_bytes.clone(), + //rlp_signed_bytes: tx.rlp_signed_bytes.clone(), rlp_bytes: tx.rlp_bytes.clone(), tx_type: tx.tx_type, ..Default::default() @@ -261,6 +264,7 @@ impl Transaction { }, rlp_bytes: vec![], rlp_unsigned_bytes: vec![], + rlp_signed_bytes: vec![], calls: Vec::new(), steps: Vec::new(), block_num: Default::default(), @@ -362,6 +366,8 @@ impl Transaction { tx_type, rlp_bytes: eth_tx.rlp().to_vec(), rlp_unsigned_bytes: get_rlp_unsigned(eth_tx), + //rlp_signed_bytes: get_rlp_signed(eth_tx), + rlp_signed_bytes: eth_tx.rlp().to_vec(), nonce: eth_tx.nonce.as_u64(), gas: eth_tx.gas.as_u64(), gas_price: eth_tx.gas_price.unwrap_or_default(), @@ -432,9 +438,18 @@ impl Transaction { /// Calculate L1 fee of this transaction. pub fn l1_fee(&self) -> u64 { - let tx_data_gas_cost = tx_data_gas_cost(&self.rlp_bytes); - - self.l1_fee.tx_l1_fee(tx_data_gas_cost).0 + //TODO: check if need to update for curie + #[cfg(not(feature = "l1_fee_curie"))] + { + let tx_data_gas_cost = tx_data_gas_cost(&self.rlp_bytes); + self.l1_fee.tx_l1_fee(tx_data_gas_cost, 0).0 + } + #[cfg(feature = "l1_fee_curie")] + { + // TODO: calculate tx rlp signed length + let tx_signed_length = self.rlp_signed_bytes.len(); + self.l1_fee.tx_l1_fee(0, tx_signed_length as u64).0 + } } } @@ -476,19 +491,58 @@ pub struct TxL1Fee { pub fee_overhead: u64, /// L1 fee scalar pub fee_scalar: u64, + #[cfg(feature = "l1_fee_curie")] + /// L1 blob fee + pub l1_blob_basefee: u64, + #[cfg(feature = "l1_fee_curie")] + /// L1 commit scalar + pub commit_scalar: u64, + #[cfg(feature = "l1_fee_curie")] + /// l1 blob scalar + pub blob_scalar: u64, } impl TxL1Fee { /// Calculate L1 fee and remainder of transaction. - pub fn tx_l1_fee(&self, tx_data_gas_cost: u64) -> (u64, u64) { + /// for non curie upgrade case, tx_rlp_signed_len is not used, set to zero + pub fn tx_l1_fee(&self, tx_data_gas_cost: u64, tx_rlp_signed_len: u64) -> (u64, u64) { // - let tx_l1_gas = tx_data_gas_cost + self.fee_overhead + TX_L1_COMMIT_EXTRA_COST; - let tx_l1_fee = self.fee_scalar as u128 * self.base_fee as u128 * tx_l1_gas as u128; + #[cfg(not(feature = "l1_fee_curie"))] + { + let tx_l1_gas = tx_data_gas_cost + self.fee_overhead + TX_L1_COMMIT_EXTRA_COST; + let tx_l1_fee = self.fee_scalar as u128 * self.base_fee as u128 * tx_l1_gas as u128; + ( + (tx_l1_fee / TX_L1_FEE_PRECISION as u128) as u64, + (tx_l1_fee % TX_L1_FEE_PRECISION as u128) as u64, + ) + } - ( - (tx_l1_fee / TX_L1_FEE_PRECISION as u128) as u64, - (tx_l1_fee % TX_L1_FEE_PRECISION as u128) as u64, - ) + #[cfg(feature = "l1_fee_curie")] + { + // for curie upgrade: + // new formula: https://github.com/scroll-tech/go-ethereum/blob/develop/rollup/fees/rollup_fee.go#L165 + // "commitScalar * l1BaseFee + blobScalar * _data.length * l1BlobBaseFee", + let tx_l1_fee = self.commit_scalar as u128 * self.base_fee as u128 + + self.blob_scalar as u128 + * tx_rlp_signed_len as u128 + * self.l1_blob_basefee as u128; + log::debug!( + "tx_l1_fee {} commit_scalar {} base_fee {} blob_scalar {} + tx_rlp_signed_len {} l1_blob_basefee {} tx_quient {},reminder {}", + tx_l1_fee, + self.commit_scalar, + self.base_fee, + self.blob_scalar, + tx_rlp_signed_len, + self.l1_blob_basefee, + tx_l1_fee / TX_L1_FEE_PRECISION as u128, + tx_l1_fee % TX_L1_FEE_PRECISION as u128 + ); + ( + (tx_l1_fee / TX_L1_FEE_PRECISION as u128) as u64, + (tx_l1_fee % TX_L1_FEE_PRECISION as u128) as u64, + ) + } } fn get_current_values_from_state_db(sdb: &StateDB) -> Self { @@ -502,11 +556,28 @@ impl TxL1Fee { .1 .as_u64() }); + #[cfg(feature = "l1_fee_curie")] + let [l1_blob_basefee, commit_scalar, blob_scalar] = [ + &l1_gas_price_oracle::L1_BLOB_BASEFEE_SLOT, + &l1_gas_price_oracle::COMMIT_SCALAR_SLOT, + &l1_gas_price_oracle::BLOB_SCALAR_SLOT, + ] + .map(|slot| { + sdb.get_storage(&l1_gas_price_oracle::ADDRESS, slot) + .1 + .as_u64() + }); Self { base_fee, fee_overhead, fee_scalar, + #[cfg(feature = "l1_fee_curie")] + l1_blob_basefee, + #[cfg(feature = "l1_fee_curie")] + commit_scalar, + #[cfg(feature = "l1_fee_curie")] + blob_scalar, } } @@ -522,10 +593,28 @@ impl TxL1Fee { .as_u64() }); + #[cfg(feature = "l1_fee_curie")] + let [l1_blob_basefee, commit_scalar, blob_scalar] = [ + &l1_gas_price_oracle::L1_BLOB_BASEFEE_SLOT, + &l1_gas_price_oracle::COMMIT_SCALAR_SLOT, + &l1_gas_price_oracle::BLOB_SCALAR_SLOT, + ] + .map(|slot| { + sdb.get_committed_storage(&l1_gas_price_oracle::ADDRESS, slot) + .1 + .as_u64() + }); + Self { base_fee, fee_overhead, fee_scalar, + #[cfg(feature = "l1_fee_curie")] + l1_blob_basefee, + #[cfg(feature = "l1_fee_curie")] + commit_scalar, + #[cfg(feature = "l1_fee_curie")] + blob_scalar, } } } diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs index dca661fdcd..27bf3e937a 100644 --- a/bus-mapping/src/evm/opcodes/begin_end_tx.rs +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -95,7 +95,7 @@ pub fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result = LazyLock::new(|| U256::from(3)); /// THe following 3 slots plus `BASE_FEE_SLOT` will be used for l1 fee after curie fork + /// L1 BlobBaseFee slot in L1GasPriceOracle after Curie fork pub static L1_BLOB_BASEFEE_SLOT: LazyLock = LazyLock::new(|| U256::from(5)); + /// L1 commitScalar slot in L1GasPriceOracle after Curie fork pub static COMMIT_SCALAR_SLOT: LazyLock = LazyLock::new(|| U256::from(6)); + /// L1 blob_scalar slot in L1GasPriceOracle after Curie fork pub static BLOB_SCALAR_SLOT: LazyLock = LazyLock::new(|| U256::from(7)); pub static IS_CURIE_SLOT: LazyLock = LazyLock::new(|| U256::from(8)); pub static INITIAL_COMMIT_SCALAR: LazyLock = diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index e0b42f5d15..35a87b165f 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -9,7 +9,7 @@ use crate::{ }; use ethers_core::types::{ transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, Eip2930TransactionRequest, - NameOrAddress, TransactionRequest, H256, + NameOrAddress, Signature, TransactionRequest, H256, }; use halo2curves::{group::ff::PrimeField, secp256k1::Fq}; use num::Integer; @@ -289,7 +289,7 @@ pub struct Transaction { pub rlp_bytes: Vec, /// RLP unsigned bytes pub rlp_unsigned_bytes: Vec, - + // TODO: add rlp_signed_bytes as well ? /// Transaction hash pub hash: H256, } diff --git a/geth-utils/l2geth/go.mod b/geth-utils/l2geth/go.mod index b479ff7e0c..f3c578d529 100644 --- a/geth-utils/l2geth/go.mod +++ b/geth-utils/l2geth/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/imdario/mergo v0.3.16 - github.com/scroll-tech/go-ethereum v1.10.14-0.20240524071237-9b4a779104ac + github.com/scroll-tech/go-ethereum v1.10.14-0.20240528204611-34162effbcc1 ) require ( diff --git a/geth-utils/l2geth/go.sum b/geth-utils/l2geth/go.sum index 708f5d810b..d7128da6ed 100644 --- a/geth-utils/l2geth/go.sum +++ b/geth-utils/l2geth/go.sum @@ -170,6 +170,8 @@ github.com/scroll-tech/go-ethereum v1.10.14-0.20240428082752-c5836a42d3b9 h1:j+r github.com/scroll-tech/go-ethereum v1.10.14-0.20240428082752-c5836a42d3b9/go.mod h1:i4VBgWoaW/y0D8MmQb7hSOulyw1dKhuiSFAbznwivCA= github.com/scroll-tech/go-ethereum v1.10.14-0.20240524071237-9b4a779104ac h1:A7tBJeDFXVjL4URIR89Ir8amS345Kgnh//+FA2v3lEw= github.com/scroll-tech/go-ethereum v1.10.14-0.20240524071237-9b4a779104ac/go.mod h1:GLhY3RmqBezwgPCqrPM/dTjrweDzVSAQ+Ggldk4dCTM= +github.com/scroll-tech/go-ethereum v1.10.14-0.20240528204611-34162effbcc1 h1:U7c23zVBjt4H01scnaglcPth0eXL9nPJCMA7RfZPXAQ= +github.com/scroll-tech/go-ethereum v1.10.14-0.20240528204611-34162effbcc1/go.mod h1:GLhY3RmqBezwgPCqrPM/dTjrweDzVSAQ+Ggldk4dCTM= github.com/scroll-tech/zktrie v0.7.1 h1:NrmZNjuBzsbrKePqdHDG+t2cXnimbtezPAFS0+L9ElE= github.com/scroll-tech/zktrie v0.7.1/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/scroll-tech/zktrie v0.8.2 h1:UMuIfA+jdgWMLmTgTL64Emo+zzMOdcnH0+eYdDcshxQ= diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 247af242aa..6eca8f2d59 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -71,3 +71,4 @@ debug-annotations = [] enable-stack = ["bus-mapping/enable-stack"] enable-memory = ["bus-mapping/enable-memory"] enable-storage = ["bus-mapping/enable-storage"] +l1_fee_curie = ["bus-mapping/l1_fee_curie"] diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index 45d64d175a..393b92b3df 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -61,6 +61,9 @@ pub(crate) struct BeginTxGadget { tx_call_data_gas_cost: Cell, // The gas cost for rlp-encoded bytes of unsigned tx tx_data_gas_cost: Cell, + #[cfg(feature = "l1_fee_curie")] + // rlp signed tx bytes' length + tx_signed_length: Cell, reversion_info: ReversionInfo, intrinsic_gas_cost: Cell, sufficient_gas_left: RangeCheckGadget, @@ -131,6 +134,8 @@ impl ExecutionGadget for BeginTxGadget { ] .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); + #[cfg(feature = "l1_fee_curie")] + let tx_signed_length = cb.tx_context(tx_id.expr(), TxContextFieldTag::TxHashLength, None); let tx_access_list = TxAccessListGadget::construct(cb, tx_id.expr(), tx_type.expr()); let is_call_data_empty = IsZeroGadget::construct(cb, tx_call_data_length.expr()); @@ -141,7 +146,13 @@ impl ExecutionGadget for BeginTxGadget { tx_nonce.expr(), sender_nonce.expr(), ); - TxL1FeeGadget::construct(cb, tx_id.expr(), tx_data_gas_cost.expr()) + TxL1FeeGadget::construct( + cb, + tx_id.expr(), + tx_data_gas_cost.expr(), + #[cfg(feature = "l1_fee_curie")] + tx_signed_length.expr(), + ) }); cb.condition(tx_l1_msg.is_l1_msg(), |cb| { cb.require_zero("l1fee is 0 for l1msg", tx_data_gas_cost.expr()); @@ -801,6 +812,8 @@ impl ExecutionGadget for BeginTxGadget { is_call_data_empty, tx_call_data_word_length, tx_call_data_gas_cost, + #[cfg(feature = "l1_fee_curie")] + tx_signed_length, tx_data_gas_cost, reversion_info, intrinsic_gas_cost, @@ -908,10 +921,16 @@ impl ExecutionGadget for BeginTxGadget { rws.offset_add(TxAccessListGadget::::rw_delta_value(tx) as usize); let rw = rws.next(); - debug_assert_eq!(rw.tag(), RwTableTag::CallContext); - debug_assert_eq!(rw.field_tag(), Some(CallContextFieldTag::L1Fee as u64)); - rws.offset_add(3); + #[cfg(not(feature = "l1_fee_curie"))] + { + debug_assert_eq!(rw.tag(), RwTableTag::CallContext); + debug_assert_eq!(rw.field_tag(), Some(CallContextFieldTag::L1Fee as u64)); + rws.offset_add(3); + } + + #[cfg(feature = "l1_fee_curie")] + rws.offset_add(6); let rw = rws.next(); debug_assert_eq!(rw.tag(), RwTableTag::Account); @@ -1077,6 +1096,13 @@ impl ExecutionGadget for BeginTxGadget { )?; self.tx_data_gas_cost .assign(region, offset, Value::known(F::from(tx.tx_data_gas_cost)))?; + #[cfg(feature = "l1_fee_curie")] + self.tx_signed_length.assign( + region, + offset, + Value::known(F::from(tx.rlp_signed.len() as u64)), + )?; + self.reversion_info.assign( region, offset, @@ -1191,10 +1217,24 @@ impl ExecutionGadget for BeginTxGadget { log::trace!("tx is l1msg and l1 fee is 0"); (U256::zero(), U256::zero()) } else { - ( - tx.l1_fee.tx_l1_fee(tx.tx_data_gas_cost).0.into(), - tx.gas_price * tx.gas, - ) + #[cfg(not(feature = "l1_fee_curie"))] + { + ( + tx.l1_fee.tx_l1_fee(tx.tx_data_gas_cost, 0).0.into(), + tx.gas_price * tx.gas, + ) + } + + #[cfg(feature = "l1_fee_curie")] + { + ( + tx.l1_fee + .tx_l1_fee(0, tx.rlp_signed.len().try_into().unwrap()) + .0 + .into(), + tx.gas_price * tx.gas, + ) + } }; if tx_fee != tx_l2_fee + tx_l1_fee { log::error!( @@ -1211,6 +1251,7 @@ impl ExecutionGadget for BeginTxGadget { tx.l1_fee, tx.l1_fee_committed, tx.tx_data_gas_cost, + tx.rlp_signed.len().try_into().unwrap(), )?; self.tx_access_list.assign(region, offset, tx)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index 71af98bc10..bd85added8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -415,7 +415,13 @@ impl ExecutionGadget for EndTxGadget { log::trace!("tx is l1msg and l1 fee is 0"); 0 } else { - tx.l1_fee.tx_l1_fee(tx.tx_data_gas_cost).0 + if cfg!(feature = "l1_fee_curie") { + tx.l1_fee + .tx_l1_fee(0, tx.rlp_signed.len().try_into().unwrap()) + .0 + } else { + tx.l1_fee.tx_l1_fee(tx.tx_data_gas_cost, 0).0 + } }; log::trace!( "tx_l1_fee: {}, coinbase_reward: {}", diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 0a85210336..83645fd445 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -841,6 +841,7 @@ impl TransferToGadget { } else { (0.into(), 0.into()) }; + debug_assert_eq!(receiver_balance, prev_receiver_balance + value); self.receiver.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs index 9e96751ca2..020b549471 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_fee.rs @@ -4,7 +4,9 @@ use crate::{ param::N_BYTES_U64, util::{ constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - from_bytes, U64Word, Word, + from_bytes, + math_gadget::LtGadget, + U64Word, Word, }, }, util::{Expr, Field}, @@ -25,18 +27,38 @@ pub(crate) struct TxL1FeeGadget { tx_l1_fee_word: Word, /// Remainder when calculating L1 fee remainder_word: U64Word, + /// Remainder must in [0, TX_L1_FEE_PRECISION) + remainder_range: LtGadget, /// Current value of L1 base fee base_fee_word: U64Word, /// Current value of L1 fee overhead fee_overhead_word: U64Word, /// Current value of L1 fee scalar fee_scalar_word: U64Word, - /// Committed value of L1 base fee + #[cfg(feature = "l1_fee_curie")] + /// Current value of L1 blob base fee + l1_blob_basefee_word: U64Word, + #[cfg(feature = "l1_fee_curie")] + /// Current value of L1 scalar fee + commit_scalar_word: U64Word, + #[cfg(feature = "l1_fee_curie")] + /// Current value of L1 blob scalar fee + blob_scalar_word: U64Word, + /// Current value of L1 base fee base_fee_committed: Cell, /// Committed value of L1 fee overhead fee_overhead_committed: Cell, /// Committed value of L1 fee scalar fee_scalar_committed: Cell, + #[cfg(feature = "l1_fee_curie")] + /// Committed value of L1 blob base fee + l1_blob_basefee_committed: Cell, + #[cfg(feature = "l1_fee_curie")] + /// Committed value of L1 scalar fee + commit_scalar_committed: Cell, + #[cfg(feature = "l1_fee_curie")] + /// Committed value of L1 blob scalar fee + blob_scalar_committed: Cell, } impl TxL1FeeGadget { @@ -44,13 +66,18 @@ impl TxL1FeeGadget { cb: &mut EVMConstraintBuilder, tx_id: Expression, tx_data_gas_cost: Expression, + #[cfg(feature = "l1_fee_curie")] tx_signed_length: Expression, ) -> Self { + #[cfg(feature = "l1_fee_curie")] + let this = Self::raw_construct(cb, tx_data_gas_cost, tx_signed_length); + #[cfg(not(feature = "l1_fee_curie"))] let this = Self::raw_construct(cb, tx_data_gas_cost); let l1_fee_address = Expression::Constant(l1_gas_price_oracle::ADDRESS.to_scalar().expect( "Unexpected address of l2 gasprice oracle contract -> Scalar conversion failure", )); + //TODO: add curie fork fields let [base_fee_slot, overhead_slot, scalar_slot] = [ &l1_gas_price_oracle::BASE_FEE_SLOT, &l1_gas_price_oracle::OVERHEAD_SLOT, @@ -58,6 +85,14 @@ impl TxL1FeeGadget { ] .map(|slot| cb.word_rlc(slot.to_le_bytes().map(|b| b.expr()))); + #[cfg(feature = "l1_fee_curie")] + let [l1_blob_basefee, commit_scalar, blob_scalar] = [ + &l1_gas_price_oracle::L1_BLOB_BASEFEE_SLOT, + &l1_gas_price_oracle::COMMIT_SCALAR_SLOT, + &l1_gas_price_oracle::BLOB_SCALAR_SLOT, + ] + .map(|slot| cb.word_rlc(slot.to_le_bytes().map(|b| b.expr()))); + // Read L1 base fee cb.account_storage_read( l1_fee_address.expr(), @@ -78,13 +113,46 @@ impl TxL1FeeGadget { // Read L1 fee scalar cb.account_storage_read( - l1_fee_address, + l1_fee_address.clone(), scalar_slot, this.fee_scalar_word.expr(), - tx_id, + tx_id.clone(), this.fee_scalar_committed.expr(), ); + // TODO: check if can really reuse base_fee_slot read rw above for curie + // now try to skip it. + // for curie hard fork + #[cfg(feature = "l1_fee_curie")] + // Read l1blob_baseFee_committed + cb.account_storage_read( + l1_fee_address.expr(), + l1_blob_basefee, + this.l1_blob_basefee_word.expr(), + tx_id.clone(), + this.l1_blob_basefee_committed.expr(), + ); + + #[cfg(feature = "l1_fee_curie")] + // Read L1 commit_scalar_committed + cb.account_storage_read( + l1_fee_address.expr(), + commit_scalar, + this.commit_scalar_word.expr(), + tx_id.expr(), + this.commit_scalar_committed.expr(), + ); + + #[cfg(feature = "l1_fee_curie")] + // Read L1 blob_scalar_committed scalar + cb.account_storage_read( + l1_fee_address, + blob_scalar, + this.blob_scalar_word.expr(), + tx_id, + this.blob_scalar_committed.expr(), + ); + this } @@ -95,8 +163,21 @@ impl TxL1FeeGadget { l1_fee: TxL1Fee, l1_fee_committed: TxL1Fee, tx_data_gas_cost: u64, + tx_signed_length: u64, ) -> Result<(), Error> { - let (tx_l1_fee, remainder) = l1_fee.tx_l1_fee(tx_data_gas_cost); + #[cfg(feature = "l1_fee_curie")] + log::debug!( + "assign: tx_l1_fee {:?} l1_fee_committed {:?} tx_signed_length {}", + l1_fee, + l1_fee_committed, + tx_signed_length + ); + let (tx_l1_fee, remainder) = if cfg!(feature = "l1_fee_curie") { + l1_fee.tx_l1_fee(0, tx_signed_length) + } else { + l1_fee.tx_l1_fee(tx_data_gas_cost, 0) + }; + self.tx_l1_fee_word .assign(region, offset, Some(U256::from(tx_l1_fee).to_le_bytes()))?; self.remainder_word @@ -107,6 +188,27 @@ impl TxL1FeeGadget { .assign(region, offset, Some(l1_fee.fee_overhead.to_le_bytes()))?; self.fee_scalar_word .assign(region, offset, Some(l1_fee.fee_scalar.to_le_bytes()))?; + self.remainder_range.assign( + region, + offset, + F::from(remainder), + F::from(TX_L1_FEE_PRECISION), + )?; + + // curie fields + #[cfg(feature = "l1_fee_curie")] + self.l1_blob_basefee_word.assign( + region, + offset, + Some(l1_fee.l1_blob_basefee.to_le_bytes()), + )?; + #[cfg(feature = "l1_fee_curie")] + self.commit_scalar_word + .assign(region, offset, Some(l1_fee.commit_scalar.to_le_bytes()))?; + #[cfg(feature = "l1_fee_curie")] + self.blob_scalar_word + .assign(region, offset, Some(l1_fee.blob_scalar.to_le_bytes()))?; + self.base_fee_committed.assign( region, offset, @@ -123,6 +225,26 @@ impl TxL1FeeGadget { region.word_rlc(l1_fee_committed.fee_scalar.into()), )?; + // curie fields + #[cfg(feature = "l1_fee_curie")] + self.l1_blob_basefee_committed.assign( + region, + offset, + region.word_rlc(l1_fee_committed.l1_blob_basefee.into()), + )?; + #[cfg(feature = "l1_fee_curie")] + self.commit_scalar_committed.assign( + region, + offset, + region.word_rlc(l1_fee_committed.commit_scalar.into()), + )?; + #[cfg(feature = "l1_fee_curie")] + self.blob_scalar_committed.assign( + region, + offset, + region.word_rlc(l1_fee_committed.blob_scalar.into()), + )?; + Ok(()) } @@ -130,7 +252,15 @@ impl TxL1FeeGadget { // L1 base fee Read // L1 fee overhead Read // L1 fee scalar Read - 3.expr() + // + curie fields + // l1 blob baseFee + // commit scalar + // blob scalar + if cfg!(feature = "l1_fee_curie") { + 6.expr() + } else { + 3.expr() + } } pub(crate) fn tx_l1_fee(&self) -> Expression { @@ -141,13 +271,24 @@ impl TxL1FeeGadget { &self.tx_l1_fee_word } - fn raw_construct(cb: &mut EVMConstraintBuilder, tx_data_gas_cost: Expression) -> Self { + fn raw_construct( + cb: &mut EVMConstraintBuilder, + tx_data_gas_cost: Expression, + #[cfg(feature = "l1_fee_curie")] tx_signed_length: Expression, + ) -> Self { let tx_l1_fee_word = cb.query_word_rlc(); let remainder_word = cb.query_word_rlc(); let base_fee_word = cb.query_word_rlc(); let fee_overhead_word = cb.query_word_rlc(); let fee_scalar_word = cb.query_word_rlc(); + // curie fields + #[cfg(feature = "l1_fee_curie")] + let l1_blob_basefee_word = cb.query_word_rlc(); + #[cfg(feature = "l1_fee_curie")] + let commit_scalar_word = cb.query_word_rlc(); + #[cfg(feature = "l1_fee_curie")] + let blob_scalar_word = cb.query_word_rlc(); let tx_l1_fee = from_bytes::expr(&tx_l1_fee_word.cells[..N_BYTES_U64]); let [remainder, base_fee, fee_overhead, fee_scalar] = [ @@ -158,8 +299,39 @@ impl TxL1FeeGadget { ] .map(|word| from_bytes::expr(&word.cells[..N_BYTES_U64])); + let remainder_range = LtGadget::construct(cb, remainder.expr(), TX_L1_FEE_PRECISION.expr()); + cb.require_equal( + "remainder must less than l1 fee precision", + 1.expr(), + remainder_range.expr(), + ); + + #[cfg(feature = "l1_fee_curie")] + let [l1_blob_basefee, commit_scalar, blob_scalar] = [ + &l1_blob_basefee_word, + &commit_scalar_word, + &blob_scalar_word, + ] + .map(|word| from_bytes::expr(&word.cells[..N_BYTES_U64])); + // - let tx_l1_gas = tx_data_gas_cost + TX_L1_COMMIT_EXTRA_COST.expr() + fee_overhead; + let tx_l1_gas = if cfg!(feature = "l1_fee_curie") { + 0.expr() + } else { + tx_data_gas_cost + TX_L1_COMMIT_EXTRA_COST.expr() + fee_overhead + }; + + // TODO: new formula for curie + #[cfg(feature = "l1_fee_curie")] + cb.require_equal( + "commitScalar * l1BaseFee + blobScalar * _data.length * l1BlobBaseFee == tx_l1_fee * 10e9 + remainder", + commit_scalar * base_fee + blob_scalar * tx_signed_length * l1_blob_basefee, + // * tx_l1_gas, + tx_l1_fee * TX_L1_FEE_PRECISION.expr() + remainder, + ); + + // old formula before curie + #[cfg(not(feature = "l1_fee_curie"))] cb.require_equal( "fee_scalar * base_fee * tx_l1_gas == tx_l1_fee * 10e9 + remainder", fee_scalar * base_fee * tx_l1_gas, @@ -169,16 +341,36 @@ impl TxL1FeeGadget { let base_fee_committed = cb.query_cell_phase2(); let fee_overhead_committed = cb.query_cell_phase2(); let fee_scalar_committed = cb.query_cell_phase2(); + // curie fields + #[cfg(feature = "l1_fee_curie")] + let l1_blob_basefee_committed = cb.query_cell_phase2(); + #[cfg(feature = "l1_fee_curie")] + let commit_scalar_committed = cb.query_cell_phase2(); + #[cfg(feature = "l1_fee_curie")] + let blob_scalar_committed = cb.query_cell_phase2(); Self { tx_l1_fee_word, remainder_word, + remainder_range, base_fee_word, fee_overhead_word, fee_scalar_word, + #[cfg(feature = "l1_fee_curie")] + l1_blob_basefee_word, + #[cfg(feature = "l1_fee_curie")] + commit_scalar_word, + #[cfg(feature = "l1_fee_curie")] + blob_scalar_word, base_fee_committed, fee_overhead_committed, fee_scalar_committed, + #[cfg(feature = "l1_fee_curie")] + l1_blob_basefee_committed, + #[cfg(feature = "l1_fee_curie")] + commit_scalar_committed, + #[cfg(feature = "l1_fee_curie")] + blob_scalar_committed, } } } @@ -240,6 +432,8 @@ mod tests { let tx_data_gas_cost = cb.query_cell(); let expected_tx_l1_fee = cb.query_cell(); + // for non "l1_fee_curie" feature, tx_signed_length is not used, can + // set to zero let gadget = TxL1FeeGadget::::raw_construct(cb, tx_data_gas_cost.expr()); cb.require_equal( @@ -273,6 +467,7 @@ mod tests { l1_fee, TxL1Fee::default(), tx_data_gas_cost.as_u64(), + 0, // TODO: check if need update here )?; self.tx_data_gas_cost.assign( region, diff --git a/zkevm-circuits/src/witness/l1_msg.rs b/zkevm-circuits/src/witness/l1_msg.rs index 70b5d2e37a..5759a2e112 100644 --- a/zkevm-circuits/src/witness/l1_msg.rs +++ b/zkevm-circuits/src/witness/l1_msg.rs @@ -93,7 +93,7 @@ mod tests { for (tx, (rlp_expected, l1fee_expected)) in txs.into_iter().zip(expected) { let rlp = tx.rlp().to_vec(); assert_eq!(rlp.len(), rlp_expected); - assert_eq!(l1fee.tx_l1_fee(tx_data_gas_cost(&rlp)).0, l1fee_expected) + assert_eq!(l1fee.tx_l1_fee(tx_data_gas_cost(&rlp), 0).0, l1fee_expected) } } }