zebra_chain/
chain_tip.rs

1//! Zebra interfaces for access to chain tip information.
2
3use std::{future, sync::Arc};
4
5use chrono::{DateTime, Utc};
6
7use crate::{block, parameters::Network, transaction, BoxError};
8
9mod network_chain_tip_height_estimator;
10
11#[cfg(any(test, feature = "proptest-impl"))]
12pub mod mock;
13#[cfg(test)]
14mod tests;
15
16pub use network_chain_tip_height_estimator::NetworkChainTipHeightEstimator;
17
18/// An interface for querying the chain tip.
19///
20/// This trait helps avoid dependencies between:
21/// * `zebra-chain` and `tokio`
22/// * `zebra-network` and `zebra-state`
23pub trait ChainTip {
24    /// Returns the height of the best chain tip.
25    ///
26    /// Does not mark the best tip as seen.
27    fn best_tip_height(&self) -> Option<block::Height>;
28
29    /// Returns the block hash of the best chain tip.
30    ///
31    /// Does not mark the best tip as seen.
32    fn best_tip_hash(&self) -> Option<block::Hash>;
33
34    /// Returns the height and the hash of the best chain tip.
35    ///
36    /// Does not mark the best tip as seen.
37    fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)>;
38
39    /// Returns the block time of the best chain tip.
40    ///
41    /// Does not mark the best tip as seen.
42    fn best_tip_block_time(&self) -> Option<DateTime<Utc>>;
43
44    /// Returns the height and the block time of the best chain tip.
45    /// Returning both values at the same time guarantees that they refer to the same chain tip.
46    ///
47    /// Does not mark the best tip as seen.
48    fn best_tip_height_and_block_time(&self) -> Option<(block::Height, DateTime<Utc>)>;
49
50    /// Returns the mined transaction IDs of the transactions in the best chain tip block.
51    ///
52    /// All transactions with these mined IDs should be rejected from the mempool,
53    /// even if their authorizing data is different.
54    ///
55    /// Does not mark the best tip as seen.
56    fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]>;
57
58    /// A future that returns when the best chain tip changes.
59    /// Can return immediately if the latest value in this [`ChainTip`] has not been seen yet.
60    ///
61    /// Marks the best tip as seen.
62    ///
63    /// Returns an error if Zebra is shutting down, or the state has permanently failed.
64    ///
65    /// See [`tokio::watch::Receiver::changed()`](https://docs.rs/tokio/latest/tokio/sync/watch/struct.Receiver.html#method.changed) for details.
66    fn best_tip_changed(
67        &mut self,
68    ) -> impl std::future::Future<Output = Result<(), BoxError>> + Send;
69
70    /// Mark the current best tip as seen.
71    ///
72    /// Later calls to [`ChainTip::best_tip_changed()`] will wait for the next change
73    /// before returning.
74    fn mark_best_tip_seen(&mut self);
75
76    // Provided methods
77    //
78    /// Return an estimate of the network chain tip's height.
79    ///
80    /// The estimate is calculated based on the current local time, the block time of the best tip
81    /// and the height of the best tip.
82    fn estimate_network_chain_tip_height(
83        &self,
84        network: &Network,
85        now: DateTime<Utc>,
86    ) -> Option<block::Height> {
87        let (current_height, current_block_time) = self.best_tip_height_and_block_time()?;
88
89        let estimator =
90            NetworkChainTipHeightEstimator::new(current_block_time, current_height, network);
91
92        Some(estimator.estimate_height_at(now))
93    }
94
95    /// Return an estimate of how many blocks there are ahead of Zebra's best chain tip until the
96    /// network chain tip, and Zebra's best chain tip height.
97    ///
98    /// The first element in the returned tuple is the estimate.
99    /// The second element in the returned tuple is the current best chain tip.
100    ///
101    /// The estimate is calculated based on the current local time, the block time of the best tip
102    /// and the height of the best tip.
103    ///
104    /// This estimate may be negative if the current local time is behind the chain tip block's
105    /// timestamp.
106    ///
107    /// Returns `None` if the state is empty.
108    fn estimate_distance_to_network_chain_tip(
109        &self,
110        network: &Network,
111    ) -> Option<(block::HeightDiff, block::Height)> {
112        let (current_height, current_block_time) = self.best_tip_height_and_block_time()?;
113
114        let estimator =
115            NetworkChainTipHeightEstimator::new(current_block_time, current_height, network);
116
117        let distance_to_tip = estimator.estimate_height_at(Utc::now()) - current_height;
118
119        Some((distance_to_tip, current_height))
120    }
121}
122
123/// A chain tip that is always empty and never changes.
124///
125/// Used in production for isolated network connections,
126/// and as a mock chain tip in tests.
127#[derive(Copy, Clone, Debug, PartialEq, Eq)]
128pub struct NoChainTip;
129
130impl ChainTip for NoChainTip {
131    fn best_tip_height(&self) -> Option<block::Height> {
132        None
133    }
134
135    fn best_tip_hash(&self) -> Option<block::Hash> {
136        None
137    }
138
139    fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)> {
140        None
141    }
142
143    fn best_tip_block_time(&self) -> Option<DateTime<Utc>> {
144        None
145    }
146
147    fn best_tip_height_and_block_time(&self) -> Option<(block::Height, DateTime<Utc>)> {
148        None
149    }
150
151    fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]> {
152        Arc::new([])
153    }
154
155    /// The [`NoChainTip`] best tip never changes, so this never returns.
156    async fn best_tip_changed(&mut self) -> Result<(), BoxError> {
157        future::pending().await
158    }
159
160    /// The [`NoChainTip`] best tip never changes, so this does nothing.
161    fn mark_best_tip_seen(&mut self) {}
162}