-
Notifications
You must be signed in to change notification settings - Fork 123
sweepbatcher: notify caller about confirmations #919
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
Conversation
@@ -213,8 +213,8 @@ type dbBatch struct { | |||
// ID is the unique identifier of the batch. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
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{}) { |
There was a problem hiding this comment.
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{}
?
There was a problem hiding this comment.
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 {
sweepbatcher/store.go
Outdated
@@ -259,7 +259,7 @@ func convertBatchRow(row sqlc.SweepBatch) *dbBatch { | |||
} | |||
|
|||
if row.Confirmed { | |||
batch.State = batchOpen | |||
batch.State = batchConfirmed |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM!
I think it makes sense to keep Also maybe greedy batch selection can work worse if we let it add to Closed. |
Converting to draft. I'm working to add more commits here and to rebase it on top of #891 |
a67a666
to
e234022
Compare
494ebfb
to
c292ec2
Compare
There was a problem hiding this comment.
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). |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM!
sweepbatcher/sweep_batch.go
Outdated
|
||
spendDetail := SpendDetail{ | ||
Tx: spendTx, | ||
OnChainFeePortion: getFeePortionPaidBySweep( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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.
I removed commits related to presigned mode from this PR. I'll open another PR. |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, great catch
@@ -1822,29 +1822,22 @@ func (b *batch) monitorSpend(ctx context.Context, primarySweep sweep) error { | |||
b.Infof("monitoring spend for outpoint %s", | |||
primarySweep.outpoint.String()) | |||
|
|||
for { | |||
select { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
sweepbatcher/sweep_batch.go
Outdated
// We are no longer able to accept new sweeps, so we mark the batch as | ||
// closed and persist on storage. | ||
b.state = Closed | ||
|
||
return b.persist(ctx) | ||
if err := b.persist(ctx); err != nil { |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks!
sweepbatcher/sweep_batch.go
Outdated
// We are no longer able to accept new sweeps, so we mark the batch as | ||
// closed and persist on storage. | ||
b.state = Closed | ||
|
||
return b.persist(ctx) | ||
if err := b.persist(ctx); err != nil { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
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.
There was a problem hiding this comment.
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(): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Add fields
ConfChan
andConfErrChan
tosweepbatcher.SpendNotifier
type which is a part ofSweepRequest
passed toAddSweep
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
release_notes.md
if your PR contains major features, breaking changes or bugfixes