From c6555fd8417172db5c08a62d9d9731d59f46383b Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 25 Feb 2026 15:34:19 +0000 Subject: [PATCH] feat: Add `chain_tip` method to `Requester` Closes #537 In more limited settings like the bindings, it's non-trivial to parse the existing `Info` messages. Adds a poll-based way to query the chain tip. --- src/client.rs | 16 +++++++++++++++- src/messages.rs | 2 ++ src/node.rs | 11 +++++++++++ tests/core.rs | 2 ++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 7eda3bf5..b87bfaaa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -6,7 +6,7 @@ use tokio::sync::oneshot; use crate::chain::block_subsidy; use crate::messages::ClientRequest; -use crate::{Event, Info, TrustedPeer, Warning}; +use crate::{Event, HeaderCheckpoint, Info, TrustedPeer, Warning}; use super::{error::ClientError, messages::ClientMessage}; use super::{error::FetchBlockError, IndexedBlock}; @@ -188,6 +188,20 @@ impl Requester { .map_err(|_| ClientError::SendError) } + /// The height and hash of the block in the chain of most work. + /// + /// # Errors + /// + /// If the node has stopped running. + pub async fn chain_tip(&self) -> Result { + let (tx, rx) = tokio::sync::oneshot::channel::(); + let request = ClientRequest::new((), tx); + self.ntx + .send(ClientMessage::BestBlock(request)) + .map_err(|_| ClientError::SendError)?; + rx.await.map_err(|_| ClientError::RecvError) + } + /// Check if the node is running. pub fn is_running(&self) -> bool { self.ntx.send(ClientMessage::NoOp).is_ok() diff --git a/src/messages.rs b/src/messages.rs index 36dd3b53..ec53868f 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -145,6 +145,8 @@ pub(crate) enum ClientMessage { Rescan, /// Explicitly request a block from the node. GetBlock(ClientRequest>), + /// Get the chain tip. + BestBlock(ClientRequest<(), HeaderCheckpoint>), /// Add another known peer to connect to. AddPeer(TrustedPeer), /// Request the broadcast minimum fee rate. diff --git a/src/node.rs b/src/node.rs index 9258770e..2a70cc66 100644 --- a/src/node.rs +++ b/src/node.rs @@ -236,6 +236,17 @@ impl Node { self.block_queue.add(request); } }, + ClientMessage::BestBlock(request) => { + let (_, oneshot) = request.into_values(); + let block_tree = &self.chain.header_chain; + let hash = block_tree.tip_hash(); + let height = block_tree.height(); + let checkpoint = HeaderCheckpoint::new(height, hash); + let send_result = oneshot.send(checkpoint); + if send_result.is_err() { + self.dialog.send_warning(Warning::ChannelDropped); + }; + }, ClientMessage::AddPeer(peer) => { self.peer_map.add_trusted_peer(peer); }, diff --git a/tests/core.rs b/tests/core.rs index 00dafacf..525c4ef5 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -248,6 +248,8 @@ async fn various_client_methods() { tokio::task::spawn(async move { print_logs(info_rx, warn_rx).await }); sync_assert(&best, &mut channel).await; let _ = requester.broadcast_min_feerate().await.unwrap(); + let cp = requester.chain_tip().await.unwrap(); + assert_eq!(cp.hash, best); assert!(requester.is_running()); requester.shutdown().unwrap(); rpc.stop().unwrap();