diff --git a/Cargo.lock b/Cargo.lock
index 1a9674584e14..1c6d731bcf52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7765,6 +7765,7 @@ dependencies = [
"reth-consensus",
"reth-db",
"reth-e2e-test-utils",
+ "reth-engine-tree",
"reth-ethereum-engine",
"reth-ethereum-engine-primitives",
"reth-ethereum-payload-builder",
diff --git a/crates/engine/tree/src/tree/config.rs b/crates/engine/tree/src/tree/config.rs
new file mode 100644
index 000000000000..2d5a3b9020f3
--- /dev/null
+++ b/crates/engine/tree/src/tree/config.rs
@@ -0,0 +1,74 @@
+//! Engine tree configuration.
+
+const DEFAULT_PERSISTENCE_THRESHOLD: u64 = 256;
+const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = 256;
+const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
+
+/// The configuration of the engine tree.
+#[derive(Debug)]
+pub struct TreeConfig {
+ /// Maximum number of blocks to be kept only in memory without triggering persistence.
+ persistence_threshold: u64,
+ /// Number of pending blocks that cannot be executed due to missing parent and
+ /// are kept in cache.
+ block_buffer_limit: u32,
+ /// Number of invalid headers to keep in cache.
+ max_invalid_header_cache_length: u32,
+}
+
+impl Default for TreeConfig {
+ fn default() -> Self {
+ Self {
+ persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
+ block_buffer_limit: DEFAULT_BLOCK_BUFFER_LIMIT,
+ max_invalid_header_cache_length: DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
+ }
+ }
+}
+
+impl TreeConfig {
+ /// Create engine tree configuration.
+ pub const fn new(
+ persistence_threshold: u64,
+ block_buffer_limit: u32,
+ max_invalid_header_cache_length: u32,
+ ) -> Self {
+ Self { persistence_threshold, block_buffer_limit, max_invalid_header_cache_length }
+ }
+
+ /// Return the persistence threshold.
+ pub const fn persistence_threshold(&self) -> u64 {
+ self.persistence_threshold
+ }
+
+ /// Return the block buffer limit.
+ pub const fn block_buffer_limit(&self) -> u32 {
+ self.block_buffer_limit
+ }
+
+ /// Return the maximum invalid cache header length.
+ pub const fn max_invalid_header_cache_length(&self) -> u32 {
+ self.max_invalid_header_cache_length
+ }
+
+ /// Setter for persistence threshold.
+ pub const fn with_persistence_threshold(mut self, persistence_threshold: u64) -> Self {
+ self.persistence_threshold = persistence_threshold;
+ self
+ }
+
+ /// Setter for block buffer limit.
+ pub const fn with_block_buffer_limit(mut self, block_buffer_limit: u32) -> Self {
+ self.block_buffer_limit = block_buffer_limit;
+ self
+ }
+
+ /// Setter for maximum invalid header cache length.
+ pub const fn with_max_invalid_header_cache_length(
+ mut self,
+ max_invalid_header_cache_length: u32,
+ ) -> Self {
+ self.max_invalid_header_cache_length = max_invalid_header_cache_length;
+ self
+ }
+}
diff --git a/crates/engine/tree/src/tree.rs b/crates/engine/tree/src/tree/mod.rs
similarity index 98%
rename from crates/engine/tree/src/tree.rs
rename to crates/engine/tree/src/tree/mod.rs
index 6f0856e29d97..98cf2590f779 100644
--- a/crates/engine/tree/src/tree.rs
+++ b/crates/engine/tree/src/tree/mod.rs
@@ -49,13 +49,8 @@ use tokio::sync::{
};
use tracing::*;
-/// Maximum number of blocks to be kept only in memory without triggering persistence.
-const PERSISTENCE_THRESHOLD: u64 = 256;
-/// Number of pending blocks that cannot be executed due to missing parent and
-/// are kept in cache.
-const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = 256;
-/// Number of invalid headers to keep in cache.
-const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
+mod config;
+pub use config::TreeConfig;
/// Keeps track of the state of the tree.
///
@@ -387,6 +382,8 @@ pub struct EngineApiTreeHandlerImpl
{
/// Handle to the payload builder that will receive payload attributes for valid forkchoice
/// updates
payload_builder: PayloadBuilderHandle,
+ /// Configuration settings.
+ config: TreeConfig,
}
impl EngineApiTreeHandlerImpl
@@ -407,6 +404,7 @@ where
canonical_in_memory_state: CanonicalInMemoryState,
persistence: PersistenceHandle,
payload_builder: PayloadBuilderHandle,
+ config: TreeConfig,
) -> Self {
Self {
provider,
@@ -421,6 +419,7 @@ where
state,
canonical_in_memory_state,
payload_builder,
+ config,
}
}
@@ -437,14 +436,15 @@ where
persistence: PersistenceHandle,
payload_builder: PayloadBuilderHandle,
canonical_in_memory_state: CanonicalInMemoryState,
+ config: TreeConfig,
) -> UnboundedReceiver {
let best_block_number = provider.best_block_number().unwrap_or(0);
let header = provider.sealed_header(best_block_number).ok().flatten().unwrap_or_default();
let (tx, outgoing) = tokio::sync::mpsc::unbounded_channel();
let state = EngineApiTreeState::new(
- DEFAULT_BLOCK_BUFFER_LIMIT,
- DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
+ config.block_buffer_limit(),
+ config.max_invalid_header_cache_length(),
header.num_hash(),
);
@@ -459,6 +459,7 @@ where
canonical_in_memory_state,
persistence,
payload_builder,
+ config,
);
std::thread::Builder::new().name("Tree Task".to_string()).spawn(|| task.run()).unwrap();
outgoing
@@ -657,12 +658,12 @@ where
fn should_persist(&self) -> bool {
self.state.tree_state.max_block_number() -
self.persistence_state.last_persisted_block_number >=
- PERSISTENCE_THRESHOLD
+ self.config.persistence_threshold()
}
fn get_blocks_to_persist(&self) -> Vec {
let start = self.persistence_state.last_persisted_block_number;
- let end = start + PERSISTENCE_THRESHOLD;
+ let end = start + self.config.persistence_threshold();
// NOTE: this is an exclusive range, to try to include exactly PERSISTENCE_THRESHOLD blocks
self.state
@@ -1607,6 +1608,7 @@ mod tests {
canonical_in_memory_state,
persistence_handle,
payload_builder,
+ TreeConfig::default(),
);
Self { tree, to_tree_tx, blocks: vec![], action_rx, payload_command_rx }
@@ -1675,6 +1677,7 @@ mod tests {
canonical_in_memory_state,
persistence_handle,
payload_builder,
+ TreeConfig::default(),
);
let last_executed_block = blocks.last().unwrap().clone();
let pending = Some(BlockState::new(last_executed_block));
@@ -1688,8 +1691,10 @@ mod tests {
async fn test_tree_persist_blocks() {
// we need more than PERSISTENCE_THRESHOLD blocks to trigger the
// persistence task.
+ let tree_config = TreeConfig::default();
+
let TestHarness { tree, to_tree_tx, action_rx, mut blocks, payload_command_rx } =
- get_default_test_harness(PERSISTENCE_THRESHOLD + 1);
+ get_default_test_harness(tree_config.persistence_threshold() + 1);
std::thread::Builder::new().name("Tree Task".to_string()).spawn(|| tree.run()).unwrap();
// send a message to the tree to enter the main loop.
@@ -1699,7 +1704,7 @@ mod tests {
if let PersistenceAction::SaveBlocks((saved_blocks, _)) = received_action {
// only PERSISTENCE_THRESHOLD will be persisted
blocks.pop();
- assert_eq!(saved_blocks.len() as u64, PERSISTENCE_THRESHOLD);
+ assert_eq!(saved_blocks.len() as u64, tree_config.persistence_threshold());
assert_eq!(saved_blocks, blocks);
} else {
panic!("unexpected action received {received_action:?}");
@@ -1731,8 +1736,10 @@ mod tests {
#[tokio::test]
async fn test_engine_request_during_backfill() {
+ let tree_config = TreeConfig::default();
+
let TestHarness { mut tree, to_tree_tx, action_rx, blocks, payload_command_rx } =
- get_default_test_harness(PERSISTENCE_THRESHOLD);
+ get_default_test_harness(tree_config.persistence_threshold());
// set backfill active
tree.backfill_sync_state = BackfillSyncState::Active;
@@ -1754,7 +1761,7 @@ mod tests {
#[tokio::test]
async fn test_holesky_payload() {
- let s = include_str!("../test-data/holesky/1.rlp");
+ let s = include_str!("../../test-data/holesky/1.rlp");
let data = Bytes::from_str(s).unwrap();
let block = Block::decode(&mut data.as_ref()).unwrap();
let sealed = block.seal_slow();
diff --git a/crates/ethereum/engine/src/service.rs b/crates/ethereum/engine/src/service.rs
index b4f63aa7254a..a258c26c9558 100644
--- a/crates/ethereum/engine/src/service.rs
+++ b/crates/ethereum/engine/src/service.rs
@@ -8,7 +8,7 @@ use reth_engine_tree::{
download::BasicBlockDownloader,
engine::{EngineApiRequestHandler, EngineHandler},
persistence::PersistenceHandle,
- tree::EngineApiTreeHandlerImpl,
+ tree::{EngineApiTreeHandlerImpl, TreeConfig},
};
pub use reth_engine_tree::{
chain::{ChainEvent, ChainOrchestrator},
@@ -68,6 +68,7 @@ where
blockchain_db: BlockchainProvider2,
pruner: Pruner>,
payload_builder: PayloadBuilderHandle,
+ tree_config: TreeConfig,
) -> Self {
let consensus = Arc::new(EthBeaconConsensus::new(chain_spec.clone()));
let downloader = BasicBlockDownloader::new(client, consensus.clone());
@@ -89,6 +90,7 @@ where
persistence_handle,
payload_builder,
canonical_in_memory_state,
+ tree_config,
);
let engine_handler = EngineApiRequestHandler::new(to_tree_tx, from_tree);
@@ -174,6 +176,7 @@ mod tests {
blockchain_db,
pruner,
PayloadBuilderHandle::new(tx),
+ TreeConfig::default(),
);
}
}
diff --git a/crates/ethereum/node/Cargo.toml b/crates/ethereum/node/Cargo.toml
index f22490859a95..7b2cef39e538 100644
--- a/crates/ethereum/node/Cargo.toml
+++ b/crates/ethereum/node/Cargo.toml
@@ -36,6 +36,7 @@ reth-node-events.workspace = true
reth-node-core.workspace = true
reth-exex.workspace = true
reth-blockchain-tree.workspace = true
+reth-engine-tree.workspace = true
# misc
eyre.workspace = true
diff --git a/crates/ethereum/node/src/launch.rs b/crates/ethereum/node/src/launch.rs
index 898b376025fb..2ecc57ac08b2 100644
--- a/crates/ethereum/node/src/launch.rs
+++ b/crates/ethereum/node/src/launch.rs
@@ -6,6 +6,7 @@ use reth_beacon_consensus::{
BeaconConsensusEngineHandle,
};
use reth_blockchain_tree::BlockchainTreeConfig;
+use reth_engine_tree::tree::TreeConfig;
use reth_ethereum_engine::service::{ChainEvent, EthService};
use reth_ethereum_engine_primitives::EthEngineTypes;
use reth_exex::ExExManagerHandle;
@@ -171,6 +172,8 @@ where
let pruner_events = pruner.events();
info!(target: "reth::cli", prune_config=?ctx.prune_config().unwrap_or_default(), "Pruner initialized");
+ let tree_config = TreeConfig::default().with_persistence_threshold(120);
+
// Configure the consensus engine
let mut eth_service = EthService::new(
ctx.chain_spec(),
@@ -182,6 +185,7 @@ where
ctx.blockchain_db().clone(),
pruner,
ctx.components().payload_builder().clone(),
+ tree_config,
);
let event_sender = EventSender::default();