staticaddr/loopin: publish status updates after persistence

This commit is contained in:
Gustavo Stingelin 2026-07-08 23:39:05 -03:00
parent f5cc85f7f3
commit e3e6205a4b
No known key found for this signature in database
GPG key ID: 15CBADFE29F2017B
8 changed files with 206 additions and 2 deletions

View file

@ -110,6 +110,10 @@ type Daemon struct {
macaroonService *lndclient.MacaroonService
}
// staticLoopInStatusChanBuffer keeps the shared swap-status fanout from
// stalling static loop-in FSM progress during transient subscriber gaps.
const staticLoopInStatusChanBuffer = 20
// New creates a new instance of the loop client daemon.
func New(config *Config, lisCfg *ListenerCfg) *Daemon {
return &Daemon{
@ -681,6 +685,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
LightningClient: d.lnd.Client,
}
openChannelManager = openchannel.NewManager(openChannelCfg)
statusChan := make(chan loop.SwapInfo, staticLoopInStatusChanBuffer)
// Run the deposit swap hash migration.
err = loopin.MigrateDepositSwapHash(
@ -701,6 +706,11 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
return err
}
statusUpdater := &staticLoopInStatusUpdater{
statusChan: statusChan,
mainCtx: d.mainCtx,
chainParams: d.lnd.ChainParams,
}
staticLoopInManager, err = loopin.NewManager(&loopin.Config{
Server: staticAddressClient,
@ -718,6 +728,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
ChainParams: d.lnd.ChainParams,
Signer: d.lnd.Signer,
ValidateLoopInContract: loop.ValidateLoopInContract,
SendUpdate: statusUpdater.sendUpdate,
MaxStaticAddrHtlcFeePercentage: d.cfg.MaxStaticAddrHtlcFeePercentage,
MaxStaticAddrHtlcBackupFeePercentage: d.cfg.MaxStaticAddrHtlcBackupFeePercentage,
}, blockHeight)
@ -783,7 +794,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
lnd: &d.lnd.LndServices,
swaps: make(map[lntypes.Hash]loop.SwapInfo),
subscribers: make(map[int]chan<- any),
statusChan: make(chan loop.SwapInfo),
statusChan: statusChan,
mainCtx: d.mainCtx,
reservationManager: reservationManager,
instantOutManager: instantOutManager,

View file

@ -0,0 +1,47 @@
package loopd
import (
"context"
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/staticaddr/loopin"
)
// staticLoopInStatusUpdater publishes static-address loop-in status updates to
// the client-facing swap stream.
type staticLoopInStatusUpdater struct {
// statusChan sends updates to the client-facing swap stream.
statusChan chan<- loop.SwapInfo
// mainCtx is canceled when loopd is shutting down.
mainCtx context.Context
// chainParams are used to reconstruct the static loop-in HTLC address.
chainParams *chaincfg.Params
}
// sendUpdate converts the persisted static-address loop-in into swap info and
// forwards it to the status stream unless either context is canceled.
func (u *staticLoopInStatusUpdater) sendUpdate(ctx context.Context,
swp *loopin.StaticAddressLoopIn) error {
info, err := staticAddressLoopInSwapInfoWithChainParams(
swp, u.chainParams,
)
if err != nil {
return fmt.Errorf("unable to notify static loop-in update: %w", err)
}
select {
case u.statusChan <- *info:
return nil
case <-ctx.Done():
return ctx.Err()
case <-u.mainCtx.Done():
return u.mainCtx.Err()
}
}

View file

@ -661,6 +661,26 @@ func TestMonitorSnapshotIncludesFinalStaticAddressLoopIns(t *testing.T) {
require.Len(t, completedSwaps, 1)
}
// TestStaticLoopInStatusUpdaterUsesSwapHtlcAddress protects the live-update
// invariant that static loop-in status events derive the HTLC address from the
// swap, not from reusable static address parameters.
func TestStaticLoopInStatusUpdaterUsesSwapHtlcAddress(t *testing.T) {
ctx := t.Context()
_, staticLoopIn := newGenericStaticLoopInServer(t)
staticLoopIn.AddressParams = nil
statusChan := make(chan loop.SwapInfo, 1)
updater := &staticLoopInStatusUpdater{
statusChan: statusChan,
mainCtx: ctx,
chainParams: &chaincfg.TestNet3Params,
}
err := updater.sendUpdate(ctx, staticLoopIn)
require.NoError(t, err)
swapInfo := <-statusChan
assertStaticLoopInUsesSwapHtlcAddress(t, staticLoopIn, swapInfo)
}
// TestMonitorSuppressesStaticAddressLoopInSnapshotLiveDuplicate protects the
// monitor race invariant that live static loop-in updates arriving during the
// initial snapshot are deduplicated without dropping newer progress.