Skip to content

Commit fff6eaf

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 5a13036 commit fff6eaf

File tree

4 files changed

+164
-29
lines changed

4 files changed

+164
-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: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use bitcoin::transaction::{Transaction, TxIn, TxOut};
1616
use bitcoin::sighash::EcdsaSighashType;
1717
use bitcoin::consensus::encode;
1818
use bitcoin::absolute::LockTime;
19-
use bitcoin::Weight;
19+
use bitcoin::{Weight, Witness};
2020

2121
use bitcoin::hashes::Hash;
2222
use bitcoin::hashes::sha256::Hash as Sha256;
@@ -2637,7 +2637,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
26372637
},
26382638
};
26392639

2640-
let funding_ready_for_sig_event = if signing_session.local_inputs_count() == 0 {
2640+
let funding_ready_for_sig_event_opt = if signing_session.local_inputs_count() == 0 {
26412641
debug_assert_eq!(our_funding_satoshis, 0);
26422642
if signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new()).is_err() {
26432643
debug_assert!(
@@ -2651,28 +2651,12 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
26512651
}
26522652
None
26532653
} else {
2654-
// TODO(dual_funding): Send event for signing if we've contributed funds.
2655-
// Inform the user that SIGHASH_ALL must be used for all signatures when contributing
2656-
// inputs/signatures.
2657-
// Also warn the user that we don't do anything to prevent the counterparty from
2658-
// providing non-standard witnesses which will prevent the funding transaction from
2659-
// confirming. This warning must appear in doc comments wherever the user is contributing
2660-
// funds, whether they are initiator or acceptor.
2661-
//
2662-
// The following warning can be used when the APIs allowing contributing inputs become available:
2663-
// <div class="warning">
2664-
// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
2665-
// will prevent the funding transaction from being relayed on the bitcoin network and hence being
2666-
// confirmed.
2667-
// </div>
2668-
debug_assert!(
2669-
false,
2670-
"We don't support users providing inputs but somehow we had more than zero inputs",
2671-
);
2672-
return Err(ChannelError::Close((
2673-
"V2 channel rejected due to sender error".into(),
2674-
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }
2675-
)));
2654+
Some(Event::FundingTransactionReadyForSigning {
2655+
channel_id: self.context.channel_id,
2656+
counterparty_node_id: self.context.counterparty_node_id,
2657+
user_channel_id: self.context.user_id,
2658+
unsigned_transaction: signing_session.unsigned_tx().build_unsigned_tx(),
2659+
})
26762660
};
26772661

26782662
let mut channel_state = ChannelState::FundingNegotiated(FundingNegotiatedFlags::new());
@@ -2683,7 +2667,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
26832667
self.interactive_tx_constructor.take();
26842668
self.interactive_tx_signing_session = Some(signing_session);
26852669

2686-
Ok((commitment_signed, funding_ready_for_sig_event))
2670+
Ok((commitment_signed, funding_ready_for_sig_event_opt))
26872671
}
26882672
}
26892673

@@ -6651,6 +6635,36 @@ impl<SP: Deref> FundedChannel<SP> where
66516635
}
66526636
}
66536637

6638+
fn verify_interactive_tx_signatures(&mut self, _witnesses: &Vec<Witness>) {
6639+
if let Some(ref mut _signing_session) = self.interactive_tx_signing_session {
6640+
// Check that sighash_all was used:
6641+
// TODO(dual_funding): Check sig for sighash
6642+
}
6643+
}
6644+
6645+
pub fn funding_transaction_signed<L: Deref>(&mut self, witnesses: Vec<Witness>, logger: &L) -> Result<Option<msgs::TxSignatures>, APIError>
6646+
where L::Target: Logger
6647+
{
6648+
self.verify_interactive_tx_signatures(&witnesses);
6649+
if let Some(ref mut signing_session) = self.interactive_tx_signing_session {
6650+
let logger = WithChannelContext::from(logger, &self.context, None);
6651+
if let Some(holder_tx_signatures) = signing_session
6652+
.provide_holder_witnesses(self.context.channel_id, witnesses)
6653+
.map_err(|err| APIError::APIMisuseError { err })?
6654+
{
6655+
if self.is_awaiting_initial_mon_persist() {
6656+
log_debug!(logger, "Not sending tx_signatures: a monitor update is in progress. Setting monitor_pending_tx_signatures.");
6657+
self.context.monitor_pending_tx_signatures = Some(holder_tx_signatures);
6658+
return Ok(None);
6659+
}
6660+
return Ok(Some(holder_tx_signatures));
6661+
} else { return Ok(None) }
6662+
} else {
6663+
return Err(APIError::APIMisuseError {
6664+
err: format!("Channel with id {} not expecting funding signatures", self.context.channel_id)});
6665+
}
6666+
}
6667+
66546668
pub fn tx_signatures<L: Deref>(&mut self, msg: &msgs::TxSignatures, logger: &L) -> Result<(Option<Transaction>, Option<msgs::TxSignatures>), ChannelError>
66556669
where L::Target: Logger
66566670
{

lightning/src/ln/channelmanager.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5692,6 +5692,59 @@ where
56925692
result
56935693
}
56945694

5695+
/// Handles a signed funding transaction generated by interactive transaction construction and
5696+
/// provided by the client.
5697+
///
5698+
/// Do NOT broadcast the funding transaction yourself. When we have safely received our
5699+
/// counterparty's signature(s) the funding transaction will automatically be broadcast via the
5700+
/// [`BroadcasterInterface`] provided when this `ChannelManager` was constructed.
5701+
///
5702+
/// SIGHASH_ALL MUST be used for all signatures when providing signatures.
5703+
///
5704+
/// <div class="warning">
5705+
/// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
5706+
/// will prevent the funding transaction from being relayed on the bitcoin network and hence being
5707+
/// confirmed.
5708+
/// </div>
5709+
pub fn funding_transaction_signed(
5710+
&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, transaction: Transaction
5711+
) -> Result<(), APIError> {
5712+
let witnesses: Vec<_> = transaction.input.into_iter().filter_map(|input| {
5713+
if input.witness.is_empty() { None } else { Some(input.witness) }
5714+
}).collect();
5715+
5716+
let per_peer_state = self.per_peer_state.read().unwrap();
5717+
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
5718+
.ok_or_else(|| APIError::ChannelUnavailable {
5719+
err: format!("Can't find a peer matching the passed counterparty node_id {}",
5720+
counterparty_node_id) })?;
5721+
5722+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
5723+
let peer_state = &mut *peer_state_lock;
5724+
5725+
match peer_state.channel_by_id.get_mut(channel_id) {
5726+
Some(channel) => {
5727+
match channel.as_funded_mut() {
5728+
Some(chan) => {
5729+
if let Some(tx_signatures) = chan.funding_transaction_signed(witnesses, &self.logger)? {
5730+
peer_state.pending_msg_events.push(MessageSendEvent::SendTxSignatures {
5731+
node_id: *counterparty_node_id,
5732+
msg: tx_signatures,
5733+
});
5734+
}
5735+
},
5736+
None => return Err(APIError::APIMisuseError {
5737+
err: format!("Channel with id {} not expecting funding signatures", channel_id)}),
5738+
}
5739+
},
5740+
None => return Err(APIError::ChannelUnavailable{
5741+
err: format!("Channel with id {} not found for the passed counterparty node_id {}", channel_id,
5742+
counterparty_node_id) }),
5743+
}
5744+
5745+
Ok(())
5746+
}
5747+
56955748
/// Atomically applies partial updates to the [`ChannelConfig`] of the given channels.
56965749
///
56975750
/// Once the updates are applied, each eligible channel (advertised with a known short channel

lightning/src/ln/interactivetxs.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,9 +396,13 @@ impl InteractiveTxSigningSession {
396396
/// unsigned transaction.
397397
pub fn provide_holder_witnesses(
398398
&mut self, channel_id: ChannelId, witnesses: Vec<Witness>,
399-
) -> Result<(), ()> {
400-
if self.local_inputs_count() != witnesses.len() {
401-
return Err(());
399+
) -> Result<Option<TxSignatures>, String> {
400+
let local_inputs_count = self.local_inputs_count();
401+
if local_inputs_count != witnesses.len() {
402+
return Err(format!(
403+
"Provided witness count of {} does not match required count for {} inputs",
404+
witnesses.len(), local_inputs_count
405+
));
402406
}
403407

404408
self.unsigned_tx.add_local_witnesses(witnesses.clone());
@@ -409,7 +413,11 @@ impl InteractiveTxSigningSession {
409413
shared_input_signature: None,
410414
});
411415

412-
Ok(())
416+
if self.holder_sends_tx_signatures_first && self.has_received_commitment_signed {
417+
Ok(self.holder_tx_signatures.clone())
418+
} else {
419+
Ok(None)
420+
}
413421
}
414422

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

0 commit comments

Comments
 (0)