Skip to content

Commit b9852cb

Browse files
committed
staticaddr: checkChange method for sweep signing
1 parent 1afdb9f commit b9852cb

File tree

3 files changed

+443
-23
lines changed

3 files changed

+443
-23
lines changed

staticaddr/loopin/interface.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ type StaticAddressLoopInStore interface {
7979
// GetLoopInByHash returns the loop-in swap with the given hash.
8080
GetLoopInByHash(ctx context.Context, swapHash lntypes.Hash) (
8181
*StaticAddressLoopIn, error)
82+
83+
// SwapHashesForDepositIDs returns a map of swap hashes to deposit IDs
84+
// for the given deposit IDs.
85+
SwapHashesForDepositIDs(ctx context.Context,
86+
depositIDs []deposit.ID) (map[lntypes.Hash][]deposit.ID, error)
8287
}
8388

8489
type QuoteGetter interface {

staticaddr/loopin/manager.go

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/lightninglabs/loop"
2020
"github.com/lightninglabs/loop/fsm"
2121
"github.com/lightninglabs/loop/labels"
22+
"github.com/lightninglabs/loop/staticaddr/address"
2223
"github.com/lightninglabs/loop/staticaddr/deposit"
2324
"github.com/lightninglabs/loop/swapserverrpc"
2425
"github.com/lightningnetwork/lnd/input"
@@ -324,29 +325,9 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
324325
// If the user selected an amount that is less than the total deposit
325326
// amount we'll check that the server sends us the correct change amount
326327
// back to our static address.
327-
totalDepositAmount := loopIn.TotalDepositAmount()
328-
changeAmt := totalDepositAmount - loopIn.SelectedAmount
329-
if changeAmt > 0 && changeAmt < totalDepositAmount {
330-
var foundChange bool
331-
changePkScript := loopIn.AddressParams.PkScript
332-
333-
for _, out := range sweepTx.TxOut {
334-
if out.Value == int64(changeAmt) &&
335-
bytes.Equal(out.PkScript, changePkScript) {
336-
337-
foundChange = true
338-
break
339-
}
340-
}
341-
342-
if !foundChange {
343-
return fmt.Errorf("expected change output to our "+
344-
"static address, total_deposit_amount=%v, "+
345-
"selected_amount=%v, "+
346-
"expected_change_amount=%v ",
347-
totalDepositAmount, loopIn.SelectedAmount,
348-
changeAmt)
349-
}
328+
err = m.checkChange(ctx, sweepTx, loopIn.AddressParams)
329+
if err != nil {
330+
return err
350331
}
351332

352333
// Check if all the deposits requested are part of the loop-in and
@@ -465,6 +446,73 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
465446
return err
466447
}
467448

449+
// checkChange ensures that the server sends us the correct change amount
450+
// back to our static address. An edge case arises if a batch contains two
451+
// swaps with identical change outputs. The client needs to ensure that any
452+
// swap referenced by the inputs has a respective change output in the batch.
453+
func (m *Manager) checkChange(ctx context.Context,
454+
sweepTx *wire.MsgTx, changeAddr *address.Parameters) error {
455+
456+
prevOuts := make([]string, len(sweepTx.TxIn))
457+
for i, in := range sweepTx.TxIn {
458+
prevOuts[i] = in.PreviousOutPoint.String()
459+
}
460+
461+
deposits, err := m.cfg.DepositManager.DepositsForOutpoints(
462+
ctx, prevOuts,
463+
)
464+
if err != nil {
465+
return err
466+
}
467+
468+
depositIDs := make([]deposit.ID, len(deposits))
469+
for i, d := range deposits {
470+
depositIDs[i] = d.ID
471+
}
472+
473+
swapHashes, err := m.cfg.Store.SwapHashesForDepositIDs(ctx, depositIDs)
474+
if err != nil {
475+
return err
476+
}
477+
478+
var expectedChange btcutil.Amount
479+
for swapHash := range swapHashes {
480+
loopIn, err := m.cfg.Store.GetLoopInByHash(ctx, swapHash)
481+
if err != nil {
482+
return err
483+
}
484+
485+
totalDepositAmount := loopIn.TotalDepositAmount()
486+
changeAmt := totalDepositAmount - loopIn.SelectedAmount
487+
if changeAmt > 0 && changeAmt < totalDepositAmount {
488+
log.Debugf("expected change output to our "+
489+
"static address, total_deposit_amount=%v, "+
490+
"selected_amount=%v, "+
491+
"expected_change_amount=%v ",
492+
totalDepositAmount, loopIn.SelectedAmount,
493+
changeAmt)
494+
495+
expectedChange += changeAmt
496+
}
497+
}
498+
499+
if expectedChange == 0 {
500+
return nil
501+
}
502+
503+
for _, out := range sweepTx.TxOut {
504+
if out.Value == int64(expectedChange) &&
505+
bytes.Equal(out.PkScript, changeAddr.PkScript) {
506+
507+
// We found the expected change output.
508+
return nil
509+
}
510+
}
511+
512+
return fmt.Errorf("couldn't find expected change of %v "+
513+
"satoshis sent to our static address", expectedChange)
514+
}
515+
468516
// recover stars a loop-in state machine for each non-final loop-in to pick up
469517
// work where it was left off before the restart.
470518
func (m *Manager) recoverLoopIns(ctx context.Context) error {

0 commit comments

Comments
 (0)