@@ -9,17 +9,16 @@ import (
99
1010 "github.com/btcsuite/btcutil"
1111
12- "github.com/lightningnetwork/lnd/chainntnfs"
13- "github.com/lightningnetwork/lnd/channeldb"
14- "github.com/lightningnetwork/lnd/lnwire"
15-
12+ "github.com/btcsuite/btcd/chaincfg/chainhash"
1613 "github.com/btcsuite/btcd/wire"
17- "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
18-
1914 "github.com/lightninglabs/lndclient"
2015 "github.com/lightninglabs/loop/loopdb"
2116 "github.com/lightninglabs/loop/swap"
17+ "github.com/lightningnetwork/lnd/chainntnfs"
18+ "github.com/lightningnetwork/lnd/channeldb"
19+ "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
2220 "github.com/lightningnetwork/lnd/lntypes"
21+ "github.com/lightningnetwork/lnd/lnwire"
2322)
2423
2524var (
@@ -58,6 +57,9 @@ type loopInSwap struct {
5857
5958 htlcNP2WSH * swap.Htlc
6059
60+ // htlcTxHash is the confirmed htlc tx id.
61+ htlcTxHash * chainhash.Hash
62+
6163 timeoutAddr btcutil.Address
6264}
6365
@@ -209,6 +211,7 @@ func resumeLoopInSwap(reqContext context.Context, cfg *swapConfig,
209211 } else {
210212 swap .state = lastUpdate .State
211213 swap .lastUpdateTime = lastUpdate .Time
214+ swap .htlcTxHash = lastUpdate .HtlcTxHash
212215 }
213216
214217 return swap , nil
@@ -394,20 +397,35 @@ func (s *loopInSwap) executeSwap(globalCtx context.Context) error {
394397func (s * loopInSwap ) waitForHtlcConf (globalCtx context.Context ) (
395398 * chainntnfs.TxConfirmation , error ) {
396399
400+ // Register for confirmation of the htlc. It is essential to specify not
401+ // just the pk script, because an attacker may publish the same htlc
402+ // with a lower value and we don't want to follow through with that tx.
403+ // In the unlikely event that our call to SendOutputs crashes and we
404+ // restart, htlcTxHash will be nil at this point. Then only register
405+ // with PkScript and accept the risk that the call triggers on a
406+ // different htlc outpoint.
407+ s .log .Infof ("Register for htlc conf (hh=%v, txid=%v)" ,
408+ s .InitiationHeight , s .htlcTxHash )
409+
410+ if s .htlcTxHash == nil {
411+ s .log .Warnf ("No htlc tx hash available, registering with " +
412+ "just the pkscript" )
413+ }
414+
397415 ctx , cancel := context .WithCancel (globalCtx )
398416 defer cancel ()
399417
400418 notifier := s .lnd .ChainNotifier
401419
402420 confChanP2WSH , confErrP2WSH , err := notifier .RegisterConfirmationsNtfn (
403- ctx , nil , s .htlcP2WSH .PkScript , 1 , s .InitiationHeight ,
421+ ctx , s . htlcTxHash , s .htlcP2WSH .PkScript , 1 , s .InitiationHeight ,
404422 )
405423 if err != nil {
406424 return nil , err
407425 }
408426
409427 confChanNP2WSH , confErrNP2WSH , err := notifier .RegisterConfirmationsNtfn (
410- ctx , nil , s .htlcNP2WSH .PkScript , 1 , s .InitiationHeight ,
428+ ctx , s . htlcTxHash , s .htlcNP2WSH .PkScript , 1 , s .InitiationHeight ,
411429 )
412430 if err != nil {
413431 return nil , err
@@ -445,6 +463,17 @@ func (s *loopInSwap) waitForHtlcConf(globalCtx context.Context) (
445463 }
446464 }
447465
466+ // Store htlc tx hash for accounting purposes. Usually this call is a
467+ // no-op because the htlc tx hash was already known. Exceptions are:
468+ //
469+ // - Old pending swaps that were initiated before we persisted the htlc
470+ // tx hash directly after publish.
471+ //
472+ // - Swaps that experienced a crash during their call to SendOutputs. In
473+ // that case, we weren't able to record the tx hash.
474+ txHash := conf .Tx .TxHash ()
475+ s .htlcTxHash = & txHash
476+
448477 return conf , nil
449478}
450479
@@ -491,7 +520,20 @@ func (s *loopInSwap) publishOnChainHtlc(ctx context.Context) (bool, error) {
491520 if err != nil {
492521 return false , fmt .Errorf ("send outputs: %v" , err )
493522 }
494- s .log .Infof ("Published on chain HTLC tx %v" , tx .TxHash ())
523+ txHash := tx .TxHash ()
524+ s .log .Infof ("Published on chain HTLC tx %v" , txHash )
525+
526+ // Persist the htlc hash so that after a restart we are still waiting
527+ // for our own htlc. We don't need to announce to clients, because the
528+ // state remains unchanged.
529+ //
530+ // TODO(joostjager): Store tx hash before calling SendOutputs. This is
531+ // not yet possible with the current lnd api.
532+ s .htlcTxHash = & txHash
533+ s .lastUpdateTime = time .Now ()
534+ if err := s .persistState (); err != nil {
535+ return false , fmt .Errorf ("persist htlc tx: %v" , err )
536+ }
495537
496538 return true , nil
497539
@@ -507,7 +549,7 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
507549 rpcCtx , cancel := context .WithCancel (ctx )
508550 defer cancel ()
509551 spendChan , spendErr , err := s .lnd .ChainNotifier .RegisterSpendNtfn (
510- rpcCtx , nil , s .htlc .PkScript , s .InitiationHeight ,
552+ rpcCtx , htlcOutpoint , s .htlc .PkScript , s .InitiationHeight ,
511553 )
512554 if err != nil {
513555 return fmt .Errorf ("register spend ntfn: %v" , err )
@@ -714,8 +756,9 @@ func (s *loopInSwap) persistState() error {
714756 return s .store .UpdateLoopIn (
715757 s .hash , s .lastUpdateTime ,
716758 loopdb.SwapStateData {
717- State : s .state ,
718- Cost : s .cost ,
759+ State : s .state ,
760+ Cost : s .cost ,
761+ HtlcTxHash : s .htlcTxHash ,
719762 },
720763 )
721764}
0 commit comments