lightning_block_sync/
init.rs

1//! Utilities to assist in the initial sync required to initialize or reload Rust-Lightning objects
2//! from disk.
3
4use crate::poll::{ChainPoller, Validate, ValidatedBlockHeader};
5use crate::{BlockSource, BlockSourceResult, Cache, ChainNotifier};
6
7use bitcoin::block::Header;
8use bitcoin::hash_types::BlockHash;
9use bitcoin::network::Network;
10
11use lightning::chain;
12
13use std::ops::Deref;
14
15/// Returns a validated block header of the source's best chain tip.
16///
17/// Upon success, the returned header can be used to initialize [`SpvClient`]. Useful during a fresh
18/// start when there are no chain listeners to sync yet.
19///
20/// [`SpvClient`]: crate::SpvClient
21pub async fn validate_best_block_header<B: Deref>(
22	block_source: B,
23) -> BlockSourceResult<ValidatedBlockHeader>
24where
25	B::Target: BlockSource,
26{
27	let (best_block_hash, best_block_height) = block_source.get_best_block().await?;
28	block_source.get_header(&best_block_hash, best_block_height).await?.validate(best_block_hash)
29}
30
31/// Performs a one-time sync of chain listeners using a single *trusted* block source, bringing each
32/// listener's view of the chain from its paired block hash to `block_source`'s best chain tip.
33///
34/// Upon success, the returned header can be used to initialize [`SpvClient`]. In the case of
35/// failure, each listener may be left at a different block hash than the one it was originally
36/// paired with.
37///
38/// Useful during startup to bring the [`ChannelManager`] and each [`ChannelMonitor`] in sync before
39/// switching to [`SpvClient`]. For example:
40///
41/// ```
42/// use bitcoin::hash_types::BlockHash;
43/// use bitcoin::network::Network;
44///
45/// use lightning::chain;
46/// use lightning::chain::Watch;
47/// use lightning::chain::chainmonitor;
48/// use lightning::chain::chainmonitor::ChainMonitor;
49/// use lightning::chain::channelmonitor::ChannelMonitor;
50/// use lightning::chain::chaininterface::BroadcasterInterface;
51/// use lightning::chain::chaininterface::FeeEstimator;
52/// use lightning::ln::channelmanager::{ChannelManager, ChannelManagerReadArgs};
53/// use lightning::onion_message::messenger::MessageRouter;
54/// use lightning::routing::router::Router;
55/// use lightning::sign;
56/// use lightning::sign::{EntropySource, NodeSigner, SignerProvider};
57/// use lightning::util::config::UserConfig;
58/// use lightning::util::logger::Logger;
59/// use lightning::util::ser::ReadableArgs;
60///
61/// use lightning_block_sync::*;
62///
63/// use lightning::io::Cursor;
64///
65/// async fn init_sync<
66/// 	B: BlockSource,
67/// 	ES: EntropySource,
68/// 	NS: NodeSigner,
69/// 	SP: SignerProvider,
70/// 	T: BroadcasterInterface,
71/// 	F: FeeEstimator,
72/// 	R: Router,
73/// 	MR: MessageRouter,
74/// 	L: Logger,
75/// 	C: chain::Filter,
76/// 	P: chainmonitor::Persist<SP::EcdsaSigner>,
77/// >(
78/// 	block_source: &B,
79/// 	chain_monitor: &ChainMonitor<SP::EcdsaSigner, &C, &T, &F, &L, &P>,
80/// 	config: UserConfig,
81/// 	entropy_source: &ES,
82/// 	node_signer: &NS,
83/// 	signer_provider: &SP,
84/// 	tx_broadcaster: &T,
85/// 	fee_estimator: &F,
86/// 	router: &R,
87/// 	message_router: &MR,
88/// 	logger: &L,
89/// 	persister: &P,
90/// ) {
91/// 	// Read a serialized channel monitor paired with the block hash when it was persisted.
92/// 	let serialized_monitor = "...";
93/// 	let (monitor_block_hash, mut monitor) = <(BlockHash, ChannelMonitor<SP::EcdsaSigner>)>::read(
94/// 		&mut Cursor::new(&serialized_monitor), (entropy_source, signer_provider)).unwrap();
95///
96/// 	// Read the channel manager paired with the block hash when it was persisted.
97/// 	let serialized_manager = "...";
98/// 	let (manager_block_hash, mut manager) = {
99/// 		let read_args = ChannelManagerReadArgs::new(
100/// 			entropy_source,
101/// 			node_signer,
102/// 			signer_provider,
103/// 			fee_estimator,
104/// 			chain_monitor,
105/// 			tx_broadcaster,
106/// 			router,
107/// 			message_router,
108/// 			logger,
109/// 			config,
110/// 			vec![&mut monitor],
111/// 		);
112/// 		<(BlockHash, ChannelManager<&ChainMonitor<SP::EcdsaSigner, &C, &T, &F, &L, &P>, &T, &ES, &NS, &SP, &F, &R, &MR, &L>)>::read(
113/// 			&mut Cursor::new(&serialized_manager), read_args).unwrap()
114/// 	};
115///
116/// 	// Synchronize any channel monitors and the channel manager to be on the best block.
117/// 	let mut cache = UnboundedCache::new();
118/// 	let mut monitor_listener = (monitor, &*tx_broadcaster, &*fee_estimator, &*logger);
119/// 	let listeners = vec![
120/// 		(monitor_block_hash, &monitor_listener as &dyn chain::Listen),
121/// 		(manager_block_hash, &manager as &dyn chain::Listen),
122/// 	];
123/// 	let chain_tip = init::synchronize_listeners(
124/// 		block_source, Network::Bitcoin, &mut cache, listeners).await.unwrap();
125///
126/// 	// Allow the chain monitor to watch any channels.
127/// 	let monitor = monitor_listener.0;
128/// 	chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor);
129///
130/// 	// Create an SPV client to notify the chain monitor and channel manager of block events.
131/// 	let chain_poller = poll::ChainPoller::new(block_source, Network::Bitcoin);
132/// 	let mut chain_listener = (chain_monitor, &manager);
133/// 	let spv_client = SpvClient::new(chain_tip, chain_poller, &mut cache, &chain_listener);
134/// }
135/// ```
136///
137/// [`SpvClient`]: crate::SpvClient
138/// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
139/// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
140pub async fn synchronize_listeners<
141	B: Deref + Sized + Send + Sync,
142	C: Cache,
143	L: chain::Listen + ?Sized,
144>(
145	block_source: B, network: Network, header_cache: &mut C,
146	mut chain_listeners: Vec<(BlockHash, &L)>,
147) -> BlockSourceResult<ValidatedBlockHeader>
148where
149	B::Target: BlockSource,
150{
151	let best_header = validate_best_block_header(&*block_source).await?;
152
153	// Fetch the header for the block hash paired with each listener.
154	let mut chain_listeners_with_old_headers = Vec::new();
155	for (old_block_hash, chain_listener) in chain_listeners.drain(..) {
156		let old_header = match header_cache.look_up(&old_block_hash) {
157			Some(header) => *header,
158			None => {
159				block_source.get_header(&old_block_hash, None).await?.validate(old_block_hash)?
160			},
161		};
162		chain_listeners_with_old_headers.push((old_header, chain_listener))
163	}
164
165	// Find differences and disconnect blocks for each listener individually.
166	let mut chain_poller = ChainPoller::new(block_source, network);
167	let mut chain_listeners_at_height = Vec::new();
168	let mut most_common_ancestor = None;
169	let mut most_connected_blocks = Vec::new();
170	for (old_header, chain_listener) in chain_listeners_with_old_headers.drain(..) {
171		// Disconnect any stale blocks, but keep them in the cache for the next iteration.
172		let header_cache = &mut ReadOnlyCache(header_cache);
173		let (common_ancestor, connected_blocks) = {
174			let chain_listener = &DynamicChainListener(chain_listener);
175			let mut chain_notifier = ChainNotifier { header_cache, chain_listener };
176			let difference =
177				chain_notifier.find_difference(best_header, &old_header, &mut chain_poller).await?;
178			chain_notifier.disconnect_blocks(difference.disconnected_blocks);
179			(difference.common_ancestor, difference.connected_blocks)
180		};
181
182		// Keep track of the most common ancestor and all blocks connected across all listeners.
183		chain_listeners_at_height.push((common_ancestor.height, chain_listener));
184		if connected_blocks.len() > most_connected_blocks.len() {
185			most_common_ancestor = Some(common_ancestor);
186			most_connected_blocks = connected_blocks;
187		}
188	}
189
190	// Connect new blocks for all listeners at once to avoid re-fetching blocks.
191	if let Some(common_ancestor) = most_common_ancestor {
192		let chain_listener = &ChainListenerSet(chain_listeners_at_height);
193		let mut chain_notifier = ChainNotifier { header_cache, chain_listener };
194		chain_notifier
195			.connect_blocks(common_ancestor, most_connected_blocks, &mut chain_poller)
196			.await
197			.map_err(|(e, _)| e)?;
198	}
199
200	Ok(best_header)
201}
202
203/// A wrapper to make a cache read-only.
204///
205/// Used to prevent losing headers that may be needed to disconnect blocks common to more than one
206/// listener.
207struct ReadOnlyCache<'a, C: Cache>(&'a mut C);
208
209impl<'a, C: Cache> Cache for ReadOnlyCache<'a, C> {
210	fn look_up(&self, block_hash: &BlockHash) -> Option<&ValidatedBlockHeader> {
211		self.0.look_up(block_hash)
212	}
213
214	fn block_connected(&mut self, _block_hash: BlockHash, _block_header: ValidatedBlockHeader) {
215		unreachable!()
216	}
217
218	fn block_disconnected(&mut self, _block_hash: &BlockHash) -> Option<ValidatedBlockHeader> {
219		None
220	}
221}
222
223/// Wrapper for supporting dynamically sized chain listeners.
224struct DynamicChainListener<'a, L: chain::Listen + ?Sized>(&'a L);
225
226impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L> {
227	fn filtered_block_connected(
228		&self, _header: &Header, _txdata: &chain::transaction::TransactionData, _height: u32,
229	) {
230		unreachable!()
231	}
232
233	fn block_disconnected(&self, header: &Header, height: u32) {
234		self.0.block_disconnected(header, height)
235	}
236}
237
238/// A set of dynamically sized chain listeners, each paired with a starting block height.
239struct ChainListenerSet<'a, L: chain::Listen + ?Sized>(Vec<(u32, &'a L)>);
240
241impl<'a, L: chain::Listen + ?Sized> chain::Listen for ChainListenerSet<'a, L> {
242	fn block_connected(&self, block: &bitcoin::Block, height: u32) {
243		for (starting_height, chain_listener) in self.0.iter() {
244			if height > *starting_height {
245				chain_listener.block_connected(block, height);
246			}
247		}
248	}
249
250	fn filtered_block_connected(
251		&self, header: &Header, txdata: &chain::transaction::TransactionData, height: u32,
252	) {
253		for (starting_height, chain_listener) in self.0.iter() {
254			if height > *starting_height {
255				chain_listener.filtered_block_connected(header, txdata, height);
256			}
257		}
258	}
259
260	fn block_disconnected(&self, _header: &Header, _height: u32) {
261		unreachable!()
262	}
263}
264
265#[cfg(test)]
266mod tests {
267	use super::*;
268	use crate::test_utils::{Blockchain, MockChainListener};
269
270	#[tokio::test]
271	async fn sync_from_same_chain() {
272		let chain = Blockchain::default().with_height(4);
273
274		let listener_1 = MockChainListener::new()
275			.expect_block_connected(*chain.at_height(2))
276			.expect_block_connected(*chain.at_height(3))
277			.expect_block_connected(*chain.at_height(4));
278		let listener_2 = MockChainListener::new()
279			.expect_block_connected(*chain.at_height(3))
280			.expect_block_connected(*chain.at_height(4));
281		let listener_3 = MockChainListener::new().expect_block_connected(*chain.at_height(4));
282
283		let listeners = vec![
284			(chain.at_height(1).block_hash, &listener_1 as &dyn chain::Listen),
285			(chain.at_height(2).block_hash, &listener_2 as &dyn chain::Listen),
286			(chain.at_height(3).block_hash, &listener_3 as &dyn chain::Listen),
287		];
288		let mut cache = chain.header_cache(0..=4);
289		match synchronize_listeners(&chain, Network::Bitcoin, &mut cache, listeners).await {
290			Ok(header) => assert_eq!(header, chain.tip()),
291			Err(e) => panic!("Unexpected error: {:?}", e),
292		}
293	}
294
295	#[tokio::test]
296	async fn sync_from_different_chains() {
297		let main_chain = Blockchain::default().with_height(4);
298		let fork_chain_1 = main_chain.fork_at_height(1);
299		let fork_chain_2 = main_chain.fork_at_height(2);
300		let fork_chain_3 = main_chain.fork_at_height(3);
301
302		let listener_1 = MockChainListener::new()
303			.expect_block_disconnected(*fork_chain_1.at_height(4))
304			.expect_block_disconnected(*fork_chain_1.at_height(3))
305			.expect_block_disconnected(*fork_chain_1.at_height(2))
306			.expect_block_connected(*main_chain.at_height(2))
307			.expect_block_connected(*main_chain.at_height(3))
308			.expect_block_connected(*main_chain.at_height(4));
309		let listener_2 = MockChainListener::new()
310			.expect_block_disconnected(*fork_chain_2.at_height(4))
311			.expect_block_disconnected(*fork_chain_2.at_height(3))
312			.expect_block_connected(*main_chain.at_height(3))
313			.expect_block_connected(*main_chain.at_height(4));
314		let listener_3 = MockChainListener::new()
315			.expect_block_disconnected(*fork_chain_3.at_height(4))
316			.expect_block_connected(*main_chain.at_height(4));
317
318		let listeners = vec![
319			(fork_chain_1.tip().block_hash, &listener_1 as &dyn chain::Listen),
320			(fork_chain_2.tip().block_hash, &listener_2 as &dyn chain::Listen),
321			(fork_chain_3.tip().block_hash, &listener_3 as &dyn chain::Listen),
322		];
323		let mut cache = fork_chain_1.header_cache(2..=4);
324		cache.extend(fork_chain_2.header_cache(3..=4));
325		cache.extend(fork_chain_3.header_cache(4..=4));
326		match synchronize_listeners(&main_chain, Network::Bitcoin, &mut cache, listeners).await {
327			Ok(header) => assert_eq!(header, main_chain.tip()),
328			Err(e) => panic!("Unexpected error: {:?}", e),
329		}
330	}
331
332	#[tokio::test]
333	async fn sync_from_overlapping_chains() {
334		let main_chain = Blockchain::default().with_height(4);
335		let fork_chain_1 = main_chain.fork_at_height(1);
336		let fork_chain_2 = fork_chain_1.fork_at_height(2);
337		let fork_chain_3 = fork_chain_2.fork_at_height(3);
338
339		let listener_1 = MockChainListener::new()
340			.expect_block_disconnected(*fork_chain_1.at_height(4))
341			.expect_block_disconnected(*fork_chain_1.at_height(3))
342			.expect_block_disconnected(*fork_chain_1.at_height(2))
343			.expect_block_connected(*main_chain.at_height(2))
344			.expect_block_connected(*main_chain.at_height(3))
345			.expect_block_connected(*main_chain.at_height(4));
346		let listener_2 = MockChainListener::new()
347			.expect_block_disconnected(*fork_chain_2.at_height(4))
348			.expect_block_disconnected(*fork_chain_2.at_height(3))
349			.expect_block_disconnected(*fork_chain_2.at_height(2))
350			.expect_block_connected(*main_chain.at_height(2))
351			.expect_block_connected(*main_chain.at_height(3))
352			.expect_block_connected(*main_chain.at_height(4));
353		let listener_3 = MockChainListener::new()
354			.expect_block_disconnected(*fork_chain_3.at_height(4))
355			.expect_block_disconnected(*fork_chain_3.at_height(3))
356			.expect_block_disconnected(*fork_chain_3.at_height(2))
357			.expect_block_connected(*main_chain.at_height(2))
358			.expect_block_connected(*main_chain.at_height(3))
359			.expect_block_connected(*main_chain.at_height(4));
360
361		let listeners = vec![
362			(fork_chain_1.tip().block_hash, &listener_1 as &dyn chain::Listen),
363			(fork_chain_2.tip().block_hash, &listener_2 as &dyn chain::Listen),
364			(fork_chain_3.tip().block_hash, &listener_3 as &dyn chain::Listen),
365		];
366		let mut cache = fork_chain_1.header_cache(2..=4);
367		cache.extend(fork_chain_2.header_cache(3..=4));
368		cache.extend(fork_chain_3.header_cache(4..=4));
369		match synchronize_listeners(&main_chain, Network::Bitcoin, &mut cache, listeners).await {
370			Ok(header) => assert_eq!(header, main_chain.tip()),
371			Err(e) => panic!("Unexpected error: {:?}", e),
372		}
373	}
374
375	#[tokio::test]
376	async fn cache_connected_and_keep_disconnected_blocks() {
377		let main_chain = Blockchain::default().with_height(2);
378		let fork_chain = main_chain.fork_at_height(1);
379		let new_tip = main_chain.tip();
380		let old_tip = fork_chain.tip();
381
382		let listener = MockChainListener::new()
383			.expect_block_disconnected(*old_tip)
384			.expect_block_connected(*new_tip);
385
386		let listeners = vec![(old_tip.block_hash, &listener as &dyn chain::Listen)];
387		let mut cache = fork_chain.header_cache(2..=2);
388		match synchronize_listeners(&main_chain, Network::Bitcoin, &mut cache, listeners).await {
389			Ok(_) => {
390				assert!(cache.contains_key(&new_tip.block_hash));
391				assert!(cache.contains_key(&old_tip.block_hash));
392			},
393			Err(e) => panic!("Unexpected error: {:?}", e),
394		}
395	}
396}