diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index b830d2e90ba..1a4ae8c0dc8 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -1676,6 +1676,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 { @@ -2117,6 +2159,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`. @@ -2694,6 +2740,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 fc161c5a7c6..f2131e1d281 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -12,9 +12,9 @@ use bitcoin::amount::Amount; use bitcoin::consensus::encode; use bitcoin::constants::ChainHash; use bitcoin::script::{Builder, Script, ScriptBuf, WScriptHash}; -use bitcoin::sighash::EcdsaSighashType; +use bitcoin::sighash::{EcdsaSighashType, SighashCache}; use bitcoin::transaction::{Transaction, TxIn, TxOut}; -use bitcoin::Weight; +use bitcoin::{TapSighashType, Weight, Witness, XOnlyPublicKey}; use bitcoin::hash_types::{BlockHash, Txid}; use bitcoin::hashes::sha256::Hash as Sha256; @@ -23,7 +23,7 @@ use bitcoin::hashes::Hash; use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE; use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1}; -use bitcoin::secp256k1::{PublicKey, SecretKey}; +use bitcoin::secp256k1::{Message, PublicKey, SecretKey}; use bitcoin::{secp256k1, sighash}; use crate::chain::chaininterface::{ @@ -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, { @@ -1786,7 +1786,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 { @@ -1813,7 +1813,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. @@ -1840,15 +1840,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 @@ -2315,7 +2315,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 @@ -2916,7 +2916,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 { @@ -2954,7 +2954,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!( @@ -2965,30 +2966,7 @@ where let reason = ClosureReason::ProcessingError { err: msg.to_owned() }; return Err(ChannelError::Close((msg.to_owned(), reason))); } - 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", - ); - let msg = "V2 channel rejected due to sender error"; - let reason = ClosureReason::ProcessingError { err: msg.to_owned() }; - return Err(ChannelError::Close((msg.to_owned(), reason))); - }; + } let mut channel_state = ChannelState::FundingNegotiated(FundingNegotiatedFlags::new()); channel_state.set_interactive_signing(); @@ -2998,7 +2976,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) } } @@ -3280,7 +3258,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, @@ -3526,7 +3504,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, @@ -6648,7 +6626,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() @@ -6670,15 +6648,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 @@ -6765,7 +6746,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)) } @@ -7605,6 +7586,188 @@ where } } + fn verify_interactive_tx_signatures( + &mut self, witnesses: &Vec, + ) -> Result<(), APIError> { + if let Some(session) = &self.interactive_tx_signing_session { + let unsigned_tx = session.unsigned_tx(); + let built_tx = unsigned_tx.build_unsigned_tx(); + let prev_outputs: Vec<&TxOut> = + unsigned_tx.inputs().map(|input| input.prev_output()).collect::>(); + let all_prevouts = sighash::Prevouts::All(&prev_outputs[..]); + + let mut cache = SighashCache::new(&built_tx); + let secp = Secp256k1::verification_only(); + + let script_pubkeys = unsigned_tx + .inputs() + .enumerate() + .filter(|(_, input)| input.is_local(unsigned_tx.holder_is_initiator())); + + 'inputs: for ((input_idx, input), witness) in script_pubkeys.zip(witnesses) { + let prev_output = input.prev_output(); + let script_pubkey = &prev_output.script_pubkey; + + // P2WPKH + if script_pubkey.is_p2wpkh() { + if witness.len() != 2 { + return Err(APIError::APIMisuseError { + err: format!( + "The witness for input at index {input_idx} does not have the correct number of elements for a P2WPKH spend. Expected 2 got {}", + witness.len() + ), + }); + } + let sighash = cache + .p2wpkh_signature_hash( + input_idx, + script_pubkey, + prev_output.value, + EcdsaSighashType::All, + ) + .expect("Transaction is ready for signing"); + let msg = Message::from_digest_slice(&sighash[..]).unwrap(); + let pubkey = PublicKey::from_slice(&witness[1]).map_err(|_| APIError::APIMisuseError { err: format!("The witness for input at index {input_idx} contains an invalid ECDSA public key") })?; + let sig = bitcoin::ecdsa::Signature::from_slice(&witness[0]).map_err(|_| APIError::APIMisuseError { err: format!("The witness for input at index {input_idx} contains an invalid signature") })?; + secp.verify_ecdsa(&msg, &sig.signature, &pubkey).map_err(|_| { + APIError::APIMisuseError { err: + format!("Failed signature verification for input at index {input_idx} for P2WPKH spend.") } + })?; + continue 'inputs; + } + + // P2TR key path spend witness includes signature and optional annex + if script_pubkey.is_p2tr() && witness.len() <= 2 { + if witness.len() == 0 { + return Err(APIError::APIMisuseError { + err: format!( + "The witness for input at index {input_idx} for a P2TR spend has 0 elements, which is invalid", + ), + }); + } + let sighash = cache + .taproot_key_spend_signature_hash( + input_idx, + &all_prevouts, + TapSighashType::All, + ) + .expect("Transaction is ready for signing"); + let msg = Message::from_digest_slice(&sighash[..]).unwrap(); + let pubkey = match script_pubkey.instructions().collect::>>()[1] { + Ok(bitcoin::script::Instruction::PushBytes(push_bytes)) => { + XOnlyPublicKey::from_slice(push_bytes.as_bytes()) + }, + _ => return Err(APIError::APIMisuseError { err: format!("The scriptPubKey of the previous output for input at index {input_idx} for a P2TR key path spend has an invalid public key") }), + }.map_err(|_| APIError::APIMisuseError { err: format!("") })?; + let sig = bitcoin::taproot::Signature::from_slice(&witness[0]).map_err(|_| APIError::APIMisuseError { err: format!("The witness for input at index {input_idx} for a P2TR key path spend has an invalid signature") })?; + secp.verify_schnorr(&sig.signature, &msg, &pubkey).map_err(|_| { + APIError::APIMisuseError { err: format!("Failed signature verification for input at index {input_idx} for P2WPKH spend.") } + })?; + continue 'inputs; + } + + // P2WSH - No validation just sighash checks + if script_pubkey.is_p2wsh() { + 'elements: for element in witness { + match element.len() { + // Possibly a DER-encoded ECDSA signature with a sighash type byte assuming low-S + 70..72 => { + if bitcoin::ecdsa::Signature::from_slice(element) + .map(|sig| matches!(sig.sighash_type, EcdsaSighashType::All)) + .unwrap_or(true) + { + continue 'elements; + } + + return Err(APIError::APIMisuseError { + err: format!( + "An ECDSA signature in the witness for input {input_idx} does not use SIGHASH_ALL" + ), + }); + }, + _ => continue 'elements, + } + } + continue 'inputs; + } + + // P2TR script path - No validation, just sighash checks + if script_pubkey.is_p2tr() { + 'elements: for element in witness { + match element.len() { + // Schnorr sig + sighash type byte. + // If this were just 64 bytes, it would implicitly be SIGHASH_DEFAULT (= SIGHASH_ALL) + 65 => { + if bitcoin::taproot::Signature::from_slice(element) + .map(|sig| matches!(sig.sighash_type, TapSighashType::All)) + .unwrap_or(true) + { + continue 'elements; + } + + return Err(APIError::APIMisuseError { + err: + format!("A (likely) Schnorr signature in the witness for input {input_idx} does not use SIGHASH_DEFAULT or SIGHASH_ALL") + }); + }, + _ => continue 'elements, + } + } + continue 'inputs; + } + + debug_assert!(false, "We don't allow contributing inputs that are not spending P2WPKH, P2WSH, or P2TR"); + return Err(APIError::APIMisuseError { err: format!("Input at index {input_idx} does not spend from one of P2WPKH, P2WSH, or P2TR") }); + } + } + Ok(()) + } + + pub fn funding_transaction_signed( + &mut self, witnesses: Vec, logger: &L, + ) -> Result, APIError> + where + L::Target: Logger, + { + self.verify_interactive_tx_signatures(&witnesses)?; + 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 @@ -7666,7 +7829,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)); } @@ -7925,14 +8088,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; @@ -8428,11 +8591,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 @@ -13192,7 +13353,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 d0d429a0abc..4c2227a9e95 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -5890,6 +5890,99 @@ 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, otherwise your + /// funds can be held hostage! + /// + /// LDK checks the following: + /// * Each input spends an output that is one of P2WPKH, P2WSH, or P2TR. + /// These were already checked by LDK when the inputs to be contributed were provided. + /// * All signatures use the `SIGHASH_ALL` sighash type. + /// * P2WPKH and P2TR key path spends are valid (verifies signatures) + /// + /// NOTE: + /// * When checking P2WSH spends, LDK tries to decode 70-72 byte witness elements as ECDSA + /// signatures with a sighash flag. If the internal DER-decoding fails, then LDK just + /// assumes it wasn't a signature and carries with checks. If the element can be decoded + /// as an ECDSA signature, the the sighash flag must be `SIGHASH_ALL`. + /// * When checking P2TR script-path spends, LDK assumes all elements of exactly 65 bytes + /// with the last byte matching any valid sighash flag byte are schnorr signatures and checks + /// that the sighash type is `SIGHASH_ALL`. If the last byte is not any valid sighash flag, the + /// element is assumed not to be a signature and is ignored. Elements of 64 bytes are not + /// checked because if they were schnorr signatures then they would implicitly be `SIGHASH_DEFAULT` + /// which is an alias of `SIGHASH_ALL`. + /// + /// 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 or if any of the checks described above fail. + /// + /// [`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 @@ -9008,14 +9101,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, @@ -9545,11 +9634,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 acf28cf8fd3..5c99621f59e 100644 --- a/lightning/src/ln/interactivetxs.rs +++ b/lightning/src/ln/interactivetxs.rs @@ -210,12 +210,24 @@ pub(crate) struct NegotiatedTxInput { txin: TxIn, // The weight of the input including an estimate of its witness weight. weight: Weight, + prev_output: TxOut, +} + +impl NegotiatedTxInput { + pub(super) fn is_local(&self, holder_is_initiator: bool) -> bool { + !is_serial_id_valid_for_counterparty(holder_is_initiator, self.serial_id) + } + + pub(super) fn prev_output(&self) -> &TxOut { + &self.prev_output + } } impl_writeable_tlv_based!(NegotiatedTxInput, { (1, serial_id, required), (3, txin, required), (5, weight, required), + (7, prev_output, required), }); impl_writeable_tlv_based!(ConstructedTransaction, { @@ -361,6 +373,10 @@ impl ConstructedTransaction { .zip(witnesses) .for_each(|(input, witness)| input.witness = witness); } + + pub fn holder_is_initiator(&self) -> bool { + self.holder_is_initiator + } } /// The InteractiveTxSigningSession coordinates the signing flow of interactively constructed @@ -447,9 +463,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()); @@ -460,7 +481,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 { @@ -1349,6 +1374,13 @@ impl InputOwned { InputOwned::Shared(shared) => estimate_input_weight(&shared.prev_output), } } + + fn prev_output(&self) -> &TxOut { + match self { + InputOwned::Single(single) => &single.prev_output, + InputOwned::Shared(shared) => &shared.prev_output, + } + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1520,7 +1552,13 @@ impl InteractiveTxInput { fn into_negotiated_input(self) -> NegotiatedTxInput { let weight = self.input.estimate_input_weight(); - NegotiatedTxInput { serial_id: self.serial_id, txin: self.input.into_tx_in(), weight } + let prev_output = self.input.prev_output().clone(); + NegotiatedTxInput { + serial_id: self.serial_id, + txin: self.input.into_tx_in(), + weight, + prev_output, + } } }