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:
rakita
2022-11-02 12:59:51 +01:00
committed by GitHub
parent 1ea98d40cf
commit ac2f3fcd8a
26 changed files with 755 additions and 59 deletions

51
Cargo.lock generated
View File

@ -1568,6 +1568,12 @@ version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hash-db"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a"
[[package]]
name = "hash32"
version = "0.2.1"
@ -2619,6 +2625,15 @@ dependencies = [
"spki",
]
[[package]]
name = "plain_hasher"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e19e6491bdde87c2c43d70f4c194bc8a758f2eb732df00f61e43f7362e3b4cc"
dependencies = [
"crunchy",
]
[[package]]
name = "plotters"
version = "0.3.4"
@ -2989,6 +3004,25 @@ dependencies = [
"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]]
name = "reth-crate-template"
version = "0.1.0"
@ -3098,11 +3132,18 @@ name = "reth-executor"
version = "0.1.0"
dependencies = [
"async-trait",
"auto_impl",
"eyre",
"hash-db",
"plain_hasher",
"reth-interfaces",
"reth-primitives",
"reth-rlp",
"revm",
"rlp",
"sha3",
"thiserror",
"triehash",
]
[[package]]
@ -4393,6 +4434,16 @@ dependencies = [
"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]]
name = "trust-dns-client"
version = "0.20.4"

View File

@ -4,6 +4,7 @@ members = [
"crate-template",
"crates/common/rlp",
"crates/common/rlp-derive",
"crates/consensus",
"crates/db",
"crates/executor",
"crates/interfaces",

View 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 }

View 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 }
}
}

View 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
}
}

View 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;

View 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

View 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"
);
}
}

View File

@ -7,9 +7,24 @@ repository = "https://github.com/foundry-rs/reth"
readme = "README.md"
[dependencies]
revm = "2.1"
# reth
reth-primitives = { path = "../primitives" }
reth-interfaces = { path = "../interfaces" }
reth-rlp = {path = "../common/rlp"}
revm = "2.1"
# common
async-trait = "0.1.57"
thiserror = "1.0.37"
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 }

View File

@ -1,6 +0,0 @@
/// Configuration for executor (TODO)
#[derive(Debug, Clone)]
pub struct Config {
/// Example
pub example: bool,
}

View File

@ -0,0 +1,5 @@
//! Reth block execution/validation configuration and constants
/// Configuration for executor
#[derive(Debug, Clone)]
pub struct Config {}

View File

@ -1,41 +1,53 @@
use crate::{revm_wrap, Config};
use reth_interfaces::{
consensus::Consensus,
executor::{BlockExecutor, Error, ExecutorDb},
use crate::{
revm_wrap::{self, State, SubState},
Config,
};
use reth_interfaces::executor::{BlockExecutor, Error, ExecutorDb};
use reth_primitives::BlockLocked;
use revm::{db::EmptyDB, AnalysisKind, Env, SpecId};
use revm::{AnalysisKind, SpecId, EVM};
/// Main block executor
pub struct Executor {
/// Configuration, Spec and optional flags.
pub config: Config,
/// Database
pub db: Box<dyn ExecutorDb>,
/// Consensus
pub consensus: Box<dyn Consensus>,
}
impl Executor {
/// Create new Executor
pub fn new(config: Config, db: Box<dyn ExecutorDb>, consensus: Box<dyn Consensus>) -> Self {
Self { config, db, consensus }
pub fn new(config: Config) -> Self {
Self { config }
}
/// Verify block. Execute all transaction and compare results.
pub fn verify(&self, block: &BlockLocked) -> Result<(), Error> {
let mut env = Env::default();
env.cfg.chain_id = 1.into();
env.cfg.spec_id = SpecId::LATEST;
env.cfg.perf_all_precompiles_have_balance = true;
env.cfg.perf_analyse_created_bytecodes = AnalysisKind::Raw;
pub fn verify<DB: ExecutorDb>(&self, block: &BlockLocked, db: DB) -> Result<(), Error> {
let db = SubState::new(State::new(db));
let mut evm = EVM::new();
evm.database(db);
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() {
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)

View File

@ -7,10 +7,9 @@
//! Reth executor executes transaction in block of data.
mod cfg;
pub mod config;
/// Executor
pub mod executor;
/// Wrapper around revm database and types
pub mod revm_wrap;
pub use cfg::Config;
pub use config::Config;

View File

@ -1,21 +1,43 @@
use reth_interfaces::executor::ExecutorDb;
use reth_primitives::{BlockLocked, Transaction, TransactionKind, H160, H256, U256};
use revm::{
db::{CacheDB, Database, EmptyDB},
db::{CacheDB, DatabaseRef},
BlockEnv, TransactTo, TxEnv,
};
use std::convert::Infallible;
/// Temporary stateDB TODO
pub type TempStateDb = CacheDB<EmptyDB>;
/// SubState of database. Uses revm internal cache with binding to reth DbExecutor trait.
pub type SubState<DB> = CacheDB<State<DB>>;
/// 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;
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 {
balance: account.balance,
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();
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();
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()))
}
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())
}
}

View File

@ -1,6 +1,5 @@
use async_trait::async_trait;
use reth_primitives::Header;
use thiserror::Error;
use reth_primitives::{BlockHash, BlockNumber, HeaderLocked, H256};
use tokio::sync::watch::Receiver;
/// Re-export forkchoice state
@ -15,13 +14,40 @@ pub trait Consensus: Send + Sync {
fn fork_choice_state(&self) -> Receiver<ForkchoiceState>;
/// 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)
#[derive(Error, Debug)]
/// Consensus Errors
#[allow(missing_docs)]
#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
pub enum Error {
/// Explanatory
#[error("Example of consensus error")]
ConsensusError,
#[error("Block used gas ({gas_used:?}) is greater then gas limit ({gas_limit:?})")]
HeaderGasUsedExceedsGasLimit { gas_used: u64, gas_limit: u64 },
#[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 },
}

View File

@ -2,7 +2,7 @@
pub type Result<T> = std::result::Result<T, Error>;
/// Core error variants possible when interacting with the blockchain
#[derive(Debug, thiserror::Error)]
#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]

View File

@ -14,7 +14,7 @@ pub trait BlockExecutor {
}
/// BlockExecutor Errors
#[derive(Error, Debug, Clone)]
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum Error {
/// Example of error
#[error("Example of error.")]

View File

@ -1,9 +1,22 @@
use crate::Result;
use auto_impl::auto_impl;
use reth_primitives::{
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.
pub trait BlockProvider: Send + Sync + 'static {
/// Returns the current info for the chain.

View File

@ -1,5 +1,5 @@
mod block;
mod storage;
pub use block::BlockProvider;
pub use block::{BlockProvider, HeaderProvider};
pub use storage::StorageProvider;

View File

@ -155,9 +155,13 @@ impl Consensus for TestConsensus {
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 {
Err(consensus::Error::ConsensusError)
Err(consensus::Error::BaseFeeMissing)
} else {
Ok(())
}

View File

@ -172,8 +172,8 @@ where
/// of used pages as well as free pages in this environment.
///
/// ```
/// # use libmdbx::Environment;
/// # use libmdbx::NoWriteMap;
/// # use reth_libmdbx::Environment;
/// # use reth_libmdbx::NoWriteMap;
/// let dir = tempfile::tempdir().unwrap();
/// let env = Environment::<NoWriteMap>::new().open(dir.path()).unwrap();
/// let info = env.info().unwrap();

View File

@ -159,7 +159,7 @@ impl Discv4 {
/// let(discv4, mut service) = Discv4::bind(socket, local_enr, secret_key, config).await.unwrap();
///
/// // get an update strea
/// let mut updates = service.update_stream();
/// let updates = service.update_stream();
///
/// let _handle = service.spawn();
///

View File

@ -28,6 +28,7 @@ thiserror = "1"
sucds = "0.5.0"
arbitrary = { version = "1.1.7", features = ["derive"], optional = true}
hex = "0.4"
hex-literal = "0.3"
[dev-dependencies]
arbitrary = { version = "1.1.7", features = ["derive"]}

View File

@ -10,6 +10,8 @@ pub struct Block {
pub body: Vec<Transaction>,
/// Block receipts.
pub receipts: Vec<Receipt>,
/// Ommers/uncles header
pub ommers: Vec<HeaderLocked>,
}
impl Deref for Block {
@ -28,6 +30,8 @@ pub struct BlockLocked {
pub body: Vec<TransactionSigned>,
/// Block receipts.
pub receipts: Vec<Receipt>,
/// Omners/uncles header
pub ommers: Vec<HeaderLocked>,
}
impl BlockLocked {

View File

@ -1,4 +1,4 @@
use crate::{BlockNumber, Bloom, H160, H256, U256};
use crate::{BlockHash, BlockNumber, Bloom, H160, H256, U256};
use bytes::{BufMut, BytesMut};
use ethers_core::{types::H64, utils::keccak256};
use reth_codecs::main_codec;
@ -180,7 +180,7 @@ pub struct HeaderLocked {
/// Locked Header fields.
header: Header,
/// Locked Header hash.
hash: H256,
hash: BlockHash,
}
impl AsRef<Header> for HeaderLocked {
@ -211,7 +211,7 @@ impl HeaderLocked {
}
/// Return header/block hash.
pub fn hash(&self) -> H256 {
pub fn hash(&self) -> BlockHash {
self.hash
}
}

View File

@ -60,6 +60,7 @@ pub use ethers_core::{
#[doc(hidden)]
mod __reexport {
pub use hex;
pub use hex_literal;
pub use tiny_keccak;
}