Consensus engine refactor (#48)

This commit is contained in:
Artem Vorotnikov
2021-10-18 09:56:39 +03:00
committed by GitHub
parent 6e8b74e56b
commit 86bf61fb16
18 changed files with 817 additions and 597 deletions

View File

@@ -24,7 +24,7 @@ clap = "2"
console-subscriber = { git = "https://github.com/tokio-rs/console" }
crossterm = { version = "0.21", optional = true }
derive_more = "0.99"
educe = { version = "0.4", features = ["Debug"] }
educe = { version = "0.4", features = ["Debug", "Default"] }
ethereum-interfaces = { git = "https://github.com/ledgerwatch/interfaces", features = [
"remotekv",
"sentry",

View File

@@ -1,12 +1,7 @@
#![allow(clippy::suspicious_else_formatting)]
use akula::{
chain::{
blockchain::Blockchain,
config::*,
consensus::{Consensus, NoProof},
difficulty::canonical_difficulty,
validity::pre_validate_transaction,
},
chain::{config::*, difficulty::canonical_difficulty},
consensus::*,
crypto::keccak256,
models::*,
*,
@@ -128,21 +123,25 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
hashmap! {
Network::Frontier => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
..ChainConfig::default()
},
Network::Homestead => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
..ChainConfig::default()
},
Network::EIP150 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
..ChainConfig::default()
},
Network::EIP158 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -150,6 +149,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::Byzantium => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -158,6 +158,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::Constantinople => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -167,6 +168,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::ConstantinopleFix => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -177,6 +179,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::Istanbul => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -188,6 +191,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::Berlin => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -201,6 +205,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::London => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -215,17 +220,20 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::FrontierToHomesteadAt5 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(5.into()),
..ChainConfig::default()
},
Network::HomesteadToEIP150At5 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(5.into()),
..ChainConfig::default()
},
Network::HomesteadToDaoAt5 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
dao_fork: Some(DaoConfig {
block_number: 5.into(),
@@ -235,6 +243,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::EIP158ToByzantiumAt5 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -243,6 +252,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::ByzantiumToConstantinopleFixAt5 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -253,6 +263,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::BerlinToLondonAt5 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -267,6 +278,7 @@ static NETWORK_CONFIG: Lazy<HashMap<Network, ChainConfig>> = Lazy::new(|| {
},
Network::EIP2384 => ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::NoProof,
homestead_block: Some(0.into()),
tangerine_block: Some(0.into()),
spurious_block: Some(0.into()),
@@ -471,23 +483,17 @@ struct BlockCommon {
}
#[instrument(skip(block_common, blockchain))]
async fn run_block<'state, C>(
consensus: &C,
async fn run_block<'state>(
block_common: &BlockCommon,
blockchain: &mut Blockchain<'state>,
) -> anyhow::Result<()>
where
C: Consensus,
{
) -> anyhow::Result<()> {
let block = rlp::decode::<Block>(&block_common.rlp)?;
debug!("Running block {:?}", block);
let check_state_root = true;
blockchain
.insert_block(consensus, block, check_state_root)
.await?;
blockchain.insert_block(block, check_state_root).await?;
Ok(())
}
@@ -588,8 +594,6 @@ async fn blockchain_test(testdata: BlockchainTest, _: Option<ChainConfig>) -> an
let mut state = InMemoryState::default();
let config = NETWORK_CONFIG[&testdata.network].clone();
let consensus = NoProof;
init_pre_state(&testdata.pre, &mut state).await;
let mut blockchain = Blockchain::new(&mut state, config, genesis_block)
@@ -600,7 +604,7 @@ async fn blockchain_test(testdata: BlockchainTest, _: Option<ChainConfig>) -> an
let block_common =
serde_json::from_value::<BlockCommon>(Value::Object(block.clone())).unwrap();
result_is_expected(
run_block(&consensus, &block_common, &mut blockchain).await,
run_block(&block_common, &mut blockchain).await,
block_common.expect_exception,
)?;
}

View File

@@ -6,6 +6,7 @@ use once_cell::sync::Lazy;
pub static MAINNET_CONFIG: Lazy<ChainConfig> = Lazy::new(|| ChainConfig {
chain_id: 1,
seal_engine: SealEngineType::Ethash,
homestead_block: Some(1_150_000.into()),
dao_fork: Some(DaoConfig {
block_number: 1_920_000.into(),
@@ -142,6 +143,7 @@ pub static MAINNET_CONFIG: Lazy<ChainConfig> = Lazy::new(|| ChainConfig {
pub static ROPSTEN_CONFIG: Lazy<ChainConfig> = Lazy::new(|| ChainConfig {
chain_id: 3,
seal_engine: SealEngineType::Ethash,
homestead_block: Some(0.into()),
dao_fork: None,
tangerine_block: Some(0.into()),

View File

@@ -1,45 +0,0 @@
use crate::models::*;
use async_trait::*;
use std::fmt::Debug;
#[async_trait]
pub trait Consensus: Debug + Send + Sync {
async fn verify_header(&self, header: &BlockHeader) -> anyhow::Result<()>;
}
#[derive(Debug)]
pub struct NoProof;
#[async_trait]
impl Consensus for NoProof {
async fn verify_header(&self, _: &BlockHeader) -> anyhow::Result<()> {
Ok(())
}
}
pub type Clique = NoProof;
pub type AuRa = NoProof;
#[derive(Debug)]
pub struct Ethash;
#[async_trait]
impl Consensus for Ethash {
async fn verify_header(&self, header: &BlockHeader) -> anyhow::Result<()> {
let _ = header;
// TODO: port Ethash PoW verification
// let epoch_number = {header.number / ethash::epoch_length};
// auto epoch_context{ethash::create_epoch_context(static_cast<int>(epoch_number))};
// auto boundary256{header.boundary()};
// auto seal_hash(header.hash(/*for_sealing =*/true));
// ethash::hash256 sealh256{*reinterpret_cast<ethash::hash256*>(seal_hash.bytes)};
// ethash::hash256 mixh256{};
// std::memcpy(mixh256.bytes, header.mix_hash.bytes, 32);
// uint64_t nonce{endian::load_big_u64(header.nonce.data())};
// return ethash::verify(*epoch_context, sealh256, mixh256, nonce, boundary256) ? ValidationError::Ok
// : ValidationError::InvalidSeal;
Ok(())
}
}

View File

@@ -1,8 +1,5 @@
pub mod blockchain;
pub mod config;
pub mod consensus;
pub mod dao;
pub mod difficulty;
pub mod intrinsic_gas;
pub mod protocol_param;
pub mod validity;

View File

@@ -1,467 +0,0 @@
use super::{consensus::*, difficulty::*, intrinsic_gas::*, protocol_param::param};
use crate::{models::*, state::*};
use anyhow::Context;
use async_recursion::*;
use ethereum_types::*;
use evmodin::Revision;
use std::fmt::Display;
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum ValidationError {
// See [YP] Section 4.3.2 "Holistic Validity", Eq (31)
WrongStateRoot {
expected: H256,
got: H256,
}, // wrong Hr
WrongOmmersHash {
expected: H256,
got: H256,
}, // wrong Ho
WrongTransactionsRoot {
expected: H256,
got: H256,
}, // wrong Ht
WrongReceiptsRoot {
expected: H256,
got: H256,
}, // wrong He
WrongLogsBloom {
expected: Bloom,
got: Bloom,
}, // wrong Hb
// See [YP] Section 4.3.4 "Block Header Validity", Eq (50)
UnknownParent, // P(H) = ∅ Hi ≠ P(H)Hi + 1
WrongDifficulty, // Hd ≠ D(H)
GasAboveLimit {
used: u64,
limit: u64,
}, // Hg > Hl
InvalidGasLimit, // |Hl-P(H)Hl|≥P(H)Hl/1024 Hl<5000
InvalidTimestamp {
parent: u64,
current: u64,
}, // Hs ≤ P(H)Hs
ExtraDataTooLong, // ‖Hx‖ > 32
WrongDaoExtraData, // see EIP-779
WrongBaseFee {
expected: Option<U256>,
got: Option<U256>,
}, // see EIP-1559
InvalidSeal, // Nonce or mix_hash
// See [YP] Section 6.2 "Execution", Eq (58)
MissingSender, // S(T) = ∅
SenderNoEOA {
sender: Address,
}, // EIP-3607: σ[S(T)]c ≠ KEC( () )
WrongNonce {
account: Address,
expected: u64,
got: u64,
}, // Tn ≠ σ[S(T)]n
IntrinsicGas, // g0 > Tg
InsufficientFunds {
account: Address,
available: U512,
required: U512,
}, // v0 > σ[S(T)]b
BlockGasLimitExceeded {
available: u64,
required: u64,
}, // Tg > BHl - l(BR)u
MaxFeeLessThanBase, // max_fee_per_gas < base_fee_per_gas (EIP-1559)
MaxPriorityFeeGreaterThanMax, // max_priority_fee_per_gas > max_fee_per_gas (EIP-1559)
// See [YP] Section 11.1 "Ommer Validation", Eq (157)
TooManyOmmers, // ‖BU‖ > 2
InvalidOmmerHeader, // ¬V(U)
NotAnOmmer, // ¬k(U, P(BH)H, 6)
DuplicateOmmer, // not well covered by the YP actually
// See [YP] Section 11.2 "Transaction Validation", Eq (160)
WrongBlockGas {
expected: u64,
got: u64,
}, // BHg ≠ l(BR)u
InvalidSignature, // EIP-2
WrongChainId, // EIP-155
UnsupportedTransactionType, // EIP-2718
}
impl Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for ValidationError {}
pub fn pre_validate_transaction(
txn: &TransactionMessage,
block_number: impl Into<BlockNumber>,
config: &ChainConfig,
base_fee_per_gas: Option<U256>,
) -> Result<(), ValidationError> {
let rev = config.revision(block_number);
if let Some(chain_id) = txn.chain_id() {
if rev < Revision::Spurious || chain_id != config.chain_id {
return Err(ValidationError::WrongChainId);
}
}
match txn.tx_type() {
TxType::EIP2930 => {
if rev < Revision::Berlin {
return Err(ValidationError::UnsupportedTransactionType);
}
}
TxType::EIP1559 => {
if rev < Revision::London {
return Err(ValidationError::UnsupportedTransactionType);
}
}
TxType::Legacy => {}
}
if let Some(base_fee_per_gas) = base_fee_per_gas {
if txn.max_fee_per_gas() < base_fee_per_gas {
return Err(ValidationError::MaxFeeLessThanBase);
}
}
// https://github.com/ethereum/EIPs/pull/3594
if txn.max_priority_fee_per_gas() > txn.max_fee_per_gas() {
return Err(ValidationError::MaxPriorityFeeGreaterThanMax);
}
let g0 = intrinsic_gas(txn, rev >= Revision::Homestead, rev >= Revision::Istanbul);
if u128::from(txn.gas_limit()) < g0 {
return Err(ValidationError::IntrinsicGas);
}
Ok(())
}
async fn get_parent<S>(state: &S, header: &BlockHeader) -> anyhow::Result<Option<BlockHeader>>
where
S: State,
{
if let Some(parent_number) = header.number.0.checked_sub(1) {
return state
.read_header(parent_number.into(), header.parent_hash)
.await;
}
Ok(None)
}
// https://eips.ethereum.org/EIPS/eip-1559
fn expected_base_fee_per_gas(
header: &BlockHeader,
parent: &BlockHeader,
config: &ChainConfig,
) -> Option<U256> {
if let Some(fork_block) = config.london_block {
if header.number >= fork_block {
if header.number == fork_block {
return Some(param::INITIAL_BASE_FEE.into());
}
let parent_gas_target = parent.gas_limit / param::ELASTICITY_MULTIPLIER;
let parent_base_fee_per_gas = parent.base_fee_per_gas.unwrap();
if parent.gas_used == parent_gas_target {
return Some(parent_base_fee_per_gas);
}
if parent.gas_used > parent_gas_target {
let gas_used_delta = parent.gas_used - parent_gas_target;
let base_fee_per_gas_delta = std::cmp::max(
U256::one(),
parent_base_fee_per_gas * U256::from(gas_used_delta)
/ U256::from(parent_gas_target)
/ U256::from(param::BASE_FEE_MAX_CHANGE_DENOMINATOR),
);
return Some(parent_base_fee_per_gas + base_fee_per_gas_delta);
} else {
let gas_used_delta = parent_gas_target - parent.gas_used;
let base_fee_per_gas_delta = parent_base_fee_per_gas * U256::from(gas_used_delta)
/ U256::from(parent_gas_target)
/ U256::from(param::BASE_FEE_MAX_CHANGE_DENOMINATOR);
return Some(parent_base_fee_per_gas.saturating_sub(base_fee_per_gas_delta));
}
}
}
None
}
async fn validate_block_header<C: Consensus, S: State>(
consensus: &C,
header: &BlockHeader,
state: &S,
config: &ChainConfig,
) -> anyhow::Result<()> {
if header.gas_used > header.gas_limit {
return Err(ValidationError::GasAboveLimit {
used: header.gas_used,
limit: header.gas_limit,
}
.into());
}
if header.gas_limit < 5000 {
return Err(ValidationError::InvalidGasLimit.into());
}
// https://github.com/ethereum/go-ethereum/blob/v1.9.25/consensus/ethash/consensus.go#L267
// https://eips.ethereum.org/EIPS/eip-1985
if header.gas_limit > 0x7fffffffffffffff {
return Err(ValidationError::InvalidGasLimit.into());
}
if header.extra_data.len() > 32 {
return Err(ValidationError::ExtraDataTooLong.into());
}
let parent = get_parent(state, header)
.await?
.ok_or(ValidationError::UnknownParent)?;
if header.timestamp <= parent.timestamp {
return Err(ValidationError::InvalidTimestamp {
parent: parent.timestamp,
current: header.timestamp,
}
.into());
}
let mut parent_gas_limit = parent.gas_limit;
if let Some(fork_block) = config.london_block {
if fork_block == header.number {
parent_gas_limit = parent.gas_limit * param::ELASTICITY_MULTIPLIER; // EIP-1559
}
}
let gas_delta = if header.gas_limit > parent_gas_limit {
header.gas_limit - parent_gas_limit
} else {
parent_gas_limit - header.gas_limit
};
if gas_delta >= parent_gas_limit / 1024 {
return Err(ValidationError::InvalidGasLimit.into());
}
let parent_has_uncles = parent.ommers_hash != EMPTY_LIST_HASH;
let difficulty = canonical_difficulty(
header.number,
header.timestamp,
parent.difficulty,
parent.timestamp,
parent_has_uncles,
config,
);
if difficulty != header.difficulty {
return Err(ValidationError::WrongDifficulty.into());
}
let expected_base_fee_per_gas = expected_base_fee_per_gas(header, &parent, config);
if header.base_fee_per_gas != expected_base_fee_per_gas {
return Err(ValidationError::WrongBaseFee {
expected: expected_base_fee_per_gas,
got: header.base_fee_per_gas,
}
.into());
}
consensus.verify_header(header).await?;
Ok(())
}
// See [YP] Section 11.1 "Ommer Validation"
#[async_recursion]
async fn is_kin<S: State>(
branch_header: &BlockHeader,
mainline_header: &BlockHeader,
mainline_hash: H256,
n: usize,
state: &S,
old_ommers: &mut Vec<BlockHeader>,
) -> anyhow::Result<bool> {
if n > 0 && branch_header != mainline_header {
if let Some(mainline_body) = state
.read_body(mainline_header.number, mainline_hash)
.await?
{
old_ommers.extend_from_slice(&mainline_body.ommers);
let mainline_parent = get_parent(state, mainline_header).await?;
let branch_parent = get_parent(state, branch_header).await?;
if let Some(mainline_parent) = mainline_parent {
if let Some(branch_parent) = branch_parent {
if branch_parent == mainline_parent {
return Ok(true);
}
}
return is_kin(
branch_header,
&mainline_parent,
mainline_header.parent_hash,
n - 1,
state,
old_ommers,
)
.await;
}
}
}
Ok(false)
}
pub async fn pre_validate_block<C: Consensus, S: State>(
consensus: &C,
block: &Block,
state: &S,
config: &ChainConfig,
) -> anyhow::Result<()> {
validate_block_header(consensus, &block.header, state, config).await?;
let expected_ommers_hash = Block::ommers_hash(&block.ommers);
if block.header.ommers_hash != expected_ommers_hash {
return Err(ValidationError::WrongOmmersHash {
expected: expected_ommers_hash,
got: block.header.ommers_hash,
}
.into());
}
let expected_transactions_root = Block::transactions_root(&block.transactions);
if block.header.transactions_root != expected_transactions_root {
return Err(ValidationError::WrongTransactionsRoot {
expected: expected_transactions_root,
got: block.header.transactions_root,
}
.into());
}
if block.ommers.len() > 2 {
return Err(ValidationError::TooManyOmmers.into());
}
if block.ommers.len() == 2 && block.ommers[0] == block.ommers[1] {
return Err(ValidationError::DuplicateOmmer.into());
}
let parent = get_parent(state, &block.header).await?.unwrap();
for ommer in &block.ommers {
validate_block_header(consensus, ommer, state, config)
.await
.context(ValidationError::InvalidOmmerHeader)?;
let mut old_ommers = vec![];
if !is_kin(
ommer,
&parent,
block.header.parent_hash,
6,
state,
&mut old_ommers,
)
.await?
{
return Err(ValidationError::NotAnOmmer.into());
}
for oo in old_ommers {
if oo == *ommer {
return Err(ValidationError::DuplicateOmmer.into());
}
}
}
for txn in &block.transactions {
pre_validate_transaction(
txn,
block.header.number,
config,
block.header.base_fee_per_gas,
)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::chain::config::MAINNET_CONFIG;
use super::*;
#[test]
fn validate_max_fee_per_gas() {
let base_fee_per_gas = 1_000_000_000_u64;
for (max_priority_fee_per_gas, max_fee_per_gas, error, not) in [
(
500_000_000_u64,
700_000_000_u64,
ValidationError::MaxFeeLessThanBase,
false,
),
(
3_000_000_000_u64,
2_000_000_000_u64,
ValidationError::MaxPriorityFeeGreaterThanMax,
false,
),
(
2_000_000_000_u64,
2_000_000_000_u64,
ValidationError::MaxPriorityFeeGreaterThanMax,
true,
),
(
1_000_000_000_u64,
2_000_000_000_u64,
ValidationError::MaxPriorityFeeGreaterThanMax,
true,
),
] {
let txn = TransactionMessage::EIP1559 {
chain_id: 1,
nonce: 0,
max_priority_fee_per_gas: max_priority_fee_per_gas.into(),
max_fee_per_gas: max_fee_per_gas.into(),
gas_limit: 0,
action: TransactionAction::Create,
value: U256::zero(),
input: vec![].into(),
access_list: vec![],
};
let res = pre_validate_transaction(
&txn,
13_500_001,
&*MAINNET_CONFIG,
Some(base_fee_per_gas.into()),
);
if not {
assert_ne!(res, Err(error));
} else {
assert_eq!(res, Err(error));
}
}
}
}

360
src/consensus/base.rs Normal file
View File

@@ -0,0 +1,360 @@
use super::*;
use crate::{
chain::{difficulty::*, protocol_param::param},
models::*,
state::*,
};
use anyhow::Context;
use async_recursion::*;
use ethereum_types::*;
use std::time::SystemTime;
#[derive(Debug)]
pub struct ConsensusEngineBase {
chain_config: ChainConfig,
}
impl ConsensusEngineBase {
pub fn new(chain_config: ChainConfig) -> Self {
Self { chain_config }
}
pub async fn validate_block_header(
&self,
header: &BlockHeader,
state: &mut dyn State,
with_future_timestamp_check: bool,
) -> anyhow::Result<()> {
if with_future_timestamp_check {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs();
if header.timestamp > now {
return Err(ValidationError::FutureBlock {
now,
got: header.timestamp,
}
.into());
}
}
if header.gas_used > header.gas_limit {
return Err(ValidationError::GasAboveLimit {
used: header.gas_used,
limit: header.gas_limit,
}
.into());
}
if header.gas_limit < 5000 {
return Err(ValidationError::InvalidGasLimit.into());
}
// https://github.com/ethereum/go-ethereum/blob/v1.9.25/consensus/ethash/consensus.go#L267
// https://eips.ethereum.org/EIPS/eip-1985
if header.gas_limit > i64::MAX.try_into().unwrap() {
return Err(ValidationError::InvalidGasLimit.into());
}
if header.extra_data.len() > 32 {
return Err(ValidationError::ExtraDataTooLong.into());
}
let parent = self
.get_parent_header(state, header)
.await?
.ok_or(ValidationError::UnknownParent)?;
if header.timestamp <= parent.timestamp {
return Err(ValidationError::InvalidTimestamp {
parent: parent.timestamp,
current: header.timestamp,
}
.into());
}
let mut parent_gas_limit = parent.gas_limit;
if let Some(fork_block) = self.chain_config.london_block {
if fork_block == header.number {
parent_gas_limit = parent.gas_limit * param::ELASTICITY_MULTIPLIER;
}
}
let gas_delta = if header.gas_limit > parent_gas_limit {
header.gas_limit - parent_gas_limit
} else {
parent_gas_limit - header.gas_limit
};
if gas_delta >= parent_gas_limit / 1024 {
return Err(ValidationError::InvalidGasLimit.into());
}
let parent_has_uncles = parent.ommers_hash != EMPTY_LIST_HASH;
let difficulty = canonical_difficulty(
header.number,
header.timestamp,
parent.difficulty,
parent.timestamp,
parent_has_uncles,
&self.chain_config,
);
if difficulty != header.difficulty {
return Err(ValidationError::WrongDifficulty.into());
}
let expected_base_fee_per_gas = self.expected_base_fee_per_gas(header, &parent);
if header.base_fee_per_gas != expected_base_fee_per_gas {
return Err(ValidationError::WrongBaseFee {
expected: expected_base_fee_per_gas,
got: header.base_fee_per_gas,
}
.into());
}
Ok(())
}
async fn get_parent_header(
&self,
state: &mut dyn State,
header: &BlockHeader,
) -> anyhow::Result<Option<BlockHeader>> {
if let Some(parent_number) = header.number.0.checked_sub(1) {
return state
.read_header(parent_number.into(), header.parent_hash)
.await;
}
Ok(None)
}
// See [YP] Section 11.1 "Ommer Validation"
#[async_recursion]
async fn is_kin(
&self,
branch_header: &BlockHeader,
mainline_header: &BlockHeader,
mainline_hash: H256,
n: usize,
state: &mut dyn State,
old_ommers: &mut Vec<BlockHeader>,
) -> anyhow::Result<bool> {
if n > 0 && branch_header != mainline_header {
if let Some(mainline_body) = state
.read_body(mainline_header.number, mainline_hash)
.await?
{
old_ommers.extend_from_slice(&mainline_body.ommers);
let mainline_parent = self.get_parent_header(state, mainline_header).await?;
let branch_parent = self.get_parent_header(state, branch_header).await?;
if let Some(mainline_parent) = mainline_parent {
if let Some(branch_parent) = branch_parent {
if branch_parent == mainline_parent {
return Ok(true);
}
}
return self
.is_kin(
branch_header,
&mainline_parent,
mainline_header.parent_hash,
n - 1,
state,
old_ommers,
)
.await;
}
}
}
Ok(false)
}
pub fn get_beneficiary(&self, header: &BlockHeader) -> Address {
header.beneficiary
}
// https://eips.ethereum.org/EIPS/eip-1559
fn expected_base_fee_per_gas(
&self,
header: &BlockHeader,
parent: &BlockHeader,
) -> Option<U256> {
if let Some(fork_block) = self.chain_config.london_block {
if header.number >= fork_block {
if header.number == fork_block {
return Some(param::INITIAL_BASE_FEE.into());
}
let parent_gas_target = parent.gas_limit / param::ELASTICITY_MULTIPLIER;
let parent_base_fee_per_gas = parent.base_fee_per_gas.unwrap();
if parent.gas_used == parent_gas_target {
return Some(parent_base_fee_per_gas);
}
if parent.gas_used > parent_gas_target {
let gas_used_delta = parent.gas_used - parent_gas_target;
let base_fee_per_gas_delta = std::cmp::max(
U256::one(),
parent_base_fee_per_gas * U256::from(gas_used_delta)
/ U256::from(parent_gas_target)
/ U256::from(param::BASE_FEE_MAX_CHANGE_DENOMINATOR),
);
return Some(parent_base_fee_per_gas + base_fee_per_gas_delta);
} else {
let gas_used_delta = parent_gas_target - parent.gas_used;
let base_fee_per_gas_delta = parent_base_fee_per_gas
* U256::from(gas_used_delta)
/ U256::from(parent_gas_target)
/ U256::from(param::BASE_FEE_MAX_CHANGE_DENOMINATOR);
return Some(parent_base_fee_per_gas.saturating_sub(base_fee_per_gas_delta));
}
}
}
None
}
pub async fn pre_validate_block(
&self,
block: &Block,
state: &mut dyn State,
) -> anyhow::Result<()> {
self.validate_block_header(&block.header, state, true)
.await?;
let expected_ommers_hash = Block::ommers_hash(&block.ommers);
if block.header.ommers_hash != expected_ommers_hash {
return Err(ValidationError::WrongOmmersHash {
expected: expected_ommers_hash,
got: block.header.ommers_hash,
}
.into());
}
let expected_transactions_root = Block::transactions_root(&block.transactions);
if block.header.transactions_root != expected_transactions_root {
return Err(ValidationError::WrongTransactionsRoot {
expected: expected_transactions_root,
got: block.header.transactions_root,
}
.into());
}
if block.ommers.len() > 2 {
return Err(ValidationError::TooManyOmmers.into());
}
if block.ommers.len() == 2 && block.ommers[0] == block.ommers[1] {
return Err(ValidationError::DuplicateOmmer.into());
}
let parent = self.get_parent_header(state, &block.header).await?.unwrap();
for ommer in &block.ommers {
self.validate_block_header(ommer, state, false)
.await
.context(ValidationError::InvalidOmmerHeader)?;
let mut old_ommers = vec![];
if !self
.is_kin(
ommer,
&parent,
block.header.parent_hash,
6,
state,
&mut old_ommers,
)
.await?
{
return Err(ValidationError::NotAnOmmer.into());
}
for oo in old_ommers {
if oo == *ommer {
return Err(ValidationError::DuplicateOmmer.into());
}
}
}
for txn in &block.transactions {
pre_validate_transaction(
txn,
block.header.number,
&self.chain_config,
block.header.base_fee_per_gas,
)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::chain::config::MAINNET_CONFIG;
use super::*;
#[test]
fn validate_max_fee_per_gas() {
let base_fee_per_gas = 1_000_000_000_u64;
for (max_priority_fee_per_gas, max_fee_per_gas, error, not) in [
(
500_000_000_u64,
700_000_000_u64,
ValidationError::MaxFeeLessThanBase,
false,
),
(
3_000_000_000_u64,
2_000_000_000_u64,
ValidationError::MaxPriorityFeeGreaterThanMax,
false,
),
(
2_000_000_000_u64,
2_000_000_000_u64,
ValidationError::MaxPriorityFeeGreaterThanMax,
true,
),
(
1_000_000_000_u64,
2_000_000_000_u64,
ValidationError::MaxPriorityFeeGreaterThanMax,
true,
),
] {
let txn = TransactionMessage::EIP1559 {
chain_id: 1,
nonce: 0,
max_priority_fee_per_gas: max_priority_fee_per_gas.into(),
max_fee_per_gas: max_fee_per_gas.into(),
gas_limit: 0,
action: TransactionAction::Create,
value: U256::zero(),
input: vec![].into(),
access_list: vec![],
};
let res = pre_validate_transaction(
&txn,
13_500_001,
&*MAINNET_CONFIG,
Some(base_fee_per_gas.into()),
);
if not {
assert_ne!(res, Err(error));
} else {
assert_eq!(res, Err(error));
}
}
}
}

View File

@@ -1,10 +1,4 @@
use super::validity::ValidationError;
use crate::{
chain::{consensus::Consensus, validity::pre_validate_block},
execution::processor::ExecutionProcessor,
models::*,
state::*,
};
use crate::{consensus::*, execution::processor::ExecutionProcessor, models::*, state::*};
use anyhow::Context;
use async_recursion::async_recursion;
use ethereum_types::*;
@@ -14,6 +8,7 @@ use std::{collections::HashMap, convert::TryFrom};
pub struct Blockchain<'state> {
state: &'state mut InMemoryState,
config: ChainConfig,
engine: Box<dyn Consensus>,
bad_blocks: HashMap<H256, ValidationError>,
receipts: Vec<Receipt>,
}
@@ -23,6 +18,21 @@ impl<'state> Blockchain<'state> {
state: &'state mut InMemoryState,
config: ChainConfig,
genesis_block: Block,
) -> anyhow::Result<Blockchain<'state>> {
Self::new_with_consensus(
state,
engine_factory(config.clone())?,
config,
genesis_block,
)
.await
}
pub async fn new_with_consensus(
state: &'state mut InMemoryState,
engine: Box<dyn Consensus>,
config: ChainConfig,
genesis_block: Block,
) -> anyhow::Result<Blockchain<'state>> {
let hash = genesis_block.header.hash();
let number = genesis_block.header.number;
@@ -31,22 +41,21 @@ impl<'state> Blockchain<'state> {
Ok(Self {
state,
engine,
config,
bad_blocks: Default::default(),
receipts: Default::default(),
})
}
pub async fn insert_block<C>(
pub async fn insert_block(
&mut self,
consensus: &C,
block: Block,
check_state_root: bool,
) -> anyhow::Result<()>
where
C: Consensus,
{
pre_validate_block(consensus, &block, self.state, &self.config).await?;
) -> anyhow::Result<()> {
self.engine
.pre_validate_block(&block, &mut self.state)
.await?;
let hash = block.header.hash();
if let Some(error) = self.bad_blocks.get(&hash) {
@@ -140,7 +149,13 @@ impl<'state> Blockchain<'state> {
ommers: block.ommers.clone(),
};
let processor = ExecutionProcessor::new(self.state, &block.header, &body, &self.config);
let processor = ExecutionProcessor::new(
self.state,
&mut *self.engine,
&block.header,
&body,
&self.config,
);
let _ = processor.execute_and_write_block().await?;

78
src/consensus/ethash.rs Normal file
View File

@@ -0,0 +1,78 @@
use super::{base::ConsensusEngineBase, *};
use crate::{chain::protocol_param::param, models::ChainConfig};
use async_trait::async_trait;
#[derive(Debug)]
pub struct Ethash {
base: ConsensusEngineBase,
}
impl Ethash {
pub fn new(chain_config: ChainConfig) -> Self {
Self {
base: ConsensusEngineBase::new(chain_config),
}
}
}
#[async_trait]
impl Consensus for Ethash {
async fn pre_validate_block(&self, block: &Block, state: &mut dyn State) -> anyhow::Result<()> {
self.base.pre_validate_block(block, state).await
}
async fn validate_block_header(
&self,
header: &BlockHeader,
state: &mut dyn State,
with_future_timestamp_check: bool,
) -> anyhow::Result<()> {
self.base
.validate_block_header(header, state, with_future_timestamp_check)
.await
}
async fn validate_seal(&self, header: &BlockHeader) -> anyhow::Result<()> {
// TOD: Ethash stuff here
let _ = header;
Ok(())
}
async fn finalize(
&self,
header: &PartialHeader,
ommers: &[BlockHeader],
revision: Revision,
) -> anyhow::Result<Vec<FinalizationChange>> {
let mut changes = Vec::with_capacity(1 + ommers.len());
let block_reward = {
if revision >= Revision::Constantinople {
*param::BLOCK_REWARD_CONSTANTINOPLE
} else if revision >= Revision::Byzantium {
*param::BLOCK_REWARD_BYZANTIUM
} else {
*param::BLOCK_REWARD_FRONTIER
}
};
let block_number = header.number;
let mut miner_reward = block_reward;
for ommer in ommers {
let ommer_reward =
(U256::from(8 + ommer.number.0 - block_number.0) * block_reward) >> 3;
changes.push(FinalizationChange::Reward {
address: ommer.beneficiary,
amount: ommer_reward,
});
miner_reward += block_reward / 32;
}
changes.push(FinalizationChange::Reward {
address: header.beneficiary,
amount: miner_reward,
});
Ok(changes)
}
async fn get_beneficiary(&self, header: &BlockHeader) -> anyhow::Result<Address> {
Ok(header.beneficiary)
}
}

211
src/consensus/mod.rs Normal file
View File

@@ -0,0 +1,211 @@
mod base;
mod blockchain;
mod ethash;
mod noproof;
pub use self::{blockchain::*, ethash::*, noproof::*};
use crate::{
chain::intrinsic_gas::*,
models::{Block, BlockHeader, *},
State,
};
use anyhow::bail;
use async_trait::async_trait;
use ethereum_types::*;
use evmodin::Revision;
use std::fmt::{Debug, Display};
#[derive(Debug)]
pub enum FinalizationChange {
Reward { address: Address, amount: U256 },
}
#[async_trait]
pub trait Consensus: Debug + Send + Sync + 'static {
/// Performs validation of block header & body that can be done prior to sender recovery and execution.
/// See [YP] Sections 4.3.2 "Holistic Validity", 4.3.4 "Block Header Validity", and 11.1 "Ommer Validation".
///
/// NOTE: Shouldn't be used for genesis block.
async fn pre_validate_block(&self, block: &Block, state: &mut dyn State) -> anyhow::Result<()>;
/// See [YP] Section 4.3.4 "Block Header Validity".
///
/// NOTE: Shouldn't be used for genesis block.
async fn validate_block_header(
&self,
header: &BlockHeader,
state: &mut dyn State,
with_future_timestamp_check: bool,
) -> anyhow::Result<()>;
/// Validates the seal of the header
async fn validate_seal(&self, header: &BlockHeader) -> anyhow::Result<()>;
/// Finalizes block execution by applying changes in the state of accounts or of the consensus itself
///
/// NOTE: For Ethash See [YP] Section 11.3 "Reward Application".
async fn finalize(
&self,
block: &PartialHeader,
ommers: &[BlockHeader],
revision: Revision,
) -> anyhow::Result<Vec<FinalizationChange>>;
/// See [YP] Section 11.3 "Reward Application".
async fn get_beneficiary(&self, header: &BlockHeader) -> anyhow::Result<Address>;
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum ValidationError {
FutureBlock {
now: u64,
got: u64,
}, // Block has a timestamp in the future
// See [YP] Section 4.3.2 "Holistic Validity", Eq (31)
WrongStateRoot {
expected: H256,
got: H256,
}, // wrong Hr
WrongOmmersHash {
expected: H256,
got: H256,
}, // wrong Ho
WrongTransactionsRoot {
expected: H256,
got: H256,
}, // wrong Ht
WrongReceiptsRoot {
expected: H256,
got: H256,
}, // wrong He
WrongLogsBloom {
expected: Bloom,
got: Bloom,
}, // wrong Hb
// See [YP] Section 4.3.4 "Block Header Validity", Eq (50)
UnknownParent, // P(H) = ∅ Hi ≠ P(H)Hi + 1
WrongDifficulty, // Hd ≠ D(H)
GasAboveLimit {
used: u64,
limit: u64,
}, // Hg > Hl
InvalidGasLimit, // |Hl-P(H)Hl|≥P(H)Hl/1024 Hl<5000
InvalidTimestamp {
parent: u64,
current: u64,
}, // Hs ≤ P(H)Hs
ExtraDataTooLong, // ‖Hx‖ > 32
WrongDaoExtraData, // see EIP-779
WrongBaseFee {
expected: Option<U256>,
got: Option<U256>,
}, // see EIP-1559
InvalidSeal, // Nonce or mix_hash
// See [YP] Section 6.2 "Execution", Eq (58)
MissingSender, // S(T) = ∅
SenderNoEOA {
sender: Address,
}, // EIP-3607: σ[S(T)]c ≠ KEC( () )
WrongNonce {
account: Address,
expected: u64,
got: u64,
}, // Tn ≠ σ[S(T)]n
IntrinsicGas, // g0 > Tg
InsufficientFunds {
account: Address,
available: U512,
required: U512,
}, // v0 > σ[S(T)]b
BlockGasLimitExceeded {
available: u64,
required: u64,
}, // Tg > BHl - l(BR)u
MaxFeeLessThanBase, // max_fee_per_gas < base_fee_per_gas (EIP-1559)
MaxPriorityFeeGreaterThanMax, // max_priority_fee_per_gas > max_fee_per_gas (EIP-1559)
// See [YP] Section 11.1 "Ommer Validation", Eq (157)
TooManyOmmers, // ‖BU‖ > 2
InvalidOmmerHeader, // ¬V(U)
NotAnOmmer, // ¬k(U, P(BH)H, 6)
DuplicateOmmer, // not well covered by the YP actually
// See [YP] Section 11.2 "Transaction Validation", Eq (160)
WrongBlockGas {
expected: u64,
got: u64,
}, // BHg ≠ l(BR)u
InvalidSignature, // EIP-2
WrongChainId, // EIP-155
UnsupportedTransactionType, // EIP-2718
}
impl Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for ValidationError {}
pub fn pre_validate_transaction(
txn: &TransactionMessage,
block_number: impl Into<BlockNumber>,
config: &ChainConfig,
base_fee_per_gas: Option<U256>,
) -> Result<(), ValidationError> {
let rev = config.revision(block_number);
if let Some(chain_id) = txn.chain_id() {
if rev < Revision::Spurious || chain_id != config.chain_id {
return Err(ValidationError::WrongChainId);
}
}
match txn.tx_type() {
TxType::EIP2930 => {
if rev < Revision::Berlin {
return Err(ValidationError::UnsupportedTransactionType);
}
}
TxType::EIP1559 => {
if rev < Revision::London {
return Err(ValidationError::UnsupportedTransactionType);
}
}
TxType::Legacy => {}
}
if let Some(base_fee_per_gas) = base_fee_per_gas {
if txn.max_fee_per_gas() < base_fee_per_gas {
return Err(ValidationError::MaxFeeLessThanBase);
}
}
// https://github.com/ethereum/EIPs/pull/3594
if txn.max_priority_fee_per_gas() > txn.max_fee_per_gas() {
return Err(ValidationError::MaxPriorityFeeGreaterThanMax);
}
let g0 = intrinsic_gas(txn, rev >= Revision::Homestead, rev >= Revision::Istanbul);
if u128::from(txn.gas_limit()) < g0 {
return Err(ValidationError::IntrinsicGas);
}
Ok(())
}
pub fn engine_factory(chain_config: ChainConfig) -> anyhow::Result<Box<dyn Consensus>> {
Ok(match chain_config.seal_engine {
SealEngineType::Ethash => Box::new(Ethash::new(chain_config)),
SealEngineType::NoProof => Box::new(NoProof::new(chain_config)),
_ => bail!("unsupported consensus engine"),
})
}

50
src/consensus/noproof.rs Normal file
View File

@@ -0,0 +1,50 @@
use super::*;
#[derive(Debug)]
pub struct NoProof {
base: Ethash,
}
impl NoProof {
pub fn new(chain_config: ChainConfig) -> Self {
Self {
base: Ethash::new(chain_config),
}
}
}
#[async_trait]
impl Consensus for NoProof {
async fn pre_validate_block(&self, block: &Block, state: &mut dyn State) -> anyhow::Result<()> {
self.base.pre_validate_block(block, state).await
}
async fn validate_block_header(
&self,
header: &BlockHeader,
state: &mut dyn State,
with_future_timestamp_check: bool,
) -> anyhow::Result<()> {
self.base
.validate_block_header(header, state, with_future_timestamp_check)
.await
}
async fn validate_seal(&self, _: &BlockHeader) -> anyhow::Result<()> {
Ok(())
}
async fn finalize(
&self,
block: &PartialHeader,
ommers: &[BlockHeader],
revision: Revision,
) -> anyhow::Result<Vec<FinalizationChange>> {
self.base.finalize(block, ommers, revision).await
}
/// See [YP] Section 11.3 "Reward Application".
async fn get_beneficiary(&self, header: &BlockHeader) -> anyhow::Result<Address> {
self.base.get_beneficiary(header).await
}
}

View File

@@ -34,6 +34,7 @@ where
revision: Revision,
chain_config: &'c ChainConfig,
txn: &'t TransactionWithSender,
beneficiary: Address,
}
pub async fn execute<B: State>(
@@ -50,6 +51,7 @@ pub async fn execute<B: State>(
revision,
chain_config,
txn,
beneficiary: header.beneficiary,
};
let res = if let TransactionAction::Call(to) = txn.action() {
@@ -470,7 +472,7 @@ where
let base_fee_per_gas = self.header.base_fee_per_gas.unwrap_or_else(U256::zero);
let tx_gas_price = self.txn.effective_gas_price(base_fee_per_gas);
let tx_origin = self.txn.sender;
let block_coinbase = self.header.beneficiary;
let block_coinbase = self.beneficiary;
let block_number = self.header.number.0;
let block_timestamp = self.header.timestamp;
let block_gas_limit = self.header.gas_limit;

View File

@@ -1,5 +1,5 @@
use self::processor::ExecutionProcessor;
use crate::{crypto::*, models::*, State};
use crate::{consensus, crypto::*, models::*, State};
mod address;
pub mod evm;
@@ -12,9 +12,10 @@ pub async fn execute_block<S: State>(
header: &PartialHeader,
block: &BlockBodyWithSenders,
) -> anyhow::Result<Vec<Receipt>> {
Ok(ExecutionProcessor::new(state, header, block, config)
let mut engine = consensus::engine_factory(config.clone())?;
ExecutionProcessor::new(state, &mut *engine, header, block, config)
.execute_and_write_block()
.await?)
.await
}
#[cfg(test)]

View File

@@ -4,8 +4,8 @@ use crate::{
dao,
intrinsic_gas::*,
protocol_param::{fee, param},
validity::{pre_validate_transaction, ValidationError},
},
consensus::*,
execution::evm,
models::*,
state::IntraBlockState,
@@ -17,11 +17,12 @@ use evmodin::{Revision, StatusCode};
use std::cmp::min;
use TransactionAction;
pub struct ExecutionProcessor<'r, 'h, 'b, 'c, S>
pub struct ExecutionProcessor<'r, 'e, 'h, 'b, 'c, S>
where
S: State,
{
state: IntraBlockState<'r, S>,
engine: &'e mut dyn Consensus,
header: &'h PartialHeader,
block: &'b BlockBodyWithSenders,
revision: Revision,
@@ -29,12 +30,13 @@ where
cumulative_gas_used: u64,
}
impl<'r, 'h, 'b, 'c, S> ExecutionProcessor<'r, 'h, 'b, 'c, S>
impl<'r, 'e, 'h, 'b, 'c, S> ExecutionProcessor<'r, 'e, 'h, 'b, 'c, S>
where
S: State,
{
pub fn new(
state: &'r mut S,
engine: &'e mut dyn Consensus,
header: &'h PartialHeader,
block: &'b BlockBodyWithSenders,
chain_config: &'c ChainConfig,
@@ -42,6 +44,7 @@ where
let revision = chain_config.revision(header.number);
Self {
state: IntraBlockState::new(state),
engine,
header,
block,
revision,
@@ -209,7 +212,17 @@ where
receipts.push(self.execute_transaction(txn).await?);
}
self.apply_rewards().await?;
for change in self
.engine
.finalize(self.header, &self.block.ommers, self.revision)
.await?
{
match change {
FinalizationChange::Reward { address, amount } => {
self.state.add_to_balance(address, amount).await?;
}
}
}
Ok(receipts)
}
@@ -283,35 +296,6 @@ where
Ok(gas_left)
}
pub async fn apply_rewards(&mut self) -> anyhow::Result<()> {
let block_reward = {
if self.revision >= Revision::Constantinople {
*param::BLOCK_REWARD_CONSTANTINOPLE
} else if self.revision >= Revision::Byzantium {
*param::BLOCK_REWARD_BYZANTIUM
} else {
*param::BLOCK_REWARD_FRONTIER
}
};
let block_number = self.header.number;
let mut miner_reward = block_reward;
for ommer in &self.block.ommers {
let ommer_reward =
(U256::from(8 + ommer.number.0 - block_number.0) * block_reward) >> 3;
self.state
.add_to_balance(ommer.beneficiary, ommer_reward)
.await?;
miner_reward += block_reward / 32;
}
self.state
.add_to_balance(self.header.beneficiary, miner_reward)
.await?;
Ok(())
}
}
#[cfg(test)]
@@ -353,8 +337,9 @@ mod tests {
};
let mut state = InMemoryState::default();
let mut engine = engine_factory(MAINNET_CONFIG.clone()).unwrap();
let mut processor =
ExecutionProcessor::new(&mut state, &header, &block, &MAINNET_CONFIG);
ExecutionProcessor::new(&mut state, &mut *engine, &header, &block, &MAINNET_CONFIG);
let receipt = processor.execute_transaction(&txn).await.unwrap();
assert!(receipt.success);
@@ -390,8 +375,9 @@ mod tests {
let block = Default::default();
let mut state = InMemoryState::default();
let mut engine = engine_factory(MAINNET_CONFIG.clone()).unwrap();
let mut processor =
ExecutionProcessor::new(&mut state, &header, &block, &MAINNET_CONFIG);
ExecutionProcessor::new(&mut state, &mut *engine, &header, &block, &MAINNET_CONFIG);
processor
.state
@@ -444,8 +430,9 @@ mod tests {
// 23 BALANCE
let mut state = InMemoryState::default();
let mut engine = engine_factory(MAINNET_CONFIG.clone()).unwrap();
let mut processor =
ExecutionProcessor::new(&mut state, &header, &block, &MAINNET_CONFIG);
ExecutionProcessor::new(&mut state, &mut *engine, &header, &block, &MAINNET_CONFIG);
let t = |action, input, nonce, gas_limit| TransactionWithSender {
message: TransactionMessage::EIP1559 {
@@ -559,8 +546,9 @@ mod tests {
// 38 CALL
let mut state = InMemoryState::default();
let mut engine = engine_factory(MAINNET_CONFIG.clone()).unwrap();
let mut processor =
ExecutionProcessor::new(&mut state, &header, &block, &MAINNET_CONFIG);
ExecutionProcessor::new(&mut state, &mut *engine, &header, &block, &MAINNET_CONFIG);
processor
.state()
@@ -673,8 +661,9 @@ mod tests {
sender: caller,
};
let mut engine = engine_factory(MAINNET_CONFIG.clone()).unwrap();
let mut processor =
ExecutionProcessor::new(&mut state, &header, &block, &MAINNET_CONFIG);
ExecutionProcessor::new(&mut state, &mut *engine, &header, &block, &MAINNET_CONFIG);
processor
.state()
.add_to_balance(caller, *ETHER)
@@ -728,9 +717,9 @@ mod tests {
};
let mut state = InMemoryState::default();
let mut engine = engine_factory(MAINNET_CONFIG.clone()).unwrap();
let mut processor =
ExecutionProcessor::new(&mut state, &header, &block, &MAINNET_CONFIG);
ExecutionProcessor::new(&mut state, &mut *engine, &header, &block, &MAINNET_CONFIG);
processor
.state()

View File

@@ -22,6 +22,7 @@ pub mod adapter;
mod bitmapdb;
pub mod chain;
mod changeset;
pub mod consensus;
pub mod crypto;
pub mod downloader;
pub mod etl;

View File

@@ -1,9 +1,20 @@
use super::BlockNumber;
use educe::Educe;
use ethereum_types::*;
use evmodin::Revision;
use serde::*;
use std::collections::{BTreeSet, HashSet};
#[derive(Clone, Copy, Debug, Educe, PartialEq, Serialize, Deserialize)]
#[educe(Default)]
pub enum SealEngineType {
#[educe(Default)]
NoProof,
Ethash,
Clique,
AuRa,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DaoConfig {
pub block_number: BlockNumber,
@@ -15,6 +26,7 @@ pub struct DaoConfig {
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChainConfig {
pub seal_engine: SealEngineType,
pub chain_id: u64,
pub homestead_block: Option<BlockNumber>,
pub dao_fork: Option<DaoConfig>,

View File

@@ -1,5 +1,6 @@
use crate::{
accessors,
consensus::engine_factory,
execution::processor::ExecutionProcessor,
kv::tables,
models::*,
@@ -26,6 +27,7 @@ async fn execute_batch_of_blocks<'db, Tx: MutableTransaction<'db>>(
prune_from: BlockNumber,
) -> anyhow::Result<BlockNumber> {
let mut buffer = Buffer::new(tx, prune_from, None);
let mut consensus_engine = engine_factory(chain_config.clone())?;
let mut block_number = starting_block;
loop {
@@ -40,9 +42,15 @@ async fn execute_batch_of_blocks<'db, Tx: MutableTransaction<'db>>(
.await?
.ok_or_else(|| anyhow!("Block body not found: {}/{:?}", block_number, block_hash))?;
let receipts = ExecutionProcessor::new(&mut buffer, &header, &block, &chain_config)
.execute_and_write_block()
.await?;
let receipts = ExecutionProcessor::new(
&mut buffer,
&mut *consensus_engine,
&header,
&block,
&chain_config,
)
.execute_and_write_block()
.await?;
if *block_number % 250 == 0 {
info!("Executed block {}", block_number);

View File

@@ -1,10 +1,12 @@
use crate::models::*;
use async_trait::async_trait;
use auto_impl::auto_impl;
use bytes::Bytes;
use ethereum_types::{Address, H256, U256};
use std::fmt::Debug;
#[async_trait]
#[auto_impl(&mut, Box)]
pub trait State: Debug + Send + Sync {
async fn read_account(&self, address: Address) -> anyhow::Result<Option<Account>>;