Skip to content

Commit 9231a59

Browse files
committed
staticaddr: fractional loop-in amount
1 parent 6e93e65 commit 9231a59

File tree

6 files changed

+208
-36
lines changed

6 files changed

+208
-36
lines changed

interface.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,11 @@ type StaticAddressLoopInRequest struct {
338338
// swap payment. If the timeout is reached the swap will be aborted and
339339
// the client can retry the swap if desired with different parameters.
340340
PaymentTimeoutSeconds uint32
341+
342+
// SelectedAmount is the amount that the user selected for the swap. If
343+
// the user did not select an amount, the amount of the selected
344+
// deposits is used.
345+
SelectedAmount btcutil.Amount
341346
}
342347

343348
// LoopInTerms are the server terms on which it executes loop in swaps.

loopd/swapclient_server.go

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -893,21 +893,26 @@ 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+
numDeposits = len(req.DepositOutpoints)
899+
err error
898900
)
899901

900902
htlcConfTarget, err := validateLoopInRequest(
901-
req.ConfTarget, req.ExternalHtlc, numDeposits, req.Amt,
903+
req.ConfTarget, req.ExternalHtlc, uint32(numDeposits),
904+
int64(selectedAmount),
902905
)
903906
if err != nil {
904907
return nil, err
905908
}
906909

907910
// Retrieve deposits to calculate their total value.
908911
var depositList *looprpc.ListStaticAddressDepositsResponse
909-
amount := btcutil.Amount(req.Amt)
910-
if len(req.DepositOutpoints) > 0 {
912+
913+
// If deposits are selected, we need to retrieve them to calculate the
914+
// total value which we request a quote for.
915+
if numDeposits > 0 {
911916
depositList, err = s.ListStaticAddressDeposits(
912917
ctx, &looprpc.ListStaticAddressDepositsRequest{
913918
Outpoints: req.DepositOutpoints,
@@ -922,20 +927,27 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
922927
"deposit outpoints")
923928
}
924929

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")
930+
if numDeposits != len(depositList.FilteredDeposits) {
931+
return nil, fmt.Errorf("expected %d deposits, got %d",
932+
numDeposits, len(depositList.FilteredDeposits))
930933
}
931934

932935
// 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-
}
936+
// selected value and the number of deposits. This is so the
937+
// server can probe the selected value and calculate the per
938+
// input fee.
939+
for _, deposit := range depositList.FilteredDeposits {
940+
totalDepositAmount += btcutil.Amount(
941+
deposit.Value,
942+
)
943+
}
944+
945+
selectedAmount, err = s.staticLoopInManager.SwapAmountFromSelectedAmount(
946+
selectedAmount, totalDepositAmount,
947+
)
948+
if err != nil {
949+
return nil, fmt.Errorf("error calculating swap "+
950+
"amount from selected amount: %v", err)
939951
}
940952
}
941953

@@ -962,14 +974,14 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
962974
}
963975

964976
quote, err := s.impl.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
965-
Amount: amount,
977+
Amount: selectedAmount,
966978
HtlcConfTarget: htlcConfTarget,
967979
ExternalHtlc: req.ExternalHtlc,
968980
LastHop: lastHop,
969981
RouteHints: routeHints,
970982
Private: req.Private,
971983
Initiator: defaultLoopdInitiator,
972-
NumDeposits: numDeposits,
984+
NumDeposits: uint32(numDeposits),
973985
})
974986
if err != nil {
975987
return nil, err
@@ -1868,6 +1880,7 @@ func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context,
18681880
}
18691881

18701882
req := &loop.StaticAddressLoopInRequest{
1883+
SelectedAmount: btcutil.Amount(in.Amount),
18711884
DepositOutpoints: in.Outpoints,
18721885
MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis),
18731886
Label: in.Label,

staticaddr/loopin/actions.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,14 @@ func (f *FSM) InitHtlcAction(ctx context.Context,
6868
}
6969

7070
// Calculate the swap invoice amount. The server needs to pay us the
71-
// sum of all deposits minus the fees that the server charges for the
72-
// swap.
73-
swapInvoiceAmt := f.loopIn.TotalDepositAmount() - f.loopIn.QuotedSwapFee
71+
// swap amount minus the fees that the server charges for the swap. The
72+
// swap amount is either the total value of the selected deposits, or
73+
// the selected amount if a specific amount was requested.
74+
swapAmount := f.loopIn.TotalDepositAmount()
75+
if f.loopIn.SelectedAmount > 0 {
76+
swapAmount = f.loopIn.SelectedAmount
77+
}
78+
swapInvoiceAmt := swapAmount - f.loopIn.QuotedSwapFee
7479

7580
// Generate random preimage.
7681
var swapPreimage lntypes.Preimage
@@ -120,6 +125,7 @@ func (f *FSM) InitHtlcAction(ctx context.Context,
120125
loopInReq := &swapserverrpc.ServerStaticAddressLoopInRequest{
121126
SwapHash: f.loopIn.SwapHash[:],
122127
DepositOutpoints: f.loopIn.DepositOutpoints,
128+
Amount: uint64(f.loopIn.SelectedAmount),
123129
HtlcClientPubKey: f.loopIn.ClientPubkey.SerializeCompressed(),
124130
SwapInvoice: f.loopIn.SwapInvoice,
125131
ProtocolVersion: version.CurrentRPCProtocolVersion(),
@@ -204,7 +210,7 @@ func (f *FSM) InitHtlcAction(ctx context.Context,
204210
// We need to defend against the server setting high fees for the htlc
205211
// tx since we might have to sweep the timeout path. We maximally allow
206212
// a configured percentage of the swap value to be spent on fees.
207-
amt := float64(f.loopIn.TotalDepositAmount())
213+
amt := float64(swapAmount)
208214
maxHtlcTxFee := btcutil.Amount(amt *
209215
f.cfg.MaxStaticAddrHtlcFeePercentage)
210216

staticaddr/loopin/loopin.go

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package loopin
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67
"fmt"
@@ -93,6 +94,11 @@ type StaticAddressLoopIn struct {
9394
// swap.
9495
DepositOutpoints []string
9596

97+
// SelectedAmount is the amount that the user selected for the swap. If
98+
// the user did not select an amount, the amount of all deposits is
99+
// used.
100+
SelectedAmount btcutil.Amount
101+
96102
// state is the current state of the swap.
97103
state fsm.StateType
98104

@@ -287,10 +293,20 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params,
287293
weight := l.htlcWeight()
288294
fee := feeRate.FeeForWeight(weight)
289295

290-
// Check if the server breaches our fee limits.
291-
amt := float64(l.TotalDepositAmount())
292-
feeLimit := btcutil.Amount(amt * maxFeePercentage)
296+
// Determine the swap amount. If the user selected a specific amount, we
297+
// use that and use the difference to the total deposit amount as the
298+
// change.
299+
var (
300+
swapAmt = l.TotalDepositAmount()
301+
changeAmount btcutil.Amount
302+
)
303+
if l.SelectedAmount > 0 {
304+
swapAmt = l.SelectedAmount
305+
changeAmount = l.TotalDepositAmount() - l.SelectedAmount
306+
}
293307

308+
// Check if the server breaches our fee limits.
309+
feeLimit := btcutil.Amount(float64(swapAmt) * maxFeePercentage)
294310
if fee > feeLimit {
295311
return nil, fmt.Errorf("htlc tx fee %v exceeds max fee %v",
296312
fee, feeLimit)
@@ -308,12 +324,20 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params,
308324

309325
// Create the sweep output
310326
sweepOutput := &wire.TxOut{
311-
Value: int64(l.TotalDepositAmount()) - int64(fee),
327+
Value: int64(swapAmt - fee),
312328
PkScript: pkscript,
313329
}
314330

315331
msgTx.AddTxOut(sweepOutput)
316332

333+
// We expect change to be sent back to our static address output script.
334+
if changeAmount > 0 {
335+
msgTx.AddTxOut(&wire.TxOut{
336+
Value: int64(changeAmount),
337+
PkScript: l.AddressParams.PkScript,
338+
})
339+
}
340+
317341
return msgTx, nil
318342
}
319343

@@ -373,11 +397,25 @@ func (l *StaticAddressLoopIn) createHtlcSweepTx(ctx context.Context,
373397
return nil, err
374398
}
375399

400+
// Check if the htlc tx has a change output. If so we need to select the
401+
// non-change output index to construct the sweep with.
402+
htlcInputIndex := uint32(0)
403+
if len(htlcTx.TxOut) == 2 {
404+
// If the first htlc tx output matches our static address
405+
// script we need to select the second output to sweep from.
406+
if bytes.Equal(
407+
htlcTx.TxOut[0].PkScript, l.AddressParams.PkScript,
408+
) {
409+
410+
htlcInputIndex = 1
411+
}
412+
}
413+
376414
// Add the htlc input.
377415
sweepTx.AddTxIn(&wire.TxIn{
378416
PreviousOutPoint: wire.OutPoint{
379417
Hash: htlcTx.TxHash(),
380-
Index: 0,
418+
Index: htlcInputIndex,
381419
},
382420
SignatureScript: htlc.SigScript,
383421
Sequence: htlc.SuccessSequence(),

0 commit comments

Comments
 (0)