@@ -20,7 +20,9 @@ import (
2020 "github.com/lightninglabs/loop/staticaddr/deposit"
2121 "github.com/lightninglabs/loop/swapserverrpc"
2222 looprpc "github.com/lightninglabs/loop/swapserverrpc"
23+ "github.com/lightningnetwork/lnd/input"
2324 "github.com/lightningnetwork/lnd/lntypes"
25+ "github.com/lightningnetwork/lnd/lnwallet"
2426 "github.com/lightningnetwork/lnd/routing/route"
2527)
2628
@@ -206,8 +208,8 @@ func (m *Manager) Run(ctx context.Context) error {
206208 case request .respChan <- resp :
207209
208210 case <- ctx .Done ():
209- // Noify subroutines that the main loop has been
210- // canceled.
211+ // Notify subroutines that the main loop has
212+ // been canceled.
211213 close (m .exitChan )
212214
213215 return ctx .Err ()
@@ -272,6 +274,14 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
272274 return err
273275 }
274276
277+ deposits , err := m .cfg .DepositManager .DepositsForOutpoints (
278+ ctx , loopIn .DepositOutpoints ,
279+ )
280+ if err != nil {
281+ return err
282+ }
283+ loopIn .Deposits = deposits
284+
275285 reader := bytes .NewReader (req .SweepTxPsbt )
276286 sweepPacket , err := psbt .NewFromRawBytes (reader , false )
277287 if err != nil {
@@ -297,6 +307,33 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context,
297307 len (req .PrevoutInfo ), len (sweepTx .TxIn ))
298308 }
299309
310+ // If the user selected an amount we'll check that the server sends us
311+ // the correct change amount back to our static address.
312+ if loopIn .SelectedAmount > 0 {
313+ var foundChange bool
314+ totalDepositAmount := loopIn .TotalDepositAmount ()
315+ changeAmt := totalDepositAmount - loopIn .SelectedAmount
316+ changePkScript := loopIn .AddressParams .PkScript
317+
318+ for _ , out := range sweepTx .TxOut {
319+ if out .Value == int64 (changeAmt ) &&
320+ bytes .Equal (out .PkScript , changePkScript ) {
321+
322+ foundChange = true
323+ break
324+ }
325+ }
326+
327+ if ! foundChange {
328+ return fmt .Errorf ("expected change output to our " +
329+ "static address, total_deposit_amount=%v, " +
330+ "selected_amount=%v, " +
331+ "expected_change_amount=%v " ,
332+ totalDepositAmount , loopIn .SelectedAmount ,
333+ changeAmt )
334+ }
335+ }
336+
300337 // Check if all the deposits requested are part of the loop-in and
301338 // find them in the requested sweep.
302339 depositToIdxMap , err := mapDepositsToIndices (req , loopIn , sweepTx )
@@ -531,14 +568,20 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
531568 req * loop.StaticAddressLoopInRequest ) (* StaticAddressLoopIn , error ) {
532569
533570 // Validate the loop-in request.
534- if len (req .DepositOutpoints ) == 0 {
535- return nil , fmt .Errorf ("no deposit outpoints provided" )
571+ if len (req .DepositOutpoints ) == 0 && req .SelectedAmount == 0 {
572+ return nil , fmt .Errorf ("no deposit outpoints provided and no " +
573+ "amount selected" )
536574 }
537575
576+ var (
577+ err error
578+ selectedOutpoints = req .DepositOutpoints
579+ )
580+
538581 // Retrieve all deposits referenced by the outpoints and ensure that
539582 // they are in state Deposited.
540583 deposits , active := m .cfg .DepositManager .AllStringOutpointsActiveDeposits ( //nolint:lll
541- req . DepositOutpoints , deposit .Deposited ,
584+ selectedOutpoints , deposit .Deposited ,
542585 )
543586 if ! active {
544587 return nil , fmt .Errorf ("one or more deposits are not in " +
@@ -551,8 +594,22 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
551594 }
552595 totalDepositAmount := tmp .TotalDepositAmount ()
553596
597+ // If the selected amount would leave a dust change output or exceeds
598+ // the total deposits value, we return an error.
599+ dustLimit := lnwallet .DustLimitForSize (input .P2TRSize )
600+ if totalDepositAmount - req .SelectedAmount < dustLimit {
601+ return nil , fmt .Errorf ("selected amount %v leaves " +
602+ "dust or exceeds total deposit value %v" ,
603+ req .SelectedAmount , totalDepositAmount )
604+ }
605+
606+ swapAmount := totalDepositAmount
607+ if req .SelectedAmount > 0 {
608+ swapAmount = req .SelectedAmount
609+ }
610+
554611 // Check that the label is valid.
555- err : = labels .Validate (req .Label )
612+ err = labels .Validate (req .Label )
556613 if err != nil {
557614 return nil , fmt .Errorf ("invalid label: %w" , err )
558615 }
@@ -576,7 +633,7 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
576633 // Because the Private flag is set, we'll generate our own set
577634 // of hop hints.
578635 req .RouteHints , err = loop .SelectHopHints (
579- ctx , m .cfg .LndClient , totalDepositAmount ,
636+ ctx , m .cfg .LndClient , swapAmount ,
580637 loop .DefaultMaxHopHints , includeNodes ,
581638 )
582639 if err != nil {
@@ -593,11 +650,11 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
593650 // directly anyway and there they have the option to add specific route
594651 // hints.
595652 // The quote call will also request a probe from the server to ensure
596- // feasibility of a loop-in for the totalDepositAmount .
653+ // feasibility of a loop-in for the selected .
597654 numDeposits := uint32 (len (deposits ))
598655 quote , err := m .cfg .QuoteGetter .GetLoopInQuote (
599- ctx , totalDepositAmount , m .cfg .NodePubkey , req .LastHop ,
600- req .RouteHints , req . Initiator , numDeposits ,
656+ ctx , swapAmount , m .cfg .NodePubkey , req .LastHop , req . RouteHints ,
657+ req .Initiator , numDeposits ,
601658 )
602659 if err != nil {
603660 return nil , fmt .Errorf ("unable to get loop in quote: %w" , err )
@@ -618,6 +675,7 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
618675 }
619676
620677 swap := & StaticAddressLoopIn {
678+ SelectedAmount : req .SelectedAmount ,
621679 DepositOutpoints : req .DepositOutpoints ,
622680 Deposits : deposits ,
623681 Label : req .Label ,
0 commit comments