From a5bf9999b882efd862e908da582de27a5a4b27fe Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Mon, 10 Aug 2026 16:01:17 +0000 Subject: [PATCH] terminal: don't wait for chain sync when tapd is integrated Before starting the sub-servers, we wait for lnd to be fully synced to its chain backend. lnd only reports itself as synced once its blockbeat dispatcher caught up, and block processing can be blocked on tapd's aux components: if a channel is being resolved on chain, the sweeper calls into tapd's aux sweeper, which blocks until tapd is started. Since tapd is only started after the full lnd client is created, litd deadlocks in that situation. And because the client creation is retried in a loop, we don't even fail, we just log "Retrying to create LND Services client" forever while tapd never comes up. We therefore skip the chain sync wait when tapd runs in-process, which is the only mode where the aux components are wired into lnd. We still wait for the chain notifier to be ready, which is what the sub-servers need to subscribe to blocks. --- terminal.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/terminal.go b/terminal.go index 41eae880..40144829 100644 --- a/terminal.go +++ b/terminal.go @@ -1003,6 +1003,23 @@ func (g *LightningTerminal) setupFullLNDClient(ctx context.Context, // so if we instruct the lndclient to wait for the wallet sync, we // should be fully ready to start all our subservers. This will just // block until lnd signals readiness. + // + // There is one exception to that: with both lnd and tapd running + // in-process, we must not wait for lnd to be synced to chain here. lnd + // only reports itself as synced once its blockbeat caught up, and block + // processing can be blocked on tapd's aux components. If for example a + // channel is being resolved on chain, the sweeper calls into tapd's aux + // sweeper, which blocks until tapd is started. As tapd is only started + // once we have the client below, waiting here would deadlock the + // startup and we'd retry forever without ever starting tapd. + integratedTapd := g.cfg.LndMode == ModeIntegrated && + g.cfg.TaprootAssetsMode == ModeIntegrated + blockUntilChainSynced := !integratedTapd + if integratedTapd { + log.Infof("Not waiting for lnd to be synced to chain, as " + + "tapd is running in integrated mode") + } + log.Infof("Connecting full lnd client") for { g.lndClient, err = lndclient.NewLndServices( @@ -1020,7 +1037,7 @@ func (g *LightningTerminal) setupFullLNDClient(ctx context.Context, RPCTimeout: g.cfg.LndRPCTimeout, ChainSyncPollInterval: g.cfg.LndConnectInterval, - BlockUntilChainSynced: true, + BlockUntilChainSynced: blockUntilChainSynced, BlockUntilUnlocked: true, BlockUntilChainNotifier: true, },