diff --git a/mutiny-core/src/event.rs b/mutiny-core/src/event.rs index d4e2ba72b..64438ee01 100644 --- a/mutiny-core/src/event.rs +++ b/mutiny-core/src/event.rs @@ -688,6 +688,7 @@ impl EventHandler { channel_id, user_channel_id, counterparty_node_id, + funding_txo, .. } => { log_debug!( @@ -703,6 +704,31 @@ impl EventHandler { "ERROR: Could not delete channel open params, but continuing: {e}" ); } + + let all_channels = self.channel_manager.list_channels(); + let found_channel = all_channels.iter().find(|chan| { + chan.funding_txo.map(|a| a.into_bitcoin_outpoint()) == Some(funding_txo) + }); + if let Some(channel) = found_channel { + let closure = ChannelClosure::new_placeholder( + user_channel_id, + channel_id, + funding_txo, + counterparty_node_id, + channel.force_close_spend_delay, + ); + if let Err(e) = self + .persister + .persist_channel_closure(user_channel_id, closure) + { + log_error!(self.logger, "Failed to persist channel closure: {e}"); + } + } else { + log_warn!( + self.logger, + "WARNING: Could not find channel with funding txo {funding_txo:?} when calling list_channels in ChannelPending event" + ); + } } Event::HTLCIntercepted { .. } => {} Event::BumpTransaction(event) => match &event { diff --git a/mutiny-core/src/ldkstorage.rs b/mutiny-core/src/ldkstorage.rs index 96bfab263..1644b3e04 100644 --- a/mutiny-core/src/ldkstorage.rs +++ b/mutiny-core/src/ldkstorage.rs @@ -398,6 +398,14 @@ impl MutinyNodePersister { "{CHANNEL_CLOSURE_PREFIX}{}", user_channel_id.to_be_bytes().to_lower_hex_string() )); + + let force_close_spend_delay = self + .storage + .get_channel_closure(&key)? + .and_then(|c| c.force_close_spend_delay); + let mut closure = closure; + closure.set_force_close_spend_delay(force_close_spend_delay); + self.storage .write_data(key.clone(), &closure, Some(closure.timestamp as u32))?; @@ -873,6 +881,7 @@ mod test { reason: "This is a test.".to_string(), timestamp: utils::now().as_secs(), channel_funding_txo: None, + force_close_spend_delay: None, }; let result = persister.persist_channel_closure(user_channel_id, closure.clone()); assert!(result.is_ok()); diff --git a/mutiny-core/src/lib.rs b/mutiny-core/src/lib.rs index d6fad78d9..d67cefa6c 100644 --- a/mutiny-core/src/lib.rs +++ b/mutiny-core/src/lib.rs @@ -2335,6 +2335,7 @@ mod tests { reason: "".to_string(), timestamp: 1686258926, channel_funding_txo: None, + force_close_spend_delay: None, }; let closure_chan_id: u128 = 6969; node.persister diff --git a/mutiny-core/src/nodemanager.rs b/mutiny-core/src/nodemanager.rs index c493c2afa..5549fa625 100644 --- a/mutiny-core/src/nodemanager.rs +++ b/mutiny-core/src/nodemanager.rs @@ -142,6 +142,7 @@ pub struct MutinyChannel { pub is_outbound: bool, pub is_usable: bool, pub is_anchor: bool, + pub force_close_spend_delay: Option, } impl From<&ChannelDetails> for MutinyChannel { @@ -173,6 +174,7 @@ impl From<&ChannelDetails> for MutinyChannel { is_outbound: c.is_outbound, is_usable: c.is_usable, is_anchor, + force_close_spend_delay: c.force_close_spend_delay, } } } @@ -187,6 +189,7 @@ pub struct ChannelClosure { pub timestamp: u64, #[serde(default)] pub channel_funding_txo: Option, + pub force_close_spend_delay: Option, } impl ChannelClosure { @@ -206,6 +209,25 @@ impl ChannelClosure { node_id, reason: reason.to_string(), timestamp: utils::now().as_secs(), + force_close_spend_delay: None, + } + } + + pub fn new_placeholder( + user_channel_id: u128, + channel_id: ChannelId, + channel_funding_txo: OutPoint, + node_id: PublicKey, + force_close_spend_delay: Option, + ) -> Self { + Self { + user_channel_id: Some(user_channel_id.to_be_bytes()), + channel_id: Some(channel_id.0), + channel_funding_txo: Some(channel_funding_txo), + node_id: Some(node_id), + reason: "".to_string(), + timestamp: 0, // Ensure that the real timestamp is used to update vss when the channel is shut down + force_close_spend_delay, } } @@ -224,6 +246,14 @@ impl ChannelClosure { Ok(()) } + + pub fn set_force_close_spend_delay(&mut self, delay: Option) { + if self.force_close_spend_delay.is_some() { + return; + } + + self.force_close_spend_delay = delay; + } } impl PartialOrd for ChannelClosure { @@ -2395,6 +2425,7 @@ mod tests { reason: "".to_string(), timestamp: 1686258926, channel_funding_txo: None, + force_close_spend_delay: None, }; let tx1: TransactionDetails = TransactionDetails { diff --git a/mutiny-wasm/src/lib.rs b/mutiny-wasm/src/lib.rs index 7535f49c2..b0ef43347 100644 --- a/mutiny-wasm/src/lib.rs +++ b/mutiny-wasm/src/lib.rs @@ -970,6 +970,7 @@ impl MutinyWallet { .list_channel_closures() .await? .into_iter() + .filter(|closure| closure.timestamp != 0) // filter out placeholder closures .map(Into::into) .collect(); channel_closures.sort(); diff --git a/mutiny-wasm/src/models.rs b/mutiny-wasm/src/models.rs index db5251b65..17c4d283f 100644 --- a/mutiny-wasm/src/models.rs +++ b/mutiny-wasm/src/models.rs @@ -295,6 +295,7 @@ pub struct MutinyChannel { pub is_outbound: bool, pub is_usable: bool, pub is_anchor: bool, + pub force_close_spend_delay: Option, } #[wasm_bindgen] @@ -343,6 +344,7 @@ impl From for MutinyChannel { is_outbound: m.is_outbound, is_usable: m.is_usable, is_anchor: m.is_anchor, + force_close_spend_delay: m.force_close_spend_delay, } } } @@ -364,6 +366,7 @@ impl From for nodemanager::MutinyChannel { is_outbound: m.is_outbound, is_usable: m.is_usable, is_anchor: m.is_anchor, + force_close_spend_delay: m.force_close_spend_delay, } } } @@ -377,6 +380,7 @@ pub struct ChannelClosure { reason: String, pub timestamp: u64, channel_funding_txo: Option, + force_close_spend_delay: Option, } #[wasm_bindgen] @@ -405,6 +409,11 @@ impl ChannelClosure { pub fn channel_funding_txo(&self) -> Option { self.channel_funding_txo.clone() } + + #[wasm_bindgen(getter)] + pub fn force_close_spend_delay(&self) -> Option { + self.force_close_spend_delay + } } impl PartialOrd for ChannelClosure { @@ -440,6 +449,7 @@ impl From for ChannelClosure { reason: c.reason, timestamp: c.timestamp, channel_funding_txo: c.channel_funding_txo.map(|txo| format!("{}", txo)), + force_close_spend_delay: c.force_close_spend_delay, } } }