Skip to content

Commit 6372ed8

Browse files
committed
cmd/loop: warn only for auto-selected low-conf deposits
Reproduce the server's static-address deposit selection order in the CLI using the already-returned deposit metadata. This keeps the low-confirmation warning focused on the deposits that auto-selection would actually choose. Add coverage for the confirmed-preferred and unconfirmed-needed cases.
1 parent eafef68 commit 6372ed8

2 files changed

Lines changed: 141 additions & 5 deletions

File tree

cmd/loop/staticaddr.go

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"sort"
78
"strings"
89

910
"github.com/lightninglabs/loop/labels"
1011
"github.com/lightninglabs/loop/looprpc"
1112
"github.com/lightninglabs/loop/staticaddr/loopin"
1213
"github.com/lightninglabs/loop/swapserverrpc"
1314
lndcommands "github.com/lightningnetwork/lnd/cmd/commands"
15+
"github.com/lightningnetwork/lnd/input"
1416
"github.com/lightningnetwork/lnd/lnrpc"
17+
"github.com/lightningnetwork/lnd/lnwallet"
1518
"github.com/lightningnetwork/lnd/routing/route"
1619
"github.com/urfave/cli/v3"
1720
)
@@ -620,11 +623,10 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error {
620623
// Warn the user if any selected deposits have fewer than 6
621624
// confirmations, as the swap payment won't be received immediately
622625
// for those.
623-
depositsToCheck := depositOutpoints
624-
if autoSelectDepositsForQuote {
625-
// When auto-selecting, any deposit could be chosen.
626-
depositsToCheck = depositsToOutpoints(allDeposits)
627-
}
626+
depositsToCheck := warningDepositOutpoints(
627+
allDeposits, depositOutpoints, autoSelectDepositsForQuote,
628+
quoteReq.Amt,
629+
)
628630
warning := lowConfDepositWarning(
629631
allDeposits, depositsToCheck,
630632
int64(summary.RelativeExpiryBlocks),
@@ -688,6 +690,83 @@ func depositsToOutpoints(deposits []*looprpc.Deposit) []string {
688690
return outpoints
689691
}
690692

693+
var warningSelectionDustLimit = int64(lnwallet.DustLimitForSize(input.P2TRSize))
694+
695+
func warningDepositOutpoints(allDeposits []*looprpc.Deposit,
696+
selectedOutpoints []string, autoSelect bool, targetAmount int64) []string {
697+
698+
if !autoSelect {
699+
return selectedOutpoints
700+
}
701+
702+
return autoSelectedWarningOutpoints(allDeposits, targetAmount)
703+
}
704+
705+
func autoSelectedWarningOutpoints(allDeposits []*looprpc.Deposit,
706+
targetAmount int64) []string {
707+
708+
if targetAmount <= 0 {
709+
return nil
710+
}
711+
712+
// KEEP IN SYNC with staticaddr/loopin.SelectDeposits.
713+
deposits := filterSwappableWarningDeposits(allDeposits)
714+
sort.Slice(deposits, func(i, j int) bool {
715+
iConfirmed := deposits[i].ConfirmationHeight > 0
716+
jConfirmed := deposits[j].ConfirmationHeight > 0
717+
if iConfirmed != jConfirmed {
718+
return iConfirmed
719+
}
720+
721+
if deposits[i].Value == deposits[j].Value {
722+
return deposits[i].BlocksUntilExpiry <
723+
deposits[j].BlocksUntilExpiry
724+
}
725+
726+
return deposits[i].Value > deposits[j].Value
727+
})
728+
729+
selectedOutpoints := make([]string, 0, len(deposits))
730+
var selectedAmount int64
731+
for _, deposit := range deposits {
732+
selectedOutpoints = append(selectedOutpoints, deposit.Outpoint)
733+
selectedAmount += deposit.Value
734+
if selectedAmount == targetAmount {
735+
return selectedOutpoints
736+
}
737+
738+
if selectedAmount > targetAmount &&
739+
selectedAmount-targetAmount >= warningSelectionDustLimit {
740+
741+
return selectedOutpoints
742+
}
743+
}
744+
745+
return nil
746+
}
747+
748+
func filterSwappableWarningDeposits(
749+
allDeposits []*looprpc.Deposit) []*looprpc.Deposit {
750+
751+
swappable := make([]*looprpc.Deposit, 0, len(allDeposits))
752+
minBlocksUntilExpiry := int64(
753+
loopin.DefaultLoopInOnChainCltvDelta + loopin.DepositHtlcDelta,
754+
)
755+
for _, deposit := range allDeposits {
756+
// Unconfirmed deposits remain swappable because their CSV timeout has
757+
// not started yet. This mirrors loopin.IsSwappable.
758+
if deposit.ConfirmationHeight > 0 &&
759+
deposit.BlocksUntilExpiry < minBlocksUntilExpiry {
760+
761+
continue
762+
}
763+
764+
swappable = append(swappable, deposit)
765+
}
766+
767+
return swappable
768+
}
769+
691770
// conservativeWarningConfs is the highest default confirmation tier used by
692771
// the server's dynamic confirmation-risk policy.
693772
//

cmd/loop/staticaddr_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,60 @@ func TestLowConfDepositWarningUnconfirmed(t *testing.T) {
5555
)
5656
require.NotContains(t, warning, "executed immediately")
5757
}
58+
59+
func TestWarningDepositOutpointsAutoSelectPrefersConfirmed(t *testing.T) {
60+
t.Parallel()
61+
62+
const csvExpiry = 1100
63+
64+
deposits := []*looprpc.Deposit{
65+
{
66+
Outpoint: "mempool-large",
67+
Value: 2_000_000,
68+
ConfirmationHeight: 0,
69+
BlocksUntilExpiry: csvExpiry,
70+
},
71+
{
72+
Outpoint: "confirmed",
73+
Value: 1_500_000,
74+
ConfirmationHeight: 100,
75+
BlocksUntilExpiry: csvExpiry - 5,
76+
},
77+
}
78+
79+
selected := warningDepositOutpoints(deposits, nil, true, 1_000_000)
80+
81+
require.Equal(t, []string{"confirmed"}, selected)
82+
require.Empty(t, lowConfDepositWarning(deposits, selected, csvExpiry))
83+
}
84+
85+
func TestWarningDepositOutpointsAutoSelectIncludesNeededUnconfirmed(t *testing.T) {
86+
t.Parallel()
87+
88+
const csvExpiry = 1100
89+
90+
deposits := []*looprpc.Deposit{
91+
{
92+
Outpoint: "confirmed-small",
93+
Value: 500_000,
94+
ConfirmationHeight: 100,
95+
BlocksUntilExpiry: csvExpiry - 5,
96+
},
97+
{
98+
Outpoint: "mempool-large",
99+
Value: 2_000_000,
100+
ConfirmationHeight: 0,
101+
BlocksUntilExpiry: csvExpiry,
102+
},
103+
}
104+
105+
selected := warningDepositOutpoints(deposits, nil, true, 1_000_000)
106+
107+
require.Equal(
108+
t, []string{"confirmed-small", "mempool-large"}, selected,
109+
)
110+
111+
warning := lowConfDepositWarning(deposits, selected, csvExpiry)
112+
require.Contains(t, warning, "mempool-large (unconfirmed)")
113+
require.NotContains(t, warning, "confirmed-small")
114+
}

0 commit comments

Comments
 (0)