Skip to content

Commit 6dad376

Browse files
committed
Revert separate non-dust HTLC sources for holder commitments
We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the `htlc_outputs` `Vec` where all HTLCs would be tracked in a holder commitment update. With splicing, we'll unfortunately be forced to store redundant copies of non-dust HTLC data within the commitment transaction for each relevant `FundingScope`. As a result, providing non-dust HTLC sources separately no longer provides any benefits. In the future, we also plan to rework how the HTLC data for holder and counterparty commitments are tracked to avoid storing duplicate `HTLCSource`s. Along the way, this commit also omits setting the `Option<Signature>` for non-dust HTLCs, as they are already tracked within the `HolderCommitmentTransaction`.
1 parent c6921fa commit 6dad376

File tree

3 files changed

+41
-173
lines changed

3 files changed

+41
-173
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 33 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -317,31 +317,27 @@ impl HolderCommitment {
317317
let delayed_payment_key = &tx_keys.broadcaster_delayed_payment_key;
318318
let per_commitment_point = &tx_keys.per_commitment_point;
319319

320-
let mut nondust_htlcs = self.tx.nondust_htlcs().iter().zip(self.tx.counterparty_htlc_sigs.iter());
321-
let mut sources = self.nondust_htlc_sources.iter();
322-
323-
// Use an iterator to write `htlc_outputs` to avoid allocations.
324-
let nondust_htlcs = core::iter::from_fn(move || {
325-
let (htlc, counterparty_htlc_sig) = if let Some(nondust_htlc) = nondust_htlcs.next() {
326-
nondust_htlc
327-
} else {
328-
debug_assert!(sources.next().is_none());
329-
return None;
330-
};
320+
debug_assert!(
321+
self.htlcs.iter().map(|(htlc, _)| htlc).zip(self.tx.nondust_htlcs().iter())
322+
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
323+
);
331324

332-
let mut source = None;
333-
if htlc.offered {
334-
source = sources.next();
335-
if source.is_none() {
336-
panic!("Every offered non-dust HTLC should have a corresponding source");
325+
let mut htlcs = self.htlcs.iter();
326+
let mut counterparty_htlc_sigs = self.tx.counterparty_htlc_sigs.iter();
327+
let htlc_outputs = core::iter::from_fn(move || {
328+
if let Some((htlc, source)) = htlcs.next() {
329+
let mut counterparty_htlc_sig = None;
330+
if htlc.transaction_output_index.is_some() {
331+
counterparty_htlc_sig = Some(counterparty_htlc_sigs.next()
332+
.expect("Every non-dust HTLC must have a counterparty signature"));
337333
}
334+
Some((htlc, counterparty_htlc_sig, source.as_ref()))
335+
} else {
336+
debug_assert!(counterparty_htlc_sigs.next().is_none());
337+
None
338338
}
339-
Some((htlc, Some(counterparty_htlc_sig), source))
340339
});
341-
342-
// Dust HTLCs go last.
343-
let dust_htlcs = self.dust_htlcs.iter().map(|(htlc, source)| (htlc, None::<&Signature>, source.as_ref()));
344-
let htlc_outputs = crate::util::ser::IterableOwned(nondust_htlcs.chain(dust_htlcs));
340+
let htlc_outputs = crate::util::ser::IterableOwned(htlc_outputs);
345341

346342
write_tlv_fields!(writer, {
347343
(0, txid, required),
@@ -572,15 +568,11 @@ pub(crate) enum ChannelMonitorUpdateStep {
572568
// Update LatestHolderCommitmentTXInfo in channel.rs if adding new fields to this variant.
573569
LatestHolderCommitmentTXInfo {
574570
commitment_tx: HolderCommitmentTransaction,
575-
/// Note that LDK after 0.0.115 supports this only containing dust HTLCs (implying the
576-
/// `Signature` field is never filled in). At that point, non-dust HTLCs are implied by the
577-
/// HTLC fields in `commitment_tx` and the sources passed via `nondust_htlc_sources`.
578-
/// Starting with 0.2, the non-dust HTLC sources will always be provided separately, and
579-
/// `htlc_outputs` will only include dust HTLCs. We still have to track the
580-
/// `Option<Signature>` for backwards compatibility.
571+
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
572+
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
573+
// track it for backwards compatibility though.
581574
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
582575
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
583-
nondust_htlc_sources: Vec<HTLCSource>,
584576
},
585577
LatestCounterpartyCommitmentTXInfo {
586578
commitment_txid: Txid,
@@ -638,7 +630,6 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
638630
(0, commitment_tx, required),
639631
(1, claimed_htlcs, optional_vec),
640632
(2, htlc_outputs, required_vec),
641-
(4, nondust_htlc_sources, optional_vec),
642633
},
643634
(1, LatestCounterpartyCommitmentTXInfo) => {
644635
(0, commitment_txid, required),
@@ -920,74 +911,34 @@ impl<Signer: EcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer:
920911
#[derive(Clone, PartialEq)]
921912
struct HolderCommitment {
922913
tx: HolderCommitmentTransaction,
923-
// These must be sorted in increasing output index order to match the expected order of the
924-
// HTLCs in the `CommitmentTransaction`.
925-
nondust_htlc_sources: Vec<HTLCSource>,
926-
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
914+
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
927915
}
928916

929917
impl TryFrom<(HolderCommitmentTransaction, HolderSignedTx)> for HolderCommitment {
930918
type Error = ();
931919
fn try_from(value: (HolderCommitmentTransaction, HolderSignedTx)) -> Result<Self, Self::Error> {
932920
let holder_commitment_tx = value.0;
933921
let holder_signed_tx = value.1;
934-
935-
// HolderSignedTx tracks all HTLCs included in the commitment (dust included). For
936-
// `HolderCommitment`, we'll need to extract the dust HTLCs and their sources, and non-dust
937-
// HTLC sources, separately. All offered, non-dust HTLCs must have a source available.
938-
939-
let mut missing_nondust_source = false;
940-
let mut nondust_htlc_sources = Vec::with_capacity(holder_commitment_tx.nondust_htlcs().len());
941-
let dust_htlcs = holder_signed_tx.htlc_outputs.into_iter().filter_map(|(htlc, _, source)| {
942-
// Filter our non-dust HTLCs, while at the same time pushing their sources into
943-
// `nondust_htlc_sources`.
944-
if htlc.transaction_output_index.is_none() {
945-
return Some((htlc, source))
946-
}
947-
if htlc.offered {
948-
if let Some(source) = source {
949-
nondust_htlc_sources.push(source);
950-
} else {
951-
missing_nondust_source = true;
952-
}
953-
}
954-
None
955-
}).collect();
956-
if missing_nondust_source {
957-
return Err(());
958-
}
959-
960922
Ok(Self {
961923
tx: holder_commitment_tx,
962-
nondust_htlc_sources,
963-
dust_htlcs,
924+
htlcs: holder_signed_tx.htlc_outputs.into_iter()
925+
.map(|(htlc, _, source)| (htlc, source))
926+
.collect(),
964927
})
965928
}
966929
}
967930

968931
impl HolderCommitment {
969932
fn has_htlcs(&self) -> bool {
970-
self.tx.nondust_htlcs().len() > 0 || self.dust_htlcs.len() > 0
933+
!self.htlcs.is_empty()
971934
}
972935

973936
fn htlcs(&self) -> impl Iterator<Item = &HTLCOutputInCommitment> {
974-
self.tx.nondust_htlcs().iter().chain(self.dust_htlcs.iter().map(|(htlc, _)| htlc))
937+
self.htlcs.iter().map(|(htlc, _)| htlc)
975938
}
976939

977940
fn htlcs_with_sources(&self) -> impl Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> {
978-
let mut sources = self.nondust_htlc_sources.iter();
979-
let nondust_htlcs = self.tx.nondust_htlcs().iter().map(move |htlc| {
980-
let mut source = None;
981-
if htlc.offered && htlc.transaction_output_index.is_some() {
982-
source = sources.next();
983-
if source.is_none() {
984-
panic!("Every offered non-dust HTLC should have a corresponding source");
985-
}
986-
}
987-
(htlc, source)
988-
});
989-
let dust_htlcs = self.dust_htlcs.iter().map(|(htlc, source)| (htlc, source.as_ref()));
990-
nondust_htlcs.chain(dust_htlcs)
941+
self.htlcs.iter().map(|(htlc, source)| (htlc, source.as_ref()))
991942
}
992943
}
993944

@@ -1554,8 +1505,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
15541505
current_holder_commitment: HolderCommitment {
15551506
tx: initial_holder_commitment_tx,
15561507
// There are never any HTLCs in the initial commitment transactions
1557-
nondust_htlc_sources: Vec::new(),
1558-
dust_htlcs: Vec::new(),
1508+
htlcs: Vec::new(),
15591509
},
15601510
prev_holder_commitment: None,
15611511
},
@@ -1680,7 +1630,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
16801630
&self, holder_commitment_tx: HolderCommitmentTransaction,
16811631
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
16821632
) {
1683-
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
1633+
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new())
16841634
}
16851635

16861636
/// This is used to provide payment preimage(s) out-of-band during startup without updating the
@@ -3092,72 +3042,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
30923042
fn provide_latest_holder_commitment_tx(
30933043
&mut self, holder_commitment_tx: HolderCommitmentTransaction,
30943044
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
3095-
claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], mut nondust_htlc_sources: Vec<HTLCSource>,
3045+
claimed_htlcs: &[(SentHTLCId, PaymentPreimage)],
30963046
) {
3097-
let dust_htlcs: Vec<_> = if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
3098-
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
3099-
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
3100-
// and just pass in source data via `nondust_htlc_sources`.
3101-
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
3102-
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
3103-
debug_assert_eq!(a, b);
3104-
}
3105-
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
3106-
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
3107-
debug_assert_eq!(a, b);
3108-
}
3109-
3110-
// Backfill the non-dust HTLC sources.
3111-
debug_assert!(nondust_htlc_sources.is_empty());
3112-
nondust_htlc_sources.reserve_exact(holder_commitment_tx.nondust_htlcs().len());
3113-
let dust_htlcs = htlc_outputs.into_iter().filter_map(|(htlc, _, source)| {
3114-
// Filter our non-dust HTLCs, while at the same time pushing their sources into
3115-
// `nondust_htlc_sources`.
3116-
if htlc.transaction_output_index.is_none() {
3117-
return Some((htlc, source));
3118-
}
3119-
if htlc.offered {
3120-
nondust_htlc_sources.push(source.expect("Outbound HTLCs should have a source"));
3121-
}
3122-
None
3123-
}).collect();
3124-
3125-
dust_htlcs
3126-
} else {
3127-
// If we don't have any non-dust HTLCs in htlc_outputs, assume they were all passed via
3128-
// `nondust_htlc_sources`, building up the final htlc_outputs by combining
3129-
// `nondust_htlc_sources` and the `holder_commitment_tx`
3130-
{
3131-
let mut prev = -1;
3132-
for htlc in holder_commitment_tx.trust().nondust_htlcs().iter() {
3133-
assert!(htlc.transaction_output_index.unwrap() as i32 > prev);
3134-
prev = htlc.transaction_output_index.unwrap() as i32;
3135-
}
3136-
}
3137-
3138-
debug_assert!(htlc_outputs.iter().all(|(htlc, _, _)| htlc.transaction_output_index.is_none()));
3139-
debug_assert!(htlc_outputs.iter().all(|(_, sig_opt, _)| sig_opt.is_none()));
3140-
debug_assert_eq!(holder_commitment_tx.trust().nondust_htlcs().len(), holder_commitment_tx.counterparty_htlc_sigs.len());
3141-
3142-
let mut sources = nondust_htlc_sources.iter();
3143-
for htlc in holder_commitment_tx.trust().nondust_htlcs().iter() {
3144-
if htlc.offered {
3145-
let source = sources.next().expect("Non-dust HTLC sources didn't match commitment tx");
3146-
assert!(source.possibly_matches_output(htlc));
3147-
}
3148-
}
3149-
assert!(sources.next().is_none(), "All HTLC sources should have been exhausted");
3150-
3151-
// This only includes dust HTLCs as checked above.
3152-
htlc_outputs.into_iter().map(|(htlc, _, source)| (htlc, source)).collect()
3153-
};
3154-
31553047
self.current_holder_commitment_number = holder_commitment_tx.trust().commitment_number();
31563048
self.onchain_tx_handler.provide_latest_holder_tx(holder_commitment_tx.clone());
31573049
let mut holder_commitment = HolderCommitment {
31583050
tx: holder_commitment_tx,
3159-
nondust_htlc_sources,
3160-
dust_htlcs,
3051+
htlcs: htlc_outputs.into_iter().map(|(htlc, _, source)| (htlc, source)).collect(),
31613052
};
31623053
mem::swap(&mut holder_commitment, &mut self.funding.current_holder_commitment);
31633054
self.funding.prev_holder_commitment = Some(holder_commitment);
@@ -3387,10 +3278,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33873278
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&**fee_estimator);
33883279
for update in updates.updates.iter() {
33893280
match update {
3390-
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
3281+
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs } => {
33913282
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
33923283
if self.lockdown_from_offchain { panic!(); }
3393-
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
3284+
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs);
33943285
}
33953286
// Soon we will drop the `LatestCounterpartyCommitmentTXInfo` variant in favor of `LatestCounterpartyCommitmentTX`.
33963287
// For now we just add the code to handle the new updates.

lightning/src/ln/channel.rs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3802,9 +3802,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38023802
}
38033803

38043804
let holder_keys = commitment_data.tx.trust().keys();
3805-
let mut nondust_htlc_sources = Vec::with_capacity(commitment_data.tx.nondust_htlcs().len());
3806-
let mut dust_htlcs = Vec::with_capacity(commitment_data.htlcs_included.len() - commitment_data.tx.nondust_htlcs().len());
3807-
for (idx, (htlc, mut source_opt)) in commitment_data.htlcs_included.into_iter().enumerate() {
3805+
let mut htlc_outputs = Vec::with_capacity(commitment_data.htlcs_included.len());
3806+
for (idx, (htlc, source_opt)) in commitment_data.htlcs_included.into_iter().enumerate() {
38083807
if let Some(_) = htlc.transaction_output_index {
38093808
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_data.tx.feerate_per_kw(),
38103809
funding.get_counterparty_selected_contest_delay().unwrap(), &htlc, funding.get_channel_type(),
@@ -3819,17 +3818,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38193818
if let Err(_) = self.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &holder_keys.countersignatory_htlc_key.to_public_key()) {
38203819
return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned()));
38213820
}
3822-
if htlc.offered {
3823-
if let Some(source) = source_opt.take() {
3824-
nondust_htlc_sources.push(source.clone());
3825-
} else {
3826-
panic!("Missing outbound HTLC source");
3827-
}
3828-
}
3829-
} else {
3830-
dust_htlcs.push((htlc, None, source_opt.take().cloned()));
38313821
}
3832-
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
3822+
htlc_outputs.push((htlc, None, source_opt.cloned()));
38333823
}
38343824

38353825
let holder_commitment_tx = HolderCommitmentTransaction::new(
@@ -3845,8 +3835,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38453835

38463836
Ok(LatestHolderCommitmentTXInfo {
38473837
commitment_tx: holder_commitment_tx,
3848-
htlc_outputs: dust_htlcs,
3849-
nondust_htlc_sources,
3838+
htlc_outputs,
38503839
})
38513840
}
38523841

@@ -5155,7 +5144,6 @@ struct CommitmentTxInfoCached {
51555144
struct LatestHolderCommitmentTXInfo {
51565145
pub commitment_tx: HolderCommitmentTransaction,
51575146
pub htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
5158-
pub nondust_htlc_sources: Vec<HTLCSource>,
51595147
}
51605148

51615149
/// Contents of a wire message that fails an HTLC backwards. Useful for [`FundedChannel::fail_htlc`] to
@@ -5945,9 +5933,9 @@ impl<SP: Deref> FundedChannel<SP> where
59455933
let updates = self
59465934
.context
59475935
.validate_commitment_signed(&self.funding, &self.holder_commitment_point, msg, logger)
5948-
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, nondust_htlc_sources }|
5936+
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs }|
59495937
vec![ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
5950-
commitment_tx, htlc_outputs, claimed_htlcs: vec![], nondust_htlc_sources,
5938+
commitment_tx, htlc_outputs, claimed_htlcs: vec![],
59515939
}]
59525940
)?;
59535941

@@ -5970,9 +5958,9 @@ impl<SP: Deref> FundedChannel<SP> where
59705958
.ok_or_else(|| ChannelError::close(format!("Peer did not send a commitment_signed for pending splice transaction: {}", funding_txid)))?;
59715959
self.context
59725960
.validate_commitment_signed(funding, &self.holder_commitment_point, msg, logger)
5973-
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, nondust_htlc_sources }|
5961+
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs }|
59745962
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
5975-
commitment_tx, htlc_outputs, claimed_htlcs: vec![], nondust_htlc_sources,
5963+
commitment_tx, htlc_outputs, claimed_htlcs: vec![],
59765964
}
59775965
)
59785966
}

lightning/src/ln/channelmanager.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -726,17 +726,6 @@ impl HTLCSource {
726726
}
727727
}
728728

729-
/// Checks whether this HTLCSource could possibly match the given HTLC output in a commitment
730-
/// transaction. Useful to ensure different datastructures match up.
731-
pub(crate) fn possibly_matches_output(&self, htlc: &super::chan_utils::HTLCOutputInCommitment) -> bool {
732-
if let HTLCSource::OutboundRoute { first_hop_htlc_msat, .. } = self {
733-
*first_hop_htlc_msat == htlc.amount_msat
734-
} else {
735-
// There's nothing we can check for forwarded HTLCs
736-
true
737-
}
738-
}
739-
740729
/// Returns the CLTV expiry of the inbound HTLC (i.e. the source referred to by this object),
741730
/// if the source was a forwarded HTLC and the HTLC was first forwarded on LDK 0.1.1 or later.
742731
pub(crate) fn inbound_htlc_expiry(&self) -> Option<u32> {

0 commit comments

Comments
 (0)