diff --git a/Cargo.lock b/Cargo.lock index 3fececbd5..eb6c2a226 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3204,6 +3204,7 @@ dependencies = [ "eyre", "hash-db", "plain_hasher", + "reth-consensus", "reth-interfaces", "reth-primitives", "reth-rlp", @@ -3352,6 +3353,7 @@ dependencies = [ "bytes", "crc", "derive_more", + "ethbloom", "ethers-core", "hash-db", "hex", diff --git a/crates/consensus/src/config.rs b/crates/consensus/src/config.rs index 880aa8bcd..c465b0bb6 100644 --- a/crates/consensus/src/config.rs +++ b/crates/consensus/src/config.rs @@ -11,16 +11,29 @@ pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; /// Configuration for consensus #[derive(Debug, Clone)] pub struct Config { - /// EIP-1559 hard fork number + /// Spurious dragon ethereum update block. + pub spurious_dragon_hard_fork_block: BlockNumber, + /// EIP-2728 hard fork number. + pub berlin_hard_fork_block: BlockNumber, + /// EIP-1559 hard fork number. pub london_hard_fork_block: BlockNumber, - /// The Merge/Paris hard fork block number + /// The Merge/Paris hard fork block number. pub paris_hard_fork_block: BlockNumber, - /// Blockchain identifier introduced in EIP-155: Simple replay attack protection + /// Blockchain identifier introduced in EIP-155: Simple replay attack protection. pub chain_id: u64, + /// Merge terminal total dificulty after the paris hardfork got activated. + pub merge_terminal_total_difficulty: u128, } impl Default for Config { fn default() -> Self { - Self { london_hard_fork_block: 12965000, paris_hard_fork_block: 15537394, chain_id: 1 } + Self { + spurious_dragon_hard_fork_block: 2675000, + berlin_hard_fork_block: 12244000, + london_hard_fork_block: 12965000, + paris_hard_fork_block: 15537394, + merge_terminal_total_difficulty: 58750000000000000000000, + chain_id: 1, + } } } diff --git a/crates/consensus/src/consensus.rs b/crates/consensus/src/consensus.rs index 3d174df72..f568931fd 100644 --- a/crates/consensus/src/consensus.rs +++ b/crates/consensus/src/consensus.rs @@ -34,14 +34,17 @@ impl Consensus for EthConsensus { fn validate_header(&self, header: &SealedHeader, parent: &SealedHeader) -> Result<(), Error> { verification::validate_header_standalone(header, &self.config)?; - verification::validate_header_regarding_parent(parent, header, &self.config) + verification::validate_header_regarding_parent(parent, header, &self.config)?; - // TODO Consensus checks for: - // * mix_hash & nonce PoW stuf - // * extra_data + if header.number < self.config.paris_hard_fork_block { + // TODO Consensus checks for old blocks: + // * difficulty, mix_hash & nonce aka PoW stuff + // low priority as syncing is done in reverse order + } + Ok(()) } fn pre_validate_block(&self, block: &BlockLocked) -> Result<(), Error> { - verification::validate_block_standalone(block, false) + verification::validate_block_standalone(block) } } diff --git a/crates/consensus/src/verification.rs b/crates/consensus/src/verification.rs index 50e357ada..ae7854047 100644 --- a/crates/consensus/src/verification.rs +++ b/crates/consensus/src/verification.rs @@ -1,8 +1,12 @@ //! ALl functions for verification of block use crate::{config, Config}; -use reth_interfaces::{consensus::Error, provider::HeaderProvider, Result as RethResult}; +use reth_interfaces::{ + consensus::Error, + provider::{AccountProvider, HeaderProvider}, + Result as RethResult, +}; use reth_primitives::{ - Account, Address, BlockLocked, SealedHeader, Transaction, TransactionSigned, + Account, BlockLocked, BlockNumber, SealedHeader, Transaction, EMPTY_OMMER_ROOT, H256, U256, }; use std::time::SystemTime; @@ -26,39 +30,37 @@ pub fn validate_header_standalone( return Err(Error::TimestampIsInFuture { timestamp: header.timestamp, present_timestamp }) } + // From yellow papper: extraData: An arbitrary byte array containing data + // relevant to this block. This must be 32 bytes or fewer; formally Hx. + if header.extra_data.len() > 32 { + return Err(Error::ExtraDataExceedsMax { len: header.extra_data.len() }) + } + // Check if base fee is set. - if config.paris_hard_fork_block >= header.number && header.base_fee_per_gas.is_some() { + if header.number >= config.london_hard_fork_block && header.base_fee_per_gas.is_none() { return Err(Error::BaseFeeMissing) } - Ok(()) -} - -/// Validate transactions standalone -pub fn validate_transactions_standalone( - transaction: &Transaction, - config: &Config, -) -> Result<(), Error> { - let chain_id = match transaction { - Transaction::Legacy { chain_id, .. } => *chain_id, - Transaction::Eip2930 { chain_id, .. } => Some(*chain_id), - Transaction::Eip1559 { chain_id, max_fee_per_gas, max_priority_fee_per_gas, .. } => { - // EIP-1559: add more constraints to the tx validation - // https://github.com/ethereum/EIPs/pull/3594 - if max_priority_fee_per_gas > max_fee_per_gas { - return Err(Error::TransactionPriorityFeeMoreThenMaxFee) - } - Some(*chain_id) + // EIP-3675: Upgrade consensus to Proof-of-Stake: + // https://eips.ethereum.org/EIPS/eip-3675#replacing-difficulty-with-0 + if header.number >= config.paris_hard_fork_block { + if header.difficulty != U256::zero() { + return Err(Error::TheMergeDifficultyIsNotZero) } - }; - if let Some(chain_id) = chain_id { - if chain_id != config.chain_id { - return Err(Error::TransactionChainId) + + if header.nonce != 0 { + return Err(Error::TheMergeNonceIsNotZero) + } + + if header.ommers_hash != EMPTY_OMMER_ROOT { + return Err(Error::TheMergeOmmerRootIsNotEmpty) + } + + if header.mix_hash != H256::zero() { + return Err(Error::TheMergeMixHashIsNotZero) } } - // signature validation? - Ok(()) } @@ -67,12 +69,49 @@ pub fn validate_transactions_standalone( /// The only parameter from the header that affects the transaction is `base_fee`. pub fn validate_transaction_regarding_header( transaction: &Transaction, + config: &Config, + at_block_number: BlockNumber, base_fee: Option, ) -> Result<(), Error> { - // check base fee and few checks that are related to that. + let chain_id = match transaction { + Transaction::Legacy { chain_id, .. } => { + // EIP-155: Simple replay attack protection: https://eips.ethereum.org/EIPS/eip-155 + if config.spurious_dragon_hard_fork_block <= at_block_number && chain_id.is_some() { + return Err(Error::TransactionOldLegacyChainId) + } + *chain_id + } + Transaction::Eip2930 { chain_id, .. } => { + // EIP-2930: Optional access lists: https://eips.ethereum.org/EIPS/eip-2930 (New transaction type) + if config.berlin_hard_fork_block > at_block_number { + return Err(Error::TransactionEip2930Disabled) + } + Some(*chain_id) + } + Transaction::Eip1559 { chain_id, max_fee_per_gas, max_priority_fee_per_gas, .. } => { + // EIP-1559: Fee market change for ETH 1.0 chain https://eips.ethereum.org/EIPS/eip-1559 + if config.berlin_hard_fork_block > at_block_number { + return Err(Error::TransactionEip1559Disabled) + } + + // EIP-1559: add more constraints to the tx validation + // https://github.com/ethereum/EIPs/pull/3594 + if max_priority_fee_per_gas > max_fee_per_gas { + return Err(Error::TransactionPriorityFeeMoreThenMaxFee) + } + + Some(*chain_id) + } + }; + if let Some(chain_id) = chain_id { + if chain_id != config.chain_id { + return Err(Error::TransactionChainId) + } + } + // Check basefee and few checks that are related to that. // https://github.com/ethereum/EIPs/pull/3594 if let Some(base_fee_per_gas) = base_fee { - if transaction.max_fee_per_gas() < base_fee_per_gas { + if transaction.max_fee_per_gas() < base_fee_per_gas as u128 { return Err(Error::TransactionMaxFeeLessThenBaseFee) } } @@ -80,38 +119,43 @@ pub fn validate_transaction_regarding_header( Ok(()) } -/// Account provider -pub trait AccountProvider { - /// Get basic account information. - fn basic_account(&self, address: Address) -> reth_interfaces::Result>; -} - /// Validate transaction in regards of State -pub fn validate_transaction_regarding_state( - _transaction: &TransactionSigned, - _config: &Config, - _account_provider: &AP, -) -> Result<(), Error> { - // sanity check: if account has a bytecode. This is not allowed.s - // check nonce - // gas_price*gas_limit+value < account.balance +pub fn validate_transaction_regarding_account( + transaction: &Transaction, + account: &Account, +) -> reth_interfaces::Result<()> { + // Signer account shoudn't have bytecode. Presence of bytecode means this is a smartcontract. + if account.has_bytecode() { + return Err(Error::SignerAccountHasBytecode.into()) + } + + let (nonce, gas_price, gas_limit, value) = match transaction { + Transaction::Legacy { nonce, gas_price, gas_limit, value, .. } => { + (nonce, gas_price, gas_limit, value) + } + Transaction::Eip2930 { nonce, gas_price, gas_limit, value, .. } => { + (nonce, gas_price, gas_limit, value) + } + Transaction::Eip1559 { nonce, gas_limit, max_fee_per_gas, value, .. } => { + (nonce, max_fee_per_gas, gas_limit, value) + } + }; + + // check nonce + if account.nonce + 1 != *nonce { + return Err(Error::TransactionNonceNotConsistent.into()) + } + + // max fee that transaction can potentially spend + let max_fee = (*gas_limit as u128).saturating_mul(*gas_price).saturating_add(*value); + + // check if account balance has enought to cover worst case + if max_fee > account.balance.as_u128() { + return Err( + Error::InsufficientFunds { max_fee, available_funds: account.balance.as_u128() }.into() + ) + } - // let max_gas_cost = U512::from(message.gas_limit()) - // * U512::from(ethereum_types::U256::from(message.max_fee_per_gas().to_be_bytes())); - // // See YP, Eq (57) in Section 6.2 "Execution" - // let v0 = max_gas_cost + - // U512::from(ethereum_types::U256::from(message.value().to_be_bytes())); - // let available_balance = - // ethereum_types::U256::from(self.state.get_balance(sender)?.to_be_bytes()).into(); - // if available_balance < v0 { - // return Err(TransactionValidationError::Validation( - // BadTransactionError::InsufficientFunds { - // account: sender, - // available: available_balance, - // required: v0, - // }, - // )); - // } Ok(()) } @@ -121,10 +165,7 @@ pub fn validate_transaction_regarding_state( /// - Compares the transactions root in the block header to the block body /// - Pre-execution transaction validation /// - (Optionally) Compares the receipts root in the block header to the block body -pub fn validate_block_standalone( - block: &BlockLocked, - validate_receipts: bool, -) -> Result<(), Error> { +pub fn validate_block_standalone(block: &BlockLocked) -> Result<(), Error> { // Check ommers hash // TODO(onbjerg): This should probably be accessible directly on [Block] let ommers_hash = @@ -146,29 +187,6 @@ pub fn validate_block_standalone( }) } - // TODO: transaction verification,maybe make it configurable as in check only - // signatures/limits/types - // Things to probably check: - // - Chain ID - // - Base fee per gas (if applicable) - // - Max priority fee per gas (if applicable) - - // TODO: Check if all transaction gas total does not go over block limit - - // Check receipts root - // TODO(onbjerg): This should probably be accessible directly on [Block] - // NOTE(onbjerg): Pre-validation does not validate the receipts root since we do not have the - // receipts yet (this validation is before execution). Maybe this should not be in here? - if validate_receipts { - let receipts_root = reth_primitives::proofs::calculate_receipt_root(block.receipts.iter()); - if block.header.receipts_root != receipts_root { - return Err(Error::BodyReceiptsRootDiff { - got: receipts_root, - expected: block.header.receipts_root, - }) - } - } - Ok(()) } @@ -298,22 +316,40 @@ pub fn validate_block_regarding_chain( } /// Full validation of block before execution. -pub fn full_validation( +pub fn full_validation( block: &BlockLocked, - provider: PROV, + provider: Provider, config: &Config, ) -> RethResult<()> { validate_header_standalone(&block.header, config)?; - validate_block_standalone(block, true)?; + validate_block_standalone(block)?; let parent = validate_block_regarding_chain(block, &provider)?; validate_header_regarding_parent(&parent, &block.header, config)?; + + for transaction in block.body.iter() { + validate_transaction_regarding_header( + transaction, + config, + block.header.number, + block.header.base_fee_per_gas, + )?; + + // NOTE: depending on the need of the stages, recovery could be done in different place. + let recovered = + transaction.try_ecrecovered().ok_or(Error::TransactionSignerRecoveryError)?; + + let account = + provider.basic_account(recovered.signer())?.ok_or(Error::SignerAccountNotExisting)?; + + validate_transaction_regarding_account(transaction, &account)?; + } Ok(()) } #[cfg(test)] mod tests { use reth_interfaces::Result; - use reth_primitives::{hex_literal::hex, BlockHash, Header}; + use reth_primitives::{hex_literal::hex, Address, BlockHash, Header}; use super::*; @@ -347,16 +383,23 @@ mod tests { struct Provider { is_known: bool, parent: Option
, + account: Option, } impl Provider { /// New provider with parent fn new(parent: Option
) -> Self { - Self { is_known: false, parent } + Self { is_known: false, parent, account: None } } /// New provider where is_known is always true fn new_known() -> Self { - Self { is_known: true, parent: None } + Self { is_known: true, parent: None, account: None } + } + } + + impl AccountProvider for Provider { + fn basic_account(&self, _address: Address) -> Result> { + Ok(self.account) } } @@ -388,7 +431,7 @@ mod tests { gas_used: 0x6e813, timestamp: 0x635f9657, extra_data: hex!("")[..].into(), - mix_hash: hex!("f8c29910a0a2fd65b260d83ffa2547a6db279095d109a6e64527d14035263cfc").into(), + mix_hash: hex!("0000000000000000000000000000000000000000000000000000000000000000").into(), nonce: 0x0000000000000000, base_fee_per_gas: 0x28f0001df.into(), }; @@ -401,10 +444,9 @@ mod tests { parent.number -= 1; let ommers = Vec::new(); - let receipts = Vec::new(); let body = Vec::new(); - (BlockLocked { header: header.seal(), body, receipts, ommers }, parent) + (BlockLocked { header: header.seal(), body, ommers }, parent) } #[test] diff --git a/crates/db/src/kv/mod.rs b/crates/db/src/kv/mod.rs index a5600702e..9333989f6 100644 --- a/crates/db/src/kv/mod.rs +++ b/crates/db/src/kv/mod.rs @@ -214,7 +214,7 @@ mod tests { let value = Account { nonce: 18446744073709551615, - bytecode_hash: H256::random(), + bytecode_hash: Some(H256::random()), balance: U256::max_value(), }; let key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047") diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml index b811b8a81..b1c3d8714 100644 --- a/crates/executor/Cargo.toml +++ b/crates/executor/Cargo.toml @@ -10,7 +10,8 @@ readme = "README.md" # reth reth-primitives = { path = "../primitives" } reth-interfaces = { path = "../interfaces" } -reth-rlp = {path = "../common/rlp"} +reth-rlp = { path = "../common/rlp" } +reth-consensus = { path = "../consensus" } revm = "2.1" @@ -27,4 +28,4 @@ hash-db = "0.15" # todo replace with faster rlp impl rlp = { version = "0.5", default-features = false } # replace with tiny-keccak (it is faster hasher) -sha3 = { version = "0.10", default-features = false } \ No newline at end of file +sha3 = { version = "0.10", default-features = false } diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index e019c1a40..3a6da8727 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -6,8 +6,8 @@ use reth_interfaces::{ executor::{BlockExecutor, Error}, provider::StateProvider, }; -use reth_primitives::BlockLocked; -use revm::{AnalysisKind, SpecId, EVM}; +use reth_primitives::{bloom::logs_bloom, BlockLocked, Bloom, Log, Receipt}; +use revm::{AnalysisKind, ExecutionResult, SpecId, EVM}; /// Main block executor pub struct Executor { @@ -33,29 +33,82 @@ impl Executor { evm.env.cfg.perf_analyse_created_bytecodes = AnalysisKind::Raw; revm_wrap::fill_block_env(&mut evm.env.block, block); + let mut cumulative_gas_used = 0; + let mut receipts = Vec::with_capacity(block.body.len()); for transaction in block.body.iter() { - // TODO Check if Transaction is new - revm_wrap::fill_tx_env(&mut evm.env.tx, transaction.as_ref()); - - let res = evm.transact_commit(); - - if res.exit_reason == revm::Return::FatalExternalError { - // stop executing. Fatal error thrown from database + // The sum of the transaction’s gas limit, Tg, and the gas utilised in this block prior, + // must be no greater than the block’s gasLimit. + let block_available_gas = block.gas_limit - cumulative_gas_used; + if transaction.gas_limit() > block_available_gas { + return Err(Error::TransactionGasLimitMoreThenAvailableBlockGas { + transaction_gas_limit: transaction.gas_limit(), + block_available_gas, + }) } - // calculate commulative gas used + // Fill revm structure. + revm_wrap::fill_tx_env(&mut evm.env.tx, transaction.as_ref()); - // create receipt - // bloom filter from logs + // Execute transaction. + let ExecutionResult { exit_reason, gas_used, logs, .. } = evm.transact_commit(); - // Sum of the transaction’s gas limit and the gas utilized in this block prior + // Fatal internal error. + if exit_reason == revm::Return::FatalExternalError { + return Err(Error::ExecutionFatalError) + } - // Receipt outcome EIP-658: Embedding transaction status code in receipts - // EIP-658 supperseeded EIP-98 in Byzantium fork + // Success flag was added in `EIP-658: Embedding transaction status code in receipts`. + let is_success = matches!( + exit_reason, + revm::Return::Continue | + revm::Return::Stop | + revm::Return::Return | + revm::Return::SelfDestruct + ); + + // Add spend gas. + cumulative_gas_used += gas_used; + + // Transform logs to reth format. + let logs: Vec = logs + .into_iter() + .map(|l| Log { address: l.address, topics: l.topics, data: l.data }) + .collect(); + + // Push receipts for calculating header bloom filter. + receipts.push(Receipt { + tx_type: transaction.tx_type(), + success: is_success, + cumulative_gas_used, + bloom: logs_bloom(logs.iter()), // TODO + logs, + }); } - Err(Error::VerificationFailed) + // TODO do state root. + + // Check if gas used matches the value set in header. + if block.gas_used != cumulative_gas_used { + return Err(Error::BlockGasUsed { got: cumulative_gas_used, expected: block.gas_used }) + } + + // Check receipts root. + let receipts_root = reth_primitives::proofs::calculate_receipt_root(receipts.iter()); + if block.receipts_root != receipts_root { + return Err(Error::ReceiptRootDiff { got: receipts_root, expected: block.receipts_root }) + } + + // Create header log bloom. + let expected_logs_bloom = receipts.iter().fold(Bloom::zero(), |bloom, r| bloom | r.bloom); + if expected_logs_bloom != block.logs_bloom { + return Err(Error::BloomLogDiff { + expected: Box::new(block.logs_bloom), + got: Box::new(expected_logs_bloom), + }) + } + + Ok(()) } } diff --git a/crates/executor/src/revm_wrap.rs b/crates/executor/src/revm_wrap.rs index a441e1f93..34aa9644b 100644 --- a/crates/executor/src/revm_wrap.rs +++ b/crates/executor/src/revm_wrap.rs @@ -1,5 +1,5 @@ use reth_interfaces::{provider::StateProvider, Error}; -use reth_primitives::{BlockLocked, Transaction, TransactionKind, H160, H256, U256}; +use reth_primitives::{BlockLocked, Transaction, TransactionKind, H160, H256, KECCAK_EMPTY, U256}; use revm::{ db::{CacheDB, DatabaseRef}, BlockEnv, TransactTo, TxEnv, @@ -40,7 +40,7 @@ impl DatabaseRef for State { Ok(self.0.basic_account(address)?.map(|account| revm::AccountInfo { balance: account.balance, nonce: account.nonce, - code_hash: account.bytecode_hash, + code_hash: account.bytecode_hash.unwrap_or(KECCAK_EMPTY), code: None, })) } @@ -83,7 +83,7 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) { TransactionKind::Call(to) => TransactTo::Call(*to), TransactionKind::Create => TransactTo::create(), }; - tx_env.value = *value; + tx_env.value = (*value).into(); tx_env.data = input.0.clone(); tx_env.chain_id = *chain_id; tx_env.nonce = Some(*nonce); @@ -105,7 +105,7 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) { TransactionKind::Call(to) => TransactTo::Call(*to), TransactionKind::Create => TransactTo::create(), }; - tx_env.value = *value; + tx_env.value = (*value).into(); tx_env.data = input.0.clone(); tx_env.chain_id = Some(*chain_id); tx_env.nonce = Some(*nonce); @@ -138,7 +138,7 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) { TransactionKind::Call(to) => TransactTo::Call(*to), TransactionKind::Create => TransactTo::create(), }; - tx_env.value = *value; + tx_env.value = (*value).into(); tx_env.data = input.0.clone(); tx_env.chain_id = Some(*chain_id); tx_env.nonce = Some(*nonce); @@ -155,3 +155,15 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) { } } } + +/// Check equality between [`reth_primitives::Log`] and [`revm::Log`] +pub fn is_log_equal(revm_log: &revm::Log, reth_log: &reth_primitives::Log) -> bool { + revm_log.topics.len() == reth_log.topics.len() && + revm_log.address == reth_log.address && + revm_log.data == reth_log.data && + !revm_log + .topics + .iter() + .zip(reth_log.topics.iter()) + .any(|(revm_topic, reth_topic)| revm_topic != reth_topic) +} diff --git a/crates/interfaces/src/consensus.rs b/crates/interfaces/src/consensus.rs index 0e98c015d..89b25825d 100644 --- a/crates/interfaces/src/consensus.rs +++ b/crates/interfaces/src/consensus.rs @@ -31,38 +31,70 @@ pub trait Consensus: Send + Sync { #[allow(missing_docs)] #[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] pub enum Error { - #[error("Block used gas ({gas_used:?}) is greater then gas limit ({gas_limit:?})")] + #[error("Block used gas ({gas_used:?}) is greater then gas limit ({gas_limit:?}).")] HeaderGasUsedExceedsGasLimit { gas_used: u64, gas_limit: u64 }, #[error("Block ommer hash ({got:?}) is different then expected: ({expected:?})")] BodyOmmersHashDiff { got: H256, expected: H256 }, #[error("Block transaction root ({got:?}) is different then expected: ({expected:?})")] BodyTransactionRootDiff { got: H256, expected: H256 }, - #[error("Block receipts root ({got:?}) is different then expected: ({expected:?})")] + #[error("Block receipts root ({got:?}) is different then expected: ({expected:?}).")] BodyReceiptsRootDiff { got: H256, expected: H256 }, - #[error("Block with [hash:{hash:?},number: {number:}] is already known")] + #[error("Block with [hash:{hash:?},number: {number:}] is already known.")] BlockKnown { hash: BlockHash, number: BlockNumber }, - #[error("Block parent [hash:{hash:?}] is not known")] + #[error("Block parent [hash:{hash:?}] is not known.")] ParentUnknown { hash: BlockHash }, #[error("Block number {block_number:?} is mismatch with parent block number {parent_block_number:?}")] ParentBlockNumberMismatch { parent_block_number: BlockNumber, block_number: BlockNumber }, #[error( - "Block timestamp {timestamp:?} is in past in comparison with parent timestamp {parent_timestamp:?}" + "Block timestamp {timestamp:?} is in past in comparison with parent timestamp {parent_timestamp:?}." )] TimestampIsInPast { parent_timestamp: u64, timestamp: u64 }, - #[error("Block timestamp {timestamp:?} is in future in comparison of our clock time {present_timestamp:?}")] + #[error("Block timestamp {timestamp:?} is in future in comparison of our clock time {present_timestamp:?}.")] TimestampIsInFuture { timestamp: u64, present_timestamp: u64 }, - #[error("Child gas_limit {child_gas_limit:?} max increase is {parent_gas_limit}/1024")] + #[error("Child gas_limit {child_gas_limit:?} max increase is {parent_gas_limit:?}/1024.")] GasLimitInvalidIncrease { parent_gas_limit: u64, child_gas_limit: u64 }, - #[error("Child gas_limit {child_gas_limit:?} max decrease is {parent_gas_limit}/1024")] + #[error("Child gas_limit {child_gas_limit:?} max decrease is {parent_gas_limit:?}/1024.")] GasLimitInvalidDecrease { parent_gas_limit: u64, child_gas_limit: u64 }, - #[error("Base fee missing")] + #[error("Base fee missing.")] BaseFeeMissing, - #[error("Block base fee ({got:?}) is different then expected: ({expected:?})")] + #[error("Block base fee ({got:?}) is different then expected: ({expected:?}).")] BaseFeeDiff { expected: u64, got: u64 }, - #[error("Transaction eip1559 priority fee is more then max fee")] + #[error("Transaction eip1559 priority fee is more then max fee.")] TransactionPriorityFeeMoreThenMaxFee, - #[error("Transaction chain_id does not match")] + #[error("Transaction chain_id does not match.")] TransactionChainId, - #[error("Transation max fee is less them block base fee")] + #[error("Transation max fee is less them block base fee.")] TransactionMaxFeeLessThenBaseFee, + #[error("Transaction signer does not have account.")] + SignerAccountNotExisting, + #[error("Transaction signer has bytecode set.")] + SignerAccountHasBytecode, + #[error("Transaction nonce is not consistent.")] + TransactionNonceNotConsistent, + #[error("Account does not have enough funds ({available_funds:?}) to cover transaction max fee: {max_fee:?}.")] + InsufficientFunds { max_fee: u128, available_funds: u128 }, + #[error("Eip2930 transaction is enabled after berlin hardfork.")] + TransactionEip2930Disabled, + #[error("Old legacy transaction before Spurious Dragon should not have chain_id.")] + TransactionOldLegacyChainId, + #[error("Eip2930 transaction is enabled after london hardfork.")] + TransactionEip1559Disabled, + #[error("Transaction signer recovery error.")] + TransactionSignerRecoveryError, + #[error( + "Transaction count {transaction_count} is different from receipt count {receipt_count}" + )] + TransactionReceiptCountDiff { transaction_count: usize, receipt_count: usize }, + #[error("Transaction had receipt of different type")] + TransactionTypeReceiptTypeDiff, + #[error("Extra data {len} exceeds max length: ")] + ExtraDataExceedsMax { len: usize }, + #[error("Difficulty after merge is not zero")] + TheMergeDifficultyIsNotZero, + #[error("Nonce after merge is not zero")] + TheMergeNonceIsNotZero, + #[error("Ommer root after merge is not empty")] + TheMergeOmmerRootIsNotEmpty, + #[error("Mix hash after merge is not zero")] + TheMergeMixHashIsNotZero, } diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index 0ffa1de85..4741bd610 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use reth_primitives::Block; +use reth_primitives::{Block, Bloom, H256}; use thiserror::Error; /// Takes block and executes it, returns error @@ -12,9 +12,30 @@ pub trait BlockExecutor { } /// BlockExecutor Errors +#[allow(missing_docs)] #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum Error { - /// Example of error #[error("Example of error.")] VerificationFailed, + #[error("Fatal internal error")] + ExecutionFatalError, + #[error("Receipt cumulative gas used {got:?} is different from expected {expected:?}")] + ReceiptCumulativeGasUsedDiff { got: u64, expected: u64 }, + #[error("Receipt log count {got:?} is different from expected {expected:?}.")] + ReceiptLogCountDiff { got: usize, expected: usize }, + #[error("Receipt log is different.")] + ReceiptLogDiff, + #[error("Receipt log is different.")] + ExecutionSuccessDiff { got: bool, expected: bool }, + #[error("Receipt root {got:?} is different then expected {expected:?}.")] + ReceiptRootDiff { got: H256, expected: H256 }, + #[error("Header bloom filter {got:?} is different then expected {expected:?}.")] + BloomLogDiff { got: Box, expected: Box }, + #[error("Transaction gas limit {transaction_gas_limit} is more then blocks available gas {block_available_gas}")] + TransactionGasLimitMoreThenAvailableBlockGas { + transaction_gas_limit: u64, + block_available_gas: u64, + }, + #[error("Block gas used {got} is different from expected gas used {expected}.")] + BlockGasUsed { got: u64, expected: u64 }, } diff --git a/crates/interfaces/src/provider/db_provider/storage.rs b/crates/interfaces/src/provider/db_provider/storage.rs index 13448ac77..2dbc59aa2 100644 --- a/crates/interfaces/src/provider/db_provider/storage.rs +++ b/crates/interfaces/src/provider/db_provider/storage.rs @@ -1,7 +1,7 @@ use super::ProviderImpl; use crate::{ db::{tables, Database, DatabaseGAT, DbCursorRO, DbDupCursorRO, DbTx}, - provider::{Error, StateProvider, StateProviderFactory}, + provider::{AccountProvider, Error, StateProvider, StateProviderFactory}, Result, }; use reth_primitives::{ @@ -66,6 +66,14 @@ impl<'a, TX: DbTx<'a>> StateProviderImplHistory<'a, TX> { } } +impl<'a, TX: DbTx<'a>> AccountProvider for StateProviderImplHistory<'a, TX> { + /// Get basic account information. + fn basic_account(&self, _address: Address) -> Result> { + // TODO add when AccountHistory is defined + Ok(None) + } +} + impl<'a, TX: DbTx<'a>> StateProvider for StateProviderImplHistory<'a, TX> { /// Get storage. fn storage(&self, account: Address, storage_key: StorageKey) -> Result> { @@ -95,12 +103,6 @@ impl<'a, TX: DbTx<'a>> StateProvider for StateProviderImplHistory<'a, TX> { Ok(None) } - /// Get basic account information. - fn basic_account(&self, _address: Address) -> Result> { - // TODO add when AccountHistory is defined - Ok(None) - } - /// Get account code by its hash fn bytecode_by_hash(&self, code_hash: H256) -> Result> { self.db.get::(code_hash).map_err(Into::into).map(|r| r.map(Bytes::from)) @@ -127,6 +129,13 @@ impl<'a, TX: DbTx<'a>> StateProviderImplLatest<'a, TX> { } } +impl<'a, TX: DbTx<'a>> AccountProvider for StateProviderImplLatest<'a, TX> { + /// Get basic account information. + fn basic_account(&self, address: Address) -> Result> { + self.db.get::(address).map_err(Into::into) + } +} + impl<'a, TX: DbTx<'a>> StateProvider for StateProviderImplLatest<'a, TX> { /// Get storage. fn storage(&self, account: Address, storage_key: StorageKey) -> Result> { @@ -145,11 +154,6 @@ impl<'a, TX: DbTx<'a>> StateProvider for StateProviderImplLatest<'a, TX> { Ok(None) } - /// Get basic account information. - fn basic_account(&self, address: Address) -> Result> { - self.db.get::(address).map_err(Into::into) - } - /// Get account code by its hash fn bytecode_by_hash(&self, code_hash: H256) -> Result> { self.db.get::(code_hash).map_err(Into::into).map(|r| r.map(Bytes::from)) diff --git a/crates/interfaces/src/provider/mod.rs b/crates/interfaces/src/provider/mod.rs index 06504fa8c..3cd6791e4 100644 --- a/crates/interfaces/src/provider/mod.rs +++ b/crates/interfaces/src/provider/mod.rs @@ -1,9 +1,9 @@ mod block; pub mod db_provider; mod error; -mod storage; +mod state; pub use block::{BlockProvider, ChainInfo, HeaderProvider}; pub use db_provider::{self as db, ProviderImpl}; pub use error::Error; -pub use storage::{StateProvider, StateProviderFactory, StorageProvider}; +pub use state::{AccountProvider, StateProvider, StateProviderFactory}; diff --git a/crates/interfaces/src/provider/storage.rs b/crates/interfaces/src/provider/state.rs similarity index 74% rename from crates/interfaces/src/provider/storage.rs rename to crates/interfaces/src/provider/state.rs index ab2ad5513..e69893101 100644 --- a/crates/interfaces/src/provider/storage.rs +++ b/crates/interfaces/src/provider/state.rs @@ -1,23 +1,19 @@ use crate::Result; use reth_primitives::{ - rpc::BlockId, Account, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, H256, - U256, + Account, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, H256, U256, }; -/// Provides access to storage data -pub trait StorageProvider: Send + Sync { - /// Returns the value from a storage position at a given address and `BlockId` - fn storage_at(&self, address: Address, index: U256, at: BlockId) -> Result>; +/// Account provider +pub trait AccountProvider: Send + Sync { + /// Get basic account information. + fn basic_account(&self, address: Address) -> Result>; } /// Function needed for executor. -pub trait StateProvider: Send + Sync { +pub trait StateProvider: AccountProvider + Send + Sync { /// Get storage. fn storage(&self, account: Address, storage_key: StorageKey) -> Result>; - /// Get basic account information. - fn basic_account(&self, address: Address) -> Result>; - /// Get account code by its hash fn bytecode_by_hash(&self, code_hash: H256) -> Result>; diff --git a/crates/interfaces/src/test_utils/generators.rs b/crates/interfaces/src/test_utils/generators.rs index c8c8489f8..f2105fa26 100644 --- a/crates/interfaces/src/test_utils/generators.rs +++ b/crates/interfaces/src/test_utils/generators.rs @@ -120,7 +120,6 @@ pub fn random_block(number: u64, parent: Option) -> BlockLocked { .seal(), body: transactions, ommers: ommers.into_iter().map(|ommer| ommer.seal()).collect(), - ..Default::default() } } diff --git a/crates/net/eth-wire/src/types/blocks.rs b/crates/net/eth-wire/src/types/blocks.rs index b5b00a6cc..f04ab0360 100644 --- a/crates/net/eth-wire/src/types/blocks.rs +++ b/crates/net/eth-wire/src/types/blocks.rs @@ -344,7 +344,7 @@ mod test { TransactionSigned::from_transaction_and_signature(Transaction::Legacy { chain_id: Some(1), nonce: 0x8u64, - gas_price: 0x4a817c808u64, + gas_price: 0x4a817c808, gas_limit: 0x2e248u64, to: TransactionKind::Call(hex!("3535353535353535353535353535353535353535").into()), @@ -362,7 +362,7 @@ mod test { TransactionSigned::from_transaction_and_signature(Transaction::Legacy { chain_id: Some(1), nonce: 0x9u64, - gas_price: 0x4a817c809u64, + gas_price: 0x4a817c809, gas_limit: 0x33450u64, to: TransactionKind::Call(hex!("3535353535353535353535353535353535353535").into()), @@ -425,7 +425,7 @@ mod test { TransactionSigned::from_transaction_and_signature(Transaction::Legacy { chain_id: Some(1), nonce: 0x8u64, - gas_price: 0x4a817c808u64, + gas_price: 0x4a817c808, gas_limit: 0x2e248u64, to: TransactionKind::Call(hex!("3535353535353535353535353535353535353535").into()), @@ -443,7 +443,7 @@ mod test { TransactionSigned::from_transaction_and_signature(Transaction::Legacy { chain_id: Some(1), nonce: 0x9u64, - gas_price: 0x4a817c809u64, + gas_price: 0x4a817c809, gas_limit: 0x33450u64, to: TransactionKind::Call(hex!("3535353535353535353535353535353535353535").into()), diff --git a/crates/net/eth-wire/src/types/transactions.rs b/crates/net/eth-wire/src/types/transactions.rs index 14d65b744..e88997f32 100644 --- a/crates/net/eth-wire/src/types/transactions.rs +++ b/crates/net/eth-wire/src/types/transactions.rs @@ -145,7 +145,7 @@ mod test { Transaction::Legacy { chain_id: Some(1), nonce: 0x8u64, - gas_price: 0x4a817c808u64, + gas_price: 0x4a817c808, gas_limit: 0x2e248u64, to: TransactionKind::Call( hex!("3535353535353535353535353535353535353535").into(), @@ -169,7 +169,7 @@ mod test { Transaction::Legacy { chain_id: Some(1), nonce: 0x09u64, - gas_price: 0x4a817c809u64, + gas_price: 0x4a817c809, gas_limit: 0x33450u64, to: TransactionKind::Call( hex!("3535353535353535353535353535353535353535").into(), @@ -207,7 +207,7 @@ mod test { Transaction::Legacy { chain_id: Some(1), nonce: 0x8u64, - gas_price: 0x4a817c808u64, + gas_price: 0x4a817c808, gas_limit: 0x2e248u64, to: TransactionKind::Call( hex!("3535353535353535353535353535353535353535").into(), @@ -231,7 +231,7 @@ mod test { Transaction::Legacy { chain_id: Some(1), nonce: 0x09u64, - gas_price: 0x4a817c809u64, + gas_price: 0x4a817c809, gas_limit: 0x33450u64, to: TransactionKind::Call( hex!("3535353535353535353535353535353535353535").into(), @@ -272,7 +272,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 15u64, - gas_price: 2200000000u64, + gas_price: 2200000000, gas_limit: 34811u64, to: TransactionKind::Call( hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into(), @@ -296,8 +296,8 @@ mod test { Transaction::Eip1559 { chain_id: 4, nonce: 26u64, - max_priority_fee_per_gas: 1500000000u64, - max_fee_per_gas: 1500000013u64, + max_priority_fee_per_gas: 1500000000, + max_fee_per_gas: 1500000013, gas_limit: 21000u64, to: TransactionKind::Call( hex!("61815774383099e24810ab832a5b2a5425c154d5").into(), @@ -322,7 +322,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 3u64, - gas_price: 2000000000u64, + gas_price: 2000000000, gas_limit: 10000000u64, to: TransactionKind::Call( hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into(), @@ -346,7 +346,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 1u64, - gas_price: 1000000000u64, + gas_price: 1000000000, gas_limit: 100000u64, to: TransactionKind::Call( hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into(), @@ -370,7 +370,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 2u64, - gas_price: 1000000000u64, + gas_price: 1000000000, gas_limit: 100000u64, to: TransactionKind::Call( hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into(), @@ -415,7 +415,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 15u64, - gas_price: 2200000000u64, + gas_price: 2200000000, gas_limit: 34811u64, to: TransactionKind::Call( hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into(), @@ -439,8 +439,8 @@ mod test { Transaction::Eip1559 { chain_id: 4, nonce: 26u64, - max_priority_fee_per_gas: 1500000000u64, - max_fee_per_gas: 1500000013u64, + max_priority_fee_per_gas: 1500000000, + max_fee_per_gas: 1500000013, gas_limit: 21000u64, to: TransactionKind::Call( hex!("61815774383099e24810ab832a5b2a5425c154d5").into(), @@ -465,7 +465,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 3u64, - gas_price: 2000000000u64, + gas_price: 2000000000, gas_limit: 10000000u64, to: TransactionKind::Call( hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into(), @@ -489,7 +489,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 1u64, - gas_price: 1000000000u64, + gas_price: 1000000000, gas_limit: 100000u64, to: TransactionKind::Call( hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into(), @@ -513,7 +513,7 @@ mod test { Transaction::Legacy { chain_id: Some(4), nonce: 2u64, - gas_price: 1000000000u64, + gas_price: 1000000000, gas_limit: 100000u64, to: TransactionKind::Call( hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into(), diff --git a/crates/net/rpc/src/eth/eth_server.rs b/crates/net/rpc/src/eth/eth_server.rs index f5f9f7e1a..474496327 100644 --- a/crates/net/rpc/src/eth/eth_server.rs +++ b/crates/net/rpc/src/eth/eth_server.rs @@ -3,7 +3,7 @@ use crate::{eth::EthApi, result::ToRpcResult}; use jsonrpsee::core::RpcResult as Result; -use reth_interfaces::provider::{BlockProvider, StorageProvider}; +use reth_interfaces::provider::{BlockProvider, StateProviderFactory}; use reth_primitives::{ rpc::{transaction::eip2930::AccessListWithGasUsed, BlockId}, Address, BlockNumber, Bytes, Transaction, H256, H64, U256, U64, @@ -20,7 +20,7 @@ use serde_json::Value; impl EthApiServer for EthApi where Pool: TransactionPool + Clone + 'static, - Client: BlockProvider + StorageProvider + 'static, + Client: BlockProvider + StateProviderFactory + 'static, { fn protocol_version(&self) -> Result { Ok(self.protocol_version()) diff --git a/crates/net/rpc/src/eth/mod.rs b/crates/net/rpc/src/eth/mod.rs index b77d623dd..7e06a30c2 100644 --- a/crates/net/rpc/src/eth/mod.rs +++ b/crates/net/rpc/src/eth/mod.rs @@ -1,7 +1,7 @@ //! Provides everything related to `eth_` namespace use reth_interfaces::{ - provider::{BlockProvider, StorageProvider}, + provider::{BlockProvider, StateProviderFactory}, Result, }; use reth_primitives::{Transaction, U256, U64}; @@ -27,7 +27,7 @@ pub struct EthApi { impl EthApi where Pool: TransactionPool + Clone, - Client: BlockProvider + StorageProvider, + Client: BlockProvider + StateProviderFactory, { /// Creates a new, shareable instance. pub fn new(client: Arc, pool: Pool) -> Self { diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 1406750d3..7bfc359d0 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -9,16 +9,24 @@ description = "Commonly used types in reth." [dependencies] # reth -reth-rlp = { path = "../common/rlp", features = ["std", "derive", "ethereum-types"]} +reth-rlp = { path = "../common/rlp", features = [ + "std", + "derive", + "ethereum-types", +] } reth-codecs = { version = "0.1.0", path = "../codecs" } # ethereum ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false } parity-scale-codec = { version = "3.2.1", features = ["derive", "bytes"] } tiny-keccak = { version = "2.0", features = ["keccak"] } +ethbloom = { version = "0.12", features = ["codec"] } # crypto -secp256k1 = { version = "0.24.0", default-features = false, features = ["alloc", "recovery"] } +secp256k1 = { version = "0.24.0", default-features = false, features = [ + "alloc", + "recovery", +] } #used for forkid crc = "1" @@ -29,7 +37,7 @@ bytes = "1.2" serde = "1.0" thiserror = "1" sucds = "0.5.0" -arbitrary = { version = "1.1.7", features = ["derive"], optional = true} +arbitrary = { version = "1.1.7", features = ["derive"], optional = true } hex = "0.4" hex-literal = "0.3" derive_more = "0.99" @@ -41,6 +49,6 @@ plain_hasher = "0.2" hash-db = "0.15" [dev-dependencies] -arbitrary = { version = "1.1.7", features = ["derive"]} +arbitrary = { version = "1.1.7", features = ["derive"] } serde_json = "1.0" hex-literal = "0.3" diff --git a/crates/primitives/src/account.rs b/crates/primitives/src/account.rs index 6e6734956..f5d667171 100644 --- a/crates/primitives/src/account.rs +++ b/crates/primitives/src/account.rs @@ -10,5 +10,12 @@ pub struct Account { /// Account balance. pub balance: U256, /// Hash of the bytecode. - pub bytecode_hash: H256, + pub bytecode_hash: Option, +} + +impl Account { + /// Does account has a bytecode. + pub fn has_bytecode(&self) -> bool { + self.bytecode_hash.is_some() + } } diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index a48fc12cf..744d1c92a 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -1,4 +1,4 @@ -use crate::{Header, Receipt, SealedHeader, Transaction, TransactionSigned, H256}; +use crate::{Header, SealedHeader, Transaction, TransactionSigned, H256}; use reth_rlp::{Decodable, DecodeError, Encodable}; use std::ops::Deref; @@ -9,8 +9,6 @@ pub struct Block { pub header: Header, /// Transactions in this block. pub body: Vec, - /// Block receipts. - pub receipts: Vec, /// Ommers/uncles header pub ommers: Vec, } @@ -29,8 +27,6 @@ pub struct BlockLocked { pub header: SealedHeader, /// Transactions with signatures. pub body: Vec, - /// Block receipts. - pub receipts: Vec, /// Ommer/uncle headers pub ommers: Vec, } diff --git a/crates/primitives/src/bloom.rs b/crates/primitives/src/bloom.rs new file mode 100644 index 000000000..48f2ec56c --- /dev/null +++ b/crates/primitives/src/bloom.rs @@ -0,0 +1,72 @@ +//! Bloom related utilities. + +use crate::{keccak256, Bloom, Log}; + +/// Length of bloom filter used for Ethereum. +pub const BLOOM_BYTE_LENGTH: usize = 256; + +// See Section 4.3.1 "Transaction Receipt" of the Yellow Paper +fn m3_2048(bloom: &mut Bloom, x: &[u8]) { + let hash = keccak256(x); + let h = hash.as_ref(); + for i in [0, 2, 4] { + let bit = (h[i + 1] as usize + ((h[i] as usize) << 8)) & 0x7FF; + bloom.0[BLOOM_BYTE_LENGTH - 1 - bit / 8] |= 1 << (bit % 8); + } +} + +/// Calculate receipt logs bloom. +pub fn logs_bloom<'a, It>(logs: It) -> Bloom +where + It: IntoIterator, +{ + let mut bloom = Bloom::zero(); + for log in logs { + m3_2048(&mut bloom, log.address.as_bytes()); + for topic in &log.topics { + m3_2048(&mut bloom, topic.as_bytes()); + } + } + bloom +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::hex_literal::hex; + + #[test] + fn hardcoded_bloom() { + let logs = vec![ + Log { + address: hex!("22341ae42d6dd7384bc8584e50419ea3ac75b83f").into(), + topics: vec![hex!( + "04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f" + ) + .into()], + data: vec![].into(), + }, + Log { + address: hex!("e7fb22dfef11920312e4989a3a2b81e2ebf05986").into(), + topics: vec![ + hex!("7f1fef85c4b037150d3675218e0cdb7cf38fea354759471e309f3354918a442f").into(), + hex!("d85629c7eaae9ea4a10234fed31bc0aeda29b2683ebe0c1882499d272621f6b6").into(), + ], + data: hex::decode("2d690516512020171c1ec870f6ff45398cc8609250326be89915fb538e7b") + .unwrap() + .into(), + }, + ]; + assert_eq!( + logs_bloom(&logs), + Bloom::from(hex!( + "000000000000000000810000000000000000000000000000000000020000000000000000000000000000008000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000280000000000400000800000004000000000" + "000000000000000000000000000000000000000000000000000000000000100000100000000000000000000000" + "00000000001400000000000000008000000000000000000000000000000000" + )) + ); + } +} diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index d51732f45..e830caba8 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -1,5 +1,14 @@ use crate::H256; +use hex_literal::hex; /// The Ethereum mainnet genesis hash. pub const MAINNET_GENESIS: H256 = - H256(hex_literal::hex!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")); + H256(hex!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")); + +/// Keccak256 over empty array. +pub const KECCAK_EMPTY: H256 = + H256(hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + +/// Ommer root of empty list. +pub const EMPTY_OMMER_ROOT: H256 = + H256(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")); diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index a513e468e..7ea9eceb3 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -11,6 +11,7 @@ mod account; mod block; +pub mod bloom; mod chain; mod constants; mod error; @@ -31,7 +32,8 @@ pub mod proofs; pub use account::Account; pub use block::{Block, BlockHashOrNumber, BlockLocked}; pub use chain::Chain; -pub use constants::MAINNET_GENESIS; +pub use constants::{EMPTY_OMMER_ROOT, KECCAK_EMPTY, MAINNET_GENESIS}; +pub use ethbloom::Bloom; pub use forkid::{ForkFilter, ForkHash, ForkId, ValidationError}; pub use hardfork::Hardfork; pub use header::{Header, SealedHeader}; @@ -74,7 +76,7 @@ pub type PeerId = H512; pub use ethers_core::{ types as rpc, - types::{BigEndianHash, Bloom, H128, H160, H256, H512, H64, U128, U256, U64}, + types::{BigEndianHash, H128, H160, H256, H512, H64, U128, U256, U64}, }; #[doc(hidden)] diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 591595987..f0318d6a1 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,4 +1,4 @@ -use crate::{Address, Bytes, ChainId, TxHash, H256, U256}; +use crate::{Address, Bytes, ChainId, TxHash, H256}; pub use access_list::{AccessList, AccessListItem}; use bytes::{Buf, BytesMut}; use derive_more::{AsRef, Deref}; @@ -28,7 +28,11 @@ pub enum Transaction { /// A scalar value equal to the number of /// Wei to be paid per unit of gas for all computation /// costs incurred as a result of the execution of this transaction; formally Tp. - gas_price: u64, + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + gas_price: u128, /// A scalar value equal to the maximum /// amount of gas that should be used in executing /// this transaction. This is paid up-front, before any @@ -42,7 +46,11 @@ pub enum Transaction { /// be transferred to the message call’s recipient or, /// in the case of contract creation, as an endowment /// to the newly created account; formally Tv. - value: U256, + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + value: u128, /// Input has two uses depending if transaction is Create or Call (if `to` field is None or /// Some). init: An unlimited size byte array specifying the /// EVM-code for the account initialisation procedure CREATE, @@ -59,7 +67,11 @@ pub enum Transaction { /// A scalar value equal to the number of /// Wei to be paid per unit of gas for all computation /// costs incurred as a result of the execution of this transaction; formally Tp. - gas_price: u64, + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + gas_price: u128, /// A scalar value equal to the maximum /// amount of gas that should be used in executing /// this transaction. This is paid up-front, before any @@ -73,7 +85,11 @@ pub enum Transaction { /// be transferred to the message call’s recipient or, /// in the case of contract creation, as an endowment /// to the newly created account; formally Tv. - value: U256, + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + value: u128, /// Input has two uses depending if transaction is Create or Call (if `to` field is None or /// Some). init: An unlimited size byte array specifying the /// EVM-code for the account initialisation procedure CREATE, @@ -104,9 +120,17 @@ pub enum Transaction { /// this transaction. This is paid up-front, before any /// computation is done and may not be increased /// later; formally Tg. - max_fee_per_gas: u64, + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + max_fee_per_gas: u128, /// Max Priority fee that transaction is paying - max_priority_fee_per_gas: u64, + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + max_priority_fee_per_gas: u128, /// The 160-bit address of the message call’s recipient or, for a contract creation /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. to: TransactionKind, @@ -114,7 +138,11 @@ pub enum Transaction { /// be transferred to the message call’s recipient or, /// in the case of contract creation, as an endowment /// to the newly created account; formally Tv. - value: U256, + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + value: u128, /// Input has two uses depending if transaction is Create or Call (if `to` field is None or /// Some). init: An unlimited size byte array specifying the /// EVM-code for the account initialisation procedure CREATE, @@ -152,14 +180,23 @@ impl Transaction { /// [`TransactionKind::Create`] if the transaction is a contract creation. pub fn kind(&self) -> &TransactionKind { match self { - Transaction::Legacy { to, .. } => to, - Transaction::Eip2930 { to, .. } => to, + Transaction::Legacy { to, .. } | + Transaction::Eip2930 { to, .. } | Transaction::Eip1559 { to, .. } => to, } } + /// Get transaction type + pub fn tx_type(&self) -> TxType { + match self { + Transaction::Legacy { .. } => TxType::Legacy, + Transaction::Eip2930 { .. } => TxType::EIP2930, + Transaction::Eip1559 { .. } => TxType::EIP1559, + } + } + /// Gets the transaction's value field. - pub fn value(&self) -> &U256 { + pub fn value(&self) -> &u128 { match self { Transaction::Legacy { value, .. } => value, Transaction::Eip2930 { value, .. } => value, @@ -185,11 +222,11 @@ impl Transaction { } } - /// Max fee per gas for eip1559 transaction, for legacy transactions this is gas_limit - pub fn max_fee_per_gas(&self) -> u64 { + /// Max fee per gas for eip1559 transaction, for legacy transactions this is gas_price + pub fn max_fee_per_gas(&self) -> u128 { match self { - Transaction::Legacy { gas_limit, .. } | Transaction::Eip2930 { gas_limit, .. } => { - *gas_limit + Transaction::Legacy { gas_price, .. } | Transaction::Eip2930 { gas_price, .. } => { + *gas_price } Transaction::Eip1559 { max_fee_per_gas, .. } => *max_fee_per_gas, } @@ -762,7 +799,7 @@ mod tests { gas_price: 1, gas_limit: 2, to: TransactionKind::Create, - value: U256::from(3), + value: 3, input: Bytes::from(vec![1, 2]), access_list: Default::default(), }; @@ -793,7 +830,7 @@ mod tests { gas_price: 1, gas_limit: 2, to: TransactionKind::Call(Address::default()), - value: U256::from(3), + value: 3, input: Bytes::from(vec![1, 2]), access_list: Default::default(), }; @@ -832,7 +869,7 @@ mod tests { to: TransactionKind::Call( Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap(), ), - value: U256::from(1000000000000000u64), + value: 1000000000000000, input: Bytes::default(), }; let expected_signature = Signature { @@ -855,7 +892,7 @@ mod tests { let expected_request = Transaction::Legacy { chain_id: Some(4), nonce: 1u64, - gas_price: 1000000000u64, + gas_price: 1000000000, gas_limit: 100000u64, to: TransactionKind::Call(Address::from_slice( &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], @@ -1009,7 +1046,7 @@ mod tests { nonce: 0x42, gas_limit: 44386, to: TransactionKind::Call( hex!("6069a6c32cf691f5982febae4faf8a6f3ab2f0f6").into()), - value: 0.into(), + value: 0, input: hex!("a22cb4650000000000000000000000005eee75727d804a2b13038928d36f8b188945a57a0000000000000000000000000000000000000000000000000000000000000000").into(), max_fee_per_gas: 0x4a817c800, max_priority_fee_per_gas: 0x3b9aca00, diff --git a/crates/stages/src/stages/bodies.rs b/crates/stages/src/stages/bodies.rs index d7916e587..5c32ebb3a 100644 --- a/crates/stages/src/stages/bodies.rs +++ b/crates/stages/src/stages/bodies.rs @@ -126,8 +126,6 @@ impl Stage for BodyStage