Skip to content

Commit 7ae4f68

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. We also swap to using a single `BestBlock` to describe the new chain tip, in anticipation of future extensions to `BestBlock`. 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 7ae4f68

File tree

11 files changed

+84
-117
lines changed

11 files changed

+84
-117
lines changed

fuzz/src/full_stack.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,9 @@ 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+
let best_block = BestBlock::new(header.prev_blockhash, self.height as u32 - 1);
348+
self.manager.blocks_disconnected(best_block);
349+
self.monitor.blocks_disconnected(best_block);
349350
self.height -= 1;
350351
let removal_height = self.height;
351352
self.txids_confirmed.retain(|_, height| removal_height != *height);

lightning-block-sync/src/init.rs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use bitcoin::hash_types::BlockHash;
99
use bitcoin::network::Network;
1010

1111
use lightning::chain;
12+
use lightning::chain::BestBlock;
1213

1314
use std::ops::Deref;
1415

@@ -230,8 +231,8 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L
230231
unreachable!()
231232
}
232233

233-
fn block_disconnected(&self, header: &Header, height: u32) {
234-
self.0.block_disconnected(header, height)
234+
fn blocks_disconnected(&self, new_best_block: BestBlock) {
235+
self.0.blocks_disconnected(new_best_block)
235236
}
236237
}
237238

@@ -257,7 +258,7 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for ChainListenerSet<'a, L> {
257258
}
258259
}
259260

260-
fn block_disconnected(&self, _header: &Header, _height: u32) {
261+
fn blocks_disconnected(&self, _new_best_block: BestBlock) {
261262
unreachable!()
262263
}
263264
}
@@ -300,19 +301,16 @@ mod tests {
300301
let fork_chain_3 = main_chain.fork_at_height(3);
301302

302303
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))
304+
.expect_blocks_disconnected(*fork_chain_1.at_height(2))
306305
.expect_block_connected(*main_chain.at_height(2))
307306
.expect_block_connected(*main_chain.at_height(3))
308307
.expect_block_connected(*main_chain.at_height(4));
309308
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))
309+
.expect_blocks_disconnected(*fork_chain_2.at_height(3))
312310
.expect_block_connected(*main_chain.at_height(3))
313311
.expect_block_connected(*main_chain.at_height(4));
314312
let listener_3 = MockChainListener::new()
315-
.expect_block_disconnected(*fork_chain_3.at_height(4))
313+
.expect_blocks_disconnected(*fork_chain_3.at_height(4))
316314
.expect_block_connected(*main_chain.at_height(4));
317315

318316
let listeners = vec![
@@ -337,23 +335,17 @@ mod tests {
337335
let fork_chain_3 = fork_chain_2.fork_at_height(3);
338336

339337
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))
338+
.expect_blocks_disconnected(*fork_chain_1.at_height(2))
343339
.expect_block_connected(*main_chain.at_height(2))
344340
.expect_block_connected(*main_chain.at_height(3))
345341
.expect_block_connected(*main_chain.at_height(4));
346342
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))
343+
.expect_blocks_disconnected(*fork_chain_2.at_height(2))
350344
.expect_block_connected(*main_chain.at_height(2))
351345
.expect_block_connected(*main_chain.at_height(3))
352346
.expect_block_connected(*main_chain.at_height(4));
353347
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))
348+
.expect_blocks_disconnected(*fork_chain_3.at_height(2))
357349
.expect_block_connected(*main_chain.at_height(2))
358350
.expect_block_connected(*main_chain.at_height(3))
359351
.expect_block_connected(*main_chain.at_height(4));
@@ -380,7 +372,7 @@ mod tests {
380372
let old_tip = fork_chain.tip();
381373

382374
let listener = MockChainListener::new()
383-
.expect_block_disconnected(*old_tip)
375+
.expect_blocks_disconnected(*old_tip)
384376
.expect_block_connected(*new_tip);
385377

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

lightning-block-sync/src/lib.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use bitcoin::hash_types::BlockHash;
4949
use bitcoin::pow::Work;
5050

5151
use lightning::chain;
52-
use lightning::chain::Listen;
52+
use lightning::chain::{BestBlock, Listen};
5353

5454
use std::future::Future;
5555
use std::ops::Deref;
@@ -398,12 +398,15 @@ 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+
let best_block = BestBlock::new(block.header.prev_blockhash, block.height - 1);
409+
self.chain_listener.blocks_disconnected(best_block);
407410
}
408411
}
409412

@@ -615,7 +618,7 @@ mod chain_notifier_tests {
615618
let new_tip = fork_chain.tip();
616619
let old_tip = main_chain.tip();
617620
let chain_listener = &MockChainListener::new()
618-
.expect_block_disconnected(*old_tip)
621+
.expect_blocks_disconnected(*old_tip)
619622
.expect_block_connected(*new_tip);
620623
let mut notifier =
621624
ChainNotifier { header_cache: &mut main_chain.header_cache(0..=2), chain_listener };
@@ -635,8 +638,7 @@ mod chain_notifier_tests {
635638
let new_tip = fork_chain.tip();
636639
let old_tip = main_chain.tip();
637640
let chain_listener = &MockChainListener::new()
638-
.expect_block_disconnected(*old_tip)
639-
.expect_block_disconnected(*main_chain.at_height(2))
641+
.expect_blocks_disconnected(*main_chain.at_height(2))
640642
.expect_block_connected(*new_tip);
641643
let mut notifier =
642644
ChainNotifier { header_cache: &mut main_chain.header_cache(0..=3), chain_listener };
@@ -656,7 +658,7 @@ mod chain_notifier_tests {
656658
let new_tip = fork_chain.tip();
657659
let old_tip = main_chain.tip();
658660
let chain_listener = &MockChainListener::new()
659-
.expect_block_disconnected(*old_tip)
661+
.expect_blocks_disconnected(*old_tip)
660662
.expect_block_connected(*fork_chain.at_height(2))
661663
.expect_block_connected(*new_tip);
662664
let mut notifier =

lightning-block-sync/src/test_utils.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use bitcoin::transaction;
1313
use bitcoin::Transaction;
1414

1515
use lightning::chain;
16+
use lightning::chain::BestBlock;
1617

1718
use std::cell::RefCell;
1819
use std::collections::VecDeque;
@@ -203,7 +204,7 @@ impl chain::Listen for NullChainListener {
203204
&self, _header: &Header, _txdata: &chain::transaction::TransactionData, _height: u32,
204205
) {
205206
}
206-
fn block_disconnected(&self, _header: &Header, _height: u32) {}
207+
fn blocks_disconnected(&self, _new_best_block: BestBlock) {}
207208
}
208209

209210
pub struct MockChainListener {
@@ -231,7 +232,7 @@ impl MockChainListener {
231232
self
232233
}
233234

234-
pub fn expect_block_disconnected(self, block: BlockHeaderData) -> Self {
235+
pub fn expect_blocks_disconnected(self, block: BlockHeaderData) -> Self {
235236
self.expected_blocks_disconnected.borrow_mut().push_back(block);
236237
self
237238
}
@@ -264,14 +265,17 @@ impl chain::Listen for MockChainListener {
264265
}
265266
}
266267

267-
fn block_disconnected(&self, header: &Header, height: u32) {
268+
fn blocks_disconnected(&self, new_best_block: BestBlock) {
268269
match self.expected_blocks_disconnected.borrow_mut().pop_front() {
269270
None => {
270-
panic!("Unexpected block disconnected: {:?}", header.block_hash());
271+
panic!(
272+
"Unexpected block(s) disconnect to {} at height {}",
273+
new_best_block.block_hash, new_best_block.height,
274+
);
271275
},
272276
Some(expected_block) => {
273-
assert_eq!(header.block_hash(), expected_block.header.block_hash());
274-
assert_eq!(height, expected_block.height);
277+
assert_eq!(new_best_block.block_hash, expected_block.header.prev_blockhash);
278+
assert_eq!(new_best_block.height, expected_block.height - 1);
275279
},
276280
}
277281
}

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: BestBlock) {
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 = new_best_block;
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 & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::chain::channelmonitor::{
3333
WithChannelMonitor,
3434
};
3535
use crate::chain::transaction::{OutPoint, TransactionData};
36-
use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput};
36+
use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Filter, WatchedOutput};
3737
use crate::events::{self, Event, EventHandler, ReplayEvent};
3838
use crate::ln::channel_state::ChannelDetails;
3939
use crate::ln::msgs::{self, BaseMessageHandler, Init, MessageSendEvent};
@@ -910,18 +910,17 @@ 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: BestBlock) {
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_block.height,
919+
new_best_block.block_hash,
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,
925924
&*self.broadcaster,
926925
&*self.fee_estimator,
927926
&self.logger,

lightning/src/chain/channelmonitor.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,23 +2099,16 @@ 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,
2109-
logger: &L,
2102+
pub fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
2103+
&self, new_best_block: BestBlock, broadcaster: B, fee_estimator: F, logger: &L,
21102104
) where
21112105
B::Target: BroadcasterInterface,
21122106
F::Target: FeeEstimator,
21132107
L::Target: Logger,
21142108
{
21152109
let mut inner = self.inner.lock().unwrap();
21162110
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
2117-
inner.block_disconnected(
2118-
header, height, broadcaster, fee_estimator, &logger)
2111+
inner.blocks_disconnected(new_best_block, broadcaster, fee_estimator, &logger)
21192112
}
21202113

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

21502143
/// Processes a transaction that was reorganized out of the chain.
21512144
///
2152-
/// Used instead of [`block_disconnected`] by clients that are notified of transactions rather
2145+
/// Used instead of [`blocks_disconnected`] by clients that are notified of transactions rather
21532146
/// than blocks. See [`chain::Confirm`] for calling expectations.
21542147
///
2155-
/// [`block_disconnected`]: Self::block_disconnected
2148+
/// [`blocks_disconnected`]: Self::blocks_disconnected
21562149
#[rustfmt::skip]
21572150
pub fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
21582151
&self,
@@ -4591,12 +4584,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45914584
!unmatured_htlcs.contains(&&source),
45924585
"An unmature HTLC transaction conflicts with a maturing one; failed to \
45934586
call either transaction_unconfirmed for the conflicting transaction \
4594-
or block_disconnected for a block containing it.");
4587+
or blocks_disconnected for a block containing it.");
45954588
debug_assert!(
45964589
!matured_htlcs.contains(&source),
45974590
"A matured HTLC transaction conflicts with a maturing one; failed to \
45984591
call either transaction_unconfirmed for the conflicting transaction \
4599-
or block_disconnected for a block containing it.");
4592+
or blocks_disconnected for a block containing it.");
46004593
matured_htlcs.push(source.clone());
46014594
}
46024595

@@ -4734,26 +4727,27 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
47344727
}
47354728

47364729
#[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>
4730+
fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
4731+
&mut self, new_best_block: BestBlock, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
47394732
) where B::Target: BroadcasterInterface,
47404733
F::Target: FeeEstimator,
47414734
L::Target: Logger,
47424735
{
4743-
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
4736+
let new_height = new_best_block.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 = new_best_block;
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: BestBlock) {
5196+
self.0.blocks_disconnected(new_best_block, &*self.1, &*self.2, &self.3);
52035197
}
52045198
}
52055199

0 commit comments

Comments
 (0)