Skip to content

Add splice details to TransactionType::Splice#4570

Open
jkczyz wants to merge 2 commits intolightningdevkit:mainfrom
jkczyz:2026-04-splice-transaction-type
Open

Add splice details to TransactionType::Splice#4570
jkczyz wants to merge 2 commits intolightningdevkit:mainfrom
jkczyz:2026-04-splice-transaction-type

Conversation

@jkczyz
Copy link
Copy Markdown
Contributor

@jkczyz jkczyz commented Apr 17, 2026

Surface splice broadcast metadata downstream, so consumers (notably LDK Node) can reconcile PaymentDetails / PendingPaymentDetails directly from the broadcaster callback.

  • TransactionType::Splice now carries contribution: Option<FundingContribution> — our local contribution for this round, and replaced_txid: Option<Txid> — the prior negotiated candidate being replaced on RBF. The alternative (reacting to SplicePending) is worse: that event races with BDK wallet sync and doesn't carry the replaced txid.
  • FundingContribution gains public getters for estimated_fee, inputs, and max_feerate; feerate is elevated from pub(super) to pub. Combined with existing value_added, outputs, change_output, this exposes everything a downstream consumer needs without reaching into the raw transaction.

The Hash derive on TransactionType is dropped (unused in the workspace; FundingContribution transitively contains ConfirmedUtxo which lacks Hash).

jkczyz and others added 2 commits April 17, 2026 10:12
`TransactionType::Splice` now carries the local contribution to the
splice and, for RBF, the txid of the prior negotiated candidate being
replaced. This lets LDK Node update its `PaymentDetails` and
`PendingPaymentDetails` from the broadcast callback without re-deriving
the contribution from the raw transaction or tracking RBF chains
itself.

Driving these updates from the `SplicePending` event instead is more
complicated because that event races with BDK wallet syncing and
doesn't carry the replaced txid; the broadcast callback is a cleaner
integration point.

The `Hash` derive on `TransactionType` is dropped since it isn't used
anywhere in the workspace and `FundingContribution` (now embedded)
transitively contains `ConfirmedUtxo`, which doesn't derive `Hash`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add public getters for `estimated_fee`, `inputs`, and `max_feerate`, and
elevate `feerate` from `pub(super)` to `pub`. Together with the existing
`value_added`, `outputs`, and `change_output`, this gives downstream
consumers of `TransactionType::Splice` (notably LDK Node, which updates
`PaymentDetails` from the broadcast callback) the data they need
without reaching into the raw transaction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Apr 17, 2026

👋 Thanks for assigning @wpaulino 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.

@jkczyz jkczyz requested review from TheBlueMatt and wpaulino April 17, 2026 21:14
@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

ldk-claude-review-bot commented Apr 17, 2026

Review Summary

No new issues found beyond what was covered in the prior review pass.

Analysis of the changes:

  • replaced_txid computation via checked_sub(2) on negotiated_candidates is correct — the current candidate is pushed at line 9338 before this code runs, so index len-2 correctly references the prior candidate being replaced.
  • contributions.last().cloned() correctly reflects the local node's most recent contribution: it returns None when we never contributed (pure acceptor), and returns the current round's (possibly fee-adjusted) contribution otherwise, matching the documented semantics at channel.rs:2906-2916.
  • The sequential RBF test (test_splice_rbf_sequential) correctly chains replaced_txid values: Nonesplice_tx_0splice_tx_1.
  • New public getters on FundingContribution are straightforward; FundingTxInput (alias for ConfirmedUtxo) is already pub with public accessors.
  • Hash derive removal is safe — no HashMap<TransactionType, _> or HashSet<TransactionType> usage exists in the workspace.
  • PartialEq/Eq on TransactionType still compiles because all nested types (FundingContribution, Option<Txid>) implement those traits.

The prior review's concern about post-0-conf-promotion rebroadcast using TransactionType::Funding instead of TransactionType::Splice (in monitor_updating_restored at channel.rs:9710-9723) remains valid as a pre-existing issue that this PR doesn't address but interacts with.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt @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
Copy link
Copy Markdown

🔔 1st Reminder

Hey @TheBlueMatt @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
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @TheBlueMatt @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
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @TheBlueMatt @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 thread lightning/src/ln/channel.rs Outdated
Comment on lines +7241 to +7256
if !self.context.is_manual_broadcast
&& !waiting_for_batch
&& self.funding.get_funding_tx_confirmation_height().is_none()
{
if let Some(tx) = &self.funding.funding_transaction {
return Some((
tx.clone(),
TransactionType::Funding {
channels: vec![(
self.context.counterparty_node_id,
self.context.channel_id,
)],
},
));
}
}
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.

Bug: After 0-conf splice promotion, self.pending_splice is None (cleared at maybe_promote_splice_funding line 11680) and self.funding holds the splice's FundingScope with funding_tx_confirmation_height == 0 and funding_transaction = Some(splice_tx). This initial-funding branch will match:

  • is_manual_broadcast = false (normal channel)
  • waiting_for_batch = false (channel is ChannelReady)
  • get_funding_tx_confirmation_height().is_none() = true (height is 0)
  • funding_transaction = Some(splice_tx)

...and return TransactionType::Funding wrapping the splice transaction. The splice branch below is unreachable because self.pending_splice is None.

The test test_splice_rebroadcast_uses_splice_type_after_0conf_promotion (added in this PR) expects TransactionType::Splice with the original contribution preserved, but this code would produce TransactionType::Funding. That test will fail.

A possible fix: check self.funding.channel_transaction_parameters.splice_parent_funding_txid — it is Some(...) for promoted splices and None for initial funding. You'd also need to persist the contribution and replaced_txid metadata somewhere that survives promotion (e.g., on FundingScope or ChannelContext), since pending_splice is cleared.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Discussed offline. Dropped the commit to retry for now. We may do this later in ChannelMonitor where we'll store the FundingContribution.

@jkczyz jkczyz force-pushed the 2026-04-splice-transaction-type branch from dbaf6d9 to 86dcede Compare April 22, 2026 18:36
Sign up for free to 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.

3 participants