Skip to content

Commit dad9947

Browse files
TheBlueMattjbesraa
authored andcommitted
f Add typed funding tx
1 parent 4a06790 commit dad9947

File tree

4 files changed

+124
-62
lines changed

4 files changed

+124
-62
lines changed

lightning/src/events/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -603,8 +603,8 @@ pub enum Event {
603603
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
604604
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
605605
user_channel_id: u128,
606-
/// Channel funding transaction
607-
funding_tx: Transaction,
606+
/// The outpoint of the channel's funding transaction.
607+
funding_txo: OutPoint,
608608
/// The `node_id` of the channel counterparty.
609609
counterparty_node_id: PublicKey,
610610
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
@@ -1559,12 +1559,12 @@ impl Writeable for Event {
15591559
(4, responder, option),
15601560
});
15611561
},
1562-
&Event::FundingTxBroadcastSafe { ref channel_id, ref user_channel_id, ref funding_tx, ref counterparty_node_id, ref former_temporary_channel_id} => {
1562+
&Event::FundingTxBroadcastSafe { ref channel_id, ref user_channel_id, ref funding_txo, ref counterparty_node_id, ref former_temporary_channel_id} => {
15631563
43u8.write(writer)?;
15641564
write_tlv_fields!(writer, {
15651565
(0, channel_id, required),
15661566
(2, user_channel_id, required),
1567-
(4, funding_tx, required),
1567+
(4, funding_txo, required),
15681568
(6, counterparty_node_id, required),
15691569
(8, former_temporary_channel_id, required),
15701570
});
@@ -2023,20 +2023,20 @@ impl MaybeReadable for Event {
20232023
43u8 => {
20242024
let mut channel_id = RequiredWrapper(None);
20252025
let mut user_channel_id = RequiredWrapper(None);
2026-
let mut funding_tx = RequiredWrapper(None);
2026+
let mut funding_txo = RequiredWrapper(None);
20272027
let mut counterparty_node_id = RequiredWrapper(None);
20282028
let mut former_temporary_channel_id = RequiredWrapper(None);
20292029
read_tlv_fields!(reader, {
20302030
(0, channel_id, required),
20312031
(2, user_channel_id, required),
2032-
(4, funding_tx, required),
2032+
(4, funding_txo, required),
20332033
(6, counterparty_node_id, required),
20342034
(8, former_temporary_channel_id, required)
20352035
});
20362036
Ok(Some(Event::FundingTxBroadcastSafe {
20372037
channel_id: channel_id.0.unwrap(),
20382038
user_channel_id: user_channel_id.0.unwrap(),
2039-
funding_tx: funding_tx.0.unwrap(),
2039+
funding_txo: funding_txo.0.unwrap(),
20402040
counterparty_node_id: counterparty_node_id.0.unwrap(),
20412041
former_temporary_channel_id: former_temporary_channel_id.0.unwrap(),
20422042
}))

lightning/src/ln/channel.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,14 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
13311331
counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,
13321332

13331333
pub(crate) channel_transaction_parameters: ChannelTransactionParameters,
1334+
/// The transaction which funds this channel. Note that for manually-funded channels(i.e,
1335+
/// is_manual_broadcast is true) this will be a dummy empty transaction.
13341336
funding_transaction: Option<Transaction>,
1337+
/// This flag indicates that it is the user's responsibility to validated and broadcast the
1338+
/// funding transaction.
1339+
///
1340+
/// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
1341+
is_manual_broadcast: bool,
13351342
is_batch_funding: Option<()>,
13361343

13371344
counterparty_cur_commitment_point: Option<PublicKey>,
@@ -1438,14 +1445,6 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
14381445
/// If we can't release a [`ChannelMonitorUpdate`] until some external action completes, we
14391446
/// store it here and only release it to the `ChannelManager` once it asks for it.
14401447
blocked_monitor_updates: Vec<PendingChannelMonitorUpdate>,
1441-
1442-
/// Using this flag will prevent the funding transaction from being broadcasted
1443-
/// and will allow the user to manually broadcast it.
1444-
///
1445-
/// The funding transaction can be accessed through the [`Event::FundingTxBroadcastSafe`] event.
1446-
///
1447-
/// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
1448-
is_manual_broadcast: bool,
14491448
}
14501449

14511450
impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {

lightning/src/ln/channelmanager.rs

Lines changed: 103 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,51 @@ struct ClaimablePayment {
698698
htlcs: Vec<ClaimableHTLC>,
699699
}
700700

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+
701746
/// Information about claimable or being-claimed payments
702747
struct ClaimablePayments {
703748
/// 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 {
25682613
}}
25692614
}
25702615
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) => {
25722617
if !$channel.context.funding_tx_broadcast_safe_event_emitted() {
25732618
$locked_events.push_back((events::Event::FundingTxBroadcastSafe {
25742619
channel_id: $channel.context.channel_id(),
25752620
user_channel_id: $channel.context.get_user_id(),
2576-
funding_tx: $funding_tx,
2621+
funding_txo: $funding_txo,
25772622
counterparty_node_id: $channel.context.get_counterparty_node_id(),
25782623
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."),
25792624
}, None));
@@ -4250,7 +4295,7 @@ where
42504295

42514296
/// Handles the generation of a funding transaction, optionally (for tests) with a function
42524297
/// 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>>(
42544299
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, is_batch_funding: bool,
42554300
mut find_funding_output: FundingOutput, is_manual_broadcast: bool,
42564301
) -> Result<(), APIError> {
@@ -4277,7 +4322,7 @@ where
42774322
let _: Result<(), _> = handle_error!(self, Err(err), counterparty);
42784323
Err($api_err)
42794324
} } }
4280-
match find_funding_output(&chan, &funding_transaction) {
4325+
match find_funding_output(&chan) {
42814326
Ok(found_funding_txo) => funding_txo = found_funding_txo,
42824327
Err(err) => {
42834328
let chan_err = ChannelError::close(err.to_owned());
@@ -4348,8 +4393,9 @@ where
43484393

43494394
#[cfg(test)]
43504395
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 })
43534399
}, false)
43544400
}
43554401

@@ -4418,11 +4464,11 @@ where
44184464
/// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
44194465
/// [`Event::ChannelClosed`]: crate::events::Event::ChannelClosed
44204466
/// [`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> {
44224468
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
44234469

44244470
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));
44264472

44274473
}
44284474

@@ -4449,33 +4495,35 @@ where
44494495
}
44504496
}
44514497
}
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)))
44534499
}
44544500

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> {
44564502
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 {
44724505
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()
44744507
}));
44754508
}
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+
}
44764524
}
44774525

4478-
let txid = funding_transaction.txid();
4526+
let txid = funding.txid();
44794527
let is_batch_funding = temporary_channels.len() > 1;
44804528
let mut funding_batch_states = if is_batch_funding {
44814529
Some(self.funding_batch_states.lock().unwrap())
@@ -4493,27 +4541,33 @@ where
44934541
btree_map::Entry::Vacant(vacant) => Some(vacant.insert(Vec::new())),
44944542
}
44954543
});
4544+
let is_manual_broadcast = funding.is_manual_broadcast();
44964545
for &(temporary_channel_id, counterparty_node_id) in temporary_channels {
44974546
result = result.and_then(|_| self.funding_transaction_generated_intern(
44984547
temporary_channel_id,
44994548
counterparty_node_id,
4500-
funding_transaction.clone(),
4549+
funding.transaction_or_dummy(),
45014550
is_batch_funding,
4502-
|chan, tx| {
4551+
|chan| {
45034552
let mut output_index = None;
45044553
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+
}
45094563
}
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+
};
45174571
if let Some(funding_batch_state) = funding_batch_state.as_mut() {
45184572
// TODO(dual_funding): We only do batch funding for V1 channels at the moment, but we'll probably
45194573
// need to fix this somehow to not rely on using the outpoint for the channel ID if we
@@ -6661,7 +6715,15 @@ where
66616715
if channel.context.is_manual_broadcast() {
66626716
log_info!(logger, "Not broadcasting funding transaction with txid {} as it is manually managed", tx.txid());
66636717
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+
};
66656727
} else {
66666728
log_info!(logger, "Broadcasting funding transaction with txid {}", tx.txid());
66676729
self.tx_broadcaster.broadcast_transactions(&[&tx]);

lightning/src/ln/functional_tests.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3796,10 +3796,10 @@ fn test_unsafe_manual_funding_transaction_generated() {
37963796
let accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
37973797
nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel);
37983798

3799-
let (temporary_channel_id, tx, _funding_output) = create_funding_tx_without_witness_data(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
3799+
let (temporary_channel_id, _, funding_outpoint) = create_funding_tx_without_witness_data(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
38003800
assert_eq!(temporary_channel_id, expected_temporary_channel_id);
38013801

3802-
assert!(nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).is_ok());
3802+
assert!(nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), funding_outpoint).is_ok());
38033803
let node_0_msg_events = nodes[0].node.get_and_clear_pending_msg_events();
38043804
match node_0_msg_events[0] {
38053805
MessageSendEvent::SendFundingCreated { ref node_id, .. } => {
@@ -11248,8 +11248,8 @@ fn test_funding_signed_event() {
1124811248
let accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
1124911249

1125011250
nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel);
11251-
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100_000, 42);
11252-
nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
11251+
let (temporary_channel_id, tx, funding_outpoint) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100_000, 42);
11252+
nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), funding_outpoint).unwrap();
1125311253
check_added_monitors!(nodes[0], 0);
1125411254

1125511255
let funding_created = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
@@ -11263,8 +11263,9 @@ fn test_funding_signed_event() {
1126311263
let events = &nodes[0].node.get_and_clear_pending_events();
1126411264
assert_eq!(events.len(), 2);
1126511265
match &events[0] {
11266-
crate::events::Event::FundingTxBroadcastSafe { funding_tx, .. } => {
11267-
assert_eq!(funding_tx.txid(), funding_created.funding_txid);
11266+
crate::events::Event::FundingTxBroadcastSafe { funding_txo, .. } => {
11267+
assert_eq!(funding_txo.txid, funding_outpoint.txid);
11268+
assert_eq!(funding_txo.vout, funding_outpoint.index.into());
1126811269
},
1126911270
_ => panic!("Unexpected event"),
1127011271
};

0 commit comments

Comments
 (0)