diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index 8787aac3b3e..2a5ce5f96b6 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -1654,6 +1654,48 @@ pub enum Event { /// [`ChannelManager::send_static_invoice`]: crate::ln::channelmanager::ChannelManager::send_static_invoice reply_path: Responder, }, + /// Indicates that a channel funding transaction constructed interactively is ready to be + /// signed. This event will only be triggered if at least one input was contributed. + /// + /// The transaction contains all inputs provided by both parties along with the channel's funding + /// output and a change output if applicable. + /// + /// No part of the transaction should be changed before signing as the content of the transaction + /// has already been negotiated with the counterparty. + /// + /// Each signature MUST use the `SIGHASH_ALL` flag to avoid invalidation of the initial commitment and + /// hence possible loss of funds. + /// + /// After signing, call [`ChannelManager::funding_transaction_signed`] with the (partially) signed + /// funding transaction. + /// + /// Generated in [`ChannelManager`] message handling. + /// + /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager + /// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed + FundingTransactionReadyForSigning { + /// The channel_id of the channel which you'll need to pass back into + /// [`ChannelManager::funding_transaction_signed`]. + /// + /// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed + channel_id: ChannelId, + /// The counterparty's node_id, which you'll need to pass back into + /// [`ChannelManager::funding_transaction_signed`]. + /// + /// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed + counterparty_node_id: PublicKey, + /// The `user_channel_id` value passed in for outbound channels, or for inbound channels if + /// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise + /// `user_channel_id` will be randomized for inbound channels. + /// + /// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels + user_channel_id: u128, + /// The unsigned transaction to be signed and passed back to + /// [`ChannelManager::funding_transaction_signed`]. + /// + /// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed + unsigned_transaction: Transaction, + }, } impl Writeable for Event { @@ -2095,6 +2137,10 @@ impl Writeable for Event { 47u8.write(writer)?; // Never write StaticInvoiceRequested events as buffered onion messages aren't serialized. }, + &Event::FundingTransactionReadyForSigning { .. } => { + 49u8.write(writer)?; + // We never write out FundingTransactionReadyForSigning events as they will be regenerated necessary. + }, // Note that, going forward, all new events must only write data inside of // `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write // data via `write_tlv_fields`. @@ -2672,6 +2718,8 @@ impl MaybeReadable for Event { // Note that we do not write a length-prefixed TLV for StaticInvoiceRequested events. #[cfg(async_payments)] 47u8 => Ok(None), + // Note that we do not write a length-prefixed TLV for FundingTransactionReadyForSigning events. + 49u8 => Ok(None), // Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue. // Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt // reads. diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 09c9f5804fc..e88c9bb773c 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -14,7 +14,7 @@ use bitcoin::constants::ChainHash; use bitcoin::script::{Builder, Script, ScriptBuf, WScriptHash}; use bitcoin::sighash::EcdsaSighashType; use bitcoin::transaction::{Transaction, TxIn, TxOut}; -use bitcoin::Weight; +use bitcoin::{Weight, Witness}; use bitcoin::hash_types::{BlockHash, Txid}; use bitcoin::hashes::sha256::Hash as Sha256; @@ -36,7 +36,7 @@ use crate::chain::channelmonitor::{ use crate::chain::transaction::{OutPoint, TransactionData}; use crate::chain::BestBlock; use crate::events::bump_transaction::BASE_INPUT_WEIGHT; -use crate::events::{ClosureReason, Event}; +use crate::events::ClosureReason; use crate::ln::chan_utils; #[cfg(splicing)] use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT; @@ -1766,7 +1766,7 @@ where pub fn funding_tx_constructed( &mut self, signing_session: InteractiveTxSigningSession, logger: &L, - ) -> Result<(msgs::CommitmentSigned, Option), ChannelError> + ) -> Result where L::Target: Logger, { @@ -1788,7 +1788,7 @@ where #[rustfmt::skip] pub fn commitment_signed( &mut self, msg: &msgs::CommitmentSigned, best_block: BestBlock, signer_provider: &SP, logger: &L - ) -> Result<(Option::EcdsaSigner>>, Option), ChannelError> + ) -> Result<(Option::EcdsaSigner>>, Option, Option), ChannelError> where L::Target: Logger { @@ -1815,7 +1815,7 @@ where pending_splice: None, }; let res = funded_channel.commitment_signed_initial_v2(msg, best_block, signer_provider, logger) - .map(|monitor| (Some(monitor), None)) + .map(|(monitor, funding_tx_opt)| (Some(monitor), None, funding_tx_opt)) // TODO: Change to `inspect_err` when MSRV is high enough. .map_err(|err| { // We always expect a `ChannelError` close. @@ -1842,15 +1842,15 @@ where let res = if has_negotiated_pending_splice && !session_received_commitment_signed { funded_channel .splice_initial_commitment_signed(msg, logger) - .map(|monitor_update_opt| (None, monitor_update_opt)) + .map(|monitor_update_opt| (None, monitor_update_opt, None)) } else { funded_channel.commitment_signed(msg, logger) - .map(|monitor_update_opt| (None, monitor_update_opt)) + .map(|monitor_update_opt| (None, monitor_update_opt, None)) }; #[cfg(not(splicing))] let res = funded_channel.commitment_signed(msg, logger) - .map(|monitor_update_opt| (None, monitor_update_opt)); + .map(|monitor_update_opt| (None, monitor_update_opt, None)); self.phase = ChannelPhase::Funded(funded_channel); res @@ -2317,7 +2317,7 @@ where monitor_pending_failures: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, monitor_pending_finalized_fulfills: Vec, monitor_pending_update_adds: Vec, - monitor_pending_tx_signatures: Option, + monitor_pending_tx_signatures: bool, /// If we went to send a revoke_and_ack but our signer was unable to give us a signature, /// we should retry at some point in the future when the signer indicates it may have a @@ -2928,7 +2928,7 @@ where #[rustfmt::skip] pub fn funding_tx_constructed( &mut self, mut signing_session: InteractiveTxSigningSession, logger: &L - ) -> Result<(msgs::CommitmentSigned, Option), ChannelError> + ) -> Result where L::Target: Logger { @@ -2970,7 +2970,8 @@ where }, }; - let funding_ready_for_sig_event = if signing_session.local_inputs_count() == 0 { + // Check that we have the expected number of local inputs + if signing_session.local_inputs_count() == 0 { debug_assert_eq!(our_funding_satoshis, 0); if signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new()).is_err() { debug_assert!( @@ -2982,31 +2983,7 @@ where ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) } ))); } - None - } else { - // TODO(dual_funding): Send event for signing if we've contributed funds. - // Inform the user that SIGHASH_ALL must be used for all signatures when contributing - // inputs/signatures. - // Also warn the user that we don't do anything to prevent the counterparty from - // providing non-standard witnesses which will prevent the funding transaction from - // confirming. This warning must appear in doc comments wherever the user is contributing - // funds, whether they are initiator or acceptor. - // - // The following warning can be used when the APIs allowing contributing inputs become available: - //
- // WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which - // will prevent the funding transaction from being relayed on the bitcoin network and hence being - // confirmed. - //
- debug_assert!( - false, - "We don't support users providing inputs but somehow we had more than zero inputs", - ); - return Err(ChannelError::Close(( - "V2 channel rejected due to sender error".into(), - ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) } - ))); - }; + } let mut channel_state = ChannelState::FundingNegotiated(FundingNegotiatedFlags::new()); channel_state.set_interactive_signing(); @@ -3016,7 +2993,7 @@ where self.interactive_tx_constructor.take(); self.interactive_tx_signing_session = Some(signing_session); - Ok((commitment_signed, funding_ready_for_sig_event)) + Ok(commitment_signed) } } @@ -3298,7 +3275,7 @@ where monitor_pending_failures: Vec::new(), monitor_pending_finalized_fulfills: Vec::new(), monitor_pending_update_adds: Vec::new(), - monitor_pending_tx_signatures: None, + monitor_pending_tx_signatures: false, signer_pending_revoke_and_ack: false, signer_pending_commitment_update: false, @@ -3544,7 +3521,7 @@ where monitor_pending_failures: Vec::new(), monitor_pending_finalized_fulfills: Vec::new(), monitor_pending_update_adds: Vec::new(), - monitor_pending_tx_signatures: None, + monitor_pending_tx_signatures: false, signer_pending_revoke_and_ack: false, signer_pending_commitment_update: false, @@ -6653,7 +6630,7 @@ where #[rustfmt::skip] pub fn commitment_signed_initial_v2( &mut self, msg: &msgs::CommitmentSigned, best_block: BestBlock, signer_provider: &SP, logger: &L - ) -> Result::EcdsaSigner>, ChannelError> + ) -> Result<(ChannelMonitor<::EcdsaSigner>, Option), ChannelError> where L::Target: Logger { if !self.context.channel_state.is_interactive_signing() @@ -6677,15 +6654,18 @@ where self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new()); - if let Some(tx_signatures) = self.interactive_tx_signing_session.as_mut().and_then( + if let Some(_) = self.interactive_tx_signing_session.as_mut().and_then( |session| session.received_commitment_signed() ) { // We're up first for submitting our tx_signatures, but our monitor has not persisted yet // so they'll be sent as soon as that's done. - self.context.monitor_pending_tx_signatures = Some(tx_signatures); + self.context.monitor_pending_tx_signatures = true; } + // Only build the unsigned transaction for signing if there are any holder inputs to actually sign + let funding_tx_opt = self.interactive_tx_signing_session.as_ref().and_then(|session| + session.local_inputs_count().gt(&0).then_some(session.unsigned_tx().build_unsigned_tx())); - Ok(channel_monitor) + Ok((channel_monitor, funding_tx_opt)) } /// Handles an incoming `commitment_signed` message for the first commitment transaction of the @@ -6772,7 +6752,7 @@ where .expect("Signing session must exist for negotiated pending splice") .received_commitment_signed(); self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new()); - self.context.monitor_pending_tx_signatures = tx_signatures; + self.context.monitor_pending_tx_signatures = tx_signatures.is_some(); Ok(self.push_ret_blockable_mon_update(monitor_update)) } @@ -7612,6 +7592,50 @@ where } } + pub fn funding_transaction_signed( + &mut self, witnesses: Vec, logger: &L, + ) -> Result, APIError> + where + L::Target: Logger, + { + if let Some(ref mut signing_session) = self.interactive_tx_signing_session { + let logger = WithChannelContext::from(logger, &self.context, None); + if let Some(holder_tx_signatures) = signing_session + .provide_holder_witnesses(self.context.channel_id, witnesses) + .map_err(|err| APIError::APIMisuseError { err })? + { + #[cfg(splicing)] + let is_monitor_update_in_progress = self.is_awaiting_initial_mon_persist() + || self + .pending_splice + .as_ref() + .and_then(|pending_splice| Some(pending_splice.funding.is_some())) + .unwrap_or(false) && self + .context + .channel_state + .is_monitor_update_in_progress(); + #[cfg(not(splicing))] + let is_monitor_update_in_progress = self.is_awaiting_initial_mon_persist(); + + if is_monitor_update_in_progress { + log_debug!(logger, "Not sending tx_signatures: a monitor update is in progress. Setting monitor_pending_tx_signatures."); + self.context.monitor_pending_tx_signatures = true; + return Ok(None); + } + return Ok(Some(holder_tx_signatures)); + } else { + return Ok(None); + } + } else { + return Err(APIError::APIMisuseError { + err: format!( + "Channel with id {} not expecting funding signatures", + self.context.channel_id + ), + }); + } + } + #[rustfmt::skip] pub fn tx_signatures(&mut self, msg: &msgs::TxSignatures, logger: &L) -> Result<(Option, Option), ChannelError> where L::Target: Logger @@ -7656,11 +7680,6 @@ where ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }, ))); } - - // TODO(dual_funding): Check all sigs are SIGHASH_ALL. - - // TODO(dual_funding): I don't see how we're going to be able to ensure witness-standardness - // for spending. Doesn't seem to be anything in rust-bitcoin. } let (holder_tx_signatures_opt, funding_tx_opt) = signing_session.received_tx_signatures(msg.clone()) @@ -7679,7 +7698,7 @@ where // and sets it as pending. if holder_tx_signatures_opt.is_some() && self.is_awaiting_initial_mon_persist() { log_debug!(logger, "Not sending tx_signatures: a monitor update is in progress. Setting monitor_pending_tx_signatures."); - self.context.monitor_pending_tx_signatures = holder_tx_signatures_opt; + self.context.monitor_pending_tx_signatures = true; return Ok((None, None)); } @@ -7938,14 +7957,14 @@ where // For channels established with V2 establishment we won't send a `tx_signatures` when we're in // MonitorUpdateInProgress (and we assume the user will never directly broadcast the funding // transaction and waits for us to do it). - let tx_signatures = self.context.monitor_pending_tx_signatures.take(); - if tx_signatures.is_some() { + let tx_signatures = if self.context.monitor_pending_tx_signatures { if self.context.channel_state.is_their_tx_signatures_sent() { self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new()); } else { self.context.channel_state.set_our_tx_signatures_ready(); } - } + self.interactive_tx_signing_session.as_ref().and_then(|session| session.holder_tx_signatures().clone()) + } else { None }; if self.context.channel_state.is_peer_disconnected() { self.context.monitor_pending_revoke_and_ack = false; @@ -8447,11 +8466,9 @@ where if self.context.channel_state.is_monitor_update_in_progress() { // The `monitor_pending_tx_signatures` field should have already been set in `commitment_signed_initial_v2` // if we were up first for signing and had a monitor update in progress, but check again just in case. - debug_assert!(self.context.monitor_pending_tx_signatures.is_some(), "monitor_pending_tx_signatures should already be set"); + debug_assert!(self.context.monitor_pending_tx_signatures, "monitor_pending_tx_signatures should already be set"); log_debug!(logger, "Not sending tx_signatures: a monitor update is in progress. Setting monitor_pending_tx_signatures."); - if self.context.monitor_pending_tx_signatures.is_none() { - self.context.monitor_pending_tx_signatures = session.holder_tx_signatures().clone(); - } + self.context.monitor_pending_tx_signatures = true; None } else { // If `holder_tx_signatures` is `None` here, the `tx_signatures` message will be sent @@ -13202,7 +13219,7 @@ where monitor_pending_failures, monitor_pending_finalized_fulfills: monitor_pending_finalized_fulfills.unwrap(), monitor_pending_update_adds: monitor_pending_update_adds.unwrap_or_default(), - monitor_pending_tx_signatures: None, + monitor_pending_tx_signatures: false, signer_pending_revoke_and_ack: false, signer_pending_commitment_update: false, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 8c034b23343..8bb60f26bb1 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -5931,6 +5931,86 @@ where result } + /// Handles a signed funding transaction generated by interactive transaction construction and + /// provided by the client. + /// + /// Do NOT broadcast the funding transaction yourself. When we have safely received our + /// counterparty's signature(s) the funding transaction will automatically be broadcast via the + /// [`BroadcasterInterface`] provided when this `ChannelManager` was constructed. + /// + /// `SIGHASH_ALL` MUST be used for all signatures when providing signatures. + /// + ///
+ /// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which + /// will prevent the funding transaction from being relayed on the bitcoin network and hence being + /// confirmed. + ///
+ /// + /// Returns [`ChannelUnavailable`] when a channel is not found or an incorrect + /// `counterparty_node_id` is provided. + /// + /// Returns [`APIMisuseError`] when a channel is not in a state where it is expecting funding + /// signatures. + /// + /// [`ChannelUnavailable`]: APIError::ChannelUnavailable + /// [`APIMisuseError`]: APIError::APIMisuseError + pub fn funding_transaction_signed( + &self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, transaction: Transaction, + ) -> Result<(), APIError> { + let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); + let witnesses: Vec<_> = transaction + .input + .into_iter() + .filter_map(|input| if input.witness.is_empty() { None } else { Some(input.witness) }) + .collect(); + + let per_peer_state = self.per_peer_state.read().unwrap(); + let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| { + APIError::ChannelUnavailable { + err: format!( + "Can't find a peer matching the passed counterparty node_id {}", + counterparty_node_id + ), + } + })?; + + let mut peer_state_lock = peer_state_mutex.lock().unwrap(); + let peer_state = &mut *peer_state_lock; + + match peer_state.channel_by_id.get_mut(channel_id) { + Some(channel) => match channel.as_funded_mut() { + Some(chan) => { + if let Some(tx_signatures) = + chan.funding_transaction_signed(witnesses, &self.logger)? + { + peer_state.pending_msg_events.push(MessageSendEvent::SendTxSignatures { + node_id: *counterparty_node_id, + msg: tx_signatures, + }); + } + }, + None => { + return Err(APIError::APIMisuseError { + err: format!( + "Channel with id {} not expecting funding signatures", + channel_id + ), + }) + }, + }, + None => { + return Err(APIError::ChannelUnavailable { + err: format!( + "Channel with id {} not found for the passed counterparty node_id {}", + channel_id, counterparty_node_id + ), + }) + }, + } + + Ok(()) + } + /// Atomically applies partial updates to the [`ChannelConfig`] of the given channels. /// /// Once the updates are applied, each eligible channel (advertised with a known short channel @@ -9057,14 +9137,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ peer_state.pending_msg_events.push(msg_send_event); }; if let Some(signing_session) = signing_session_opt { - let (commitment_signed, funding_ready_for_sig_event_opt) = chan_entry + let commitment_signed = chan_entry .get_mut() .funding_tx_constructed(signing_session, &self.logger) .map_err(|err| MsgHandleErrInternal::send_err_msg_no_close(format!("{}", err), msg.channel_id))?; - if let Some(funding_ready_for_sig_event) = funding_ready_for_sig_event_opt { - let mut pending_events = self.pending_events.lock().unwrap(); - pending_events.push_back((funding_ready_for_sig_event, None)); - } peer_state.pending_msg_events.push(MessageSendEvent::UpdateHTLCs { node_id: counterparty_node_id, channel_id: msg.channel_id, @@ -9596,11 +9672,21 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ let chan = chan_entry.get_mut(); let logger = WithChannelContext::from(&self.logger, &chan.context(), None); let funding_txo = chan.funding().get_funding_txo(); - let (monitor_opt, monitor_update_opt) = try_channel_entry!( + let (monitor_opt, monitor_update_opt, funding_tx_opt) = try_channel_entry!( self, peer_state, chan.commitment_signed(msg, best_block, &self.signer_provider, &&logger), chan_entry); if let Some(chan) = chan.as_funded_mut() { + if let Some(unsigned_transaction) = funding_tx_opt { + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back(( + Event::FundingTransactionReadyForSigning { + unsigned_transaction, + counterparty_node_id: *counterparty_node_id, + channel_id: msg.channel_id, + user_channel_id: chan.context.get_user_id(), + }, None)); + } if let Some(monitor) = monitor_opt { let monitor_res = self.chain_monitor.watch_channel(monitor.channel_id(), monitor); if let Ok(persist_state) = monitor_res { diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs index a1c3c8fcf94..da27274665b 100644 --- a/lightning/src/ln/interactivetxs.rs +++ b/lightning/src/ln/interactivetxs.rs @@ -396,9 +396,14 @@ impl InteractiveTxSigningSession { /// unsigned transaction. pub fn provide_holder_witnesses( &mut self, channel_id: ChannelId, witnesses: Vec, - ) -> Result<(), ()> { - if self.local_inputs_count() != witnesses.len() { - return Err(()); + ) -> Result, String> { + let local_inputs_count = self.local_inputs_count(); + if local_inputs_count != witnesses.len() { + return Err(format!( + "Provided witness count of {} does not match required count for {} inputs", + witnesses.len(), + local_inputs_count + )); } self.unsigned_tx.add_local_witnesses(witnesses.clone()); @@ -409,7 +414,11 @@ impl InteractiveTxSigningSession { shared_input_signature: None, }); - Ok(()) + if self.holder_sends_tx_signatures_first && self.has_received_commitment_signed { + Ok(self.holder_tx_signatures.clone()) + } else { + Ok(None) + } } pub fn remote_inputs_count(&self) -> usize {