Skip to content

Commit c6dc765

Browse files
committed
loopin: handle unconfirmed deposits in swap selection
Unconfirmed deposits (ConfirmationHeight == 0) are considered swappable because their CSV timeout has not started yet. Extract blocksUntilDepositExpiry helper that returns MaxUint32 for unconfirmed deposits and use it in SelectDeposits sorting and IsSwappable.
1 parent d0963ba commit c6dc765

3 files changed

Lines changed: 40 additions & 12 deletions

File tree

staticaddr/deposit/manager_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ func TestManager(t *testing.T) {
234234
runErrChan <- testContext.manager.Run(ctx, initChan)
235235
}()
236236

237+
// Send an initial block so the manager can proceed past its startup
238+
// block wait.
239+
testContext.blockChan <- int32(defaultDepositConfirmations)
240+
237241
// Ensure that the manager has been initialized.
238242
select {
239243
case <-initChan:

staticaddr/loopin/manager.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7+
"math"
78
"slices"
89
"sort"
910
"sync/atomic"
@@ -879,10 +880,14 @@ func SelectDeposits(targetAmount btcutil.Amount,
879880
// blocks-until-expiry in ascending order.
880881
sort.Slice(deposits, func(i, j int) bool {
881882
if deposits[i].Value == deposits[j].Value {
882-
iExp := uint32(deposits[i].ConfirmationHeight) +
883-
csvExpiry - blockHeight
884-
jExp := uint32(deposits[j].ConfirmationHeight) +
885-
csvExpiry - blockHeight
883+
iExp := blocksUntilDepositExpiry(
884+
uint32(deposits[i].ConfirmationHeight),
885+
blockHeight, csvExpiry,
886+
)
887+
jExp := blocksUntilDepositExpiry(
888+
uint32(deposits[j].ConfirmationHeight),
889+
blockHeight, csvExpiry,
890+
)
886891

887892
return iExp < jExp
888893
}
@@ -914,20 +919,33 @@ func SelectDeposits(targetAmount btcutil.Amount,
914919
// IsSwappable checks if a deposit is swappable. It returns true if the deposit
915920
// is not expired and the htlc is not too close to expiry.
916921
func IsSwappable(confirmationHeight, blockHeight, csvExpiry uint32) bool {
922+
if confirmationHeight == 0 {
923+
return true
924+
}
925+
917926
// The deposit expiry height is the confirmation height plus the csv
918927
// expiry.
919-
depositExpiryHeight := confirmationHeight + csvExpiry
928+
return blocksUntilDepositExpiry(
929+
confirmationHeight, blockHeight, csvExpiry,
930+
) >= DefaultLoopInOnChainCltvDelta+DepositHtlcDelta
931+
}
932+
933+
// blocksUntilDepositExpiry returns the remaining number of blocks until a
934+
// deposit expires. Unconfirmed deposits return MaxUint32 because their CSV has
935+
// not started yet.
936+
func blocksUntilDepositExpiry(confirmationHeight, blockHeight,
937+
csvExpiry uint32) uint32 {
920938

921-
// The htlc expiry height is the current height plus the htlc
922-
// cltv delta.
923-
htlcExpiryHeight := blockHeight + DefaultLoopInOnChainCltvDelta
939+
if confirmationHeight == 0 {
940+
return math.MaxUint32
941+
}
924942

925-
// Ensure that the deposit doesn't expire before the htlc.
926-
if depositExpiryHeight < htlcExpiryHeight+DepositHtlcDelta {
927-
return false
943+
depositExpiryHeight := confirmationHeight + csvExpiry
944+
if depositExpiryHeight <= blockHeight {
945+
return 0
928946
}
929947

930-
return true
948+
return depositExpiryHeight - blockHeight
931949
}
932950

933951
// DeduceSwapAmount calculates the swap amount based on the selected amount and

staticaddr/loopin/manager_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ func TestSelectDeposits(t *testing.T) {
176176
}
177177
}
178178

179+
// TestIsSwappableUnconfirmed checks that an unconfirmed deposit is considered
180+
// swappable because its CSV timeout has not started yet.
181+
func TestIsSwappableUnconfirmed(t *testing.T) {
182+
require.True(t, IsSwappable(0, 5000, 1000))
183+
}
184+
179185
// mockDepositManager implements DepositManager for tests.
180186
type mockDepositManager struct {
181187
byOutpoint map[string]*deposit.Deposit

0 commit comments

Comments
 (0)