Commit graph

78 commits

Author SHA1 Message Date
Slyghtning
a10c741a26
lint: fix linter issues 2026-03-09 14:39:45 +01:00
Boris Nagaev
0b53296aa8
multi: factor out sweep fee clamping function 2026-01-15 03:06:22 -05:00
Boris Nagaev
6d58965930
sweepbatcher: persist confirmed batches atomically
Fixes a crash window where handleConf updated the batch row to confirmed but
failed before marking sweeps complete, so re-added sweeps spawned a duplicate
batch or kept retrying. Batch confirmation and sweep completion are now
persisted inside a single DB transaction ConfirmBatchWithSweeps, and handleConf
uses the helper to atomically store the batch and the set of confirmed sweeps.

Added TestSweepBatcherConfirmedBatchIncompleteSweeps that runs against the real
loopdb backend, injects a failure mid-transaction, and verifies the database
never ends up with confirmed=true batches paired with completed=false sweeps.
2025-12-04 00:37:01 -03:00
Boris Nagaev
d82b3624b9
sweepbatcher: fix code format 2025-11-12 01:45:18 -03:00
Boris Nagaev
a5871d6f3e
sweepbatcher: fix change fee accounting, add test
Presigned sweeps that produce a change output misreported the on-chain fee.
The fee portion was derived from the total swept amount minus only the
first transaction output, so any change output was treated as additional fee.
Update getFeePortionForSweep to subtract the value of every tx output so
the fee portion reflects only the actual miner fee paid. Add regression
coverage that sweeps a presigned input with change and asserts the spend
and confirmation notifications report the corrected fee.
2025-10-11 15:21:30 -03:00
Slyghtning
422bcd7077
sweepbatcher: fast-flag enables zero delay publishing 2025-10-07 08:53:39 +02:00
Slyghtning
d00b78ef6b
sweepbatcher: consolidate identical change pkscripts
batch separate change outputs with identical pkscripts
are tallied up and consolidated to a single change output.
2025-07-29 21:22:17 +02:00
Boris Nagaev
54652dc641
sweepbatcher: subtle adjustments for change
- ensurePresigned: use passed minRelayFeeRate instead of chainfee.FeePerKwFloor
 - presign: use minRelayFeeRate for start and minRelayFee
 - presign: make sure minRelayFeeRate is set
 - add tests for presign to test this new behavior; make sure the number of
   transactions is lower if minRelayFeeRate is higher
 - update error message in constructUnsignedTx: use <, not <= (more accurate)
 - use utils.DustLimitForPkScript instead of lnwallet.DustLimitForSize in tests
 - in tests adjust amounts to edge values, add controls
2025-07-29 12:33:56 +02:00
Slyghtning
29348a94df
sweepbatcher: consider min relay fee when constructing batch tx
if constructUnsignedTx constructs a batch transaction that is
below the minimum relay fee, an error is returned.
2025-07-29 12:33:56 +02:00
Slyghtning
bc7d155e69
sweepbatcher: consider change in presigning and batch tx
Presigning sweeps takes change outputs into account.
Each primary deposit id of a sweep group points to
an optional change output.
sweepbatcher.presign scans all passed sweeps for
change outputs and passes them to constructUnsignedTx.
Optional change of a swap is encoded in its sweeps
as a pointer to the same change output. This change
is taken into account when constructing the unsigned
batch transaction when it comes to tx weight and
outputs.
2025-07-29 12:33:55 +02:00
Slyghtning
f30db74756
sweepbatcher: optional change in sweep struct
This commit adds an optional change output to the sweep struct.
2025-07-29 12:33:55 +02:00
Boris Nagaev
8431784338
sweepbatcher: update feerate outside AddSweep
AddSweep may not be called after getting the first confirmation, but feerate
updates are still needed in case of reorg.

Update test TestFeeRateGrows not to call AddSweep again and make sure feerate
is updated itself.
2025-07-24 02:22:06 -03:00
Boris Nagaev
957ca21e61
sweepbatcher: fix reorg detection
The reorg channel is now passed to RegisterSpendNtfn, and waiting for spend
notifications remains active even after the transaction receives its first
confirmation. The dedicated goroutine previously used to wait for the spend
is no longer needed, as we now handle both spend and potential reorg events
in the main event loop while the batch is running.

Following this change, RegisterConfirmationsNtfn runs without a reorg channel,
as it would only detect deep reorgs that undo the final confirmation - something
we can't handle anyway. We can't track fully confirmed swaps indefinitely to
guard against such rare reorgs; instead, we can mitigate the risk by increasing
the required confirmation depth.
2025-07-24 02:21:20 -03:00
Boris Nagaev
0dc5d9eb69
sweepbatcher: check of conf notification has tx 2025-06-26 14:01:16 -03:00
Boris Nagaev
e7d5172a79
sweepbatcher: remove method Presign
Method Presign is not as reliable as SignTx, because it checks transaction by
txid and can miss for example if LockTime is different. SignTx can do everything
Presign was used for.
2025-06-26 14:01:16 -03:00
Boris Nagaev
8a07107811
sweepbatcher: make sure dest pkscript is filled 2025-06-26 14:01:16 -03:00
Boris Nagaev
b961e18fa0
sweepbatcher: fix OnChainFeePortion values
There were two mistakes.

In case of a swap with multiple sweeps only the fee of the first sweep
of a swap was accounted.

Rounding diff (the remainder) was attributed to all the sweeps rather than to
the first (primary) sweep of the batch. The sweep to attribute the remainder
was chosen by comparing SignatureScript which is always empty. New approach is
to find the primary sweep and to compare its outpoint directly.
2025-06-26 14:01:16 -03:00
Boris Nagaev
650cf20fe9
sweepbatcher: re-add sweeps after fully confirmed
In case of a reorg sweeps should not go to another batch but stay in the current
batch until it is reorg-safely confirmed. Only after that the remaining sweeps
are re-added to another batch.

Field sweep.completed is now set to true only for reorg-safely confirmed sweeps.

In handleConf we now use batch.persist() (i.e. store.UpdateSweepBatch) instead
of ConfirmBatch, because we set not only Confirmed flag, but also batchTxid.
2025-06-26 13:36:55 -03:00
Boris Nagaev
1036214160
sweepbatcher: add an option to ignore HTLC txids
Added option WithSkippedTxns, which has one historical problematic tx by default.
Sweeps originating from these transactions are omitted when reading from DB.

loopdb: add column sweep_batches.cancelled and replaced DropBatch with
CancelBatch. It is needed, because sweep.batch_id is a foreign key to batch.

Changed StoreMock.InsertSweepBatch not to reuse batch_id. This is needed by
the test, which checks that new batch has fresh ID.
2025-06-17 14:47:49 -03:00
Boris Nagaev
c70257a4a5
sweepbatcher: notify caller about confirmations
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.
2025-06-12 12:32:09 -03:00
Boris Nagaev
980c7d4e00
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
the State field with a Confirmed boolean.
2025-06-12 12:32:09 -03:00
Boris Nagaev
7edbfeadd8
sweepbatcher: store batch status before monitoring
If monitorConfirmations fails, we still want to persist the state to DB.
2025-06-12 12:32:09 -03:00
Boris Nagaev
fa1bc48a68
sweepbatcher: remove unneeded for loops
The loop always had exactly one iteration.
2025-06-12 12:31:58 -03:00
Boris Nagaev
39f859a046
sweepbatcher: send spend error to notifier 2025-06-11 10:34:22 -03:00
Boris Nagaev
b9b256f4b3
sweepbatcher: add mode with presigned transactions
In this mode sweepbatcher uses transactions provided by the Presigned helper.
Transactions are signed upon adding an input to a batch.

A single Batcher instance can handle both presigned and regular batches.
Currently presigned and non-presigned sweeps never appear in the same batch.
2025-05-08 17:24:10 -03:00
Boris Nagaev
a4a2bfbee3
sweepbatcher: use WithoutCancel for notifications 2025-05-08 17:24:10 -03:00
Boris Nagaev
bb837a4aec
sweepbatcher: load sweeps in AddSweep
This is needed to run additional checks in AddSweep in the next commit.
2025-05-08 17:24:10 -03:00
Boris Nagaev
78ca6bd069
sweepbatcher: rm unused field batchKit.returnChan 2025-05-08 17:24:10 -03:00
Boris Nagaev
149bffcad7
sweepbatcher: fix rounding in constructUnsignedTx
Previously it may round one satoshi towards zero resulting in fee-rate
slightly lower than requested.
2025-05-08 17:24:10 -03:00
Boris Nagaev
1983cc2d80
sweepbatcher: make func constructUnsignedTx pure
Also added a unit test for it.
2025-05-08 17:24:10 -03:00
Boris Nagaev
4c26aefe80
sweepbatcher: fix height hint for confirmations
Previously, we passed the current best block height as the height hint to
RegisterConfirmationsNtfn. If Loop was shut down when a sweep was confirmed
and restarted later, it would use the current best height as the hint. This
caused the confirmation to be missed, since it had already occurred before
that height.

This commit fixes the issue by passing the initiation height of the swap as the
height hint instead. This is consistent with what we do in other places where
we use RegisterConfirmationsNtfn.

Since LND caches previously passed height hints for RegisterConfirmationsNtfn
and uses maximum passed value, to actually recover in such a condition, one has
to restart LND passing the option --height-hint-cache-query-disable and then
restart Loop applying this fix. If a pruned bitcoind backend is used, this might
not work.
2025-04-05 18:01:16 -03:00
Boris Nagaev
e8cf6386cc
sweepbatcher: allow adding groups of inputs
A group of inputs can be added by passing it in SweepRequest.Inputs field.
All the inputs belong to the same swap and are added to the same batch.
2025-04-03 09:49:46 -03:00
Boris Nagaev
472bec0be5
sweepbatcher: customize initialDelay per sweep
Option WithInitialDelay now accepts a function returning initialDelay
depending on sweep's data.

This is needed to be able to wait longer for sweeps with low priority, but
still sweeping high priority sweeps soon.
2025-04-03 09:22:58 -03:00
Boris Nagaev
c80ebb96e9
sweepbatcher: allow swap_hash to be non-unique
SwapHash used to be a key in the sweeps table. Now the key is outpoint which
replaces columns outpoint_txid and outpoint_index. In-memory structures and unit
tests were also updated to use outpoint as key. Outpoint is truly unique.
2025-04-01 11:11:44 -03:00
Boris Nagaev
90e36b5b71
sweepbatcher: add more details to errors
This is useful to debug errors from logs, to follow error propagation path.
2025-03-21 10:58:07 -03:00
Oliver Gugger
65251e8821
multi: update linter, fix issues 2025-03-10 22:59:50 +01:00
Oliver Gugger
722ad8839d
multi: update lnd compile-time dependency to v0.19.0 2025-03-10 22:59:49 +01:00
Boris Nagaev
a2cee86783
sweepbatcher/test: fix races in require.Eventually
The code inside require.Eventually runs in parallel with the event loops
of the batcher and its batches. Accessing fields of the batcher and batches
must be done within an event loop.

To address this, testRunInEventLoop methods were added to the Batcher and batch
types. Unit tests were then rewritten to use this approach when accessing
batcher and batch fields.

Additionally, in many cases, receive operations from RegisterSpendChannel
were moved before require.Eventually. This prevents testRunInEventLoop from
getting stuck in an event loop while blocked on a RegisterSpendChannel send
operation.
2025-03-04 13:45:03 -03:00
Boris Nagaev
ab59bdafd2
sweepbatcher: replace batch logger atomically
This is needed to fix crashes in unit tests under -race.
2025-03-04 12:56:54 -03:00
Boris Nagaev
2c38de121c
sweepbatcher: always enabled mixed batches
Mixed batches approach covers the case where all the inputs are cooperative
(function publishBatchCoop) and it is better than fully non-cooperative case
(function publishBatch), but it can also create such transaction if needed, i.e.
if all the sweeps are non-cooperative. So we can remove functions publishBatch,
publishBatchCoop, option WithMixedBatch and associated code in greedy batch
selection algorithm.
2025-02-05 15:16:57 -03:00
Boris Nagaev
6efd010014
sweepbatcher: run batch with currentHeight set
Prevent a crash with "a height hint greater than 0 must be provided" error when
monitorSpend starts at the beginning of batch.Run.

The timer timerChan is now initialized at the start, because it was previously
initialized after the first block (the current tip) was read from blockChan and
now the first block is read before the main for-select loop to fill the field
currentHeight in advance.
2025-01-17 12:39:31 -03:00
Boris Nagaev
89fbf9a5cf
sweepbatcher: rm unused field batch.blockEpochChan 2025-01-17 12:39:31 -03:00
Boris Nagaev
06ed714e9a
sweepbatcher: check that spending tx has an output
Previously the code handling spending tx crashed if it doesn't have an output.
This is likely to occur only in tests.
2025-01-17 12:39:24 -03:00
Boris Nagaev
864d7f24cd
sweepbatcher: make sure feerate does not decrease
Previous behaviour was to overwrite batch's feerate with minFeeRate of
its primary sweep, which could be lower that previus batch's feerate or
lower that feerate of some other sweep.

Instead, batch's feerate only grows and never declines and is at least as high
as the highest feerate of its sweeps.

Added a test to verify this.
2024-10-15 11:28:40 -03:00
Slyghtning
3ee3963030
sweepbatcher: log confirmed batch tx id 2024-09-02 15:42:51 +02:00
Boris Nagaev
dba3d15b02
sweepbatcher: fix typo in log message 2024-09-01 13:00:36 -03:00
Boris Nagaev
7c93dff830
sweepbatcher: add option WithPublishErrorHandler
WithPublishErrorHandler sets the callback used to handle publish errors.
It can be used to filter out noisy messages.
2024-08-27 19:26:29 -03:00
Boris Nagaev
7780aca492
sweepbatcher: log the reason of acceptance failure
Add Info log message in cases when addSweep returns accept=false and err=nil.
2024-08-26 20:53:29 -03:00
Boris Nagaev
f21a21ee41
sweepbatcher: set max batch size to 1000 sweeps
This is needed to avoid non-standard batch transactions (larger than 400k wu).
A non-cooperative input is 393 wu, so 1000 inputs are still under 400k wu.
2024-08-26 14:17:24 -03:00
Boris Nagaev
26eda00ef2
sweepbatcher: add mixed batches option
Option WithMixedBatch instructs sweepbatcher to create mixed batches with regard
to cooperativeness. Such a batch can include both sweeps signed both
cooperatively and non-cooperatively. If cooperative signing fails for a sweep,
transaction is updated to sign that sweep non-cooperatively and another round of
cooperative signing runs on the remaining sweeps. The remaining sweeps are
signed in non-cooperative (more expensive) way. If the whole procedure fails for
whatever reason, the batch is signed non-cooperatively (the fallback).
2024-08-21 14:05:50 -03:00