Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Consensus crate and verification functions. #152

Merged
merged 13 commits into from
Nov 2, 2022
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