mod polling;
mod source;
#[cfg(all(feature = "bitcoind-rpc", not(target_arch = "wasm32")))]
mod zmq;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::watch;
use bark_runtime::CancellationToken;
use bitcoin_ext::BlockRef;
pub use source::TipSource;
use polling::PollingTipWatcher;
#[cfg(all(feature = "bitcoind-rpc", not(target_arch = "wasm32")))]
use zmq::ZmqTipWatcher;
#[derive(Clone)]
pub struct TipWatcher {
shutdown: CancellationToken,
_shutdown_on_drop: Arc<ShutdownGuard>,
rx: watch::Receiver<BlockRef>,
}
struct ShutdownGuard(CancellationToken);
impl Drop for ShutdownGuard {
fn drop(&mut self) {
self.0.cancel();
}
}
impl TipWatcher {
fn new(shutdown: CancellationToken, rx: watch::Receiver<BlockRef>) -> Self {
let guard = ShutdownGuard(shutdown.clone());
Self { shutdown, _shutdown_on_drop: Arc::new(guard), rx }
}
pub async fn start_poll<S: TipSource + 'static>(
source: Arc<S>,
poll_interval: Duration,
) -> anyhow::Result<Self> {
let initial = source.tip_ref().await?;
let (tx, rx) = watch::channel(initial);
let shutdown = CancellationToken::new();
let proc = PollingTipWatcher {
source,
poll_interval,
shutdown: shutdown.clone(),
tx,
};
bark_runtime::spawn(proc.run());
Ok(Self::new(shutdown, rx))
}
#[cfg(all(feature = "bitcoind-rpc", not(target_arch = "wasm32")))]
pub async fn start_zmq<S: TipSource + 'static>(
source: Arc<S>,
zmq_endpoint: &str,
reconcile_interval: Duration,
) -> anyhow::Result<Self> {
let socket = zmq::connect(zmq_endpoint).await?;
let initial = source.tip_ref().await?;
let (tx, rx) = watch::channel(initial);
let shutdown = CancellationToken::new();
let proc = ZmqTipWatcher {
source,
reconcile_interval,
shutdown: shutdown.clone(),
tx,
socket,
};
bark_runtime::spawn(proc.run());
Ok(Self::new(shutdown, rx))
}
pub fn tip(&self) -> BlockRef {
*self.rx.borrow()
}
pub fn subscribe(&self) -> TipSubscription {
TipSubscription {
rx: self.rx.clone(),
_shutdown_on_drop: self._shutdown_on_drop.clone(),
}
}
pub async fn wait_for_height(&self, height: u32) -> anyhow::Result<BlockRef> {
let mut subscription = self.rx.clone();
let tip = subscription.wait_for(|tip| tip.height >= height).await?;
Ok(*tip)
}
pub fn stop(&self) {
self.shutdown.cancel();
}
}
#[derive(Clone)]
pub struct TipSubscription {
rx: watch::Receiver<BlockRef>,
_shutdown_on_drop: Arc<ShutdownGuard>,
}
impl TipSubscription {
pub fn tip(&self) -> BlockRef {
*self.rx.borrow()
}
pub async fn changed(&mut self) -> anyhow::Result<BlockRef> {
self.rx.changed().await?;
Ok(*self.rx.borrow_and_update())
}
}