37f4268513f45ec2558a0d8f0635370c37e2d5f0
[rust-lightning] / lightning-block-sync / src / gossip.rs
1 //! When fetching gossip from peers, lightning nodes need to validate that gossip against the
2 //! current UTXO set. This module defines an implementation of the LDK API required to do so
3 //! against a [`BlockSource`] which implements a few additional methods for accessing the UTXO set.
4
5 use crate::{AsyncBlockSourceResult, BlockData, BlockSource, BlockSourceError};
6
7 use bitcoin::blockdata::block::Block;
8 use bitcoin::blockdata::transaction::{TxOut, OutPoint};
9 use bitcoin::hash_types::BlockHash;
10
11 use lightning::sign::NodeSigner;
12
13 use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor};
14 use lightning::ln::msgs::{ChannelMessageHandler, OnionMessageHandler};
15
16 use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
17 use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoResult, UtxoLookupError};
18
19 use lightning::util::logger::Logger;
20
21 use std::sync::{Arc, Mutex};
22 use std::collections::VecDeque;
23 use std::future::Future;
24 use std::ops::Deref;
25 use std::pin::Pin;
26 use std::task::Poll;
27
28 /// A trait which extends [`BlockSource`] and can be queried to fetch the block at a given height
29 /// as well as whether a given output is unspent (i.e. a member of the current UTXO set).
30 ///
31 /// Note that while this is implementable for a [`BlockSource`] which returns filtered block data
32 /// (i.e. [`BlockData::HeaderOnly`] for [`BlockSource::get_block`] requests), such an
33 /// implementation will reject all gossip as it is not fully able to verify the UTXOs referenced.
34 pub trait UtxoSource : BlockSource + 'static {
35         /// Fetches the block hash of the block at the given height.
36         ///
37         /// This will, in turn, be passed to to [`BlockSource::get_block`] to fetch the block needed
38         /// for gossip validation.
39         fn get_block_hash_by_height<'a>(&'a self, block_height: u32) -> AsyncBlockSourceResult<'a, BlockHash>;
40
41         /// Returns true if the given output has *not* been spent, i.e. is a member of the current UTXO
42         /// set.
43         fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
44 }
45
46 /// A generic trait which is able to spawn futures in the background.
47 ///
48 /// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
49 /// delegates to `tokio::spawn()`.
50 pub trait FutureSpawner : Send + Sync + 'static {
51         /// Spawns the given future as a background task.
52         ///
53         /// This method MUST NOT block on the given future immediately.
54         fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
55 }
56
57 #[cfg(feature = "tokio")]
58 /// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
59 pub struct TokioSpawner;
60 #[cfg(feature = "tokio")]
61 impl FutureSpawner for TokioSpawner {
62         fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T) {
63                 tokio::spawn(future);
64         }
65 }
66
67 /// A trivial future which joins two other futures and polls them at the same time, returning only
68 /// once both complete.
69 pub(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
79 impl<
80         A: Future<Output=Result<(BlockHash, Option<u32>), BlockSourceError>> + Unpin,
81         B: Future<Output=Result<BlockHash, BlockSourceError>> + Unpin,
82 > Joiner<A, B> {
83         fn new(a: A, b: B) -> Self { Self { a, b, a_res: None, b_res: None } }
84 }
85
86 impl<
87         A: Future<Output=Result<(BlockHash, Option<u32>), BlockSourceError>> + Unpin,
88         B: Future<Output=Result<BlockHash, BlockSourceError>> + Unpin,
89 > Future for Joiner<A, B> {
90         type Output = Result<((BlockHash, Option<u32>), BlockHash), BlockSourceError>;
91         fn poll(mut self: Pin<&mut Self>, ctx: &mut core::task::Context<'_>) -> Poll<Self::Output> {
92                 if self.a_res.is_none() {
93                         match Pin::new(&mut self.a).poll(ctx) {
94                                 Poll::Ready(res) => {
95                                         if let Ok(ok) = res {
96                                                 self.a_res = Some(ok);
97                                         } else {
98                                                 return Poll::Ready(Err(res.unwrap_err()));
99                                         }
100                                 },
101                                 Poll::Pending => {},
102                         }
103                 }
104                 if self.b_res.is_none() {
105                         match Pin::new(&mut self.b).poll(ctx) {
106                                 Poll::Ready(res) => {
107                                         if let Ok(ok) = res {
108                                                 self.b_res = Some(ok);
109                                         } else {
110                                                 return Poll::Ready(Err(res.unwrap_err()));
111                                         }
112
113                                 },
114                                 Poll::Pending => {},
115                         }
116                 }
117                 if let Some(b_res) = self.b_res {
118                         if let Some(a_res) = self.a_res {
119                                 return Poll::Ready(Ok((a_res, b_res)))
120                         }
121                 }
122                 Poll::Pending
123         }
124 }
125
126 /// A struct which wraps a [`UtxoSource`] and a few LDK objects and implements the LDK
127 /// [`UtxoLookup`] trait.
128 ///
129 /// Note that if you're using this against a Bitcoin Core REST or RPC server, you likely wish to
130 /// increase the `rpcworkqueue` setting in Bitcoin Core as LDK attempts to parallelize requests (a
131 /// value of 1024 should more than suffice), and ensure you have sufficient file descriptors
132 /// available on both Bitcoin Core and your LDK application for each request to hold its own
133 /// connection.
134 pub struct GossipVerifier<S: FutureSpawner,
135         Blocks: Deref + Send + Sync + 'static + Clone,
136         L: Deref + Send + Sync + 'static,
137         Descriptor: SocketDescriptor + Send + Sync + 'static,
138         CM: Deref + Send + Sync + 'static,
139         OM: Deref + Send + Sync + 'static,
140         CMH: Deref + Send + Sync + 'static,
141         NS: Deref + Send + Sync + 'static,
142 > where
143         Blocks::Target: UtxoSource,
144         L::Target: Logger,
145         CM::Target: ChannelMessageHandler,
146         OM::Target: OnionMessageHandler,
147         CMH::Target: CustomMessageHandler,
148         NS::Target: NodeSigner,
149 {
150         source: Blocks,
151         peer_manager: Arc<PeerManager<Descriptor, CM, Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, OM, L, CMH, NS>>,
152         gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>,
153         spawn: S,
154         block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>,
155 }
156
157 const BLOCK_CACHE_SIZE: usize = 5;
158
159 impl<S: FutureSpawner,
160         Blocks: Deref + Send + Sync + Clone,
161         L: Deref + Send + Sync,
162         Descriptor: SocketDescriptor + Send + Sync,
163         CM: Deref + Send + Sync,
164         OM: Deref + Send + Sync,
165         CMH: Deref + Send + Sync,
166         NS: Deref + Send + Sync,
167 > GossipVerifier<S, Blocks, L, Descriptor, CM, OM, CMH, NS> where
168         Blocks::Target: UtxoSource,
169         L::Target: Logger,
170         CM::Target: ChannelMessageHandler,
171         OM::Target: OnionMessageHandler,
172         CMH::Target: CustomMessageHandler,
173         NS::Target: NodeSigner,
174 {
175         /// Constructs a new [`GossipVerifier`].
176         ///
177         /// This is expected to be given to a [`P2PGossipSync`] (initially constructed with `None` for
178         /// the UTXO lookup) via [`P2PGossipSync::add_utxo_lookup`].
179         pub fn new(source: Blocks, spawn: S, gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, peer_manager: Arc<PeerManager<Descriptor, CM, Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, OM, L, CMH, NS>>) -> Self {
180                 Self {
181                         source, spawn, gossiper, peer_manager,
182                         block_cache: Arc::new(Mutex::new(VecDeque::with_capacity(BLOCK_CACHE_SIZE))),
183                 }
184         }
185
186         async fn retrieve_utxo(
187                 source: Blocks, block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>, short_channel_id: u64
188         ) -> Result<TxOut, UtxoLookupError> {
189                 let block_height = (short_channel_id >> 5 * 8) as u32; // block height is most significant three bytes
190                 let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32;
191                 let output_index = (short_channel_id & 0xffff) as u16;
192
193                 let (outpoint, output);
194
195                 'tx_found: loop { // Used as a simple goto
196                         macro_rules! process_block {
197                                 ($block: expr) => { {
198                                         if transaction_index as usize >= $block.txdata.len() {
199                                                 return Err(UtxoLookupError::UnknownTx);
200                                         }
201                                         let transaction = &$block.txdata[transaction_index as usize];
202                                         if output_index as usize >= transaction.output.len() {
203                                                 return Err(UtxoLookupError::UnknownTx);
204                                         }
205
206                                         outpoint = OutPoint::new(transaction.txid(), output_index.into());
207                                         output = transaction.output[output_index as usize].clone();
208                                 } }
209                         }
210                         {
211                                 let recent_blocks = block_cache.lock().unwrap();
212                                 for (height, block) in recent_blocks.iter() {
213                                         if *height == block_height {
214                                                 process_block!(block);
215                                                 break 'tx_found;
216                                         }
217                                 }
218                         }
219
220                         let ((_, tip_height_opt), block_hash) =
221                                 Joiner::new(source.get_best_block(), source.get_block_hash_by_height(block_height))
222                                 .await
223                                 .map_err(|_| UtxoLookupError::UnknownTx)?;
224                         if let Some(tip_height) = tip_height_opt {
225                                 // If the block doesn't yet have five confirmations, error out.
226                                 //
227                                 // The BOLT spec requires nodes wait for six confirmations before announcing a
228                                 // channel, and we give them one block of headroom in case we're delayed seeing a
229                                 // block.
230                                 if block_height + 5 > tip_height {
231                                         return Err(UtxoLookupError::UnknownTx);
232                                 }
233                         }
234                         let block_data = source.get_block(&block_hash).await
235                                 .map_err(|_| UtxoLookupError::UnknownTx)?;
236                         let block = match block_data {
237                                 BlockData::HeaderOnly(_) => return Err(UtxoLookupError::UnknownTx),
238                                 BlockData::FullBlock(block) => block,
239                         };
240                         process_block!(block);
241                         {
242                                 let mut recent_blocks = block_cache.lock().unwrap();
243                                 let mut insert = true;
244                                 for (height, _) in recent_blocks.iter() {
245                                         if *height == block_height {
246                                                 insert = false;
247                                         }
248                                 }
249                                 if insert {
250                                         if recent_blocks.len() >= BLOCK_CACHE_SIZE {
251                                                 recent_blocks.pop_front();
252                                         }
253                                         recent_blocks.push_back((block_height, block));
254                                 }
255                         }
256                         break 'tx_found;
257                 };
258                 let outpoint_unspent =
259                         source.is_output_unspent(outpoint).await.map_err(|_| UtxoLookupError::UnknownTx)?;
260                 if outpoint_unspent {
261                         Ok(output)
262                 } else {
263                         Err(UtxoLookupError::UnknownTx)
264                 }
265         }
266 }
267
268 impl<S: FutureSpawner,
269         Blocks: Deref + Send + Sync + Clone,
270         L: Deref + Send + Sync,
271         Descriptor: SocketDescriptor + Send + Sync,
272         CM: Deref + Send + Sync,
273         OM: Deref + Send + Sync,
274         CMH: Deref + Send + Sync,
275         NS: Deref + Send + Sync,
276 > Deref for GossipVerifier<S, Blocks, L, Descriptor, CM, OM, CMH, NS> where
277         Blocks::Target: UtxoSource,
278         L::Target: Logger,
279         CM::Target: ChannelMessageHandler,
280         OM::Target: OnionMessageHandler,
281         CMH::Target: CustomMessageHandler,
282         NS::Target: NodeSigner,
283 {
284         type Target = Self;
285         fn deref(&self) -> &Self { self }
286 }
287
288
289 impl<S: FutureSpawner,
290         Blocks: Deref + Send + Sync + Clone,
291         L: Deref + Send + Sync,
292         Descriptor: SocketDescriptor + Send + Sync,
293         CM: Deref + Send + Sync,
294         OM: Deref + Send + Sync,
295         CMH: Deref + Send + Sync,
296         NS: Deref + Send + Sync,
297 > UtxoLookup for GossipVerifier<S, Blocks, L, Descriptor, CM, OM, CMH, NS> where
298         Blocks::Target: UtxoSource,
299         L::Target: Logger,
300         CM::Target: ChannelMessageHandler,
301         OM::Target: OnionMessageHandler,
302         CMH::Target: CustomMessageHandler,
303         NS::Target: NodeSigner,
304 {
305         fn get_utxo(&self, _genesis_hash: &BlockHash, short_channel_id: u64) -> UtxoResult {
306                 let res = UtxoFuture::new();
307                 let fut = res.clone();
308                 let source = self.source.clone();
309                 let gossiper = Arc::clone(&self.gossiper);
310                 let block_cache = Arc::clone(&self.block_cache);
311                 let pm = Arc::clone(&self.peer_manager);
312                 self.spawn.spawn(async move {
313                         let res = Self::retrieve_utxo(source, block_cache, short_channel_id).await;
314                         fut.resolve(gossiper.network_graph(), &*gossiper, res);
315                         pm.process_events();
316                 });
317                 UtxoResult::Async(res)
318         }
319 }