Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Revert separate non-dust HTLC sources for holder commitments - #3745

Closed
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources
Closed

Revert separate non-dust HTLC sources for holder commitments#3745
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:revert-separate-nondusts-htlc-sources

Conversation

@wpaulino

Copy link
Copy Markdown
Contributor

We previously provided non-dust HTLC sources to avoid storing duplicate non-dust HTLC data in the htlc_outputsVec 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 HTLCSources.

Along the way, this commit also omits setting the Option<Signature> for non-dust HTLCs, as they are already tracked within the HolderCommitmentTransaction.

@ldk-reviews-bot

ldk-reviews-bot commented Apr 17, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov

codecovBot commented Apr 17, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 95.00000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.11%. Comparing base (c6921fa) to head (4d0eca1).
Report is 69 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs81.81%2 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3745 +/- ##
==========================================
- Coverage 89.15% 89.11% -0.04% 
==========================================
Files 156 157 +1 Lines 123837 123923 +86 Branches 123837 123923 +86 ==========================================
+ Hits 110408 110440 +32 - Misses 10754 10806 +52 - Partials 2675 2677 +2 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wpaulino
wpaulino requested review from TheBlueMatt and removed request for joostjagerApril 18, 2025 02:18
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Sorry for the delay here. So given we're gonna do #3738, is the point of this change to have less diff across the two update types? In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

So given we're gonna do #3738, is the point of this change to have less diff across the two update types?

Correct.

In #3738 it was proposed that we finally split HTLCOutputInCommitment so that we don't deal with the spurious output index field anymore as well as avoids the redundant signature field. The PR details on this one explain that this is about splicing, but post-#3738 it wouldn't be used for splicing, and its not clear to me that this simplifies the code (eg back to the 0.1 state) much?

This change isn't about splicing specifically, it just notes that the goal of having separate non-dust HTLC sources is unnecessary because splicing monitor updates will have duplicate HTLC data tracked in each CommitmentTransaction anyway.

So this change keeps both the legacy LatestHolderCommitmentTXInfo and the new LatestHolderCommitmentTX closer together, as we'll eventually replace the latter with the former.

commitment_tx: holder_commitment_tx,
htlc_outputs: dust_htlcs,
nondust_htlc_sources,
htlc_outputs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LDK 0.1 support handling the case with htlc_sources containing nondust HTLC sources with None for the signature? Or does it get confused cause it only expects either a separate nondust_htlc_sources or Some for some of the signatures?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah, I had misread how this worked. We do still need to track the signatures redundantly for backwards compat.

// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth just doing the sigs at write-time?

$ git diff
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a1acfd7df..687ebc61d 100644
--- a/lightning/src/chain/channelmonitor.rs+++ b/lightning/src/chain/channelmonitor.rs@@ -571,7 +571,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
// Includes both dust and non-dust HTLCs. The `Option<Signature>` is always `None`, as they
// are already tracked within the `HolderCommitmentTransaction` above. We still have to
// track it for backwards compatibility though.
- htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
},
LatestCounterpartyCommitmentTXInfo {
@@ -629,7 +629,12 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
- (2, htlc_outputs, required_vec),+ (2, legacy_htlc_outputs, (legacy, crate::util::ser::WithoutLength<Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>>, |us| {+ if let &Self::LatestHolderCommitmentTXInfo { htlc_outputs, .. } = us {+ Some(crate::util::ser::IterableOwned(htlc_outputs.iter().map(|(a, b)| (a, None::<Signature>, b))))+ } else { unreachable!() }+ })),+ (99999999, htlc_outputs, (static_value, legacy_htlc_outputs.ok_or(DecodeError::InvalidValue)?.0.into_iter().map(|(a, _, b)| (a, b)).collect()))
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs
index c3cf2044d..a2727e2ba 100644
--- a/lightning/src/util/ser_macros.rs+++ b/lightning/src/util/ser_macros.rs@@ -56,7 +56,7 @@ macro_rules! _encode_tlv {
if let Some(v) = &value {
let encoded_value = v.encode();
let mut read_slice = &encoded_value[..];
- let _: $fieldty = $crate::util::ser::Readable::read(&mut read_slice)+ let _: $fieldty = $crate::util::ser::LengthReadable::read_from_fixed_length_buffer(&mut read_slice)
.expect("Failed to read written TLV, check types");
assert!(read_slice.is_empty(), "Reading written TLV was short, check types");
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can do this anymore without unrolling the macro, since we'd need to write it with the sigs, but do the legacy thing for reads.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have Matt's question about potential downgrades to 0.1 from here.

If none of the HTLC signatures are set, 0.1 assumes that the sources are provided separately by nondust_htlc_sources. But this is a wrong assumption to make after this commit.

Comment threadlightning/src/chain/channelmonitor.rs
(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking: we can remove this even field because we have never written it.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct since we never released a version with it written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

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.
@wpaulino
wpaulinoforce-pushed the revert-separate-nondusts-htlc-sources branch from 6dad376 to 4d0eca1CompareApril 29, 2025 18:41

@tankyleotankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nearly onboard, some more nits / questions

Comment on lines -3101 to -3108
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.trust().nondust_htlcs().len());
for (a, b) in htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).map(|(h, _, _)| h).zip(holder_commitment_tx.trust().nondust_htlcs().iter()) {
debug_assert_eq!(a, b);
}
debug_assert_eq!(htlc_outputs.iter().filter(|(_, s, _)| s.is_some()).count(), holder_commitment_tx.counterparty_htlc_sigs.len());
for (a, b) in htlc_outputs.iter().filter_map(|(_, s, _)| s.as_ref()).zip(holder_commitment_tx.counterparty_htlc_sigs.iter()) {
debug_assert_eq!(a, b);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept these debug statements around - would you prefer we remove them ?

return Err(());
}

Ok(Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to add these debug checks above this line ?

Suggested change
Ok(Self{
debug_assert!(
holder_commitment_tx.nondust_htlcs().iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(htlc, _, _)| htlc))
.all(|(htlc_a, htlc_b)| htlc_a == htlc_b)
);
debug_assert!(
holder_commitment_tx.counterparty_htlc_sigs.iter().zip(holder_signed_tx.htlc_outputs.iter().map(|(_, sig, _)| sig.as_ref().unwrap()))
.all(|(sig_a, sig_b)| sig_a == sig_b)
);
Ok(Self{

(0, commitment_tx, required),
(1, claimed_htlcs, optional_vec),
(2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks feel free to resolve

counterparty_htlc_sig = Some(msg.htlc_signatures[idx]);
}
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
htlc_outputs.push((htlc, counterparty_htlc_sig, source_opt.cloned()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like this above this line:

 // For backwards compatibility, set the signature of non-dust HTLCs here

// HTLCs in the `CommitmentTransaction`.
nondust_htlc_sources: Vec<HTLCSource>,
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Non-dust `HTLCOutputInCommitment`'s are stored in both `tx`, and `htlcs`; we accept this tradeoff (since it allows us to keep the HTLC-source pairs intact / things will make more sense in splicing)

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess I'm not really clear on if we need to do this now? Seems like it kinda sucks to go back to duplicating the signatures in the HTLC output list and and how much code does it actually clean up to map htlc_outputs from a 3-tuple to a pair vs joining the dust and non-dust outputs?

@wpaulino

Copy link
Copy Markdown
ContributorAuthor

Closing in favor of #3774.

@wpaulino
wpaulino deleted the revert-separate-nondusts-htlc-sources branch May 12, 2025 16:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@wpaulino@ldk-reviews-bot@TheBlueMatt@tankyleo