Skip to content

Commit 4d0eca1

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.
1 parent c6921fa commit 4d0eca1

File tree

3 files changed

+47
-173
lines changed

3 files changed

+47
-173
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 36 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -317,31 +317,30 @@ 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"));
333+
} else {
334+
// Once we see a dust HTLC, we should not expect to see any non-dust ones.
335+
debug_assert!(counterparty_htlc_sigs.next().is_none());
337336
}
337+
Some((htlc, counterparty_htlc_sig, source.as_ref()))
338+
} else {
339+
debug_assert!(counterparty_htlc_sigs.next().is_none());
340+
None
338341
}
339-
Some((htlc, Some(counterparty_htlc_sig), source))
340342
});
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));
343+
let htlc_outputs = crate::util::ser::IterableOwned(htlc_outputs);
345344

346345
write_tlv_fields!(writer, {
347346
(0, txid, required),
@@ -572,15 +571,11 @@ pub(crate) enum ChannelMonitorUpdateStep {
572571
// Update LatestHolderCommitmentTXInfo in channel.rs if adding new fields to this variant.
573572
LatestHolderCommitmentTXInfo {
574573
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.
574+
// Includes both dust and non-dust HTLCs. The `Option<Signature>` must be `Some` for
575+
// non-dust HTLCs due to backwards compatibility, even though they are already tracked
576+
// within the `HolderCommitmentTransaction` above.
581577
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
582578
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
583-
nondust_htlc_sources: Vec<HTLCSource>,
584579
},
585580
LatestCounterpartyCommitmentTXInfo {
586581
commitment_txid: Txid,
@@ -638,7 +633,6 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
638633
(0, commitment_tx, required),
639634
(1, claimed_htlcs, optional_vec),
640635
(2, htlc_outputs, required_vec),
641-
(4, nondust_htlc_sources, optional_vec),
642636
},
643637
(1, LatestCounterpartyCommitmentTXInfo) => {
644638
(0, commitment_txid, required),
@@ -920,74 +914,34 @@ impl<Signer: EcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer:
920914
#[derive(Clone, PartialEq)]
921915
struct HolderCommitment {
922916
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>)>,
917+
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
927918
}
928919

929920
impl TryFrom<(HolderCommitmentTransaction, HolderSignedTx)> for HolderCommitment {
930921
type Error = ();
931922
fn try_from(value: (HolderCommitmentTransaction, HolderSignedTx)) -> Result<Self, Self::Error> {
932923
let holder_commitment_tx = value.0;
933924
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-
960925
Ok(Self {
961926
tx: holder_commitment_tx,
962-
nondust_htlc_sources,
963-
dust_htlcs,
927+
htlcs: holder_signed_tx.htlc_outputs.into_iter()
928+
.map(|(htlc, _, source)| (htlc, source))
929+
.collect(),
964930
})
965931
}
966932
}
967933

968934
impl HolderCommitment {
969935
fn has_htlcs(&self) -> bool {
970-
self.tx.nondust_htlcs().len() > 0 || self.dust_htlcs.len() > 0
936+
!self.htlcs.is_empty()
971937
}
972938

973939
fn htlcs(&self) -> impl Iterator<Item = &HTLCOutputInCommitment> {
974-
self.tx.nondust_htlcs().iter().chain(self.dust_htlcs.iter().map(|(htlc, _)| htlc))
940+
self.htlcs.iter().map(|(htlc, _)| htlc)
975941
}
976942

977943
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)
944+
self.htlcs.iter().map(|(htlc, source)| (htlc, source.as_ref()))
991945
}
992946
}
993947

@@ -1554,8 +1508,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
15541508
current_holder_commitment: HolderCommitment {
15551509
tx: initial_holder_commitment_tx,
15561510
// There are never any HTLCs in the initial commitment transactions
1557-
nondust_htlc_sources: Vec::new(),
1558-
dust_htlcs: Vec::new(),
1511+
htlcs: Vec::new(),
15591512
},
15601513
prev_holder_commitment: None,
15611514
},
@@ -1680,7 +1633,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
16801633
&self, holder_commitment_tx: HolderCommitmentTransaction,
16811634
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
16821635
) {
1683-
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
1636+
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new())
16841637
}
16851638

16861639
/// This is used to provide payment preimage(s) out-of-band during startup without updating the
@@ -3092,72 +3045,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
30923045
fn provide_latest_holder_commitment_tx(
30933046
&mut self, holder_commitment_tx: HolderCommitmentTransaction,
30943047
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
3095-
claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], mut nondust_htlc_sources: Vec<HTLCSource>,
3048+
claimed_htlcs: &[(SentHTLCId, PaymentPreimage)],
30963049
) {
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-
31553050
self.current_holder_commitment_number = holder_commitment_tx.trust().commitment_number();
31563051
self.onchain_tx_handler.provide_latest_holder_tx(holder_commitment_tx.clone());
31573052
let mut holder_commitment = HolderCommitment {
31583053
tx: holder_commitment_tx,
3159-
nondust_htlc_sources,
3160-
dust_htlcs,
3054+
htlcs: htlc_outputs.into_iter().map(|(htlc, _, source)| (htlc, source)).collect(),
31613055
};
31623056
mem::swap(&mut holder_commitment, &mut self.funding.current_holder_commitment);
31633057
self.funding.prev_holder_commitment = Some(holder_commitment);
@@ -3387,10 +3281,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33873281
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&**fee_estimator);
33883282
for update in updates.updates.iter() {
33893283
match update {
3390-
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
3284+
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs } => {
33913285
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
33923286
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());
3287+
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs);
33943288
}
33953289
// Soon we will drop the `LatestCounterpartyCommitmentTXInfo` variant in favor of `LatestCounterpartyCommitmentTX`.
33963290
// For now we just add the code to handle the new updates.

lightning/src/ln/channel.rs

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3802,9 +3802,9 @@ 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() {
3807+
let mut counterparty_htlc_sig = None;
38083808
if let Some(_) = htlc.transaction_output_index {
38093809
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_data.tx.feerate_per_kw(),
38103810
funding.get_counterparty_selected_contest_delay().unwrap(), &htlc, funding.get_channel_type(),
@@ -3819,17 +3819,10 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38193819
if let Err(_) = self.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &holder_keys.countersignatory_htlc_key.to_public_key()) {
38203820
return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned()));
38213821
}
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()));
3822+
3823+
counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
38313824
}
3832-
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
3825+
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));
38333826
}
38343827

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

38463839
Ok(LatestHolderCommitmentTXInfo {
38473840
commitment_tx: holder_commitment_tx,
3848-
htlc_outputs: dust_htlcs,
3849-
nondust_htlc_sources,
3841+
htlc_outputs,
38503842
})
38513843
}
38523844

@@ -5155,7 +5147,6 @@ struct CommitmentTxInfoCached {
51555147
struct LatestHolderCommitmentTXInfo {
51565148
pub commitment_tx: HolderCommitmentTransaction,
51575149
pub htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
5158-
pub nondust_htlc_sources: Vec<HTLCSource>,
51595150
}
51605151

51615152
/// Contents of a wire message that fails an HTLC backwards. Useful for [`FundedChannel::fail_htlc`] to
@@ -5945,9 +5936,9 @@ impl<SP: Deref> FundedChannel<SP> where
59455936
let updates = self
59465937
.context
59475938
.validate_commitment_signed(&self.funding, &self.holder_commitment_point, msg, logger)
5948-
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, nondust_htlc_sources }|
5939+
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs }|
59495940
vec![ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
5950-
commitment_tx, htlc_outputs, claimed_htlcs: vec![], nondust_htlc_sources,
5941+
commitment_tx, htlc_outputs, claimed_htlcs: vec![],
59515942
}]
59525943
)?;
59535944

@@ -5970,9 +5961,9 @@ impl<SP: Deref> FundedChannel<SP> where
59705961
.ok_or_else(|| ChannelError::close(format!("Peer did not send a commitment_signed for pending splice transaction: {}", funding_txid)))?;
59715962
self.context
59725963
.validate_commitment_signed(funding, &self.holder_commitment_point, msg, logger)
5973-
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, nondust_htlc_sources }|
5964+
.map(|LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs }|
59745965
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
5975-
commitment_tx, htlc_outputs, claimed_htlcs: vec![], nondust_htlc_sources,
5966+
commitment_tx, htlc_outputs, claimed_htlcs: vec![],
59765967
}
59775968
)
59785969
}

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)