staticaddr: require positive heights at startup

Loop now guards all static-address managers against zero block heights: each
constructor returns an error when invoked with a non-positive current height,
and `loopd` validates the height from `GetInfo` before instantiating them.
Tests and helper code were updated accordingly so we fail fast instead of
registering chain notifications with invalid hints.
This commit is contained in:
Boris Nagaev 2025-11-16 00:08:19 -03:00
parent bde49d7a91
commit 6d480cfd9e
No known key found for this signature in database
7 changed files with 69 additions and 12 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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.

View file

@ -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,

View file

@ -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.

View file

@ -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.

View file

@ -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)
}