]> git.bitcoin.ninja Git - rust-lightning/blob - lightning-block-sync/src/gossip.rs
4e66c0ce9756f016523bb9a9cfd2d65f973f5eca
[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};
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
26 /// A trait which extends [`BlockSource`] and can be queried to fetch the block at a given height
27 /// as well as whether a given output is unspent (i.e. a member of the current UTXO set).
28 ///
29 /// Note that while this is implementable for a [`BlockSource`] which returns filtered block data
30 /// (i.e. [`BlockData::HeaderOnly`] for [`BlockSource::get_block`] requests), such an
31 /// implementation will reject all gossip as it is not fully able to verify the UTXOs referenced.
32 pub trait UtxoSource : BlockSource + 'static {
33         /// Fetches the block hash of the block at the given height.
34         ///
35         /// This will, in turn, be passed to to [`BlockSource::get_block`] to fetch the block needed
36         /// for gossip validation.
37         fn get_block_hash_by_height<'a>(&'a self, block_height: u32) -> AsyncBlockSourceResult<'a, BlockHash>;
38
39         /// Returns true if the given output has *not* been spent, i.e. is a member of the current UTXO
40         /// set.
41         fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
42 }
43
44 /// A generic trait which is able to spawn futures in the background.
45 ///
46 /// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
47 /// delegates to `tokio::spawn()`.
48 pub trait FutureSpawner : Send + Sync + 'static {
49         /// Spawns the given future as a background task.
50         ///
51         /// This method MUST NOT block on the given future immediately.
52         fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
53 }
54
55 #[cfg(feature = "tokio")]
56 /// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
57 pub struct TokioSpawner;
58 #[cfg(feature = "tokio")]
59 impl FutureSpawner for TokioSpawner {
60         fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T) {
61                 tokio::spawn(future);
62         }
63 }
64
65 /// A struct which wraps a [`UtxoSource`] and a few LDK objects and implements the LDK
66 /// [`UtxoLookup`] trait.
67 ///
68 /// Note that if you're using this against a Bitcoin Core REST or RPC server, you likely wish to
69 /// increase the `rpcworkqueue` setting in Bitcoin Core as LDK attempts to parallelize requests (a
70 /// value of 1024 should more than suffice), and ensure you have sufficient file descriptors
71 /// available on both Bitcoin Core and your LDK application for each request to hold its own
72 /// connection.
73 pub struct GossipVerifier<S: FutureSpawner,
74         Blocks: Deref + Send + Sync + 'static + Clone,
75         L: Deref + Send + Sync + 'static,
76         Descriptor: SocketDescriptor + Send + Sync + 'static,
77         CM: Deref + Send + Sync + 'static,
78         OM: Deref + Send + Sync + 'static,
79         CMH: Deref + Send + Sync + 'static,
80         NS: Deref + Send + Sync + 'static,
81 > where
82         Blocks::Target: UtxoSource,
83         L::Target: Logger,
84         CM::Target: ChannelMessageHandler,
85         OM::Target: OnionMessageHandler,
86         CMH::Target: CustomMessageHandler,
87         NS::Target: NodeSigner,
88 {
89         source: Blocks,
90         peer_manager: Arc<PeerManager<Descriptor, CM, Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, OM, L, CMH, NS>>,
91         gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>,
92         spawn: S,
93         block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>,
94 }
95
96 const BLOCK_CACHE_SIZE: usize = 5;
97
98 impl<S: FutureSpawner,
99         Blocks: Deref + Send + Sync + Clone,
100         L: Deref + Send + Sync,
101         Descriptor: SocketDescriptor + Send + Sync,
102         CM: Deref + Send + Sync,
103         OM: Deref + Send + Sync,
104         CMH: Deref + Send + Sync,
105         NS: Deref + Send + Sync,
106 > GossipVerifier<S, Blocks, L, Descriptor, CM, OM, CMH, NS> where
107         Blocks::Target: UtxoSource,
108         L::Target: Logger,
109         CM::Target: ChannelMessageHandler,
110         OM::Target: OnionMessageHandler,
111         CMH::Target: CustomMessageHandler,
112         NS::Target: NodeSigner,
113 {
114         /// Constructs a new [`GossipVerifier`].
115         ///
116         /// This is expected to be given to a [`P2PGossipSync`] (initially constructed with `None` for
117         /// the UTXO lookup) via [`P2PGossipSync::add_utxo_lookup`].
118         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 {
119                 Self {
120                         source, spawn, gossiper, peer_manager,
121                         block_cache: Arc::new(Mutex::new(VecDeque::with_capacity(BLOCK_CACHE_SIZE))),
122                 }
123         }
124
125         async fn retrieve_utxo(
126                 source: Blocks, block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>, short_channel_id: u64
127         ) -> Result<TxOut, UtxoLookupError> {
128                 let block_height = (short_channel_id >> 5 * 8) as u32; // block height is most significant three bytes
129                 let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32;
130                 let output_index = (short_channel_id & 0xffff) as u16;
131
132                 let (outpoint, output);
133
134                 'tx_found: loop { // Used as a simple goto
135                         macro_rules! process_block {
136                                 ($block: expr) => { {
137                                         if transaction_index as usize >= $block.txdata.len() {
138                                                 return Err(UtxoLookupError::UnknownTx);
139                                         }
140                                         let transaction = &$block.txdata[transaction_index as usize];
141                                         if output_index as usize >= transaction.output.len() {
142                                                 return Err(UtxoLookupError::UnknownTx);
143                                         }
144
145                                         outpoint = OutPoint::new(transaction.txid(), output_index.into());
146                                         output = transaction.output[output_index as usize].clone();
147                                 } }
148                         }
149                         {
150                                 let recent_blocks = block_cache.lock().unwrap();
151                                 for (height, block) in recent_blocks.iter() {
152                                         if *height == block_height {
153                                                 process_block!(block);
154                                                 break 'tx_found;
155                                         }
156                                 }
157                         }
158
159                         let block_hash = source.get_block_hash_by_height(block_height).await
160                                 .map_err(|_| UtxoLookupError::UnknownTx)?;
161                         let block_data = source.get_block(&block_hash).await
162                                 .map_err(|_| UtxoLookupError::UnknownTx)?;
163                         let block = match block_data {
164                                 BlockData::HeaderOnly(_) => return Err(UtxoLookupError::UnknownTx),
165                                 BlockData::FullBlock(block) => block,
166                         };
167                         process_block!(block);
168                         {
169                                 let mut recent_blocks = block_cache.lock().unwrap();
170                                 let mut insert = true;
171                                 for (height, _) in recent_blocks.iter() {
172                                         if *height == block_height {
173                                                 insert = false;
174                                         }
175                                 }
176                                 if insert {
177                                         if recent_blocks.len() >= BLOCK_CACHE_SIZE {
178                                                 recent_blocks.pop_front();
179                                         }
180                                         recent_blocks.push_back((block_height, block));
181                                 }
182                         }
183                         break 'tx_found;
184                 };
185                 let outpoint_unspent =
186                         source.is_output_unspent(outpoint).await.map_err(|_| UtxoLookupError::UnknownTx)?;
187                 if outpoint_unspent {
188                         Ok(output)
189                 } else {
190                         Err(UtxoLookupError::UnknownTx)
191                 }
192         }
193 }
194
195 impl<S: FutureSpawner,
196         Blocks: Deref + Send + Sync + Clone,
197         L: Deref + Send + Sync,
198         Descriptor: SocketDescriptor + Send + Sync,
199         CM: Deref + Send + Sync,
200         OM: Deref + Send + Sync,
201         CMH: Deref + Send + Sync,
202         NS: Deref + Send + Sync,
203 > Deref for GossipVerifier<S, Blocks, L, Descriptor, CM, OM, CMH, NS> where
204         Blocks::Target: UtxoSource,
205         L::Target: Logger,
206         CM::Target: ChannelMessageHandler,
207         OM::Target: OnionMessageHandler,
208         CMH::Target: CustomMessageHandler,
209         NS::Target: NodeSigner,
210 {
211         type Target = Self;
212         fn deref(&self) -> &Self { self }
213 }
214
215
216 impl<S: FutureSpawner,
217         Blocks: Deref + Send + Sync + Clone,
218         L: Deref + Send + Sync,
219         Descriptor: SocketDescriptor + Send + Sync,
220         CM: Deref + Send + Sync,
221         OM: Deref + Send + Sync,
222         CMH: Deref + Send + Sync,
223         NS: Deref + Send + Sync,
224 > UtxoLookup for GossipVerifier<S, Blocks, L, Descriptor, CM, OM, CMH, NS> where
225         Blocks::Target: UtxoSource,
226         L::Target: Logger,
227         CM::Target: ChannelMessageHandler,
228         OM::Target: OnionMessageHandler,
229         CMH::Target: CustomMessageHandler,
230         NS::Target: NodeSigner,
231 {
232         fn get_utxo(&self, _genesis_hash: &BlockHash, short_channel_id: u64) -> UtxoResult {
233                 let res = UtxoFuture::new();
234                 let fut = res.clone();
235                 let source = self.source.clone();
236                 let gossiper = Arc::clone(&self.gossiper);
237                 let block_cache = Arc::clone(&self.block_cache);
238                 let pm = Arc::clone(&self.peer_manager);
239                 self.spawn.spawn(async move {
240                         let res = Self::retrieve_utxo(source, block_cache, short_channel_id).await;
241                         fut.resolve(gossiper.network_graph(), &*gossiper, res);
242                         pm.process_events();
243                 });
244                 UtxoResult::Async(res)
245         }
246 }