Skip to content

Commit 0503a55

Browse files
committed
bitcoind RPC: Skip validating header chain for newly setup nodes
When we just setup a new node, all chain listeners will still be synced up to the genesis hash. In order to allow for faster startup, we skip validating the entirety of the header chain by continuing with the current best block polled from `bitcoind`. Note that this doesn't noticeably change our security model as we're trusting the chain data source, for example to not censor us, anyways. And, we're of course still validating the PoW for the best block we're polling.
1 parent c605781 commit 0503a55

File tree

1 file changed

+68
-34
lines changed

1 file changed

+68
-34
lines changed

src/chain/mod.rs

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -273,43 +273,76 @@ impl ChainSource {
273273
}
274274
}
275275

276-
let channel_manager_best_block_hash =
276+
let mut channel_manager_best_block_hash =
277277
channel_manager.current_best_block().block_hash;
278-
let sweeper_best_block_hash = output_sweeper.current_best_block().block_hash;
279-
let onchain_wallet_best_block_hash = onchain_wallet.current_best_block().block_hash;
280-
281-
let mut chain_listeners = vec![
282-
(
283-
onchain_wallet_best_block_hash,
284-
&**onchain_wallet as &(dyn Listen + Send + Sync),
285-
),
286-
(
287-
channel_manager_best_block_hash,
288-
&*channel_manager as &(dyn Listen + Send + Sync),
289-
),
290-
(sweeper_best_block_hash, &*output_sweeper as &(dyn Listen + Send + Sync)),
291-
];
278+
let mut sweeper_best_block_hash = output_sweeper.current_best_block().block_hash;
279+
let mut onchain_wallet_best_block_hash =
280+
onchain_wallet.current_best_block().block_hash;
292281

293-
// TODO: Eventually we might want to see if we can synchronize `ChannelMonitor`s
294-
// before giving them to `ChainMonitor` it the first place. However, this isn't
295-
// trivial as we load them on initialization (in the `Builder`) and only gain
296-
// network access during `start`. For now, we just make sure we get the worst known
297-
// block hash and sychronize them via `ChainMonitor`.
298-
if let Some(worst_channel_monitor_block_hash) = chain_monitor
299-
.list_monitors()
300-
.iter()
301-
.flat_map(|(txo, _)| chain_monitor.get_monitor(*txo))
302-
.map(|m| m.current_best_block())
303-
.min_by_key(|b| b.height)
304-
.map(|b| b.block_hash)
305-
{
306-
chain_listeners.push((
307-
worst_channel_monitor_block_hash,
308-
&*chain_monitor as &(dyn Listen + Send + Sync),
309-
));
310-
}
282+
let genesis_block_hash =
283+
bitcoin::blockdata::constants::genesis_block(config.network).block_hash();
284+
285+
'initial_listener_sync: loop {
286+
if channel_manager_best_block_hash == genesis_block_hash
287+
&& sweeper_best_block_hash == genesis_block_hash
288+
&& onchain_wallet_best_block_hash == genesis_block_hash
289+
{
290+
// If all chain listeners are still at the genesis we're positive this is a
291+
// newly setup node that was never synced to tip before. So we query the best
292+
// tip and continue with it, avoiding to validate the entire header chain back to genesis.
293+
let chain_tip =
294+
match validate_best_block_header(bitcoind_rpc_client.as_ref()).await {
295+
Ok(tip) => {
296+
*latest_chain_tip.write().unwrap() = Some(tip);
297+
tip
298+
},
299+
Err(e) => {
300+
log_error!(logger, "Failed to poll for chain data: {:?}", e);
301+
tokio::time::sleep(Duration::from_secs(
302+
CHAIN_POLLING_INTERVAL_SECS,
303+
))
304+
.await;
305+
continue 'initial_listener_sync;
306+
},
307+
};
308+
309+
let tip_hash = chain_tip.to_best_block().block_hash;
310+
channel_manager_best_block_hash = tip_hash;
311+
sweeper_best_block_hash = tip_hash;
312+
onchain_wallet_best_block_hash = tip_hash;
313+
}
314+
315+
let mut chain_listeners = vec![
316+
(
317+
onchain_wallet_best_block_hash,
318+
&**onchain_wallet as &(dyn Listen + Send + Sync),
319+
),
320+
(
321+
channel_manager_best_block_hash,
322+
&*channel_manager as &(dyn Listen + Send + Sync),
323+
),
324+
(sweeper_best_block_hash, &*output_sweeper as &(dyn Listen + Send + Sync)),
325+
];
326+
327+
// TODO: Eventually we might want to see if we can synchronize `ChannelMonitor`s
328+
// before giving them to `ChainMonitor` it the first place. However, this isn't
329+
// trivial as we load them on initialization (in the `Builder`) and only gain
330+
// network access during `start`. For now, we just make sure we get the worst known
331+
// block hash and sychronize them via `ChainMonitor`.
332+
if let Some(worst_channel_monitor_block_hash) = chain_monitor
333+
.list_monitors()
334+
.iter()
335+
.flat_map(|(txo, _)| chain_monitor.get_monitor(*txo))
336+
.map(|m| m.current_best_block())
337+
.min_by_key(|b| b.height)
338+
.map(|b| b.block_hash)
339+
{
340+
chain_listeners.push((
341+
worst_channel_monitor_block_hash,
342+
&*chain_monitor as &(dyn Listen + Send + Sync),
343+
));
344+
}
311345

312-
loop {
313346
let mut locked_header_cache = header_cache.lock().await;
314347
match synchronize_listeners(
315348
bitcoind_rpc_client.as_ref(),
@@ -347,6 +380,7 @@ impl ChainSource {
347380
log_error!(logger, "Failed to synchronize chain listeners: {:?}", e);
348381
tokio::time::sleep(Duration::from_secs(CHAIN_POLLING_INTERVAL_SECS))
349382
.await;
383+
continue 'initial_listener_sync;
350384
},
351385
}
352386
}

0 commit comments

Comments
 (0)