bark/daemon/tip_watcher/
mod.rs1mod polling;
4mod source;
5#[cfg(all(feature = "bitcoind-rpc", not(target_arch = "wasm32")))]
6mod zmq;
7
8use std::sync::Arc;
9use std::time::Duration;
10
11use tokio::sync::watch;
12
13use bark_runtime::CancellationToken;
14use bitcoin_ext::BlockRef;
15
16pub use source::TipSource;
17
18use polling::PollingTipWatcher;
19#[cfg(all(feature = "bitcoind-rpc", not(target_arch = "wasm32")))]
20use zmq::ZmqTipWatcher;
21
22#[derive(Clone)]
23pub struct TipWatcher {
24 shutdown: CancellationToken,
25 _shutdown_on_drop: Arc<ShutdownGuard>,
27 rx: watch::Receiver<BlockRef>,
28}
29
30struct ShutdownGuard(CancellationToken);
31
32impl Drop for ShutdownGuard {
33 fn drop(&mut self) {
34 self.0.cancel();
35 }
36}
37
38impl TipWatcher {
39 fn new(shutdown: CancellationToken, rx: watch::Receiver<BlockRef>) -> Self {
40 let guard = ShutdownGuard(shutdown.clone());
41 Self { shutdown, _shutdown_on_drop: Arc::new(guard), rx }
42 }
43
44 pub async fn start_poll<S: TipSource + 'static>(
45 source: Arc<S>,
46 poll_interval: Duration,
47 ) -> anyhow::Result<Self> {
48 let initial = source.tip_ref().await?;
49 let (tx, rx) = watch::channel(initial);
50 let shutdown = CancellationToken::new();
51 let proc = PollingTipWatcher {
52 source,
53 poll_interval,
54 shutdown: shutdown.clone(),
55 tx,
56 };
57
58 bark_runtime::spawn(proc.run());
59 Ok(Self::new(shutdown, rx))
60 }
61
62 #[cfg(all(feature = "bitcoind-rpc", not(target_arch = "wasm32")))]
74 pub async fn start_zmq<S: TipSource + 'static>(
75 source: Arc<S>,
76 zmq_endpoint: &str,
77 reconcile_interval: Duration,
78 ) -> anyhow::Result<Self> {
79 let socket = zmq::connect(zmq_endpoint).await?;
80 let initial = source.tip_ref().await?;
81 let (tx, rx) = watch::channel(initial);
82 let shutdown = CancellationToken::new();
83
84 let proc = ZmqTipWatcher {
85 source,
86 reconcile_interval,
87 shutdown: shutdown.clone(),
88 tx,
89 socket,
90 };
91
92 bark_runtime::spawn(proc.run());
93 Ok(Self::new(shutdown, rx))
94 }
95
96 pub fn tip(&self) -> BlockRef {
97 *self.rx.borrow()
98 }
99
100 pub fn subscribe(&self) -> TipSubscription {
104 TipSubscription {
105 rx: self.rx.clone(),
106 _shutdown_on_drop: self._shutdown_on_drop.clone(),
107 }
108 }
109
110 pub async fn wait_for_height(&self, height: u32) -> anyhow::Result<BlockRef> {
112 let mut subscription = self.rx.clone();
115 let tip = subscription.wait_for(|tip| tip.height >= height).await?;
116 Ok(*tip)
117 }
118
119 pub fn stop(&self) {
120 self.shutdown.cancel();
121 }
122}
123
124#[derive(Clone)]
128pub struct TipSubscription {
129 rx: watch::Receiver<BlockRef>,
130 _shutdown_on_drop: Arc<ShutdownGuard>,
131}
132
133impl TipSubscription {
134 pub fn tip(&self) -> BlockRef {
136 *self.rx.borrow()
137 }
138
139 pub async fn changed(&mut self) -> anyhow::Result<BlockRef> {
144 self.rx.changed().await?;
145 Ok(*self.rx.borrow_and_update())
148 }
149}