use std::sync::Arc;
use chrono::{DateTime, Utc};
use futures::{future, FutureExt, TryFutureExt};
use tokio::sync::watch;
use crate::{
block,
chain_tip::{BestTipChanged, ChainTip},
parameters::Network,
transaction,
};
pub struct MockChainTipSender {
best_tip_height: watch::Sender<Option<block::Height>>,
best_tip_hash: watch::Sender<Option<block::Hash>>,
best_tip_block_time: watch::Sender<Option<DateTime<Utc>>>,
estimated_distance_to_network_chain_tip: watch::Sender<Option<block::HeightDiff>>,
}
#[derive(Clone, Debug)]
pub struct MockChainTip {
best_tip_height: watch::Receiver<Option<block::Height>>,
best_tip_hash: watch::Receiver<Option<block::Hash>>,
best_tip_block_time: watch::Receiver<Option<DateTime<Utc>>>,
estimated_distance_to_network_chain_tip: watch::Receiver<Option<block::HeightDiff>>,
}
impl MockChainTip {
pub fn new() -> (Self, MockChainTipSender) {
let (height_sender, height_receiver) = watch::channel(None);
let (hash_sender, hash_receiver) = watch::channel(None);
let (time_sender, time_receiver) = watch::channel(None);
let (estimated_distance_to_tip_sender, estimated_distance_to_tip_receiver) =
watch::channel(None);
let mock_chain_tip = MockChainTip {
best_tip_height: height_receiver,
best_tip_hash: hash_receiver,
best_tip_block_time: time_receiver,
estimated_distance_to_network_chain_tip: estimated_distance_to_tip_receiver,
};
let mock_chain_tip_sender = MockChainTipSender {
best_tip_height: height_sender,
best_tip_hash: hash_sender,
best_tip_block_time: time_sender,
estimated_distance_to_network_chain_tip: estimated_distance_to_tip_sender,
};
(mock_chain_tip, mock_chain_tip_sender)
}
}
impl ChainTip for MockChainTip {
fn best_tip_height(&self) -> Option<block::Height> {
*self.best_tip_height.borrow()
}
fn best_tip_hash(&self) -> Option<block::Hash> {
*self.best_tip_hash.borrow()
}
fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)> {
let height = (*self.best_tip_height.borrow())?;
let hash = (*self.best_tip_hash.borrow())?;
Some((height, hash))
}
fn best_tip_block_time(&self) -> Option<DateTime<Utc>> {
*self.best_tip_block_time.borrow()
}
fn best_tip_height_and_block_time(&self) -> Option<(block::Height, DateTime<Utc>)> {
let height = (*self.best_tip_height.borrow())?;
let block_time = (*self.best_tip_block_time.borrow())?;
Some((height, block_time))
}
fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]> {
Arc::new([])
}
fn estimate_distance_to_network_chain_tip(
&self,
_network: &Network,
) -> Option<(block::HeightDiff, block::Height)> {
self.estimated_distance_to_network_chain_tip
.borrow()
.and_then(|estimated_distance| {
self.best_tip_height()
.map(|tip_height| (estimated_distance, tip_height))
})
}
fn best_tip_changed(&mut self) -> BestTipChanged {
let select_changed = future::select_all([
BestTipChanged::new(self.best_tip_height.changed().err_into()),
BestTipChanged::new(self.best_tip_hash.changed().err_into()),
BestTipChanged::new(self.best_tip_block_time.changed().err_into()),
BestTipChanged::new(
self.estimated_distance_to_network_chain_tip
.changed()
.err_into(),
),
])
.map(|(changed_result, _changed_index, _remaining_futures)| changed_result);
BestTipChanged::new(select_changed)
}
fn mark_best_tip_seen(&mut self) {
self.best_tip_height.borrow_and_update();
self.best_tip_hash.borrow_and_update();
self.best_tip_block_time.borrow_and_update();
self.estimated_distance_to_network_chain_tip
.borrow_and_update();
}
}
impl MockChainTipSender {
pub fn send_best_tip_height(&self, height: impl Into<Option<block::Height>>) {
self.best_tip_height
.send(height.into())
.expect("attempt to send a best tip height to a dropped `MockChainTip`");
}
pub fn send_best_tip_hash(&self, hash: impl Into<Option<block::Hash>>) {
self.best_tip_hash
.send(hash.into())
.expect("attempt to send a best tip hash to a dropped `MockChainTip`");
}
pub fn send_best_tip_block_time(&self, block_time: impl Into<Option<DateTime<Utc>>>) {
self.best_tip_block_time
.send(block_time.into())
.expect("attempt to send a best tip block time to a dropped `MockChainTip`");
}
pub fn send_estimated_distance_to_network_chain_tip(
&self,
distance: impl Into<Option<block::HeightDiff>>,
) {
self.estimated_distance_to_network_chain_tip
.send(distance.into())
.expect("attempt to send a best tip height to a dropped `MockChainTip`");
}
}