Skip to content

Commit a4a01d0

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 de2005a commit a4a01d0

File tree

11 files changed

+99
-120
lines changed

11 files changed

+99
-120
lines changed

fuzz/src/full_stack.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,9 @@ impl<'a> MoneyLossDetector<'a> {
345345
self.header_hashes[self.height - 1].0,
346346
self.header_hashes[self.height].1,
347347
);
348-
self.manager.block_disconnected(&header, self.height as u32);
349-
self.monitor.block_disconnected(&header, self.height as u32);
348+
let best_block = BestBlock::new(header.prev_blockhash, self.height as u32 - 1);
349+
self.manager.blocks_disconnected(best_block);
350+
self.monitor.blocks_disconnected(best_block);
350351
self.height -= 1;
351352
let removal_height = self.height;
352353
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, fork_point: BestBlock) {
235+
self.0.blocks_disconnected(fork_point)
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, _fork_point: 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(1))
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(2))
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(3))
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(1))
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(1))
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(1))
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(*fork_chain.at_height(1))
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 fork_point = BestBlock::new(block.header.prev_blockhash, block.height - 1);
409+
self.chain_listener.blocks_disconnected(fork_point);
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(*fork_chain.at_height(1))
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(1))
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(*fork_chain.at_height(1))
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: 12 additions & 8 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, _fork_point: BestBlock) {}
207208
}
208209

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

234-
pub fn expect_block_disconnected(self, block: BlockHeaderData) -> Self {
235-
self.expected_blocks_disconnected.borrow_mut().push_back(block);
235+
pub fn expect_blocks_disconnected(self, fork_point: BlockHeaderData) -> Self {
236+
self.expected_blocks_disconnected.borrow_mut().push_back(fork_point);
236237
self
237238
}
238239
}
@@ -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, fork_point: 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+
fork_point.block_hash, fork_point.height,
274+
);
271275
},
272-
Some(expected_block) => {
273-
assert_eq!(header.block_hash(), expected_block.header.block_hash());
274-
assert_eq!(height, expected_block.height);
276+
Some(expected) => {
277+
assert_eq!(fork_point.block_hash, expected.header.block_hash());
278+
assert_eq!(fork_point.height, expected.height);
275279
},
276280
}
277281
}

lightning-liquidity/src/manager.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -754,14 +754,11 @@ where
754754
self.best_block_updated(header, height);
755755
}
756756

757-
fn block_disconnected(&self, header: &bitcoin::block::Header, height: u32) {
758-
let new_height = height - 1;
757+
fn blocks_disconnected(&self, fork_point: BestBlock) {
759758
if let Some(best_block) = self.best_block.write().unwrap().as_mut() {
760-
assert_eq!(best_block.block_hash, header.block_hash(),
761-
"Blocks must be disconnected in chain-order - the disconnected header must be the last connected header");
762-
assert_eq!(best_block.height, height,
763-
"Blocks must be disconnected in chain-order - the disconnected block must have the correct height");
764-
*best_block = BestBlock::new(header.prev_blockhash, new_height)
759+
assert!(best_block.height > fork_point.height,
760+
"Blocks disconnected must indicate disconnection from the current best height, i.e. the new chain tip must be lower than the previous best height");
761+
*best_block = fork_point;
765762
}
766763

767764
// 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, fork_point: 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+
fork_point.height,
938+
fork_point.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+
fork_point,
944943
&*self.broadcaster,
945944
&*self.fee_estimator,
946945
&self.logger,

lightning/src/chain/channelmonitor.rs

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

22102210
/// Determines if the disconnected block contained any transactions of interest and updates
22112211
/// appropriately.
2212-
#[rustfmt::skip]
2213-
pub fn block_disconnected<B: Deref, F: Deref, L: Deref>(
2214-
&self,
2215-
header: &Header,
2216-
height: u32,
2217-
broadcaster: B,
2218-
fee_estimator: F,
2219-
logger: &L,
2212+
pub fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
2213+
&self, fork_point: BestBlock, broadcaster: B, fee_estimator: F, logger: &L,
22202214
) where
22212215
B::Target: BroadcasterInterface,
22222216
F::Target: FeeEstimator,
22232217
L::Target: Logger,
22242218
{
22252219
let mut inner = self.inner.lock().unwrap();
22262220
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
2227-
inner.block_disconnected(
2228-
header, height, broadcaster, fee_estimator, &logger)
2221+
inner.blocks_disconnected(fork_point, broadcaster, fee_estimator, &logger)
22292222
}
22302223

22312224
/// Processes transactions confirmed in a block with the given header and height, returning new
@@ -2259,10 +2252,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
22592252

22602253
/// Processes a transaction that was reorganized out of the chain.
22612254
///
2262-
/// Used instead of [`block_disconnected`] by clients that are notified of transactions rather
2255+
/// Used instead of [`blocks_disconnected`] by clients that are notified of transactions rather
22632256
/// than blocks. See [`chain::Confirm`] for calling expectations.
22642257
///
2265-
/// [`block_disconnected`]: Self::block_disconnected
2258+
/// [`blocks_disconnected`]: Self::blocks_disconnected
22662259
#[rustfmt::skip]
22672260
pub fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
22682261
&self,
@@ -5171,12 +5164,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51715164
!unmatured_htlcs.contains(&source),
51725165
"An unmature HTLC transaction conflicts with a maturing one; failed to \
51735166
call either transaction_unconfirmed for the conflicting transaction \
5174-
or block_disconnected for a block containing it.");
5167+
or blocks_disconnected for a block before it.");
51755168
debug_assert!(
51765169
!matured_htlcs.contains(&source),
51775170
"A matured HTLC transaction conflicts with a maturing one; failed to \
51785171
call either transaction_unconfirmed for the conflicting transaction \
5179-
or block_disconnected for a block containing it.");
5172+
or blocks_disconnected for a block before it.");
51805173
matured_htlcs.push(source.clone());
51815174
}
51825175

@@ -5324,22 +5317,25 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
53245317
}
53255318

53265319
#[rustfmt::skip]
5327-
fn block_disconnected<B: Deref, F: Deref, L: Deref>(
5328-
&mut self, header: &Header, height: u32, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
5320+
fn blocks_disconnected<B: Deref, F: Deref, L: Deref>(
5321+
&mut self, fork_point: BestBlock, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
53295322
) where B::Target: BroadcasterInterface,
53305323
F::Target: FeeEstimator,
53315324
L::Target: Logger,
53325325
{
5333-
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
5326+
let new_height = fork_point.height;
5327+
log_trace!(logger, "Block(s) disconnected to height {}", new_height);
5328+
assert!(self.best_block.height > fork_point.height,
5329+
"Blocks disconnected must indicate disconnection from the current best height, i.e. the new chain tip must be lower than the previous best height");
53345330

53355331
//We may discard:
53365332
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
53375333
//- maturing spendable output has transaction paying us has been disconnected
5338-
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height);
5334+
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= new_height);
53395335

53405336
// TODO: Replace with `take_if` once our MSRV is >= 1.80.
53415337
if let Some((_, conf_height)) = self.alternative_funding_confirmed.as_ref() {
5342-
if *conf_height == height {
5338+
if *conf_height > new_height {
53435339
self.alternative_funding_confirmed.take();
53445340
if self.holder_tx_signed {
53455341
// Cancel any previous claims that are no longer valid as they stemmed from a
@@ -5355,10 +5351,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
53555351
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
53565352
let conf_target = self.closure_conf_target();
53575353
self.onchain_tx_handler.block_disconnected(
5358-
height, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger
5354+
new_height + 1, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger
53595355
);
53605356

5361-
self.best_block = BestBlock::new(header.prev_blockhash, height - 1);
5357+
5358+
self.best_block = fork_point;
53625359
}
53635360

53645361
#[rustfmt::skip]
@@ -5818,8 +5815,8 @@ where
58185815
self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &self.3);
58195816
}
58205817

5821-
fn block_disconnected(&self, header: &Header, height: u32) {
5822-
self.0.block_disconnected(header, height, &*self.1, &*self.2, &self.3);
5818+
fn blocks_disconnected(&self, fork_point: BestBlock) {
5819+
self.0.blocks_disconnected(fork_point, &*self.1, &*self.2, &self.3);
58235820
}
58245821
}
58255822

0 commit comments

Comments
 (0)