feat(executor): transaction and receipts verification (#195)

* transaction verification

* Add providers to interfaces

* add receipt check in executor verify

* Removed receipts, added bloom

* post merge header checks

* fmt clippy

* gas price type removal

* rm leftover

* move consts to contstant.rs
This commit is contained in:
rakita
2022-11-16 20:10:36 +01:00
committed by GitHub
parent 75a6d06301
commit 09c5c3449e
26 changed files with 534 additions and 227 deletions

2
Cargo.lock generated
View File

@ -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",

View File

@ -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,
}
}
}

View File

@ -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)
}
}

View File

@ -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<u64>,
) -> 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<Option<Account>>;
}
/// Validate transaction in regards of State
pub fn validate_transaction_regarding_state<AP: AccountProvider>(
_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<AP: AccountProvider>(
/// - 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<PROV: HeaderProvider>(
}
/// Full validation of block before execution.
pub fn full_validation<PROV: HeaderProvider>(
pub fn full_validation<Provider: HeaderProvider + AccountProvider>(
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<Header>,
account: Option<Account>,
}
impl Provider {
/// New provider with parent
fn new(parent: Option<Header>) -> 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<Option<Account>> {
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]

View File

@ -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")

View File

@ -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"

View File

@ -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 transactions gas limit, Tg, and the gas utilised in this block prior,
// must be no greater than the blocks 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 transactions 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<Log> = 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(())
}
}

View File

@ -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<DB: StateProvider> DatabaseRef for State<DB> {
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)
}

View File

@ -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,
}

View File

@ -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<Bloom>, expected: Box<Bloom> },
#[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 },
}

View File

@ -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<Option<Account>> {
// 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<Option<StorageValue>> {
@ -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<Option<Account>> {
// TODO add when AccountHistory is defined
Ok(None)
}
/// Get account code by its hash
fn bytecode_by_hash(&self, code_hash: H256) -> Result<Option<Bytes>> {
self.db.get::<tables::Bytecodes>(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<Option<Account>> {
self.db.get::<tables::PlainAccountState>(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<Option<StorageValue>> {
@ -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<Option<Account>> {
self.db.get::<tables::PlainAccountState>(address).map_err(Into::into)
}
/// Get account code by its hash
fn bytecode_by_hash(&self, code_hash: H256) -> Result<Option<Bytes>> {
self.db.get::<tables::Bytecodes>(code_hash).map_err(Into::into).map(|r| r.map(Bytes::from))

View File

@ -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};

View File

@ -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<Option<H256>>;
/// Account provider
pub trait AccountProvider: Send + Sync {
/// Get basic account information.
fn basic_account(&self, address: Address) -> Result<Option<Account>>;
}
/// 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<Option<StorageValue>>;
/// Get basic account information.
fn basic_account(&self, address: Address) -> Result<Option<Account>>;
/// Get account code by its hash
fn bytecode_by_hash(&self, code_hash: H256) -> Result<Option<Bytes>>;

View File

@ -120,7 +120,6 @@ pub fn random_block(number: u64, parent: Option<H256>) -> BlockLocked {
.seal(),
body: transactions,
ommers: ommers.into_iter().map(|ommer| ommer.seal()).collect(),
..Default::default()
}
}

View File

@ -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()),

View File

@ -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(),

View File

@ -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<Pool, Client> EthApiServer for EthApi<Pool, Client>
where
Pool: TransactionPool<Transaction = Transaction> + Clone + 'static,
Client: BlockProvider + StorageProvider + 'static,
Client: BlockProvider + StateProviderFactory + 'static,
{
fn protocol_version(&self) -> Result<U64> {
Ok(self.protocol_version())

View File

@ -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<Pool, Client> {
impl<Pool, Client> EthApi<Pool, Client>
where
Pool: TransactionPool<Transaction = Transaction> + Clone,
Client: BlockProvider + StorageProvider,
Client: BlockProvider + StateProviderFactory,
{
/// Creates a new, shareable instance.
pub fn new(client: Arc<Client>, pool: Pool) -> Self {

View File

@ -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"

View File

@ -10,5 +10,12 @@ pub struct Account {
/// Account balance.
pub balance: U256,
/// Hash of the bytecode.
pub bytecode_hash: H256,
pub bytecode_hash: Option<H256>,
}
impl Account {
/// Does account has a bytecode.
pub fn has_bytecode(&self) -> bool {
self.bytecode_hash.is_some()
}
}

View File

@ -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<Transaction>,
/// Block receipts.
pub receipts: Vec<Receipt>,
/// Ommers/uncles header
pub ommers: Vec<SealedHeader>,
}
@ -29,8 +27,6 @@ pub struct BlockLocked {
pub header: SealedHeader,
/// Transactions with signatures.
pub body: Vec<TransactionSigned>,
/// Block receipts.
pub receipts: Vec<Receipt>,
/// Ommer/uncle headers
pub ommers: Vec<SealedHeader>,
}

View File

@ -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<Item = &'a Log>,
{
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"
))
);
}
}

View File

@ -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"));

View File

@ -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)]

View File

@ -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 calls 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 calls 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 calls 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 calls 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,

View File

@ -126,8 +126,6 @@ impl<DB: Database, D: BodyDownloader, C: Consensus> Stage<DB> for BodyStage<D, C
header_hash,
),
body: body.transactions,
// TODO: We should have a type w/o receipts probably, no reason to allocate here
receipts: vec![],
ommers: body.ommers.into_iter().map(|header| header.seal()).collect(),
};