Skip to content

Commit 9ff296f

Browse files
committed
Introduce FundingTransactionReadyForSignatures event
The `FundingTransactionReadyForSignatures` event requests witnesses from the client for their contributed inputs to an interactively constructed transaction. The client calls `ChannelManager::funding_transaction_signed` to provide the witnesses to LDK.
1 parent 252b231 commit 9ff296f

File tree

4 files changed

+191
-29
lines changed

4 files changed

+191
-29
lines changed

lightning/src/events/mod.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,55 @@ pub enum Event {
15721572
/// onion messages.
15731573
peer_node_id: PublicKey,
15741574
},
1575+
/// Indicates that a funding transaction constructed via interactive transaction construction for a
1576+
/// channel is ready to be signed by the client. This event will only be triggered
1577+
/// if at least one input was contributed by the holder and needs to be signed.
1578+
///
1579+
/// The transaction contains all inputs provided by both parties along with the channel's funding
1580+
/// output and a change output if applicable.
1581+
///
1582+
/// No part of the transaction should be changed before signing as the content of the transaction
1583+
/// has already been negotiated with the counterparty.
1584+
///
1585+
/// Each signature MUST use the SIGHASH_ALL flag to avoid invalidation of the initial commitment and
1586+
/// hence possible loss of funds.
1587+
///
1588+
/// After signing, call [`ChannelManager::funding_transaction_signed`] with the (partially) signed
1589+
/// funding transaction.
1590+
///
1591+
/// Generated in [`ChannelManager`] message handling.
1592+
///
1593+
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
1594+
/// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed
1595+
FundingTransactionReadyForSigning {
1596+
/// The channel_id of the channel which you'll need to pass back into
1597+
/// [`ChannelManager::funding_transaction_signed`].
1598+
///
1599+
/// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed
1600+
channel_id: ChannelId,
1601+
/// The counterparty's node_id, which you'll need to pass back into
1602+
/// [`ChannelManager::funding_transaction_signed`].
1603+
///
1604+
/// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed
1605+
counterparty_node_id: PublicKey,
1606+
// TODO(dual_funding): Enable links when methods are implemented
1607+
/// The `user_channel_id` value passed in to `ChannelManager::create_dual_funded_channel` for outbound
1608+
/// channels, or to [`ChannelManager::accept_inbound_channel`] or `ChannelManager::accept_inbound_channel_with_contribution`
1609+
/// for inbound channels if [`UserConfig::manually_accept_inbound_channels`] config flag is set to true.
1610+
/// Otherwise `user_channel_id` will be randomized for an inbound channel.
1611+
/// This may be zero for objects serialized with LDK versions prior to 0.0.113.
1612+
///
1613+
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
1614+
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
1615+
// [`ChannelManager::create_dual_funded_channel`]: crate::ln::channelmanager::ChannelManager::create_dual_funded_channel
1616+
// [`ChannelManager::accept_inbound_channel_with_contribution`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel_with_contribution
1617+
user_channel_id: u128,
1618+
/// The unsigned transaction to be signed and passed back to
1619+
/// [`ChannelManager::funding_transaction_signed`].
1620+
///
1621+
/// [`ChannelManager::funding_transaction_signed`]: crate::ln::channelmanager::ChannelManager::funding_transaction_signed
1622+
unsigned_transaction: Transaction,
1623+
},
15751624
}
15761625

15771626
impl Writeable for Event {
@@ -1996,6 +2045,13 @@ impl Writeable for Event {
19962045
(8, former_temporary_channel_id, required),
19972046
});
19982047
},
2048+
&Event::FundingTransactionReadyForSigning { .. } => {
2049+
45u8.write(writer)?;
2050+
// We never write out FundingTransactionReadyForSigning events as, upon disconnection, peers
2051+
// drop any V2-established/spliced channels which have not yet exchanged the initial `commitment_signed`.
2052+
// We only exhange the initial `commitment_signed` after the client calls
2053+
// `ChannelManager::funding_transaction_signed` and ALWAYS before we send a `tx_signatures`
2054+
},
19992055
// Note that, going forward, all new events must only write data inside of
20002056
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
20012057
// data via `write_tlv_fields`.
@@ -2560,6 +2616,10 @@ impl MaybeReadable for Event {
25602616
former_temporary_channel_id: former_temporary_channel_id.0.unwrap(),
25612617
}))
25622618
},
2619+
45u8 => {
2620+
// Value 45 is used for `Event::FundingTransactionReadyForSigning`.
2621+
Ok(None)
2622+
},
25632623
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
25642624
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
25652625
// reads.

lightning/src/ln/channel.rs

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use bitcoin::constants::ChainHash;
1414
use bitcoin::script::{Builder, Script, ScriptBuf, WScriptHash};
1515
use bitcoin::sighash::EcdsaSighashType;
1616
use bitcoin::transaction::{Transaction, TxIn, TxOut};
17-
use bitcoin::Weight;
17+
use bitcoin::{Weight, Witness};
1818

1919
use bitcoin::hash_types::{BlockHash, Txid};
2020
use bitcoin::hashes::sha256::Hash as Sha256;
@@ -2800,7 +2800,7 @@ where
28002800
},
28012801
};
28022802

2803-
let funding_ready_for_sig_event = if signing_session.local_inputs_count() == 0 {
2803+
let funding_ready_for_sig_event_opt = if signing_session.local_inputs_count() == 0 {
28042804
debug_assert_eq!(our_funding_satoshis, 0);
28052805
if signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new()).is_err() {
28062806
debug_assert!(
@@ -2814,28 +2814,12 @@ where
28142814
}
28152815
None
28162816
} else {
2817-
// TODO(dual_funding): Send event for signing if we've contributed funds.
2818-
// Inform the user that SIGHASH_ALL must be used for all signatures when contributing
2819-
// inputs/signatures.
2820-
// Also warn the user that we don't do anything to prevent the counterparty from
2821-
// providing non-standard witnesses which will prevent the funding transaction from
2822-
// confirming. This warning must appear in doc comments wherever the user is contributing
2823-
// funds, whether they are initiator or acceptor.
2824-
//
2825-
// The following warning can be used when the APIs allowing contributing inputs become available:
2826-
// <div class="warning">
2827-
// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
2828-
// will prevent the funding transaction from being relayed on the bitcoin network and hence being
2829-
// confirmed.
2830-
// </div>
2831-
debug_assert!(
2832-
false,
2833-
"We don't support users providing inputs but somehow we had more than zero inputs",
2834-
);
2835-
return Err(ChannelError::Close((
2836-
"V2 channel rejected due to sender error".into(),
2837-
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }
2838-
)));
2817+
Some(Event::FundingTransactionReadyForSigning {
2818+
channel_id: self.context.channel_id,
2819+
counterparty_node_id: self.context.counterparty_node_id,
2820+
user_channel_id: self.context.user_id,
2821+
unsigned_transaction: signing_session.unsigned_tx().build_unsigned_tx(),
2822+
})
28392823
};
28402824

28412825
let mut channel_state = ChannelState::FundingNegotiated(FundingNegotiatedFlags::new());
@@ -2846,7 +2830,7 @@ where
28462830
self.interactive_tx_constructor.take();
28472831
self.interactive_tx_signing_session = Some(signing_session);
28482832

2849-
Ok((commitment_signed, funding_ready_for_sig_event))
2833+
Ok((commitment_signed, funding_ready_for_sig_event_opt))
28502834
}
28512835
}
28522836

@@ -7104,6 +7088,45 @@ where
71047088
}
71057089
}
71067090

7091+
fn verify_interactive_tx_signatures(&mut self, _witnesses: &Vec<Witness>) {
7092+
if let Some(ref mut _signing_session) = self.interactive_tx_signing_session {
7093+
// Check that sighash_all was used:
7094+
// TODO(dual_funding): Check sig for sighash
7095+
}
7096+
}
7097+
7098+
pub fn funding_transaction_signed<L: Deref>(
7099+
&mut self, witnesses: Vec<Witness>, logger: &L,
7100+
) -> Result<Option<msgs::TxSignatures>, APIError>
7101+
where
7102+
L::Target: Logger,
7103+
{
7104+
self.verify_interactive_tx_signatures(&witnesses);
7105+
if let Some(ref mut signing_session) = self.interactive_tx_signing_session {
7106+
let logger = WithChannelContext::from(logger, &self.context, None);
7107+
if let Some(holder_tx_signatures) = signing_session
7108+
.provide_holder_witnesses(self.context.channel_id, witnesses)
7109+
.map_err(|err| APIError::APIMisuseError { err })?
7110+
{
7111+
if self.is_awaiting_initial_mon_persist() {
7112+
log_debug!(logger, "Not sending tx_signatures: a monitor update is in progress. Setting monitor_pending_tx_signatures.");
7113+
self.context.monitor_pending_tx_signatures = Some(holder_tx_signatures);
7114+
return Ok(None);
7115+
}
7116+
return Ok(Some(holder_tx_signatures));
7117+
} else {
7118+
return Ok(None);
7119+
}
7120+
} else {
7121+
return Err(APIError::APIMisuseError {
7122+
err: format!(
7123+
"Channel with id {} not expecting funding signatures",
7124+
self.context.channel_id
7125+
),
7126+
});
7127+
}
7128+
}
7129+
71077130
#[rustfmt::skip]
71087131
pub fn tx_signatures<L: Deref>(&mut self, msg: &msgs::TxSignatures, logger: &L) -> Result<(Option<Transaction>, Option<msgs::TxSignatures>), ChannelError>
71097132
where L::Target: Logger

lightning/src/ln/channelmanager.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5863,6 +5863,76 @@ where
58635863
result
58645864
}
58655865

5866+
/// Handles a signed funding transaction generated by interactive transaction construction and
5867+
/// provided by the client.
5868+
///
5869+
/// Do NOT broadcast the funding transaction yourself. When we have safely received our
5870+
/// counterparty's signature(s) the funding transaction will automatically be broadcast via the
5871+
/// [`BroadcasterInterface`] provided when this `ChannelManager` was constructed.
5872+
///
5873+
/// SIGHASH_ALL MUST be used for all signatures when providing signatures.
5874+
///
5875+
/// <div class="warning">
5876+
/// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
5877+
/// will prevent the funding transaction from being relayed on the bitcoin network and hence being
5878+
/// confirmed.
5879+
/// </div>
5880+
pub fn funding_transaction_signed(
5881+
&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, transaction: Transaction,
5882+
) -> Result<(), APIError> {
5883+
let witnesses: Vec<_> = transaction
5884+
.input
5885+
.into_iter()
5886+
.filter_map(|input| if input.witness.is_empty() { None } else { Some(input.witness) })
5887+
.collect();
5888+
5889+
let per_peer_state = self.per_peer_state.read().unwrap();
5890+
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
5891+
APIError::ChannelUnavailable {
5892+
err: format!(
5893+
"Can't find a peer matching the passed counterparty node_id {}",
5894+
counterparty_node_id
5895+
),
5896+
}
5897+
})?;
5898+
5899+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
5900+
let peer_state = &mut *peer_state_lock;
5901+
5902+
match peer_state.channel_by_id.get_mut(channel_id) {
5903+
Some(channel) => match channel.as_funded_mut() {
5904+
Some(chan) => {
5905+
if let Some(tx_signatures) =
5906+
chan.funding_transaction_signed(witnesses, &self.logger)?
5907+
{
5908+
peer_state.pending_msg_events.push(MessageSendEvent::SendTxSignatures {
5909+
node_id: *counterparty_node_id,
5910+
msg: tx_signatures,
5911+
});
5912+
}
5913+
},
5914+
None => {
5915+
return Err(APIError::APIMisuseError {
5916+
err: format!(
5917+
"Channel with id {} not expecting funding signatures",
5918+
channel_id
5919+
),
5920+
})
5921+
},
5922+
},
5923+
None => {
5924+
return Err(APIError::ChannelUnavailable {
5925+
err: format!(
5926+
"Channel with id {} not found for the passed counterparty node_id {}",
5927+
channel_id, counterparty_node_id
5928+
),
5929+
})
5930+
},
5931+
}
5932+
5933+
Ok(())
5934+
}
5935+
58665936
/// Atomically applies partial updates to the [`ChannelConfig`] of the given channels.
58675937
///
58685938
/// Once the updates are applied, each eligible channel (advertised with a known short channel

lightning/src/ln/interactivetxs.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,14 @@ impl InteractiveTxSigningSession {
407407
/// unsigned transaction.
408408
pub fn provide_holder_witnesses(
409409
&mut self, channel_id: ChannelId, witnesses: Vec<Witness>,
410-
) -> Result<(), ()> {
411-
if self.local_inputs_count() != witnesses.len() {
412-
return Err(());
410+
) -> Result<Option<TxSignatures>, String> {
411+
let local_inputs_count = self.local_inputs_count();
412+
if local_inputs_count != witnesses.len() {
413+
return Err(format!(
414+
"Provided witness count of {} does not match required count for {} inputs",
415+
witnesses.len(),
416+
local_inputs_count
417+
));
413418
}
414419

415420
self.unsigned_tx.add_local_witnesses(witnesses.clone());
@@ -420,7 +425,11 @@ impl InteractiveTxSigningSession {
420425
shared_input_signature: None,
421426
});
422427

423-
Ok(())
428+
if self.holder_sends_tx_signatures_first && self.has_received_commitment_signed {
429+
Ok(self.holder_tx_signatures.clone())
430+
} else {
431+
Ok(None)
432+
}
424433
}
425434

426435
pub fn remote_inputs_count(&self) -> usize {

0 commit comments

Comments
 (0)