Skip to content

sweepbatcher: notify caller about confirmations#919

Merged
starius merged 13 commits into
lightninglabs:masterfrom
starius:conf-chan
Jun 12, 2025
Merged

sweepbatcher: notify caller about confirmations#919
starius merged 13 commits into
lightninglabs:masterfrom
starius:conf-chan

Conversation

@starius

@starius starius commented Apr 9, 2025

Copy link
Copy Markdown
Collaborator

Add fields ConfChan and ConfErrChan to sweepbatcher.SpendNotifier type which is a part of SweepRequest passed to AddSweep method.

This is needed to reuse confirmation notifications on the calling side the same way it is done for spending notifications.

Added missing tests for notifications sent through channels of SpendNotifier.

Pull Request Checklist

  • Update release_notes.md if your PR contains major features, breaking changes or bugfixes

@starius
starius marked this pull request as ready for review April 9, 2025 04:06
@starius
starius requested review from bhandras, hieblmi and sputn1ck April 9, 2025 04:15
Comment thread sweepbatcher/store.go
@@ -213,8 +213,8 @@ type dbBatch struct {
// ID is the unique identifier of the batch.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe stupid question, but missing lots of context. This commit changes more than the mock, and feels a bit complicated, can you maybe elaborate what the changes to the other files are?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I updated the commit message:

sweepbatcher: align dbBatch type with DB schema

Previously, dbBatch had a State field (enum: Open, Closed, Confirmed), but in
the database it is represented as a boolean Confirmed. The Closed state was
stored the same way as Open. This wasn't an issue in practice, since an Open
batch is quickly transitioned to Closed after startup.

However, the in-memory mock stores plain dbBatch instances, leading to
inconsistent behavior between the mock and the real DB-backed store. This
commit updates dbBatch to match the database representation by replacing

Also added a note to the description of Closed const:

// Closed is the state in which the batch is no longer able to accept
// new sweeps. NOTE: this state exists only in-memory. In the database
// it is stored as Open and converted to Closed after a spend
// notification arrives (quickly after start of Batch.Run).
Closed batchState = 1

@bhandras bhandras left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good. Perhaps we don't even need the "closed" state?

Comment thread sweepbatcher/sweep_batcher.go
Comment thread sweepbatcher/sweep_batch.go Outdated
// If the sweep's notifier is empty then this means that a swap
// is not waiting to read an update from it, so we can skip
// the notification part.
if s.notifier == nil || *s.notifier == (SpendNotifier{}) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why check *s.notifier == (SpendNotifier{} ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It was a copy-paste. I updated this line:

if s.notifier == nil || s.notifier.SpendErrChan == nil {

Comment thread sweepbatcher/store_mock.go
Comment thread sweepbatcher/store.go Outdated

if row.Confirmed {
batch.State = batchOpen
batch.State = batchConfirmed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume this just resulted in the batches restarting (and then getting to confirmed state again after spend notification received)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, and then after confirmation notification (after 3 confs).

Open -(spend notif)-> Closed -(conf notif)-> Confirmed

// Then we get the total amount that was swept by the batch.
totalSwept, err := b.store.TotalSweptAmount(ctx, parentBatchID)
if err != nil {
cancel()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm, this is a great fix, but how did we ever not get cancelled?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This code path is executed only when a fully confirmed sweep is added again to the sweeper. This may happen if the daemon was down when the sweep was fully confirmed (got 3rd confirmation). This is not the main code path.

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.

+1, great catch

// manages to be added during this time, it will be
// detected as missing when analyzing the spend
// notification and will be added to new batch.
batch.state = Open

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But this also means that any new sweeps could be added, and then we just would try to publish the batch again (and fail as inputs are spent). It's robust so not a huge issue, but perhaps we could avoid.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Avoiding this would require a DB migration, which doesn’t seem worthwhile just to improve this edge case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SGTM!

Comment thread sweepbatcher/sweep_batch.go
@starius

starius commented Apr 13, 2025

Copy link
Copy Markdown
Collaborator Author

Looks good. Perhaps we don't even need the "closed" state?

I think it makes sense to keep Closed to make this thing easier to understand / debug.

Also maybe greedy batch selection can work worse if we let it add to Closed.

@starius
starius requested review from bhandras and sputn1ck April 13, 2025 21:15
@levmi
levmi removed the request for review from hieblmi April 15, 2025 15:29
@starius
starius marked this pull request as draft April 26, 2025 04:35
@starius

starius commented Apr 26, 2025

Copy link
Copy Markdown
Collaborator Author

Converting to draft. I'm working to add more commits here and to rebase it on top of #891

@starius starius changed the title sweepbatcher: notify caller about confirmations [WIP] sweepbatcher: notify caller about confirmations Apr 26, 2025
@starius
starius force-pushed the conf-chan branch 2 times, most recently from a67a666 to e234022 Compare April 29, 2025 03:52
@starius starius changed the title [WIP] sweepbatcher: notify caller about confirmations sweepbatcher: notify caller about confirmations Apr 29, 2025
@starius
starius marked this pull request as ready for review April 29, 2025 03:53
@starius
starius force-pushed the conf-chan branch 4 times, most recently from 494ebfb to c292ec2 Compare May 5, 2025 20:46
@levmi
levmi requested review from hieblmi and removed request for sputn1ck May 6, 2025 14:22
@starius
starius requested a review from sputn1ck May 8, 2025 21:29

@bhandras bhandras left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Really nice set of fixes! Making sweep-batcher more robust one step at a time 🚀
LGTM 🎉

// new sweeps.
// new sweeps. NOTE: this state exists only in-memory. In the database
// it is stored as Open and converted to Closed after a spend
// notification arrives (quickly after start of Batch.Run).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍

// manages to be added during this time, it will be
// detected as missing when analyzing the spend
// notification and will be added to new batch.
batch.state = Open

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SGTM!

Comment thread sweepbatcher/sweep_batch.go
Comment thread sweepbatcher/sweep_batch.go Outdated

spendDetail := SpendDetail{
Tx: spendTx,
OnChainFeePortion: getFeePortionPaidBySweep(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alternatively we could communicate the fee portion once the sweep is fully-confirmed. wdyt?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I added OnChainFeePortion to type ConfDetail, which is send through ConfChan.

I did it in a separate commit to make it clear what changed. I plan to squash it with the previous commit before merging.

@lightninglabs-deploy

Copy link
Copy Markdown

@hieblmi: review reminder
@sputn1ck: review reminder

starius added 8 commits June 11, 2025 10:34
Forgot to return the calculated total value.
Support sending errors to error channels returned by RegisterSpendNtfn and
RegisterConfirmationsNtfn.
Function monitorSpendAndNotify used to cancel the context passed to
RegisterSpendNtfn right after starting the goroutine processing results.
Spend notifications were missed.

Now the context is canceled when the goroutine finishes.
@starius

starius commented Jun 11, 2025

Copy link
Copy Markdown
Collaborator Author

I removed commits related to presigned mode from this PR. I'll open another PR.

@hieblmi hieblmi 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.

LGTM, great set of fixes!

// Then we get the total amount that was swept by the batch.
totalSwept, err := b.store.TotalSweptAmount(ctx, parentBatchID)
if err != nil {
cancel()

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.

+1, great catch

primarySweep.outpoint.String())

for {
select {

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.

please fix typo in commit message.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

Comment thread sweepbatcher/sweep_batch.go Outdated
b.state = Closed

return b.persist(ctx)
if err := b.persist(ctx); err != nil {

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.

nit: can do err = b.persist...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. Thanks!

Comment thread sweepbatcher/sweep_batch.go Outdated
b.state = Closed

return b.persist(ctx)
if err := b.persist(ctx); err != nil {

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.

another great catch!

// continue.
case <-notifier.QuitChan:
}
}(s.notifier)

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.

👍

starius added 5 commits June 12, 2025 12:31
The loop always had exactly one iteration.
If monitorConfirmations fails, we still want to persist the state to DB.
Previously, dbBatch had a State field (enum: Open, Closed, Confirmed), but in
the database it is represented as a boolean Confirmed. The Closed state was
stored the same way as Open. This wasn't an issue in practice, since an Open
batch is quickly transitioned to Closed after startup.

However, the in-memory mock stores plain dbBatch instances, leading to
inconsistent behavior between the mock and the real DB-backed store. This
commit updates dbBatch to match the database representation by replacing
the State field with a Confirmed boolean.
Add fields ConfChan and ConfErrChan to SpendNotifier type which is a part of
SweepRequest passed to AddSweep method.

This is needed to reuse confirmation notifications on the calling side the same
way it is done for spending notifications.

@sputn1ck sputn1ck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

case <-ctx.Done():
return
}
case <-ctx.Done():

@sputn1ck sputn1ck Jun 12, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

prior we returned here and in all other ctx.done cases, is this intended? We just fall through to the last return right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Prior we returned in all the cases of the select, so the for loop always used to have exactly one iteration. That is why the for loop is not needed and return statements can be removed, because this is the end of the function anyway.

@starius
starius merged commit 59f67de into lightninglabs:master Jun 12, 2025
4 checks passed
@starius
starius deleted the conf-chan branch June 12, 2025 18: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.

6 participants