Skip to content

Commit f8cfa51

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 95ca0dc commit f8cfa51

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) disconnected {} 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, SendOnlyMessageHandler};
@@ -929,18 +929,17 @@ where
929929
self.event_notifier.notify();
930930
}
931931

932-
fn block_disconnected(&self, header: &Header, height: u32) {
932+
fn blocks_disconnected(&self, new_best_block: BestBlock) {
933933
let monitor_states = self.monitors.read().unwrap();
934934
log_debug!(
935935
self.logger,
936-
"Latest block {} at height {} removed via block_disconnected",
937-
header.block_hash(),
938-
height
936+
"Block(s) removed to height {} via blocks_disconnected. New best block is {}",
937+
new_best_block.height,
938+
new_best_block.block_hash,
939939
);
940940
for monitor_state in monitor_states.values() {
941-
monitor_state.monitor.block_disconnected(
942-
header,
943-
height,
941+
monitor_state.monitor.blocks_disconnected(
942+
new_best_block,
944943
&*self.broadcaster,
945944
&*self.fee_estimator,
946945
&self.logger,

lightning/src/chain/channelmonitor.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,23 +2175,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
21752175

21762176
/// Determines if the disconnected block contained any transactions of interest and updates
21772177
/// appropriately.
2178-
#[rustfmt::skip]
2179-
pub fn block_disconnected<B: Deref, F: Deref, L: Deref>(
2180-
&self,
2181-
header: &Header,
2182-
height: u32,
2183-
broadcaster: B,
2184-
fee_estimator: F,
2185-
logger: &L,
2178+
pub fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
2179+
&self, new_best_block: BestBlock, broadcaster: B, fee_estimator: F, logger: &L,
21862180
) where
21872181
B::Target: BroadcasterInterface,
21882182
F::Target: FeeEstimator,
21892183
L::Target: Logger,
21902184
{
21912185
let mut inner = self.inner.lock().unwrap();
21922186
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
2193-
inner.block_disconnected(
2194-
header, height, broadcaster, fee_estimator, &logger)
2187+
inner.blocks_disconnected(new_best_block, broadcaster, fee_estimator, &logger)
21952188
}
21962189

21972190
/// Processes transactions confirmed in a block with the given header and height, returning new
@@ -2225,10 +2218,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
22252218

22262219
/// Processes a transaction that was reorganized out of the chain.
22272220
///
2228-
/// Used instead of [`block_disconnected`] by clients that are notified of transactions rather
2221+
/// Used instead of [`blocks_disconnected`] by clients that are notified of transactions rather
22292222
/// than blocks. See [`chain::Confirm`] for calling expectations.
22302223
///
2231-
/// [`block_disconnected`]: Self::block_disconnected
2224+
/// [`blocks_disconnected`]: Self::blocks_disconnected
22322225
#[rustfmt::skip]
22332226
pub fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
22342227
&self,
@@ -4975,12 +4968,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49754968
!unmatured_htlcs.contains(&&source),
49764969
"An unmature HTLC transaction conflicts with a maturing one; failed to \
49774970
call either transaction_unconfirmed for the conflicting transaction \
4978-
or block_disconnected for a block containing it.");
4971+
or blocks_disconnected for a block before it.");
49794972
debug_assert!(
49804973
!matured_htlcs.contains(&source),
49814974
"A matured HTLC transaction conflicts with a maturing one; failed to \
49824975
call either transaction_unconfirmed for the conflicting transaction \
4983-
or block_disconnected for a block containing it.");
4976+
or blocks_disconnected for a block before it.");
49844977
matured_htlcs.push(source.clone());
49854978
}
49864979

@@ -5118,26 +5111,27 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51185111
}
51195112

51205113
#[rustfmt::skip]
5121-
fn block_disconnected<B: Deref, F: Deref, L: Deref>(
5122-
&mut self, header: &Header, height: u32, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
5114+
fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
5115+
&mut self, new_best_block: BestBlock, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
51235116
) where B::Target: BroadcasterInterface,
51245117
F::Target: FeeEstimator,
51255118
L::Target: Logger,
51265119
{
5127-
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
5120+
let new_height = new_best_block.height;
5121+
log_trace!(logger, "Block(s) disconnected to height {}", new_height);
51285122

51295123
//We may discard:
51305124
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
51315125
//- maturing spendable output has transaction paying us has been disconnected
5132-
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height);
5126+
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= new_height);
51335127

51345128
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
51355129
let conf_target = self.closure_conf_target();
51365130
self.onchain_tx_handler.block_disconnected(
5137-
height, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger
5131+
new_height + 1, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger
51385132
);
51395133

5140-
self.best_block = BestBlock::new(header.prev_blockhash, height - 1);
5134+
self.best_block = new_best_block;
51415135
}
51425136

51435137
#[rustfmt::skip]
@@ -5582,8 +5576,8 @@ where
55825576
self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &self.3);
55835577
}
55845578

5585-
fn block_disconnected(&self, header: &Header, height: u32) {
5586-
self.0.block_disconnected(header, height, &*self.1, &*self.2, &self.3);
5579+
fn blocks_disconnected(&self, new_best_block: BestBlock) {
5580+
self.0.blocks_disconnected(new_best_block, &*self.1, &*self.2, &self.3);
55875581
}
55885582
}
55895583

0 commit comments

Comments
 (0)