Skip to content

Commit e5ea196

Browse files
committed
revert changes made by exceeding blob size
1 parent 5f9b54d commit e5ea196

File tree

4 files changed

+105
-9
lines changed

4 files changed

+105
-9
lines changed

crates/vm/backends/levm/l2_utils.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::str::FromStr;
22

33
use ethrex_common::{
4-
types::{Receipt, Transaction, TxKind, SAFE_BYTES_PER_BLOB},
4+
types::{Log, Transaction, TxKind, SAFE_BYTES_PER_BLOB},
55
H256,
66
};
77
use ethrex_levm::db::gen_db::GeneralizedDatabase;
@@ -79,14 +79,14 @@ pub fn has_nonce_changed(
7979
pub fn update_state_diff_size(
8080
acc_state_diff_size: &mut Option<usize>,
8181
tx: &Transaction,
82-
receipt: &Receipt,
82+
logs: &[Log],
8383
db: &GeneralizedDatabase,
8484
) -> Result<(), EvmError> {
8585
if acc_state_diff_size.is_none() {
8686
return Ok(());
8787
}
8888
let mut actual_size = 0;
89-
if is_withdrawal_l2(tx, receipt)? {
89+
if is_withdrawal_l2(tx, logs)? {
9090
actual_size += L2_WITHDRAWAL_SIZE;
9191
}
9292
if is_deposit_l2(tx) {
@@ -107,14 +107,14 @@ pub fn update_state_diff_size(
107107
Ok(())
108108
}
109109

110-
fn is_withdrawal_l2(tx: &Transaction, receipt: &Receipt) -> Result<bool, EvmError> {
110+
fn is_withdrawal_l2(tx: &Transaction, logs: &[Log]) -> Result<bool, EvmError> {
111111
// WithdrawalInitiated(address,address,uint256)
112112
let withdrawal_event_selector: H256 =
113113
H256::from_str("bb2689ff876f7ef453cf8865dde5ab10349d222e2e1383c5152fbdb083f02da2")
114114
.map_err(|e| EvmError::Custom(e.to_string()))?;
115115

116116
let is_withdrawal = match tx.to() {
117-
TxKind::Call(to) if to == *COMMON_BRIDGE_L2_ADDRESS => receipt.logs.iter().any(|log| {
117+
TxKind::Call(to) if to == *COMMON_BRIDGE_L2_ADDRESS => logs.iter().any(|log| {
118118
log.topics
119119
.iter()
120120
.any(|topic| *topic == withdrawal_event_selector)

crates/vm/backends/levm/mod.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,83 @@ impl LEVM {
145145

146146
vm.execute().map_err(VMError::into)
147147
}
148+
149+
pub fn execute_tx_l2(
150+
// The transaction to execute.
151+
tx: &Transaction,
152+
// The transactions recovered address
153+
tx_sender: Address,
154+
// The block header for the current block.
155+
block_header: &BlockHeader,
156+
db: &mut GeneralizedDatabase,
157+
acc_state_diff_size: &mut Option<usize>,
158+
) -> Result<ExecutionReport, EvmError> {
159+
let chain_config = db.store.get_chain_config();
160+
let gas_price: U256 = tx
161+
.effective_gas_price(block_header.base_fee_per_gas)
162+
.ok_or(VMError::TxValidation(
163+
TxValidationError::InsufficientMaxFeePerGas,
164+
))?
165+
.into();
166+
167+
let config = EVMConfig::new_from_chain_config(&chain_config, block_header);
168+
let env = Environment {
169+
origin: tx_sender,
170+
refunded_gas: 0,
171+
gas_limit: tx.gas_limit(),
172+
config,
173+
block_number: block_header.number.into(),
174+
coinbase: block_header.coinbase,
175+
timestamp: block_header.timestamp.into(),
176+
prev_randao: Some(block_header.prev_randao),
177+
chain_id: chain_config.chain_id.into(),
178+
base_fee_per_gas: block_header.base_fee_per_gas.unwrap_or_default().into(),
179+
gas_price,
180+
block_excess_blob_gas: block_header.excess_blob_gas.map(U256::from),
181+
block_blob_gas_used: block_header.blob_gas_used.map(U256::from),
182+
tx_blob_hashes: tx.blob_versioned_hashes(),
183+
tx_max_priority_fee_per_gas: tx.max_priority_fee().map(U256::from),
184+
tx_max_fee_per_gas: tx.max_fee_per_gas().map(U256::from),
185+
tx_max_fee_per_blob_gas: tx.max_fee_per_blob_gas().map(U256::from),
186+
tx_nonce: tx.nonce(),
187+
block_gas_limit: block_header.gas_limit,
188+
transient_storage: HashMap::new(),
189+
difficulty: block_header.difficulty,
190+
is_privileged: matches!(tx, Transaction::PrivilegedL2Transaction(_)),
191+
};
192+
193+
let mut vm = VM::new(env, db, tx)?;
194+
195+
let report = vm.execute().map_err(EvmError::from)?;
196+
197+
// Here we differ from the execute_tx function from the L1.
198+
// We need to check if the transaction exceeded the blob size limit.
199+
// If it did, we need to revert the state changes made by the transaction and return the error.
200+
let call_frame_backup = vm
201+
.call_frames
202+
.pop()
203+
.ok_or(VMError::OutOfBounds)?
204+
.call_frame_backup;
205+
206+
if let Err(e) = update_state_diff_size(acc_state_diff_size, tx, &report.logs, db) {
207+
for (address, account) in call_frame_backup.original_accounts_info {
208+
if let Some(current_account) = db.cache.get_mut(&address) {
209+
current_account.info = account.info;
210+
current_account.code = account.code;
211+
}
212+
}
213+
for (address, storage) in call_frame_backup.original_account_storage_slots {
214+
let account = db.cache.get_mut(&address).ok_or(VMError::InvalidBytecode)?;
215+
for (key, value) in storage {
216+
account.storage.insert(key, value);
217+
}
218+
}
219+
return Err(e);
220+
}
221+
222+
Ok(report)
223+
}
224+
148225
pub fn simulate_tx_from_generic(
149226
// The transaction to execute.
150227
tx: &GenericTransaction,

crates/vm/backends/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ethrex_levm::db::gen_db::GeneralizedDatabase;
1515
use ethrex_levm::db::CacheDB;
1616
use ethrex_storage::Store;
1717
use ethrex_storage::{error::StoreError, AccountUpdate};
18-
use levm::{update_state_diff_size, LEVM};
18+
use levm::LEVM;
1919
use revm::db::EvmState;
2020
use revm::REVM;
2121
use std::sync::Arc;
@@ -185,7 +185,8 @@ impl Evm {
185185
Ok((receipt, execution_result.gas_used()))
186186
}
187187
Evm::LEVM { db } => {
188-
let execution_report = LEVM::execute_tx(tx, sender, block_header, db)?;
188+
let execution_report =
189+
LEVM::execute_tx_l2(tx, sender, block_header, db, acc_state_diff_size)?;
189190

190191
*remaining_gas = remaining_gas.saturating_sub(execution_report.gas_used);
191192

@@ -196,8 +197,6 @@ impl Evm {
196197
execution_report.logs.clone(),
197198
);
198199

199-
update_state_diff_size(acc_state_diff_size, tx, &receipt, db)?;
200-
201200
Ok((receipt, execution_report.gas_used))
202201
}
203202
}

crates/vm/levm/src/vm.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,11 @@ impl<'a> VM<'a> {
389389
return Err(e);
390390
}
391391

392+
// Here we need to backup the callframe because in the L2 we want to revert a transaction if it exceeds blob size
393+
// even if the transaction succeeds.
394+
#[cfg(feature = "l2")]
395+
let callframe_backup = self.current_call_frame()?.call_frame_backup.clone();
396+
392397
// Here we clear the cache backup because if prepare_execution succeeded we don't want to
393398
// revert the changes it made.
394399
// Even if the transaction reverts we want to apply these kind of changes!
@@ -429,6 +434,21 @@ impl<'a> VM<'a> {
429434
let mut report = self.run_execution()?;
430435

431436
self.finalize_execution(&mut report)?;
437+
438+
// We want to restore to the initial state, this includes reverting the changes made by the prepare execution
439+
// and the changes made by the execution itself.
440+
#[cfg(feature = "l2")]
441+
{
442+
let current_backup = &mut self.current_call_frame_mut()?.call_frame_backup;
443+
for (addr, acc) in callframe_backup.original_accounts_info {
444+
current_backup.original_accounts_info.insert(addr, acc);
445+
}
446+
for (addr, storage) in callframe_backup.original_account_storage_slots {
447+
current_backup
448+
.original_account_storage_slots
449+
.insert(addr, storage);
450+
}
451+
}
432452
Ok(report)
433453
}
434454

0 commit comments

Comments
 (0)