1use crate::{
2 EVMConfig, Environment,
3 account::{AccountStatus, LevmAccount},
4 call_frame::CallFrameBackup,
5 constants::*,
6 db::gen_db::GeneralizedDatabase,
7 errors::{ExceptionalHalt, InternalError, TxValidationError, VMError},
8 gas_cost::{
9 self, ACCESS_LIST_ADDRESS_COST, ACCESS_LIST_STORAGE_KEY_COST, BLOB_GAS_PER_BLOB,
10 COLD_ADDRESS_ACCESS_COST, CREATE_BASE_COST, REGULAR_GAS_CREATE, STANDARD_TOKEN_COST,
11 STATE_BYTES_PER_AUTH_TOTAL, STATE_BYTES_PER_NEW_ACCOUNT, WARM_ADDRESS_ACCESS_COST,
12 cost_per_state_byte, floor_tokens_in_access_list, total_cost_floor_per_token,
13 },
14 vm::{Substate, VM},
15};
16use ExceptionalHalt::OutOfGas;
17use bytes::Bytes;
18use ethrex_common::constants::SYSTEM_ADDRESS;
19use ethrex_common::types::Log;
20use ethrex_common::{
21 Address, H256, U256,
22 evm::calculate_create_address,
23 types::{Account, Code, Fork, Transaction, fake_exponential, tx_fields::*},
24 utils::{keccak, u256_to_big_endian},
25};
26use ethrex_common::{types::TxKind, utils::u256_from_big_endian_const};
27use ethrex_rlp;
28use rustc_hash::FxHashMap;
29pub type Storage = FxHashMap<U256, H256>;
30
31pub fn address_to_word(address: Address) -> U256 {
34 let mut word = [0u8; 32];
35
36 for (word_byte, address_byte) in word.iter_mut().skip(12).zip(address.as_bytes().iter()) {
37 *word_byte = *address_byte;
38 }
39
40 u256_from_big_endian_const(word)
41}
42
43pub fn calculate_create2_address(
49 sender_address: Address,
50 initialization_code: &Bytes,
51 salt: U256,
52) -> Result<Address, InternalError> {
53 let init_code_hash = keccak(initialization_code);
54
55 let generated_address = Address::from_slice(
56 keccak(
57 [
58 &[0xff],
59 sender_address.as_bytes(),
60 &salt.to_big_endian(),
61 init_code_hash.as_bytes(),
62 ]
63 .concat(),
64 )
65 .as_bytes()
66 .get(12..)
67 .ok_or(InternalError::Slicing)?,
68 );
69 Ok(generated_address)
70}
71
72pub fn restore_cache_state(
77 db: &mut GeneralizedDatabase,
78 callframe_backup: CallFrameBackup,
79) -> Result<(), VMError> {
80 for (address, account) in callframe_backup.original_accounts_info {
81 if let Some(current_account) = db.current_accounts_state.get_mut(&address) {
82 current_account.info = account.info;
83 current_account.status = account.status;
84 current_account.has_storage = account.has_storage;
85 current_account.exists = account.exists;
86 }
87 }
88
89 for (address, storage) in callframe_backup.original_account_storage_slots {
90 let account = db
94 .current_accounts_state
95 .get_mut(&address)
96 .ok_or(InternalError::AccountNotFound)?;
97
98 for (key, value) in storage {
99 account.storage.insert(key, value);
100 }
101 }
102
103 for code_hash in callframe_backup.inserted_code_hashes {
109 db.codes.remove(&code_hash);
110 }
111
112 if let Some(checkpoint) = callframe_backup.bal_checkpoint
114 && let Some(recorder) = db.bal_recorder.as_mut()
115 {
116 recorder.restore(checkpoint);
117 }
118
119 Ok(())
120}
121
122pub fn get_base_fee_per_blob_gas(
124 block_excess_blob_gas: Option<u64>,
125 evm_config: &EVMConfig,
126) -> Result<U256, VMError> {
127 let base_fee_update_fraction = evm_config.blob_schedule.base_fee_update_fraction;
128 let excess_blob_gas = block_excess_blob_gas.unwrap_or_default();
129
130 fake_exponential(
131 MIN_BASE_FEE_PER_BLOB_GAS.into(),
132 excess_blob_gas.into(),
133 base_fee_update_fraction,
134 )
135 .map_err(|err| VMError::Internal(InternalError::FakeExponentialError(err)))
136}
137
138pub fn get_max_blob_gas_price(
141 tx_blob_hashes: &[H256],
142 tx_max_fee_per_blob_gas: Option<U256>,
143) -> Result<U256, VMError> {
144 let blobhash_amount: u64 = tx_blob_hashes
145 .len()
146 .try_into()
147 .map_err(|_| InternalError::TypeConversion)?;
148
149 let blob_gas_used: u64 = blobhash_amount
150 .checked_mul(BLOB_GAS_PER_BLOB)
151 .unwrap_or_default();
152
153 let max_blob_gas_cost = tx_max_fee_per_blob_gas
154 .unwrap_or_default()
155 .checked_mul(blob_gas_used.into())
156 .ok_or(InternalError::Overflow)?;
157
158 Ok(max_blob_gas_cost)
159}
160pub fn calculate_blob_gas_cost(
162 tx_blob_hashes: &[H256],
163 base_blob_fee_per_gas: U256,
164) -> Result<U256, VMError> {
165 let blobhash_amount: u64 = tx_blob_hashes
166 .len()
167 .try_into()
168 .map_err(|_| InternalError::TypeConversion)?;
169
170 let blob_gas_used: u64 = blobhash_amount
171 .checked_mul(BLOB_GAS_PER_BLOB)
172 .unwrap_or_default();
173
174 let blob_gas_used: U256 = blob_gas_used.into();
175 let blob_fee: U256 = blob_gas_used
176 .checked_mul(base_blob_fee_per_gas)
177 .ok_or(InternalError::Overflow)?;
178
179 Ok(blob_fee)
180}
181
182pub fn word_to_address(word: U256) -> Address {
184 Address::from_slice(&u256_to_big_endian(word)[12..])
185}
186
187pub fn code_has_delegation(code: &[u8]) -> Result<bool, VMError> {
190 if code.len() == EIP7702_DELEGATED_CODE_LEN {
191 let first_3_bytes = &code.get(..3).ok_or(InternalError::Slicing)?;
192 return Ok(*first_3_bytes == SET_CODE_DELEGATION_BYTES);
193 }
194 Ok(false)
195}
196
197pub fn get_authorized_address_from_code(code: &[u8]) -> Result<Address, VMError> {
200 if code_has_delegation(code)? {
201 let address_bytes = &code
202 .get(SET_CODE_DELEGATION_BYTES.len()..)
203 .ok_or(InternalError::Slicing)?;
204 let address = Address::from_slice(address_bytes);
207 Ok(address)
208 } else {
209 Err(InternalError::AccountNotDelegated.into())
211 }
212}
213
214pub fn eip7702_recover_address(
215 auth_tuple: &AuthorizationTuple,
216 crypto: &dyn ethrex_crypto::Crypto,
217) -> Result<Option<Address>, VMError> {
218 use ethrex_rlp::encode::RLPEncode;
219
220 if auth_tuple.s_signature > *SECP256K1_ORDER_OVER2 || U256::zero() >= auth_tuple.s_signature {
221 return Ok(None);
222 }
223 if auth_tuple.r_signature > *SECP256K1_ORDER || U256::zero() >= auth_tuple.r_signature {
224 return Ok(None);
225 }
226 if auth_tuple.y_parity != U256::one() && auth_tuple.y_parity != U256::zero() {
227 return Ok(None);
228 }
229
230 let mut rlp_buf = Vec::with_capacity(128);
231 rlp_buf.push(MAGIC);
232 (auth_tuple.chain_id, auth_tuple.address, auth_tuple.nonce).encode(&mut rlp_buf);
233 let msg = crypto.keccak256(&rlp_buf);
234
235 let y_parity: u8 =
236 TryInto::<u8>::try_into(auth_tuple.y_parity).map_err(|_| InternalError::TypeConversion)?;
237
238 let mut sig = [0u8; 65];
239 sig[..32].copy_from_slice(&auth_tuple.r_signature.to_big_endian());
240 sig[32..64].copy_from_slice(&auth_tuple.s_signature.to_big_endian());
241 sig[64] = y_parity;
242
243 match crypto.recover_signer(&sig, &msg) {
244 Ok(address) => Ok(Some(address)),
245 Err(_) => Ok(None),
246 }
247}
248
249pub fn eip7702_get_code(
259 db: &mut GeneralizedDatabase,
260 accrued_substate: &mut Substate,
261 address: Address,
262) -> Result<(bool, u64, Address, Code), VMError> {
263 let (bytecode, delegation) = eip7702_peek_delegation(db, accrued_substate, address)?;
264 let Some((auth_address, access_cost)) = delegation else {
265 return Ok((false, 0, address, bytecode));
266 };
267
268 accrued_substate.add_accessed_address(auth_address);
269 let authorized_bytecode = db.get_account_code(auth_address)?.clone();
270
271 Ok((true, access_cost, auth_address, authorized_bytecode))
272}
273
274pub fn eip7702_peek_delegation(
283 db: &mut GeneralizedDatabase,
284 substate: &Substate,
285 address: Address,
286) -> Result<(Code, Option<(Address, u64)>), VMError> {
287 let bytecode = db.get_account_code(address)?.clone();
288 if !code_has_delegation(bytecode.code())? {
289 return Ok((bytecode, None));
290 }
291 let auth_address = get_authorized_address_from_code(bytecode.code())?;
292 let access_cost = if substate.is_address_accessed(&auth_address) {
293 WARM_ADDRESS_ACCESS_COST
294 } else {
295 COLD_ADDRESS_ACCESS_COST
296 };
297 Ok((bytecode, Some((auth_address, access_cost))))
298}
299
300#[derive(Clone, Copy, Debug)]
308pub struct IntrinsicGas {
309 pub regular: u64,
311 pub state: u64,
313 pub calldata_cost: u64,
316}
317
318impl<'a> VM<'a> {
319 pub fn eip7702_set_access_code(&mut self) -> Result<(), VMError> {
321 let mut refunded_gas: u64 = 0;
322 for auth_tuple in self.tx.authorization_list().cloned().unwrap_or_default() {
326 let chain_id_not_equals_this_chain_id = auth_tuple.chain_id != self.env.chain_id;
327 let chain_id_not_zero = !auth_tuple.chain_id.is_zero();
328
329 if chain_id_not_zero && chain_id_not_equals_this_chain_id {
331 continue;
332 }
333
334 if auth_tuple.nonce == u64::MAX {
337 continue;
338 }
339
340 let Some(authority_address) = eip7702_recover_address(&auth_tuple, self.crypto)? else {
343 continue;
344 };
345
346 let authority_account = self.db.get_account(authority_address)?;
348 let authority_exists = authority_account.exists;
349 let authority_info = authority_account.info.clone();
350 let authority_code = self.db.get_code(authority_info.code_hash)?;
351 self.substate.add_accessed_address(authority_address);
352
353 let authority_code_is_empty = authority_code.is_empty();
356 let empty_or_delegated =
357 authority_code_is_empty || code_has_delegation(authority_code.code())?;
358
359 if let Some(recorder) = self.db.bal_recorder.as_mut() {
364 recorder.record_touched_address(authority_address);
365 }
366
367 if !empty_or_delegated {
368 continue;
369 }
370
371 if authority_info.nonce != auth_tuple.nonce {
375 continue;
376 }
377
378 if authority_exists {
385 if self.env.config.fork >= Fork::Amsterdam {
386 let refund = self.state_gas_new_account;
397 self.state_gas_reservoir = self
398 .state_gas_reservoir
399 .checked_add(refund)
400 .ok_or(InternalError::Overflow)?;
401 self.state_refund = self
402 .state_refund
403 .checked_add(refund)
404 .ok_or(InternalError::Overflow)?;
405 } else {
406 refunded_gas = refunded_gas
407 .checked_add(REFUND_AUTH_PER_EXISTING_ACCOUNT)
408 .ok_or(InternalError::Overflow)?;
409 }
410 }
411
412 let writes_no_new_indicator =
425 !authority_code_is_empty || auth_tuple.address == Address::zero();
426 if self.env.config.fork >= Fork::Amsterdam && writes_no_new_indicator {
427 let refund = self.state_gas_auth_base;
428 self.state_gas_reservoir = self
429 .state_gas_reservoir
430 .checked_add(refund)
431 .ok_or(InternalError::Overflow)?;
432 self.state_refund = self
433 .state_refund
434 .checked_add(refund)
435 .ok_or(InternalError::Overflow)?;
436 }
437
438 let delegation_bytes = [
440 &SET_CODE_DELEGATION_BYTES[..],
441 auth_tuple.address.as_bytes(),
442 ]
443 .concat();
444
445 let code = if auth_tuple.address != Address::zero() {
448 delegation_bytes.into()
449 } else {
450 Bytes::new()
451 };
452 self.update_account_bytecode(
453 authority_address,
454 Code::from_bytecode(code, self.crypto),
455 )?;
456
457 self.increment_account_nonce(authority_address)
459 .map_err(|_| TxValidationError::NonceIsMax)?;
460 }
461
462 self.substate.refunded_gas = self
463 .substate
464 .refunded_gas
465 .checked_add(refunded_gas)
466 .ok_or(InternalError::Overflow)?;
467
468 Ok(())
469 }
470
471 pub fn add_intrinsic_gas(&mut self, intrinsic: &IntrinsicGas) -> Result<(), VMError> {
472 let regular_gas = intrinsic.regular;
475 let state_gas = intrinsic.state;
476
477 let total_gas = regular_gas.checked_add(state_gas).ok_or(OutOfGas)?;
478
479 self.current_call_frame
480 .increase_consumed_gas(total_gas)
481 .map_err(|_| TxValidationError::IntrinsicGasTooLow)?;
482
483 self.state_gas_used = self
485 .state_gas_used
486 .checked_add(i64::try_from(state_gas).map_err(|_| InternalError::Overflow)?)
487 .ok_or(InternalError::Overflow)?;
488 debug_assert_eq!(self.intrinsic_state_gas, 0, "intrinsic_state_gas set twice");
492 self.intrinsic_state_gas = state_gas;
493
494 if self.env.config.fork >= Fork::Amsterdam {
499 if self.env.is_system_call {
500 let sys_reservoir = self
510 .state_gas_storage_set
511 .saturating_mul(SYSTEM_MAX_SSTORES_PER_CALL);
512 self.state_gas_reservoir = sys_reservoir;
513 self.state_gas_reservoir_initial = sys_reservoir;
514 } else {
515 let gas_limit = self.tx.gas_limit();
516 let execution_gas = gas_limit.saturating_sub(total_gas);
517 let regular_gas_budget = TX_MAX_GAS_LIMIT_AMSTERDAM.saturating_sub(regular_gas);
518 let gas_left = regular_gas_budget.min(execution_gas);
519 let reservoir = execution_gas.saturating_sub(gas_left);
520 if reservoir > 0 {
521 let reservoir_i64 =
523 i64::try_from(reservoir).map_err(|_| InternalError::Overflow)?;
524 self.current_call_frame.gas_remaining = self
525 .current_call_frame
526 .gas_remaining
527 .checked_sub(reservoir_i64)
528 .ok_or(InternalError::Overflow)?;
529 self.state_gas_reservoir = reservoir;
530 }
531 self.state_gas_reservoir_initial = reservoir;
533 }
534 }
535
536 Ok(())
537 }
538
539 pub fn get_intrinsic_gas(&self) -> Result<IntrinsicGas, VMError> {
544 let mut regular_gas: u64 = 0;
546 let mut state_gas: u64 = 0;
547 let fork = self.env.config.fork;
548
549 let calldata_cost = gas_cost::tx_calldata(&self.current_call_frame.calldata)?;
552
553 regular_gas = regular_gas.checked_add(calldata_cost).ok_or(OutOfGas)?;
554
555 regular_gas = regular_gas.checked_add(TX_BASE_COST).ok_or(OutOfGas)?;
557
558 if self.is_create()? {
560 if fork >= Fork::Amsterdam {
561 regular_gas = regular_gas
563 .checked_add(REGULAR_GAS_CREATE)
564 .ok_or(OutOfGas)?;
565 state_gas = state_gas
566 .checked_add(self.state_gas_new_account)
567 .ok_or(OutOfGas)?;
568 } else {
569 regular_gas = regular_gas.checked_add(CREATE_BASE_COST).ok_or(OutOfGas)?;
571 }
572
573 if fork >= Fork::Shanghai {
575 let number_of_words = &self.current_call_frame.calldata.len().div_ceil(WORD_SIZE);
576 let double_number_of_words: u64 = number_of_words
577 .checked_mul(2)
578 .ok_or(OutOfGas)?
579 .try_into()
580 .map_err(|_| InternalError::TypeConversion)?;
581
582 regular_gas = regular_gas
583 .checked_add(double_number_of_words)
584 .ok_or(OutOfGas)?;
585 }
586 }
587
588 let mut access_lists_cost: u64 = 0;
590 for (_, keys) in self.tx.access_list() {
591 access_lists_cost = access_lists_cost
592 .checked_add(ACCESS_LIST_ADDRESS_COST)
593 .ok_or(OutOfGas)?;
594 for _ in keys {
595 access_lists_cost = access_lists_cost
596 .checked_add(ACCESS_LIST_STORAGE_KEY_COST)
597 .ok_or(OutOfGas)?;
598 }
599 }
600
601 if fork >= Fork::Amsterdam {
606 let al_floor_tokens = floor_tokens_in_access_list(self.tx.access_list());
607 let al_data_cost = al_floor_tokens
608 .checked_mul(total_cost_floor_per_token(fork))
609 .ok_or(InternalError::Overflow)?;
610 access_lists_cost = access_lists_cost
611 .checked_add(al_data_cost)
612 .ok_or(InternalError::Overflow)?;
613 }
614
615 regular_gas = regular_gas.checked_add(access_lists_cost).ok_or(OutOfGas)?;
616
617 let amount_of_auth_tuples: u64 = match self.tx.authorization_list() {
621 None => 0,
622 Some(list) => list
623 .len()
624 .try_into()
625 .map_err(|_| InternalError::TypeConversion)?,
626 };
627
628 if fork >= Fork::Amsterdam {
629 let regular_auth_cost = PER_AUTH_BASE_COST
631 .checked_mul(amount_of_auth_tuples)
632 .ok_or(InternalError::Overflow)?;
633 regular_gas = regular_gas.checked_add(regular_auth_cost).ok_or(OutOfGas)?;
634 let state_auth_cost = self
635 .state_gas_auth_total
636 .checked_mul(amount_of_auth_tuples)
637 .ok_or(InternalError::Overflow)?;
638 state_gas = state_gas.checked_add(state_auth_cost).ok_or(OutOfGas)?;
639 } else {
640 let authorization_list_cost = PER_EMPTY_ACCOUNT_COST
641 .checked_mul(amount_of_auth_tuples)
642 .ok_or(InternalError::Overflow)?;
643 regular_gas = regular_gas
644 .checked_add(authorization_list_cost)
645 .ok_or(OutOfGas)?;
646 }
647
648 Ok(IntrinsicGas {
649 regular: regular_gas,
650 state: state_gas,
651 calldata_cost,
652 })
653 }
654
655 pub fn get_min_gas_used(&self) -> Result<u64, VMError> {
657 let fork = self.env.config.fork;
658
659 let calldata = if self.is_create()? {
661 self.current_call_frame.bytecode.code()
662 } else {
663 self.current_call_frame.calldata.as_ref()
664 };
665
666 let mut tokens_in_calldata: u64 = if fork >= Fork::Amsterdam {
670 let total_bytes: u64 = calldata
672 .len()
673 .try_into()
674 .map_err(|_| InternalError::TypeConversion)?;
675 total_bytes
676 .checked_mul(STANDARD_TOKEN_COST)
677 .ok_or(InternalError::Overflow)?
678 } else {
679 gas_cost::tx_calldata(calldata)? / STANDARD_TOKEN_COST
681 };
682
683 if fork >= Fork::Amsterdam {
687 let al_floor_tokens = floor_tokens_in_access_list(self.tx.access_list());
688 tokens_in_calldata = tokens_in_calldata
689 .checked_add(al_floor_tokens)
690 .ok_or(InternalError::Overflow)?;
691 }
692
693 let mut min_gas_used: u64 = tokens_in_calldata
696 .checked_mul(total_cost_floor_per_token(fork))
697 .ok_or(InternalError::Overflow)?;
698
699 min_gas_used = min_gas_used
700 .checked_add(TX_BASE_COST)
701 .ok_or(InternalError::Overflow)?;
702
703 Ok(min_gas_used)
704 }
705
706 pub fn get_tx_callee(
709 tx: &Transaction,
710 db: &mut GeneralizedDatabase,
711 env: &Environment,
712 substate: &mut Substate,
713 ) -> Result<(Address, bool), VMError> {
714 match tx.to() {
715 TxKind::Call(address_to) => {
716 substate.add_accessed_address(address_to);
717
718 Ok((address_to, false))
719 }
720
721 TxKind::Create => {
722 let sender_nonce = db.get_account(env.origin)?.info.nonce;
723
724 let created_address = calculate_create_address(env.origin, sender_nonce);
725
726 substate.add_accessed_address(created_address);
727 substate.add_created_account(created_address);
728
729 Ok((created_address, true))
730 }
731 }
732 }
733}
734
735pub fn intrinsic_gas_dimensions(
743 tx: &Transaction,
744 fork: Fork,
745 block_gas_limit: u64,
746) -> Result<(u64, u64), VMError> {
747 let mut regular_gas: u64 = 0;
748 let mut state_gas: u64 = 0;
749
750 let (state_gas_new_account, state_gas_auth_total) = if fork >= Fork::Amsterdam {
751 let cpsb = cost_per_state_byte(block_gas_limit);
752 (
753 STATE_BYTES_PER_NEW_ACCOUNT
754 .checked_mul(cpsb)
755 .ok_or(InternalError::Overflow)?,
756 STATE_BYTES_PER_AUTH_TOTAL
757 .checked_mul(cpsb)
758 .ok_or(InternalError::Overflow)?,
759 )
760 } else {
761 (0, 0)
762 };
763
764 let calldata_cost = gas_cost::tx_calldata(tx.data())?;
766 regular_gas = regular_gas.checked_add(calldata_cost).ok_or(OutOfGas)?;
767
768 regular_gas = regular_gas.checked_add(TX_BASE_COST).ok_or(OutOfGas)?;
770
771 let is_create = matches!(tx.to(), TxKind::Create);
772 if is_create {
773 if fork >= Fork::Amsterdam {
774 regular_gas = regular_gas
775 .checked_add(REGULAR_GAS_CREATE)
776 .ok_or(OutOfGas)?;
777 state_gas = state_gas
778 .checked_add(state_gas_new_account)
779 .ok_or(OutOfGas)?;
780 } else {
781 regular_gas = regular_gas.checked_add(CREATE_BASE_COST).ok_or(OutOfGas)?;
782 }
783
784 if fork >= Fork::Shanghai {
786 let words = tx.data().len().div_ceil(WORD_SIZE);
787 let double_words: u64 = words
788 .checked_mul(2)
789 .ok_or(OutOfGas)?
790 .try_into()
791 .map_err(|_| InternalError::TypeConversion)?;
792 regular_gas = regular_gas.checked_add(double_words).ok_or(OutOfGas)?;
793 }
794 }
795
796 let mut access_lists_cost: u64 = 0;
798 for (_, keys) in tx.access_list() {
799 access_lists_cost = access_lists_cost
800 .checked_add(ACCESS_LIST_ADDRESS_COST)
801 .ok_or(OutOfGas)?;
802 for _ in keys {
803 access_lists_cost = access_lists_cost
804 .checked_add(ACCESS_LIST_STORAGE_KEY_COST)
805 .ok_or(OutOfGas)?;
806 }
807 }
808
809 if fork >= Fork::Amsterdam {
811 let al_floor_tokens = floor_tokens_in_access_list(tx.access_list());
812 let al_data_cost = al_floor_tokens
813 .checked_mul(total_cost_floor_per_token(fork))
814 .ok_or(InternalError::Overflow)?;
815 access_lists_cost = access_lists_cost
816 .checked_add(al_data_cost)
817 .ok_or(InternalError::Overflow)?;
818 }
819 regular_gas = regular_gas.checked_add(access_lists_cost).ok_or(OutOfGas)?;
820
821 let amount_of_auth_tuples: u64 = match tx.authorization_list() {
823 None => 0,
824 Some(list) => list
825 .len()
826 .try_into()
827 .map_err(|_| InternalError::TypeConversion)?,
828 };
829
830 if fork >= Fork::Amsterdam {
831 let regular_auth_cost = PER_AUTH_BASE_COST
832 .checked_mul(amount_of_auth_tuples)
833 .ok_or(InternalError::Overflow)?;
834 regular_gas = regular_gas.checked_add(regular_auth_cost).ok_or(OutOfGas)?;
835 let state_auth_cost = state_gas_auth_total
836 .checked_mul(amount_of_auth_tuples)
837 .ok_or(InternalError::Overflow)?;
838 state_gas = state_gas.checked_add(state_auth_cost).ok_or(OutOfGas)?;
839 } else {
840 let auth_cost = PER_EMPTY_ACCOUNT_COST
841 .checked_mul(amount_of_auth_tuples)
842 .ok_or(InternalError::Overflow)?;
843 regular_gas = regular_gas.checked_add(auth_cost).ok_or(OutOfGas)?;
844 }
845
846 Ok((regular_gas, state_gas))
847}
848
849pub fn intrinsic_gas_floor(tx: &Transaction, fork: Fork) -> Result<u64, VMError> {
862 let calldata = tx.data();
865
866 let mut tokens_in_calldata: u64 = if fork >= Fork::Amsterdam {
867 let total_bytes: u64 = calldata
868 .len()
869 .try_into()
870 .map_err(|_| InternalError::TypeConversion)?;
871 total_bytes
872 .checked_mul(STANDARD_TOKEN_COST)
873 .ok_or(InternalError::Overflow)?
874 } else {
875 gas_cost::tx_calldata(calldata)? / STANDARD_TOKEN_COST
876 };
877
878 if fork >= Fork::Amsterdam {
879 let al_floor_tokens = floor_tokens_in_access_list(tx.access_list());
880 tokens_in_calldata = tokens_in_calldata
881 .checked_add(al_floor_tokens)
882 .ok_or(InternalError::Overflow)?;
883 }
884
885 tokens_in_calldata
886 .checked_mul(total_cost_floor_per_token(fork))
887 .ok_or(InternalError::Overflow)?
888 .checked_add(TX_BASE_COST)
889 .ok_or(InternalError::Overflow.into())
890}
891
892pub fn account_to_levm_account(account: Account) -> (LevmAccount, Code) {
895 (
896 LevmAccount {
897 info: account.info,
898 has_storage: !account.storage.is_empty(), storage: account.storage,
900 status: AccountStatus::Unmodified,
901 exists: true,
902 },
903 account.code,
904 )
905}
906
907#[expect(clippy::as_conversions)]
910pub fn u256_to_usize(val: U256) -> Result<usize, VMError> {
911 if val.0[0] > u32::MAX as u64 || val.0[1] != 0 || val.0[2] != 0 || val.0[3] != 0 {
912 return Err(VMError::ExceptionalHalt(ExceptionalHalt::VeryLargeNumber));
913 }
914 Ok(val.0[0] as usize)
915}
916
917pub fn size_offset_to_usize(size: U256, offset: U256) -> Result<(usize, usize), VMError> {
920 if size.is_zero() {
921 Ok((0, 0))
923 } else {
924 Ok((u256_to_usize(size)?, u256_to_usize(offset)?))
925 }
926}
927
928#[inline]
933pub fn create_eth_transfer_log(from: Address, to: Address, value: U256) -> Log {
934 let mut from_topic = [0u8; 32];
935 from_topic[12..].copy_from_slice(from.as_bytes());
936
937 let mut to_topic = [0u8; 32];
938 to_topic[12..].copy_from_slice(to.as_bytes());
939
940 let data = value.to_big_endian();
941
942 Log {
943 address: SYSTEM_ADDRESS,
944 topics: vec![
945 TRANSFER_EVENT_TOPIC,
946 H256::from(from_topic),
947 H256::from(to_topic),
948 ],
949 data: Bytes::from(data.to_vec()),
950 }
951}
952
953#[inline]
956pub fn create_burn_log(address: Address, amount: U256) -> Log {
957 let mut address_topic = [0u8; 32];
958 address_topic[12..].copy_from_slice(address.as_bytes());
959
960 let data = amount.to_big_endian();
961
962 Log {
963 address: SYSTEM_ADDRESS,
964 topics: vec![BURN_EVENT_TOPIC, H256::from(address_topic)],
965 data: Bytes::from(data.to_vec()),
966 }
967}