Skip to content

Commit a052f8a

Browse files
committed
staticaddr: fractional loop-in amount
1 parent 51bac8f commit a052f8a

File tree

5 files changed

+64
-9
lines changed

5 files changed

+64
-9
lines changed

interface.go

+2
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ 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 btcutil.Amount
341343
}
342344

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

staticaddr/loopin/actions.go

+8-3
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

staticaddr/loopin/loopin.go

+38-4
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,8 @@ type StaticAddressLoopIn struct {
9394
// swap.
9495
DepositOutpoints []string
9596

97+
SelectedAmount btcutil.Amount
98+
9699
// state is the current state of the swap.
97100
state fsm.StateType
98101

@@ -287,10 +290,20 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params,
287290
weight := l.htlcWeight()
288291
fee := feeRate.FeeForWeight(weight)
289292

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

305+
// Check if the server breaches our fee limits.
306+
feeLimit := btcutil.Amount(swapAmt * maxFeePercentage)
294307
if fee > feeLimit {
295308
return nil, fmt.Errorf("htlc tx fee %v exceeds max fee %v",
296309
fee, feeLimit)
@@ -314,6 +327,14 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params,
314327

315328
msgTx.AddTxOut(sweepOutput)
316329

330+
// We expect change to be sent back to our static address output script.
331+
if changeAmount > 0 {
332+
msgTx.AddTxOut(&wire.TxOut{
333+
Value: int64(changeAmount),
334+
PkScript: l.AddressParams.PkScript,
335+
})
336+
}
337+
317338
return msgTx, nil
318339
}
319340

@@ -373,11 +394,24 @@ func (l *StaticAddressLoopIn) createHtlcSweepTx(ctx context.Context,
373394
return nil, err
374395
}
375396

397+
// Check if the htlc tx has a change output. If so we need to select the
398+
// non-change output index to construct the sweep with.
399+
htlcInputIndex := uint32(0)
400+
if len(htlcTx.TxOut) == 2 {
401+
// If the first htlc tx output matches our static address
402+
// script we need to select the second output to sweep from.
403+
if bytes.Equal(
404+
htlcTx.TxOut[0].PkScript, l.AddressParams.PkScript,
405+
) {
406+
htlcInputIndex = 1
407+
}
408+
}
409+
376410
// Add the htlc input.
377411
sweepTx.AddTxIn(&wire.TxIn{
378412
PreviousOutPoint: wire.OutPoint{
379413
Hash: htlcTx.TxHash(),
380-
Index: 0,
414+
Index: htlcInputIndex,
381415
},
382416
SignatureScript: htlc.SigScript,
383417
Sequence: htlc.SuccessSequence(),

staticaddr/loopin/manager.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020
"github.com/lightninglabs/loop/staticaddr/deposit"
2121
"github.com/lightninglabs/loop/swapserverrpc"
2222
looprpc "github.com/lightninglabs/loop/swapserverrpc"
23+
"github.com/lightningnetwork/lnd/input"
2324
"github.com/lightningnetwork/lnd/lntypes"
25+
"github.com/lightningnetwork/lnd/lnwallet"
2426
"github.com/lightningnetwork/lnd/routing/route"
2527
)
2628

@@ -205,8 +207,8 @@ func (m *Manager) Run(ctx context.Context, currentHeight uint32) error {
205207
case request.respChan <- resp:
206208

207209
case <-ctx.Done():
208-
// Noify subroutines that the main loop has been
209-
// canceled.
210+
// Notify subroutines that the main loop has
211+
// been canceled.
210212
close(m.exitChan)
211213

212214
return ctx.Err()
@@ -549,6 +551,15 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
549551
}
550552
totalDepositAmount := tmp.TotalDepositAmount()
551553

554+
// If the selected amount would leave a dust change output or exceeds
555+
// the total deposits value, we return an error.
556+
dustLimit := lnwallet.DustLimitForSize(input.P2TRSize)
557+
if totalDepositAmount-req.SelectedAmount < dustLimit {
558+
return nil, fmt.Errorf("selected amount %v leaves "+
559+
"dust or exceeds total deposit value %v",
560+
req.SelectedAmount, totalDepositAmount)
561+
}
562+
552563
// Check that the label is valid.
553564
err := labels.Validate(req.Label)
554565
if err != nil {
@@ -616,6 +627,7 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
616627
}
617628

618629
swap := &StaticAddressLoopIn{
630+
SelectedAmount: req.SelectedAmount,
619631
DepositOutpoints: req.DepositOutpoints,
620632
Deposits: deposits,
621633
Label: req.Label,

staticaddr/loopin/sql_store.go

+2
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ func (s *SqlStore) CreateLoopIn(ctx context.Context,
230230
HtlcTimeoutSweepAddress: loopIn.HtlcTimeoutSweepAddress.String(),
231231
HtlcTxFeeRateSatKw: int64(loopIn.HtlcTxFeeRate),
232232
DepositOutpoints: joinedOutpoints,
233+
SelectedAmount: int64(loopIn.SelectedAmount),
233234
PaymentTimeoutSeconds: int32(loopIn.PaymentTimeoutSeconds),
234235
}
235236

@@ -378,6 +379,7 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
378379
LastHop: row.LastHop,
379380
QuotedSwapFee: btcutil.Amount(row.QuotedSwapFeeSatoshis),
380381
DepositOutpoints: depositOutpoints,
382+
SelectedAmount: btcutil.Amount(row.SelectedAmount),
381383
HtlcTxFeeRate: chainfee.SatPerKWeight(
382384
row.HtlcTxFeeRateSatKw,
383385
),

0 commit comments

Comments
 (0)