@@ -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.
470518func (m * Manager ) recoverLoopIns (ctx context.Context ) error {
0 commit comments