diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index f6fa07199fa..135be0a4e30 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -344,8 +344,9 @@ impl<'a> MoneyLossDetector<'a> { self.header_hashes[self.height - 1].0, self.header_hashes[self.height].1, ); - self.manager.block_disconnected(&header, self.height as u32); - self.monitor.block_disconnected(&header, self.height as u32); + let best_block = BestBlock::new(header.prev_blockhash, self.height as u32 - 1); + self.manager.blocks_disconnected(best_block); + self.monitor.blocks_disconnected(best_block); self.height -= 1; let removal_height = self.height; self.txids_confirmed.retain(|_, height| removal_height != *height); diff --git a/lightning-block-sync/src/init.rs b/lightning-block-sync/src/init.rs index f71a72456dc..ef16dc31c8c 100644 --- a/lightning-block-sync/src/init.rs +++ b/lightning-block-sync/src/init.rs @@ -9,6 +9,7 @@ use bitcoin::hash_types::BlockHash; use bitcoin::network::Network; use lightning::chain; +use lightning::chain::BestBlock; use std::ops::Deref; @@ -230,8 +231,8 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L unreachable!() } - fn block_disconnected(&self, header: &Header, height: u32) { - self.0.block_disconnected(header, height) + fn blocks_disconnected(&self, new_best_block: BestBlock) { + self.0.blocks_disconnected(new_best_block) } } @@ -257,7 +258,7 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for ChainListenerSet<'a, L> { } } - fn block_disconnected(&self, _header: &Header, _height: u32) { + fn blocks_disconnected(&self, _new_best_block: BestBlock) { unreachable!() } } @@ -300,19 +301,16 @@ mod tests { let fork_chain_3 = main_chain.fork_at_height(3); let listener_1 = MockChainListener::new() - .expect_block_disconnected(*fork_chain_1.at_height(4)) - .expect_block_disconnected(*fork_chain_1.at_height(3)) - .expect_block_disconnected(*fork_chain_1.at_height(2)) + .expect_blocks_disconnected(*fork_chain_1.at_height(2)) .expect_block_connected(*main_chain.at_height(2)) .expect_block_connected(*main_chain.at_height(3)) .expect_block_connected(*main_chain.at_height(4)); let listener_2 = MockChainListener::new() - .expect_block_disconnected(*fork_chain_2.at_height(4)) - .expect_block_disconnected(*fork_chain_2.at_height(3)) + .expect_blocks_disconnected(*fork_chain_2.at_height(3)) .expect_block_connected(*main_chain.at_height(3)) .expect_block_connected(*main_chain.at_height(4)); let listener_3 = MockChainListener::new() - .expect_block_disconnected(*fork_chain_3.at_height(4)) + .expect_blocks_disconnected(*fork_chain_3.at_height(4)) .expect_block_connected(*main_chain.at_height(4)); let listeners = vec![ @@ -337,23 +335,17 @@ mod tests { let fork_chain_3 = fork_chain_2.fork_at_height(3); let listener_1 = MockChainListener::new() - .expect_block_disconnected(*fork_chain_1.at_height(4)) - .expect_block_disconnected(*fork_chain_1.at_height(3)) - .expect_block_disconnected(*fork_chain_1.at_height(2)) + .expect_blocks_disconnected(*fork_chain_1.at_height(2)) .expect_block_connected(*main_chain.at_height(2)) .expect_block_connected(*main_chain.at_height(3)) .expect_block_connected(*main_chain.at_height(4)); let listener_2 = MockChainListener::new() - .expect_block_disconnected(*fork_chain_2.at_height(4)) - .expect_block_disconnected(*fork_chain_2.at_height(3)) - .expect_block_disconnected(*fork_chain_2.at_height(2)) + .expect_blocks_disconnected(*fork_chain_2.at_height(2)) .expect_block_connected(*main_chain.at_height(2)) .expect_block_connected(*main_chain.at_height(3)) .expect_block_connected(*main_chain.at_height(4)); let listener_3 = MockChainListener::new() - .expect_block_disconnected(*fork_chain_3.at_height(4)) - .expect_block_disconnected(*fork_chain_3.at_height(3)) - .expect_block_disconnected(*fork_chain_3.at_height(2)) + .expect_blocks_disconnected(*fork_chain_3.at_height(2)) .expect_block_connected(*main_chain.at_height(2)) .expect_block_connected(*main_chain.at_height(3)) .expect_block_connected(*main_chain.at_height(4)); @@ -380,7 +372,7 @@ mod tests { let old_tip = fork_chain.tip(); let listener = MockChainListener::new() - .expect_block_disconnected(*old_tip) + .expect_blocks_disconnected(*old_tip) .expect_block_connected(*new_tip); let listeners = vec![(old_tip.block_hash, &listener as &dyn chain::Listen)]; diff --git a/lightning-block-sync/src/lib.rs b/lightning-block-sync/src/lib.rs index 3f981cd8786..2c5370efe58 100644 --- a/lightning-block-sync/src/lib.rs +++ b/lightning-block-sync/src/lib.rs @@ -49,7 +49,7 @@ use bitcoin::hash_types::BlockHash; use bitcoin::pow::Work; use lightning::chain; -use lightning::chain::Listen; +use lightning::chain::{BestBlock, Listen}; use std::future::Future; use std::ops::Deref; @@ -398,12 +398,15 @@ where } /// Notifies the chain listeners of disconnected blocks. - fn disconnect_blocks(&mut self, mut disconnected_blocks: Vec) { - for header in disconnected_blocks.drain(..) { + fn disconnect_blocks(&mut self, disconnected_blocks: Vec) { + for header in disconnected_blocks.iter() { if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) { - assert_eq!(cached_header, header); + assert_eq!(cached_header, *header); } - self.chain_listener.block_disconnected(&header.header, header.height); + } + if let Some(block) = disconnected_blocks.last() { + let best_block = BestBlock::new(block.header.prev_blockhash, block.height - 1); + self.chain_listener.blocks_disconnected(best_block); } } @@ -615,7 +618,7 @@ mod chain_notifier_tests { let new_tip = fork_chain.tip(); let old_tip = main_chain.tip(); let chain_listener = &MockChainListener::new() - .expect_block_disconnected(*old_tip) + .expect_blocks_disconnected(*old_tip) .expect_block_connected(*new_tip); let mut notifier = ChainNotifier { header_cache: &mut main_chain.header_cache(0..=2), chain_listener }; @@ -635,8 +638,7 @@ mod chain_notifier_tests { let new_tip = fork_chain.tip(); let old_tip = main_chain.tip(); let chain_listener = &MockChainListener::new() - .expect_block_disconnected(*old_tip) - .expect_block_disconnected(*main_chain.at_height(2)) + .expect_blocks_disconnected(*main_chain.at_height(2)) .expect_block_connected(*new_tip); let mut notifier = ChainNotifier { header_cache: &mut main_chain.header_cache(0..=3), chain_listener }; @@ -656,7 +658,7 @@ mod chain_notifier_tests { let new_tip = fork_chain.tip(); let old_tip = main_chain.tip(); let chain_listener = &MockChainListener::new() - .expect_block_disconnected(*old_tip) + .expect_blocks_disconnected(*old_tip) .expect_block_connected(*fork_chain.at_height(2)) .expect_block_connected(*new_tip); let mut notifier = diff --git a/lightning-block-sync/src/test_utils.rs b/lightning-block-sync/src/test_utils.rs index 098f1a8769a..2b15f3c81e5 100644 --- a/lightning-block-sync/src/test_utils.rs +++ b/lightning-block-sync/src/test_utils.rs @@ -13,6 +13,7 @@ use bitcoin::transaction; use bitcoin::Transaction; use lightning::chain; +use lightning::chain::BestBlock; use std::cell::RefCell; use std::collections::VecDeque; @@ -203,7 +204,7 @@ impl chain::Listen for NullChainListener { &self, _header: &Header, _txdata: &chain::transaction::TransactionData, _height: u32, ) { } - fn block_disconnected(&self, _header: &Header, _height: u32) {} + fn blocks_disconnected(&self, _new_best_block: BestBlock) {} } pub struct MockChainListener { @@ -231,7 +232,7 @@ impl MockChainListener { self } - pub fn expect_block_disconnected(self, block: BlockHeaderData) -> Self { + pub fn expect_blocks_disconnected(self, block: BlockHeaderData) -> Self { self.expected_blocks_disconnected.borrow_mut().push_back(block); self } @@ -264,14 +265,17 @@ impl chain::Listen for MockChainListener { } } - fn block_disconnected(&self, header: &Header, height: u32) { + fn blocks_disconnected(&self, new_best_block: BestBlock) { match self.expected_blocks_disconnected.borrow_mut().pop_front() { None => { - panic!("Unexpected block disconnected: {:?}", header.block_hash()); + panic!( + "Unexpected block(s) disconnect to {} at height {}", + new_best_block.block_hash, new_best_block.height, + ); }, Some(expected_block) => { - assert_eq!(header.block_hash(), expected_block.header.block_hash()); - assert_eq!(height, expected_block.height); + assert_eq!(new_best_block.block_hash, expected_block.header.prev_blockhash); + assert_eq!(new_best_block.height, expected_block.height - 1); }, } } diff --git a/lightning-liquidity/src/manager.rs b/lightning-liquidity/src/manager.rs index f4cce6855cd..308fa216c92 100644 --- a/lightning-liquidity/src/manager.rs +++ b/lightning-liquidity/src/manager.rs @@ -583,14 +583,9 @@ where self.best_block_updated(header, height); } - fn block_disconnected(&self, header: &bitcoin::block::Header, height: u32) { - let new_height = height - 1; + fn blocks_disconnected(&self, new_best_block: BestBlock) { if let Some(best_block) = self.best_block.write().unwrap().as_mut() { - assert_eq!(best_block.block_hash, header.block_hash(), - "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header"); - assert_eq!(best_block.height, height, - "Blocks must be disconnected in chain-order - the disconnected block must have the correct height"); - *best_block = BestBlock::new(header.prev_blockhash, new_height) + *best_block = new_best_block; } // TODO: Call block_disconnected on all sub-modules that require it, e.g., LSPS1MessageHandler. diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index 46ede6fd850..27cc56b4d99 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -33,7 +33,7 @@ use crate::chain::channelmonitor::{ WithChannelMonitor, }; use crate::chain::transaction::{OutPoint, TransactionData}; -use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput}; +use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Filter, WatchedOutput}; use crate::events::{self, Event, EventHandler, ReplayEvent}; use crate::ln::channel_state::ChannelDetails; use crate::ln::msgs::{self, BaseMessageHandler, Init, MessageSendEvent}; @@ -910,18 +910,17 @@ where self.event_notifier.notify(); } - fn block_disconnected(&self, header: &Header, height: u32) { + fn blocks_disconnected(&self, new_best_block: BestBlock) { let monitor_states = self.monitors.read().unwrap(); log_debug!( self.logger, - "Latest block {} at height {} removed via block_disconnected", - header.block_hash(), - height + "Block(s) removed to height {} via blocks_disconnected. New best block is {}", + new_best_block.height, + new_best_block.block_hash, ); for monitor_state in monitor_states.values() { - monitor_state.monitor.block_disconnected( - header, - height, + monitor_state.monitor.blocks_disconnected( + new_best_block, &*self.broadcaster, &*self.fee_estimator, &self.logger, diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 54f170bcbe3..0ff14fcb59c 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -2099,14 +2099,8 @@ impl ChannelMonitor { /// Determines if the disconnected block contained any transactions of interest and updates /// appropriately. - #[rustfmt::skip] - pub fn block_disconnected( - &self, - header: &Header, - height: u32, - broadcaster: B, - fee_estimator: F, - logger: &L, + pub fn blocks_disconnected( + &self, new_best_block: BestBlock, broadcaster: B, fee_estimator: F, logger: &L, ) where B::Target: BroadcasterInterface, F::Target: FeeEstimator, @@ -2114,8 +2108,7 @@ impl ChannelMonitor { { let mut inner = self.inner.lock().unwrap(); let logger = WithChannelMonitor::from_impl(logger, &*inner, None); - inner.block_disconnected( - header, height, broadcaster, fee_estimator, &logger) + inner.blocks_disconnected(new_best_block, broadcaster, fee_estimator, &logger) } /// Processes transactions confirmed in a block with the given header and height, returning new @@ -2149,10 +2142,10 @@ impl ChannelMonitor { /// Processes a transaction that was reorganized out of the chain. /// - /// Used instead of [`block_disconnected`] by clients that are notified of transactions rather + /// Used instead of [`blocks_disconnected`] by clients that are notified of transactions rather /// than blocks. See [`chain::Confirm`] for calling expectations. /// - /// [`block_disconnected`]: Self::block_disconnected + /// [`blocks_disconnected`]: Self::blocks_disconnected #[rustfmt::skip] pub fn transaction_unconfirmed( &self, @@ -4591,12 +4584,12 @@ impl ChannelMonitorImpl { !unmatured_htlcs.contains(&&source), "An unmature HTLC transaction conflicts with a maturing one; failed to \ call either transaction_unconfirmed for the conflicting transaction \ - or block_disconnected for a block containing it."); + or blocks_disconnected for a block containing it."); debug_assert!( !matured_htlcs.contains(&source), "A matured HTLC transaction conflicts with a maturing one; failed to \ call either transaction_unconfirmed for the conflicting transaction \ - or block_disconnected for a block containing it."); + or blocks_disconnected for a block containing it."); matured_htlcs.push(source.clone()); } @@ -4734,26 +4727,27 @@ impl ChannelMonitorImpl { } #[rustfmt::skip] - fn block_disconnected( - &mut self, header: &Header, height: u32, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor + fn blocks_disconnected( + &mut self, new_best_block: BestBlock, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor ) where B::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, { - log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height); + let new_height = new_best_block.height; + log_trace!(logger, "Block(s) disconnected to height {}", new_height); //We may discard: //- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected //- maturing spendable output has transaction paying us has been disconnected - self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height); + self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= new_height); let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator); let conf_target = self.closure_conf_target(); self.onchain_tx_handler.block_disconnected( - height, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger + new_height + 1, broadcaster, conf_target, &self.destination_script, &bounded_fee_estimator, logger ); - self.best_block = BestBlock::new(header.prev_blockhash, height - 1); + self.best_block = new_best_block; } #[rustfmt::skip] @@ -5198,8 +5192,8 @@ where self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &self.3); } - fn block_disconnected(&self, header: &Header, height: u32) { - self.0.block_disconnected(header, height, &*self.1, &*self.2, &self.3); + fn blocks_disconnected(&self, new_best_block: BestBlock) { + self.0.blocks_disconnected(new_best_block, &*self.1, &*self.2, &self.3); } } diff --git a/lightning/src/chain/mod.rs b/lightning/src/chain/mod.rs index c16ee2519f7..3783281354d 100644 --- a/lightning/src/chain/mod.rs +++ b/lightning/src/chain/mod.rs @@ -84,8 +84,10 @@ pub trait Listen { self.filtered_block_connected(&block.header, &txdata, height); } - /// Notifies the listener that a block was removed at the given height. - fn block_disconnected(&self, header: &Header, height: u32); + /// Notifies the listener that one or more blocks were removed in anticipation of a reorg. + /// + /// Indicates the new best tip is the provided [`BestBlock`]. + fn blocks_disconnected(&self, new_best_block: BestBlock); } /// The `Confirm` trait is used to notify LDK when relevant transactions have been confirmed on @@ -272,7 +274,7 @@ pub trait Watch { /// /// Implementations are responsible for watching the chain for the funding transaction along /// with any spends of outputs returned by [`get_outputs_to_watch`]. In practice, this means - /// calling [`block_connected`] and [`block_disconnected`] on the monitor. + /// calling [`block_connected`] and [`blocks_disconnected`] on the monitor. /// /// A return of `Err(())` indicates that the channel should immediately be force-closed without /// broadcasting the funding transaction. @@ -282,7 +284,7 @@ pub trait Watch { /// /// [`get_outputs_to_watch`]: channelmonitor::ChannelMonitor::get_outputs_to_watch /// [`block_connected`]: channelmonitor::ChannelMonitor::block_connected - /// [`block_disconnected`]: channelmonitor::ChannelMonitor::block_disconnected + /// [`blocks_disconnected`]: channelmonitor::ChannelMonitor::blocks_disconnected fn watch_channel( &self, channel_id: ChannelId, monitor: ChannelMonitor, ) -> Result; @@ -393,8 +395,8 @@ impl Listen for dyn core::ops::Deref { (**self).filtered_block_connected(header, txdata, height); } - fn block_disconnected(&self, header: &Header, height: u32) { - (**self).block_disconnected(header, height); + fn blocks_disconnected(&self, new_best_block: BestBlock) { + (**self).blocks_disconnected(new_best_block); } } @@ -408,9 +410,9 @@ where self.1.filtered_block_connected(header, txdata, height); } - fn block_disconnected(&self, header: &Header, height: u32) { - self.0.block_disconnected(header, height); - self.1.block_disconnected(header, height); + fn blocks_disconnected(&self, new_best_block: BestBlock) { + self.0.blocks_disconnected(new_best_block); + self.1.blocks_disconnected(new_best_block); } } diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index fb58b51d4dc..adccde87017 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -9291,8 +9291,8 @@ where /// May return some HTLCs (and their payment_hash) which have timed out and should be failed /// back. pub fn best_block_updated( - &mut self, height: u32, highest_header_time: u32, chain_hash: ChainHash, node_signer: &NS, - user_config: &UserConfig, logger: &L, + &mut self, height: u32, highest_header_time: Option, chain_hash: ChainHash, + node_signer: &NS, user_config: &UserConfig, logger: &L, ) -> Result where NS::Target: NodeSigner, @@ -9308,7 +9308,7 @@ where #[rustfmt::skip] fn do_best_block_updated( - &mut self, height: u32, highest_header_time: u32, + &mut self, height: u32, highest_header_time: Option, chain_node_signer: Option<(ChainHash, &NS, &UserConfig)>, logger: &L ) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>, Option), ClosureReason> where @@ -9332,7 +9332,9 @@ where } }); - self.context.update_time_counter = cmp::max(self.context.update_time_counter, highest_header_time); + if let Some(time) = highest_header_time { + self.context.update_time_counter = cmp::max(self.context.update_time_counter, time); + } // Check if the funding transaction was unconfirmed let funding_tx_confirmations = self.funding.get_funding_tx_confirmations(height); @@ -9482,12 +9484,10 @@ where // We handle the funding disconnection by calling best_block_updated with a height one // below where our funding was connected, implying a reorg back to conf_height - 1. let reorg_height = funding.funding_tx_confirmation_height - 1; - // We use the time field to bump the current time we set on channel updates if its - // larger. If we don't know that time has moved forward, we can just set it to the last - // time we saw and it will be ignored. - let best_time = self.context.update_time_counter; - match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&dyn NodeSigner, &UserConfig)>, logger) { + let signer_config = None::<(ChainHash, &&dyn NodeSigner, &UserConfig)>; + let res = self.do_best_block_updated(reorg_height, None, signer_config, logger); + match res { Ok((channel_ready, timed_out_htlcs, announcement_sigs)) => { assert!(channel_ready.is_none(), "We can't generate a funding with 0 confirmations?"); assert!(timed_out_htlcs.is_empty(), "We can't have accepted HTLCs with a timeout before our funding confirmation?"); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d99d1fb82a4..8dace935716 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -3726,12 +3726,12 @@ where /// Non-proportional fees are fixed according to our risk using the provided fee estimator. /// /// Users need to notify the new `ChannelManager` when a new block is connected or - /// disconnected using its [`block_connected`] and [`block_disconnected`] methods, starting + /// disconnected using its [`block_connected`] and [`blocks_disconnected`] methods, starting /// from after [`params.best_block.block_hash`]. See [`chain::Listen`] and [`chain::Confirm`] for /// more details. /// /// [`block_connected`]: chain::Listen::block_connected - /// [`block_disconnected`]: chain::Listen::block_disconnected + /// [`blocks_disconnected`]: chain::Listen::blocks_disconnected /// [`params.best_block.block_hash`]: chain::BestBlock::block_hash #[rustfmt::skip] pub fn new( @@ -12021,26 +12021,21 @@ where self.best_block_updated(header, height); } - fn block_disconnected(&self, header: &Header, height: u32) { + fn blocks_disconnected(&self, new_best_block: BestBlock) { let _persistence_guard = PersistenceNotifierGuard::optionally_notify_skipping_background_events( self, || -> NotifyOption { NotifyOption::DoPersist }, ); - let new_height = height - 1; { let mut best_block = self.best_block.write().unwrap(); - assert_eq!(best_block.block_hash, header.block_hash(), - "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header"); - assert_eq!(best_block.height, height, - "Blocks must be disconnected in chain-order - the disconnected block must have the correct height"); - *best_block = BestBlock::new(header.prev_blockhash, new_height) + *best_block = new_best_block; } - self.do_chain_event(Some(new_height), |channel| { + self.do_chain_event(Some(new_best_block.height), |channel| { channel.best_block_updated( - new_height, - header.time, + new_best_block.height, + None, self.chain_hash, &self.node_signer, &self.default_configuration, @@ -12090,7 +12085,17 @@ where let last_best_block_height = self.best_block.read().unwrap().height; if height < last_best_block_height { let timestamp = self.highest_seen_timestamp.load(Ordering::Acquire); - self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None))); + let do_update = |channel: &mut FundedChannel| { + channel.best_block_updated( + last_best_block_height, + Some(timestamp as u32), + self.chain_hash, + &self.node_signer, + &self.default_configuration, + &&WithChannelContext::from(&self.logger, &channel.context, None) + ) + }; + self.do_chain_event(Some(last_best_block_height), do_update); } } @@ -12150,7 +12155,14 @@ where } } - channel.best_block_updated(height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None)) + channel.best_block_updated( + height, + Some(header.time), + self.chain_hash, + &self.node_signer, + &self.default_configuration, + &&WithChannelContext::from(&self.logger, &channel.context, None) + ) }); macro_rules! max_time { diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index ce794cd7cb5..f4df2e98b24 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -171,6 +171,9 @@ pub enum ConnectStyle { /// Provides the full block via the `chain::Listen` interface. In the current code this is /// equivalent to `TransactionsFirst` with some additional assertions. FullBlockViaListen, + /// Provides the full block via the `chain::Listen` interface, condensing multiple block + /// disconnections into a single `blocks_disconnected` call. + FullBlockDisconnectionsSkippingViaListen, } impl ConnectStyle { @@ -185,6 +188,7 @@ impl ConnectStyle { ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks => true, ConnectStyle::TransactionsFirstReorgsOnlyTip => true, ConnectStyle::FullBlockViaListen => false, + ConnectStyle::FullBlockDisconnectionsSkippingViaListen => false, } } @@ -199,6 +203,7 @@ impl ConnectStyle { ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks => false, ConnectStyle::TransactionsFirstReorgsOnlyTip => false, ConnectStyle::FullBlockViaListen => false, + ConnectStyle::FullBlockDisconnectionsSkippingViaListen => false, } } @@ -206,7 +211,7 @@ impl ConnectStyle { use core::hash::{BuildHasher, Hasher}; // Get a random value using the only std API to do so - the DefaultHasher let rand_val = std::collections::hash_map::RandomState::new().build_hasher().finish(); - let res = match rand_val % 9 { + let res = match rand_val % 10 { 0 => ConnectStyle::BestBlockFirst, 1 => ConnectStyle::BestBlockFirstSkippingBlocks, 2 => ConnectStyle::BestBlockFirstReorgsOnlyTip, @@ -216,6 +221,7 @@ impl ConnectStyle { 6 => ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks, 7 => ConnectStyle::TransactionsFirstReorgsOnlyTip, 8 => ConnectStyle::FullBlockViaListen, + 9 => ConnectStyle::FullBlockDisconnectionsSkippingViaListen, _ => unreachable!(), }; eprintln!("Using Block Connection Style: {:?}", res); @@ -319,7 +325,7 @@ fn do_connect_block_without_consistency_checks<'a, 'b, 'c, 'd>(node: &'a Node<'b node.node.transactions_confirmed(&block.header, &txdata, height); node.node.best_block_updated(&block.header, height); }, - ConnectStyle::FullBlockViaListen => { + ConnectStyle::FullBlockViaListen|ConnectStyle::FullBlockDisconnectionsSkippingViaListen => { node.chain_monitor.chain_monitor.block_connected(&block, height); node.node.block_connected(&block, height); } @@ -350,8 +356,16 @@ pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32) match *node.connect_style.borrow() { ConnectStyle::FullBlockViaListen => { - node.chain_monitor.chain_monitor.block_disconnected(&orig.0.header, orig.1); - Listen::block_disconnected(node.node, &orig.0.header, orig.1); + let best_block = BestBlock::new(orig.0.header.prev_blockhash, orig.1 - 1); + node.chain_monitor.chain_monitor.blocks_disconnected(best_block); + Listen::blocks_disconnected(node.node, best_block); + }, + ConnectStyle::FullBlockDisconnectionsSkippingViaListen => { + if i == count - 1 { + let best_block = BestBlock::new(orig.0.header.prev_blockhash, orig.1 - 1); + node.chain_monitor.chain_monitor.blocks_disconnected(best_block); + Listen::blocks_disconnected(node.node, best_block); + } }, ConnectStyle::BestBlockFirstSkippingBlocks|ConnectStyle::TransactionsFirstSkippingBlocks| ConnectStyle::HighlyRedundantTransactionsFirstSkippingBlocks|ConnectStyle::TransactionsDuplicativelyFirstSkippingBlocks => { diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 60aa21bc44e..b9d39165cf4 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -2745,11 +2745,15 @@ pub fn test_htlc_ignore_latest_remote_commitment() { let node_a_id = nodes[0].node.get_our_node_id(); let node_b_id = nodes[1].node.get_our_node_id(); - if *nodes[1].connect_style.borrow() == ConnectStyle::FullBlockViaListen { - // We rely on the ability to connect a block redundantly, which isn't allowed via - // `chain::Listen`, so we never run the test if we randomly get assigned that - // connect_style. - return; + match *nodes[1].connect_style.borrow() { + ConnectStyle::FullBlockViaListen + | ConnectStyle::FullBlockDisconnectionsSkippingViaListen => { + // We rely on the ability to connect a block redundantly, which isn't allowed via + // `chain::Listen`, so we never run the test if we randomly get assigned that + // connect_style. + return; + }, + _ => {}, } let funding_tx = create_announced_chan_between_nodes(&nodes, 0, 1).3; let error_message = "Channel force-closed"; diff --git a/lightning/src/util/sweep.rs b/lightning/src/util/sweep.rs index cea8dd76031..c1435f18815 100644 --- a/lightning/src/util/sweep.rs +++ b/lightning/src/util/sweep.rs @@ -280,16 +280,6 @@ impl OutputSpendStatus { } } - fn confirmation_hash(&self) -> Option { - match self { - Self::PendingInitialBroadcast { .. } => None, - Self::PendingFirstConfirmation { .. } => None, - Self::PendingThresholdConfirmations { confirmation_hash, .. } => { - Some(*confirmation_hash) - }, - } - } - fn latest_spending_tx(&self) -> Option<&Transaction> { match self { Self::PendingInitialBroadcast { .. } => None, @@ -679,21 +669,13 @@ where }); } - fn block_disconnected(&self, header: &Header, height: u32) { + fn blocks_disconnected(&self, new_best_block: BestBlock) { let mut state_lock = self.sweeper_state.lock().unwrap(); - let new_height = height - 1; - let block_hash = header.block_hash(); - - assert_eq!(state_lock.best_block.block_hash, block_hash, - "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header"); - assert_eq!(state_lock.best_block.height, height, - "Blocks must be disconnected in chain-order - the disconnected block must have the correct height"); - state_lock.best_block = BestBlock::new(header.prev_blockhash, new_height); + state_lock.best_block = new_best_block; for output_info in state_lock.outputs.iter_mut() { - if output_info.status.confirmation_hash() == Some(block_hash) { - debug_assert_eq!(output_info.status.confirmation_height(), Some(height)); + if output_info.status.confirmation_height() > Some(new_best_block.height) { output_info.status.unconfirmed(); } }