Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/core/src/types/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Transaction costs
pub const TX_BASE_COST: u64 = 21000;
pub const TX_DATA_COST_PER_NON_ZERO: u64 = 16;
pub const TX_DATA_COST_PER_ZERO: u64 = 4;
pub const TX_CREATE_COST: u64 = 32000;
pub const TX_ACCESS_LIST_ADDRESS_COST: u64 = 2400;
pub const TX_ACCESS_LIST_STORAGE_KEY_COST: u64 = 1900;
pub const MAX_CODE_SIZE: usize = 0x6000;
pub const GAS_INIT_CODE_WORD_COST: u64 = 2;
4 changes: 4 additions & 0 deletions crates/core/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
mod account;
mod block;
mod constants;
mod engine;
mod genesis;
mod receipt;
mod transaction;
mod utils;

pub use account::*;
pub use block::*;
pub use constants::*;
pub use engine::*;
pub use genesis::*;
pub use receipt::*;
pub use transaction::*;
pub use utils::*;
44 changes: 44 additions & 0 deletions crates/core/src/types/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ use crate::rlp::{
structs::{Decoder, Encoder},
};

use super::{
ceil32, GAS_INIT_CODE_WORD_COST, MAX_CODE_SIZE, TX_ACCESS_LIST_ADDRESS_COST, TX_BASE_COST,
TX_CREATE_COST, TX_DATA_COST_PER_NON_ZERO, TX_DATA_COST_PER_ZERO,
};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Transaction {
LegacyTransaction(LegacyTransaction),
Expand Down Expand Up @@ -442,6 +447,37 @@ fn recover_address(
Address::from_slice(&hash[12..])
}

/// Verifies a transaction.
// reference: https://github.com/ethereum/execution-specs/blob/c854868f4abf2ab0c3e8790d4c40607e0d251147/src/ethereum/shanghai/fork.py#L678
pub fn validate_transaction(tx: &Transaction) -> bool {
calculate_intrinsic_cost(tx) <= tx.gas_limit()
&& !(matches!(tx.to(), TxKind::Create) && tx.data().len() > 2 * MAX_CODE_SIZE)
}

/// Calculates the gas that is charged before execution is started.
fn calculate_intrinsic_cost(tx: &Transaction) -> u64 {
let data_cost = tx.data().iter().fold(0, |acc, byte| {
acc + if *byte == 0 {
TX_DATA_COST_PER_ZERO
} else {
TX_DATA_COST_PER_NON_ZERO
}
});
let create_cost = match tx.to() {
TxKind::Call(_) => 0,
TxKind::Create => TX_CREATE_COST + init_code_cost(tx.data().len() as u64),
};
let access_list_cost = tx.access_list().iter().fold(0, |acc, (_, keys)| {
acc + TX_ACCESS_LIST_ADDRESS_COST + keys.len() as u64 * TX_ACCESS_LIST_ADDRESS_COST
});
TX_BASE_COST + data_cost + create_cost + access_list_cost
}

/// Calculates the gas to be charged for the init code in a create transaction.
fn init_code_cost(init_code_length: u64) -> u64 {
GAS_INIT_CODE_WORD_COST * ceil32(init_code_length) / 32
}

#[cfg(test)]
mod tests {
use crate::types::{compute_receipts_root, BlockBody, Receipt};
Expand Down Expand Up @@ -552,4 +588,12 @@ mod tests {
};
assert_eq!(tx, expected_tx);
}

#[test]
fn validate_legacy_tx() {
let legacy_tx = LegacyTransaction::decode(&hex!("f86d80843baa0c4082f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee538000808360306ba0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4")).unwrap();
assert!(validate_transaction(&Transaction::LegacyTransaction(
legacy_tx
)))
}
}
9 changes: 9 additions & 0 deletions crates/core/src/types/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/// Converts a unsigned integer to the next closest multiple of 32
pub fn ceil32(n: u64) -> u64 {
let rem = n % 32;
if rem == 0 {
n
} else {
n + 32 - rem
}
}