From cc5113da0af3da7a0286d28fe5d90075e5718ec2 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Sun, 22 Jun 2025 21:55:22 -0300 Subject: [PATCH 1/6] staticaddr: remove an unused interface method --- staticaddr/withdraw/interface.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/staticaddr/withdraw/interface.go b/staticaddr/withdraw/interface.go index dd6587e8..065a6f8d 100644 --- a/staticaddr/withdraw/interface.go +++ b/staticaddr/withdraw/interface.go @@ -8,7 +8,6 @@ import ( "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" "github.com/lightninglabs/loop/staticaddr/script" - "github.com/lightningnetwork/lnd/lnwallet" ) // Store is the database interface that is used to store and retrieve @@ -32,10 +31,6 @@ type AddressManager interface { // GetStaticAddress returns the deposit address for the given // client and server public keys. GetStaticAddress(ctx context.Context) (*script.StaticAddress, error) - - // ListUnspent returns a list of utxos at the static address. - ListUnspent(ctx context.Context, minConfs, - maxConfs int32) ([]*lnwallet.Utxo, error) } type DepositManager interface { From 3ed8c7a9fc07fe951995587085b3b30559cf031c Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Sun, 22 Jun 2025 22:29:07 -0300 Subject: [PATCH 2/6] staticaddr/withdraw: log PublishTransaction start --- staticaddr/withdraw/manager.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/staticaddr/withdraw/manager.go b/staticaddr/withdraw/manager.go index 0cd4adad..7d12709b 100644 --- a/staticaddr/withdraw/manager.go +++ b/staticaddr/withdraw/manager.go @@ -558,6 +558,9 @@ func (m *Manager) publishFinalizedWithdrawalTx(ctx context.Context, "withdrawal tx is nil") } + log.Debugf("Publishing deposit withdrawal with txid: %v ...", + tx.TxHash()) + txLabel := fmt.Sprintf("deposit-withdrawal-%v", tx.TxHash()) // Publish the withdrawal sweep transaction. @@ -577,7 +580,7 @@ func (m *Manager) publishFinalizedWithdrawalTx(ctx context.Context, return false, nil } } else { - log.Debugf("published deposit withdrawal with txid: %v", + log.Debugf("Published deposit withdrawal with txid: %v", tx.TxHash()) } From 6f50b27b7dc11e0992f088dd2650cefa61073210 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Sun, 22 Jun 2025 22:29:48 -0300 Subject: [PATCH 3/6] loopd: increase withdrawal manager start timeout This is needed for neutrino. --- loopd/daemon.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/loopd/daemon.go b/loopd/daemon.go index 9feca79e..7212933a 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -922,10 +922,15 @@ func (d *Daemon) initialize(withMacaroonService bool) error { } }() + // We need a higher timeout here, because withdrawalManager + // publishes transactions and each PublishTransaction call can + // wait for getting inv messages from a peer (neutrino). + const withdrawalManagerTimeout = time.Minute + // Wait for the static address withdrawal manager to be ready // before starting the grpc server. timeOutCtx, cancel := context.WithTimeout( - d.mainCtx, initManagerTimeout, + d.mainCtx, withdrawalManagerTimeout, ) select { case <-timeOutCtx.Done(): From 38aaa4d07a903661c36f35efbbde8f5f20a4247e Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Mon, 23 Jun 2025 00:56:58 -0300 Subject: [PATCH 4/6] staticaddr: recover withdrawals in parallel Use errgroup to publish each transaction in a separate goroutine. Publishing a transaction can take a while in neutrino mode. --- go.mod | 7 ++-- staticaddr/withdraw/manager.go | 58 +++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 5cd43d6c..a609a47b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/btcutil/psbt v1.1.10 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 - github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c // indirect + github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318 github.com/btcsuite/btcwallet v0.16.13 github.com/btcsuite/btcwallet/wtxmgr v1.5.6 github.com/davecgh/go-spew v1.1.1 @@ -36,6 +36,7 @@ require ( github.com/urfave/cli v1.22.14 go.etcd.io/bbolt v1.3.11 golang.org/x/net v0.38.0 + golang.org/x/sync v0.12.0 google.golang.org/grpc v1.64.1 google.golang.org/protobuf v1.34.2 gopkg.in/macaroon-bakery.v2 v2.3.0 @@ -43,8 +44,6 @@ require ( modernc.org/sqlite v1.34.5 ) -require github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318 - require ( dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -56,6 +55,7 @@ require ( github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/siphash v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c // indirect github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 // indirect github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 // indirect github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 // indirect @@ -185,7 +185,6 @@ require ( golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/mod v0.21.0 // indirect - golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/term v0.30.0 // indirect golang.org/x/text v0.23.0 // indirect diff --git a/staticaddr/withdraw/manager.go b/staticaddr/withdraw/manager.go index 7d12709b..2914d518 100644 --- a/staticaddr/withdraw/manager.go +++ b/staticaddr/withdraw/manager.go @@ -26,6 +26,7 @@ import ( "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "golang.org/x/sync/errgroup" ) var ( @@ -238,32 +239,45 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error { ) } + // Publishing a transaction can take a while in neutrino mode, so + // do it in parallel. + eg := &errgroup.Group{} + // We can now reinstate each cluster of deposits for a withdrawal. - for finalizedWithdrawalTx, deposits := range depositsByWithdrawalTx { - tx := finalizedWithdrawalTx - err = m.cfg.DepositManager.TransitionDeposits( - ctx, deposits, deposit.OnWithdrawInitiated, - deposit.Withdrawing, - ) - if err != nil { - return err - } + for tx, deposits := range depositsByWithdrawalTx { + eg.Go(func() error { + err := m.cfg.DepositManager.TransitionDeposits( + ctx, deposits, deposit.OnWithdrawInitiated, + deposit.Withdrawing, + ) + if err != nil { + return err + } - _, err = m.publishFinalizedWithdrawalTx(ctx, tx) - if err != nil { - return err - } + _, err = m.publishFinalizedWithdrawalTx(ctx, tx) + if err != nil { + return err + } - err = m.handleWithdrawal( - ctx, deposits, tx.TxHash(), tx.TxOut[0].PkScript, - ) - if err != nil { - return err - } + err = m.handleWithdrawal( + ctx, deposits, tx.TxHash(), + tx.TxOut[0].PkScript, + ) + if err != nil { + return err + } - m.mu.Lock() - m.finalizedWithdrawalTxns[tx.TxHash()] = tx - m.mu.Unlock() + m.mu.Lock() + m.finalizedWithdrawalTxns[tx.TxHash()] = tx + m.mu.Unlock() + + return nil + }) + } + + // Wait for all goroutines to report back. + if err := eg.Wait(); err != nil { + return fmt.Errorf("error recovering withdrawals: %w", err) } return nil From 61542197f68308ff87ea13875dd9676193446cd5 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Mon, 23 Jun 2025 09:36:29 -0300 Subject: [PATCH 5/6] staticaddr: use tx hash not *wire.MsgTx as a key Do not rely on GetActiveDepositsInState reusing the same *wire.MsgTx pointer for a unique transaction. --- staticaddr/withdraw/manager.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/staticaddr/withdraw/manager.go b/staticaddr/withdraw/manager.go index 2914d518..408d0338 100644 --- a/staticaddr/withdraw/manager.go +++ b/staticaddr/withdraw/manager.go @@ -227,15 +227,18 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error { } // Group the deposits by their finalized withdrawal transaction. - depositsByWithdrawalTx := make(map[*wire.MsgTx][]*deposit.Deposit) + depositsByWithdrawalTx := make(map[chainhash.Hash][]*deposit.Deposit) + hash2tx := make(map[chainhash.Hash]*wire.MsgTx) for _, d := range activeDeposits { withdrawalTx := d.FinalizedWithdrawalTx if withdrawalTx == nil { continue } + txid := withdrawalTx.TxHash() + hash2tx[txid] = withdrawalTx - depositsByWithdrawalTx[withdrawalTx] = append( - depositsByWithdrawalTx[withdrawalTx], d, + depositsByWithdrawalTx[txid] = append( + depositsByWithdrawalTx[txid], d, ) } @@ -244,7 +247,7 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error { eg := &errgroup.Group{} // We can now reinstate each cluster of deposits for a withdrawal. - for tx, deposits := range depositsByWithdrawalTx { + for txid, deposits := range depositsByWithdrawalTx { eg.Go(func() error { err := m.cfg.DepositManager.TransitionDeposits( ctx, deposits, deposit.OnWithdrawInitiated, @@ -254,6 +257,11 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error { return err } + tx, ok := hash2tx[txid] + if !ok { + return fmt.Errorf("can't find tx %v", txid) + } + _, err = m.publishFinalizedWithdrawalTx(ctx, tx) if err != nil { return err From 51b74512b8df8dbac54f0dcccc3a2ab76852893c Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Tue, 24 Jun 2025 01:44:01 -0300 Subject: [PATCH 6/6] staticaddr: fix error formatting in the log --- staticaddr/deposit/fsm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staticaddr/deposit/fsm.go b/staticaddr/deposit/fsm.go index 2d1751ff..1fad4dc2 100644 --- a/staticaddr/deposit/fsm.go +++ b/staticaddr/deposit/fsm.go @@ -399,7 +399,7 @@ func (f *FSM) updateDeposit(ctx context.Context, err := f.cfg.Store.UpdateDeposit(ctx, f.deposit) if err != nil { - f.Errorf("unable to update deposit: %w", err) + f.Errorf("unable to update deposit: %v", err) } }