mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
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.
This commit is contained in:
parent
581761a12f
commit
95a456791b
2 changed files with 130 additions and 35 deletions
|
|
@ -893,22 +893,62 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
|
|||
infof("Loop in quote request received")
|
||||
|
||||
var (
|
||||
numDeposits = uint32(len(req.DepositOutpoints))
|
||||
err error
|
||||
selectedAmount = btcutil.Amount(req.Amt)
|
||||
totalDepositAmount btcutil.Amount
|
||||
autoSelectDeposits = req.AutoSelectDeposits
|
||||
err error
|
||||
)
|
||||
|
||||
htlcConfTarget, err := validateLoopInRequest(
|
||||
req.ConfTarget, req.ExternalHtlc, numDeposits, req.Amt,
|
||||
req.ConfTarget, req.ExternalHtlc,
|
||||
uint32(len(req.DepositOutpoints)), selectedAmount,
|
||||
autoSelectDeposits,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Retrieve deposits to calculate their total value.
|
||||
var depositList *looprpc.ListStaticAddressDepositsResponse
|
||||
amount := btcutil.Amount(req.Amt)
|
||||
if len(req.DepositOutpoints) > 0 {
|
||||
depositList, err = s.ListStaticAddressDeposits(
|
||||
// If deposits should be automatically selected, we do so and count the
|
||||
// number of deposits to quote for.
|
||||
numDeposits := 0
|
||||
if autoSelectDeposits {
|
||||
deposits, err := s.depositManager.GetActiveDepositsInState(
|
||||
deposit.Deposited,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve all "+
|
||||
"deposits: %w", err)
|
||||
}
|
||||
|
||||
// TODO(hieblmi): add params to deposit for multi-address
|
||||
// support.
|
||||
params, err := s.staticAddressManager.GetStaticAddressParameters(
|
||||
ctx,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve static "+
|
||||
"address parameters: %w", err)
|
||||
}
|
||||
|
||||
info, err := s.lnd.Client.GetInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get lnd info: %w",
|
||||
err)
|
||||
}
|
||||
selectedDeposits, err := loopin.SelectDeposits(
|
||||
selectedAmount, deposits, params.Expiry,
|
||||
info.BlockHeight,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to select deposits: %w",
|
||||
err)
|
||||
}
|
||||
|
||||
numDeposits = len(selectedDeposits)
|
||||
} else if len(req.DepositOutpoints) > 0 {
|
||||
// If deposits are selected, we need to retrieve them to
|
||||
// calculate the total value which we request a quote for.
|
||||
depositList, err := s.ListStaticAddressDeposits(
|
||||
ctx, &looprpc.ListStaticAddressDepositsRequest{
|
||||
Outpoints: req.DepositOutpoints,
|
||||
},
|
||||
|
|
@ -922,20 +962,35 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
|
|||
"deposit outpoints")
|
||||
}
|
||||
|
||||
// The requested amount should be 0 here if the request
|
||||
// contained deposit outpoints.
|
||||
if amount != 0 && len(depositList.FilteredDeposits) > 0 {
|
||||
return nil, fmt.Errorf("amount should be 0 for " +
|
||||
"deposit quotes")
|
||||
if len(req.DepositOutpoints) !=
|
||||
len(depositList.FilteredDeposits) {
|
||||
|
||||
return nil, fmt.Errorf("expected %d deposits, got %d",
|
||||
len(req.DepositOutpoints),
|
||||
len(depositList.FilteredDeposits))
|
||||
} else {
|
||||
numDeposits = len(depositList.FilteredDeposits)
|
||||
}
|
||||
|
||||
// In case we quote for deposits we send the server both the
|
||||
// total value and the number of deposits. This is so the server
|
||||
// can probe the total amount and calculate the per input fee.
|
||||
if amount == 0 && len(depositList.FilteredDeposits) > 0 {
|
||||
for _, deposit := range depositList.FilteredDeposits {
|
||||
amount += btcutil.Amount(deposit.Value)
|
||||
}
|
||||
// In case we quote for deposits, we send the server both the
|
||||
// selected value and the number of deposits. This is so the
|
||||
// server can probe the selected value and calculate the per
|
||||
// input fee.
|
||||
for _, deposit := range depositList.FilteredDeposits {
|
||||
totalDepositAmount += btcutil.Amount(
|
||||
deposit.Value,
|
||||
)
|
||||
}
|
||||
|
||||
// If a fractional amount is also selected, we check if it
|
||||
// leads to a dust change output.
|
||||
selectedAmount, err = loopin.DeduceSwapAmount(
|
||||
totalDepositAmount, selectedAmount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error calculating "+
|
||||
"swap amount from selected amount: %v",
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -962,14 +1017,14 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
|
|||
}
|
||||
|
||||
quote, err := s.impl.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
|
||||
Amount: amount,
|
||||
Amount: selectedAmount,
|
||||
HtlcConfTarget: htlcConfTarget,
|
||||
ExternalHtlc: req.ExternalHtlc,
|
||||
LastHop: lastHop,
|
||||
RouteHints: routeHints,
|
||||
Private: req.Private,
|
||||
Initiator: defaultLoopdInitiator,
|
||||
NumDeposits: numDeposits,
|
||||
NumDeposits: uint32(numDeposits),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1065,8 +1120,11 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
|
|||
|
||||
infof("Loop in request received")
|
||||
|
||||
selectDeposits := false
|
||||
numDeposits := uint32(0)
|
||||
htlcConfTarget, err := validateLoopInRequest(
|
||||
in.HtlcConfTarget, in.ExternalHtlc, 0, in.Amt,
|
||||
in.HtlcConfTarget, in.ExternalHtlc, numDeposits,
|
||||
btcutil.Amount(in.Amt), selectDeposits,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1980,6 +2038,7 @@ func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context,
|
|||
}
|
||||
|
||||
req := &loop.StaticAddressLoopInRequest{
|
||||
SelectedAmount: btcutil.Amount(in.Amount),
|
||||
DepositOutpoints: in.Outpoints,
|
||||
MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis),
|
||||
Label: in.Label,
|
||||
|
|
@ -2282,12 +2341,24 @@ func validateConfTarget(target, defaultTarget int32) (int32, error) {
|
|||
}
|
||||
|
||||
// validateLoopInRequest fails if the mutually exclusive conf target and
|
||||
// external parameters are both set.
|
||||
// external parameters are both set. It returns the confirmation target of the
|
||||
// legacy loop-in.
|
||||
func validateLoopInRequest(htlcConfTarget int32, external bool,
|
||||
numDeposits uint32, amount int64) (int32, error) {
|
||||
numDeposits uint32, amount btcutil.Amount,
|
||||
autoSelectDeposits bool) (int32, error) {
|
||||
|
||||
if amount < 0 {
|
||||
return 0, errors.New("amount cannot be negative")
|
||||
}
|
||||
|
||||
if amount == 0 && numDeposits == 0 {
|
||||
return 0, errors.New("either amount or deposits must be set")
|
||||
return 0, errors.New("either amount, or deposits or both " +
|
||||
"must be set")
|
||||
}
|
||||
|
||||
if autoSelectDeposits && numDeposits > 0 {
|
||||
return 0, errors.New("cannot auto-select deposits while " +
|
||||
"providing deposits at the same time")
|
||||
}
|
||||
|
||||
// If the htlc is going to be externally set, the htlcConfTarget should
|
||||
|
|
@ -2305,7 +2376,7 @@ func validateLoopInRequest(htlcConfTarget int32, external bool,
|
|||
|
||||
// If the loop in uses static address deposits, we do not need to set a
|
||||
// confirmation target since the HTLC won't be published by the client.
|
||||
if numDeposits > 0 {
|
||||
if numDeposits > 0 || autoSelectDeposits {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,13 +153,14 @@ func TestValidateConfTarget(t *testing.T) {
|
|||
// TestValidateLoopInRequest tests validation of loop in requests.
|
||||
func TestValidateLoopInRequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
amount int64
|
||||
numDeposits uint32
|
||||
external bool
|
||||
confTarget int32
|
||||
expectErr bool
|
||||
expectedTarget int32
|
||||
name string
|
||||
amount int64
|
||||
numDeposits uint32
|
||||
external bool
|
||||
confTarget int32
|
||||
autoSelectDeposits bool
|
||||
expectErr bool
|
||||
expectedTarget int32
|
||||
}{
|
||||
{
|
||||
name: "external and htlc conf set",
|
||||
|
|
@ -216,6 +217,28 @@ func TestValidateLoopInRequest(t *testing.T) {
|
|||
external: false,
|
||||
expectErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "not external, deposit fractional amount",
|
||||
amount: 100_000,
|
||||
numDeposits: 1,
|
||||
external: false,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "amount with deposit coin select",
|
||||
amount: 100_000,
|
||||
autoSelectDeposits: true,
|
||||
external: false,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "amount with deposit coin select",
|
||||
numDeposits: 1,
|
||||
autoSelectDeposits: true,
|
||||
external: false,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
@ -223,7 +246,8 @@ func TestValidateLoopInRequest(t *testing.T) {
|
|||
external := test.external
|
||||
conf, err := validateLoopInRequest(
|
||||
test.confTarget, external, test.numDeposits,
|
||||
test.amount,
|
||||
btcutil.Amount(test.amount),
|
||||
test.autoSelectDeposits,
|
||||
)
|
||||
|
||||
if test.expectErr {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue