Skip to content

Commit f94db43

Browse files
committed
loopd: deposit selection for GetLoopInQuote
If a quote request contains an amount and flag SelectDeposits set to true the quoting coin- selects the required deposits to meet the swap amount in order to quote for the number of deposits.
1 parent be871bc commit f94db43

File tree

2 files changed

+114
-26
lines changed

2 files changed

+114
-26
lines changed

loopd/swapclient_server.go

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -893,22 +893,62 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
893893
infof("Loop in quote request received")
894894

895895
var (
896-
numDeposits = uint32(len(req.DepositOutpoints))
897-
err error
896+
selectedAmount = btcutil.Amount(req.Amt)
897+
totalDepositAmount btcutil.Amount
898+
autoSelectDeposits = req.SelectDeposits
899+
err error
898900
)
899901

900902
htlcConfTarget, err := validateLoopInRequest(
901-
req.ConfTarget, req.ExternalHtlc, numDeposits, req.Amt,
903+
req.ConfTarget, req.ExternalHtlc,
904+
uint32(len(req.DepositOutpoints)), int64(selectedAmount),
905+
autoSelectDeposits,
902906
)
903907
if err != nil {
904908
return nil, err
905909
}
906910

907-
// Retrieve deposits to calculate their total value.
908-
var depositList *looprpc.ListStaticAddressDepositsResponse
909-
amount := btcutil.Amount(req.Amt)
910-
if len(req.DepositOutpoints) > 0 {
911-
depositList, err = s.ListStaticAddressDeposits(
911+
// If deposits should be automatically selected we do so and count the
912+
// number of deposits to quote for.
913+
numDeposits := 0
914+
if autoSelectDeposits {
915+
deposits, err := s.depositManager.GetActiveDepositsInState(
916+
deposit.Deposited,
917+
)
918+
if err != nil {
919+
return nil, fmt.Errorf("unable to retrieve all "+
920+
"deposits: %w", err)
921+
}
922+
923+
// TODO(hieblmi): add params to deposit for multi-address
924+
// support.
925+
params, err := s.staticAddressManager.GetStaticAddressParameters(
926+
ctx,
927+
)
928+
if err != nil {
929+
return nil, fmt.Errorf("unable to retrieve static "+
930+
"address parameters: %w", err)
931+
}
932+
933+
info, err := s.lnd.Client.GetInfo(ctx)
934+
if err != nil {
935+
return nil, fmt.Errorf("unable to get lnd info: %w",
936+
err)
937+
}
938+
selectedDeposits, err := loopin.SelectDeposits(
939+
selectedAmount, deposits, params.Expiry,
940+
info.BlockHeight,
941+
)
942+
if err != nil {
943+
return nil, fmt.Errorf("unable to select deposits: %w",
944+
err)
945+
}
946+
947+
numDeposits = len(selectedDeposits)
948+
} else if len(req.DepositOutpoints) > 0 {
949+
// If deposits are selected, we need to retrieve them to
950+
// calculate the total value which we request a quote for.
951+
depositList, err := s.ListStaticAddressDeposits(
912952
ctx, &looprpc.ListStaticAddressDepositsRequest{
913953
Outpoints: req.DepositOutpoints,
914954
},
@@ -922,20 +962,34 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
922962
"deposit outpoints")
923963
}
924964

925-
// The requested amount should be 0 here if the request
926-
// contained deposit outpoints.
927-
if amount != 0 && len(depositList.FilteredDeposits) > 0 {
928-
return nil, fmt.Errorf("amount should be 0 for " +
929-
"deposit quotes")
965+
if len(req.DepositOutpoints) !=
966+
len(depositList.FilteredDeposits) {
967+
968+
return nil, fmt.Errorf("expected %d deposits, got %d",
969+
numDeposits, len(depositList.FilteredDeposits))
970+
} else {
971+
numDeposits = len(depositList.FilteredDeposits)
930972
}
931973

932974
// In case we quote for deposits we send the server both the
933-
// total value and the number of deposits. This is so the server
934-
// can probe the total amount and calculate the per input fee.
935-
if amount == 0 && len(depositList.FilteredDeposits) > 0 {
936-
for _, deposit := range depositList.FilteredDeposits {
937-
amount += btcutil.Amount(deposit.Value)
938-
}
975+
// selected value and the number of deposits. This is so the
976+
// server can probe the selected value and calculate the per
977+
// input fee.
978+
for _, deposit := range depositList.FilteredDeposits {
979+
totalDepositAmount += btcutil.Amount(
980+
deposit.Value,
981+
)
982+
}
983+
984+
// If a fractional amount is also selected, we check if it would
985+
// lead to a dust change output.
986+
selectedAmount, err = loopin.SwapAmountFromSelectedAmount(
987+
totalDepositAmount, selectedAmount,
988+
)
989+
if err != nil {
990+
return nil, fmt.Errorf("error calculating "+
991+
"swap amount from selected amount: %v",
992+
err)
939993
}
940994
}
941995

@@ -962,14 +1016,14 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
9621016
}
9631017

9641018
quote, err := s.impl.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
965-
Amount: amount,
1019+
Amount: selectedAmount,
9661020
HtlcConfTarget: htlcConfTarget,
9671021
ExternalHtlc: req.ExternalHtlc,
9681022
LastHop: lastHop,
9691023
RouteHints: routeHints,
9701024
Private: req.Private,
9711025
Initiator: defaultLoopdInitiator,
972-
NumDeposits: numDeposits,
1026+
NumDeposits: uint32(numDeposits),
9731027
})
9741028
if err != nil {
9751029
return nil, err
@@ -1065,8 +1119,11 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
10651119

10661120
infof("Loop in request received")
10671121

1122+
selectDeposits := false
1123+
numDeposits := uint32(0)
10681124
htlcConfTarget, err := validateLoopInRequest(
1069-
in.HtlcConfTarget, in.ExternalHtlc, 0, in.Amt,
1125+
in.HtlcConfTarget, in.ExternalHtlc, numDeposits, in.Amt,
1126+
selectDeposits,
10701127
)
10711128
if err != nil {
10721129
return nil, err
@@ -1980,6 +2037,7 @@ func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context,
19802037
}
19812038

19822039
req := &loop.StaticAddressLoopInRequest{
2040+
SelectedAmount: btcutil.Amount(in.Amount),
19832041
DepositOutpoints: in.Outpoints,
19842042
MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis),
19852043
Label: in.Label,
@@ -2284,10 +2342,17 @@ func validateConfTarget(target, defaultTarget int32) (int32, error) {
22842342
// validateLoopInRequest fails if the mutually exclusive conf target and
22852343
// external parameters are both set.
22862344
func validateLoopInRequest(htlcConfTarget int32, external bool,
2287-
numDeposits uint32, amount int64) (int32, error) {
2345+
numDeposits uint32, amount int64, autoSelectDeposits bool) (int32,
2346+
error) {
22882347

22892348
if amount == 0 && numDeposits == 0 {
2290-
return 0, errors.New("either amount or deposits must be set")
2349+
return 0, errors.New("either amount, or deposits or both " +
2350+
"must be set")
2351+
}
2352+
2353+
if autoSelectDeposits && numDeposits > 0 {
2354+
return 0, errors.New("cannot auto-select deposits while " +
2355+
"providing deposits at the same time")
22912356
}
22922357

22932358
// If the htlc is going to be externally set, the htlcConfTarget should
@@ -2305,7 +2370,7 @@ func validateLoopInRequest(htlcConfTarget int32, external bool,
23052370

23062371
// If the loop in uses static address deposits, we do not need to set a
23072372
// confirmation target since the HTLC won't be published by the client.
2308-
if numDeposits > 0 {
2373+
if numDeposits > 0 || autoSelectDeposits {
23092374
return 0, nil
23102375
}
23112376

loopd/swapclient_server_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ func TestValidateLoopInRequest(t *testing.T) {
158158
numDeposits uint32
159159
external bool
160160
confTarget int32
161+
depositSelect bool
161162
expectErr bool
162163
expectedTarget int32
163164
}{
@@ -216,14 +217,36 @@ func TestValidateLoopInRequest(t *testing.T) {
216217
external: false,
217218
expectErr: false,
218219
},
220+
221+
{
222+
name: "not external, deposit fractional amount",
223+
amount: 100_000,
224+
numDeposits: 1,
225+
external: false,
226+
expectErr: false,
227+
},
228+
{
229+
name: "amount with deposit coin select",
230+
amount: 100_000,
231+
depositSelect: true,
232+
external: false,
233+
expectErr: false,
234+
},
235+
{
236+
name: "amount with deposit coin select",
237+
numDeposits: 1,
238+
depositSelect: true,
239+
external: false,
240+
expectErr: true,
241+
},
219242
}
220243

221244
for _, test := range tests {
222245
t.Run(test.name, func(t *testing.T) {
223246
external := test.external
224247
conf, err := validateLoopInRequest(
225248
test.confTarget, external, test.numDeposits,
226-
test.amount,
249+
test.amount, test.depositSelect,
227250
)
228251

229252
if test.expectErr {

0 commit comments

Comments
 (0)