mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat: Consensus crate and verification functions. (#152)
* wip executor * wip * Cleanup added some checks and structure to executor * adding additional block/header checks * add basefee calculation and check * some cleanup * Sanity check test * Test for sanity check * move verification to consensus crate * cleanup * Better Error handling
This commit is contained in:
51
Cargo.lock
generated
51
Cargo.lock
generated
@ -1568,6 +1568,12 @@ version = "1.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash-db"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -2619,6 +2625,15 @@ dependencies = [
|
|||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plain_hasher"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e19e6491bdde87c2c43d70f4c194bc8a758f2eb732df00f61e43f7362e3b4cc"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotters"
|
name = "plotters"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@ -2989,6 +3004,25 @@ dependencies = [
|
|||||||
"codecs-derive",
|
"codecs-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reth-consensus"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"auto_impl",
|
||||||
|
"eyre",
|
||||||
|
"hash-db",
|
||||||
|
"plain_hasher",
|
||||||
|
"reth-interfaces",
|
||||||
|
"reth-primitives",
|
||||||
|
"reth-rlp",
|
||||||
|
"rlp",
|
||||||
|
"sha3",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"triehash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reth-crate-template"
|
name = "reth-crate-template"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -3098,11 +3132,18 @@ name = "reth-executor"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"auto_impl",
|
||||||
"eyre",
|
"eyre",
|
||||||
|
"hash-db",
|
||||||
|
"plain_hasher",
|
||||||
"reth-interfaces",
|
"reth-interfaces",
|
||||||
"reth-primitives",
|
"reth-primitives",
|
||||||
|
"reth-rlp",
|
||||||
"revm",
|
"revm",
|
||||||
|
"rlp",
|
||||||
|
"sha3",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"triehash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4393,6 +4434,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "triehash"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c"
|
||||||
|
dependencies = [
|
||||||
|
"hash-db",
|
||||||
|
"rlp",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trust-dns-client"
|
name = "trust-dns-client"
|
||||||
version = "0.20.4"
|
version = "0.20.4"
|
||||||
|
|||||||
@ -4,6 +4,7 @@ members = [
|
|||||||
"crate-template",
|
"crate-template",
|
||||||
"crates/common/rlp",
|
"crates/common/rlp",
|
||||||
"crates/common/rlp-derive",
|
"crates/common/rlp-derive",
|
||||||
|
"crates/consensus",
|
||||||
"crates/db",
|
"crates/db",
|
||||||
"crates/executor",
|
"crates/executor",
|
||||||
"crates/interfaces",
|
"crates/interfaces",
|
||||||
|
|||||||
30
crates/consensus/Cargo.toml
Normal file
30
crates/consensus/Cargo.toml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "reth-consensus"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/foundry-rs/reth"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# reth
|
||||||
|
reth-primitives = { path = "../primitives" }
|
||||||
|
reth-interfaces = { path = "../interfaces" }
|
||||||
|
reth-rlp = {path = "../common/rlp"}
|
||||||
|
|
||||||
|
# common
|
||||||
|
async-trait = "0.1.57"
|
||||||
|
thiserror = "1.0.37"
|
||||||
|
eyre = "0.6.8"
|
||||||
|
auto_impl = "1.0"
|
||||||
|
tokio = { version = "1.21.2", features = ["sync"] }
|
||||||
|
|
||||||
|
# proof related
|
||||||
|
triehash = "0.8"
|
||||||
|
# See to replace hashers to simplify libraries
|
||||||
|
plain_hasher = "0.2"
|
||||||
|
hash-db = "0.15"
|
||||||
|
# todo replace with faster rlp impl
|
||||||
|
rlp = { version = "0.5", default-features = false }
|
||||||
|
# replace with tiny-keccak (it is faster hasher)
|
||||||
|
sha3 = { version = "0.10", default-features = false }
|
||||||
24
crates/consensus/src/config.rs
Normal file
24
crates/consensus/src/config.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//! Reth block execution/validation configuration and constants
|
||||||
|
use reth_primitives::BlockNumber;
|
||||||
|
|
||||||
|
/// Initial base fee as defined in: https://eips.ethereum.org/EIPS/eip-1559
|
||||||
|
pub const EIP1559_INITIAL_BASE_FEE: u64 = 1_000_000_000;
|
||||||
|
/// Base fee max change denominator as defined in: https://eips.ethereum.org/EIPS/eip-1559
|
||||||
|
pub const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8;
|
||||||
|
/// Elasticity multiplier as defined in: https://eips.ethereum.org/EIPS/eip-1559
|
||||||
|
pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2;
|
||||||
|
|
||||||
|
/// Configuration for consensus
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
/// EIP-1559 hard fork number
|
||||||
|
pub london_hard_fork_block: BlockNumber,
|
||||||
|
/// The Merge/Paris hard fork block number
|
||||||
|
pub paris_hard_fork_block: BlockNumber,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { london_hard_fork_block: 12965000, paris_hard_fork_block: 15537394 }
|
||||||
|
}
|
||||||
|
}
|
||||||
43
crates/consensus/src/consensus.rs
Normal file
43
crates/consensus/src/consensus.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//! Consensus for ethereum network
|
||||||
|
|
||||||
|
use crate::{verification, Config};
|
||||||
|
use reth_interfaces::consensus::{Consensus, Error, ForkchoiceState};
|
||||||
|
use reth_primitives::{HeaderLocked, H256};
|
||||||
|
use tokio::sync::watch;
|
||||||
|
|
||||||
|
/// Ethereum consensus
|
||||||
|
pub struct EthConsensus {
|
||||||
|
/// Watcher over the forkchoice state
|
||||||
|
channel: (watch::Sender<ForkchoiceState>, watch::Receiver<ForkchoiceState>),
|
||||||
|
/// Configuration
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthConsensus {
|
||||||
|
/// Create new object
|
||||||
|
pub fn new(config: Config) -> Self {
|
||||||
|
Self {
|
||||||
|
channel: watch::channel(ForkchoiceState {
|
||||||
|
head_block_hash: H256::zero(),
|
||||||
|
finalized_block_hash: H256::zero(),
|
||||||
|
safe_block_hash: H256::zero(),
|
||||||
|
}),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Consensus for EthConsensus {
|
||||||
|
fn fork_choice_state(&self) -> watch::Receiver<ForkchoiceState> {
|
||||||
|
self.channel.1.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_header(&self, header: &HeaderLocked, parent: &HeaderLocked) -> Result<(), Error> {
|
||||||
|
verification::validate_header_standalone(header, &self.config)?;
|
||||||
|
verification::validate_header_regarding_parent(parent, header, &self.config)
|
||||||
|
|
||||||
|
// TODO Consensus checks for:
|
||||||
|
// * mix_hash & nonce PoW stuf
|
||||||
|
// * extra_data
|
||||||
|
}
|
||||||
|
}
|
||||||
18
crates/consensus/src/lib.rs
Normal file
18
crates/consensus/src/lib.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#![warn(missing_docs, unreachable_pub)]
|
||||||
|
#![deny(unused_must_use, rust_2018_idioms)]
|
||||||
|
#![doc(test(
|
||||||
|
no_crate_inject,
|
||||||
|
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||||
|
))]
|
||||||
|
|
||||||
|
//! Reth consensus.
|
||||||
|
pub mod config;
|
||||||
|
pub mod consensus;
|
||||||
|
pub mod verification;
|
||||||
|
|
||||||
|
/// Helper function for calculating Merkle proofs and hashes
|
||||||
|
pub mod proofs;
|
||||||
|
|
||||||
|
pub use config::Config;
|
||||||
|
pub use consensus::EthConsensus;
|
||||||
|
pub use reth_interfaces::consensus::Error;
|
||||||
96
crates/consensus/src/proofs.rs
Normal file
96
crates/consensus/src/proofs.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use hash_db::Hasher;
|
||||||
|
use plain_hasher::PlainHasher;
|
||||||
|
use reth_primitives::{Bytes, Header, Log, Receipt, TransactionSigned, H256};
|
||||||
|
use reth_rlp::Encodable;
|
||||||
|
use rlp::RlpStream;
|
||||||
|
use sha3::{Digest, Keccak256};
|
||||||
|
use triehash::sec_trie_root;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||||
|
struct KeccakHasher;
|
||||||
|
impl Hasher for KeccakHasher {
|
||||||
|
type Out = H256;
|
||||||
|
type StdHasher = PlainHasher;
|
||||||
|
const LENGTH: usize = 32;
|
||||||
|
fn hash(x: &[u8]) -> Self::Out {
|
||||||
|
let out = Keccak256::digest(x);
|
||||||
|
// TODO make more performant, H256 from slice is not good enought.
|
||||||
|
H256::from_slice(out.as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate Transaction root. Iterate over transaction and create merkle trie of
|
||||||
|
/// (rlp(index),encoded(tx)) pairs.
|
||||||
|
pub fn calculate_transaction_root<'a>(
|
||||||
|
transactions: impl IntoIterator<Item = &'a TransactionSigned>,
|
||||||
|
) -> H256 {
|
||||||
|
sec_trie_root::<KeccakHasher, _, _, _>(
|
||||||
|
transactions
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, tx)| {
|
||||||
|
// TODO replace with reth-rlp
|
||||||
|
let mut stream = RlpStream::new();
|
||||||
|
stream.append(&index);
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
tx.encode(&mut bytes);
|
||||||
|
(stream.out().freeze().into(), bytes)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(Bytes, Vec<u8>)>>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create receipt root for header
|
||||||
|
pub fn calculate_receipt_root<'a>(receipts: impl IntoIterator<Item = &'a Receipt>) -> H256 {
|
||||||
|
sec_trie_root::<KeccakHasher, _, _, _>(
|
||||||
|
receipts
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, receipt)| {
|
||||||
|
let mut stream = RlpStream::new();
|
||||||
|
stream.append(&index);
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
receipt.encode(&mut bytes);
|
||||||
|
(stream.out().freeze().into(), bytes)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(Bytes, Vec<u8>)>>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create log hash for header
|
||||||
|
pub fn calculate_log_root<'a>(logs: impl IntoIterator<Item = &'a Log>) -> H256 {
|
||||||
|
//https://github.com/ethereum/go-ethereum/blob/356bbe343a30789e77bb38f25983c8f2f2bfbb47/cmd/evm/internal/t8ntool/execution.go#L255
|
||||||
|
let mut stream = RlpStream::new();
|
||||||
|
stream.begin_unbounded_list();
|
||||||
|
for log in logs {
|
||||||
|
stream.begin_list(3);
|
||||||
|
stream.append(&log.address);
|
||||||
|
stream.append_list(&log.topics);
|
||||||
|
stream.append(&log.data);
|
||||||
|
}
|
||||||
|
stream.finalize_unbounded_list();
|
||||||
|
let out = stream.out().freeze();
|
||||||
|
|
||||||
|
let out = Keccak256::digest(out);
|
||||||
|
H256::from_slice(out.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate hash over omners/uncles headers
|
||||||
|
pub fn calculate_omners_root<'a>(_omners: impl IntoIterator<Item = &'a Header>) -> H256 {
|
||||||
|
// RLP Encode
|
||||||
|
let mut stream = RlpStream::new();
|
||||||
|
stream.begin_unbounded_list();
|
||||||
|
/* TODO
|
||||||
|
for omner in omners {
|
||||||
|
stream.append(omner)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
stream.finalize_unbounded_list();
|
||||||
|
let bytes = stream.out().freeze();
|
||||||
|
let out = Keccak256::digest(bytes);
|
||||||
|
H256::from_slice(out.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO state root
|
||||||
|
|
||||||
|
// TODO bloom
|
||||||
337
crates/consensus/src/verification.rs
Normal file
337
crates/consensus/src/verification.rs
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
//! ALl functions for verification of block
|
||||||
|
use crate::{config, Config};
|
||||||
|
use reth_interfaces::{consensus::Error, provider::HeaderProvider, Result as RethResult};
|
||||||
|
use reth_primitives::{BlockLocked, HeaderLocked, TransactionSigned};
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
/// Validate header standalone
|
||||||
|
pub fn validate_header_standalone(
|
||||||
|
header: &HeaderLocked,
|
||||||
|
config: &config::Config,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Gas used needs to be less then gas limit. Gas used is going to be check after execution.
|
||||||
|
if header.gas_used > header.gas_limit {
|
||||||
|
return Err(Error::HeaderGasUsedExceedsGasLimit {
|
||||||
|
gas_used: header.gas_used,
|
||||||
|
gas_limit: header.gas_limit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if timestamp is in future. Clock can drift but this can be consensus issue.
|
||||||
|
let present_timestamp =
|
||||||
|
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
|
||||||
|
if header.timestamp > present_timestamp {
|
||||||
|
return Err(Error::TimestampIsInFuture { timestamp: header.timestamp, present_timestamp })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if base fee is set.
|
||||||
|
if config.paris_hard_fork_block >= header.number && header.base_fee_per_gas.is_some() {
|
||||||
|
return Err(Error::BaseFeeMissing)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate transactions standlone
|
||||||
|
pub fn validate_transactions_standalone(
|
||||||
|
_transactions: &[TransactionSigned],
|
||||||
|
_config: &Config,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// TODO
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate block standalone
|
||||||
|
pub fn validate_block_standalone(block: &BlockLocked) -> Result<(), Error> {
|
||||||
|
// check omners hash
|
||||||
|
let omners_hash = crate::proofs::calculate_omners_root(block.ommers.iter().map(|h| h.as_ref()));
|
||||||
|
if block.header.ommers_hash != omners_hash {
|
||||||
|
return Err(Error::BodyOmmnersHashDiff {
|
||||||
|
got: omners_hash,
|
||||||
|
expected: block.header.ommers_hash,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// check transaction root
|
||||||
|
let transaction_root = crate::proofs::calculate_transaction_root(block.body.iter());
|
||||||
|
if block.header.transactions_root != transaction_root {
|
||||||
|
return Err(Error::BodyTransactionRootDiff {
|
||||||
|
got: transaction_root,
|
||||||
|
expected: block.header.transactions_root,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO transaction verification, Maybe make it configurable as in check only
|
||||||
|
// signatures/limits/types
|
||||||
|
|
||||||
|
// check if all transactions limit does not goes over block limit
|
||||||
|
|
||||||
|
// check receipts root
|
||||||
|
let receipts_root = crate::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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate base fee for next block. EIP-1559 spec
|
||||||
|
pub fn calculate_next_block_base_fee(gas_used: u64, gas_limit: u64, base_fee: u64) -> u64 {
|
||||||
|
let gas_target = gas_limit / config::EIP1559_ELASTICITY_MULTIPLIER;
|
||||||
|
|
||||||
|
if gas_used == gas_target {
|
||||||
|
return base_fee
|
||||||
|
}
|
||||||
|
if gas_used > gas_target {
|
||||||
|
let gas_used_delta = gas_used - gas_target;
|
||||||
|
let base_fee_delta = std::cmp::max(
|
||||||
|
1,
|
||||||
|
base_fee as u128 * gas_used_delta as u128 /
|
||||||
|
gas_target as u128 /
|
||||||
|
config::EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR as u128,
|
||||||
|
);
|
||||||
|
base_fee + (base_fee_delta as u64)
|
||||||
|
} else {
|
||||||
|
let gas_used_delta = gas_target - gas_used;
|
||||||
|
let base_fee_per_gas_delta = base_fee as u128 * gas_used_delta as u128 /
|
||||||
|
gas_target as u128 /
|
||||||
|
config::EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR as u128;
|
||||||
|
|
||||||
|
base_fee.saturating_sub(base_fee_per_gas_delta as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate block in regards to parent
|
||||||
|
pub fn validate_header_regarding_parent(
|
||||||
|
parent: &HeaderLocked,
|
||||||
|
child: &HeaderLocked,
|
||||||
|
config: &config::Config,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Parent number is consistent.
|
||||||
|
if parent.number + 1 != child.number {
|
||||||
|
return Err(Error::ParentBlockNumberMissmatch {
|
||||||
|
parent_block_number: parent.number,
|
||||||
|
block_number: child.number,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// timestamp in past check
|
||||||
|
if child.timestamp < parent.timestamp {
|
||||||
|
return Err(Error::TimestampIsInPast {
|
||||||
|
parent_timestamp: parent.timestamp,
|
||||||
|
timestamp: child.timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// difficulty check is done by consensus.
|
||||||
|
if config.paris_hard_fork_block > child.number {
|
||||||
|
// TODO how this needs to be checked? As ice age did increment it by some formula
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parent_gas_limit = parent.gas_limit;
|
||||||
|
|
||||||
|
// By consensus, gas_limit is multiplied by elasticity (*2) on
|
||||||
|
// on exact block that hardfork happens.
|
||||||
|
if config.london_hard_fork_block == child.number {
|
||||||
|
parent_gas_limit = parent.gas_limit * config::EIP1559_ELASTICITY_MULTIPLIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check gas limit, max diff between child/parent gas_limit should be max_diff=parent_gas/1024
|
||||||
|
if child.gas_limit > parent_gas_limit {
|
||||||
|
if child.gas_limit - parent_gas_limit >= parent_gas_limit / 1024 {
|
||||||
|
return Err(Error::GasLimitInvalidIncrease {
|
||||||
|
parent_gas_limit,
|
||||||
|
child_gas_limit: child.gas_limit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if parent_gas_limit - child.gas_limit >= parent_gas_limit / 1024 {
|
||||||
|
return Err(Error::GasLimitInvalidDecrease {
|
||||||
|
parent_gas_limit,
|
||||||
|
child_gas_limit: child.gas_limit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EIP-1559 check base fee
|
||||||
|
if child.number >= config.london_hard_fork_block {
|
||||||
|
let base_fee = child.base_fee_per_gas.ok_or(Error::BaseFeeMissing)?;
|
||||||
|
|
||||||
|
let expected_base_fee = if config.london_hard_fork_block == child.number {
|
||||||
|
config::EIP1559_INITIAL_BASE_FEE
|
||||||
|
} else {
|
||||||
|
// This BaseFeeMissing will not happen as previous blocks are checked to have them.
|
||||||
|
calculate_next_block_base_fee(
|
||||||
|
parent.gas_used,
|
||||||
|
parent.gas_limit,
|
||||||
|
parent.base_fee_per_gas.ok_or(Error::BaseFeeMissing)?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if expected_base_fee != base_fee {
|
||||||
|
return Err(Error::BaseFeeDiff { expected: expected_base_fee, got: base_fee })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate block in regards to chain (parent)
|
||||||
|
///
|
||||||
|
/// Checks:
|
||||||
|
/// If we already know the block.
|
||||||
|
/// If parent is known
|
||||||
|
///
|
||||||
|
/// Returns parent block header
|
||||||
|
pub fn validate_block_regarding_chain<PROV: HeaderProvider>(
|
||||||
|
block: &BlockLocked,
|
||||||
|
provider: &PROV,
|
||||||
|
) -> RethResult<HeaderLocked> {
|
||||||
|
let hash = block.header.hash();
|
||||||
|
|
||||||
|
// Check if block is known.
|
||||||
|
if provider.is_known(&hash)? {
|
||||||
|
return Err(Error::BlockKnown { hash, number: block.header.number }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if parent is known.
|
||||||
|
let parent = provider
|
||||||
|
.header(&block.parent_hash)?
|
||||||
|
.ok_or(Error::ParentUnknown { hash: block.parent_hash })?;
|
||||||
|
|
||||||
|
// Return parent header.
|
||||||
|
Ok(parent.lock())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Full validation of block before execution.
|
||||||
|
pub fn full_validation<PROV: HeaderProvider>(
|
||||||
|
block: &BlockLocked,
|
||||||
|
provider: PROV,
|
||||||
|
config: &Config,
|
||||||
|
) -> RethResult<()> {
|
||||||
|
validate_header_standalone(&block.header, config)?;
|
||||||
|
validate_block_standalone(block)?;
|
||||||
|
let parent = validate_block_regarding_chain(block, &provider)?;
|
||||||
|
validate_header_regarding_parent(&parent, &block.header, config)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use reth_interfaces::Result;
|
||||||
|
use reth_primitives::{hex_literal::hex, BlockHash, Header};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculate_base_fee_success() {
|
||||||
|
let base_fee = [
|
||||||
|
1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
|
||||||
|
1, 2,
|
||||||
|
];
|
||||||
|
let gas_used = [
|
||||||
|
10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
|
||||||
|
10000000,
|
||||||
|
];
|
||||||
|
let gas_limit = [
|
||||||
|
10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
|
||||||
|
18000000, 18000000,
|
||||||
|
];
|
||||||
|
let next_base_fee = [
|
||||||
|
1125000000, 1083333333, 1053571428, 1179939062, 1116028649, 918084097, 1063811730, 1,
|
||||||
|
2, 3,
|
||||||
|
];
|
||||||
|
|
||||||
|
for i in 0..base_fee.len() {
|
||||||
|
assert_eq!(
|
||||||
|
next_base_fee[i],
|
||||||
|
calculate_next_block_base_fee(gas_used[i], gas_limit[i], base_fee[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Provider {
|
||||||
|
is_known: bool,
|
||||||
|
parent: Option<Header>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Provider {
|
||||||
|
/// New provider with parent
|
||||||
|
fn new(parent: Option<Header>) -> Self {
|
||||||
|
Self { is_known: false, parent }
|
||||||
|
}
|
||||||
|
/// New provider where is_known is always true
|
||||||
|
fn new_known() -> Self {
|
||||||
|
Self { is_known: true, parent: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderProvider for Provider {
|
||||||
|
fn is_known(&self, _block_hash: &BlockHash) -> Result<bool> {
|
||||||
|
Ok(self.is_known)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn header(&self, _block_number: &BlockHash) -> Result<Option<Header>> {
|
||||||
|
Ok(self.parent.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// got test block
|
||||||
|
fn mock_block() -> (BlockLocked, Header) {
|
||||||
|
// https://etherscan.io/block/15867168 where transaction root and receipts root are cleared
|
||||||
|
// empty merkle tree: 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
|
||||||
|
|
||||||
|
let header = Header {
|
||||||
|
parent_hash: hex!("859fad46e75d9be177c2584843501f2270c7e5231711e90848290d12d7c6dcdd").into(),
|
||||||
|
ommers_hash: hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").into(),
|
||||||
|
beneficiary: hex!("4675c7e5baafbffbca748158becba61ef3b0a263").into(),
|
||||||
|
state_root: hex!("8337403406e368b3e40411138f4868f79f6d835825d55fd0c2f6e17b1a3948e9").into(),
|
||||||
|
transactions_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||||
|
receipts_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||||
|
logs_bloom: hex!("002400000000004000220000800002000000000000000000000000000000100000000000000000100000000000000021020000000800000006000000002100040000000c0004000000000008000008200000000000000000000000008000000001040000020000020000002000000800000002000020000000022010000000000000010002001000000000020200000000000001000200880000004000000900020000000000020000000040000000000000000000000000000080000000000001000002000000000000012000200020000000000000001000000000000020000010321400000000100000000000000000000000000000400000000000000000").into(),
|
||||||
|
difficulty: 0x00.into(), // total diffuculty: 0xc70d815d562d3cfa955).into(),
|
||||||
|
number: 0xf21d20,
|
||||||
|
gas_limit: 0x1c9c380,
|
||||||
|
gas_used: 0x6e813,
|
||||||
|
timestamp: 0x635f9657,
|
||||||
|
extra_data: hex!("")[..].into(),
|
||||||
|
mix_hash: hex!("f8c29910a0a2fd65b260d83ffa2547a6db279095d109a6e64527d14035263cfc").into(),
|
||||||
|
nonce: 0x0000000000000000,
|
||||||
|
base_fee_per_gas: 0x28f0001df.into(),
|
||||||
|
};
|
||||||
|
// size: 0x9b5
|
||||||
|
|
||||||
|
let mut parent = header.clone();
|
||||||
|
parent.gas_used = 17763076;
|
||||||
|
parent.gas_limit = 30000000;
|
||||||
|
parent.base_fee_per_gas = Some(0x28041f7f5);
|
||||||
|
parent.number = parent.number - 1;
|
||||||
|
|
||||||
|
let ommers = Vec::new();
|
||||||
|
let receipts = Vec::new();
|
||||||
|
let body = Vec::new();
|
||||||
|
|
||||||
|
(BlockLocked { header: header.lock(), body, receipts, ommers }, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sanity_check() {
|
||||||
|
let (block, parent) = mock_block();
|
||||||
|
let provider = Provider::new(Some(parent));
|
||||||
|
let config = Config::default();
|
||||||
|
|
||||||
|
assert_eq!(full_validation(&block, provider, &config), Ok(()), "Validation should pass");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_known_block() {
|
||||||
|
let (block, _) = mock_block();
|
||||||
|
let provider = Provider::new_known();
|
||||||
|
let config = Config::default();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
full_validation(&block, provider, &config),
|
||||||
|
Err(Error::BlockKnown { hash: block.hash(), number: block.number }.into()),
|
||||||
|
"Should fail with error"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,9 +7,24 @@ repository = "https://github.com/foundry-rs/reth"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
revm = "2.1"
|
# reth
|
||||||
reth-primitives = { path = "../primitives" }
|
reth-primitives = { path = "../primitives" }
|
||||||
reth-interfaces = { path = "../interfaces" }
|
reth-interfaces = { path = "../interfaces" }
|
||||||
|
reth-rlp = {path = "../common/rlp"}
|
||||||
|
|
||||||
|
revm = "2.1"
|
||||||
|
|
||||||
|
# common
|
||||||
async-trait = "0.1.57"
|
async-trait = "0.1.57"
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
eyre = "0.6.8"
|
eyre = "0.6.8"
|
||||||
|
auto_impl = "1.0"
|
||||||
|
|
||||||
|
triehash = "0.8"
|
||||||
|
# See to replace hashers to simplify libraries
|
||||||
|
plain_hasher = "0.2"
|
||||||
|
hash-db = "0.15"
|
||||||
|
# todo replace with faster rlp impl
|
||||||
|
rlp = { version = "0.5", default-features = false }
|
||||||
|
# replace with tiny-keccak (it is faster hasher)
|
||||||
|
sha3 = { version = "0.10", default-features = false }
|
||||||
@ -1,6 +0,0 @@
|
|||||||
/// Configuration for executor (TODO)
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Config {
|
|
||||||
/// Example
|
|
||||||
pub example: bool,
|
|
||||||
}
|
|
||||||
5
crates/executor/src/config.rs
Normal file
5
crates/executor/src/config.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//! Reth block execution/validation configuration and constants
|
||||||
|
|
||||||
|
/// Configuration for executor
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Config {}
|
||||||
@ -1,41 +1,53 @@
|
|||||||
use crate::{revm_wrap, Config};
|
use crate::{
|
||||||
use reth_interfaces::{
|
revm_wrap::{self, State, SubState},
|
||||||
consensus::Consensus,
|
Config,
|
||||||
executor::{BlockExecutor, Error, ExecutorDb},
|
|
||||||
};
|
};
|
||||||
|
use reth_interfaces::executor::{BlockExecutor, Error, ExecutorDb};
|
||||||
use reth_primitives::BlockLocked;
|
use reth_primitives::BlockLocked;
|
||||||
use revm::{db::EmptyDB, AnalysisKind, Env, SpecId};
|
use revm::{AnalysisKind, SpecId, EVM};
|
||||||
|
|
||||||
/// Main block executor
|
/// Main block executor
|
||||||
pub struct Executor {
|
pub struct Executor {
|
||||||
/// Configuration, Spec and optional flags.
|
/// Configuration, Spec and optional flags.
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
/// Database
|
|
||||||
pub db: Box<dyn ExecutorDb>,
|
|
||||||
/// Consensus
|
|
||||||
pub consensus: Box<dyn Consensus>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Executor {
|
impl Executor {
|
||||||
/// Create new Executor
|
/// Create new Executor
|
||||||
pub fn new(config: Config, db: Box<dyn ExecutorDb>, consensus: Box<dyn Consensus>) -> Self {
|
pub fn new(config: Config) -> Self {
|
||||||
Self { config, db, consensus }
|
Self { config }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify block. Execute all transaction and compare results.
|
/// Verify block. Execute all transaction and compare results.
|
||||||
pub fn verify(&self, block: &BlockLocked) -> Result<(), Error> {
|
pub fn verify<DB: ExecutorDb>(&self, block: &BlockLocked, db: DB) -> Result<(), Error> {
|
||||||
let mut env = Env::default();
|
let db = SubState::new(State::new(db));
|
||||||
env.cfg.chain_id = 1.into();
|
let mut evm = EVM::new();
|
||||||
env.cfg.spec_id = SpecId::LATEST;
|
evm.database(db);
|
||||||
env.cfg.perf_all_precompiles_have_balance = true;
|
|
||||||
env.cfg.perf_analyse_created_bytecodes = AnalysisKind::Raw;
|
|
||||||
|
|
||||||
revm_wrap::fill_block_env(&mut env.block, block);
|
evm.env.cfg.chain_id = 1.into();
|
||||||
|
evm.env.cfg.spec_id = SpecId::LATEST;
|
||||||
|
evm.env.cfg.perf_all_precompiles_have_balance = true;
|
||||||
|
evm.env.cfg.perf_analyse_created_bytecodes = AnalysisKind::Raw;
|
||||||
|
|
||||||
let _database = revm_wrap::TempStateDb::new(EmptyDB::default());
|
revm_wrap::fill_block_env(&mut evm.env.block, block);
|
||||||
|
|
||||||
for transaction in block.body.iter() {
|
for transaction in block.body.iter() {
|
||||||
revm_wrap::fill_tx_env(&mut env.tx, transaction.as_ref());
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate commulative gas used
|
||||||
|
|
||||||
|
// create receipt
|
||||||
|
// bloom filter from logs
|
||||||
|
|
||||||
|
// Receipt outcome EIP-658: Embedding transaction status code in receipts
|
||||||
|
// EIP-658 supperseeded EIP-98 in Byzantium fork
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::VerificationFailed)
|
Err(Error::VerificationFailed)
|
||||||
|
|||||||
@ -7,10 +7,9 @@
|
|||||||
|
|
||||||
//! Reth executor executes transaction in block of data.
|
//! Reth executor executes transaction in block of data.
|
||||||
|
|
||||||
mod cfg;
|
pub mod config;
|
||||||
/// Executor
|
/// Executor
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
/// Wrapper around revm database and types
|
/// Wrapper around revm database and types
|
||||||
pub mod revm_wrap;
|
pub mod revm_wrap;
|
||||||
|
pub use config::Config;
|
||||||
pub use cfg::Config;
|
|
||||||
|
|||||||
@ -1,21 +1,43 @@
|
|||||||
use reth_interfaces::executor::ExecutorDb;
|
use reth_interfaces::executor::ExecutorDb;
|
||||||
use reth_primitives::{BlockLocked, Transaction, TransactionKind, H160, H256, U256};
|
use reth_primitives::{BlockLocked, Transaction, TransactionKind, H160, H256, U256};
|
||||||
use revm::{
|
use revm::{
|
||||||
db::{CacheDB, Database, EmptyDB},
|
db::{CacheDB, DatabaseRef},
|
||||||
BlockEnv, TransactTo, TxEnv,
|
BlockEnv, TransactTo, TxEnv,
|
||||||
};
|
};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
/// Temporary stateDB TODO
|
/// SubState of database. Uses revm internal cache with binding to reth DbExecutor trait.
|
||||||
pub type TempStateDb = CacheDB<EmptyDB>;
|
pub type SubState<DB> = CacheDB<State<DB>>;
|
||||||
|
|
||||||
/// Wrapper around ExeuctorDb that implements revm database trait
|
/// Wrapper around ExeuctorDb that implements revm database trait
|
||||||
pub struct Wrapper<'a>(&'a dyn ExecutorDb);
|
pub struct State<DB: ExecutorDb>(DB);
|
||||||
|
|
||||||
impl<'a> Database for Wrapper<'a> {
|
impl<DB: ExecutorDb> State<DB> {
|
||||||
|
/// Create new State with generic ExecutorDb.
|
||||||
|
pub fn new(db: DB) -> Self {
|
||||||
|
Self(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return inner state reference
|
||||||
|
pub fn state(&self) -> &DB {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return inner state mutable reference
|
||||||
|
pub fn state_mut(&mut self) -> &mut DB {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume State and return inner DbExecutable.
|
||||||
|
pub fn into_inner(self) -> DB {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DB: ExecutorDb> DatabaseRef for State<DB> {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn basic(&mut self, address: H160) -> Result<Option<revm::AccountInfo>, Self::Error> {
|
fn basic(&self, address: H160) -> Result<Option<revm::AccountInfo>, Self::Error> {
|
||||||
Ok(self.0.basic_account(address).map(|account| revm::AccountInfo {
|
Ok(self.0.basic_account(address).map(|account| revm::AccountInfo {
|
||||||
balance: account.balance,
|
balance: account.balance,
|
||||||
nonce: account.nonce,
|
nonce: account.nonce,
|
||||||
@ -24,19 +46,19 @@ impl<'a> Database for Wrapper<'a> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code_by_hash(&mut self, code_hash: H256) -> Result<revm::Bytecode, Self::Error> {
|
fn code_by_hash(&self, code_hash: H256) -> Result<revm::Bytecode, Self::Error> {
|
||||||
let (bytecode, size) = self.0.bytecode_by_hash(code_hash).unwrap_or_default();
|
let (bytecode, size) = self.0.bytecode_by_hash(code_hash).unwrap_or_default();
|
||||||
Ok(unsafe { revm::Bytecode::new_checked(bytecode.0, size, Some(code_hash)) })
|
Ok(unsafe { revm::Bytecode::new_checked(bytecode.0, size, Some(code_hash)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn storage(&mut self, address: H160, index: U256) -> Result<U256, Self::Error> {
|
fn storage(&self, address: H160, index: U256) -> Result<U256, Self::Error> {
|
||||||
let mut h_index = H256::zero();
|
let mut h_index = H256::zero();
|
||||||
index.to_big_endian(h_index.as_bytes_mut());
|
index.to_big_endian(h_index.as_bytes_mut());
|
||||||
|
|
||||||
Ok(U256::from_big_endian(self.0.storage(address, h_index).unwrap_or_default().as_ref()))
|
Ok(U256::from_big_endian(self.0.storage(address, h_index).unwrap_or_default().as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_hash(&mut self, number: U256) -> Result<H256, Self::Error> {
|
fn block_hash(&self, number: U256) -> Result<H256, Self::Error> {
|
||||||
Ok(self.0.block_hash(number).unwrap_or_default())
|
Ok(self.0.block_hash(number).unwrap_or_default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use reth_primitives::Header;
|
use reth_primitives::{BlockHash, BlockNumber, HeaderLocked, H256};
|
||||||
use thiserror::Error;
|
|
||||||
use tokio::sync::watch::Receiver;
|
use tokio::sync::watch::Receiver;
|
||||||
|
|
||||||
/// Re-export forkchoice state
|
/// Re-export forkchoice state
|
||||||
@ -15,13 +14,40 @@ pub trait Consensus: Send + Sync {
|
|||||||
fn fork_choice_state(&self) -> Receiver<ForkchoiceState>;
|
fn fork_choice_state(&self) -> Receiver<ForkchoiceState>;
|
||||||
|
|
||||||
/// Validate if header is correct and follows consensus specification
|
/// Validate if header is correct and follows consensus specification
|
||||||
fn validate_header(&self, header: &Header, parent: &Header) -> Result<(), Error>;
|
fn validate_header(&self, header: &HeaderLocked, parent: &HeaderLocked) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consensus errors (TODO)
|
/// Consensus Errors
|
||||||
#[derive(Error, Debug)]
|
#[allow(missing_docs)]
|
||||||
|
#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Explanatory
|
#[error("Block used gas ({gas_used:?}) is greater then gas limit ({gas_limit:?})")]
|
||||||
#[error("Example of consensus error")]
|
HeaderGasUsedExceedsGasLimit { gas_used: u64, gas_limit: u64 },
|
||||||
ConsensusError,
|
#[error("Block ommner hash ({got:?}) is different then expected: ({expected:?})")]
|
||||||
|
BodyOmmnersHashDiff { 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:?})")]
|
||||||
|
BodyReceiptsRootDiff { got: H256, expected: H256 },
|
||||||
|
#[error("Block with [hash:{hash:?},number: {number:}] is already known")]
|
||||||
|
BlockKnown { hash: BlockHash, number: BlockNumber },
|
||||||
|
#[error("Block parent [hash:{hash:?}] is not known")]
|
||||||
|
ParentUnknown { hash: BlockHash },
|
||||||
|
#[error("Block number {block_number:?} is missmatch with parent block number {parent_block_number:?}")]
|
||||||
|
ParentBlockNumberMissmatch { parent_block_number: BlockNumber, block_number: BlockNumber },
|
||||||
|
#[error(
|
||||||
|
"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:?}")]
|
||||||
|
TimestampIsInFuture { timestamp: u64, present_timestamp: u64 },
|
||||||
|
// TODO make better error msg :)
|
||||||
|
#[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")]
|
||||||
|
GasLimitInvalidDecrease { parent_gas_limit: u64, child_gas_limit: u64 },
|
||||||
|
#[error("Base fee missing")]
|
||||||
|
BaseFeeMissing,
|
||||||
|
#[error("Block base fee ({got:?}) is different then expected: ({expected:?})")]
|
||||||
|
BaseFeeDiff { expected: u64, got: u64 },
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
/// Core error variants possible when interacting with the blockchain
|
/// Core error variants possible when interacting with the blockchain
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
|||||||
@ -14,7 +14,7 @@ pub trait BlockExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// BlockExecutor Errors
|
/// BlockExecutor Errors
|
||||||
#[derive(Error, Debug, Clone)]
|
#[derive(Error, Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Example of error
|
/// Example of error
|
||||||
#[error("Example of error.")]
|
#[error("Example of error.")]
|
||||||
|
|||||||
@ -1,9 +1,22 @@
|
|||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
use auto_impl::auto_impl;
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
rpc::{BlockId, BlockNumber},
|
rpc::{BlockId, BlockNumber},
|
||||||
Block, H256, U256,
|
Block, BlockHash, Header, H256, U256,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Client trait for fetching `Header` related data.
|
||||||
|
#[auto_impl(&)]
|
||||||
|
pub trait HeaderProvider: Send + Sync + 'static {
|
||||||
|
/// Check if block is known
|
||||||
|
fn is_known(&self, block_hash: &BlockHash) -> Result<bool> {
|
||||||
|
self.header(block_hash).map(|header| header.is_some())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get header by block hash
|
||||||
|
fn header(&self, block_hash: &BlockHash) -> Result<Option<Header>>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Client trait for fetching `Block` related data.
|
/// Client trait for fetching `Block` related data.
|
||||||
pub trait BlockProvider: Send + Sync + 'static {
|
pub trait BlockProvider: Send + Sync + 'static {
|
||||||
/// Returns the current info for the chain.
|
/// Returns the current info for the chain.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
mod block;
|
mod block;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
pub use block::BlockProvider;
|
pub use block::{BlockProvider, HeaderProvider};
|
||||||
pub use storage::StorageProvider;
|
pub use storage::StorageProvider;
|
||||||
|
|||||||
@ -155,9 +155,13 @@ impl Consensus for TestConsensus {
|
|||||||
self.channel.1.clone()
|
self.channel.1.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_header(&self, _header: &Header, _parent: &Header) -> Result<(), consensus::Error> {
|
fn validate_header(
|
||||||
|
&self,
|
||||||
|
_header: &HeaderLocked,
|
||||||
|
_parent: &HeaderLocked,
|
||||||
|
) -> Result<(), consensus::Error> {
|
||||||
if self.fail_validation {
|
if self.fail_validation {
|
||||||
Err(consensus::Error::ConsensusError)
|
Err(consensus::Error::BaseFeeMissing)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -172,8 +172,8 @@ where
|
|||||||
/// of used pages as well as free pages in this environment.
|
/// of used pages as well as free pages in this environment.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use libmdbx::Environment;
|
/// # use reth_libmdbx::Environment;
|
||||||
/// # use libmdbx::NoWriteMap;
|
/// # use reth_libmdbx::NoWriteMap;
|
||||||
/// let dir = tempfile::tempdir().unwrap();
|
/// let dir = tempfile::tempdir().unwrap();
|
||||||
/// let env = Environment::<NoWriteMap>::new().open(dir.path()).unwrap();
|
/// let env = Environment::<NoWriteMap>::new().open(dir.path()).unwrap();
|
||||||
/// let info = env.info().unwrap();
|
/// let info = env.info().unwrap();
|
||||||
|
|||||||
@ -159,7 +159,7 @@ impl Discv4 {
|
|||||||
/// let(discv4, mut service) = Discv4::bind(socket, local_enr, secret_key, config).await.unwrap();
|
/// let(discv4, mut service) = Discv4::bind(socket, local_enr, secret_key, config).await.unwrap();
|
||||||
///
|
///
|
||||||
/// // get an update strea
|
/// // get an update strea
|
||||||
/// let mut updates = service.update_stream();
|
/// let updates = service.update_stream();
|
||||||
///
|
///
|
||||||
/// let _handle = service.spawn();
|
/// let _handle = service.spawn();
|
||||||
///
|
///
|
||||||
|
|||||||
@ -28,6 +28,7 @@ thiserror = "1"
|
|||||||
sucds = "0.5.0"
|
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 = "0.4"
|
||||||
|
hex-literal = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
arbitrary = { version = "1.1.7", features = ["derive"]}
|
arbitrary = { version = "1.1.7", features = ["derive"]}
|
||||||
|
|||||||
@ -10,6 +10,8 @@ pub struct Block {
|
|||||||
pub body: Vec<Transaction>,
|
pub body: Vec<Transaction>,
|
||||||
/// Block receipts.
|
/// Block receipts.
|
||||||
pub receipts: Vec<Receipt>,
|
pub receipts: Vec<Receipt>,
|
||||||
|
/// Ommers/uncles header
|
||||||
|
pub ommers: Vec<HeaderLocked>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Block {
|
impl Deref for Block {
|
||||||
@ -28,6 +30,8 @@ pub struct BlockLocked {
|
|||||||
pub body: Vec<TransactionSigned>,
|
pub body: Vec<TransactionSigned>,
|
||||||
/// Block receipts.
|
/// Block receipts.
|
||||||
pub receipts: Vec<Receipt>,
|
pub receipts: Vec<Receipt>,
|
||||||
|
/// Omners/uncles header
|
||||||
|
pub ommers: Vec<HeaderLocked>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockLocked {
|
impl BlockLocked {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::{BlockNumber, Bloom, H160, H256, U256};
|
use crate::{BlockHash, BlockNumber, Bloom, H160, H256, U256};
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use ethers_core::{types::H64, utils::keccak256};
|
use ethers_core::{types::H64, utils::keccak256};
|
||||||
use reth_codecs::main_codec;
|
use reth_codecs::main_codec;
|
||||||
@ -180,7 +180,7 @@ pub struct HeaderLocked {
|
|||||||
/// Locked Header fields.
|
/// Locked Header fields.
|
||||||
header: Header,
|
header: Header,
|
||||||
/// Locked Header hash.
|
/// Locked Header hash.
|
||||||
hash: H256,
|
hash: BlockHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Header> for HeaderLocked {
|
impl AsRef<Header> for HeaderLocked {
|
||||||
@ -211,7 +211,7 @@ impl HeaderLocked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return header/block hash.
|
/// Return header/block hash.
|
||||||
pub fn hash(&self) -> H256 {
|
pub fn hash(&self) -> BlockHash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,6 +60,7 @@ pub use ethers_core::{
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
mod __reexport {
|
mod __reexport {
|
||||||
pub use hex;
|
pub use hex;
|
||||||
|
pub use hex_literal;
|
||||||
pub use tiny_keccak;
|
pub use tiny_keccak;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user