Skip to content

Commit 8acd7ce

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 0769945 commit 8acd7ce

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
@@ -1868,6 +1925,7 @@ func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context,
18681925
}
18691926

18701927
req := &loop.StaticAddressLoopInRequest{
1928+
SelectedAmount: btcutil.Amount(in.Amount),
18711929
DepositOutpoints: in.Outpoints,
18721930
MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis),
18731931
Label: in.Label,
@@ -2166,10 +2224,17 @@ func validateConfTarget(target, defaultTarget int32) (int32, error) {
21662224
// validateLoopInRequest fails if the mutually exclusive conf target and
21672225
// external parameters are both set.
21682226
func validateLoopInRequest(htlcConfTarget int32, external bool,
2169-
numDeposits uint32, amount int64) (int32, error) {
2227+
numDeposits uint32, amount int64, autoSelectDeposits bool) (int32,
2228+
error) {
21702229

21712230
if amount == 0 && numDeposits == 0 {
2172-
return 0, errors.New("either amount or deposits must be set")
2231+
return 0, errors.New("either amount, or deposits or both " +
2232+
"must be set")
2233+
}
2234+
2235+
if autoSelectDeposits && numDeposits > 0 {
2236+
return 0, errors.New("cannot auto-select deposits while " +
2237+
"providing deposits at the same time")
21732238
}
21742239

21752240
// If the htlc is going to be externally set, the htlcConfTarget should
@@ -2187,7 +2252,7 @@ func validateLoopInRequest(htlcConfTarget int32, external bool,
21872252

21882253
// If the loop in uses static address deposits, we do not need to set a
21892254
// confirmation target since the HTLC won't be published by the client.
2190-
if numDeposits > 0 {
2255+
if numDeposits > 0 || autoSelectDeposits {
21912256
return 0, nil
21922257
}
21932258

loopd/swapclient_server_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ func TestValidateLoopInRequest(t *testing.T) {
152152
numDeposits uint32
153153
external bool
154154
confTarget int32
155+
depositSelect bool
155156
expectErr bool
156157
expectedTarget int32
157158
}{
@@ -210,14 +211,36 @@ func TestValidateLoopInRequest(t *testing.T) {
210211
external: false,
211212
expectErr: false,
212213
},
214+
215+
{
216+
name: "not external, deposit fractional amount",
217+
amount: 100_000,
218+
numDeposits: 1,
219+
external: false,
220+
expectErr: false,
221+
},
222+
{
223+
name: "amount with deposit coin select",
224+
amount: 100_000,
225+
depositSelect: true,
226+
external: false,
227+
expectErr: false,
228+
},
229+
{
230+
name: "amount with deposit coin select",
231+
numDeposits: 1,
232+
depositSelect: true,
233+
external: false,
234+
expectErr: true,
235+
},
213236
}
214237

215238
for _, test := range tests {
216239
t.Run(test.name, func(t *testing.T) {
217240
external := test.external
218241
conf, err := validateLoopInRequest(
219242
test.confTarget, external, test.numDeposits,
220-
test.amount,
243+
test.amount, test.depositSelect,
221244
)
222245

223246
if test.expectErr {

0 commit comments

Comments
 (0)