Skip to content

Commit

Permalink
feat: Consensus crate and verification functions. (#152)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rakita committed Nov 2, 2022
1 parent 1ea98d4 commit ac2f3fc
Show file tree
Hide file tree
Showing 26 changed files with 755 additions and 59 deletions.
51 changes: 51 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"crate-template",
"crates/common/rlp",
"crates/common/rlp-derive",
"crates/consensus",
"crates/db",
"crates/executor",
"crates/interfaces",
Expand Down
30 changes: 30 additions & 0 deletions crates/consensus/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 changes: 24 additions & 0 deletions crates/consensus/src/config.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 43 additions & 0 deletions crates/consensus/src/consensus.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions crates/consensus/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 96 additions & 0 deletions crates/consensus/src/proofs.rs
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ac2f3fc

Please sign in to comment.