mirror of
https://github.com/akula-bft/akula.git
synced 2026-01-15 00:38:28 -05:00
Consensus engine refactor (#48)
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
360
src/consensus/base.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
78
src/consensus/ethash.rs
Normal 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
211
src/consensus/mod.rs
Normal 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
50
src/consensus/noproof.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user