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