@@ -698,6 +698,51 @@ struct ClaimablePayment {
698
698
htlcs: Vec<ClaimableHTLC>,
699
699
}
700
700
701
+ /// Represent the channel funding transaction type.
702
+ enum FundingType {
703
+ /// This variant is useful when we want LDK to validate the funding transaction and
704
+ /// broadcast it automatically.
705
+ ///
706
+ /// This is the normal flow.
707
+ Checked(Transaction),
708
+ /// This variant is useful when we want to loosen the validation checks and allow to
709
+ /// manually broadcast the funding transaction, leaving the responsibility to the caller.
710
+ ///
711
+ /// This is useful in cases of constructing the funding transaction as part of another
712
+ /// flow and the caller wants to perform the validation and broadcasting. An example of such
713
+ /// scenario could be when constructing the funding transaction as part of a Payjoin
714
+ /// transaction.
715
+ Unchecked(OutPoint),
716
+ }
717
+
718
+ impl FundingType {
719
+ fn txid(&self) -> Txid {
720
+ match self {
721
+ FundingType::Checked(tx) => tx.txid(),
722
+ FundingType::Unchecked(outp) => outp.txid,
723
+ }
724
+ }
725
+
726
+ fn transaction_or_dummy(&self) -> Transaction {
727
+ match self {
728
+ FundingType::Checked(tx) => tx.clone(),
729
+ FundingType::Unchecked(_) => Transaction {
730
+ version: bitcoin::transaction::Version::TWO,
731
+ lock_time: bitcoin::absolute::LockTime::ZERO,
732
+ input: Vec::new(),
733
+ output: Vec::new(),
734
+ },
735
+ }
736
+ }
737
+
738
+ fn is_manual_broadcast(&self) -> bool {
739
+ match self {
740
+ FundingType::Checked(_) => false,
741
+ FundingType::Unchecked(_) => true,
742
+ }
743
+ }
744
+ }
745
+
701
746
/// Information about claimable or being-claimed payments
702
747
struct ClaimablePayments {
703
748
/// Map from payment hash to the payment data and any HTLCs which are to us and can be
@@ -2568,12 +2613,12 @@ macro_rules! send_channel_ready {
2568
2613
}}
2569
2614
}
2570
2615
macro_rules! emit_funding_tx_broadcast_safe_event {
2571
- ($locked_events: expr, $channel: expr, $funding_tx : expr) => {
2616
+ ($locked_events: expr, $channel: expr, $funding_txo : expr) => {
2572
2617
if !$channel.context.funding_tx_broadcast_safe_event_emitted() {
2573
2618
$locked_events.push_back((events::Event::FundingTxBroadcastSafe {
2574
2619
channel_id: $channel.context.channel_id(),
2575
2620
user_channel_id: $channel.context.get_user_id(),
2576
- funding_tx : $funding_tx ,
2621
+ funding_txo : $funding_txo ,
2577
2622
counterparty_node_id: $channel.context.get_counterparty_node_id(),
2578
2623
former_temporary_channel_id: $channel.context.temporary_channel_id().expect("Unreachable: FundingTxBroadcastSafe event feature added to channel establishment process in LDK v0.124.0 where this should never be None."),
2579
2624
}, None));
@@ -4250,7 +4295,7 @@ where
4250
4295
4251
4296
/// Handles the generation of a funding transaction, optionally (for tests) with a function
4252
4297
/// which checks the correctness of the funding transaction given the associated channel.
4253
- fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction ) -> Result<OutPoint, &'static str>>(
4298
+ fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>) -> Result<OutPoint, &'static str>>(
4254
4299
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, is_batch_funding: bool,
4255
4300
mut find_funding_output: FundingOutput, is_manual_broadcast: bool,
4256
4301
) -> Result<(), APIError> {
@@ -4277,7 +4322,7 @@ where
4277
4322
let _: Result<(), _> = handle_error!(self, Err(err), counterparty);
4278
4323
Err($api_err)
4279
4324
} } }
4280
- match find_funding_output(&chan, &funding_transaction ) {
4325
+ match find_funding_output(&chan) {
4281
4326
Ok(found_funding_txo) => funding_txo = found_funding_txo,
4282
4327
Err(err) => {
4283
4328
let chan_err = ChannelError::close(err.to_owned());
@@ -4348,8 +4393,9 @@ where
4348
4393
4349
4394
#[cfg(test)]
4350
4395
pub(crate) fn funding_transaction_generated_unchecked(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, output_index: u16) -> Result<(), APIError> {
4351
- self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, false, |_, tx| {
4352
- Ok(OutPoint { txid: tx.txid(), index: output_index })
4396
+ let txid = funding_transaction.txid();
4397
+ self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, false, |_| {
4398
+ Ok(OutPoint { txid, index: output_index })
4353
4399
}, false)
4354
4400
}
4355
4401
@@ -4418,11 +4464,11 @@ where
4418
4464
/// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
4419
4465
/// [`Event::ChannelClosed`]: crate::events::Event::ChannelClosed
4420
4466
/// [`ChannelManager::funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::funding_transaction_generated
4421
- pub fn unsafe_manual_funding_transaction_generated(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction ) -> Result<(), APIError> {
4467
+ pub fn unsafe_manual_funding_transaction_generated(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding: OutPoint ) -> Result<(), APIError> {
4422
4468
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
4423
4469
4424
4470
let temporary_channels = &[(temporary_channel_id, counterparty_node_id)];
4425
- return self.batch_funding_transaction_generated_intern(temporary_channels, funding_transaction, true );
4471
+ return self.batch_funding_transaction_generated_intern(temporary_channels, FundingType::Unchecked(funding) );
4426
4472
4427
4473
}
4428
4474
@@ -4449,33 +4495,35 @@ where
4449
4495
}
4450
4496
}
4451
4497
}
4452
- result.and(self.batch_funding_transaction_generated_intern(temporary_channels, funding_transaction, false ))
4498
+ result.and(self.batch_funding_transaction_generated_intern(temporary_channels, FundingType::Checked( funding_transaction) ))
4453
4499
}
4454
4500
4455
- fn batch_funding_transaction_generated_intern(&self, temporary_channels: &[(&ChannelId, &PublicKey)], funding_transaction: Transaction, is_manual_broadcast: bool ) -> Result<(), APIError> {
4501
+ fn batch_funding_transaction_generated_intern(&self, temporary_channels: &[(&ChannelId, &PublicKey)], funding: FundingType ) -> Result<(), APIError> {
4456
4502
let mut result = Ok(());
4457
- if funding_transaction.output.len() > u16::max_value() as usize {
4458
- result = result.and(Err(APIError::APIMisuseError {
4459
- err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
4460
- }));
4461
- }
4462
- {
4463
- let height = self.best_block.read().unwrap().height;
4464
- // Transactions are evaluated as final by network mempools if their locktime is strictly
4465
- // lower than the next block height. However, the modules constituting our Lightning
4466
- // node might not have perfect sync about their blockchain views. Thus, if the wallet
4467
- // module is ahead of LDK, only allow one more block of headroom.
4468
- if !funding_transaction.input.iter().all(|input| input.sequence == Sequence::MAX) &&
4469
- funding_transaction.lock_time.is_block_height() &&
4470
- funding_transaction.lock_time.to_consensus_u32() > height + 1
4471
- {
4503
+ if let FundingType::Checked(funding_transaction) = &funding {
4504
+ if funding_transaction.output.len() > u16::max_value() as usize {
4472
4505
result = result.and(Err(APIError::APIMisuseError {
4473
- err: "Funding transaction absolute timelock is non-final ".to_owned()
4506
+ err: "Transaction had more than 2^16 outputs, which is not supported ".to_owned()
4474
4507
}));
4475
4508
}
4509
+ {
4510
+ let height = self.best_block.read().unwrap().height;
4511
+ // Transactions are evaluated as final by network mempools if their locktime is strictly
4512
+ // lower than the next block height. However, the modules constituting our Lightning
4513
+ // node might not have perfect sync about their blockchain views. Thus, if the wallet
4514
+ // module is ahead of LDK, only allow one more block of headroom.
4515
+ if !funding_transaction.input.iter().all(|input| input.sequence == Sequence::MAX) &&
4516
+ funding_transaction.lock_time.is_block_height() &&
4517
+ funding_transaction.lock_time.to_consensus_u32() > height + 1
4518
+ {
4519
+ result = result.and(Err(APIError::APIMisuseError {
4520
+ err: "Funding transaction absolute timelock is non-final".to_owned()
4521
+ }));
4522
+ }
4523
+ }
4476
4524
}
4477
4525
4478
- let txid = funding_transaction .txid();
4526
+ let txid = funding .txid();
4479
4527
let is_batch_funding = temporary_channels.len() > 1;
4480
4528
let mut funding_batch_states = if is_batch_funding {
4481
4529
Some(self.funding_batch_states.lock().unwrap())
@@ -4493,27 +4541,33 @@ where
4493
4541
btree_map::Entry::Vacant(vacant) => Some(vacant.insert(Vec::new())),
4494
4542
}
4495
4543
});
4544
+ let is_manual_broadcast = funding.is_manual_broadcast();
4496
4545
for &(temporary_channel_id, counterparty_node_id) in temporary_channels {
4497
4546
result = result.and_then(|_| self.funding_transaction_generated_intern(
4498
4547
temporary_channel_id,
4499
4548
counterparty_node_id,
4500
- funding_transaction.clone (),
4549
+ funding.transaction_or_dummy (),
4501
4550
is_batch_funding,
4502
- |chan, tx | {
4551
+ |chan| {
4503
4552
let mut output_index = None;
4504
4553
let expected_spk = chan.context.get_funding_redeemscript().to_p2wsh();
4505
- for (idx, outp) in tx.output.iter().enumerate() {
4506
- if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
4507
- if output_index.is_some() {
4508
- return Err("Multiple outputs matched the expected script and value");
4554
+ let outpoint = match &funding {
4555
+ FundingType::Checked(tx) => {
4556
+ for (idx, outp) in tx.output.iter().enumerate() {
4557
+ if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
4558
+ if output_index.is_some() {
4559
+ return Err("Multiple outputs matched the expected script and value");
4560
+ }
4561
+ output_index = Some(idx as u16);
4562
+ }
4509
4563
}
4510
- output_index = Some(idx as u16);
4511
- }
4512
- }
4513
- if output_index.is_none () {
4514
- return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
4515
- }
4516
- let outpoint = OutPoint { txid: tx.txid(), index: output_index.unwrap() };
4564
+ if output_index.is_none() {
4565
+ return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
4566
+ }
4567
+ OutPoint { txid, index: output_index.unwrap () }
4568
+ },
4569
+ FundingType::Unchecked(outpoint) => outpoint.clone(),
4570
+ };
4517
4571
if let Some(funding_batch_state) = funding_batch_state.as_mut() {
4518
4572
// TODO(dual_funding): We only do batch funding for V1 channels at the moment, but we'll probably
4519
4573
// need to fix this somehow to not rely on using the outpoint for the channel ID if we
@@ -6661,7 +6715,15 @@ where
6661
6715
if channel.context.is_manual_broadcast() {
6662
6716
log_info!(logger, "Not broadcasting funding transaction with txid {} as it is manually managed", tx.txid());
6663
6717
let mut pending_events = self.pending_events.lock().unwrap();
6664
- emit_funding_tx_broadcast_safe_event!(pending_events, channel, tx);
6718
+ match channel.context.get_funding_txo() {
6719
+ Some(funding_txo) => {
6720
+ emit_funding_tx_broadcast_safe_event!(pending_events, channel, funding_txo.into_bitcoin_outpoint())
6721
+ },
6722
+ None => {
6723
+ log_error!(logger, "Channel resumed without a funding txo, this should never happen!");
6724
+ return (htlc_forwards, decode_update_add_htlcs);
6725
+ }
6726
+ };
6665
6727
} else {
6666
6728
log_info!(logger, "Broadcasting funding transaction with txid {}", tx.txid());
6667
6729
self.tx_broadcaster.broadcast_transactions(&[&tx]);
0 commit comments