1use crate::{AsyncBlockSourceResult, BlockData, BlockSource, BlockSourceError};
6
7use bitcoin::block::Block;
8use bitcoin::constants::ChainHash;
9use bitcoin::hash_types::BlockHash;
10use bitcoin::transaction::{OutPoint, TxOut};
11
12use lightning::ln::peer_handler::APeerManager;
13
14use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
15use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};
16
17use lightning::util::logger::Logger;
18
19use std::collections::VecDeque;
20use std::future::Future;
21use std::ops::Deref;
22use std::pin::Pin;
23use std::sync::{Arc, Mutex};
24use std::task::Poll;
25
26pub trait UtxoSource: BlockSource + 'static {
33 fn get_block_hash_by_height<'a>(
38 &'a self, block_height: u32,
39 ) -> AsyncBlockSourceResult<'a, BlockHash>;
40
41 fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
44}
45
46pub trait FutureSpawner: Send + Sync + 'static {
51 fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
55}
56
57#[cfg(feature = "tokio")]
58pub struct TokioSpawner;
60#[cfg(feature = "tokio")]
61impl FutureSpawner for TokioSpawner {
62 fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T) {
63 tokio::spawn(future);
64 }
65}
66
67pub(crate) struct Joiner<
70 A: Future<Output = Result<(BlockHash, Option<u32>), BlockSourceError>> + Unpin,
71 B: Future<Output = Result<BlockHash, BlockSourceError>> + Unpin,
72> {
73 pub a: A,
74 pub b: B,
75 a_res: Option<(BlockHash, Option<u32>)>,
76 b_res: Option<BlockHash>,
77}
78
79impl<
80 A: Future<Output = Result<(BlockHash, Option<u32>), BlockSourceError>> + Unpin,
81 B: Future<Output = Result<BlockHash, BlockSourceError>> + Unpin,
82 > Joiner<A, B>
83{
84 fn new(a: A, b: B) -> Self {
85 Self { a, b, a_res: None, b_res: None }
86 }
87}
88
89impl<
90 A: Future<Output = Result<(BlockHash, Option<u32>), BlockSourceError>> + Unpin,
91 B: Future<Output = Result<BlockHash, BlockSourceError>> + Unpin,
92 > Future for Joiner<A, B>
93{
94 type Output = Result<((BlockHash, Option<u32>), BlockHash), BlockSourceError>;
95 fn poll(mut self: Pin<&mut Self>, ctx: &mut core::task::Context<'_>) -> Poll<Self::Output> {
96 if self.a_res.is_none() {
97 match Pin::new(&mut self.a).poll(ctx) {
98 Poll::Ready(res) => {
99 if let Ok(ok) = res {
100 self.a_res = Some(ok);
101 } else {
102 return Poll::Ready(Err(res.unwrap_err()));
103 }
104 },
105 Poll::Pending => {},
106 }
107 }
108 if self.b_res.is_none() {
109 match Pin::new(&mut self.b).poll(ctx) {
110 Poll::Ready(res) => {
111 if let Ok(ok) = res {
112 self.b_res = Some(ok);
113 } else {
114 return Poll::Ready(Err(res.unwrap_err()));
115 }
116 },
117 Poll::Pending => {},
118 }
119 }
120 if let Some(b_res) = self.b_res {
121 if let Some(a_res) = self.a_res {
122 return Poll::Ready(Ok((a_res, b_res)));
123 }
124 }
125 Poll::Pending
126 }
127}
128
129pub struct GossipVerifier<
138 S: FutureSpawner,
139 Blocks: Deref + Send + Sync + 'static + Clone,
140 L: Deref + Send + Sync + 'static,
141> where
142 Blocks::Target: UtxoSource,
143 L::Target: Logger,
144{
145 source: Blocks,
146 peer_manager_wake: Arc<dyn Fn() + Send + Sync>,
147 gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Arc<Self>, L>>,
148 spawn: S,
149 block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>,
150}
151
152const BLOCK_CACHE_SIZE: usize = 5;
153
154impl<S: FutureSpawner, Blocks: Deref + Send + Sync + Clone, L: Deref + Send + Sync>
155 GossipVerifier<S, Blocks, L>
156where
157 Blocks::Target: UtxoSource,
158 L::Target: Logger,
159{
160 pub fn new<APM: Deref + Send + Sync + Clone + 'static>(
165 source: Blocks, spawn: S, gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Arc<Self>, L>>,
166 peer_manager: APM,
167 ) -> Self
168 where
169 APM::Target: APeerManager,
170 {
171 let peer_manager_wake = Arc::new(move || peer_manager.as_ref().process_events());
172 Self {
173 source,
174 spawn,
175 gossiper,
176 peer_manager_wake,
177 block_cache: Arc::new(Mutex::new(VecDeque::with_capacity(BLOCK_CACHE_SIZE))),
178 }
179 }
180
181 async fn retrieve_utxo(
182 source: Blocks, block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>, short_channel_id: u64,
183 ) -> Result<TxOut, UtxoLookupError> {
184 let block_height = (short_channel_id >> 5 * 8) as u32; let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32;
186 let output_index = (short_channel_id & 0xffff) as u16;
187
188 let (outpoint, output);
189
190 'tx_found: loop {
191 macro_rules! process_block {
192 ($block: expr) => {{
193 if transaction_index as usize >= $block.txdata.len() {
194 return Err(UtxoLookupError::UnknownTx);
195 }
196 let transaction = &$block.txdata[transaction_index as usize];
197 if output_index as usize >= transaction.output.len() {
198 return Err(UtxoLookupError::UnknownTx);
199 }
200
201 outpoint = OutPoint::new(transaction.compute_txid(), output_index.into());
202 output = transaction.output[output_index as usize].clone();
203 }};
204 }
205 {
206 let recent_blocks = block_cache.lock().unwrap();
207 for (height, block) in recent_blocks.iter() {
208 if *height == block_height {
209 process_block!(block);
210 break 'tx_found;
211 }
212 }
213 }
214
215 let ((_, tip_height_opt), block_hash) =
216 Joiner::new(source.get_best_block(), source.get_block_hash_by_height(block_height))
217 .await
218 .map_err(|_| UtxoLookupError::UnknownTx)?;
219 if let Some(tip_height) = tip_height_opt {
220 if block_height + 5 > tip_height {
226 return Err(UtxoLookupError::UnknownTx);
227 }
228 }
229 let block_data =
230 source.get_block(&block_hash).await.map_err(|_| UtxoLookupError::UnknownTx)?;
231 let block = match block_data {
232 BlockData::HeaderOnly(_) => return Err(UtxoLookupError::UnknownTx),
233 BlockData::FullBlock(block) => block,
234 };
235 process_block!(block);
236 {
237 let mut recent_blocks = block_cache.lock().unwrap();
238 let mut insert = true;
239 for (height, _) in recent_blocks.iter() {
240 if *height == block_height {
241 insert = false;
242 }
243 }
244 if insert {
245 if recent_blocks.len() >= BLOCK_CACHE_SIZE {
246 recent_blocks.pop_front();
247 }
248 recent_blocks.push_back((block_height, block));
249 }
250 }
251 break 'tx_found;
252 }
253 let outpoint_unspent =
254 source.is_output_unspent(outpoint).await.map_err(|_| UtxoLookupError::UnknownTx)?;
255 if outpoint_unspent {
256 Ok(output)
257 } else {
258 Err(UtxoLookupError::UnknownTx)
259 }
260 }
261}
262
263impl<S: FutureSpawner, Blocks: Deref + Send + Sync + Clone, L: Deref + Send + Sync> Deref
264 for GossipVerifier<S, Blocks, L>
265where
266 Blocks::Target: UtxoSource,
267 L::Target: Logger,
268{
269 type Target = Self;
270 fn deref(&self) -> &Self {
271 self
272 }
273}
274
275impl<S: FutureSpawner, Blocks: Deref + Send + Sync + Clone, L: Deref + Send + Sync> UtxoLookup
276 for GossipVerifier<S, Blocks, L>
277where
278 Blocks::Target: UtxoSource,
279 L::Target: Logger,
280{
281 fn get_utxo(&self, _chain_hash: &ChainHash, short_channel_id: u64) -> UtxoResult {
282 let res = UtxoFuture::new();
283 let fut = res.clone();
284 let source = self.source.clone();
285 let gossiper = Arc::clone(&self.gossiper);
286 let block_cache = Arc::clone(&self.block_cache);
287 let pmw = Arc::clone(&self.peer_manager_wake);
288 self.spawn.spawn(async move {
289 let res = Self::retrieve_utxo(source, block_cache, short_channel_id).await;
290 fut.resolve(gossiper.network_graph(), &*gossiper, res);
291 (pmw)();
292 });
293 UtxoResult::Async(res)
294 }
295}