Skip to content

Commit 6b94af9

Browse files
committed
Drop the need for fork headers when calling Listen's disconnect
The `Listen::block_disconnected` method is nice in that listeners learn about each block disconnected in series. Further, it included the header of the block that is being disconnected to allow the listeners to do some checking that the interface is being used correctly (namely, asserting that the header's block hash matches their current understanding of the best chain). However, this interface has some substantial drawbacks. Namely, the requirement that fork headers be passed in means that restarting with a new node that has no idea about a previous fork leaves us unable to replay the chain at all. Further, while when various listeners were initially written learning about each block disconnected in series seemed useful, but now we no longer rely on that anyway because the `Confirm` interface does not allow for it. Thus, here, we replace `Listen::block_disconnected` with a new `Listen::blocks_disconnected`, taking only information about the fork point/new best chain tip (in the form of its block hash and height) rather than information about previous fork blocks and only requiring a single call to complete multiple block disconnections during a reorg. This requires removing some assertions on block disconnection ordering, but because we now provide `lightning-block-sync` and expect users to use it when using the `Listen` interface, these assertions are much less critical.
1 parent 9db0fb2 commit 6b94af9

File tree

11 files changed

+74
-114
lines changed

11 files changed

+74
-114
lines changed

fuzz/src/full_stack.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,8 @@ impl<'a> MoneyLossDetector<'a> {
344344
self.header_hashes[self.height - 1].0,
345345
self.header_hashes[self.height].1,
346346
);
347-
self.manager.block_disconnected(&header, self.height as u32);
348-
self.monitor.block_disconnected(&header, self.height as u32);
347+
self.manager.blocks_disconnected(header.prev_blockhash, self.height as u32 - 1);
348+
self.monitor.blocks_disconnected(header.prev_blockhash, self.height as u32 - 1);
349349
self.height -= 1;
350350
let removal_height = self.height;
351351
self.txids_confirmed.retain(|_, height| removal_height != *height);

lightning-block-sync/src/init.rs

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L
230230
unreachable!()
231231
}
232232

233-
fn block_disconnected(&self, header: &Header, height: u32) {
234-
self.0.block_disconnected(header, height)
233+
fn blocks_disconnected(&self, new_best_block: BlockHash, new_best_height: u32) {
234+
self.0.blocks_disconnected(new_best_block, new_best_height)
235235
}
236236
}
237237

@@ -257,7 +257,7 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for ChainListenerSet<'a, L> {
257257
}
258258
}
259259

260-
fn block_disconnected(&self, _header: &Header, _height: u32) {
260+
fn blocks_disconnected(&self, _new_best_block: BlockHash, _new_best_height: u32) {
261261
unreachable!()
262262
}
263263
}
@@ -300,19 +300,16 @@ mod tests {
300300
let fork_chain_3 = main_chain.fork_at_height(3);
301301

302302
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))
303+
.expect_blocks_disconnected(*fork_chain_1.at_height(2))
306304
.expect_block_connected(*main_chain.at_height(2))
307305
.expect_block_connected(*main_chain.at_height(3))
308306
.expect_block_connected(*main_chain.at_height(4));
309307
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))
308+
.expect_blocks_disconnected(*fork_chain_2.at_height(3))
312309
.expect_block_connected(*main_chain.at_height(3))
313310
.expect_block_connected(*main_chain.at_height(4));
314311
let listener_3 = MockChainListener::new()
315-
.expect_block_disconnected(*fork_chain_3.at_height(4))
312+
.expect_blocks_disconnected(*fork_chain_3.at_height(4))
316313
.expect_block_connected(*main_chain.at_height(4));
317314

318315
let listeners = vec![
@@ -337,23 +334,17 @@ mod tests {
337334
let fork_chain_3 = fork_chain_2.fork_at_height(3);
338335

339336
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))
337+
.expect_blocks_disconnected(*fork_chain_1.at_height(2))
343338
.expect_block_connected(*main_chain.at_height(2))
344339
.expect_block_connected(*main_chain.at_height(3))
345340
.expect_block_connected(*main_chain.at_height(4));
346341
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))
342+
.expect_blocks_disconnected(*fork_chain_2.at_height(2))
350343
.expect_block_connected(*main_chain.at_height(2))
351344
.expect_block_connected(*main_chain.at_height(3))
352345
.expect_block_connected(*main_chain.at_height(4));
353346
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))
347+
.expect_blocks_disconnected(*fork_chain_3.at_height(2))
357348
.expect_block_connected(*main_chain.at_height(2))
358349
.expect_block_connected(*main_chain.at_height(3))
359350
.expect_block_connected(*main_chain.at_height(4));
@@ -380,7 +371,7 @@ mod tests {
380371
let old_tip = fork_chain.tip();
381372

382373
let listener = MockChainListener::new()
383-
.expect_block_disconnected(*old_tip)
374+
.expect_blocks_disconnected(*old_tip)
384375
.expect_block_connected(*new_tip);
385376

386377
let listeners = vec![(old_tip.block_hash, &listener as &dyn chain::Listen)];

lightning-block-sync/src/lib.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -398,12 +398,14 @@ where
398398
}
399399

400400
/// Notifies the chain listeners of disconnected blocks.
401-
fn disconnect_blocks(&mut self, mut disconnected_blocks: Vec<ValidatedBlockHeader>) {
402-
for header in disconnected_blocks.drain(..) {
401+
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
402+
for header in disconnected_blocks.iter() {
403403
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {
404-
assert_eq!(cached_header, header);
404+
assert_eq!(cached_header, *header);
405405
}
406-
self.chain_listener.block_disconnected(&header.header, header.height);
406+
}
407+
if let Some(block) = disconnected_blocks.last() {
408+
self.chain_listener.blocks_disconnected(block.header.prev_blockhash, block.height - 1);
407409
}
408410
}
409411

@@ -615,7 +617,7 @@ mod chain_notifier_tests {
615617
let new_tip = fork_chain.tip();
616618
let old_tip = main_chain.tip();
617619
let chain_listener = &MockChainListener::new()
618-
.expect_block_disconnected(*old_tip)
620+
.expect_blocks_disconnected(*old_tip)
619621
.expect_block_connected(*new_tip);
620622
let mut notifier =
621623
ChainNotifier { header_cache: &mut main_chain.header_cache(0..=2), chain_listener };
@@ -635,8 +637,7 @@ mod chain_notifier_tests {
635637
let new_tip = fork_chain.tip();
636638
let old_tip = main_chain.tip();
637639
let chain_listener = &MockChainListener::new()
638-
.expect_block_disconnected(*old_tip)
639-
.expect_block_disconnected(*main_chain.at_height(2))
640+
.expect_blocks_disconnected(*main_chain.at_height(2))
640641
.expect_block_connected(*new_tip);
641642
let mut notifier =
642643
ChainNotifier { header_cache: &mut main_chain.header_cache(0..=3), chain_listener };
@@ -656,7 +657,7 @@ mod chain_notifier_tests {
656657
let new_tip = fork_chain.tip();
657658
let old_tip = main_chain.tip();
658659
let chain_listener = &MockChainListener::new()
659-
.expect_block_disconnected(*old_tip)
660+
.expect_blocks_disconnected(*old_tip)
660661
.expect_block_connected(*fork_chain.at_height(2))
661662
.expect_block_connected(*new_tip);
662663
let mut notifier =

lightning-block-sync/src/test_utils.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl chain::Listen for NullChainListener {
203203
&self, _header: &Header, _txdata: &chain::transaction::TransactionData, _height: u32,
204204
) {
205205
}
206-
fn block_disconnected(&self, _header: &Header, _height: u32) {}
206+
fn blocks_disconnected(&self, _new_best_block: BlockHash, _new_best_height: u32) {}
207207
}
208208

209209
pub struct MockChainListener {
@@ -231,7 +231,7 @@ impl MockChainListener {
231231
self
232232
}
233233

234-
pub fn expect_block_disconnected(self, block: BlockHeaderData) -> Self {
234+
pub fn expect_blocks_disconnected(self, block: BlockHeaderData) -> Self {
235235
self.expected_blocks_disconnected.borrow_mut().push_back(block);
236236
self
237237
}
@@ -264,14 +264,14 @@ impl chain::Listen for MockChainListener {
264264
}
265265
}
266266

267-
fn block_disconnected(&self, header: &Header, height: u32) {
267+
fn blocks_disconnected(&self, new_best_block: BlockHash, new_height: u32) {
268268
match self.expected_blocks_disconnected.borrow_mut().pop_front() {
269269
None => {
270-
panic!("Unexpected block disconnected: {:?}", header.block_hash());
270+
panic!("Unexpected disconnect to {} at height {}", new_best_block, new_height);
271271
},
272272
Some(expected_block) => {
273-
assert_eq!(header.block_hash(), expected_block.header.block_hash());
274-
assert_eq!(height, expected_block.height);
273+
assert_eq!(new_best_block, expected_block.header.prev_blockhash);
274+
assert_eq!(new_height, expected_block.height - 1);
275275
},
276276
}
277277
}

lightning-liquidity/src/manager.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -583,14 +583,9 @@ where
583583
self.best_block_updated(header, height);
584584
}
585585

586-
fn block_disconnected(&self, header: &bitcoin::block::Header, height: u32) {
587-
let new_height = height - 1;
586+
fn blocks_disconnected(&self, new_best_block: bitcoin::BlockHash, new_best_height: u32) {
588587
if let Some(best_block) = self.best_block.write().unwrap().as_mut() {
589-
assert_eq!(best_block.block_hash, header.block_hash(),
590-
"Blocks must be disconnected in chain-order - the disconnected header must be the last connected header");
591-
assert_eq!(best_block.height, height,
592-
"Blocks must be disconnected in chain-order - the disconnected block must have the correct height");
593-
*best_block = BestBlock::new(header.prev_blockhash, new_height)
588+
*best_block = BestBlock::new(new_best_block, new_best_height)
594589
}
595590

596591
// TODO: Call block_disconnected on all sub-modules that require it, e.g., LSPS1MessageHandler.

lightning/src/chain/chainmonitor.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -910,18 +910,18 @@ where
910910
self.event_notifier.notify();
911911
}
912912

913-
fn block_disconnected(&self, header: &Header, height: u32) {
913+
fn blocks_disconnected(&self, new_best_block: BlockHash, new_best_height: u32) {
914914
let monitor_states = self.monitors.read().unwrap();
915915
log_debug!(
916916
self.logger,
917-
"Latest block {} at height {} removed via block_disconnected",
918-
header.block_hash(),
919-
height
917+
"Block(s) removed to height {} via blocks_disconnected. New best block is {}",
918+
new_best_height,
919+
new_best_block,
920920
);
921921
for monitor_state in monitor_states.values() {
922-
monitor_state.monitor.block_disconnected(
923-
header,
924-
height,
922+
monitor_state.monitor.blocks_disconnected(
923+
new_best_block,
924+
new_best_height,
925925
&*self.broadcaster,
926926
&*self.fee_estimator,
927927
&self.logger,

lightning/src/chain/channelmonitor.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,13 +2099,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
20992099

21002100
/// Determines if the disconnected block contained any transactions of interest and updates
21012101
/// appropriately.
2102-
#[rustfmt::skip]
2103-
pub fn block_disconnected<B: Deref, F: Deref, L: Deref>(
2104-
&self,
2105-
header: &Header,
2106-
height: u32,
2107-
broadcaster: B,
2108-
fee_estimator: F,
2102+
pub fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
2103+
&self, new_best_block: BlockHash, new_height: u32, broadcaster: B, fee_estimator: F,
21092104
logger: &L,
21102105
) where
21112106
B::Target: BroadcasterInterface,
@@ -2114,8 +2109,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
21142109
{
21152110
let mut inner = self.inner.lock().unwrap();
21162111
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
2117-
inner.block_disconnected(
2118-
header, height, broadcaster, fee_estimator, &logger)
2112+
inner.blocks_disconnected(new_best_block, new_height, broadcaster, fee_estimator, &logger)
21192113
}
21202114

21212115
/// Processes transactions confirmed in a block with the given header and height, returning new
@@ -2149,10 +2143,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
21492143

21502144
/// Processes a transaction that was reorganized out of the chain.
21512145
///
2152-
/// Used instead of [`block_disconnected`] by clients that are notified of transactions rather
2146+
/// Used instead of [`blocks_disconnected`] by clients that are notified of transactions rather
21532147
/// than blocks. See [`chain::Confirm`] for calling expectations.
21542148
///
2155-
/// [`block_disconnected`]: Self::block_disconnected
2149+
/// [`blocks_disconnected`]: Self::blocks_disconnected
21562150
#[rustfmt::skip]
21572151
pub fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
21582152
&self,
@@ -4591,12 +4585,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45914585
!unmatured_htlcs.contains(&&source),
45924586
"An unmature HTLC transaction conflicts with a maturing one; failed to \
45934587
call either transaction_unconfirmed for the conflicting transaction \
4594-
or block_disconnected for a block containing it.");
4588+
or blocks_disconnected for a block containing it.");
45954589
debug_assert!(
45964590
!matured_htlcs.contains(&source),
45974591
"A matured HTLC transaction conflicts with a maturing one; failed to \
45984592
call either transaction_unconfirmed for the conflicting transaction \
4599-
or block_disconnected for a block containing it.");
4593+
or blocks_disconnected for a block containing it.");
46004594
matured_htlcs.push(source.clone());
46014595
}
46024596

@@ -4734,26 +4728,26 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
47344728
}
47354729

47364730
#[rustfmt::skip]
4737-
fn block_disconnected<B: Deref, F: Deref, L: Deref>(
4738-
&mut self, header: &Header, height: u32, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
4731+
fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
4732+
&mut self, new_best_block: BlockHash, new_height: u32, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
47394733
) where B::Target: BroadcasterInterface,
47404734
F::Target: FeeEstimator,
47414735
L::Target: Logger,
47424736
{
4743-
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
4737+
log_trace!(logger, "Block(s) disconnected to height {}", new_height);
47444738

47454739
//We may discard:
47464740
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
47474741
//- maturing spendable output has transaction paying us has been disconnected
4748-
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height);
4742+
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= new_height);
47494743

47504744
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
47514745
let conf_target = self.closure_conf_target();
47524746
self.onchain_tx_handler.block_disconnected(
4753-
height, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger
4747+
new_height + 1, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger
47544748
);
47554749

4756-
self.best_block = BestBlock::new(header.prev_blockhash, height - 1);
4750+
self.best_block = BestBlock::new(new_best_block, new_height);
47574751
}
47584752

47594753
#[rustfmt::skip]
@@ -5198,8 +5192,8 @@ where
51985192
self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &self.3);
51995193
}
52005194

5201-
fn block_disconnected(&self, header: &Header, height: u32) {
5202-
self.0.block_disconnected(header, height, &*self.1, &*self.2, &self.3);
5195+
fn blocks_disconnected(&self, new_best_block: BlockHash, new_best_height: u32) {
5196+
self.0.blocks_disconnected(new_best_block, new_best_height, &*self.1, &*self.2, &self.3);
52035197
}
52045198
}
52055199

lightning/src/chain/mod.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ pub trait Listen {
8484
self.filtered_block_connected(&block.header, &txdata, height);
8585
}
8686

87-
/// Notifies the listener that a block was removed at the given height.
88-
fn block_disconnected(&self, header: &Header, height: u32);
87+
/// Notifies the listener that one or more blocks were removed in anticipation of a reorg.
88+
///
89+
/// Indicates the new best tip is `new_best_block` at height `disconnected_height` - 1.
90+
fn blocks_disconnected(&self, new_best_block: BlockHash, disconnected_height: u32);
8991
}
9092

9193
/// The `Confirm` trait is used to notify LDK when relevant transactions have been confirmed on
@@ -272,7 +274,7 @@ pub trait Watch<ChannelSigner: EcdsaChannelSigner> {
272274
///
273275
/// Implementations are responsible for watching the chain for the funding transaction along
274276
/// with any spends of outputs returned by [`get_outputs_to_watch`]. In practice, this means
275-
/// calling [`block_connected`] and [`block_disconnected`] on the monitor.
277+
/// calling [`block_connected`] and [`blocks_disconnected`] on the monitor.
276278
///
277279
/// A return of `Err(())` indicates that the channel should immediately be force-closed without
278280
/// broadcasting the funding transaction.
@@ -282,7 +284,7 @@ pub trait Watch<ChannelSigner: EcdsaChannelSigner> {
282284
///
283285
/// [`get_outputs_to_watch`]: channelmonitor::ChannelMonitor::get_outputs_to_watch
284286
/// [`block_connected`]: channelmonitor::ChannelMonitor::block_connected
285-
/// [`block_disconnected`]: channelmonitor::ChannelMonitor::block_disconnected
287+
/// [`blocks_disconnected`]: channelmonitor::ChannelMonitor::blocks_disconnected
286288
fn watch_channel(
287289
&self, channel_id: ChannelId, monitor: ChannelMonitor<ChannelSigner>,
288290
) -> Result<ChannelMonitorUpdateStatus, ()>;
@@ -393,8 +395,8 @@ impl<T: Listen> Listen for dyn core::ops::Deref<Target = T> {
393395
(**self).filtered_block_connected(header, txdata, height);
394396
}
395397

396-
fn block_disconnected(&self, header: &Header, height: u32) {
397-
(**self).block_disconnected(header, height);
398+
fn blocks_disconnected(&self, new_best_block: BlockHash, new_best_height: u32) {
399+
(**self).blocks_disconnected(new_best_block, new_best_height);
398400
}
399401
}
400402

@@ -408,9 +410,9 @@ where
408410
self.1.filtered_block_connected(header, txdata, height);
409411
}
410412

411-
fn block_disconnected(&self, header: &Header, height: u32) {
412-
self.0.block_disconnected(header, height);
413-
self.1.block_disconnected(header, height);
413+
fn blocks_disconnected(&self, new_best_block: BlockHash, new_best_height: u32) {
414+
self.0.blocks_disconnected(new_best_block, new_best_height);
415+
self.1.blocks_disconnected(new_best_block, new_best_height);
414416
}
415417
}
416418

0 commit comments

Comments
 (0)