mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
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:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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",
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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<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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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 },
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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>>;
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>,
|
||||
}
|
||||
|
||||
72
crates/primitives/src/bloom.rs
Normal file
72
crates/primitives/src/bloom.rs
Normal 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"
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user