Skip to content

Conversation

@t-bast
Copy link
Member

@t-bast t-bast commented May 28, 2025

We previously immediately watched for confirmation of transactions that our peer couldn't double-spend, which had a few issues:

  • if we RBF-ed those transactions after a restart and a previous version confirmed, we wouldn't detect it and wouldn't move to the CLOSED state
  • if the transaction had a long CSV delay, we were wasting performance in the watcher while that CSV isn't complete

We change that behavior and instead watch all outputs of the commitment transaction that we may spend. We only watch for confirmations after we detect that the output has been spent (either in the mempool or in a block). This ensures that RBF attempts are correctly handled, and that we don't watch transactions until they've been published (and thus CSV delays are satisfied).

We also explicitly split 2nd-stage and 3rd-stage transactions. It is a bit verbose and awkward for now, but it will become cleaner once we stop storing unconfirmed transactions and instead re-compute them when restarting. It also makes testing easier: we took this opportunity to ensure that closing tests cover all scenarios and use better assertions on watches and transactions.

Updating the closing tests was a lot of work, but it was a good opportunity to ensure that they are more complete and easier to read.

@t-bast t-bast requested a review from sstone May 28, 2025 10:35
We previously immediately watched for confirmation of transactions that
our peer couldn't double-spend, which had a few issues:

- if we RBF-ed those transactions after a restart and a previous version
  confirmed, we wouldn't detect it and wouldn't move to the CLOSED state
- if the transaction had a long CSV delay, we were wasting performance
  in the watcher while that CSV isn't complete

We change that behavior and instead watch all outputs of the commitment
transaction that we may spend. We only watch for confirmations after we
detect that the output has been spent (either in the mempool or in a
block). This ensures that RBF attempts are correctly handled, and that
we don't watch transactions until they've been published (and thus CSV
delays are satisfied).

We also explicitly split 2nd-stage and 3rd-stage transactions. It is a
bit verbose and awkward for now, but it will become cleaner once we
stop storing unconfirmed transactions and instead re-compute them when
restarting. It also makes testing easier: we took this opportunity to
ensure that closing tests cover all scenarios and use better assertions
on watches and transactions.
@t-bast t-bast force-pushed the refactor-watch-spent-before-confirmed branch from 0e01118 to fe6748c Compare June 2, 2025 08:57
Copy link
Member

@sstone sstone left a comment

Choose a reason for hiding this comment

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

We now set WatchOutputSpent on our main and htlc outputs, and will only set WatchConfirm once these outputs have been spent, but it seems we only test this behaviour with HTLC transactions, not with our "claim main ouput" tx ? (maybe I've missed it)

@t-bast
Copy link
Member Author

t-bast commented Jun 2, 2025

We now set WatchOutputSpent on our main and htlc outputs, and will only set WatchConfirm once these outputs have been spent, but it seems we only test this behaviour with HTLC transactions, not with our "claim main ouput" tx ? (maybe I've missed it)

This is tested in ChannelStateTestsHelperMethods.scala, in the main localClose and remoteClose function. Let me know if it doesn't include what you're suggesting, but I think that covers your concern!

@sstone
Copy link
Member

sstone commented Jun 3, 2025

We now set WatchOutputSpent on our main and htlc outputs, and will only set WatchConfirm once these outputs have been spent, but it seems we only test this behaviour with HTLC transactions, not with our "claim main ouput" tx ? (maybe I've missed it)

This is tested in ChannelStateTestsHelperMethods.scala, in the main localClose and remoteClose function. Let me know if it doesn't include what you're suggesting, but I think that covers your concern!

Yes it does. I was looking for explicit tests in ClosingStateSpec and missed this.

@t-bast t-bast merged commit f8b1272 into master Jun 3, 2025
1 check passed
@t-bast t-bast deleted the refactor-watch-spent-before-confirmed branch June 3, 2025 09:15
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