From 377ca5f2538795953841b74a51014fb7fb147e87 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 14 Nov 2025 11:36:18 +0100 Subject: [PATCH 1/2] Channel logging improvements Additional trace logs to help with debugging. --- lightning/src/ln/channel.rs | 4 ++-- lightning/src/ln/channelmanager.rs | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index e55e4144ef2..b2b9520b94f 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -8877,8 +8877,8 @@ where ); return_with_htlcs_to_fail!(htlcs_to_fail); } else { - log_debug!(logger, "Received a valid revoke_and_ack with no reply necessary. {} monitor update.", - release_state_str); + log_debug!(logger, "Received a valid revoke_and_ack with no reply necessary. {} monitor update {}.", + release_state_str, monitor_update.update_id); self.monitor_updating_paused( false, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d84d46e8995..3234f62eef4 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -9379,6 +9379,9 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ for action in actions.into_iter() { match action { MonitorUpdateCompletionAction::PaymentClaimed { payment_hash, pending_mpp_claim } => { + let logger = WithContext::from(&self.logger, None, None, Some(payment_hash)); + log_trace!(logger, "Handling PaymentClaimed monitor update completion action"); + if let Some((counterparty_node_id, chan_id, claim_ptr)) = pending_mpp_claim { let per_peer_state = self.per_peer_state.read().unwrap(); per_peer_state.get(&counterparty_node_id).map(|peer_state_mutex| { @@ -9454,6 +9457,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ // `payment_id` should suffice to ensure we never spuriously drop a second // event for a duplicate payment. if !pending_events.contains(&event_action) { + log_trace!(logger, "Queuing PaymentClaimed event with event completion action {:?}", event_action.1); pending_events.push_back(event_action); } } @@ -16984,10 +16988,6 @@ where let logger = WithChannelMonitor::from(&args.logger, monitor, None); let channel_id = monitor.channel_id(); - log_info!( - logger, - "Queueing monitor update to ensure missing channel is force closed", - ); let monitor_update = ChannelMonitorUpdate { update_id: monitor.get_latest_update_id().saturating_add(1), updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { @@ -16995,6 +16995,11 @@ where }], channel_id: Some(monitor.channel_id()), }; + log_info!( + logger, + "Queueing monitor update {} to ensure missing channel is force closed", + monitor_update.update_id + ); let funding_txo = monitor.get_funding_txo(); let update = BackgroundEvent::MonitorUpdateRegeneratedOnStartup { counterparty_node_id, From d4d0879e3b8683d3e7aba3f87cc64ed38dabf826 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 11 Nov 2025 15:17:36 +0100 Subject: [PATCH 2/2] Store and check channel data in channel monitors Add a new `safe_channels` feature flag that gates in-development work toward persisting channel monitors and channels atomically, preventing them from desynchronizing and causing force closures. This commit begins that transition by storing both pieces together and adding consistency checks during writes. These checks mirror what the channel manager currently validates only on reload, but performing them earlier increases coverage and surfaces inconsistencies sooner. --- ci/ci-tests.sh | 5 + lightning/Cargo.toml | 1 + lightning/src/chain/channelmonitor.rs | 167 +++++++++++++++++++++++++- lightning/src/ln/channel.rs | 118 +++++++++++++++++- lightning/src/ln/channelmanager.rs | 20 +++ 5 files changed, 304 insertions(+), 7 deletions(-) diff --git a/ci/ci-tests.sh b/ci/ci-tests.sh index 91ead9903cb..98d8086bfd5 100755 --- a/ci/ci-tests.sh +++ b/ci/ci-tests.sh @@ -46,6 +46,11 @@ cargo test -p lightning --verbose --color always --features dnssec cargo check -p lightning --verbose --color always --features dnssec cargo doc -p lightning --document-private-items --features dnssec +echo -e "\n\nChecking and testing lightning with safe_channels" +cargo test -p lightning --verbose --color always --features safe_channels +cargo check -p lightning --verbose --color always --features safe_channels +cargo doc -p lightning --document-private-items --features safe_channels + echo -e "\n\nChecking and testing Block Sync Clients with features" cargo test -p lightning-block-sync --verbose --color always --features rest-client diff --git a/lightning/Cargo.toml b/lightning/Cargo.toml index 9df4a725e54..2d3d2a8d967 100644 --- a/lightning/Cargo.toml +++ b/lightning/Cargo.toml @@ -23,6 +23,7 @@ _externalize_tests = ["inventory", "_test_utils"] # Allow signing of local transactions that may have been revoked or will be revoked, for functional testing (e.g. justice tx handling). # This is unsafe to use in production because it may result in the counterparty publishing taking our funds. unsafe_revoked_tx_signing = [] +safe_channels = [] std = [] diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 10e5049682e..afb247c41d5 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -50,6 +50,8 @@ use crate::ln::chan_utils::{ self, ChannelTransactionParameters, CommitmentTransaction, CounterpartyCommitmentSecrets, HTLCClaim, HTLCOutputInCommitment, HolderCommitmentTransaction, }; +#[cfg(feature = "safe_channels")] +use crate::ln::channel::read_check_data; use crate::ln::channel::INITIAL_COMMITMENT_NUMBER; use crate::ln::channel_keys::{ DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey, RevocationBasepoint, @@ -111,6 +113,10 @@ pub struct ChannelMonitorUpdate { /// Will be `None` for `ChannelMonitorUpdate`s constructed on LDK versions prior to 0.0.121 and /// always `Some` otherwise. pub channel_id: Option, + + /// The encoded channel data associated with this ChannelMonitor, if any. + #[cfg(feature = "safe_channels")] + pub encoded_channel: Option>, } impl ChannelMonitorUpdate { @@ -156,9 +162,16 @@ impl Writeable for ChannelMonitorUpdate { for update_step in self.updates.iter() { update_step.write(w)?; } + #[cfg(not(feature = "safe_channels"))] + write_tlv_fields!(w, { + // 1 was previously used to store `counterparty_node_id` + (3, self.channel_id, option), + }); + #[cfg(feature = "safe_channels")] write_tlv_fields!(w, { // 1 was previously used to store `counterparty_node_id` (3, self.channel_id, option), + (5, self.encoded_channel, option) }); Ok(()) } @@ -176,11 +189,24 @@ impl Readable for ChannelMonitorUpdate { } } let mut channel_id = None; + #[cfg(not(feature = "safe_channels"))] read_tlv_fields!(r, { // 1 was previously used to store `counterparty_node_id` (3, channel_id, option), }); - Ok(Self { update_id, updates, channel_id }) + #[cfg(feature = "safe_channels")] + let mut encoded_channel = None; + #[cfg(feature = "safe_channels")] + read_tlv_fields!(r, { + // 1 was previously used to store `counterparty_node_id` + (3, channel_id, option), + (5, encoded_channel, option) + }); + Ok(Self { + update_id, updates, channel_id, + #[cfg(feature = "safe_channels")] + encoded_channel + }) } } @@ -1402,6 +1428,11 @@ pub(crate) struct ChannelMonitorImpl { /// make deciding whether to do so simple, here we track whether this monitor was last written /// prior to 0.1. written_by_0_1_or_later: bool, + + /// The serialized channel state as provided via the last `ChannelMonitorUpdate` or via a call to + /// [`ChannelMonitor::update_encoded_channel`]. + #[cfg(feature = "safe_channels")] + encoded_channel: Option>, } // Returns a `&FundingScope` for the one we are currently observing/handling commitment transactions @@ -1521,6 +1552,12 @@ const MIN_SERIALIZATION_VERSION: u8 = 1; pub(crate) fn write_chanmon_internal( channel_monitor: &ChannelMonitorImpl, _is_stub: bool, writer: &mut W, ) -> Result<(), Error> { + // Check that the encoded channel (if present) is consistent with the rest of the monitor. This sets an invariant + // for the safe_channels feature. + #[cfg(feature = "safe_channels")] + if let Some(ref encoded_channel) = channel_monitor.encoded_channel { + channel_monitor.check_encoded_channel_consistency(encoded_channel); + } write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); channel_monitor.latest_update_id.write(writer)?; @@ -1733,6 +1770,7 @@ pub(crate) fn write_chanmon_internal( _ => channel_monitor.pending_monitor_events.clone(), }; + #[cfg(not(feature = "safe_channels"))] write_tlv_fields!(writer, { (1, channel_monitor.funding_spend_confirmed, option), (3, channel_monitor.htlcs_resolved_on_chain, required_vec), @@ -1757,6 +1795,32 @@ pub(crate) fn write_chanmon_internal( (37, channel_monitor.funding_seen_onchain, required), }); + #[cfg(feature = "safe_channels")] + write_tlv_fields!(writer, { + (1, channel_monitor.funding_spend_confirmed, option), + (3, channel_monitor.htlcs_resolved_on_chain, required_vec), + (5, pending_monitor_events, required_vec), + (7, channel_monitor.funding_spend_seen, required), + (9, channel_monitor.counterparty_node_id, required), + (11, channel_monitor.confirmed_commitment_tx_counterparty_output, option), + (13, channel_monitor.spendable_txids_confirmed, required_vec), + (15, channel_monitor.counterparty_fulfilled_htlcs, required), + (17, channel_monitor.initial_counterparty_commitment_info, option), + (19, channel_monitor.channel_id, required), + (21, channel_monitor.balances_empty_height, option), + (23, channel_monitor.holder_pays_commitment_tx_fee, option), + (25, channel_monitor.payment_preimages, required), + (27, channel_monitor.first_negotiated_funding_txo, required), + (29, channel_monitor.initial_counterparty_commitment_tx, option), + (31, channel_monitor.funding.channel_parameters, required), + (32, channel_monitor.pending_funding, optional_vec), + (33, channel_monitor.htlcs_resolved_to_user, required), + (34, channel_monitor.alternative_funding_confirmed, option), + (35, channel_monitor.is_manual_broadcast, required), + (37, channel_monitor.funding_seen_onchain, required), + (39, channel_monitor.encoded_channel, option), + }); + Ok(()) } @@ -1994,6 +2058,8 @@ impl ChannelMonitor { alternative_funding_confirmed: None, written_by_0_1_or_later: true, + #[cfg(feature = "safe_channels")] + encoded_channel: None, }) } @@ -2114,6 +2180,19 @@ impl ChannelMonitor { inner.update_monitor(updates, broadcaster, fee_estimator, &logger) } + /// Gets the encoded channel data, if any, associated with this ChannelMonitor. + #[cfg(feature = "safe_channels")] + pub fn get_encoded_channel(&self) -> Option> { + self.inner.lock().unwrap().encoded_channel.clone() + } + + /// Updates the encoded channel data associated with this ChannelMonitor. To clear the encoded channel data (for + /// example after shut down of a channel), pass an empty vector. + #[cfg(feature = "safe_channels")] + pub fn update_encoded_channel(&self, encoded: Vec) { + self.inner.lock().unwrap().update_encoded_channel(encoded); + } + /// Gets the update_id from the latest ChannelMonitorUpdate which was applied to this /// ChannelMonitor. /// @@ -2719,6 +2798,55 @@ impl ChannelMonitor { } impl ChannelMonitorImpl { + #[cfg(feature = "safe_channels")] + fn check_encoded_channel_consistency(&self, encoded: &[u8]) { + let encoded_channel_reader = &mut &encoded[..]; + let check_res = read_check_data(encoded_channel_reader); + if let Ok(check_data) = check_res { + debug_assert!( + check_data.cur_holder_commitment_transaction_number + <= self.get_cur_holder_commitment_number(), + "cur_holder_commitment_transaction_number - channel: {} vs monitor: {}", + check_data.cur_holder_commitment_transaction_number, + self.get_cur_holder_commitment_number() + ); + debug_assert!( + check_data.revoked_counterparty_commitment_transaction_number + <= self.get_min_seen_secret(), + "revoked_counterparty_commitment_transaction_number - channel: {} vs monitor: {}", + check_data.revoked_counterparty_commitment_transaction_number, + self.get_min_seen_secret() + ); + debug_assert!( + check_data.cur_counterparty_commitment_transaction_number + <= self.get_cur_counterparty_commitment_number(), + "cur_counterparty_commitment_transaction_number - channel: {} vs monitor: {}", + check_data.cur_counterparty_commitment_transaction_number, + self.get_cur_counterparty_commitment_number() + ); + debug_assert!( + check_data.latest_monitor_update_id >= self.get_latest_update_id(), + "latest_monitor_update_id - channel: {} vs monitor: {}", + check_data.latest_monitor_update_id, + self.get_latest_update_id() + ); + } else { + debug_assert!(false, "Failed to read check data from encoded channel"); + } + } + + #[cfg(feature = "safe_channels")] + fn update_encoded_channel(&mut self, encoded: Vec) { + if encoded.len() > 0 { + // Check that the encoded channel is consistent with the rest of the monitor. This sets an invariant for the + // safe_channels feature. + self.check_encoded_channel_consistency(&encoded); + self.encoded_channel = Some(encoded); + } else { + self.encoded_channel = None; + } + } + /// Helper for get_claimable_balances which does the work for an individual HTLC, generating up /// to one `Balance` for the HTLC. #[rustfmt::skip] @@ -4405,6 +4533,13 @@ impl ChannelMonitorImpl { } } + // Assume that if the update contains no encoded channel, that the channel remained unchanged. We + // therefore do not update the monitor. + #[cfg(feature="safe_channels")] + if let Some(encoded_channel) = updates.encoded_channel.as_ref() { + self.update_encoded_channel(encoded_channel.clone()); + } + if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update { log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent"); Err(()) @@ -6644,6 +6779,33 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP let mut alternative_funding_confirmed = None; let mut is_manual_broadcast = RequiredWrapper(None); let mut funding_seen_onchain = RequiredWrapper(None); + #[cfg(not(feature="safe_channels"))] + read_tlv_fields!(reader, { + (1, funding_spend_confirmed, option), + (3, htlcs_resolved_on_chain, optional_vec), + (5, pending_monitor_events, optional_vec), + (7, funding_spend_seen, option), + (9, counterparty_node_id, option), + (11, confirmed_commitment_tx_counterparty_output, option), + (13, spendable_txids_confirmed, optional_vec), + (15, counterparty_fulfilled_htlcs, option), + (17, initial_counterparty_commitment_info, option), + (19, channel_id, option), + (21, balances_empty_height, option), + (23, holder_pays_commitment_tx_fee, option), + (25, payment_preimages_with_info, option), + (27, first_negotiated_funding_txo, (default_value, outpoint)), + (29, initial_counterparty_commitment_tx, option), + (31, channel_parameters, (option: ReadableArgs, None)), + (32, pending_funding, optional_vec), + (33, htlcs_resolved_to_user, option), + (34, alternative_funding_confirmed, option), + (35, is_manual_broadcast, (default_value, false)), + (37, funding_seen_onchain, (default_value, true)), + }); + #[cfg(feature="safe_channels")] + let mut encoded_channel = None; + #[cfg(feature="safe_channels")] read_tlv_fields!(reader, { (1, funding_spend_confirmed, option), (3, htlcs_resolved_on_chain, optional_vec), @@ -6666,6 +6828,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP (34, alternative_funding_confirmed, option), (35, is_manual_broadcast, (default_value, false)), (37, funding_seen_onchain, (default_value, true)), + (39, encoded_channel, option), }); // Note that `payment_preimages_with_info` was added (and is always written) in LDK 0.1, so // we can use it to determine if this monitor was last written by LDK 0.1 or later. @@ -6843,6 +7006,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP alternative_funding_confirmed, written_by_0_1_or_later, + #[cfg(feature="safe_channels")] + encoded_channel, }); if counterparty_node_id.is_none() { diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index b2b9520b94f..772671a1d31 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -6073,6 +6073,8 @@ where should_broadcast: broadcast, }], channel_id: Some(self.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: Some(Vec::new()), // Clear channel on shut down. }; Some((self.get_counterparty_node_id(), funding_txo, self.channel_id(), update)) } else { @@ -7316,6 +7318,8 @@ where payment_info, }], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: Some(self.encode()), }; if !self.context.channel_state.can_generate_new_commitment() { @@ -7429,6 +7433,10 @@ where // to be strictly increasing by one, so decrement it here. self.context.latest_monitor_update_id = monitor_update.update_id; monitor_update.updates.append(&mut additional_update.updates); + #[cfg(feature = "safe_channels")] + { + monitor_update.encoded_channel = Some(self.encode()); + } } else { let blocked_upd = self.context.blocked_monitor_updates.get(0); let new_mon_id = blocked_upd @@ -7932,7 +7940,8 @@ where ); self.context.latest_monitor_update_id += 1; - let monitor_update = ChannelMonitorUpdate { + #[allow(unused_mut)] + let mut monitor_update = ChannelMonitorUpdate { update_id: self.context.latest_monitor_update_id, updates: vec![ChannelMonitorUpdateStep::RenegotiatedFunding { channel_parameters: pending_splice_funding.channel_transaction_parameters.clone(), @@ -7940,6 +7949,8 @@ where counterparty_commitment_tx, }], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: None, }; self.context @@ -7949,6 +7960,10 @@ where .received_commitment_signed(); self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new()); + #[cfg(feature = "safe_channels")] + { + monitor_update.encoded_channel = Some(self.encode()); + } Ok(self.push_ret_blockable_mon_update(monitor_update)) } @@ -8205,6 +8220,8 @@ where update_id: self.context.latest_monitor_update_id, updates: vec![update], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: None, }; self.context.expecting_peer_commitment_signed = false; @@ -8229,6 +8246,10 @@ where } log_debug!(logger, "Received valid commitment_signed from peer, updated HTLC state but awaiting a monitor update resolution to reply.", ); + #[cfg(feature = "safe_channels")] + { + monitor_update.encoded_channel = Some(self.encode()); + } return Ok(self.push_ret_blockable_mon_update(monitor_update)); } @@ -8257,6 +8278,10 @@ where Vec::new(), Vec::new(), ); + #[cfg(feature = "safe_channels")] + { + monitor_update.encoded_channel = Some(self.encode()); + } return Ok(self.push_ret_blockable_mon_update(monitor_update)); } @@ -8309,6 +8334,8 @@ where update_id: self.context.latest_monitor_update_id + 1, // We don't increment this yet! updates: Vec::new(), channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: None, }; let mut htlc_updates = Vec::new(); @@ -8404,6 +8431,11 @@ where unreachable!() }; update_fulfill_count += 1; + + #[cfg(feature = "safe_channels")] + { + additional_monitor_update.encoded_channel = Some(self.encode()); + } monitor_update.updates.append(&mut additional_monitor_update.updates); None }, @@ -8462,6 +8494,11 @@ where update_add_count, update_fulfill_count, update_fail_count); self.monitor_updating_paused(false, true, false, Vec::new(), Vec::new(), Vec::new()); + + #[cfg(feature = "safe_channels")] + { + monitor_update.encoded_channel = Some(self.encode()); + } (self.push_ret_blockable_mon_update(monitor_update), htlcs_to_fail) } else { (None, Vec::new()) @@ -8578,6 +8615,8 @@ where secret: msg.per_commitment_secret, }], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: None, }; // Update state now that we've passed all the can-fail calls... @@ -8799,6 +8838,10 @@ where }; macro_rules! return_with_htlcs_to_fail { ($htlcs_to_fail: expr) => { + #[cfg(feature = "safe_channels")] + { + monitor_update.encoded_channel = Some(self.encode()); + } if !release_monitor { self.context .blocked_monitor_updates @@ -10417,6 +10460,8 @@ where scriptpubkey: self.get_closing_scriptpubkey(), }], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: Some(self.encode()), }; self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new()); self.push_ret_blockable_mon_update(monitor_update) @@ -10871,10 +10916,13 @@ where if self.context.blocked_monitor_updates.is_empty() { return None; } - Some(( - self.context.blocked_monitor_updates.remove(0).update, - !self.context.blocked_monitor_updates.is_empty(), - )) + #[allow(unused_mut)] + let mut update = self.context.blocked_monitor_updates.remove(0).update; + #[cfg(feature = "safe_channels")] + { + update.encoded_channel = Some(self.encode()); + } + Some((update, !self.context.blocked_monitor_updates.is_empty())) } /// Pushes a new monitor update into our monitor update queue, returning it if it should be @@ -11174,6 +11222,8 @@ where funding_txid: funding_txo.txid, }], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: Some(self.encode()), }; self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new()); let monitor_update = self.push_ret_blockable_mon_update(monitor_update); @@ -12723,12 +12773,14 @@ where } self.context.latest_monitor_update_id += 1; + self.context.channel_state.set_awaiting_remote_revoke(); let monitor_update = ChannelMonitorUpdate { update_id: self.context.latest_monitor_update_id, updates: vec![update], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: Some(self.encode()), }; - self.context.channel_state.set_awaiting_remote_revoke(); monitor_update } @@ -12973,6 +13025,8 @@ where scriptpubkey: self.get_closing_scriptpubkey(), }], channel_id: Some(self.context.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: Some(self.encode()), }; self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new()); self.push_ret_blockable_mon_update(monitor_update) @@ -15604,6 +15658,58 @@ where } } +#[cfg(feature = "safe_channels")] +pub struct ChannelStateCheckData { + pub cur_holder_commitment_transaction_number: u64, + pub revoked_counterparty_commitment_transaction_number: u64, + pub cur_counterparty_commitment_transaction_number: u64, + pub latest_monitor_update_id: u64, +} + +#[cfg(feature = "safe_channels")] +pub fn read_check_data(reader: &mut R) -> Result { + let ver = read_ver_prefix!(reader, SERIALIZATION_VERSION); + if ver <= 2 { + return Err(DecodeError::UnknownVersion); + } + + let _user_id_low: u64 = Readable::read(reader)?; + + let mut _config = LegacyChannelConfig::default(); + let mut _val: u64 = Readable::read(reader)?; + + let _channel_id: ChannelId = Readable::read(reader)?; + let channel_state = + ChannelState::from_u32(Readable::read(reader)?).map_err(|_| DecodeError::InvalidValue)?; + let _channel_value_satoshis: u64 = Readable::read(reader)?; + + let latest_monitor_update_id = Readable::read(reader)?; + + // Read the old serialization for shutdown_pubkey, preferring the TLV field later if set. + let mut _shutdown_scriptpubkey = match ::read(reader) { + Ok(pubkey) => Some(ShutdownScript::new_p2wpkh_from_pubkey(pubkey)), + Err(_) => None, + }; + let _destination_script: ScriptBuf = Readable::read(reader)?; + + let holder_commitment_next_transaction_number: u64 = Readable::read(reader)?; + let counterparty_next_commitment_transaction_number: u64 = Readable::read(reader)?; + + let cur_holder_commitment_transaction_number = holder_commitment_next_transaction_number + 1; + let revoked_counterparty_commitment_transaction_number = + counterparty_next_commitment_transaction_number + 2; + let cur_counterparty_commitment_transaction_number = + counterparty_next_commitment_transaction_number + + if channel_state.is_awaiting_remote_revoke() { 0 } else { 1 }; + + Ok(ChannelStateCheckData { + cur_holder_commitment_transaction_number, + revoked_counterparty_commitment_transaction_number, + cur_counterparty_commitment_transaction_number, + latest_monitor_update_id, + }) +} + fn duration_since_epoch() -> Option { #[cfg(not(feature = "std"))] let now = None; diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 3234f62eef4..7b0b247d592 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -9087,6 +9087,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ payment_info, }], channel_id: Some(prev_hop.channel_id), + #[cfg(feature = "safe_channels")] + encoded_channel: None, }; // We don't have any idea if this is a duplicate claim without interrogating the @@ -10305,6 +10307,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ fail_chan!("Already had channel with the new channel_id"); }, hash_map::Entry::Vacant(e) => { + #[cfg(feature = "safe_channels")] + { + monitor.update_encoded_channel(chan.encode()); + } let monitor_res = self.chain_monitor.watch_channel(monitor.channel_id(), monitor); if let Ok(persist_state) = monitor_res { // There's no problem signing a counterparty's funding transaction if our monitor @@ -10469,6 +10475,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ match chan .funding_signed(&msg, best_block, &self.signer_provider, &self.logger) .and_then(|(funded_chan, monitor)| { + #[cfg(feature = "safe_channels")] + { + monitor.update_encoded_channel(funded_chan.encode()); + } self.chain_monitor .watch_channel(funded_chan.context.channel_id(), monitor) .map_err(|()| { @@ -11186,6 +11196,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ if let Some(chan) = chan.as_funded_mut() { if let Some(monitor) = monitor_opt { + #[cfg(feature = "safe_channels")] + { + monitor.update_encoded_channel(chan.encode()); + } let monitor_res = self.chain_monitor.watch_channel(monitor.channel_id(), monitor); if let Ok(persist_state) = monitor_res { handle_initial_monitor!(self, persist_state, peer_state_lock, peer_state, @@ -13608,6 +13622,8 @@ where updates: vec![ChannelMonitorUpdateStep::ReleasePaymentComplete { htlc: htlc_id, }], + #[cfg(feature = "safe_channels")] + encoded_channel: None, }; let during_startup = @@ -16994,6 +17010,8 @@ where should_broadcast: true, }], channel_id: Some(monitor.channel_id()), + #[cfg(feature = "safe_channels")] + encoded_channel: Some(Vec::new()), }; log_info!( logger, @@ -17630,6 +17648,8 @@ where updates: vec![ChannelMonitorUpdateStep::ReleasePaymentComplete { htlc: htlc_id, }], + #[cfg(feature = "safe_channels")] + encoded_channel: None, }, }); }