Skip to content

Commit ca7c14a

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

File tree

3 files changed

+441
-22
lines changed

3 files changed

+441
-22
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 & 22 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,10 @@ 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-
}
328+
err = m.checkChange(ctx, sweepTx, loopIn.AddressParams)
329+
if err != nil {
341330

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-
}
331+
return err
350332
}
351333

352334
// Check if all the deposits requested are part of the loop-in and
@@ -465,6 +447,73 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
465447
return err
466448
}
467449

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

0 commit comments

Comments
 (0)