Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Splicing] Preserve funding_transaction for the later lifecycle of the channel #3317

Closed
wants to merge 1 commit into from

Conversation

optout21
Copy link
Contributor

@optout21 optout21 commented Sep 16, 2024

Fixes #3300

In splicing, the funding transaction (not just the ID) is needed during splice negotiation.
Funding transaction is kept in field ChannelContext.funding_transaction.
However, the way it is currently used is that it is cleared when the tx is broadcast, and this fact is used in some logic.

This change:

  • does not clear ChannelContext.funding_transaction when tx is broadcast, but it's preserved
  • adds a funding_transaction_broadcast bool field to indicate if it has been broadcast

So behavior of not (re)broadcasting twice is preserve (though I am not sure whether this was intentional and it is needed, one test fails if this is changed (test_completed_payment_not_retryable_on_reload)).

Alternative would be to leave funding_transaction unchanged, and introduce a new funding_transaction_saved field, that is set the same way, but never cleared. This way existing logic would not be affected, but the tx would be kept in two copies.

Copy link

codecov bot commented Sep 16, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 90.88%. Comparing base (66fb520) to head (8339270).
Report is 64 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3317      +/-   ##
==========================================
+ Coverage   89.66%   90.88%   +1.22%     
==========================================
  Files         126      127       +1     
  Lines      102676   115316   +12640     
  Branches   102676   115316   +12640     
==========================================
+ Hits        92062   104806   +12744     
+ Misses       7894     7869      -25     
+ Partials     2720     2641      -79     
Flag Coverage Δ
?

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.

Copy link
Contributor

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

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

Does this mean existing channels cannot be spliced?

@@ -5336,7 +5349,10 @@ impl<SP: Deref> Channel<SP> where
(matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(flags) if !flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH)) ||
matches!(self.context.channel_state, ChannelState::ChannelReady(_)))
{
self.context.funding_transaction.take()
let res = self.context.funding_transaction_unless_rebroadcast();
// Note: this is legacy logic, prevents (re)broadcasting twice, unclear if needed
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure what is meant by this. Why is it legacy logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What I mean by legacy:

  • I don't fully understand why it works this way
  • I suspect it is not by intention
  • but I chose to introduce an extra field just to keep the exact behavior
  • changing it would break one unit tests, and who knows what else in the field

Copy link
Contributor

Choose a reason for hiding this comment

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

@TheBlueMatt Any thoughts on making funding_transaction a three-state enum instead of introducing a flag and continuing to use Option? We'd probably need the flag in the serialization logic, though.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We could do that. That said, I do think the rebroadcasting logic is really dumb right now, so this may be an opportunity to clean it up. We could/maybe should drop all the tracking of if we broadcasted a transaction and replace it with a much simpler "if its not confirmed, broadcast it" heuristic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there any downside for occasional repeated broadcast? At most some unnecessary resource usage?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not hugely, no. We do get users confused when broadcasts fail all the time, so we mostly try to avoid it, but there's not a huge cost. We should definitely not rebroadcast forever though.

lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
@@ -8934,6 +8954,8 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
let cur_holder_commitment_point = Some(self.context.holder_commitment_point.current_point());
let next_holder_commitment_point = self.context.holder_commitment_point.next_point();

let funding_transaction_rebroadcast_flag = Some(self.context.funding_transaction_rebroadcast_flag);
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of wrapping this, use required below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's optional due to backwards compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's an odd TLV so older versions will simply ignore it.

@@ -9289,6 +9312,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut next_holder_commitment_point_opt: Option<PublicKey> = None;
let mut is_manual_broadcast = None;

let mut funding_transaction_rebroadcast_flag: Option<bool> = None;
Copy link
Contributor

Choose a reason for hiding this comment

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

And then use (default_value, false) below instead of option.

@@ -5336,7 +5349,10 @@ impl<SP: Deref> Channel<SP> where
(matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(flags) if !flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH)) ||
matches!(self.context.channel_state, ChannelState::ChannelReady(_)))
{
self.context.funding_transaction.take()
let res = self.context.funding_transaction_unless_rebroadcast();
// Note: this is legacy logic, prevents (re)broadcasting twice, unclear if needed
Copy link
Contributor

Choose a reason for hiding this comment

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

@TheBlueMatt Any thoughts on making funding_transaction a three-state enum instead of introducing a flag and continuing to use Option? We'd probably need the flag in the serialization logic, though.

@optout21
Copy link
Contributor Author

optout21 commented Oct 9, 2024

Note: Changing this behavior causes a single test case to fail: ln::payment_tests::test_completed_payment_not_retryable_on_reload
(reproducible by not resetting funding_transaction in the original code, or keeping funding_transaction_rebroadcast_flag always false in the version of this PR)

The check that fails expects 1 broadcasted transaction, while with the change it will have the transaction broadcasted 4 times. I suspect this change does not really affect the desired behavior, this checks is simply over-specific and that can be updated, and the change is acceptable.

  assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0).len(), 4);

@dunxen dunxen self-requested a review October 10, 2024 15:50
@optout21 optout21 marked this pull request as draft October 17, 2024 16:59
@optout21
Copy link
Contributor Author

Alternative simpler solution in #3380 -- never clears the funding_transaction field, no new fields, which may result in additional rebroadcasts.
One should be chosen -- I lean for the simpler solution, but I have no strong opinion.

@TheBlueMatt
Copy link
Collaborator

Supersceded by #3380.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Splicing] Make funding transaction available in funded channel
3 participants