diff --git a/loopd/daemon.go b/loopd/daemon.go index 6ac443e7..eb1b4947 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -451,6 +451,10 @@ func (d *Daemon) initialize(withMacaroonService bool) error { return fmt.Errorf("failed to get current block height: %w", err) } blockHeight := getInfo.BlockHeight + if blockHeight <= 0 { + return fmt.Errorf("invalid block height reported by lnd: %d", + blockHeight) + } // If we're running an asset client, we'll log something here. if d.assetClient != nil { @@ -586,7 +590,13 @@ func (d *Daemon) initialize(withMacaroonService bool) error { ChainParams: d.lnd.ChainParams, ChainNotifier: d.lnd.ChainNotifier, } - staticAddressManager = address.NewManager(addrCfg, int32(blockHeight)) + staticAddressManager, err = address.NewManager( + addrCfg, int32(blockHeight), + ) + if err != nil { + return fmt.Errorf("unable to create static address manager: %w", + err) + } // Static address deposit manager setup. depositStore := deposit.NewSqlStore(baseDb) @@ -617,7 +627,13 @@ func (d *Daemon) initialize(withMacaroonService bool) error { Signer: d.lnd.Signer, Store: withdrawalStore, } - withdrawalManager = withdraw.NewManager(withdrawalCfg, blockHeight) + withdrawalManager, err = withdraw.NewManager( + withdrawalCfg, blockHeight, + ) + if err != nil { + return fmt.Errorf("unable to create withdrawal manager: %w", + err) + } // Static address loop-in manager setup. staticAddressLoopInStore := loopin.NewSqlStore( @@ -645,7 +661,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { return err } - staticLoopInManager = loopin.NewManager(&loopin.Config{ + staticLoopInManager, err = loopin.NewManager(&loopin.Config{ Server: staticAddressClient, QuoteGetter: swapClient.Server, LndClient: d.lnd.Client, @@ -663,6 +679,9 @@ func (d *Daemon) initialize(withMacaroonService bool) error { MaxStaticAddrHtlcFeePercentage: d.cfg.MaxStaticAddrHtlcFeePercentage, MaxStaticAddrHtlcBackupFeePercentage: d.cfg.MaxStaticAddrHtlcBackupFeePercentage, }, blockHeight) + if err != nil { + return fmt.Errorf("unable to create loop-in manager: %w", err) + } var ( reservationManager *reservation.Manager diff --git a/loopd/swapclient_server_test.go b/loopd/swapclient_server_test.go index 655942d2..a72c98d4 100644 --- a/loopd/swapclient_server_test.go +++ b/loopd/swapclient_server_test.go @@ -1006,12 +1006,13 @@ func TestListUnspentDeposits(t *testing.T) { addrStore := &mockAddressStore{params: []*address.Parameters{addrParams}} // Build an address manager using our mock lnd and fake address store. - addrMgr := address.NewManager(&address.ManagerConfig{ + addrMgr, err := address.NewManager(&address.ManagerConfig{ Store: addrStore, WalletKit: mock.WalletKit, ChainParams: mock.ChainParams, // ChainNotifier and AddressClient are not needed for this test. - }, 0) + }, 1) + require.NoError(t, err) // Construct several UTXOs with different confirmation counts. makeUtxo := func(idx uint32, confs int64) *lnwallet.Utxo { diff --git a/staticaddr/address/manager.go b/staticaddr/address/manager.go index 1d63bd7f..e96d362b 100644 --- a/staticaddr/address/manager.go +++ b/staticaddr/address/manager.go @@ -57,13 +57,18 @@ type Manager struct { } // NewManager creates a new address manager. -func NewManager(cfg *ManagerConfig, currentHeight int32) *Manager { +func NewManager(cfg *ManagerConfig, currentHeight int32) (*Manager, error) { + if currentHeight <= 0 { + return nil, fmt.Errorf("invalid current height %d", + currentHeight) + } + m := &Manager{ cfg: cfg, } m.currentHeight.Store(currentHeight) - return m + return m, nil } // Run runs the address manager. diff --git a/staticaddr/address/manager_test.go b/staticaddr/address/manager_test.go index 017ad4c8..05f2a5b8 100644 --- a/staticaddr/address/manager_test.go +++ b/staticaddr/address/manager_test.go @@ -195,7 +195,8 @@ func NewAddressManagerTestContext(t *testing.T) *ManagerTestContext { getInfo, err := mockLnd.Client.GetInfo(ctxb) require.NoError(t, err) - manager := NewManager(cfg, int32(getInfo.BlockHeight)) + manager, err := NewManager(cfg, int32(getInfo.BlockHeight)) + require.NoError(t, err) return &ManagerTestContext{ manager: manager, diff --git a/staticaddr/loopin/manager.go b/staticaddr/loopin/manager.go index 05f28f1c..2cd1a85b 100644 --- a/staticaddr/loopin/manager.go +++ b/staticaddr/loopin/manager.go @@ -143,7 +143,12 @@ type Manager struct { } // NewManager creates a new deposit withdrawal manager. -func NewManager(cfg *Config, currentHeight uint32) *Manager { +func NewManager(cfg *Config, currentHeight uint32) (*Manager, error) { + if currentHeight == 0 { + return nil, fmt.Errorf("invalid current height %d", + currentHeight) + } + m := &Manager{ cfg: cfg, newLoopInChan: make(chan *newSwapRequest), @@ -153,7 +158,7 @@ func NewManager(cfg *Config, currentHeight uint32) *Manager { } m.currentHeight.Store(currentHeight) - return m + return m, nil } // Run runs the static address loop-in manager. diff --git a/staticaddr/withdraw/manager.go b/staticaddr/withdraw/manager.go index 0bc29d82..8f542d4b 100644 --- a/staticaddr/withdraw/manager.go +++ b/staticaddr/withdraw/manager.go @@ -138,7 +138,12 @@ type Manager struct { } // NewManager creates a new deposit withdrawal manager. -func NewManager(cfg *ManagerConfig, currentHeight uint32) *Manager { +func NewManager(cfg *ManagerConfig, currentHeight uint32) (*Manager, error) { + if currentHeight == 0 { + return nil, fmt.Errorf("invalid current height %d", + currentHeight) + } + m := &Manager{ cfg: cfg, finalizedWithdrawalTxns: make(map[chainhash.Hash]*wire.MsgTx), @@ -148,7 +153,7 @@ func NewManager(cfg *ManagerConfig, currentHeight uint32) *Manager { } m.initiationHeight.Store(currentHeight) - return m + return m, nil } // Run runs the deposit withdrawal manager. diff --git a/staticaddr/withdraw/manager_test.go b/staticaddr/withdraw/manager_test.go new file mode 100644 index 00000000..0bfbad75 --- /dev/null +++ b/staticaddr/withdraw/manager_test.go @@ -0,0 +1,21 @@ +package withdraw + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestNewManagerHeightValidation ensures the constructor rejects zero heights. +func TestNewManagerHeightValidation(t *testing.T) { + t.Parallel() + + cfg := &ManagerConfig{} + + _, err := NewManager(cfg, 0) + require.ErrorContains(t, err, "invalid current height 0") + + manager, err := NewManager(cfg, 1) + require.NoError(t, err) + require.NotNil(t, manager) +}