mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: remove auto-seal consensus (#12385)
This commit is contained in:
1
.github/assets/check_wasm.sh
vendored
1
.github/assets/check_wasm.sh
vendored
@ -11,7 +11,6 @@ exclude_crates=(
|
|||||||
# The following are not working yet, but known to be fixable
|
# The following are not working yet, but known to be fixable
|
||||||
reth-exex-types # https://github.com/paradigmxyz/reth/issues/9946
|
reth-exex-types # https://github.com/paradigmxyz/reth/issues/9946
|
||||||
# The following require investigation if they can be fixed
|
# The following require investigation if they can be fixed
|
||||||
reth-auto-seal-consensus
|
|
||||||
reth-basic-payload-builder
|
reth-basic-payload-builder
|
||||||
reth-beacon-consensus
|
reth-beacon-consensus
|
||||||
reth-bench
|
reth-bench
|
||||||
|
|||||||
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -6385,37 +6385,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "reth-auto-seal-consensus"
|
|
||||||
version = "1.1.1"
|
|
||||||
dependencies = [
|
|
||||||
"alloy-eips",
|
|
||||||
"alloy-primitives",
|
|
||||||
"alloy-rpc-types-engine",
|
|
||||||
"futures-util",
|
|
||||||
"reth-beacon-consensus",
|
|
||||||
"reth-chainspec",
|
|
||||||
"reth-consensus",
|
|
||||||
"reth-engine-primitives",
|
|
||||||
"reth-evm",
|
|
||||||
"reth-execution-errors",
|
|
||||||
"reth-execution-types",
|
|
||||||
"reth-network-p2p",
|
|
||||||
"reth-network-peers",
|
|
||||||
"reth-optimism-consensus",
|
|
||||||
"reth-primitives",
|
|
||||||
"reth-provider",
|
|
||||||
"reth-revm",
|
|
||||||
"reth-stages-api",
|
|
||||||
"reth-tokio-util",
|
|
||||||
"reth-transaction-pool",
|
|
||||||
"reth-trie",
|
|
||||||
"revm-primitives",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reth-basic-payload-builder"
|
name = "reth-basic-payload-builder"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@ -7924,7 +7893,6 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"jsonrpsee",
|
"jsonrpsee",
|
||||||
"rayon",
|
"rayon",
|
||||||
"reth-auto-seal-consensus",
|
|
||||||
"reth-beacon-consensus",
|
"reth-beacon-consensus",
|
||||||
"reth-blockchain-tree",
|
"reth-blockchain-tree",
|
||||||
"reth-chain-state",
|
"reth-chain-state",
|
||||||
@ -8043,7 +8011,6 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"reth",
|
"reth",
|
||||||
"reth-auto-seal-consensus",
|
|
||||||
"reth-basic-payload-builder",
|
"reth-basic-payload-builder",
|
||||||
"reth-beacon-consensus",
|
"reth-beacon-consensus",
|
||||||
"reth-chainspec",
|
"reth-chainspec",
|
||||||
@ -8266,7 +8233,6 @@ dependencies = [
|
|||||||
"op-alloy-rpc-types-engine",
|
"op-alloy-rpc-types-engine",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"reth",
|
"reth",
|
||||||
"reth-auto-seal-consensus",
|
|
||||||
"reth-basic-payload-builder",
|
"reth-basic-payload-builder",
|
||||||
"reth-beacon-consensus",
|
"reth-beacon-consensus",
|
||||||
"reth-chainspec",
|
"reth-chainspec",
|
||||||
|
|||||||
@ -20,7 +20,6 @@ members = [
|
|||||||
"crates/cli/runner/",
|
"crates/cli/runner/",
|
||||||
"crates/cli/util/",
|
"crates/cli/util/",
|
||||||
"crates/config/",
|
"crates/config/",
|
||||||
"crates/consensus/auto-seal/",
|
|
||||||
"crates/consensus/beacon/",
|
"crates/consensus/beacon/",
|
||||||
"crates/consensus/common/",
|
"crates/consensus/common/",
|
||||||
"crates/consensus/consensus/",
|
"crates/consensus/consensus/",
|
||||||
@ -299,7 +298,6 @@ overflow-checks = true
|
|||||||
# reth
|
# reth
|
||||||
op-reth = { path = "crates/optimism/bin" }
|
op-reth = { path = "crates/optimism/bin" }
|
||||||
reth = { path = "bin/reth" }
|
reth = { path = "bin/reth" }
|
||||||
reth-auto-seal-consensus = { path = "crates/consensus/auto-seal" }
|
|
||||||
reth-basic-payload-builder = { path = "crates/payload/basic" }
|
reth-basic-payload-builder = { path = "crates/payload/basic" }
|
||||||
reth-beacon-consensus = { path = "crates/consensus/beacon" }
|
reth-beacon-consensus = { path = "crates/consensus/beacon" }
|
||||||
reth-bench = { path = "bin/reth-bench" }
|
reth-bench = { path = "bin/reth-bench" }
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "reth-auto-seal-consensus"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
description = "A consensus impl for local testing purposes"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# reth
|
|
||||||
reth-chainspec.workspace = true
|
|
||||||
reth-beacon-consensus.workspace = true
|
|
||||||
reth-primitives.workspace = true
|
|
||||||
reth-execution-errors.workspace = true
|
|
||||||
reth-execution-types.workspace = true
|
|
||||||
reth-network-p2p.workspace = true
|
|
||||||
reth-provider.workspace = true
|
|
||||||
reth-stages-api.workspace = true
|
|
||||||
reth-revm.workspace = true
|
|
||||||
reth-transaction-pool.workspace = true
|
|
||||||
reth-evm.workspace = true
|
|
||||||
reth-engine-primitives.workspace = true
|
|
||||||
reth-consensus.workspace = true
|
|
||||||
reth-network-peers.workspace = true
|
|
||||||
reth-tokio-util.workspace = true
|
|
||||||
reth-trie.workspace = true
|
|
||||||
|
|
||||||
# ethereum
|
|
||||||
alloy-eips.workspace = true
|
|
||||||
alloy-primitives.workspace = true
|
|
||||||
revm-primitives.workspace = true
|
|
||||||
alloy-rpc-types-engine.workspace = true
|
|
||||||
|
|
||||||
# optimism
|
|
||||||
reth-optimism-consensus = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
# async
|
|
||||||
futures-util.workspace = true
|
|
||||||
tokio = { workspace = true, features = ["sync", "time"] }
|
|
||||||
tokio-stream.workspace = true
|
|
||||||
tracing.workspace = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
optimism = [
|
|
||||||
"reth-provider/optimism",
|
|
||||||
"reth-optimism-consensus",
|
|
||||||
"reth-beacon-consensus/optimism",
|
|
||||||
"reth-execution-types/optimism",
|
|
||||||
"reth-optimism-consensus?/optimism",
|
|
||||||
"reth-primitives/optimism",
|
|
||||||
"revm-primitives/optimism"
|
|
||||||
]
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
//! This includes download client implementations for auto sealing miners.
|
|
||||||
|
|
||||||
use crate::Storage;
|
|
||||||
use alloy_eips::BlockHashOrNumber;
|
|
||||||
use alloy_primitives::B256;
|
|
||||||
use reth_network_p2p::{
|
|
||||||
bodies::client::{BodiesClient, BodiesFut},
|
|
||||||
download::DownloadClient,
|
|
||||||
headers::client::{HeadersClient, HeadersDirection, HeadersFut, HeadersRequest},
|
|
||||||
priority::Priority,
|
|
||||||
};
|
|
||||||
use reth_network_peers::{PeerId, WithPeerId};
|
|
||||||
use reth_primitives::{BlockBody, Header};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use tracing::{trace, warn};
|
|
||||||
|
|
||||||
/// A download client that polls the miner for transactions and assembles blocks to be returned in
|
|
||||||
/// the download process.
|
|
||||||
///
|
|
||||||
/// When polled, the miner will assemble blocks when miners produce ready transactions and store the
|
|
||||||
/// blocks in memory.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct AutoSealClient {
|
|
||||||
storage: Storage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AutoSealClient {
|
|
||||||
pub(crate) const fn new(storage: Storage) -> Self {
|
|
||||||
Self { storage }
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_headers(&self, request: HeadersRequest) -> Vec<Header> {
|
|
||||||
trace!(target: "consensus::auto", ?request, "received headers request");
|
|
||||||
|
|
||||||
let storage = self.storage.read().await;
|
|
||||||
let HeadersRequest { start, limit, direction } = request;
|
|
||||||
let mut headers = Vec::new();
|
|
||||||
|
|
||||||
let mut block: BlockHashOrNumber = match start {
|
|
||||||
BlockHashOrNumber::Hash(start) => start.into(),
|
|
||||||
BlockHashOrNumber::Number(num) => {
|
|
||||||
if let Some(hash) = storage.block_hash(num) {
|
|
||||||
hash.into()
|
|
||||||
} else {
|
|
||||||
warn!(target: "consensus::auto", num, "no matching block found");
|
|
||||||
return headers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for _ in 0..limit {
|
|
||||||
// fetch from storage
|
|
||||||
if let Some(header) = storage.header_by_hash_or_number(block) {
|
|
||||||
match direction {
|
|
||||||
HeadersDirection::Falling => block = header.parent_hash.into(),
|
|
||||||
HeadersDirection::Rising => {
|
|
||||||
let next = header.number + 1;
|
|
||||||
block = next.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
headers.push(header);
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!(target: "consensus::auto", ?headers, "returning headers");
|
|
||||||
|
|
||||||
headers
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_bodies(&self, hashes: Vec<B256>) -> Vec<BlockBody> {
|
|
||||||
trace!(target: "consensus::auto", ?hashes, "received bodies request");
|
|
||||||
let storage = self.storage.read().await;
|
|
||||||
let mut bodies = Vec::new();
|
|
||||||
for hash in hashes {
|
|
||||||
if let Some(body) = storage.bodies.get(&hash).cloned() {
|
|
||||||
bodies.push(body);
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!(target: "consensus::auto", ?bodies, "returning bodies");
|
|
||||||
|
|
||||||
bodies
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HeadersClient for AutoSealClient {
|
|
||||||
type Output = HeadersFut;
|
|
||||||
|
|
||||||
fn get_headers_with_priority(
|
|
||||||
&self,
|
|
||||||
request: HeadersRequest,
|
|
||||||
_priority: Priority,
|
|
||||||
) -> Self::Output {
|
|
||||||
let this = self.clone();
|
|
||||||
Box::pin(async move {
|
|
||||||
let headers = this.fetch_headers(request).await;
|
|
||||||
Ok(WithPeerId::new(PeerId::random(), headers))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BodiesClient for AutoSealClient {
|
|
||||||
type Output = BodiesFut;
|
|
||||||
|
|
||||||
fn get_block_bodies_with_priority(
|
|
||||||
&self,
|
|
||||||
hashes: Vec<B256>,
|
|
||||||
_priority: Priority,
|
|
||||||
) -> Self::Output {
|
|
||||||
let this = self.clone();
|
|
||||||
Box::pin(async move {
|
|
||||||
let bodies = this.fetch_bodies(hashes).await;
|
|
||||||
Ok(WithPeerId::new(PeerId::random(), bodies))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DownloadClient for AutoSealClient {
|
|
||||||
fn report_bad_message(&self, _peer_id: PeerId) {
|
|
||||||
warn!("Reported a bad message on a miner, we should never produce bad blocks");
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
fn num_connected_peers(&self) -> usize {
|
|
||||||
// no such thing as connected peers when we are mining ourselves
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,690 +0,0 @@
|
|||||||
//! A [Consensus] implementation for local testing purposes
|
|
||||||
//! that automatically seals blocks.
|
|
||||||
//!
|
|
||||||
//! The Mining task polls a [`MiningMode`], and will return a list of transactions that are ready to
|
|
||||||
//! be mined.
|
|
||||||
//!
|
|
||||||
//! These downloaders poll the miner, assemble the block, and return transactions that are ready to
|
|
||||||
//! be mined.
|
|
||||||
|
|
||||||
#![doc(
|
|
||||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
|
||||||
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
|
|
||||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
|
||||||
)]
|
|
||||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
|
||||||
|
|
||||||
use alloy_eips::{eip1898::BlockHashOrNumber, eip7685::Requests};
|
|
||||||
use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256};
|
|
||||||
use reth_beacon_consensus::BeaconEngineMessage;
|
|
||||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
|
||||||
use reth_consensus::{Consensus, ConsensusError, PostExecutionInput};
|
|
||||||
use reth_engine_primitives::EngineTypes;
|
|
||||||
use reth_execution_errors::{
|
|
||||||
BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
|
|
||||||
};
|
|
||||||
use reth_execution_types::ExecutionOutcome;
|
|
||||||
use reth_primitives::{
|
|
||||||
proofs, Block, BlockBody, BlockWithSenders, Header, SealedBlock, SealedHeader,
|
|
||||||
TransactionSigned, Withdrawals,
|
|
||||||
};
|
|
||||||
use reth_provider::{BlockReaderIdExt, StateProviderFactory, StateRootProvider};
|
|
||||||
use reth_revm::database::StateProviderDatabase;
|
|
||||||
use reth_transaction_pool::TransactionPool;
|
|
||||||
use reth_trie::HashedPostState;
|
|
||||||
use revm_primitives::calc_excess_blob_gas;
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
fmt::Debug,
|
|
||||||
sync::Arc,
|
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
|
||||||
};
|
|
||||||
use tokio::sync::{mpsc::UnboundedSender, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
|
||||||
use tracing::trace;
|
|
||||||
|
|
||||||
mod client;
|
|
||||||
mod mode;
|
|
||||||
mod task;
|
|
||||||
|
|
||||||
pub use crate::client::AutoSealClient;
|
|
||||||
pub use mode::{FixedBlockTimeMiner, MiningMode, ReadyTransactionMiner};
|
|
||||||
use reth_evm::execute::{BlockExecutorProvider, Executor};
|
|
||||||
pub use task::MiningTask;
|
|
||||||
|
|
||||||
/// A consensus implementation intended for local development and testing purposes.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct AutoSealConsensus<ChainSpec> {
|
|
||||||
/// Configuration
|
|
||||||
chain_spec: Arc<ChainSpec>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ChainSpec> AutoSealConsensus<ChainSpec> {
|
|
||||||
/// Create a new instance of [`AutoSealConsensus`]
|
|
||||||
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
|
|
||||||
Self { chain_spec }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ChainSpec: Send + Sync + Debug> Consensus for AutoSealConsensus<ChainSpec> {
|
|
||||||
fn validate_header(&self, _header: &SealedHeader) -> Result<(), ConsensusError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_header_against_parent(
|
|
||||||
&self,
|
|
||||||
_header: &SealedHeader,
|
|
||||||
_parent: &SealedHeader,
|
|
||||||
) -> Result<(), ConsensusError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_header_with_total_difficulty(
|
|
||||||
&self,
|
|
||||||
_header: &Header,
|
|
||||||
_total_difficulty: U256,
|
|
||||||
) -> Result<(), ConsensusError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_block_pre_execution(&self, _block: &SealedBlock) -> Result<(), ConsensusError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_block_post_execution(
|
|
||||||
&self,
|
|
||||||
_block: &BlockWithSenders,
|
|
||||||
_input: PostExecutionInput<'_>,
|
|
||||||
) -> Result<(), ConsensusError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builder type for configuring the setup
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AutoSealBuilder<Client, Pool, Engine: EngineTypes, EvmConfig, ChainSpec> {
|
|
||||||
client: Client,
|
|
||||||
consensus: AutoSealConsensus<ChainSpec>,
|
|
||||||
pool: Pool,
|
|
||||||
mode: MiningMode,
|
|
||||||
storage: Storage,
|
|
||||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
|
||||||
evm_config: EvmConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl AutoSealBuilder ===
|
|
||||||
|
|
||||||
impl<Client, Pool, Engine, EvmConfig, ChainSpec>
|
|
||||||
AutoSealBuilder<Client, Pool, Engine, EvmConfig, ChainSpec>
|
|
||||||
where
|
|
||||||
Client: BlockReaderIdExt,
|
|
||||||
Pool: TransactionPool,
|
|
||||||
Engine: EngineTypes,
|
|
||||||
ChainSpec: EthChainSpec,
|
|
||||||
{
|
|
||||||
/// Creates a new builder instance to configure all parts.
|
|
||||||
pub fn new(
|
|
||||||
chain_spec: Arc<ChainSpec>,
|
|
||||||
client: Client,
|
|
||||||
pool: Pool,
|
|
||||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
|
||||||
mode: MiningMode,
|
|
||||||
evm_config: EvmConfig,
|
|
||||||
) -> Self {
|
|
||||||
let latest_header = client.latest_header().ok().flatten().unwrap_or_else(|| {
|
|
||||||
SealedHeader::new(chain_spec.genesis_header().clone(), chain_spec.genesis_hash())
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
storage: Storage::new(latest_header),
|
|
||||||
client,
|
|
||||||
consensus: AutoSealConsensus::new(chain_spec),
|
|
||||||
pool,
|
|
||||||
mode,
|
|
||||||
to_engine,
|
|
||||||
evm_config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [`MiningMode`] it operates in, default is [`MiningMode::Auto`]
|
|
||||||
pub fn mode(mut self, mode: MiningMode) -> Self {
|
|
||||||
self.mode = mode;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes the type and returns all components
|
|
||||||
#[track_caller]
|
|
||||||
pub fn build(
|
|
||||||
self,
|
|
||||||
) -> (
|
|
||||||
AutoSealConsensus<ChainSpec>,
|
|
||||||
AutoSealClient,
|
|
||||||
MiningTask<Client, Pool, EvmConfig, Engine, ChainSpec>,
|
|
||||||
) {
|
|
||||||
let Self { client, consensus, pool, mode, storage, to_engine, evm_config } = self;
|
|
||||||
let auto_client = AutoSealClient::new(storage.clone());
|
|
||||||
let task = MiningTask::new(
|
|
||||||
Arc::clone(&consensus.chain_spec),
|
|
||||||
mode,
|
|
||||||
to_engine,
|
|
||||||
storage,
|
|
||||||
client,
|
|
||||||
pool,
|
|
||||||
evm_config,
|
|
||||||
);
|
|
||||||
(consensus, auto_client, task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In memory storage
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub(crate) struct Storage {
|
|
||||||
inner: Arc<RwLock<StorageInner>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// == impl Storage ===
|
|
||||||
|
|
||||||
impl Storage {
|
|
||||||
/// Initializes the [Storage] with the given best block. This should be initialized with the
|
|
||||||
/// highest block in the chain, if there is a chain already stored on-disk.
|
|
||||||
fn new(best_block: SealedHeader) -> Self {
|
|
||||||
let (header, best_hash) = best_block.split();
|
|
||||||
let mut storage = StorageInner {
|
|
||||||
best_hash,
|
|
||||||
total_difficulty: header.difficulty,
|
|
||||||
best_block: header.number,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
storage.headers.insert(header.number, header);
|
|
||||||
storage.bodies.insert(best_hash, BlockBody::default());
|
|
||||||
Self { inner: Arc::new(RwLock::new(storage)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the write lock of the storage
|
|
||||||
pub(crate) async fn write(&self) -> RwLockWriteGuard<'_, StorageInner> {
|
|
||||||
self.inner.write().await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the read lock of the storage
|
|
||||||
pub(crate) async fn read(&self) -> RwLockReadGuard<'_, StorageInner> {
|
|
||||||
self.inner.read().await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In-memory storage for the chain the auto seal engine is building.
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub(crate) struct StorageInner {
|
|
||||||
/// Headers buffered for download.
|
|
||||||
pub(crate) headers: HashMap<BlockNumber, Header>,
|
|
||||||
/// A mapping between block hash and number.
|
|
||||||
pub(crate) hash_to_number: HashMap<BlockHash, BlockNumber>,
|
|
||||||
/// Bodies buffered for download.
|
|
||||||
pub(crate) bodies: HashMap<BlockHash, BlockBody>,
|
|
||||||
/// Tracks best block
|
|
||||||
pub(crate) best_block: u64,
|
|
||||||
/// Tracks hash of best block
|
|
||||||
pub(crate) best_hash: B256,
|
|
||||||
/// The total difficulty of the chain until this block
|
|
||||||
pub(crate) total_difficulty: U256,
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl StorageInner ===
|
|
||||||
|
|
||||||
impl StorageInner {
|
|
||||||
/// Returns the block hash for the given block number if it exists.
|
|
||||||
pub(crate) fn block_hash(&self, num: u64) -> Option<BlockHash> {
|
|
||||||
self.hash_to_number.iter().find_map(|(k, v)| num.eq(v).then_some(*k))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the matching header if it exists.
|
|
||||||
pub(crate) fn header_by_hash_or_number(
|
|
||||||
&self,
|
|
||||||
hash_or_num: BlockHashOrNumber,
|
|
||||||
) -> Option<Header> {
|
|
||||||
let num = match hash_or_num {
|
|
||||||
BlockHashOrNumber::Hash(hash) => self.hash_to_number.get(&hash).copied()?,
|
|
||||||
BlockHashOrNumber::Number(num) => num,
|
|
||||||
};
|
|
||||||
self.headers.get(&num).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a new header+body pair
|
|
||||||
pub(crate) fn insert_new_block(&mut self, mut header: Header, body: BlockBody) {
|
|
||||||
header.number = self.best_block + 1;
|
|
||||||
header.parent_hash = self.best_hash;
|
|
||||||
|
|
||||||
self.best_hash = header.hash_slow();
|
|
||||||
self.best_block = header.number;
|
|
||||||
self.total_difficulty += header.difficulty;
|
|
||||||
|
|
||||||
trace!(target: "consensus::auto", num=self.best_block, hash=?self.best_hash, "inserting new block");
|
|
||||||
self.headers.insert(header.number, header);
|
|
||||||
self.bodies.insert(self.best_hash, body);
|
|
||||||
self.hash_to_number.insert(self.best_hash, self.best_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fills in pre-execution header fields based on the current best block and given
|
|
||||||
/// transactions.
|
|
||||||
pub(crate) fn build_header_template<ChainSpec>(
|
|
||||||
&self,
|
|
||||||
timestamp: u64,
|
|
||||||
transactions: &[TransactionSigned],
|
|
||||||
ommers: &[Header],
|
|
||||||
withdrawals: Option<&Withdrawals>,
|
|
||||||
requests: Option<&Requests>,
|
|
||||||
chain_spec: &ChainSpec,
|
|
||||||
) -> Header
|
|
||||||
where
|
|
||||||
ChainSpec: EthChainSpec + EthereumHardforks,
|
|
||||||
{
|
|
||||||
// check previous block for base fee
|
|
||||||
let base_fee_per_gas = self.headers.get(&self.best_block).and_then(|parent| {
|
|
||||||
parent.next_block_base_fee(chain_spec.base_fee_params_at_timestamp(timestamp))
|
|
||||||
});
|
|
||||||
|
|
||||||
let blob_gas_used = chain_spec.is_cancun_active_at_timestamp(timestamp).then(|| {
|
|
||||||
transactions
|
|
||||||
.iter()
|
|
||||||
.filter_map(|tx| tx.transaction.as_eip4844())
|
|
||||||
.map(|blob_tx| blob_tx.blob_gas())
|
|
||||||
.sum::<u64>()
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut header = Header {
|
|
||||||
parent_hash: self.best_hash,
|
|
||||||
ommers_hash: proofs::calculate_ommers_root(ommers),
|
|
||||||
transactions_root: proofs::calculate_transaction_root(transactions),
|
|
||||||
withdrawals_root: withdrawals.map(|w| proofs::calculate_withdrawals_root(w)),
|
|
||||||
difficulty: U256::from(2),
|
|
||||||
number: self.best_block + 1,
|
|
||||||
gas_limit: chain_spec.max_gas_limit(),
|
|
||||||
timestamp,
|
|
||||||
base_fee_per_gas,
|
|
||||||
blob_gas_used,
|
|
||||||
requests_hash: requests.map(|r| r.requests_hash()),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
if chain_spec.is_cancun_active_at_timestamp(timestamp) {
|
|
||||||
let parent = self.headers.get(&self.best_block);
|
|
||||||
header.parent_beacon_block_root =
|
|
||||||
parent.and_then(|parent| parent.parent_beacon_block_root);
|
|
||||||
header.blob_gas_used = Some(0);
|
|
||||||
|
|
||||||
let (parent_excess_blob_gas, parent_blob_gas_used) = match parent {
|
|
||||||
Some(parent) if chain_spec.is_cancun_active_at_timestamp(parent.timestamp) => (
|
|
||||||
parent.excess_blob_gas.unwrap_or_default(),
|
|
||||||
parent.blob_gas_used.unwrap_or_default(),
|
|
||||||
),
|
|
||||||
_ => (0, 0),
|
|
||||||
};
|
|
||||||
header.excess_blob_gas =
|
|
||||||
Some(calc_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used))
|
|
||||||
}
|
|
||||||
|
|
||||||
header
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds and executes a new block with the given transactions, on the provided executor.
|
|
||||||
///
|
|
||||||
/// This returns the header of the executed block, as well as the poststate from execution.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub(crate) fn build_and_execute<Provider, Executor, ChainSpec>(
|
|
||||||
&mut self,
|
|
||||||
transactions: Vec<TransactionSigned>,
|
|
||||||
ommers: Vec<Header>,
|
|
||||||
provider: &Provider,
|
|
||||||
chain_spec: Arc<ChainSpec>,
|
|
||||||
executor: &Executor,
|
|
||||||
) -> Result<(SealedHeader, ExecutionOutcome), BlockExecutionError>
|
|
||||||
where
|
|
||||||
Executor: BlockExecutorProvider,
|
|
||||||
Provider: StateProviderFactory,
|
|
||||||
ChainSpec: EthChainSpec + EthereumHardforks,
|
|
||||||
{
|
|
||||||
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
|
|
||||||
|
|
||||||
// if shanghai is active, include empty withdrawals
|
|
||||||
let withdrawals =
|
|
||||||
chain_spec.is_shanghai_active_at_timestamp(timestamp).then_some(Withdrawals::default());
|
|
||||||
// if prague is active, include empty requests
|
|
||||||
let requests =
|
|
||||||
chain_spec.is_prague_active_at_timestamp(timestamp).then_some(Requests::default());
|
|
||||||
|
|
||||||
let header = self.build_header_template(
|
|
||||||
timestamp,
|
|
||||||
&transactions,
|
|
||||||
&ommers,
|
|
||||||
withdrawals.as_ref(),
|
|
||||||
requests.as_ref(),
|
|
||||||
&chain_spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
let block = Block {
|
|
||||||
header,
|
|
||||||
body: BlockBody {
|
|
||||||
transactions,
|
|
||||||
ommers: ommers.clone(),
|
|
||||||
withdrawals: withdrawals.clone(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.with_recovered_senders()
|
|
||||||
.ok_or(BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError))?;
|
|
||||||
|
|
||||||
trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions");
|
|
||||||
|
|
||||||
let mut db = StateProviderDatabase::new(
|
|
||||||
provider.latest().map_err(InternalBlockExecutionError::LatestBlock)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
// execute the block
|
|
||||||
let block_execution_output =
|
|
||||||
executor.executor(&mut db).execute((&block, U256::ZERO).into())?;
|
|
||||||
let gas_used = block_execution_output.gas_used;
|
|
||||||
let execution_outcome = ExecutionOutcome::from((block_execution_output, block.number));
|
|
||||||
let hashed_state = HashedPostState::from_bundle_state(&execution_outcome.state().state);
|
|
||||||
|
|
||||||
// todo(onbjerg): we should not pass requests around as this is building a block, which
|
|
||||||
// means we need to extract the requests from the execution output and compute the requests
|
|
||||||
// root here
|
|
||||||
|
|
||||||
let Block { mut header, body, .. } = block.block;
|
|
||||||
let body = BlockBody { transactions: body.transactions, ommers, withdrawals };
|
|
||||||
|
|
||||||
trace!(target: "consensus::auto", ?execution_outcome, ?header, ?body, "executed block, calculating state root and completing header");
|
|
||||||
|
|
||||||
// now we need to update certain header fields with the results of the execution
|
|
||||||
header.state_root = db.state_root(hashed_state)?;
|
|
||||||
header.gas_used = gas_used;
|
|
||||||
|
|
||||||
let receipts = execution_outcome.receipts_by_block(header.number);
|
|
||||||
|
|
||||||
// update logs bloom
|
|
||||||
let receipts_with_bloom =
|
|
||||||
receipts.iter().map(|r| r.as_ref().unwrap().bloom_slow()).collect::<Vec<Bloom>>();
|
|
||||||
header.logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | *r);
|
|
||||||
|
|
||||||
// update receipts root
|
|
||||||
header.receipts_root = {
|
|
||||||
#[cfg(feature = "optimism")]
|
|
||||||
let receipts_root = execution_outcome
|
|
||||||
.generic_receipts_root_slow(header.number, |receipts| {
|
|
||||||
reth_optimism_consensus::calculate_receipt_root_no_memo_optimism(
|
|
||||||
receipts,
|
|
||||||
&chain_spec,
|
|
||||||
header.timestamp,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.expect("Receipts is present");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "optimism"))]
|
|
||||||
let receipts_root =
|
|
||||||
execution_outcome.receipts_root_slow(header.number).expect("Receipts is present");
|
|
||||||
|
|
||||||
receipts_root
|
|
||||||
};
|
|
||||||
trace!(target: "consensus::auto", root=?header.state_root, ?body, "calculated root");
|
|
||||||
|
|
||||||
// finally insert into storage
|
|
||||||
self.insert_new_block(header.clone(), body);
|
|
||||||
|
|
||||||
// set new header with hash that should have been updated by insert_new_block
|
|
||||||
let new_header = SealedHeader::new(header, self.best_hash);
|
|
||||||
|
|
||||||
Ok((new_header, execution_outcome))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use reth_chainspec::{ChainHardforks, ChainSpec, EthereumHardfork, ForkCondition};
|
|
||||||
use reth_primitives::Transaction;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block_hash() {
|
|
||||||
let mut storage = StorageInner::default();
|
|
||||||
|
|
||||||
// Define two block hashes and their corresponding block numbers.
|
|
||||||
let block_hash_1: BlockHash = B256::random();
|
|
||||||
let block_number_1: BlockNumber = 1;
|
|
||||||
let block_hash_2: BlockHash = B256::random();
|
|
||||||
let block_number_2: BlockNumber = 2;
|
|
||||||
|
|
||||||
// Insert the block number and hash pairs into the `hash_to_number` map.
|
|
||||||
storage.hash_to_number.insert(block_hash_1, block_number_1);
|
|
||||||
storage.hash_to_number.insert(block_hash_2, block_number_2);
|
|
||||||
|
|
||||||
// Verify that `block_hash` returns the correct block hash for the given block number.
|
|
||||||
assert_eq!(storage.block_hash(block_number_1), Some(block_hash_1));
|
|
||||||
assert_eq!(storage.block_hash(block_number_2), Some(block_hash_2));
|
|
||||||
|
|
||||||
// Test that `block_hash` returns `None` for a non-existent block number.
|
|
||||||
let block_number_3: BlockNumber = 3;
|
|
||||||
assert_eq!(storage.block_hash(block_number_3), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_header_by_hash_or_number() {
|
|
||||||
let mut storage = StorageInner::default();
|
|
||||||
|
|
||||||
// Define block numbers, headers, and hashes.
|
|
||||||
let block_number_1: u64 = 1;
|
|
||||||
let block_number_2: u64 = 2;
|
|
||||||
let header_1 = Header { number: block_number_1, ..Default::default() };
|
|
||||||
let header_2 = Header { number: block_number_2, ..Default::default() };
|
|
||||||
let block_hash_1: BlockHash = B256::random();
|
|
||||||
let block_hash_2: BlockHash = B256::random();
|
|
||||||
|
|
||||||
// Insert headers and hash-to-number mappings.
|
|
||||||
storage.headers.insert(block_number_1, header_1.clone());
|
|
||||||
storage.headers.insert(block_number_2, header_2.clone());
|
|
||||||
storage.hash_to_number.insert(block_hash_1, block_number_1);
|
|
||||||
storage.hash_to_number.insert(block_hash_2, block_number_2);
|
|
||||||
|
|
||||||
// Test header retrieval by block number.
|
|
||||||
assert_eq!(
|
|
||||||
storage.header_by_hash_or_number(BlockHashOrNumber::Number(block_number_1)),
|
|
||||||
Some(header_1.clone())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
storage.header_by_hash_or_number(BlockHashOrNumber::Number(block_number_2)),
|
|
||||||
Some(header_2.clone())
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test header retrieval by block hash.
|
|
||||||
assert_eq!(
|
|
||||||
storage.header_by_hash_or_number(BlockHashOrNumber::Hash(block_hash_1)),
|
|
||||||
Some(header_1)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
storage.header_by_hash_or_number(BlockHashOrNumber::Hash(block_hash_2)),
|
|
||||||
Some(header_2)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test non-existent block number and hash.
|
|
||||||
assert_eq!(storage.header_by_hash_or_number(BlockHashOrNumber::Number(999)), None);
|
|
||||||
let non_existent_hash: BlockHash = B256::random();
|
|
||||||
assert_eq!(
|
|
||||||
storage.header_by_hash_or_number(BlockHashOrNumber::Hash(non_existent_hash)),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_insert_new_block() {
|
|
||||||
let mut storage = StorageInner::default();
|
|
||||||
|
|
||||||
// Define headers and block bodies.
|
|
||||||
let header_1 = Header { difficulty: U256::from(100), ..Default::default() };
|
|
||||||
let body_1 = BlockBody::default();
|
|
||||||
let header_2 = Header { difficulty: U256::from(200), ..Default::default() };
|
|
||||||
let body_2 = BlockBody::default();
|
|
||||||
|
|
||||||
// Insert the first block.
|
|
||||||
storage.insert_new_block(header_1.clone(), body_1.clone());
|
|
||||||
let best_block_1 = storage.best_block;
|
|
||||||
let best_hash_1 = storage.best_hash;
|
|
||||||
|
|
||||||
// Verify the block was inserted correctly.
|
|
||||||
assert_eq!(
|
|
||||||
storage.headers.get(&best_block_1),
|
|
||||||
Some(&Header { number: 1, ..header_1.clone() })
|
|
||||||
);
|
|
||||||
assert_eq!(storage.bodies.get(&best_hash_1), Some(&body_1));
|
|
||||||
assert_eq!(storage.hash_to_number.get(&best_hash_1), Some(&best_block_1));
|
|
||||||
|
|
||||||
// Insert the second block.
|
|
||||||
storage.insert_new_block(header_2.clone(), body_2.clone());
|
|
||||||
let best_block_2 = storage.best_block;
|
|
||||||
let best_hash_2 = storage.best_hash;
|
|
||||||
|
|
||||||
// Verify the second block was inserted correctly.
|
|
||||||
assert_eq!(
|
|
||||||
storage.headers.get(&best_block_2),
|
|
||||||
Some(&Header {
|
|
||||||
number: 2,
|
|
||||||
parent_hash: Header { number: 1, ..header_1 }.hash_slow(),
|
|
||||||
..header_2
|
|
||||||
})
|
|
||||||
);
|
|
||||||
assert_eq!(storage.bodies.get(&best_hash_2), Some(&body_2));
|
|
||||||
assert_eq!(storage.hash_to_number.get(&best_hash_2), Some(&best_block_2));
|
|
||||||
|
|
||||||
// Check that the total difficulty was updated.
|
|
||||||
assert_eq!(storage.total_difficulty, header_1.difficulty + header_2.difficulty);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_build_basic_header_template() {
|
|
||||||
let mut storage = StorageInner::default();
|
|
||||||
let chain_spec = ChainSpec::default();
|
|
||||||
|
|
||||||
let best_block_number = 1;
|
|
||||||
let best_block_hash = B256::random();
|
|
||||||
let timestamp = 1_600_000_000;
|
|
||||||
|
|
||||||
// Set up best block information
|
|
||||||
storage.best_block = best_block_number;
|
|
||||||
storage.best_hash = best_block_hash;
|
|
||||||
|
|
||||||
// Build header template
|
|
||||||
let header = storage.build_header_template(
|
|
||||||
timestamp,
|
|
||||||
&[], // no transactions
|
|
||||||
&[], // no ommers
|
|
||||||
None, // no withdrawals
|
|
||||||
None, // no requests
|
|
||||||
&chain_spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify basic fields
|
|
||||||
assert_eq!(header.parent_hash, best_block_hash);
|
|
||||||
assert_eq!(header.number, best_block_number + 1);
|
|
||||||
assert_eq!(header.timestamp, timestamp);
|
|
||||||
assert_eq!(header.gas_limit, chain_spec.max_gas_limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ommers_and_transactions_roots() {
|
|
||||||
let storage = StorageInner::default();
|
|
||||||
let chain_spec = ChainSpec::default();
|
|
||||||
let timestamp = 1_600_000_000;
|
|
||||||
|
|
||||||
// Setup ommers and transactions
|
|
||||||
let ommers = vec![Header::default()];
|
|
||||||
let transactions = vec![TransactionSigned::default()];
|
|
||||||
|
|
||||||
// Build header template
|
|
||||||
let header = storage.build_header_template(
|
|
||||||
timestamp,
|
|
||||||
&transactions,
|
|
||||||
&ommers,
|
|
||||||
None, // no withdrawals
|
|
||||||
None, // no requests
|
|
||||||
&chain_spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify ommers and transactions roots
|
|
||||||
assert_eq!(header.ommers_hash, proofs::calculate_ommers_root(&ommers));
|
|
||||||
assert_eq!(header.transactions_root, proofs::calculate_transaction_root(&transactions));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test base fee calculation from the parent block
|
|
||||||
#[test]
|
|
||||||
fn test_base_fee_calculation() {
|
|
||||||
let mut storage = StorageInner::default();
|
|
||||||
let chain_spec = ChainSpec::default();
|
|
||||||
let timestamp = 1_600_000_000;
|
|
||||||
|
|
||||||
// Set up the parent header with base fee
|
|
||||||
let base_fee = Some(100);
|
|
||||||
let parent_header = Header { base_fee_per_gas: base_fee, ..Default::default() };
|
|
||||||
storage.headers.insert(storage.best_block, parent_header);
|
|
||||||
|
|
||||||
// Build header template
|
|
||||||
let header = storage.build_header_template(
|
|
||||||
timestamp,
|
|
||||||
&[], // no transactions
|
|
||||||
&[], // no ommers
|
|
||||||
None, // no withdrawals
|
|
||||||
None, // no requests
|
|
||||||
&chain_spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify base fee is correctly propagated
|
|
||||||
assert_eq!(header.base_fee_per_gas, base_fee);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test blob gas and excess blob gas calculation when Cancun is active
|
|
||||||
#[test]
|
|
||||||
fn test_blob_gas_calculation_cancun() {
|
|
||||||
let storage = StorageInner::default();
|
|
||||||
let chain_spec = ChainSpec {
|
|
||||||
hardforks: ChainHardforks::new(vec![(
|
|
||||||
EthereumHardfork::Cancun.boxed(),
|
|
||||||
ForkCondition::Timestamp(25),
|
|
||||||
)]),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let timestamp = 26;
|
|
||||||
|
|
||||||
// Set up a transaction with blob gas
|
|
||||||
let blob_tx = TransactionSigned {
|
|
||||||
transaction: Transaction::Eip4844(Default::default()),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let transactions = vec![blob_tx];
|
|
||||||
|
|
||||||
// Build header template
|
|
||||||
let header = storage.build_header_template(
|
|
||||||
timestamp,
|
|
||||||
&transactions,
|
|
||||||
&[], // no ommers
|
|
||||||
None, // no withdrawals
|
|
||||||
None, // no requests
|
|
||||||
&chain_spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify that the header has the correct fields including blob gas
|
|
||||||
assert_eq!(
|
|
||||||
header,
|
|
||||||
Header {
|
|
||||||
parent_hash: B256::ZERO,
|
|
||||||
ommers_hash: proofs::calculate_ommers_root(&[]),
|
|
||||||
transactions_root: proofs::calculate_transaction_root(&transactions),
|
|
||||||
withdrawals_root: None,
|
|
||||||
difficulty: U256::from(2),
|
|
||||||
number: 1,
|
|
||||||
gas_limit: chain_spec.max_gas_limit,
|
|
||||||
timestamp,
|
|
||||||
base_fee_per_gas: None,
|
|
||||||
blob_gas_used: Some(0),
|
|
||||||
requests_hash: None,
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,166 +0,0 @@
|
|||||||
//! The mode the auto seal miner is operating in.
|
|
||||||
|
|
||||||
use alloy_primitives::TxHash;
|
|
||||||
use futures_util::{stream::Fuse, StreamExt};
|
|
||||||
use reth_transaction_pool::{TransactionPool, ValidPoolTransaction};
|
|
||||||
use std::{
|
|
||||||
fmt,
|
|
||||||
pin::Pin,
|
|
||||||
sync::Arc,
|
|
||||||
task::{Context, Poll},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
use tokio::{sync::mpsc::Receiver, time::Interval};
|
|
||||||
use tokio_stream::{wrappers::ReceiverStream, Stream};
|
|
||||||
|
|
||||||
/// Mode of operations for the `Miner`
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum MiningMode {
|
|
||||||
/// A miner that does nothing
|
|
||||||
None,
|
|
||||||
/// A miner that listens for new transactions that are ready.
|
|
||||||
///
|
|
||||||
/// Either one transaction will be mined per block, or any number of transactions will be
|
|
||||||
/// allowed
|
|
||||||
Auto(ReadyTransactionMiner),
|
|
||||||
/// A miner that constructs a new block every `interval` tick
|
|
||||||
FixedBlockTime(FixedBlockTimeMiner),
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl MiningMode ===
|
|
||||||
|
|
||||||
impl MiningMode {
|
|
||||||
/// Creates a new instant mining mode that listens for new transactions and tries to build
|
|
||||||
/// non-empty blocks as soon as transactions arrive.
|
|
||||||
pub fn instant(max_transactions: usize, listener: Receiver<TxHash>) -> Self {
|
|
||||||
Self::Auto(ReadyTransactionMiner {
|
|
||||||
max_transactions,
|
|
||||||
has_pending_txs: None,
|
|
||||||
rx: ReceiverStream::new(listener).fuse(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new interval miner that builds a block ever `duration`.
|
|
||||||
pub fn interval(duration: Duration) -> Self {
|
|
||||||
Self::FixedBlockTime(FixedBlockTimeMiner::new(duration))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// polls the Pool and returns those transactions that should be put in a block, if any.
|
|
||||||
pub(crate) fn poll<Pool>(
|
|
||||||
&mut self,
|
|
||||||
pool: &Pool,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Vec<Arc<ValidPoolTransaction<<Pool as TransactionPool>::Transaction>>>>
|
|
||||||
where
|
|
||||||
Pool: TransactionPool,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
Self::None => Poll::Pending,
|
|
||||||
Self::Auto(miner) => miner.poll(pool, cx),
|
|
||||||
Self::FixedBlockTime(miner) => miner.poll(pool, cx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for MiningMode {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let kind = match self {
|
|
||||||
Self::None => "None",
|
|
||||||
Self::Auto(_) => "Auto",
|
|
||||||
Self::FixedBlockTime(_) => "FixedBlockTime",
|
|
||||||
};
|
|
||||||
write!(f, "{kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A miner that's supposed to create a new block every `interval`, mining all transactions that are
|
|
||||||
/// ready at that time.
|
|
||||||
///
|
|
||||||
/// The default blocktime is set to 6 seconds
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FixedBlockTimeMiner {
|
|
||||||
/// The interval this fixed block time miner operates with
|
|
||||||
interval: Interval,
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl FixedBlockTimeMiner ===
|
|
||||||
|
|
||||||
impl FixedBlockTimeMiner {
|
|
||||||
/// Creates a new instance with an interval of `duration`
|
|
||||||
pub(crate) fn new(duration: Duration) -> Self {
|
|
||||||
let start = tokio::time::Instant::now() + duration;
|
|
||||||
Self { interval: tokio::time::interval_at(start, duration) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll<Pool>(
|
|
||||||
&mut self,
|
|
||||||
pool: &Pool,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Vec<Arc<ValidPoolTransaction<<Pool as TransactionPool>::Transaction>>>>
|
|
||||||
where
|
|
||||||
Pool: TransactionPool,
|
|
||||||
{
|
|
||||||
if self.interval.poll_tick(cx).is_ready() {
|
|
||||||
// drain the pool
|
|
||||||
return Poll::Ready(pool.best_transactions().collect())
|
|
||||||
}
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FixedBlockTimeMiner {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(Duration::from_secs(6))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A miner that Listens for new ready transactions
|
|
||||||
pub struct ReadyTransactionMiner {
|
|
||||||
/// how many transactions to mine per block
|
|
||||||
max_transactions: usize,
|
|
||||||
/// stores whether there are pending transactions (if known)
|
|
||||||
has_pending_txs: Option<bool>,
|
|
||||||
/// Receives hashes of transactions that are ready
|
|
||||||
rx: Fuse<ReceiverStream<TxHash>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl ReadyTransactionMiner ===
|
|
||||||
|
|
||||||
impl ReadyTransactionMiner {
|
|
||||||
fn poll<Pool>(
|
|
||||||
&mut self,
|
|
||||||
pool: &Pool,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Vec<Arc<ValidPoolTransaction<<Pool as TransactionPool>::Transaction>>>>
|
|
||||||
where
|
|
||||||
Pool: TransactionPool,
|
|
||||||
{
|
|
||||||
// drain the notification stream
|
|
||||||
while let Poll::Ready(Some(_hash)) = Pin::new(&mut self.rx).poll_next(cx) {
|
|
||||||
self.has_pending_txs = Some(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.has_pending_txs == Some(false) {
|
|
||||||
return Poll::Pending
|
|
||||||
}
|
|
||||||
|
|
||||||
let transactions = pool.best_transactions().take(self.max_transactions).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// there are pending transactions if we didn't drain the pool
|
|
||||||
self.has_pending_txs = Some(transactions.len() >= self.max_transactions);
|
|
||||||
|
|
||||||
if transactions.is_empty() {
|
|
||||||
return Poll::Pending
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Ready(transactions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ReadyTransactionMiner {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("ReadyTransactionMiner")
|
|
||||||
.field("max_transactions", &self.max_transactions)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,221 +0,0 @@
|
|||||||
use crate::{mode::MiningMode, Storage};
|
|
||||||
use alloy_rpc_types_engine::ForkchoiceState;
|
|
||||||
use futures_util::{future::BoxFuture, FutureExt};
|
|
||||||
use reth_beacon_consensus::{BeaconEngineMessage, ForkchoiceStatus};
|
|
||||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
|
||||||
use reth_engine_primitives::{EngineApiMessageVersion, EngineTypes};
|
|
||||||
use reth_evm::execute::BlockExecutorProvider;
|
|
||||||
use reth_provider::{CanonChainTracker, StateProviderFactory};
|
|
||||||
use reth_stages_api::PipelineEvent;
|
|
||||||
use reth_tokio_util::EventStream;
|
|
||||||
use reth_transaction_pool::{TransactionPool, ValidPoolTransaction};
|
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::Arc,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
|
||||||
use tracing::{debug, error, warn};
|
|
||||||
|
|
||||||
/// A Future that listens for new ready transactions and puts new blocks into storage
|
|
||||||
pub struct MiningTask<Client, Pool: TransactionPool, Executor, Engine: EngineTypes, ChainSpec> {
|
|
||||||
/// The configured chain spec
|
|
||||||
chain_spec: Arc<ChainSpec>,
|
|
||||||
/// The client used to interact with the state
|
|
||||||
client: Client,
|
|
||||||
/// The active miner
|
|
||||||
miner: MiningMode,
|
|
||||||
/// Single active future that inserts a new block into `storage`
|
|
||||||
insert_task: Option<BoxFuture<'static, Option<EventStream<PipelineEvent>>>>,
|
|
||||||
/// Shared storage to insert new blocks
|
|
||||||
storage: Storage,
|
|
||||||
/// Pool where transactions are stored
|
|
||||||
pool: Pool,
|
|
||||||
/// backlog of sets of transactions ready to be mined
|
|
||||||
queued: VecDeque<Vec<Arc<ValidPoolTransaction<<Pool as TransactionPool>::Transaction>>>>,
|
|
||||||
// TODO: ideally this would just be a sender of hashes
|
|
||||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
|
||||||
/// The pipeline events to listen on
|
|
||||||
pipe_line_events: Option<EventStream<PipelineEvent>>,
|
|
||||||
/// The type used for block execution
|
|
||||||
block_executor: Executor,
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl MiningTask ===
|
|
||||||
|
|
||||||
impl<Executor, Client, Pool: TransactionPool, Engine: EngineTypes, ChainSpec>
|
|
||||||
MiningTask<Client, Pool, Executor, Engine, ChainSpec>
|
|
||||||
{
|
|
||||||
/// Creates a new instance of the task
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub(crate) fn new(
|
|
||||||
chain_spec: Arc<ChainSpec>,
|
|
||||||
miner: MiningMode,
|
|
||||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
|
||||||
storage: Storage,
|
|
||||||
client: Client,
|
|
||||||
pool: Pool,
|
|
||||||
block_executor: Executor,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
chain_spec,
|
|
||||||
client,
|
|
||||||
miner,
|
|
||||||
insert_task: None,
|
|
||||||
storage,
|
|
||||||
pool,
|
|
||||||
to_engine,
|
|
||||||
queued: Default::default(),
|
|
||||||
pipe_line_events: None,
|
|
||||||
block_executor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the pipeline events to listen on.
|
|
||||||
pub fn set_pipeline_events(&mut self, events: EventStream<PipelineEvent>) {
|
|
||||||
self.pipe_line_events = Some(events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Executor, Client, Pool, Engine, ChainSpec> Future
|
|
||||||
for MiningTask<Client, Pool, Executor, Engine, ChainSpec>
|
|
||||||
where
|
|
||||||
Client: StateProviderFactory + CanonChainTracker + Clone + Unpin + 'static,
|
|
||||||
Pool: TransactionPool + Unpin + 'static,
|
|
||||||
Engine: EngineTypes,
|
|
||||||
Executor: BlockExecutorProvider,
|
|
||||||
ChainSpec: EthChainSpec + EthereumHardforks + 'static,
|
|
||||||
{
|
|
||||||
type Output = ();
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
|
|
||||||
// this drives block production and
|
|
||||||
loop {
|
|
||||||
if let Poll::Ready(transactions) = this.miner.poll(&this.pool, cx) {
|
|
||||||
// miner returned a set of transaction that we feed to the producer
|
|
||||||
this.queued.push_back(transactions);
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.insert_task.is_none() {
|
|
||||||
if this.queued.is_empty() {
|
|
||||||
// nothing to insert
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// ready to queue in new insert task
|
|
||||||
let storage = this.storage.clone();
|
|
||||||
let transactions = this.queued.pop_front().expect("not empty");
|
|
||||||
|
|
||||||
let to_engine = this.to_engine.clone();
|
|
||||||
let client = this.client.clone();
|
|
||||||
let chain_spec = Arc::clone(&this.chain_spec);
|
|
||||||
let events = this.pipe_line_events.take();
|
|
||||||
let executor = this.block_executor.clone();
|
|
||||||
|
|
||||||
// Create the mining future that creates a block, notifies the engine that drives
|
|
||||||
// the pipeline
|
|
||||||
this.insert_task = Some(Box::pin(async move {
|
|
||||||
let mut storage = storage.write().await;
|
|
||||||
|
|
||||||
let transactions: Vec<_> = transactions
|
|
||||||
.into_iter()
|
|
||||||
.map(|tx| {
|
|
||||||
let recovered = tx.to_recovered_transaction();
|
|
||||||
recovered.into_signed()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let ommers = vec![];
|
|
||||||
|
|
||||||
match storage.build_and_execute(
|
|
||||||
transactions.clone(),
|
|
||||||
ommers.clone(),
|
|
||||||
&client,
|
|
||||||
chain_spec,
|
|
||||||
&executor,
|
|
||||||
) {
|
|
||||||
Ok((new_header, _bundle_state)) => {
|
|
||||||
let state = ForkchoiceState {
|
|
||||||
head_block_hash: new_header.hash(),
|
|
||||||
finalized_block_hash: new_header.hash(),
|
|
||||||
safe_block_hash: new_header.hash(),
|
|
||||||
};
|
|
||||||
drop(storage);
|
|
||||||
|
|
||||||
// TODO: make this a future
|
|
||||||
// await the fcu call rx for SYNCING, then wait for a VALID response
|
|
||||||
loop {
|
|
||||||
// send the new update to the engine, this will trigger the engine
|
|
||||||
// to download and execute the block we just inserted
|
|
||||||
let (tx, rx) = oneshot::channel();
|
|
||||||
let _ = to_engine.send(BeaconEngineMessage::ForkchoiceUpdated {
|
|
||||||
state,
|
|
||||||
payload_attrs: None,
|
|
||||||
tx,
|
|
||||||
version: EngineApiMessageVersion::default(),
|
|
||||||
});
|
|
||||||
debug!(target: "consensus::auto", ?state, "Sent fork choice update");
|
|
||||||
|
|
||||||
match rx.await.unwrap() {
|
|
||||||
Ok(fcu_response) => {
|
|
||||||
match fcu_response.forkchoice_status() {
|
|
||||||
ForkchoiceStatus::Valid => break,
|
|
||||||
ForkchoiceStatus::Invalid => {
|
|
||||||
error!(target: "consensus::auto", ?fcu_response, "Forkchoice update returned invalid response");
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
ForkchoiceStatus::Syncing => {
|
|
||||||
debug!(target: "consensus::auto", ?fcu_response, "Forkchoice update returned SYNCING, waiting for VALID");
|
|
||||||
// wait for the next fork choice update
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(target: "consensus::auto", %err, "Autoseal fork choice update failed");
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update canon chain for rpc
|
|
||||||
client.set_canonical_head(new_header.clone());
|
|
||||||
client.set_safe(new_header.clone());
|
|
||||||
client.set_finalized(new_header.clone());
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(target: "consensus::auto", %err, "failed to execute block")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
events
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mut fut) = this.insert_task.take() {
|
|
||||||
match fut.poll_unpin(cx) {
|
|
||||||
Poll::Ready(events) => {
|
|
||||||
this.pipe_line_events = events;
|
|
||||||
}
|
|
||||||
Poll::Pending => {
|
|
||||||
this.insert_task = Some(fut);
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Client, Pool: TransactionPool, EvmConfig: std::fmt::Debug, Engine: EngineTypes, ChainSpec>
|
|
||||||
std::fmt::Debug for MiningTask<Client, Pool, EvmConfig, Engine, ChainSpec>
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("MiningTask").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -24,7 +24,6 @@ reth-network.workspace = true
|
|||||||
reth-evm.workspace = true
|
reth-evm.workspace = true
|
||||||
reth-evm-ethereum.workspace = true
|
reth-evm-ethereum.workspace = true
|
||||||
reth-consensus.workspace = true
|
reth-consensus.workspace = true
|
||||||
reth-auto-seal-consensus.workspace = true
|
|
||||||
reth-beacon-consensus.workspace = true
|
reth-beacon-consensus.workspace = true
|
||||||
reth-rpc.workspace = true
|
reth-rpc.workspace = true
|
||||||
reth-node-api.workspace = true
|
reth-node-api.workspace = true
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use reth_auto_seal_consensus::AutoSealConsensus;
|
|
||||||
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
|
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
|
||||||
use reth_beacon_consensus::EthBeaconConsensus;
|
use reth_beacon_consensus::EthBeaconConsensus;
|
||||||
use reth_chainspec::ChainSpec;
|
use reth_chainspec::ChainSpec;
|
||||||
@ -335,11 +334,7 @@ where
|
|||||||
type Consensus = Arc<dyn reth_consensus::Consensus>;
|
type Consensus = Arc<dyn reth_consensus::Consensus>;
|
||||||
|
|
||||||
async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
|
async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
|
||||||
if ctx.is_dev() {
|
Ok(Arc::new(EthBeaconConsensus::new(ctx.chain_spec())))
|
||||||
Ok(Arc::new(AutoSealConsensus::new(ctx.chain_spec())))
|
|
||||||
} else {
|
|
||||||
Ok(Arc::new(EthBeaconConsensus::new(ctx.chain_spec())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::utils::eth_payload_attributes;
|
|
||||||
use alloy_genesis::Genesis;
|
use alloy_genesis::Genesis;
|
||||||
use alloy_primitives::{b256, hex};
|
use alloy_primitives::{b256, hex};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use reth::{args::DevArgs, rpc::api::eth::helpers::EthTransactions};
|
use reth::{args::DevArgs, rpc::api::eth::helpers::EthTransactions};
|
||||||
use reth_chainspec::ChainSpec;
|
use reth_chainspec::ChainSpec;
|
||||||
use reth_e2e_test_utils::setup;
|
|
||||||
use reth_node_api::FullNodeComponents;
|
use reth_node_api::FullNodeComponents;
|
||||||
use reth_node_builder::{
|
use reth_node_builder::{
|
||||||
rpc::RethRpcAddOns, EngineNodeLauncher, FullNode, NodeBuilder, NodeConfig, NodeHandle,
|
rpc::RethRpcAddOns, EngineNodeLauncher, FullNode, NodeBuilder, NodeConfig, NodeHandle,
|
||||||
@ -17,16 +15,6 @@ use reth_tasks::TaskManager;
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn can_run_dev_node() -> eyre::Result<()> {
|
async fn can_run_dev_node() -> eyre::Result<()> {
|
||||||
reth_tracing::init_test_tracing();
|
|
||||||
let (mut nodes, _tasks, _) =
|
|
||||||
setup::<EthereumNode>(1, custom_chain(), true, eth_payload_attributes).await?;
|
|
||||||
|
|
||||||
assert_chain_advances(nodes.pop().unwrap().inner).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn can_run_dev_node_new_engine() -> eyre::Result<()> {
|
|
||||||
reth_tracing::init_test_tracing();
|
reth_tracing::init_test_tracing();
|
||||||
let tasks = TaskManager::current();
|
let tasks = TaskManager::current();
|
||||||
let exec = tasks.executor();
|
let exec = tasks.executor();
|
||||||
|
|||||||
@ -13,7 +13,6 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
## reth
|
## reth
|
||||||
reth-auto-seal-consensus.workspace = true
|
|
||||||
reth-beacon-consensus.workspace = true
|
reth-beacon-consensus.workspace = true
|
||||||
reth-blockchain-tree.workspace = true
|
reth-blockchain-tree.workspace = true
|
||||||
reth-chain-state.workspace = true
|
reth-chain-state.workspace = true
|
||||||
|
|||||||
@ -5,7 +5,6 @@ use std::{sync::Arc, thread::available_parallelism};
|
|||||||
use alloy_primitives::{BlockNumber, B256};
|
use alloy_primitives::{BlockNumber, B256};
|
||||||
use eyre::{Context, OptionExt};
|
use eyre::{Context, OptionExt};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use reth_auto_seal_consensus::MiningMode;
|
|
||||||
use reth_beacon_consensus::EthBeaconConsensus;
|
use reth_beacon_consensus::EthBeaconConsensus;
|
||||||
use reth_blockchain_tree::{
|
use reth_blockchain_tree::{
|
||||||
BlockchainTree, BlockchainTreeConfig, ShareableBlockchainTree, TreeExternals,
|
BlockchainTree, BlockchainTreeConfig, ShareableBlockchainTree, TreeExternals,
|
||||||
@ -16,6 +15,7 @@ use reth_consensus::Consensus;
|
|||||||
use reth_db_api::database::Database;
|
use reth_db_api::database::Database;
|
||||||
use reth_db_common::init::{init_genesis, InitDatabaseError};
|
use reth_db_common::init::{init_genesis, InitDatabaseError};
|
||||||
use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader};
|
use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader};
|
||||||
|
use reth_engine_local::MiningMode;
|
||||||
use reth_engine_tree::tree::{InvalidBlockHook, InvalidBlockHooks, NoopInvalidBlockHook};
|
use reth_engine_tree::tree::{InvalidBlockHook, InvalidBlockHooks, NoopInvalidBlockHook};
|
||||||
use reth_evm::noop::NoopBlockExecutorProvider;
|
use reth_evm::noop::NoopBlockExecutorProvider;
|
||||||
use reth_fs_util as fs;
|
use reth_fs_util as fs;
|
||||||
@ -52,8 +52,9 @@ use reth_stages::{sets::DefaultStages, MetricEvent, PipelineBuilder, PipelineTar
|
|||||||
use reth_static_file::StaticFileProducer;
|
use reth_static_file::StaticFileProducer;
|
||||||
use reth_tasks::TaskExecutor;
|
use reth_tasks::TaskExecutor;
|
||||||
use reth_tracing::tracing::{debug, error, info, warn};
|
use reth_tracing::tracing::{debug, error, info, warn};
|
||||||
|
use reth_transaction_pool::TransactionPool;
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
mpsc::{unbounded_channel, Receiver, UnboundedSender},
|
mpsc::{unbounded_channel, UnboundedSender},
|
||||||
oneshot, watch,
|
oneshot, watch,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -386,13 +387,11 @@ impl<R, ChainSpec: EthChainSpec> LaunchContextWith<Attached<WithConfigs<ChainSpe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`MiningMode`] intended for --dev mode.
|
/// Returns the [`MiningMode`] intended for --dev mode.
|
||||||
pub fn dev_mining_mode(&self, pending_transactions_listener: Receiver<B256>) -> MiningMode {
|
pub fn dev_mining_mode(&self, pool: impl TransactionPool) -> MiningMode {
|
||||||
if let Some(interval) = self.node_config().dev.block_time {
|
if let Some(interval) = self.node_config().dev.block_time {
|
||||||
MiningMode::interval(interval)
|
MiningMode::interval(interval)
|
||||||
} else if let Some(max_transactions) = self.node_config().dev.block_max_transactions {
|
|
||||||
MiningMode::instant(max_transactions, pending_transactions_listener)
|
|
||||||
} else {
|
} else {
|
||||||
MiningMode::instant(1, pending_transactions_listener)
|
MiningMode::instant(pool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use reth_beacon_consensus::{
|
|||||||
use reth_blockchain_tree::BlockchainTreeConfig;
|
use reth_blockchain_tree::BlockchainTreeConfig;
|
||||||
use reth_chainspec::EthChainSpec;
|
use reth_chainspec::EthChainSpec;
|
||||||
use reth_consensus_debug_client::{DebugConsensusClient, EtherscanBlockProvider};
|
use reth_consensus_debug_client::{DebugConsensusClient, EtherscanBlockProvider};
|
||||||
use reth_engine_local::{LocalEngineService, LocalPayloadAttributesBuilder, MiningMode};
|
use reth_engine_local::{LocalEngineService, LocalPayloadAttributesBuilder};
|
||||||
use reth_engine_service::service::{ChainEvent, EngineService};
|
use reth_engine_service::service::{ChainEvent, EngineService};
|
||||||
use reth_engine_tree::{
|
use reth_engine_tree::{
|
||||||
engine::{EngineApiRequest, EngineRequestHandler},
|
engine::{EngineApiRequest, EngineRequestHandler},
|
||||||
@ -208,11 +208,6 @@ where
|
|||||||
info!(target: "reth::cli", prune_config=?ctx.prune_config().unwrap_or_default(), "Pruner initialized");
|
info!(target: "reth::cli", prune_config=?ctx.prune_config().unwrap_or_default(), "Pruner initialized");
|
||||||
|
|
||||||
let mut engine_service = if ctx.is_dev() {
|
let mut engine_service = if ctx.is_dev() {
|
||||||
let mining_mode = if let Some(block_time) = ctx.node_config().dev.block_time {
|
|
||||||
MiningMode::interval(block_time)
|
|
||||||
} else {
|
|
||||||
MiningMode::instant(ctx.components().pool().clone())
|
|
||||||
};
|
|
||||||
let eth_service = LocalEngineService::new(
|
let eth_service = LocalEngineService::new(
|
||||||
ctx.consensus(),
|
ctx.consensus(),
|
||||||
ctx.components().block_executor().clone(),
|
ctx.components().block_executor().clone(),
|
||||||
@ -225,7 +220,7 @@ where
|
|||||||
ctx.sync_metrics_tx(),
|
ctx.sync_metrics_tx(),
|
||||||
consensus_engine_tx.clone(),
|
consensus_engine_tx.clone(),
|
||||||
Box::pin(consensus_engine_stream),
|
Box::pin(consensus_engine_stream),
|
||||||
mining_mode,
|
ctx.dev_mining_mode(ctx.components().pool()),
|
||||||
LocalPayloadAttributesBuilder::new(ctx.chain_spec()),
|
LocalPayloadAttributesBuilder::new(ctx.chain_spec()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ pub use exex::ExExLauncher;
|
|||||||
|
|
||||||
use std::{future::Future, sync::Arc};
|
use std::{future::Future, sync::Arc};
|
||||||
|
|
||||||
use alloy_primitives::utils::format_ether;
|
|
||||||
use futures::{future::Either, stream, stream_select, StreamExt};
|
use futures::{future::Either, stream, stream_select, StreamExt};
|
||||||
use reth_beacon_consensus::{
|
use reth_beacon_consensus::{
|
||||||
hooks::{EngineHooks, PruneHook, StaticFileHook},
|
hooks::{EngineHooks, PruneHook, StaticFileHook},
|
||||||
@ -33,7 +32,6 @@ use reth_provider::providers::BlockchainProvider;
|
|||||||
use reth_rpc::eth::RpcNodeCore;
|
use reth_rpc::eth::RpcNodeCore;
|
||||||
use reth_tasks::TaskExecutor;
|
use reth_tasks::TaskExecutor;
|
||||||
use reth_tracing::tracing::{debug, info};
|
use reth_tracing::tracing::{debug, info};
|
||||||
use reth_transaction_pool::TransactionPool;
|
|
||||||
use tokio::sync::{mpsc::unbounded_channel, oneshot};
|
use tokio::sync::{mpsc::unbounded_channel, oneshot};
|
||||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
|
|
||||||
@ -210,47 +208,7 @@ where
|
|||||||
let pipeline_exex_handle =
|
let pipeline_exex_handle =
|
||||||
exex_manager_handle.clone().unwrap_or_else(ExExManagerHandle::empty);
|
exex_manager_handle.clone().unwrap_or_else(ExExManagerHandle::empty);
|
||||||
let (pipeline, client) = if ctx.is_dev() {
|
let (pipeline, client) = if ctx.is_dev() {
|
||||||
info!(target: "reth::cli", "Starting Reth in dev mode");
|
eyre::bail!("Dev mode is not supported for legacy engine")
|
||||||
|
|
||||||
for (idx, (address, alloc)) in ctx.chain_spec().genesis().alloc.iter().enumerate() {
|
|
||||||
info!(target: "reth::cli", "Allocated Genesis Account: {:02}. {} ({} ETH)", idx, address.to_string(), format_ether(alloc.balance));
|
|
||||||
}
|
|
||||||
|
|
||||||
// install auto-seal
|
|
||||||
let mining_mode =
|
|
||||||
ctx.dev_mining_mode(ctx.components().pool().pending_transactions_listener());
|
|
||||||
info!(target: "reth::cli", mode=%mining_mode, "configuring dev mining mode");
|
|
||||||
|
|
||||||
let (_, client, mut task) = reth_auto_seal_consensus::AutoSealBuilder::new(
|
|
||||||
ctx.chain_spec(),
|
|
||||||
ctx.blockchain_db().clone(),
|
|
||||||
ctx.components().pool().clone(),
|
|
||||||
consensus_engine_tx.clone(),
|
|
||||||
mining_mode,
|
|
||||||
ctx.components().block_executor().clone(),
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let pipeline = crate::setup::build_networked_pipeline(
|
|
||||||
&ctx.toml_config().stages,
|
|
||||||
client.clone(),
|
|
||||||
ctx.consensus(),
|
|
||||||
ctx.provider_factory().clone(),
|
|
||||||
ctx.task_executor(),
|
|
||||||
ctx.sync_metrics_tx(),
|
|
||||||
ctx.prune_config(),
|
|
||||||
max_block,
|
|
||||||
static_file_producer,
|
|
||||||
ctx.components().block_executor().clone(),
|
|
||||||
pipeline_exex_handle,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let pipeline_events = pipeline.events();
|
|
||||||
task.set_pipeline_events(pipeline_events);
|
|
||||||
debug!(target: "reth::cli", "Spawning auto mine task");
|
|
||||||
ctx.task_executor().spawn(Box::pin(task));
|
|
||||||
|
|
||||||
(pipeline, Either::Left(client))
|
|
||||||
} else {
|
} else {
|
||||||
let pipeline = crate::setup::build_networked_pipeline(
|
let pipeline = crate::setup::build_networked_pipeline(
|
||||||
&ctx.toml_config().stages,
|
&ctx.toml_config().stages,
|
||||||
@ -266,7 +224,7 @@ where
|
|||||||
pipeline_exex_handle,
|
pipeline_exex_handle,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
(pipeline, Either::Right(network_client.clone()))
|
(pipeline, network_client.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let pipeline_events = pipeline.events();
|
let pipeline_events = pipeline.events();
|
||||||
|
|||||||
@ -16,7 +16,6 @@ reth-chainspec.workspace = true
|
|||||||
reth-engine-local.workspace = true
|
reth-engine-local.workspace = true
|
||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-payload-builder.workspace = true
|
reth-payload-builder.workspace = true
|
||||||
reth-auto-seal-consensus.workspace = true
|
|
||||||
reth-basic-payload-builder.workspace = true
|
reth-basic-payload-builder.workspace = true
|
||||||
reth-consensus.workspace = true
|
reth-consensus.workspace = true
|
||||||
reth-node-api.workspace = true
|
reth-node-api.workspace = true
|
||||||
@ -76,7 +75,6 @@ optimism = [
|
|||||||
"reth-optimism-payload-builder/optimism",
|
"reth-optimism-payload-builder/optimism",
|
||||||
"reth-beacon-consensus/optimism",
|
"reth-beacon-consensus/optimism",
|
||||||
"revm/optimism",
|
"revm/optimism",
|
||||||
"reth-auto-seal-consensus/optimism",
|
|
||||||
"reth-optimism-rpc/optimism",
|
"reth-optimism-rpc/optimism",
|
||||||
"reth-engine-local/optimism",
|
"reth-engine-local/optimism",
|
||||||
"reth-optimism-consensus/optimism",
|
"reth-optimism-consensus/optimism",
|
||||||
|
|||||||
@ -467,11 +467,7 @@ where
|
|||||||
type Consensus = Arc<dyn reth_consensus::Consensus>;
|
type Consensus = Arc<dyn reth_consensus::Consensus>;
|
||||||
|
|
||||||
async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
|
async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
|
||||||
if ctx.is_dev() {
|
Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
|
||||||
Ok(Arc::new(reth_auto_seal_consensus::AutoSealConsensus::new(ctx.chain_spec())))
|
|
||||||
} else {
|
|
||||||
Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,6 @@ The networking component mainly lives in [`net/network`](../../crates/net/networ
|
|||||||
Different consensus mechanisms.
|
Different consensus mechanisms.
|
||||||
|
|
||||||
- [`consensus/common`](../../crates/consensus/common): Common consensus functions and traits (e.g. fee calculation)
|
- [`consensus/common`](../../crates/consensus/common): Common consensus functions and traits (e.g. fee calculation)
|
||||||
- [`consensus/auto-seal`](../../crates/consensus/auto-seal): A consensus mechanism that auto-seals blocks for local development (also commonly known as "auto-mine")
|
|
||||||
- [`consensus/beacon`](../../crates/consensus/beacon): Consensus mechanism that handles messages from a beacon node ("eth2")
|
- [`consensus/beacon`](../../crates/consensus/beacon): Consensus mechanism that handles messages from a beacon node ("eth2")
|
||||||
|
|
||||||
### Execution
|
### Execution
|
||||||
|
|||||||
Reference in New Issue
Block a user