2021-09-01 15:31:16 -07:00
|
|
|
//! Zebra interfaces for access to chain tip information.
|
2021-08-26 18:34:33 -07:00
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
use std::{future, sync::Arc};
|
2021-08-30 11:42:07 -07:00
|
|
|
|
2022-02-10 17:27:02 -08:00
|
|
|
use chrono::{DateTime, Utc};
|
2022-12-15 07:33:00 -08:00
|
|
|
use futures::{future::BoxFuture, Future, FutureExt};
|
2022-02-10 17:27:02 -08:00
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
use crate::{block, parameters::Network, transaction, BoxError};
|
2021-08-26 18:34:33 -07:00
|
|
|
|
2022-12-07 17:06:11 -08:00
|
|
|
mod network_chain_tip_height_estimator;
|
|
|
|
|
2022-01-11 09:11:35 -08:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
pub mod mock;
|
2022-02-10 17:27:02 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
2022-01-11 09:11:35 -08:00
|
|
|
|
2022-12-07 17:06:11 -08:00
|
|
|
use network_chain_tip_height_estimator::NetworkChainTipHeightEstimator;
|
|
|
|
|
2021-08-26 18:34:33 -07:00
|
|
|
/// An interface for querying the chain tip.
|
|
|
|
///
|
|
|
|
/// This trait helps avoid dependencies between:
|
2022-12-15 07:33:00 -08:00
|
|
|
/// * `zebra-chain` and `tokio`
|
|
|
|
/// * `zebra-network` and `zebra-state`
|
2021-08-26 18:34:33 -07:00
|
|
|
pub trait ChainTip {
|
2022-12-15 07:33:00 -08:00
|
|
|
/// Returns the height of the best chain tip.
|
|
|
|
///
|
|
|
|
/// Does not mark the best tip as seen.
|
2021-08-26 18:34:33 -07:00
|
|
|
fn best_tip_height(&self) -> Option<block::Height>;
|
2021-08-27 12:18:47 -07:00
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
/// Returns the block hash of the best chain tip.
|
|
|
|
///
|
|
|
|
/// Does not mark the best tip as seen.
|
2021-08-27 12:18:47 -07:00
|
|
|
fn best_tip_hash(&self) -> Option<block::Hash>;
|
2021-08-29 19:38:41 -07:00
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
/// Returns the height and the hash of the best chain tip.
|
|
|
|
///
|
|
|
|
/// Does not mark the best tip as seen.
|
2022-03-25 05:25:31 -07:00
|
|
|
fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)>;
|
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
/// Returns the block time of the best chain tip.
|
|
|
|
///
|
|
|
|
/// Does not mark the best tip as seen.
|
2022-02-10 17:27:02 -08:00
|
|
|
fn best_tip_block_time(&self) -> Option<DateTime<Utc>>;
|
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
/// Returns the height and the block time of the best chain tip.
|
2022-02-10 17:27:02 -08:00
|
|
|
/// Returning both values at the same time guarantees that they refer to the same chain tip.
|
2022-12-15 07:33:00 -08:00
|
|
|
///
|
|
|
|
/// Does not mark the best tip as seen.
|
2022-02-10 17:27:02 -08:00
|
|
|
fn best_tip_height_and_block_time(&self) -> Option<(block::Height, DateTime<Utc>)>;
|
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
/// Returns the mined transaction IDs of the transactions in the best chain tip block.
|
2021-08-29 19:38:41 -07:00
|
|
|
///
|
|
|
|
/// All transactions with these mined IDs should be rejected from the mempool,
|
|
|
|
/// even if their authorizing data is different.
|
2022-12-15 07:33:00 -08:00
|
|
|
///
|
|
|
|
/// Does not mark the best tip as seen.
|
2021-08-30 11:42:07 -07:00
|
|
|
fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]>;
|
2022-02-10 17:27:02 -08:00
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
/// A future that returns when the best chain tip changes.
|
|
|
|
/// Can return immediately if the latest value in this [`ChainTip`] has not been seen yet.
|
|
|
|
///
|
|
|
|
/// Marks the best tip as seen.
|
|
|
|
///
|
|
|
|
/// Returns an error if Zebra is shutting down, or the state has permanently failed.
|
|
|
|
///
|
|
|
|
/// See [`tokio::watch::Receiver::changed()`](https://docs.rs/tokio/latest/tokio/sync/watch/struct.Receiver.html#method.changed) for details.
|
|
|
|
//
|
|
|
|
// TODO:
|
|
|
|
// Use async_fn_in_trait or return_position_impl_trait_in_trait when one of them stabilises:
|
|
|
|
// https://github.com/rust-lang/rust/issues/91611
|
|
|
|
fn best_tip_changed(&mut self) -> BestTipChanged;
|
|
|
|
|
|
|
|
/// Mark the current best tip as seen.
|
|
|
|
///
|
|
|
|
/// Later calls to [`ChainTip::best_tip_changed()`] will wait for the next change
|
|
|
|
/// before returning.
|
|
|
|
fn mark_best_tip_seen(&mut self);
|
|
|
|
|
|
|
|
// Provided methods
|
|
|
|
//
|
2022-02-10 17:27:02 -08:00
|
|
|
/// Return an estimate of the network chain tip's height.
|
|
|
|
///
|
|
|
|
/// The estimate is calculated based on the current local time, the block time of the best tip
|
|
|
|
/// and the height of the best tip.
|
|
|
|
fn estimate_network_chain_tip_height(
|
|
|
|
&self,
|
|
|
|
network: Network,
|
|
|
|
now: DateTime<Utc>,
|
|
|
|
) -> Option<block::Height> {
|
|
|
|
let (current_height, current_block_time) = self.best_tip_height_and_block_time()?;
|
|
|
|
|
|
|
|
let estimator =
|
|
|
|
NetworkChainTipHeightEstimator::new(current_block_time, current_height, network);
|
|
|
|
|
|
|
|
Some(estimator.estimate_height_at(now))
|
|
|
|
}
|
2022-11-17 16:12:10 -08:00
|
|
|
|
2023-03-29 16:06:31 -07:00
|
|
|
/// Return an estimate of how many blocks there are ahead of Zebra's best chain tip until the
|
|
|
|
/// network chain tip, and Zebra's best chain tip height.
|
|
|
|
///
|
|
|
|
/// The first element in the returned tuple is the estimate.
|
|
|
|
/// The second element in the returned tuple is the current best chain tip.
|
2022-11-17 16:12:10 -08:00
|
|
|
///
|
|
|
|
/// The estimate is calculated based on the current local time, the block time of the best tip
|
|
|
|
/// and the height of the best tip.
|
|
|
|
///
|
2023-03-29 16:06:31 -07:00
|
|
|
/// This estimate may be negative if the current local time is behind the chain tip block's
|
|
|
|
/// timestamp.
|
|
|
|
///
|
|
|
|
/// Returns `None` if the state is empty.
|
2022-11-17 16:12:10 -08:00
|
|
|
fn estimate_distance_to_network_chain_tip(
|
|
|
|
&self,
|
|
|
|
network: Network,
|
2023-03-29 16:06:31 -07:00
|
|
|
) -> Option<(block::HeightDiff, block::Height)> {
|
2022-11-17 16:12:10 -08:00
|
|
|
let (current_height, current_block_time) = self.best_tip_height_and_block_time()?;
|
|
|
|
|
|
|
|
let estimator =
|
|
|
|
NetworkChainTipHeightEstimator::new(current_block_time, current_height, network);
|
|
|
|
|
2023-03-29 16:06:31 -07:00
|
|
|
let distance_to_tip = estimator.estimate_height_at(Utc::now()) - current_height;
|
|
|
|
|
|
|
|
Some((distance_to_tip, current_height))
|
2022-11-17 16:12:10 -08:00
|
|
|
}
|
2021-08-26 18:34:33 -07:00
|
|
|
}
|
|
|
|
|
2022-12-15 07:33:00 -08:00
|
|
|
/// A future for the [`ChainTip::best_tip_changed()`] method.
|
|
|
|
/// See that method for details.
|
|
|
|
pub struct BestTipChanged<'f> {
|
|
|
|
fut: BoxFuture<'f, Result<(), BoxError>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'f> BestTipChanged<'f> {
|
|
|
|
/// Returns a new [`BestTipChanged`] containing `fut`.
|
|
|
|
pub fn new<Fut>(fut: Fut) -> Self
|
|
|
|
where
|
|
|
|
Fut: Future<Output = Result<(), BoxError>> + Send + 'f,
|
|
|
|
{
|
|
|
|
Self { fut: Box::pin(fut) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'f> Future for BestTipChanged<'f> {
|
|
|
|
type Output = Result<(), BoxError>;
|
|
|
|
|
|
|
|
fn poll(
|
|
|
|
mut self: std::pin::Pin<&mut Self>,
|
|
|
|
cx: &mut std::task::Context<'_>,
|
|
|
|
) -> std::task::Poll<Self::Output> {
|
|
|
|
self.fut.poll_unpin(cx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A chain tip that is always empty and never changes.
|
2022-12-07 17:06:11 -08:00
|
|
|
///
|
|
|
|
/// Used in production for isolated network connections,
|
|
|
|
/// and as a mock chain tip in tests.
|
2021-08-26 18:34:33 -07:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct NoChainTip;
|
|
|
|
|
|
|
|
impl ChainTip for NoChainTip {
|
|
|
|
fn best_tip_height(&self) -> Option<block::Height> {
|
|
|
|
None
|
|
|
|
}
|
2021-08-27 12:18:47 -07:00
|
|
|
|
|
|
|
fn best_tip_hash(&self) -> Option<block::Hash> {
|
|
|
|
None
|
|
|
|
}
|
2021-08-29 19:38:41 -07:00
|
|
|
|
2022-03-25 05:25:31 -07:00
|
|
|
fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2022-02-10 17:27:02 -08:00
|
|
|
fn best_tip_block_time(&self) -> Option<DateTime<Utc>> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn best_tip_height_and_block_time(&self) -> Option<(block::Height, DateTime<Utc>)> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2021-08-30 11:42:07 -07:00
|
|
|
fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]> {
|
|
|
|
Arc::new([])
|
2021-08-29 19:38:41 -07:00
|
|
|
}
|
2022-12-15 07:33:00 -08:00
|
|
|
|
|
|
|
/// The [`NoChainTip`] best tip never changes, so this never returns.
|
|
|
|
fn best_tip_changed(&mut self) -> BestTipChanged {
|
|
|
|
BestTipChanged::new(future::pending())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The [`NoChainTip`] best tip never changes, so this does nothing.
|
|
|
|
fn mark_best_tip_seen(&mut self) {}
|
2021-08-26 18:34:33 -07:00
|
|
|
}
|