From 875304f891bfa8186ec7ec4bee4b6f5eccbb2cd3 Mon Sep 17 00:00:00 2001 From: sprites0 <199826320+sprites0@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:53:47 +0000 Subject: [PATCH] feat: Add debug CLI flag to enforce latest blocks (--debug-cutoff-height) This is useful when syncing to specific testnet blocks --- src/main.rs | 3 ++- src/node/cli.rs | 6 ++++++ src/node/mod.rs | 12 +++++++++++- src/node/network/mod.rs | 4 ++++ src/pseudo_peer/mod.rs | 2 ++ src/pseudo_peer/network.rs | 17 +++++++++++++++-- src/pseudo_peer/service.rs | 15 +++++++++++---- 7 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0062a49cb..72910f4ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,7 +40,8 @@ fn main() -> eyre::Result<()> { ext: HlNodeArgs| async move { let default_upstream_rpc_url = builder.config().chain.official_rpc_url(); - let (node, engine_handle_tx) = HlNode::new(ext.block_source_args.parse().await?); + let (node, engine_handle_tx) = + HlNode::new(ext.block_source_args.parse().await?, ext.debug_cutoff_height); let NodeHandle { node, node_exit_future: exit_future } = builder .node(node) .extend_rpc_modules(move |mut ctx| { diff --git a/src/node/cli.rs b/src/node/cli.rs index c29a3d92c..aa51d9740 100644 --- a/src/node/cli.rs +++ b/src/node/cli.rs @@ -35,6 +35,12 @@ pub struct HlNodeArgs { #[command(flatten)] pub block_source_args: BlockSourceArgs, + /// Debug cutoff height. + /// + /// This option is used to cut off the block import at a specific height. + #[arg(long, env = "DEBUG_CUTOFF_HEIGHT")] + pub debug_cutoff_height: Option, + /// Upstream RPC URL to forward incoming transactions. /// /// Default to Hyperliquid's RPC URL when not provided (https://rpc.hyperliquid.xyz/evm). diff --git a/src/node/mod.rs b/src/node/mod.rs index 63dd730a3..7b6aeee04 100644 --- a/src/node/mod.rs +++ b/src/node/mod.rs @@ -49,14 +49,23 @@ pub type HlNodeAddOns = pub struct HlNode { engine_handle_rx: Arc>>>>, block_source_config: BlockSourceConfig, + debug_cutoff_height: Option, } impl HlNode { pub fn new( block_source_config: BlockSourceConfig, + debug_cutoff_height: Option, ) -> (Self, oneshot::Sender>) { let (tx, rx) = oneshot::channel(); - (Self { engine_handle_rx: Arc::new(Mutex::new(Some(rx))), block_source_config }, tx) + ( + Self { + engine_handle_rx: Arc::new(Mutex::new(Some(rx))), + block_source_config, + debug_cutoff_height, + }, + tx, + ) } } @@ -84,6 +93,7 @@ impl HlNode { .network(HlNetworkBuilder { engine_handle_rx: self.engine_handle_rx.clone(), block_source_config: self.block_source_config.clone(), + debug_cutoff_height: self.debug_cutoff_height, }) .consensus(HlConsensusBuilder::default()) } diff --git a/src/node/network/mod.rs b/src/node/network/mod.rs index 81d35fff1..12b23d525 100644 --- a/src/node/network/mod.rs +++ b/src/node/network/mod.rs @@ -142,6 +142,8 @@ pub struct HlNetworkBuilder { Arc>>>>, pub(crate) block_source_config: BlockSourceConfig, + + pub(crate) debug_cutoff_height: Option, } impl HlNetworkBuilder { @@ -203,6 +205,7 @@ where pool: Pool, ) -> eyre::Result { let block_source_config = self.block_source_config.clone(); + let debug_cutoff_height = self.debug_cutoff_height; let handle = ctx.start_network(NetworkManager::builder(self.network_config(ctx)?).await?, pool); let local_node_record = handle.local_node_record(); @@ -223,6 +226,7 @@ where block_source_config .create_cached_block_source((*chain_spec).clone(), next_block_number) .await, + debug_cutoff_height, ) .await .unwrap(); diff --git a/src/pseudo_peer/mod.rs b/src/pseudo_peer/mod.rs index a6f354434..1522958f2 100644 --- a/src/pseudo_peer/mod.rs +++ b/src/pseudo_peer/mod.rs @@ -37,6 +37,7 @@ pub async fn start_pseudo_peer( chain_spec: Arc, destination_peer: String, block_source: BlockSourceBoxed, + debug_cutoff_height: Option, ) -> eyre::Result<()> { let blockhash_cache = new_blockhash_cache(); @@ -46,6 +47,7 @@ pub async fn start_pseudo_peer( destination_peer, block_source.clone(), blockhash_cache.clone(), + debug_cutoff_height, ) .await?; diff --git a/src/pseudo_peer/network.rs b/src/pseudo_peer/network.rs index 9fcd50dfb..336326093 100644 --- a/src/pseudo_peer/network.rs +++ b/src/pseudo_peer/network.rs @@ -20,6 +20,7 @@ pub struct NetworkBuilder { discovery_port: u16, listener_port: u16, chain_spec: HlChainSpec, + debug_cutoff_height: Option, } impl Default for NetworkBuilder { @@ -31,6 +32,7 @@ impl Default for NetworkBuilder { discovery_port: 0, listener_port: 0, chain_spec: HlChainSpec::default(), + debug_cutoff_height: None, } } } @@ -46,6 +48,11 @@ impl NetworkBuilder { self } + pub fn with_debug_cutoff_height(mut self, debug_cutoff_height: Option) -> Self { + self.debug_cutoff_height = debug_cutoff_height; + self + } + pub async fn build( self, block_source: Arc>, @@ -58,8 +65,12 @@ impl NetworkBuilder { .listener_addr(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), self.listener_port)); let chain_id = self.chain_spec.inner.chain().id(); - let (block_poller, start_tx) = - BlockPoller::new_suspended(chain_id, block_source, blockhash_cache); + let (block_poller, start_tx) = BlockPoller::new_suspended( + chain_id, + block_source, + blockhash_cache, + self.debug_cutoff_height, + ); let config = builder.block_import(Box::new(block_poller)).build(Arc::new(NoopProvider::< HlChainSpec, HlPrimitives, @@ -77,10 +88,12 @@ pub async fn create_network_manager( destination_peer: String, block_source: Arc>, blockhash_cache: BlockHashCache, + debug_cutoff_height: Option, ) -> eyre::Result<(NetworkManager, mpsc::Sender<()>)> { NetworkBuilder::default() .with_boot_nodes(vec![TrustedPeer::from_str(&destination_peer).unwrap()]) .with_chain_spec(chain_spec) + .with_debug_cutoff_height(debug_cutoff_height) .build::(block_source, blockhash_cache) .await } diff --git a/src/pseudo_peer/service.rs b/src/pseudo_peer/service.rs index 5639e899a..c201f2145 100644 --- a/src/pseudo_peer/service.rs +++ b/src/pseudo_peer/service.rs @@ -52,12 +52,12 @@ impl BlockPoller { chain_id: u64, block_source: BS, blockhash_cache: BlockHashCache, + debug_cutoff_height: Option, ) -> (Self, mpsc::Sender<()>) { let block_source = Arc::new(block_source); let (start_tx, start_rx) = mpsc::channel(1); let (block_tx, block_rx) = mpsc::channel(100); - let block_tx_clone = block_tx.clone(); - let task = tokio::spawn(Self::task(start_rx, block_source, block_tx_clone)); + let task = tokio::spawn(Self::task(start_rx, block_source, block_tx, debug_cutoff_height)); (Self { chain_id, block_rx, task, blockhash_cache: blockhash_cache.clone() }, start_tx) } @@ -69,7 +69,8 @@ impl BlockPoller { async fn task( mut start_rx: mpsc::Receiver<()>, block_source: Arc, - block_tx_clone: mpsc::Sender<(u64, BlockAndReceipts)>, + block_tx: mpsc::Sender<(u64, BlockAndReceipts)>, + debug_cutoff_height: Option, ) -> eyre::Result<()> { start_rx.recv().await.ok_or(eyre::eyre!("Failed to receive start signal"))?; info!("Starting block poller"); @@ -80,10 +81,16 @@ impl BlockPoller { .await .ok_or(eyre::eyre!("Failed to find latest block number"))?; + if let Some(debug_cutoff_height) = debug_cutoff_height { + if next_block_number > debug_cutoff_height { + next_block_number = debug_cutoff_height; + } + } + loop { match block_source.collect_block(next_block_number).await { Ok(block) => { - block_tx_clone.send((next_block_number, block)).await?; + block_tx.send((next_block_number, block)).await?; next_block_number += 1; } Err(_) => tokio::time::sleep(polling_interval).await,