') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); [Splicing] Tx negotiation during splicing by optout21 · Pull Request #3736 · lightningdevkit/rust-lightning · GitHub
Skip to content

[Splicing] Tx negotiation during splicing - #3736

Merged
wpaulino merged 9 commits into
lightningdevkit:mainfrom
optout21:splice-dual-tx4
Jul 31, 2025
Merged

[Splicing] Tx negotiation during splicing#3736
wpaulino merged 9 commits into
lightningdevkit:mainfrom
optout21:splice-dual-tx4

Conversation

@optout21

@optout21optout21 commented Apr 15, 2025

Copy link
Copy Markdown
Contributor

Implementation of transaction negotiation during splicing.
Builds on 3407 and 3443.

  • No new phase, Funded(FundedChannel) is used throughout splicing
  • Both FundedChannel and PendingV2Channel can act as a transaction constructor
  • PendingV2Channel logic is put behind a trait -- FundingTxConstructorV2
  • A RenegotiatingScope is used to store extra state during splicing
  • FundingChannel can act as a FundingTxConstructorV2, using the state from RenegotiatingScope (if present)
  • Since both FundedChannel and FundingTxConstructor has context(), context accessors are extracted into a common base trait, ChannelContextProvider (it is also shared by InitialRemoteCommitmentReceiver).

(Also relevant: #3444)

@ldk-reviews-bot

ghost commented Apr 15, 2025

Copy link
Copy Markdown

👋 I see @wpaulino was un-assigned.
If you'd like another reviewer assignment, please click here.

@ldk-reviews-bot

ghost commented Apr 17, 2025

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz@wpaulino! 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.

1 similar comment
@ldk-reviews-bot

ghost commented Apr 17, 2025

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz@wpaulino! 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.

@ldk-reviews-bot

ghost commented Apr 19, 2025

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz@wpaulino! 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.

1 similar comment
@ldk-reviews-bot

ghost commented Apr 19, 2025

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz@wpaulino! 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.

@ldk-reviews-bot

ghost commented Apr 21, 2025

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz@wpaulino! 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.

1 similar comment
@ldk-reviews-bot

ghost commented Apr 21, 2025

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz@wpaulino! 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.

@ldk-reviews-bot

ghost commented Apr 23, 2025

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz@wpaulino! 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.

1 similar comment
@ldk-reviews-bot

ghost commented Apr 23, 2025

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz@wpaulino! 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.

@ldk-reviews-bot

ghost commented Apr 26, 2025

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz@wpaulino! 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.

1 similar comment
@ldk-reviews-bot

ghost commented Apr 26, 2025

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz@wpaulino! 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.

@ldk-reviews-bot

ghost commented Apr 28, 2025

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz@wpaulino! 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.

1 similar comment
@ldk-reviews-bot

ghost commented Apr 28, 2025

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz@wpaulino! 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.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated

ghost 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.

Sorry about the late review. We were traveling to an off site last week. Just a high-level pass on the first four commits. Will need to take a closer look at the last one.

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
@codecov

ghost commented Apr 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.36111% with 94 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.95%. Comparing base (7ec13dc) to head (41cbc1f).
⚠️ Report is 49 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channel.rs59.83%49 Missing ⚠️
lightning/src/ln/channelmanager.rs33.33%32 Missing and 6 partials ⚠️
lightning/src/ln/interactivetxs.rs93.57%6 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3736 +/- ##
==========================================
+ Coverage 88.92% 88.95% +0.02% 
==========================================
Files 174 174 Lines 123869 124200 +331 Branches 123869 124200 +331 ==========================================
+ Hits 110152 110476 +324 + Misses 11256 11247 -9 - Partials 2461 2477 +16 
FlagCoverage Δ
fuzzing22.63% <0.00%> (+0.45%)⬆️
tests88.77% <67.36%> (+0.02%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@optout21
optout21force-pushed the splice-dual-tx4 branch 3 times, most recently from 88d2e83 to 866368dCompareMay 5, 2025 11:59
@optout21

ghost commented May 6, 2025

Copy link
Copy Markdown
ContributorAuthor

Ready for a new round of review. I have addressed the comments, applied most of them. There is still one to-do (update channel reserve values), that I will do, but the rest is ready for review.
I did the changes in separate 'fix' commits.

@optout21

ghost commented May 13, 2025

Copy link
Copy Markdown
ContributorAuthor

Ready for a new round of review. All pending and newly raised comments addressed.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment on lines +2256 to +2273
let post_value_to_self_msat = AddSigned::checked_add_signed(
prev_funding.value_to_self_msat,
our_funding_contribution_sats * 1000,
);
debug_assert!(post_value_to_self_msat.is_some());
let post_value_to_self_msat = post_value_to_self_msat.unwrap();

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.

The debug_assert!(post_value_to_self_msat.is_some()) assumes that adding our_funding_contribution_sats * 1000 to prev_funding.value_to_self_msat will never overflow. However, this isn't guaranteed, especially with large values.

If the addition overflows, post_value_to_self_msat will be None, and the subsequent unwrap() will panic. Consider handling the overflow case explicitly, perhaps by capping the value at u64::MAX or returning an error if the addition would overflow. This would make the code more robust against edge cases.

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

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.

This shouldn't be possible so long as our_funding_contribution_sats doesn't exceed the total bitcoin supply. I will add more checks in the splice-out follow-up PR.

This is a simple rename, DualFundingContext to FundingNegotiationContext,
to suggest that this is use not only in dual-funded channel open.
Also rename the field dual_funding_context to funding_negotiation_context.
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/interactivetxs.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated

ghost 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.

Couple comments still left to address

Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/interactivetxs.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs
@jkczyz
jkczyzforce-pushed the splice-dual-tx4 branch 3 times, most recently from 9766f21 to cdc393cCompareJuly 29, 2025 22:15
Comment threadlightning/src/ln/channel.rs
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/interactivetxs.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Jeffrey Czyzand others added 6 commits July 31, 2025 10:24
PendingSplice holds a FundingScope being negotiated. However, when
implementing funding negotiation, other states are possible depending on
which party initiated the splice. Using an enum prevents needing various
Option fields which may result in invalid states.
When the user initiates the splice, the FundingNegotiationContext must
be held until the counterparty responds with splice_ack. At that point
enough information becomes available to create a new FundingScope and an
InteractiveTxConstructor.
When the counterparty initiates the splice, both a new FundingScope and
an InteractiveTxConstructor can be created immediately when responding
with splice_ack.
After the transaction is constructed, those are no longer needed. At
that point an InteractiveTxSigningSession is tracked until signatures
are exchanged.
FundingNegotiationContext and PendingSplice both hold the user's
contribution to a splice, which doesn't need to be duplicated. Instead,
only store this in FundingNegotiationContext, which then can be used to
create an InteractiveTxConstructor when transitioning to
FundingNegotiation:::ConstructingTransaction.
This commit updates that code to properly compute change outputs using
the FundingNegotiationContext by not considering the shared input since
it is accounted for in the shared output.
Co-authored-by: Wilmer Paulino <wilmer@wilmerpaulino.com>
Co-authored-by: Jeffrey Czyz <jkczyz@gmail.com>
Instead of passing the shared funding input as another parameter to
FundingNegotiationContext::into_interactive_tx_constructor, make it a
member of FundingNegotiationContext.
InteractiveTxConstructor was only used in PendingV2Channel methods, but
for splicing those methods are needed for FundedChannel, too. Refactor
the code such that each type has a method for accessing its
InteractiveTxConstructor such that it can be called in either use,
refactoring code out of PendingV2Channel as needed.
Co-authored-by: Wilmer Paulino <wilmer@wilmerpaulino.com>
Co-authored-by: Jeffrey Czyz <jkczyz@gmail.com>
Update splice_channel, split_init, and splice_ack to implement
transitioning from splice initialization to funding transaction
negotiation.
Co-authored-by: optout <13562139+optout21@users.noreply.github.com>
Co-authored-by: Jeffrey Czyz <jkczyz@gmail.com>
Instead of passing the optional change script as another parameter to
FundingNegotiationContext::into_interactive_tx_constructor, make it a
member of FundingNegotiationContext. Also, allow it to be passed to
ChannelManager::splice_channel.
@jkczyzjkczyz mentioned this pull request Jul 31, 2025
Jeffrey Czyz added 2 commits July 31, 2025 17:11
If splicing fails, the previous funding is still usable. Send a tx_abort
message if the channel was already successfully funded, thus aborting
the negotiation. Otherwise, close the channel as this means the failure
occurred during v2 channel establishment.
Once splice has been promoted, the interactive_tx_signing_session is no
longer needed. Clear it at this time to prevent being in an inconsistent
state.

ghost 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.

Thanks @optout21 for getting this started and @jkczyz for taking over ⚡

Ok((commitment_signed, event)) => (commitment_signed, event),
Err(tx_abort) => {
if chan_entry.get().is_funded() {
peer_state.pending_msg_events.push(MessageSendEvent::SendTxAbort {

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 don't think it applies here since the negotiated completed and we consumed the constructor, but generally when we send this we want to make sure we're no longer tracking one, and also probably assert that tx_signatures have not been exchanged.

We'll also want to follow-up with sending tx_abort in most other error cases until the tx_signatures exchange.

@wpaulino
wpaulino merged commit 1e1d300 into lightningdevkit:mainJul 31, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants

@optout21@ldk-reviews-bot@jkczyz@wpaulino