 = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(3);
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 3); // Now 3 deleted

        // Increment again
        limiter.increment_deleted_entries_count_by(2);
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 5); // Now 5 deleted
    }

    #[test]
    fn test_increment_deleted_entries_count() {
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(5);
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 0); // Initially 0

        limiter.increment_deleted_entries_count(); // Increment by 1
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 1); // Now 1
    }

    #[test]
    fn test_deleted_entries_limit_left() {
        // Test when limit is set and some entries are deleted
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(3); // Simulate 3 deleted entries
        assert_eq!(limiter.deleted_entries_limit_left(), Some(7)); // 10 - 3 = 7

        // Test when no entries are deleted
        limiter = PruneLimiter::default().set_deleted_entries_limit(5);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(5)); // 5 - 0 = 5

        // Test when limit is reached
        limiter.increment_deleted_entries_count_by(5); // Simulate deleting 5 entries
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0)); // 5 - 5 = 0

        // Test when limit is not set
        limiter = PruneLimiter::default(); // No limit set
        assert_eq!(limiter.deleted_entries_limit_left(), None); // Should be None
    }

    #[test]
    fn test_set_time_limit() {
        // Create a PruneLimiter instance with no time limit set
        let mut limiter = PruneLimiter::default();

        // Set a time limit of 5 seconds
        limiter = limiter.set_time_limit(Duration::new(5, 0));

        // Verify that the time limit is set correctly
        assert!(limiter.time_limit.is_some());
        let time_limit = limiter.time_limit.as_ref().unwrap();
        assert_eq!(time_limit.limit, Duration::new(5, 0));
        // Ensure the start time is recent
        assert!(time_limit.start.elapsed() < Duration::new(1, 0));
    }

    #[test]
    fn test_is_time_limit_reached() {
        // Create a PruneLimiter instance and set a time limit of 10 milliseconds
        let mut limiter = PruneLimiter::default();

        // Time limit should not be reached initially
        assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet");

        limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds

        // Sleep for 5 milliseconds (less than the time limit)
        sleep(Duration::new(0, 5_000_000)); // 5 milliseconds
        assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet");

        // Sleep for an additional 10 milliseconds (totaling 15 milliseconds)
        sleep(Duration::new(0, 10_000_000)); // 10 milliseconds
        assert!(limiter.is_time_limit_reached(), "Time limit should be reached now");
    }

    #[test]
    fn test_is_limit_reached()

{
        // Create a PruneLimiter instance
        let mut limiter = PruneLimiter::default();

        // Test when no limits are set
        assert!(!limiter.is_limit_reached(), "Limit should not be reached with no limits set");

        // Set a deleted entries limit
        limiter = limiter.set_deleted_entries_limit(5);
        assert!(
            !limiter.is_limit_reached(),
            "Limit should not be reached when deleted entries are less than limit"
        );

        // Increment deleted entries count to reach the limit
        limiter.increment_deleted_entries_count_by(5);
        assert!(
            limiter.is_limit_reached(),
            "Limit should be reached when deleted entries equal the limit"
        );

        // Reset the limiter
        limiter = PruneLimiter::default();

        // Set a time limit and check
        limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds

        // Sleep for 5 milliseconds (less than the time limit)
        sleep(Duration::new(0, 5_000_000)); // 5 milliseconds
        assert!(
            !limiter.is_limit_reached(),
            "Limit should not be reached when time limit not reached"
        );

        // Sleep for another 10 milliseconds (totaling 15 milliseconds)
        sleep(Duration::new(0, 10_000_000)); // 10 milliseconds
        assert!(limiter.is_limit_reached(), "Limit should be reached when time limit is reached");
    }


ntries_limit.is_limit_reached());
    }

    #[test]
    fn test_floor_deleted_entries_limit_to_multiple_of() {
        let limiter = PruneLimiter::default().set_deleted_entries_limit(15);
        let denominator = NonZeroUsize::new(4).unwrap();

        // Floor limit to the largest multiple of 4 less than or equal to 15 (that is 12)
        let updated_limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator);
        assert_eq!(updated_limiter.deleted_entries_limit.unwrap().limit, 12);

        // Test when the limit is already a multiple of the denominator
        let limiter = PruneLimiter::default().set_deleted_entries_limit(16);
        let updated_limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator);
        assert_eq!(updated_limiter.deleted_entries_limit.unwrap().limit, 16);

        // Test when there's no limit set (should not panic)
        let limiter = PruneLimiter::default();
        let updated_limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator);
        assert!(updated_limiter.deleted_entries_limit.is_none());
    }

    #[test]
    fn test_is_deleted_entries_limit_reached() {
        // Limit is not set, should return false
        let limiter = PruneLimiter::default();
        assert!(!limiter.is_deleted_entries_limit_reached());

        // Limit is set but not reached, should return false
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.deleted_entries_limit.as_mut().unwrap().deleted = 5;
        // 5 entries deleted out of 10
        assert!(!limiter.is_deleted_entries_limit_reached());

        // Limit is reached, should return true
        limiter.deleted_entries_limit.as_mut().unwrap().deleted = 10;
        // 10 entries deleted out of 10
        assert!(limiter.is_deleted_entries_limit_reached());

        // Deleted entries exceed the limit, should return true
        limiter.deleted_entries_limit.as_mut().unwrap().deleted = 12;
        // 12 entries deleted out of 10
        assert!(limiter.is_deleted_entries_limit_reached());
    }

    #[test]
    fn test_increment_deleted_entries_count_by() {
        // Increment when no limit is set
        let mut limiter = PruneLimiter::default();
        limiter.increment_deleted_entries_count_by(5);
        assert_eq!(limiter.deleted_entries_limit.as_ref().map(|l| l.deleted), None); // Still None

        // Increment when limit is set
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(3);
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 3); // Now 3 deleted

        // Increment again
        limiter.increment_deleted_entries_count_by(2);
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 5); // Now 5 deleted
    }

    #[test]
    fn test_increment_deleted_entries_count() {
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(5);
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 0); // Initially 0

        limiter.increment_deleted_entries_count(); // Increment by 1
        assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 1); // Now 1
    }

    #[test]
    fn test_deleted_entries_limit_left() {
        // Test when limit is set and some entries are deleted
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(3); // Simulate 3 deleted entries
        assert_eq!(limiter.deleted_entries_limit_left(), Some(7)); // 10 - 3 = 7

        // Test when no entries are deleted
        limiter = PruneLimiter::default().set_deleted_entries_limit(5);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(5)); // 5 - 0 = 5

        // Test when limit is reached
        limiter.increment_deleted_entries_count_by(5); // Simulate deleting 5 entries
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0)); // 5 - 5 = 0

        // Test when limit is not set
        limiter = PruneLimiter::default(); // No limit set
        assert_eq!(limiter.deleted_entries_limit_left(), None); // Should be None
    }

    #[test]
    fn test_set_time_limit() {
        // Create a PruneLimiter instance with no time limit set
        let mut limiter = PruneLimiter::default();

        // Set a time limit of 5 seconds
        limiter = limiter.set_time_limit(Duration::new(5, 0));

        // Verify that the time limit is set correctly
        assert!(limiter.time_limit.is_some());
        let time_limit = limiter.time_limit.as_ref().unwrap();
        assert_eq!(time_limit.limit, Duration::new(5, 0));
        // Ensure the start time is recent
        assert!(time_limit.start.elapsed() < Duration::new(1, 0));
    }

    #[test]
    fn test_is_time_limit_reached() {
        // Create a PruneLimiter instance and set a time limit of 10 milliseconds
        let mut limiter = PruneLimiter::default();

        // Time limit should not be reached initially
        assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet");

        limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds

        // Sleep for 5 milliseconds (less than the time limit)
        sleep(Duration::new(0, 5_000_000)); // 5 milliseconds
        assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet");

        // Sleep for an additional 10 milliseconds (totaling 15 milliseconds)
        sleep(Duration::new(0, 10_000_000)); // 10 milliseconds
        assert!(limiter.is_time_limit_reached(), "Time limit should be reached now");
    }

    #[test]
    fn test_is_limit_reached() {
        // Create a PruneLimiter instance
        let mut limiter = PruneLimiter::default();

        // Test when no limits are set
        assert!(!limiter.is_limit_reached(), "Limit should not be reached with no limits set");

        // Set a deleted entries limit
        limiter = limiter.set_deleted_entries_limit(5);
        assert!(
            !limiter.is_limit_reached(),
            "Limit should not be reached when deleted entries are less than limit"
        );

        // Increment deleted entries count to reach the limit
        limiter.increment_deleted_entries_count_by(5);
        assert!(
            limiter.is_limit_reached(),
            "Limit should be reached when deleted entries equal the limit"
        );

        // Reset the limiter
        limiter = PruneLimiter::default();

        // Set a time limit and check
        limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds

        // Sleep for 5 milliseconds (less than the time limit)
        sleep(Duration::new(0, 5_000_000)); // 5 milliseconds
        assert!(
            !limiter.is_limit_reached(),
            "Limit should not be reached when time limit not reached"
        );

        // Sleep for another 10 milliseconds (totaling 15 milliseconds)
        sleep(Duration::new(0, 10_000_000)); // 10 milliseconds
        assert!(limiter.is_limit_reached(), "Limit should be reached when time limit is reached");
    }

    #[test]

    fn test_deleted_entries_limit_left_saturation_and_normal() {
        // less than limit → no saturation
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(3);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(7));

        // equal to limit → saturates to 0
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(3);
        limiter.increment_deleted_entries_count_by(3);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));

        // overrun past limit → saturates to 0
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(12);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));

        // lowering limit via set → saturates to 0 if below deleted
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(20);
        limiter.increment_deleted_entries_count_by(15);
        let limiter = limiter.set_deleted_entries_limit(10);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));

        // lowering limit via floor → saturates to 0 if below deleted
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(15);
        limiter.increment_deleted_entries_count_by(14);
        let denominator = NonZeroUsize::new(8).unwrap();
        let limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));
    }


/ 5 - 5 = 0

        // Test when limit is not set
        limiter = PruneLimiter::default(); // No limit set
        assert_eq!(limiter.deleted_entries_limit_left(), None); // Should be None
    }

    #[test]
    fn test_set_time_limit() {
        // Create a PruneLimiter instance with no time limit set
        let mut limiter = PruneLimiter::default();

        // Set a time limit of 5 seconds
        limiter = limiter.set_time_limit(Duration::new(5, 0));

        // Verify that the time limit is set correctly
        assert!(limiter.time_limit.is_some());
        let time_limit = limiter.time_limit.as_ref().unwrap();
        assert_eq!(time_limit.limit, Duration::new(5, 0));
        // Ensure the start time is recent
        assert!(time_limit.start.elapsed() < Duration::new(1, 0));
    }

    #[test]
    fn test_is_time_limit_reached() {
        // Create a PruneLimiter instance and set a time limit of 10 milliseconds
        let mut limiter = PruneLimiter::default();

        // Time limit should not be reached initially
        assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet");

        limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds

        // Sleep for 5 milliseconds (less than the time limit)
        sleep(Duration::new(0, 5_000_000)); // 5 milliseconds
        assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet");

        // Sleep for an additional 10 milliseconds (totaling 15 milliseconds)
        sleep(Duration::new(0, 10_000_000)); // 10 milliseconds
        assert!(limiter.is_time_limit_reached(), "Time limit should be reached now");
    }

    #[test]
    fn test_is_limit_reached() {
        // Create a PruneLimiter instance
        let mut limiter = PruneLimiter::default();

        // Test when no limits are set
        assert!(!limiter.is_limit_reached(), "Limit should not be reached with no limits set");

        // Set a deleted entries limit
        limiter = limiter.set_deleted_entries_limit(5);
        assert!(
            !limiter.is_limit_reached(),
            "Limit should not be reached when deleted entries are less than limit"
        );

        // Increment deleted entries count to reach the limit
        limiter.increment_deleted_entries_count_by(5);
        assert!(
            limiter.is_limit_reached(),
            "Limit should be reached when deleted entries equal the limit"
        );

        // Reset the limiter
        limiter = PruneLimiter::default();

        // Set a time limit and check
        limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds

        // Sleep for 5 milliseconds (less than the time limit)
        sleep(Duration::new(0, 5_000_000)); // 5 milliseconds
        assert!(
            !limiter.is_limit_reached(),
            "Limit should not be reached when time limit not reached"
        );

        // Sleep for another 10 milliseconds (totaling 15 milliseconds)
        sleep(Duration::new(0, 10_000_000)); // 10 milliseconds
        assert!(limiter.is_limit_reached(), "Limit should be reached when time limit is reached");
    }

    #[test]
    fn test_deleted_entries_limit_left_saturation_and_normal()

{
        // less than limit → no saturation
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(3);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(7));

        // equal to limit → saturates to 0
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(3);
        limiter.increment_deleted_entries_count_by(3);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));

        // overrun past limit → saturates to 0
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10);
        limiter.increment_deleted_entries_count_by(12);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));

        // lowering limit via set → saturates to 0 if below deleted
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(20);
        limiter.increment_deleted_entries_count_by(15);
        let limiter = limiter.set_deleted_entries_limit(10);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));

        // lowering limit via floor → saturates to 0 if below deleted
        let mut limiter = PruneLimiter::default().set_deleted_entries_limit(15);
        limiter.increment_deleted_entries_count_by(14);
        let denominator = NonZeroUsize::new(8).unwrap();
        let limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator);
        assert_eq!(limiter.deleted_entries_limit_left(), Some(0));
    }


//! Support for launching execution extensions.

use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: component


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};

use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(
                    "exex",
                    async move {
                        info!(target: "reth::cli", "ExEx started");
                        match exex.await {
                            Ok(_) => panic!("ExEx {id} finished. ExExes should run indefinitely"),
                            Err(err) => panic!("ExEx {id} crashed: {err}"),
                        }
                    }
                    .instrument(span),
                );

                Ok::<(), eyre::Error>(())
            });
        }

        future::try_join_all(exexes).await?;

        // spawn exex manager
        debug!(target: "reth::cli", "spawning exex manager");
        let exex_manager = ExExManager::new(
            components.provider().clone(),
            exex_handles,
            capacity,
            exex_wal,
            components.provider().finalized_block_stream(),
        )
        .with_wal_blocks_warning(wal_blocks_warning);
        let exex_manager_handle = exex_manager.handle();
        components.task_executor().spawn_critical_task("exex manager", async move {
            exex_manager.await.expect("exex manager crashed");
        });

        // send notifications from the blockchain tree to exex manager
        let mut canon_state_notifications = components.provider().subscribe_to_canonical_state();
        let mut handle = exex_manager_handle.clone();
        components.task_executor().spawn_critical_task(
            "exex manager blockchain tree notifications",
            async move {
                while let Ok(notification) = canon_state_notifications.recv().await {
                    handle
                        .send_async(ExExNotificationSource::BlockchainTree, notification.into())
                        .await
                        .expect("blockchain tree notification could not be sent to exex manager");
                }
            },
        );

        info!(target: "reth::cli", "ExEx Manager started");

        Ok(Some(exex_manager_handle))
    }
}

impl<Node: FullNodeComponents> Debug for ExExLauncher<Node> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExExLauncher")
            .field("head", &self.head)
            .field("extensions", &self.extensions.iter().map(|(id, _)| id).collect::<Vec<_>>())
            .field("components", &"...")
            .field("config_container", &self.config_container)
            .field("wal_blocks_warning", &self.wal_blocks_warning)
            .finish()
    }
}


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;

use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;

use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(
                    "exex",
                    async move {
                        info!(target: "reth::cli", "ExEx started");
                        match exex.await {
                            Ok(_) => panic!("ExEx {id} finished. ExExes should run indefinitely"),
                            Err(err) => panic!("ExEx {id} crashed: {err}"),
                        }
                    }
                    .instrument(span),
                );

                Ok::<(), eyre::Error>(())
            });
        }

        future::try_join_all(exexes).await?;

        // spawn exex manager
        debug!(target: "reth::cli", "spawning exex manager");
        let exex_manager = ExExManager::new(
            components.provider().clone(),
            exex_handles,
            capacity,
            exex_wal,
            components.provider().finalized_block_stream(),
        )
        .with_wal_blocks_warning(wal_blocks_warning);
        let exex_manager_handle = exex_manager.handle();
        components.task_executor().spawn_critical_task("exex manager", async move {
            exex_manager.await.expect("exex manager crashed");
        });

        // send notifications from the blockchain tree to exex manager
        let mut canon_state_notifications = components.provider().subscribe_to_canonical_state();
        let mut handle = exex_manager_handle.clone();
        components.task_executor().spawn_critical_task(
            "exex manager blockchain tree notifications",
            async move {
                while let Ok(notification) = canon_state_notifications.recv().await {
                    handle
                        .send_async(ExExNotificationSource::BlockchainTree, notification.into())
                        .await
                        .expect("blockchain tree notification could not be sent to exex manager");
                }
            },
        );

        info!(target: "reth::cli", "ExEx Manager started");

        Ok(Some(exex_manager_handle))
    }
}

impl<Node: FullNodeComponents> Debug for ExExLauncher<Node> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExExLauncher")
            .field("head", &self.head)
            .field("extensions", &self.extensions.iter().map(|(id, _)| id).collect::<Vec<_>>())
            .field("components", &"...")
            .field("config_container", &self.config_container)
            .field("wal_blocks_warning", &self.wal_blocks_warning)
            .finish()
    }
}


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;

use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};

use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target:


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};

use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};

use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};

use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(
                    "exex",
                    async move {
                        info!(target: "reth::cli", "ExEx started");
                        match exex.await {
                            Ok(_) => panic!("ExEx {id} finished. ExExes should run indefinitely"),
                            Err(err) => panic!("ExEx {id} crashed: {err}"),
                        }
                    }
                    .instrument(span),
                );

                Ok::<(), eyre::Error>(())
            });
        }

        future::try_join_all(exexes).await?;

        // spawn exex manager
        debug!(target: "reth::cli", "spawning exex manager");
        let exex_manager = ExExManager::new(
            components.provider().clone(),
            exex_handles,
            capacity,
            exex_wal,
            components.provider().finalized_block_stream(),
        )
        .with_wal_blocks_warning(wal_blocks_warning);
        let exex_manager_handle = exex_manager.handle();
        components.task_executor().spawn_critical_task("exex manager", async move {
            exex_manager.await.expect("exex manager crashed");
        });

        // send notifications from the blockchain tree to exex manager
        let mut canon_state_notifications = components.provider().subscribe_to_canonical_state();
        let mut handle = exex_manager_handle.clone();
        components.task_executor().spawn_critical_task(
            "exex manager blockchain tree notifications",
            async move {
                while let Ok(notification) = canon_state_notifications.recv().await {
                    handle
                        .send_async(ExExNotificationSource::BlockchainTree, notification.into())
                        .await
                        .expect("blockchain tree notification could not be sent to exex manager");
                }
            },
        );

        info!(target: "reth::cli", "ExEx Manager started");

        Ok(Some(exex_manager_handle))
    }
}

impl<Node: FullNodeComponents> Debug for ExExLauncher<Node> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExExLauncher")
            .field("head", &self.head)
            .field("extensions", &self.extensions.iter().map(|(id, _)| id).collect::<Vec<_>>())
            .field("components", &"...")
            .field("config_container", &self.config_container)
            .field("wal_blocks_warning", &self.wal_blocks_warning)
            .finish()
    }
}


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;

use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};

use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(
                    "exex",
                    async move {
                        info!(target: "reth::cli", "ExEx started");
                        match exex.await {
                            Ok(_) => panic!("ExEx {id} finished. ExExes should run indefinitely"),
                            Err(err) => panic!("ExEx {id} crashed: {err}"),
                        }
                    }
                    .instrument(span),
                );

                Ok::<(), eyre::Error>(())
            });
        }

        future::try_join_all(exexes).await?;

        // spawn exex manager
        debug!(target: "reth::cli", "spawning exex manager");
        let exex_manager = ExExManager::new(
            components.provider().clone(),
            exex_handles,
            capacity,
            exex_wal,
            components.provider().finalized_block_stream(),
        )
        .with_wal_blocks_warning(wal_blocks_warning);
        let exex_manager_handle = exex_manager.handle();
        components.task_executor().spawn_critical_task("exex manager", async move {
            exex_manager.await.expect("exex manager crashed");
        });

        // send notifications from the blockchain tree to exex manager
        let mut canon_state_notifications = components.provider().subscribe_to_canonical_state();
        let mut handle = exex_manager_handle.clone();
        components.task_executor().spawn_critical_task(
            "exex manager blockchain tree notifications",
            async move {
                while let Ok(notification) = canon_state_notifications.recv().await {
                    handle
                        .send_async(ExExNotificationSource::BlockchainTree, notification.into())
                        .await
                        .expect("blockchain tree notification could not be sent to exex manager");
                }
            },
        );

        info!(target: "reth::cli", "ExEx Manager started");

        Ok(Some(exex_manager_handle))
    }
}

impl<Node: FullNodeComponents> Debug for ExExLauncher<Node> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExExLauncher")
            .field("head", &self.head)
            .field("extensions", &self.extensions.iter().map(|(id, _)| id).collect::<Vec<_>>())
            .field("components", &"...")
            .field("config_container", &self.config_container)
            .field("wal_blocks_warning", &self.wal_blocks_warning)
            .finish()
    }
}


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(
                    "exex",
                    async move {
                        info!(target: "reth::cli", "ExEx started");
                        match exex.await {
                            Ok(_) => panic!("ExEx {id} finished. ExExes should run indefinitely"),
                            Err(err) => panic!("ExEx {id} crashed: {err}"),
                        }
                    }
                    .instrument(span),
                );

                Ok::<(), eyre::Error>(())
            });
        }

        future::try_join_all(exexes).await?;

        // spawn exex manager
        debug!(target: "reth::cli", "spawning exex manager");
        let exex_manager = ExExManager::new(
            components.provider().clone(),
            exex_handles,
            capacity,
            exex_wal,
            components.provider().finalized_block_stream(),
        )
        .with_wal_blocks_warning(wal_blocks_warning);
        let exex_manager_handle = exex_manager.handle();
        components.task_executor().spawn_critical_task("exex manager", async move {
            exex_manager.await.expect("exex manager crashed");
        });

        // send notifications from the blockchain tree to exex manager
        let mut canon_state_notifications = components.provider().subscribe_to_canonical_state();
        let mut handle = exex_manager_handle.clone();
        components.task_executor().spawn_critical_task(
            "exex manager blockchain tree notifications",
            async move {
                while let Ok(notification) = canon_state_notifications.recv().await {
                    handle
                        .send_async(ExExNotificationSource::BlockchainTree, notification.into())
                        .await
                        .expect("blockchain tree notification could not be sent to exex manager");
                }
            },
        );

        info!(target: "reth::cli", "ExEx Manager started");

        Ok(Some(exex_manager_handle))
    }
}

impl<Node: FullNodeComponents> Debug for ExExLauncher<Node> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExExLauncher")
            .field("head", &self.head)
            .field("extensions", &self.extensions.iter().map(|(id, _)| id).collect::<Vec<_>>())
            .field("components", &"...")
            .field("config_container", &self.config_container)
            .field("wal_blocks_warning", &self.wal_blocks_warning)
            .finish()
    }
}


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(
                    "exex",
                    async move {
                        info!(target: "reth::cli", "ExEx started");
                        match exex.await {
                            Ok(_) => panic!("ExEx {id} finished. ExExes should run indefinitely"),
                            Err(err) => panic!("ExEx {id} crashed: {err}"),
                        }
                    }
                    .instrument(span),
                );

                Ok::<(), eyre::Error>(())
            });
        }

        future::try_join_all(exexes).await?;

        // spawn exex manager
        debug!(target: "reth::cli", "spawning exex manager");
        let exex_manager = ExExManager::new(
            components.provider().clone(),
            exex_handles,
            capacity,
            exex_wal,
            components.provider().finalized_block_stream(),
        )
        .with_wal_blocks_warning(wal_blocks_warning);
        let exex_manager_handle = exex_manager.handle();
        components.task_executor().spawn_critical_task("exex manager", async move {
            exex_manager.await.expect("exex manager crashed");
        });

        // send notifications from the blockchain tree to exex manager
        let mut canon_state_notifications = components.provider().subscribe_to_canonical_state();
        let mut handle = exex_manager_handle.clone();
        components.task_executor().spawn_critical_task(
            "exex manager blockchain tree notifications",
            async move {
                while let Ok(notification) = canon_state_notifications.recv().await {
                    handle
                        .send_async(ExExNotificationSource::BlockchainTree, notification.into())
                        .await
                        .expect("blockchain tree notification could not be sent to exex manager");
                }
            },
        );

        info!(target: "reth::cli", "ExEx Manager started");

        Ok(Some(exex_manager_handle))
    }
}

impl<Node: FullNodeComponents> Debug for ExExLauncher<Node> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExExLauncher")
            .field("head", &self.head)
            .field("extensions", &self.extensions.iter().map(|(id, _)| id).collect::<Vec<_>>())
            .field("components", &"...")
            .field("config_container", &self.config_container)
            .field("wal_blocks_warning", &self.wal_blocks_warning)
            .finish()
    }
}


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.

    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self {
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }

    /// Sets the threshold for the number of blocks in the WAL before emitting a warning.
    ///
    /// For L2 chains with faster block times, this value should be increased proportionally
    /// to avoid excessive warnings. For example, a chain with 2-second block times might use
    /// a value 6x higher than the default (768 instead of 128).
    pub const fn with_wal_blocks_warning(mut self, threshold: usize) -> Self {
        self.wal_blocks_warning = threshold;
        self
    }

    /// Sets the max notification buffer capacity for the [`ExExManager`].
    pub const fn with_capacity(mut self, capacity: usize) -> Self {
        self.capacity = capacity;
        self
    }

    /// Launches all execution extensions.
    ///
    /// Spawns all extensions and returns the handle to the exex manager if any extensions are
    /// installed.
    pub async fn launch(
        self,
    ) -> eyre::Result<Option<ExExManagerHandle<PrimitivesTy<Node::Types>>>> {
        let Self { head, extensions, components, config_container, wal_blocks_warning, capacity } =
            self;
        let head = BlockNumHash::new(head.number, head.hash);

        if extensions.is_empty() {
            // nothing to launch
            return Ok(None)
        }

        info!(target: "reth::cli", "Loading ExEx Write-Ahead Log...");
        let exex_wal = Wal::new(
            config_container
                .config
                .datadir
                .clone()
                .resolve_datadir(config_container.config.chain.chain())
                .exex_wal(),
        )?;

        let mut exex_handles = Vec::with_capacity(extensions.len());
        let mut exexes = Vec::with_capacity(extensions.len());

        for (id, exex) in extensions {
            // create a new exex handle
            let (handle, events, notifications) = ExExHandle::new(
                id.clone(),
                head,
                components.provider().clone(),
                components.evm_config().clone(),
                exex_wal.handle(),
            );
            exex_handles.push(handle);

            // create the launch context for the exex
            let context = ExExContext {
                head,
                config: config_container.config.clone(),
                reth_config: config_container.toml_config.clone(),
                components: components.clone(),
                events,
                notifications,
            };

            let executor = components.task_executor().clone();
            exexes.push(async move {
                debug!(target: "reth::cli", id, "spawning exex");
                let span = reth_tracing::tracing::info_span!("exex", id);

                // init the exex
                let exex = exex.launch(context).instrument(span.clone()).await?;

                // spawn it as a crit task
                executor.spawn_critical_task(
                    "exex",
                    async move {
                        info!(target: "reth::cli", "ExEx started");
                        match exex.await {
                            Ok(_) => panic!("ExEx {id} finished. ExExes should run indefinitely"),
                            Err(err) => panic!("ExEx {id} crashed: {err}"),
                        }
                    }
                    .instrument(span),
                );

                Ok::<(), eyre::Error>(())
            });
        }

        future::try_join_all(exexes).await?;

        // spawn exex manager
        debug!(target: "reth::cli", "spawning exex manager");
        let exex_manager = ExExManager::new(
            components.provider().clone(),
            exex_handles,
            capacity,
            exex_wal,
            components.provider().finalized_block_stream(),
        )
        .with_wal_blocks_warning(wal_blocks_warning);
        let exex_manager_handle = exex_manager.handle();
        components.task_executor().spawn_critical_task("exex manager", async move {
            exex_manager.await.expect("exex manager crashed");
        });

        // send notifications from the blockchain tree to exex manager
        let mut canon_state_notifications = components.provider().subscribe_to_canonical_state();
        let mut handle = exex_manager_handle.clone();
        components.task_executor().spawn_critical_task(
            "exex manager blockchain tree notifications",
            async move {
                while let Ok(notification) = canon_state_notifications.recv().await {
                    handle
                        .send_async(ExExNotificationSource::BlockchainTree, notification.into())
                        .await
                        .expect("blockchain tree notification could not be sent to exex manager");
                }
            },
        );

        info!(target: "reth::cli", "ExEx Manager started");

        Ok(Some(exex_manager_handle))
    }
}

impl<Node: FullNodeComponents> Debug for ExExLauncher<Node> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExExLauncher")
            .field("head", &self.head)
            .field("extensions", &self.extensions.iter().map(|(id, _)| id).collect::<Vec<_>>())
            .field("components", &"...")
            .field("config_container", &self.config_container)
            .field("wal_blocks_warning", &self.wal_blocks_warning)
            .finish()
    }
}


//! Support for launching execution extensions.

use alloy_eips::{eip2124::Head, BlockNumHash};
use futures::future;
use reth_chain_state::ForkChoiceSubscriptions;
use reth_chainspec::EthChainSpec;
use reth_exex::{
    ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal,
    DEFAULT_EXEX_MANAGER_CAPACITY, DEFAULT_WAL_BLOCKS_WARNING,
};
use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use std::{fmt, fmt::Debug};
use tracing::Instrument;

use crate::{common::WithConfigs, exex::BoxedLaunchExEx};

/// Can launch execution extensions.
pub struct ExExLauncher<Node: FullNodeComponents> {
    head: Head,
    extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
    components: Node,
    config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    /// The threshold for the number of blocks in the WAL before emitting a warning.
    wal_blocks_warning: usize,
    /// The max notification buffer capacity for the ExEx manager.
    capacity: usize,
}

impl<Node: FullNodeComponents + Clone> ExExLauncher<Node> {
    /// Create a new `ExExLauncher` with the given extensions.
    pub const fn new(
        head: Head,
        components: Node,
        extensions: Vec<(String, Box<dyn BoxedLaunchExEx<Node>>)>,
        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
    ) -> Self

{
        Self {
            head,
            extensions,
            components,
            config_container,
            wal_blocks_warning: DEFAULT_WAL_BLOCKS_WARNING,
            capacity: DEFAULT_EXEX_MANAGER_CAPACITY,
        }
    }