Skip to content

Commit e7d64da

Browse files
committed
staticaddr: arbitrary loop-in amount
In this commit we add a new function SelectDeposits to the loop-in manager. It coin-selects deposits that meet an arbitrary swap amount provided by the client. We have to ensure that the server creates the correct change outputs for the htlc- and sweepless sweep transactions.
1 parent f1ec92b commit e7d64da

File tree

7 files changed

+400
-35
lines changed

7 files changed

+400
-35
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.

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/interface.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ type DepositManager interface {
5353
// outpoints.
5454
DepositsForOutpoints(ctx context.Context, outpoints []string) (
5555
[]*deposit.Deposit, error)
56+
57+
// GetActiveDepositsInState returns all active deposits in the given
58+
// state.
59+
GetActiveDepositsInState(stateFilter fsm.StateType) ([]*deposit.Deposit,
60+
error)
5661
}
5762

5863
// StaticAddressLoopInStore provides access to the static address loop-in DB.

staticaddr/loopin/loopin.go

Lines changed: 50 additions & 7 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

@@ -283,14 +289,25 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params,
283289
})
284290
}
285291

292+
// Determine the swap amount. If the user selected a specific amount, we
293+
// use that and use the difference to the total deposit amount as the
294+
// change.
295+
var (
296+
swapAmt = l.TotalDepositAmount()
297+
changeAmount btcutil.Amount
298+
)
299+
if l.SelectedAmount > 0 {
300+
swapAmt = l.SelectedAmount
301+
changeAmount = l.TotalDepositAmount() - l.SelectedAmount
302+
}
303+
286304
// Calculate htlc tx fee for server provided fee rate.
287-
weight := l.htlcWeight()
305+
hasChange := changeAmount > 0
306+
weight := l.htlcWeight(hasChange)
288307
fee := feeRate.FeeForWeight(weight)
289308

290309
// Check if the server breaches our fee limits.
291-
amt := float64(l.TotalDepositAmount())
292-
feeLimit := btcutil.Amount(amt * maxFeePercentage)
293-
310+
feeLimit := btcutil.Amount(float64(swapAmt) * maxFeePercentage)
294311
if fee > feeLimit {
295312
return nil, fmt.Errorf("htlc tx fee %v exceeds max fee %v",
296313
fee, feeLimit)
@@ -308,12 +325,20 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params,
308325

309326
// Create the sweep output
310327
sweepOutput := &wire.TxOut{
311-
Value: int64(l.TotalDepositAmount()) - int64(fee),
328+
Value: int64(swapAmt - fee),
312329
PkScript: pkscript,
313330
}
314331

315332
msgTx.AddTxOut(sweepOutput)
316333

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

@@ -325,7 +350,7 @@ func (l *StaticAddressLoopIn) isHtlcTimedOut(height int32) bool {
325350
}
326351

327352
// htlcWeight returns the weight for the htlc transaction.
328-
func (l *StaticAddressLoopIn) htlcWeight() lntypes.WeightUnit {
353+
func (l *StaticAddressLoopIn) htlcWeight(hasChange bool) lntypes.WeightUnit {
329354
var weightEstimator input.TxWeightEstimator
330355
for i := 0; i < len(l.Deposits); i++ {
331356
weightEstimator.AddTaprootKeySpendInput(
@@ -335,6 +360,10 @@ func (l *StaticAddressLoopIn) htlcWeight() lntypes.WeightUnit {
335360

336361
weightEstimator.AddP2WSHOutput()
337362

363+
if hasChange {
364+
weightEstimator.AddP2TROutput()
365+
}
366+
338367
return weightEstimator.Weight()
339368
}
340369

@@ -373,11 +402,25 @@ func (l *StaticAddressLoopIn) createHtlcSweepTx(ctx context.Context,
373402
return nil, err
374403
}
375404

405+
// Check if the htlc tx has a change output. If so we need to select the
406+
// non-change output index to construct the sweep with.
407+
htlcInputIndex := uint32(0)
408+
if len(htlcTx.TxOut) == 2 {
409+
// If the first htlc tx output matches our static address
410+
// script we need to select the second output to sweep from.
411+
if bytes.Equal(
412+
htlcTx.TxOut[0].PkScript, l.AddressParams.PkScript,
413+
) {
414+
415+
htlcInputIndex = 1
416+
}
417+
}
418+
376419
// Add the htlc input.
377420
sweepTx.AddTxIn(&wire.TxIn{
378421
PreviousOutPoint: wire.OutPoint{
379422
Hash: htlcTx.TxHash(),
380-
Index: 0,
423+
Index: htlcInputIndex,
381424
},
382425
SignatureScript: htlc.SigScript,
383426
Sequence: htlc.SuccessSequence(),

0 commit comments

Comments
 (0)